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
6 * PURPOSE: Wave streaming
8 * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
21 Check if there is streaming to be done, and if so, do it.
26 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance
)
29 MMDEVICE_TYPE DeviceType
;
30 PSOUND_DEVICE SoundDevice
;
31 PMMFUNCTION_TABLE FunctionTable
;
33 PWAVEHDR_EXTENSION HeaderExtension
;
35 Result
= GetSoundDeviceFromInstance(SoundDeviceInstance
, &SoundDevice
);
36 SND_ASSERT( MMSUCCESS(Result
) );
38 Result
= GetSoundDeviceType(SoundDevice
, &DeviceType
);
39 SND_ASSERT( MMSUCCESS(Result
) );
41 Result
= GetSoundDeviceFunctionTable(SoundDevice
, &FunctionTable
);
42 SND_ASSERT( MMSUCCESS(Result
) );
43 SND_ASSERT( FunctionTable
);
44 SND_ASSERT( FunctionTable
->CommitWaveBuffer
);
46 /* No point in doing anything if no resources available to use */
47 if ( SoundDeviceInstance
->OutstandingBuffers
>= SoundDeviceInstance
->BufferCount
)
49 SND_TRACE(L
"DoWaveStreaming: No available buffers to stream with - doing nothing\n");
53 /* Is there any work to do? */
54 Header
= SoundDeviceInstance
->HeadWaveHeader
;
58 SND_TRACE(L
"DoWaveStreaming: No work to do - doing nothing\n");
62 while ( ( SoundDeviceInstance
->OutstandingBuffers
< SoundDeviceInstance
->BufferCount
) &&
65 HeaderExtension
= (PWAVEHDR_EXTENSION
) Header
->reserved
;
66 SND_ASSERT( HeaderExtension
);
69 SND_ASSERT(Header
->dwFlags
& WHDR_PREPARED
);
70 SND_ASSERT(Header
->dwFlags
& WHDR_INQUEUE
);
72 /* Can never be *above* the length */
73 SND_ASSERT( HeaderExtension
->BytesCommitted
<= Header
->dwBufferLength
);
75 /* Is this header entirely committed? */
76 if ( HeaderExtension
->BytesCommitted
== Header
->dwBufferLength
)
79 /* Move on to the next header */
80 SND_ASSERT(Header
!= Header
->lpNext
);
81 Header
= Header
->lpNext
;
86 PSOUND_OVERLAPPED Overlap
;
88 DWORD BytesRemaining
, BytesToCommit
;
91 /* Where within the header buffer to stream from */
92 OffsetPtr
= Header
->lpData
+ HeaderExtension
->BytesCommitted
;
94 /* How much of this header has not been committed */
95 BytesRemaining
= Header
->dwBufferLength
- HeaderExtension
->BytesCommitted
;
97 /* We can commit anything up to the buffer size limit */
98 BytesToCommit
= BytesRemaining
> SoundDeviceInstance
->FrameSize
?
99 SoundDeviceInstance
->FrameSize
:
102 /* Should always have something to commit by this point */
103 SND_ASSERT( BytesToCommit
> 0 );
105 /* We need a new overlapped info structure for each buffer */
106 Overlap
= AllocateStruct(SOUND_OVERLAPPED
);
110 ZeroMemory(Overlap
, sizeof(SOUND_OVERLAPPED
));
111 Overlap
->SoundDeviceInstance
= SoundDeviceInstance
;
112 Overlap
->Header
= Header
;
114 /* Don't complete this header if it's part of a loop */
115 Overlap
->PerformCompletion
= TRUE
;
116 // ( SoundDeviceInstance->LoopsRemaining > 0 );
118 /* Adjust the commit-related counters */
119 HeaderExtension
->BytesCommitted
+= BytesToCommit
;
120 ++ SoundDeviceInstance
->OutstandingBuffers
;
122 OK
= MMSUCCESS(FunctionTable
->CommitWaveBuffer(SoundDeviceInstance
,
130 /* Clean-up and try again on the next iteration (is this OK?) */
131 SND_WARN(L
"FAILED\n");
134 HeaderExtension
->BytesCommitted
-= BytesToCommit
;
135 -- SoundDeviceInstance
->OutstandingBuffers
;
145 An APC called as a result of a call to CommitWaveHeaderToKernelDevice.
146 This will count up the number of bytes which have been dealt with,
147 and when the entire wave header has been dealt with, will call
148 CompleteWaveHeader to have the wave header returned to the client.
150 CommitWaveHeaderToKernelDevice
151 Sends portions of the buffer described by the wave header to a kernel
152 device. This must only be called from within the context of the sound
153 thread. The caller supplies either their own commit routine, or uses
154 WriteFileEx_Committer. The committer is called with portions of the
155 buffer specified in the wave header.
157 WriteFileEx_Committer
158 Commit buffers using the WriteFileEx API.
163 IN DWORD dwErrorCode
,
164 IN DWORD dwNumberOfBytesTransferred
,
165 IN LPOVERLAPPED lpOverlapped
)
167 MMDEVICE_TYPE DeviceType
;
168 PSOUND_DEVICE SoundDevice
;
169 PSOUND_DEVICE_INSTANCE SoundDeviceInstance
;
170 PSOUND_OVERLAPPED SoundOverlapped
= (PSOUND_OVERLAPPED
) lpOverlapped
;
172 PWAVEHDR_EXTENSION HdrExtension
;
176 WaveHdr
= (PWAVEHDR
) SoundOverlapped
->Header
;
177 SND_ASSERT( WaveHdr
);
179 SND_ASSERT( ERROR_SUCCESS
== dwErrorCode
);
181 HdrExtension
= (PWAVEHDR_EXTENSION
) WaveHdr
->reserved
;
182 SND_ASSERT( HdrExtension
);
184 SoundDeviceInstance
= SoundOverlapped
->SoundDeviceInstance
;
186 Result
= GetSoundDeviceFromInstance(SoundDeviceInstance
, &SoundDevice
);
187 SND_ASSERT( MMSUCCESS(Result
) );
189 Result
= GetSoundDeviceType(SoundDevice
, &DeviceType
);
190 SND_ASSERT( MMSUCCESS(Result
) );
195 /* We have an available buffer now */
196 -- SoundDeviceInstance
->OutstandingBuffers
;
198 /* Did we finish a WAVEHDR and aren't looping? */
199 if ( HdrExtension
->BytesCompleted
+ dwNumberOfBytesTransferred
>= WaveHdr
->dwBufferLength
&&
200 SoundOverlapped
->PerformCompletion
)
202 /* Wave buffer fully completed */
203 Bytes
= WaveHdr
->dwBufferLength
- HdrExtension
->BytesCompleted
;
205 HdrExtension
->BytesCompleted
+= Bytes
;
206 dwNumberOfBytesTransferred
-= Bytes
;
208 CompleteWaveHeader(SoundDeviceInstance
, WaveHdr
);
209 SND_TRACE(L
"%d/%d bytes of wavehdr completed\n", HdrExtension
->BytesCompleted
, WaveHdr
->dwBufferLength
);
213 /* Partially completed */
214 HdrExtension
->BytesCompleted
+= dwNumberOfBytesTransferred
;
215 SND_TRACE(L
"%d/%d bytes of wavehdr completed\n", HdrExtension
->BytesCompleted
, WaveHdr
->dwBufferLength
);
219 /* Move to next wave header */
220 WaveHdr
= WaveHdr
->lpNext
;
224 /* No following WaveHdr */
225 SND_ASSERT(dwNumberOfBytesTransferred
== 0);
229 HdrExtension
= (PWAVEHDR_EXTENSION
) WaveHdr
->reserved
;
230 SND_ASSERT( HdrExtension
);
233 }while(dwNumberOfBytesTransferred
);
236 // completion callback is performed in a thread
237 DoWaveStreaming(SoundDeviceInstance
);
239 //CompleteWavePortion(SoundDeviceInstance, dwNumberOfBytesTransferred);
241 FreeMemory(lpOverlapped
);
245 WriteFileEx_Committer(
246 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance
,
249 IN PSOUND_OVERLAPPED Overlap
,
250 IN LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine
)
254 VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance
);
255 VALIDATE_MMSYS_PARAMETER( OffsetPtr
);
256 VALIDATE_MMSYS_PARAMETER( Overlap
);
257 VALIDATE_MMSYS_PARAMETER( CompletionRoutine
);
259 GetSoundDeviceInstanceHandle(SoundDeviceInstance
, &Handle
);
261 if ( ! WriteFileEx(Handle
, OffsetPtr
, Length
, (LPOVERLAPPED
)Overlap
, CompletionRoutine
) )
266 return MMSYSERR_NOERROR
;
271 Stream control functions
272 (External/internal thread pairs)
274 TODO - Move elsewhere as these shouldn't be wave specific!
278 StopStreamingInSoundThread(
279 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance
,
282 MMDEVICE_TYPE DeviceType
;
283 PMMFUNCTION_TABLE FunctionTable
;
285 PSOUND_DEVICE SoundDevice
;
287 /* set state reset in progress */
288 SoundDeviceInstance
->ResetInProgress
= TRUE
;
290 /* Get sound device */
291 Result
= GetSoundDeviceFromInstance(SoundDeviceInstance
, &SoundDevice
);
292 SND_ASSERT( Result
== MMSYSERR_NOERROR
);
294 /* Obtain the function table */
295 Result
= GetSoundDeviceFunctionTable(SoundDevice
, &FunctionTable
);
296 SND_ASSERT( Result
== MMSYSERR_NOERROR
);
298 /* Obtain device instance type */
299 Result
= GetSoundDeviceType(SoundDevice
, &DeviceType
);
300 SND_ASSERT( Result
== MMSYSERR_NOERROR
);
302 /* Check if reset function is supported */
303 if (FunctionTable
->ResetStream
)
305 /* cancel all current audio buffers */
306 FunctionTable
->ResetStream(SoundDeviceInstance
, DeviceType
, TRUE
);
309 /* complete all current headers */
310 while( SoundDeviceInstance
->HeadWaveHeader
)
312 SND_TRACE(L
"StopStreamingInSoundThread: Completing Header %p\n", SoundDeviceInstance
->HeadWaveHeader
);
313 CompleteWaveHeader( SoundDeviceInstance
, SoundDeviceInstance
->HeadWaveHeader
);
316 /* there should be no oustanding buffers now */
317 SND_ASSERT(SoundDeviceInstance
->OutstandingBuffers
== 0);
319 while(SoundDeviceInstance
->OutstandingBuffers
)
321 SND_ERR("StopStreamingInSoundThread OutStandingBufferCount %lu\n", SoundDeviceInstance
->OutstandingBuffers
);
322 /* my hack of doom */
326 /* Check if reset function is supported */
327 if (FunctionTable
->ResetStream
)
329 /* finish the reset */
330 FunctionTable
->ResetStream(SoundDeviceInstance
, DeviceType
, FALSE
);
333 /* clear state reset in progress */
334 SoundDeviceInstance
->ResetInProgress
= FALSE
;
337 return MMSYSERR_NOERROR
;
342 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance
)
345 PSOUND_DEVICE SoundDevice
;
346 MMDEVICE_TYPE DeviceType
;
348 if ( ! IsValidSoundDeviceInstance(SoundDeviceInstance
) )
349 return MMSYSERR_INVALHANDLE
;
351 Result
= GetSoundDeviceFromInstance(SoundDeviceInstance
, &SoundDevice
);
352 if ( ! MMSUCCESS(Result
) )
353 return TranslateInternalMmResult(Result
);
355 Result
= GetSoundDeviceType(SoundDevice
, &DeviceType
);
356 if ( ! MMSUCCESS(Result
) )
357 return TranslateInternalMmResult(Result
);
359 if ( DeviceType
!= WAVE_OUT_DEVICE_TYPE
&& DeviceType
!= WAVE_IN_DEVICE_TYPE
)
360 return MMSYSERR_NOTSUPPORTED
;
362 return CallSoundThread(SoundDeviceInstance
,
363 StopStreamingInSoundThread
,