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/streaming.c 5 * 6 * PURPOSE: Wave streaming 7 * 8 * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org) 9 */ 10 11 #include "precomp.h" 12 13 /* 14 DoWaveStreaming 15 Check if there is streaming to be done, and if so, do it. 16 */ 17 18 VOID 19 DoWaveStreaming( 20 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance) 21 { 22 MMRESULT Result; 23 MMDEVICE_TYPE DeviceType; 24 PSOUND_DEVICE SoundDevice; 25 PMMFUNCTION_TABLE FunctionTable; 26 PWAVEHDR Header; 27 PWAVEHDR_EXTENSION HeaderExtension; 28 29 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice); 30 SND_ASSERT( MMSUCCESS(Result) ); 31 32 Result = GetSoundDeviceType(SoundDevice, &DeviceType); 33 SND_ASSERT( MMSUCCESS(Result) ); 34 35 Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable); 36 SND_ASSERT( MMSUCCESS(Result) ); 37 SND_ASSERT( FunctionTable ); 38 SND_ASSERT( FunctionTable->CommitWaveBuffer ); 39 40 /* No point in doing anything if no resources available to use */ 41 if ( SoundDeviceInstance->OutstandingBuffers >= SoundDeviceInstance->BufferCount ) 42 { 43 SND_TRACE(L"DoWaveStreaming: No available buffers to stream with - doing nothing\n"); 44 return; 45 } 46 47 /* Is there any work to do? */ 48 Header = SoundDeviceInstance->HeadWaveHeader; 49 50 if ( ! Header ) 51 { 52 SND_TRACE(L"DoWaveStreaming: No work to do - doing nothing\n"); 53 return; 54 } 55 56 /* Do we need to loop a header? */ 57 if (DeviceType == WAVE_OUT_DEVICE_TYPE && (Header->dwFlags & WHDR_BEGINLOOP)) 58 { 59 if ((Header->dwFlags & WHDR_ENDLOOP)) 60 { 61 /* Get loop count */ 62 SoundDeviceInstance->LoopsRemaining = Header->dwLoops; 63 } 64 else 65 { 66 /* Report and help notice such a case */ 67 SND_WARN(L"Looping multiple headers is UNIMPLEMENTED. Will play once only\n"); 68 SND_ASSERT((Header->dwFlags & (WHDR_BEGINLOOP | WHDR_ENDLOOP)) == (WHDR_BEGINLOOP | WHDR_ENDLOOP)); 69 } 70 } 71 72 while ( ( SoundDeviceInstance->OutstandingBuffers < SoundDeviceInstance->BufferCount ) && 73 ( Header ) && SoundDeviceInstance->ResetInProgress == FALSE) 74 { 75 HeaderExtension = (PWAVEHDR_EXTENSION) Header->reserved; 76 SND_ASSERT( HeaderExtension ); 77 78 /* Saniy checks */ 79 SND_ASSERT(Header->dwFlags & WHDR_PREPARED); 80 SND_ASSERT(Header->dwFlags & WHDR_INQUEUE); 81 82 /* Can never be *above* the length */ 83 SND_ASSERT( HeaderExtension->BytesCommitted <= Header->dwBufferLength ); 84 85 /* Is this header entirely committed? */ 86 if ( HeaderExtension->BytesCommitted == Header->dwBufferLength ) 87 { 88 { 89 /* Move on to the next header */ 90 SND_ASSERT(Header != Header->lpNext); 91 Header = Header->lpNext; 92 } 93 } 94 else 95 { 96 PSOUND_OVERLAPPED Overlap; 97 LPVOID OffsetPtr; 98 DWORD BytesRemaining, BytesToCommit; 99 BOOL OK; 100 101 /* Where within the header buffer to stream from */ 102 OffsetPtr = Header->lpData + HeaderExtension->BytesCommitted; 103 104 /* How much of this header has not been committed */ 105 BytesRemaining = Header->dwBufferLength - HeaderExtension->BytesCommitted; 106 107 /* We can commit anything up to the buffer size limit */ 108 BytesToCommit = BytesRemaining > SoundDeviceInstance->FrameSize ? 109 SoundDeviceInstance->FrameSize : 110 BytesRemaining; 111 112 /* Should always have something to commit by this point */ 113 SND_ASSERT( BytesToCommit > 0 ); 114 115 /* We need a new overlapped info structure for each buffer */ 116 Overlap = AllocateStruct(SOUND_OVERLAPPED); 117 118 if ( Overlap ) 119 { 120 ZeroMemory(Overlap, sizeof(SOUND_OVERLAPPED)); 121 Overlap->SoundDeviceInstance = SoundDeviceInstance; 122 Overlap->Header = Header; 123 124 /* Adjust the commit-related counters */ 125 HeaderExtension->BytesCommitted += BytesToCommit; 126 ++ SoundDeviceInstance->OutstandingBuffers; 127 128 OK = MMSUCCESS(FunctionTable->CommitWaveBuffer(SoundDeviceInstance, 129 OffsetPtr, 130 BytesToCommit, 131 Overlap, 132 CompleteIO)); 133 134 if ( ! OK ) 135 { 136 /* Clean-up and try again on the next iteration (is this OK?) */ 137 SND_WARN(L"FAILED\n"); 138 139 FreeMemory(Overlap); 140 HeaderExtension->BytesCommitted -= BytesToCommit; 141 -- SoundDeviceInstance->OutstandingBuffers; 142 } 143 } 144 } 145 } 146 } 147 148 149 /* 150 CompleteIO 151 An APC called as a result of a call to CommitWaveHeaderToKernelDevice. 152 This will count up the number of bytes which have been dealt with, 153 and when the entire wave header has been dealt with, will call 154 CompleteWaveHeader to have the wave header returned to the client. 155 156 CommitWaveHeaderToKernelDevice 157 Sends portions of the buffer described by the wave header to a kernel 158 device. This must only be called from within the context of the sound 159 thread. The caller supplies either their own commit routine, or uses 160 WriteFileEx_Committer. The committer is called with portions of the 161 buffer specified in the wave header. 162 163 WriteFileEx_Committer 164 Commit buffers using the WriteFileEx API. 165 */ 166 167 VOID CALLBACK 168 CompleteIO( 169 IN DWORD dwErrorCode, 170 IN DWORD dwNumberOfBytesTransferred, 171 IN LPOVERLAPPED lpOverlapped) 172 { 173 MMDEVICE_TYPE DeviceType; 174 PSOUND_DEVICE SoundDevice; 175 PSOUND_DEVICE_INSTANCE SoundDeviceInstance; 176 PSOUND_OVERLAPPED SoundOverlapped = (PSOUND_OVERLAPPED) lpOverlapped; 177 PWAVEHDR WaveHdr; 178 PWAVEHDR_EXTENSION HdrExtension; 179 MMRESULT Result; 180 DWORD Bytes; 181 182 WaveHdr = (PWAVEHDR) SoundOverlapped->Header; 183 SND_ASSERT( WaveHdr ); 184 185 HdrExtension = (PWAVEHDR_EXTENSION) WaveHdr->reserved; 186 SND_ASSERT( HdrExtension ); 187 188 SoundDeviceInstance = SoundOverlapped->SoundDeviceInstance; 189 190 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice); 191 SND_ASSERT( MMSUCCESS(Result) ); 192 193 Result = GetSoundDeviceType(SoundDevice, &DeviceType); 194 SND_ASSERT( MMSUCCESS(Result) ); 195 196 do 197 { 198 199 /* We have an available buffer now */ 200 -- SoundDeviceInstance->OutstandingBuffers; 201 202 /* Did we finish a WAVEHDR and aren't looping? */ 203 if (HdrExtension->BytesCompleted + dwNumberOfBytesTransferred >= WaveHdr->dwBufferLength && 204 SoundDeviceInstance->LoopsRemaining == 0) 205 { 206 /* Wave buffer fully completed */ 207 Bytes = WaveHdr->dwBufferLength - HdrExtension->BytesCompleted; 208 209 HdrExtension->BytesCompleted += Bytes; 210 dwNumberOfBytesTransferred -= Bytes; 211 212 CompleteWaveHeader(SoundDeviceInstance, WaveHdr); 213 SND_TRACE(L"%d/%d bytes of wavehdr completed\n", HdrExtension->BytesCompleted, WaveHdr->dwBufferLength); 214 } 215 else 216 { 217 /* Do we loop a header? */ 218 if (HdrExtension->BytesCommitted == WaveHdr->dwBufferLength && 219 SoundDeviceInstance->LoopsRemaining != 0) 220 { 221 /* Reset amount of bytes and decrement loop count, to play next iteration */ 222 HdrExtension->BytesCommitted = 0; 223 224 if (SoundDeviceInstance->LoopsRemaining != INFINITE) 225 --SoundDeviceInstance->LoopsRemaining; 226 SND_TRACE(L"Looping the header, remaining loops %u\n", SoundDeviceInstance->LoopsRemaining); 227 } 228 else 229 { 230 /* Partially completed */ 231 HdrExtension->BytesCompleted += dwNumberOfBytesTransferred; 232 SND_TRACE(L"%u/%u bytes of wavehdr completed\n", HdrExtension->BytesCompleted, WaveHdr->dwBufferLength); 233 } 234 235 break; 236 } 237 238 /* Move to next wave header */ 239 WaveHdr = WaveHdr->lpNext; 240 241 if (!WaveHdr) 242 { 243 /* No following WaveHdr */ 244 SND_ASSERT(dwNumberOfBytesTransferred == 0); 245 break; 246 } 247 248 HdrExtension = (PWAVEHDR_EXTENSION) WaveHdr->reserved; 249 SND_ASSERT( HdrExtension ); 250 251 252 }while(dwNumberOfBytesTransferred); 253 254 // AUDIO-BRANCH DIFF 255 // completion callback is performed in a thread 256 DoWaveStreaming(SoundDeviceInstance); 257 258 //CompleteWavePortion(SoundDeviceInstance, dwNumberOfBytesTransferred); 259 260 FreeMemory(lpOverlapped); 261 } 262 263 MMRESULT 264 WriteFileEx_Committer( 265 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, 266 IN PVOID OffsetPtr, 267 IN DWORD Length, 268 IN PSOUND_OVERLAPPED Overlap, 269 IN LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine) 270 { 271 HANDLE Handle; 272 273 VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance ); 274 VALIDATE_MMSYS_PARAMETER( OffsetPtr ); 275 VALIDATE_MMSYS_PARAMETER( Overlap ); 276 VALIDATE_MMSYS_PARAMETER( CompletionRoutine ); 277 278 GetSoundDeviceInstanceHandle(SoundDeviceInstance, &Handle); 279 280 if ( ! WriteFileEx(Handle, OffsetPtr, Length, (LPOVERLAPPED)Overlap, CompletionRoutine) ) 281 { 282 // TODO 283 } 284 285 return MMSYSERR_NOERROR; 286 } 287 288 289 /* 290 Stream control functions 291 (External/internal thread pairs) 292 293 TODO - Move elsewhere as these shouldn't be wave specific! 294 */ 295 296 MMRESULT 297 StopStreamingInSoundThread( 298 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, 299 IN PVOID Parameter) 300 { 301 MMDEVICE_TYPE DeviceType; 302 PMMFUNCTION_TABLE FunctionTable; 303 MMRESULT Result; 304 PSOUND_DEVICE SoundDevice; 305 306 /* set state reset in progress */ 307 SoundDeviceInstance->ResetInProgress = TRUE; 308 309 /* Get sound device */ 310 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice); 311 SND_ASSERT( Result == MMSYSERR_NOERROR ); 312 313 /* Obtain the function table */ 314 Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable); 315 SND_ASSERT( Result == MMSYSERR_NOERROR ); 316 317 /* Obtain device instance type */ 318 Result = GetSoundDeviceType(SoundDevice, &DeviceType); 319 SND_ASSERT( Result == MMSYSERR_NOERROR ); 320 321 /* Check if reset function is supported */ 322 if (FunctionTable->ResetStream) 323 { 324 /* cancel all current audio buffers */ 325 FunctionTable->ResetStream(SoundDeviceInstance, DeviceType, TRUE); 326 } 327 while(SoundDeviceInstance->OutstandingBuffers) 328 { 329 SND_TRACE(L"StopStreamingInSoundThread OutStandingBufferCount %lu\n", SoundDeviceInstance->OutstandingBuffers); 330 /* wait until pending i/o has completed */ 331 SleepEx(10, TRUE); 332 } 333 334 /* complete all current headers */ 335 while( SoundDeviceInstance->HeadWaveHeader ) 336 { 337 SND_TRACE(L"StopStreamingInSoundThread: Completing Header %p\n", SoundDeviceInstance->HeadWaveHeader); 338 CompleteWaveHeader( SoundDeviceInstance, SoundDeviceInstance->HeadWaveHeader ); 339 } 340 341 /* there should be no oustanding buffers now */ 342 SND_ASSERT(SoundDeviceInstance->OutstandingBuffers == 0); 343 344 345 /* Check if reset function is supported */ 346 if (FunctionTable->ResetStream) 347 { 348 /* finish the reset */ 349 FunctionTable->ResetStream(SoundDeviceInstance, DeviceType, FALSE); 350 } 351 352 /* clear state reset in progress */ 353 SoundDeviceInstance->ResetInProgress = FALSE; 354 355 356 return MMSYSERR_NOERROR; 357 } 358 359 MMRESULT 360 StopStreaming( 361 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance) 362 { 363 MMRESULT Result; 364 PSOUND_DEVICE SoundDevice; 365 MMDEVICE_TYPE DeviceType; 366 367 if ( ! IsValidSoundDeviceInstance(SoundDeviceInstance) ) 368 return MMSYSERR_INVALHANDLE; 369 370 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice); 371 if ( ! MMSUCCESS(Result) ) 372 return TranslateInternalMmResult(Result); 373 374 Result = GetSoundDeviceType(SoundDevice, &DeviceType); 375 if ( ! MMSUCCESS(Result) ) 376 return TranslateInternalMmResult(Result); 377 378 if ( DeviceType != WAVE_OUT_DEVICE_TYPE && DeviceType != WAVE_IN_DEVICE_TYPE ) 379 return MMSYSERR_NOTSUPPORTED; 380 381 return CallSoundThread(SoundDeviceInstance, 382 StopStreamingInSoundThread, 383 NULL); 384 } 385 386 MMRESULT 387 PerformWaveStreaming( 388 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, 389 IN PVOID Parameter) 390 { 391 DoWaveStreaming(SoundDeviceInstance); 392 393 return MMSYSERR_NOERROR; 394 } 395 396 DWORD 397 WINAPI 398 WaveActivateSoundStreaming( 399 IN PVOID lpParameter) 400 { 401 CallSoundThread((PSOUND_DEVICE_INSTANCE)lpParameter, 402 PerformWaveStreaming, 403 NULL); 404 405 ExitThread(0); 406 } 407 408 VOID 409 InitiateSoundStreaming( 410 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance) 411 { 412 HANDLE hThread; 413 414 hThread = CreateThread(NULL, 0, WaveActivateSoundStreaming, (PVOID)SoundDeviceInstance, 0, NULL); 415 416 if (hThread != NULL) 417 CloseHandle(hThread); 418 } 419