ReactOS  0.4.13-dev-100-gc8611ae
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 /*
15  DoWaveStreaming
16  Check if there is streaming to be done, and if so, do it.
17 */
18 
19 VOID
21  IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
22 {
25  PSOUND_DEVICE SoundDevice;
28  PWAVEHDR_EXTENSION HeaderExtension;
29 
30  Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
32 
33  Result = GetSoundDeviceType(SoundDevice, &DeviceType);
35 
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 
158  IN DWORD dwErrorCode,
159  IN DWORD dwNumberOfBytesTransferred,
161 {
163  PSOUND_DEVICE SoundDevice;
164  PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
166  PWAVEHDR WaveHdr;
167  PWAVEHDR_EXTENSION HdrExtension;
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);
181 
182  Result = GetSoundDeviceType(SoundDevice, &DeviceType);
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 
235 }
236 
237 MMRESULT
239  IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
240  IN PVOID OffsetPtr,
241  IN DWORD Length,
242  IN PSOUND_OVERLAPPED Overlap,
244 {
245  HANDLE Handle;
246 
247  VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
248  VALIDATE_MMSYS_PARAMETER( OffsetPtr );
249  VALIDATE_MMSYS_PARAMETER( Overlap );
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
272  IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
274 {
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);
286 
287  /* Obtain the function table */
290 
291  /* Obtain device instance type */
292  Result = GetSoundDeviceType(SoundDevice, &DeviceType);
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
335  IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
336 {
338  PSOUND_DEVICE SoundDevice;
340 
341  if ( ! IsValidSoundDeviceInstance(SoundDeviceInstance) )
342  return MMSYSERR_INVALHANDLE;
343 
344  Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
345  if ( ! MMSUCCESS(Result) )
347 
348  Result = GetSoundDeviceType(SoundDevice, &DeviceType);
349  if ( ! MMSUCCESS(Result) )
351 
353  return MMSYSERR_NOTSUPPORTED;
354 
355  return CallSoundThread(SoundDeviceInstance,
357  NULL);
358 }
359 
360 MMRESULT
362  IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
364 {
365  DoWaveStreaming(SoundDeviceInstance);
366 
367  return MMSYSERR_NOERROR;
368 }
369 
370 DWORD
371 WINAPI
374 {
377  NULL);
378 
379  ExitThread(0);
380 }
381 
382 VOID
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)
392 }
struct wavehdr_tag * PWAVEHDR
#define IN
Definition: typedefs.h:38
static VOID FreeMemory(PCREATE_DATA Data)
Definition: create.c:134
BOOL PerformCompletion
Definition: mmebuddy.h:123
#define OK(condition, fail_message,...)
#define TRUE
Definition: types.h:120
#define CloseHandle
Definition: compat.h:398
MMRESULT CallSoundThread(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, IN SOUND_THREAD_REQUEST_HANDLER RequestHandler, IN PVOID Parameter OPTIONAL)
Definition: thread.c:71
#define MMSUCCESS(result)
Definition: mmebuddy.h:80
DeviceType
Definition: mmdrv.h:41
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:238
#define CALLBACK
Definition: compat.h:27
DWORD BytesCommitted
Definition: mmebuddy.h:312
_In_ UINT Bytes
Definition: mmcopy.h:9
MMRESULT TranslateInternalMmResult(IN MMRESULT Result)
Definition: utility.c:132
#define ZeroMemory
Definition: winbase.h:1635
UINT MMRESULT
Definition: mmsystem.h:962
#define MMSYSERR_INVALHANDLE
Definition: mmsystem.h:101
_In_ PVOID Parameter
Definition: ldrtypes.h:240
struct _SOUND_DEVICE_INSTANCE * SoundDeviceInstance
Definition: mmebuddy.h:121
#define VALIDATE_MMSYS_PARAMETER(parameter_condition)
Definition: mmebuddy.h:71
UCHAR MMDEVICE_TYPE
Definition: mmebuddy.h:88
MMRESULT GetSoundDeviceFunctionTable(IN PSOUND_DEVICE SoundDevice, OUT PMMFUNCTION_TABLE *FunctionTable)
Definition: functiontable.c:47
VOID InitiateSoundStreaming(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
Definition: streaming.c:383
Definition: Header.h:8
unsigned int BOOL
Definition: ntddk_ex.h:94
DWORD WINAPI WaveActivateSoundStreaming(IN PVOID lpParameter)
Definition: streaming.c:372
MMRESULT StopStreaming(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
Definition: streaming.c:334
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:112
smooth NULL
Definition: ftsmooth.c:416
#define SND_ASSERT(condition)
_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:426
DWORD BytesCompleted
Definition: mmebuddy.h:313
VOID WINAPI ExitThread(IN DWORD uExitCode)
Definition: thread.c:327
BOOLEAN IsValidSoundDeviceInstance(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
struct _SOUND_OVERLAPPED * PSOUND_OVERLAPPED
_In_ HANDLE Handle
Definition: extypes.h:390
DWORD WINAPI SleepEx(IN DWORD dwMilliseconds, IN BOOL bAlertable)
Definition: synch.c:748
#define MMSYSERR_NOERROR
Definition: mmsystem.h:96
#define AllocateStruct(thing)
Definition: mmebuddy.h:27
#define WINAPI
Definition: msvc.h:8
unsigned long DWORD
Definition: ntddk_ex.h:95
struct _WAVEHDR_EXTENSION * PWAVEHDR_EXTENSION
_In_ ULONG _In_ ULONG _In_ ULONG Length
Definition: ntddpcm.h:101
#define MMSYSERR_NOTSUPPORTED
Definition: mmsystem.h:104
static const WCHAR L[]
Definition: oid.c:1250
LPVOID lpParameter
Definition: kernel32.h:234
DWORD dwBufferLength
Definition: mmsystem.h:1015
#define SND_WARN(...)
void(CALLBACK * LPOVERLAPPED_COMPLETION_ROUTINE)(DWORD, DWORD, LPOVERLAPPED)
Definition: winbase.h:1404
struct wavehdr_tag * lpNext
Definition: mmsystem.h:1020
_In_ HANDLE _In_ DWORD _In_ DWORD _Inout_opt_ LPOVERLAPPED lpOverlapped
Definition: mswsock.h:90
MMRESULT GetSoundDeviceType(IN PSOUND_DEVICE SoundDevice, OUT PMMDEVICE_TYPE DeviceType)
Definition: devicelist.c:346
#define WHDR_INQUEUE
Definition: mmsystem.h:197
HANDLE hThread
Definition: wizard.c:27
#define WHDR_PREPARED
Definition: mmsystem.h:194
MMRESULT StopStreamingInSoundThread(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, IN PVOID Parameter)
Definition: streaming.c:271
VOID CompleteWaveHeader(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, IN PWAVEHDR Header)
Definition: header.c:274
_In_ PIRP _In_opt_ PVOID _In_opt_ POPLOCK_WAIT_COMPLETE_ROUTINE CompletionRoutine
Definition: fsrtlfuncs.h:673
PWAVEHDR Header
Definition: mmebuddy.h:122
DWORD_PTR reserved
Definition: mmsystem.h:1021
static WLX_DISPATCH_VERSION_1_4 FunctionTable
Definition: wlx.c:736
MMRESULT GetSoundDeviceFromInstance(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, OUT PSOUND_DEVICE *SoundDevice)
MMRESULT GetSoundDeviceInstanceHandle(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, OUT PVOID *Handle)
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
MMRESULT PerformWaveStreaming(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance, IN PVOID Parameter)
Definition: streaming.c:361
VOID CALLBACK CompleteIO(IN DWORD dwErrorCode, IN DWORD dwNumberOfBytesTransferred, IN LPOVERLAPPED lpOverlapped)
Definition: streaming.c:157
#define SND_TRACE(...)
VOID DoWaveStreaming(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
Definition: streaming.c:20