ReactOS 0.4.15-dev-8241-g63935f8
streaming.c
Go to the documentation of this file.
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
18VOID
20 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
21{
24 PSOUND_DEVICE SoundDevice;
27 PWAVEHDR_EXTENSION HeaderExtension;
28
29 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
31
32 Result = GetSoundDeviceType(SoundDevice, &DeviceType);
34
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? */
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");
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 */
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 */
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
169 IN DWORD dwErrorCode,
170 IN DWORD dwNumberOfBytesTransferred,
172{
174 PSOUND_DEVICE SoundDevice;
175 PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
177 PWAVEHDR WaveHdr;
178 PWAVEHDR_EXTENSION HdrExtension;
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);
192
193 Result = GetSoundDeviceType(SoundDevice, &DeviceType);
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
261}
262
265 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
266 IN PVOID OffsetPtr,
268 IN PSOUND_OVERLAPPED Overlap,
270{
272
273 VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
274 VALIDATE_MMSYS_PARAMETER( OffsetPtr );
275 VALIDATE_MMSYS_PARAMETER( Overlap );
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
298 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
300{
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);
312
313 /* Obtain the function table */
316
317 /* Obtain device instance type */
318 Result = GetSoundDeviceType(SoundDevice, &DeviceType);
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
361 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
362{
364 PSOUND_DEVICE SoundDevice;
366
367 if ( ! IsValidSoundDeviceInstance(SoundDeviceInstance) )
369
370 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
371 if ( ! MMSUCCESS(Result) )
373
374 Result = GetSoundDeviceType(SoundDevice, &DeviceType);
375 if ( ! MMSUCCESS(Result) )
377
380
381 return CallSoundThread(SoundDeviceInstance,
383 NULL);
384}
385
388 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
390{
391 DoWaveStreaming(SoundDeviceInstance);
392
393 return MMSYSERR_NOERROR;
394}
395
396DWORD
397WINAPI
399 IN PVOID lpParameter)
400{
403 NULL);
404
405 ExitThread(0);
406}
407
408VOID
410 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
411{
413
414 hThread = CreateThread(NULL, 0, WaveActivateSoundStreaming, (PVOID)SoundDeviceInstance, 0, NULL);
415
416 if (hThread != NULL)
418}
static VOID FreeMemory(PCREATE_DATA Data)
Definition: create.c:134
Definition: Header.h:9
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define CloseHandle
Definition: compat.h:739
#define CALLBACK
Definition: compat.h:35
BOOL WINAPI WriteFileEx(IN HANDLE hFile, IN LPCVOID lpBuffer, IN DWORD nNumberOfBytesToWrite OPTIONAL, IN LPOVERLAPPED lpOverlapped, IN LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
Definition: rw.c:262
VOID WINAPI ExitThread(IN DWORD uExitCode)
Definition: thread.c:365
HANDLE WINAPI DECLSPEC_HOTPATCH CreateThread(IN LPSECURITY_ATTRIBUTES lpThreadAttributes, IN DWORD dwStackSize, IN LPTHREAD_START_ROUTINE lpStartAddress, IN LPVOID lpParameter, IN DWORD dwCreationFlags, OUT LPDWORD lpThreadId)
Definition: thread.c:137
#define INFINITE
Definition: serial.h:102
unsigned int BOOL
Definition: ntddk_ex.h:94
unsigned long DWORD
Definition: ntddk_ex.h:95
ULONG Handle
Definition: gdb_input.c:15
_In_ UINT Bytes
Definition: mmcopy.h:9
DeviceType
Definition: mmdrv.h:42
struct _WAVEHDR_EXTENSION * PWAVEHDR_EXTENSION
struct _SOUND_OVERLAPPED * PSOUND_OVERLAPPED
MMRESULT CallSoundThread(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, IN SOUND_THREAD_REQUEST_HANDLER RequestHandler, IN PVOID Parameter OPTIONAL)
Definition: thread.c:71
#define VALIDATE_MMSYS_PARAMETER(parameter_condition)
Definition: mmebuddy.h:71
#define AllocateStruct(thing)
Definition: mmebuddy.h:27
MMRESULT GetSoundDeviceFunctionTable(IN PSOUND_DEVICE SoundDevice, OUT PMMFUNCTION_TABLE *FunctionTable)
Definition: functiontable.c:47
UCHAR MMDEVICE_TYPE
Definition: mmebuddy.h:88
MMRESULT GetSoundDeviceFromInstance(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, OUT PSOUND_DEVICE *SoundDevice)
MMRESULT TranslateInternalMmResult(IN MMRESULT Result)
Definition: utility.c:132
MMRESULT GetSoundDeviceInstanceHandle(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, OUT PVOID *Handle)
MMRESULT GetSoundDeviceType(IN PSOUND_DEVICE SoundDevice, OUT PMMDEVICE_TYPE DeviceType)
Definition: devicelist.c:346
VOID CompleteWaveHeader(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, IN PWAVEHDR Header)
Definition: header.c:274
#define MMSUCCESS(result)
Definition: mmebuddy.h:80
BOOLEAN IsValidSoundDeviceInstance(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
#define SND_TRACE(...)
#define SND_ASSERT(condition)
#define SND_WARN(...)
UINT MMRESULT
Definition: mmsystem.h:962
struct wavehdr_tag * PWAVEHDR
#define MMSYSERR_NOTSUPPORTED
Definition: mmsystem.h:104
#define WHDR_ENDLOOP
Definition: mmsystem.h:196
#define WHDR_INQUEUE
Definition: mmsystem.h:197
#define WHDR_BEGINLOOP
Definition: mmsystem.h:195
#define MMSYSERR_INVALHANDLE
Definition: mmsystem.h:101
#define MMSYSERR_NOERROR
Definition: mmsystem.h:96
#define WHDR_PREPARED
Definition: mmsystem.h:194
_In_ HANDLE _In_ DWORD _In_ DWORD _Inout_opt_ LPOVERLAPPED lpOverlapped
Definition: mswsock.h:93
HANDLE hThread
Definition: wizard.c:28
_In_ ULONG _In_ ULONG _In_ ULONG Length
Definition: ntddpcm.h:102
#define L(x)
Definition: ntvdm.h:50
@ WAVE_IN_DEVICE_TYPE
Definition: sndtypes.h:28
@ WAVE_OUT_DEVICE_TYPE
Definition: sndtypes.h:29
MMRESULT WriteFileEx_Committer(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, IN PVOID OffsetPtr, IN DWORD Length, IN PSOUND_OVERLAPPED Overlap, IN LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine)
Definition: streaming.c:264
VOID DoWaveStreaming(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
Definition: streaming.c:19
VOID InitiateSoundStreaming(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
Definition: streaming.c:409
MMRESULT StopStreaming(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
Definition: streaming.c:360
MMRESULT StopStreamingInSoundThread(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, IN PVOID Parameter)
Definition: streaming.c:297
DWORD WINAPI WaveActivateSoundStreaming(IN PVOID lpParameter)
Definition: streaming.c:398
VOID CALLBACK CompleteIO(IN DWORD dwErrorCode, IN DWORD dwNumberOfBytesTransferred, IN LPOVERLAPPED lpOverlapped)
Definition: streaming.c:168
MMRESULT PerformWaveStreaming(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, IN PVOID Parameter)
Definition: streaming.c:387
PWAVEHDR Header
Definition: mmebuddy.h:122
struct _SOUND_DEVICE_INSTANCE * SoundDeviceInstance
Definition: mmebuddy.h:121
DWORD BytesCommitted
Definition: mmebuddy.h:324
DWORD BytesCompleted
Definition: mmebuddy.h:325
DWORD dwBufferLength
Definition: mmsystem.h:1015
DWORD_PTR reserved
Definition: mmsystem.h:1021
struct wavehdr_tag * lpNext
Definition: mmsystem.h:1020
DWORD WINAPI SleepEx(IN DWORD dwMilliseconds, IN BOOL bAlertable)
Definition: synch.c:802
#define IN
Definition: typedefs.h:39
#define OK(condition, fail_message,...)
_In_ WDFREQUEST _In_opt_ PFN_WDF_REQUEST_COMPLETION_ROUTINE CompletionRoutine
Definition: wdfrequest.h:895
#define ZeroMemory
Definition: winbase.h:1712
void(CALLBACK * LPOVERLAPPED_COMPLETION_ROUTINE)(DWORD, DWORD, LPOVERLAPPED)
Definition: winbase.h:1451
#define WINAPI
Definition: msvc.h:6
static WLX_DISPATCH_VERSION_1_4 FunctionTable
Definition: wlx.c:722
_At_(*)(_In_ PWSK_CLIENT Client, _In_opt_ PUNICODE_STRING NodeName, _In_opt_ PUNICODE_STRING ServiceName, _In_opt_ ULONG NameSpace, _In_opt_ GUID *Provider, _In_opt_ PADDRINFOEXW Hints, _Outptr_ PADDRINFOEXW *Result, _In_opt_ PEPROCESS OwningProcess, _In_opt_ PETHREAD OwningThread, _Inout_ PIRP Irp Result)(Mem)) NTSTATUS(WSKAPI *PFN_WSK_GET_ADDRESS_INFO
Definition: wsk.h:409
_Inout_opt_ PVOID Parameter
Definition: rtltypes.h:336