1 /* 2 * PROJECT: ReactOS Sound System "MME Buddy" Library 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: lib/drivers/sound/mmebuddy/wave/header.c 5 * 6 * PURPOSE: Wave header preparation and submission routines 7 * 8 * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org) 9 */ 10 11 #include "precomp.h" 12 13 14 /* 15 This structure gets used locally within functions as a way to shuttle data 16 to the sound thread. It's safe to use locally since CallSoundThread will 17 not return until the operation has been carried out. 18 */ 19 20 typedef struct 21 { 22 MMWAVEHEADER_FUNC Function; 23 PWAVEHDR Header; 24 } THREADED_WAVEHEADER_PARAMETERS; 25 26 27 /* 28 Helper routines to simplify the call to the sound thread for the header 29 functions. 30 */ 31 32 MMRESULT 33 WaveHeaderOperationInSoundThread( 34 PSOUND_DEVICE_INSTANCE SoundDeviceInstance, 35 IN PVOID Parameter) 36 { 37 THREADED_WAVEHEADER_PARAMETERS* Parameters = (THREADED_WAVEHEADER_PARAMETERS*) Parameter; 38 return Parameters->Function(SoundDeviceInstance, Parameters->Header); 39 } 40 41 MMRESULT 42 WaveHeaderOperation( 43 MMWAVEHEADER_FUNC Function, 44 PSOUND_DEVICE_INSTANCE SoundDeviceInstance, 45 PWAVEHDR Header) 46 { 47 THREADED_WAVEHEADER_PARAMETERS Parameters; 48 49 Parameters.Function = Function; 50 Parameters.Header = Header; 51 52 return CallSoundThread(SoundDeviceInstance, 53 WaveHeaderOperationInSoundThread, 54 &Parameters); 55 } 56 57 58 /* 59 SanitizeWaveHeader 60 Clean up a header / reinitialize 61 */ 62 63 VOID 64 SanitizeWaveHeader( 65 PWAVEHDR Header) 66 { 67 PWAVEHDR_EXTENSION Extension = (PWAVEHDR_EXTENSION) Header->reserved; 68 SND_ASSERT( Extension ); 69 70 Header->dwBytesRecorded = 0; 71 72 Extension->BytesCommitted = 0; 73 Extension->BytesCompleted = 0; 74 } 75 76 77 /* 78 The following routines are basically handlers for: 79 - WODM_PREPARE 80 - WODM_UNPREPARE 81 - WODM_WRITE 82 83 All of these calls are ultimately dealt with in the context of the 84 appropriate sound thread, so the implementation should expect itself to 85 be running in this other thread when any of these operations take place. 86 */ 87 88 MMRESULT 89 PrepareWaveHeader( 90 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, 91 IN PWAVEHDR Header) 92 { 93 MMRESULT Result; 94 PSOUND_DEVICE SoundDevice; 95 PMMFUNCTION_TABLE FunctionTable; 96 PWAVEHDR_EXTENSION Extension; 97 98 VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) ); 99 VALIDATE_MMSYS_PARAMETER( Header ); 100 101 SND_TRACE(L"Preparing wave header\n"); 102 103 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice); 104 if ( ! MMSUCCESS(Result) ) 105 return TranslateInternalMmResult(Result); 106 107 Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable); 108 if ( ! MMSUCCESS(Result) ) 109 return TranslateInternalMmResult(Result); 110 111 Extension = AllocateStruct(WAVEHDR_EXTENSION); 112 if ( ! Extension ) 113 return MMSYSERR_NOMEM; 114 115 Header->reserved = (DWORD_PTR) Extension; 116 Extension->BytesCommitted = 0; 117 Extension->BytesCompleted = 0; 118 119 /* Configure the flags */ 120 Header->dwFlags |= WHDR_PREPARED; 121 122 return MMSYSERR_NOERROR; 123 } 124 125 MMRESULT 126 UnprepareWaveHeader( 127 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, 128 IN PWAVEHDR Header) 129 { 130 MMRESULT Result; 131 PSOUND_DEVICE SoundDevice; 132 PMMFUNCTION_TABLE FunctionTable; 133 PWAVEHDR_EXTENSION Extension; 134 135 VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) ); 136 VALIDATE_MMSYS_PARAMETER( Header ); 137 138 SND_TRACE(L"Un-preparing wave header\n"); 139 140 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice); 141 if ( ! MMSUCCESS(Result) ) 142 return TranslateInternalMmResult(Result); 143 144 Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable); 145 if ( ! MMSUCCESS(Result) ) 146 return TranslateInternalMmResult(Result); 147 148 SND_ASSERT( Header->reserved ); 149 Extension = (PWAVEHDR_EXTENSION) Header->reserved; 150 FreeMemory(Extension); 151 152 /* Configure the flags */ 153 Header->dwFlags &= ~WHDR_PREPARED; 154 155 return MMSYSERR_NOERROR; 156 } 157 158 MMRESULT 159 WriteWaveHeader( 160 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, 161 IN PWAVEHDR Header) 162 { 163 MMRESULT Result; 164 PSOUND_DEVICE SoundDevice; 165 PMMFUNCTION_TABLE FunctionTable; 166 167 VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) ); 168 VALIDATE_MMSYS_PARAMETER( Header ); 169 170 SND_TRACE(L"Submitting wave header\n"); 171 172 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice); 173 if ( ! MMSUCCESS(Result) ) 174 return TranslateInternalMmResult(Result); 175 176 Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable); 177 if ( ! MMSUCCESS(Result) ) 178 return TranslateInternalMmResult(Result); 179 180 if ( ! FunctionTable->CommitWaveBuffer ) 181 return MMSYSERR_NOTSUPPORTED; 182 183 /* 184 A few minor sanity checks - any custom checks should've been carried 185 out during wave header preparation etc. 186 */ 187 VALIDATE_MMSYS_PARAMETER( Header->lpData != NULL ); 188 VALIDATE_MMSYS_PARAMETER( Header->dwBufferLength > 0 ); 189 VALIDATE_MMSYS_PARAMETER( Header->dwFlags & WHDR_PREPARED ); 190 VALIDATE_MMSYS_PARAMETER( ! (Header->dwFlags & WHDR_INQUEUE) ); 191 192 SanitizeWaveHeader(Header); 193 194 /* Clear the "done" flag for the buffer */ 195 Header->dwFlags &= ~WHDR_DONE; 196 197 Result = CallSoundThread(SoundDeviceInstance, 198 EnqueueWaveHeader, 199 Header); 200 201 return Result; 202 } 203 204 205 /* 206 EnqueueWaveHeader 207 Put the header in the record/playback queue. This is performed within 208 the context of the sound thread, it must NEVER be called from another 209 thread. 210 211 CompleteWaveHeader 212 Set the header information to indicate that it has finished playing, 213 and return it to the client application. This again must be called 214 within the context of the sound thread. 215 */ 216 217 MMRESULT 218 EnqueueWaveHeader( 219 PSOUND_DEVICE_INSTANCE SoundDeviceInstance, 220 IN PVOID Parameter) 221 { 222 PWAVEHDR WaveHeader = (PWAVEHDR) Parameter; 223 224 VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance ); 225 VALIDATE_MMSYS_PARAMETER( Parameter ); 226 227 /* Initialise */ 228 WaveHeader->lpNext = NULL; 229 230 /* Set the "in queue" flag */ 231 WaveHeader->dwFlags |= WHDR_INQUEUE; 232 233 if ( ! SoundDeviceInstance->HeadWaveHeader ) 234 { 235 /* This is the first header in the queue */ 236 SND_TRACE(L"Enqueued first wave header\n"); 237 SoundDeviceInstance->HeadWaveHeader = WaveHeader; 238 SoundDeviceInstance->TailWaveHeader = WaveHeader; 239 240 /* Only do wave streaming when the stream has not been paused */ 241 if (SoundDeviceInstance->bPaused == FALSE) 242 { 243 DoWaveStreaming(SoundDeviceInstance); 244 } 245 } 246 else 247 { 248 /* There are already queued headers - make this one the tail */ 249 SND_TRACE(L"Enqueued next wave header\n"); 250 251 /* FIXME - Make sure that the buffer has not already been added to the list */ 252 if ( SoundDeviceInstance->TailWaveHeader != WaveHeader ) 253 { 254 SND_ASSERT(SoundDeviceInstance->TailWaveHeader != WaveHeader); 255 256 SoundDeviceInstance->TailWaveHeader->lpNext = WaveHeader; 257 SoundDeviceInstance->TailWaveHeader = WaveHeader; 258 DUMP_WAVEHDR_QUEUE(SoundDeviceInstance); 259 260 /* Only do wave streaming when the stream has not been paused */ 261 if ( SoundDeviceInstance->bPaused == FALSE ) 262 { 263 DoWaveStreaming(SoundDeviceInstance); 264 } 265 } 266 } 267 268 DUMP_WAVEHDR_QUEUE(SoundDeviceInstance); 269 270 return MMSYSERR_NOERROR; 271 } 272 273 VOID 274 CompleteWaveHeader( 275 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, 276 IN PWAVEHDR Header) 277 { 278 PWAVEHDR PrevHdr = NULL, CurrHdr = NULL; 279 PWAVEHDR_EXTENSION Extension; 280 PSOUND_DEVICE SoundDevice; 281 MMDEVICE_TYPE DeviceType; 282 MMRESULT Result; 283 284 SND_TRACE(L"BUFFER COMPLETE :)\n"); 285 286 // TODO: Set header flags? 287 // TODO: Call client 288 // TODO: Streaming 289 290 //DoWaveStreaming(SoundDeviceInstance); 291 292 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice); 293 SND_ASSERT( MMSUCCESS(Result) ); 294 Result = GetSoundDeviceType(SoundDevice, &DeviceType); 295 SND_ASSERT( MMSUCCESS(Result) ); 296 297 Extension = (PWAVEHDR_EXTENSION)Header->reserved; 298 SND_ASSERT( Extension ); 299 300 /* Remove the header from the queue, like so */ 301 if ( SoundDeviceInstance->HeadWaveHeader == Header ) 302 { 303 SoundDeviceInstance->HeadWaveHeader = Header->lpNext; 304 305 SND_TRACE(L"Dropping head node\n"); 306 307 /* If nothing after the head, then there is no tail */ 308 if ( Header->lpNext == NULL ) 309 { 310 SND_TRACE(L"Dropping tail node\n"); 311 SoundDeviceInstance->TailWaveHeader = NULL; 312 } 313 } 314 else 315 { 316 PrevHdr = NULL; 317 CurrHdr = SoundDeviceInstance->HeadWaveHeader; 318 319 SND_TRACE(L"Relinking nodes\n"); 320 321 while ( CurrHdr != Header ) 322 { 323 PrevHdr = CurrHdr; 324 CurrHdr = CurrHdr->lpNext; 325 SND_ASSERT( CurrHdr ); 326 } 327 328 SND_ASSERT( PrevHdr ); 329 330 PrevHdr->lpNext = CurrHdr->lpNext; 331 332 /* If this is the tail node, update the tail */ 333 if ( Header->lpNext == NULL ) 334 { 335 SND_TRACE(L"Updating tail node\n"); 336 SoundDeviceInstance->TailWaveHeader = PrevHdr; 337 } 338 } 339 340 /* Make sure we're not using this as the current buffer any more, either! */ 341 /* 342 if ( SoundDeviceInstance->CurrentWaveHeader == Header ) 343 { 344 SoundDeviceInstance->CurrentWaveHeader = Header->lpNext; 345 } 346 */ 347 348 DUMP_WAVEHDR_QUEUE(SoundDeviceInstance); 349 350 SND_TRACE(L"Returning buffer to client...\n"); 351 352 /* Update the header */ 353 Header->dwFlags &= ~WHDR_INQUEUE; 354 Header->dwFlags |= WHDR_DONE; 355 356 if ( DeviceType == WAVE_IN_DEVICE_TYPE ) 357 { 358 // FIXME: We won't be called on incomplete buffer! 359 Header->dwBytesRecorded = Extension->BytesCompleted; 360 } 361 362 /* Safe to do this without thread protection, as we're done with the header */ 363 NotifyMmeClient(SoundDeviceInstance, 364 DeviceType == WAVE_OUT_DEVICE_TYPE ? WOM_DONE : WIM_DATA, 365 (DWORD_PTR)Header); 366 } 367