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
DoWaveStreaming(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)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
CompleteIO(IN DWORD dwErrorCode,IN DWORD dwNumberOfBytesTransferred,IN LPOVERLAPPED lpOverlapped)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
WriteFileEx_Committer(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,IN PVOID OffsetPtr,IN DWORD Length,IN PSOUND_OVERLAPPED Overlap,IN LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine)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
StopStreamingInSoundThread(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,IN PVOID Parameter)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
StopStreaming(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)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
PerformWaveStreaming(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,IN PVOID Parameter)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
WaveActivateSoundStreaming(IN PVOID lpParameter)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
InitiateSoundStreaming(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)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