Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenwave.c
Go to the documentation of this file.
00001 /* 00002 * 00003 * COPYRIGHT: See COPYING in the top level directory 00004 * PROJECT: ReactOS Multimedia 00005 * FILE: dll/win32/mmdrv/wave.c 00006 * PURPOSE: Multimedia User Mode Driver (Wave Audio) 00007 * PROGRAMMER: Andrew Greenwood 00008 * UPDATE HISTORY: 00009 * Jan 30, 2004: Imported into ReactOS tree 00010 * Jan 14, 2007: Rewritten and tidied up 00011 */ 00012 00013 #include <mmdrv.h> 00014 00015 00016 #define MAX_WAVE_BUFFER_SIZE 65536 00017 00018 00019 MMRESULT 00020 QueueWaveBuffer( 00021 SessionInfo* session_info, 00022 LPWAVEHDR wave_header) 00023 { 00024 PWAVEHDR queue_node, previous_node; 00025 DPRINT("Queueing wave buffer\n"); 00026 00027 if ( ! wave_header ) 00028 { 00029 return MMSYSERR_INVALPARAM; 00030 } 00031 00032 if ( ! wave_header->lpData ) 00033 { 00034 return MMSYSERR_INVALPARAM; 00035 } 00036 00037 /* Headers must be prepared first */ 00038 if ( ! ( wave_header->dwFlags & WHDR_PREPARED ) ) 00039 { 00040 DPRINT("I was given a header which hasn't been prepared yet!\n"); 00041 return WAVERR_UNPREPARED; 00042 } 00043 00044 /* ...and they must not already be in the playing queue! */ 00045 if ( wave_header->dwFlags & WHDR_INQUEUE ) 00046 { 00047 DPRINT("I was given a header for a buffer which is already playing\n"); 00048 return WAVERR_STILLPLAYING; 00049 } 00050 00051 /* Initialize */ 00052 wave_header->dwBytesRecorded = 0; 00053 00054 /* Clear the DONE bit, and mark the buffer as queued */ 00055 wave_header->dwFlags &= ~WHDR_DONE; 00056 wave_header->dwFlags |= WHDR_INQUEUE; 00057 00058 /* Save our handle in the header */ 00059 wave_header->reserved = (DWORD_PTR) session_info; 00060 00061 /* Locate the end of the queue */ 00062 previous_node = NULL; 00063 queue_node = session_info->wave_queue; 00064 00065 while ( queue_node ) 00066 { 00067 previous_node = queue_node; 00068 queue_node = queue_node->lpNext; 00069 } 00070 00071 /* Go back a step to obtain the previous node (non-NULL) */ 00072 queue_node = previous_node; 00073 00074 /* Append our buffer here, and terminate the queue */ 00075 queue_node->lpNext = wave_header; 00076 wave_header->lpNext = NULL; 00077 00078 /* When no buffers are playing there's no play queue so we start one */ 00079 #if 0 00080 if ( ! session_info->next_buffer ) 00081 { 00082 session_info->buffer_position = 0; 00083 session_info->next_buffer = wave_header; 00084 } 00085 #endif 00086 00087 /* Pass to the driver - happens automatically during playback */ 00088 // return PerformWaveIO(session_info); 00089 return MMSYSERR_NOERROR; 00090 } 00091 00092 VOID 00093 ReturnCompletedBuffers(SessionInfo* session_info) 00094 { 00095 PWAVEHDR header = NULL; 00096 00097 /* Set the current header and test to ensure it's not NULL */ 00098 while ( ( header = session_info->wave_queue ) ) 00099 { 00100 if ( header->dwFlags & WHDR_DONE ) 00101 { 00102 DWORD message; 00103 00104 /* Mark as done, and unqueued */ 00105 header->dwFlags &= ~WHDR_INQUEUE; 00106 header->dwFlags |= WHDR_DONE; 00107 00108 /* Trim it from the start of the queue */ 00109 session_info->wave_queue = header->lpNext; 00110 00111 /* Choose appropriate notification */ 00112 message = (session_info->device_type == WaveOutDevice) ? WOM_DONE : 00113 WIM_DATA; 00114 00115 DPRINT("Notifying client that buffer 0x%p is done\n", header); 00116 00117 /* Notify the client */ 00118 NotifyClient(session_info, message, (DWORD_PTR) header, 0); 00119 } 00120 } 00121 00122 /* TODO: Perform I/O as a new buffer may have arrived */ 00123 } 00124 00125 00126 /* 00127 Each thread function/request is packed into the SessionInfo structure 00128 using a function ID and a parameter (in some cases.) When the function 00129 completes, the function code is set to an "invalid" value. This is, 00130 effectively, a hub for operations where sound driver I/O is concerned. 00131 It handles MME message codes so is a form of deferred wodMessage(). 00132 */ 00133 00134 DWORD 00135 ProcessSessionThreadRequest(SessionInfo* session_info) 00136 { 00137 MMRESULT result = MMSYSERR_NOERROR; 00138 00139 switch ( session_info->thread.function ) 00140 { 00141 case WODM_WRITE : 00142 { 00143 result = QueueWaveBuffer(session_info, 00144 (LPWAVEHDR) session_info->thread.parameter); 00145 break; 00146 } 00147 00148 case WODM_RESET : 00149 { 00150 /* TODO */ 00151 break; 00152 } 00153 00154 case WODM_PAUSE : 00155 { 00156 /* TODO */ 00157 break; 00158 } 00159 00160 case WODM_RESTART : 00161 { 00162 /* TODO */ 00163 break; 00164 } 00165 00166 case WODM_GETPOS : 00167 { 00168 /* TODO */ 00169 break; 00170 } 00171 00172 case WODM_SETPITCH : 00173 { 00174 result = SetDeviceData(session_info->kernel_device_handle, 00175 IOCTL_WAVE_SET_PITCH, 00176 (PBYTE) session_info->thread.parameter, 00177 sizeof(DWORD)); 00178 break; 00179 } 00180 00181 case WODM_GETPITCH : 00182 { 00183 result = GetDeviceData(session_info->kernel_device_handle, 00184 IOCTL_WAVE_GET_PITCH, 00185 (PBYTE) session_info->thread.parameter, 00186 sizeof(DWORD)); 00187 break; 00188 } 00189 00190 case WODM_SETVOLUME : 00191 { 00192 break; 00193 } 00194 00195 case WODM_GETVOLUME : 00196 { 00197 #if 0 00198 result = GetDeviceData(session_info->kernel_device_handle, 00199 IOCTL_WAVE_GET_VOLUME, 00200 (PBYTE) session_info->thread.parameter,); 00201 #endif 00202 break; 00203 } 00204 00205 case WODM_SETPLAYBACKRATE : 00206 { 00207 result = SetDeviceData(session_info->kernel_device_handle, 00208 IOCTL_WAVE_SET_PLAYBACK_RATE, 00209 (PBYTE) session_info->thread.parameter, 00210 sizeof(DWORD)); 00211 break; 00212 } 00213 00214 case WODM_GETPLAYBACKRATE : 00215 { 00216 result = GetDeviceData(session_info->kernel_device_handle, 00217 IOCTL_WAVE_GET_PLAYBACK_RATE, 00218 (PBYTE) session_info->thread.parameter, 00219 sizeof(DWORD)); 00220 break; 00221 } 00222 00223 case WODM_CLOSE : 00224 { 00225 DPRINT("Thread was asked if OK to close device\n"); 00226 00227 if ( session_info->wave_queue != NULL ) 00228 result = WAVERR_STILLPLAYING; 00229 else 00230 result = MMSYSERR_NOERROR; 00231 00232 break; 00233 } 00234 00235 case DRVM_TERMINATE : 00236 { 00237 DPRINT("Terminating thread...\n"); 00238 result = MMSYSERR_NOERROR; 00239 break; 00240 } 00241 00242 default : 00243 { 00244 DPRINT("INVALID FUNCTION\n"); 00245 result = MMSYSERR_ERROR; 00246 break; 00247 } 00248 } 00249 00250 /* We're done with the function now */ 00251 00252 return result; 00253 } 00254 00255 00256 /* 00257 The wave "session". This starts, sets itself as high priority, then waits 00258 for the "go" event. When this occurs, it processes the requested function, 00259 tidies up any buffers that have finished playing, sends new buffers to the 00260 sound driver, then continues handing finished buffers back to the calling 00261 application until it's asked to do something else. 00262 */ 00263 00264 DWORD 00265 WaveThread(LPVOID parameter) 00266 { 00267 MMRESULT result = MMSYSERR_ERROR; 00268 SessionInfo* session_info = (SessionInfo*) parameter; 00269 BOOL terminate = FALSE; 00270 00271 /* All your CPU time are belong to us */ 00272 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); 00273 00274 DPRINT("Wave processing thread setting ready state\n"); 00275 00276 SetEvent(session_info->thread.ready_event); 00277 00278 while ( ! terminate ) 00279 { 00280 /* Wait for GO event, or IO completion notification */ 00281 while ( WaitForSingleObjectEx(session_info->thread.go_event, 00282 INFINITE, 00283 TRUE) == WAIT_IO_COMPLETION ) 00284 { 00285 /* A buffer has been finished with - pass back to the client */ 00286 ReturnCompletedBuffers(session_info); 00287 } 00288 00289 DPRINT("Wave processing thread woken up\n"); 00290 00291 /* Set the terminate flag if that's what the caller wants */ 00292 terminate = (session_info->thread.function == DRVM_TERMINATE); 00293 00294 /* Process the request */ 00295 DPRINT("Processing thread request\n"); 00296 result = ProcessSessionThreadRequest(session_info); 00297 00298 /* Store the result code */ 00299 session_info->thread.result = result; 00300 00301 /* Submit new buffers and continue existing ones */ 00302 DPRINT("Performing wave I/O\n"); 00303 PerformWaveIO(session_info); 00304 00305 /* Now we're ready for more action */ 00306 DPRINT("Wave processing thread sleeping\n"); 00307 SetEvent(session_info->thread.ready_event); 00308 } 00309 00310 return 0; 00311 } 00312 00313 00314 /* 00315 Convenience function for calculating the size of the WAVEFORMATEX struct. 00316 */ 00317 00318 DWORD 00319 GetWaveFormatExSize(PWAVEFORMATEX format) 00320 { 00321 if ( format->wFormatTag == WAVE_FORMAT_PCM ) 00322 return sizeof(PCMWAVEFORMAT); 00323 else 00324 return sizeof(WAVEFORMATEX) + format->cbSize; 00325 } 00326 00327 00328 /* 00329 Query if the driver/device is capable of handling a format. This is called 00330 if the device is a wave device, and the QUERYFORMAT flag is set. 00331 */ 00332 00333 DWORD 00334 QueryWaveFormat( 00335 DeviceType device_type, 00336 PVOID lpFormat) 00337 { 00338 /* TODO */ 00339 return WAVERR_BADFORMAT; 00340 } 00341 00342 00343 /* 00344 Set the format to be used. 00345 */ 00346 00347 BOOL 00348 SetWaveFormat( 00349 HANDLE device_handle, 00350 PWAVEFORMATEX format) 00351 { 00352 DWORD bytes_returned; 00353 DWORD size; 00354 00355 size = GetWaveFormatExSize(format); 00356 00357 DPRINT("SetWaveFormat\n"); 00358 00359 return DeviceIoControl(device_handle, 00360 IOCTL_WAVE_SET_FORMAT, 00361 (PVOID) format, 00362 size, 00363 NULL, 00364 0, 00365 &bytes_returned, 00366 NULL); 00367 } 00368 00369 00370 DWORD 00371 WriteWaveBuffer( 00372 DWORD_PTR private_handle, 00373 PWAVEHDR wave_header, 00374 DWORD wave_header_size) 00375 { 00376 SessionInfo* session_info = (SessionInfo*) private_handle; 00377 ASSERT(session_info); 00378 00379 /* Let the processing thread know that it has work to do */ 00380 return CallSessionThread(session_info, WODM_WRITE, wave_header); 00381 } Generated on Sat May 26 2012 04:23:16 for ReactOS by
1.7.6.1
|