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