[WDMAUD.DRV]
[reactos.git] / reactos / lib / drivers / sound / mmebuddy / wave / streaming.c
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 <windows.h>
12 #include <mmsystem.h>
13 #include <mmddk.h>
14 #include <ntddsnd.h>
15 #include <mmebuddy.h>
16 #include <sndtypes.h>
17
18
19 /*
20 DoWaveStreaming
21 Check if there is streaming to be done, and if so, do it.
22 */
23
24 VOID
25 DoWaveStreaming(
26 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
27 {
28 MMRESULT Result;
29 MMDEVICE_TYPE DeviceType;
30 PSOUND_DEVICE SoundDevice;
31 PMMFUNCTION_TABLE FunctionTable;
32 PWAVEHDR Header;
33 PWAVEHDR_EXTENSION HeaderExtension;
34
35 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
36 SND_ASSERT( MMSUCCESS(Result) );
37
38 Result = GetSoundDeviceType(SoundDevice, &DeviceType);
39 SND_ASSERT( MMSUCCESS(Result) );
40
41 Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
42 SND_ASSERT( MMSUCCESS(Result) );
43 SND_ASSERT( FunctionTable );
44 SND_ASSERT( FunctionTable->CommitWaveBuffer );
45
46 /* No point in doing anything if no resources available to use */
47 if ( SoundDeviceInstance->OutstandingBuffers >= SoundDeviceInstance->BufferCount )
48 {
49 SND_TRACE(L"DoWaveStreaming: No available buffers to stream with - doing nothing\n");
50 return;
51 }
52
53 /* Is there any work to do? */
54 Header = SoundDeviceInstance->HeadWaveHeader;
55
56 if ( ! Header )
57 {
58 SND_TRACE(L"DoWaveStreaming: No work to do - doing nothing\n");
59 return;
60 }
61
62 while ( ( SoundDeviceInstance->OutstandingBuffers < SoundDeviceInstance->BufferCount ) &&
63 ( Header ) )
64 {
65 HeaderExtension = (PWAVEHDR_EXTENSION) Header->reserved;
66 SND_ASSERT( HeaderExtension );
67
68 /* Saniy checks */
69 SND_ASSERT(Header->dwFlags & WHDR_PREPARED);
70 SND_ASSERT(Header->dwFlags & WHDR_INQUEUE);
71
72 /* Can never be *above* the length */
73 SND_ASSERT( HeaderExtension->BytesCommitted <= Header->dwBufferLength );
74
75 /* Is this header entirely committed? */
76 if ( HeaderExtension->BytesCommitted == Header->dwBufferLength )
77 {
78 {
79 /* Move on to the next header */
80 Header = Header->lpNext;
81 }
82 }
83 else
84 {
85 PSOUND_OVERLAPPED Overlap;
86 LPVOID OffsetPtr;
87 DWORD BytesRemaining, BytesToCommit;
88 BOOL OK;
89
90 /* Where within the header buffer to stream from */
91 OffsetPtr = Header->lpData + HeaderExtension->BytesCommitted;
92
93 /* How much of this header has not been committed */
94 BytesRemaining = Header->dwBufferLength - HeaderExtension->BytesCommitted;
95
96 /* We can commit anything up to the buffer size limit */
97 BytesToCommit = BytesRemaining > SoundDeviceInstance->FrameSize ?
98 SoundDeviceInstance->FrameSize :
99 BytesRemaining;
100
101 /* Should always have something to commit by this point */
102 SND_ASSERT( BytesToCommit > 0 );
103
104 /* We need a new overlapped info structure for each buffer */
105 Overlap = AllocateStruct(SOUND_OVERLAPPED);
106
107 if ( Overlap )
108 {
109 ZeroMemory(Overlap, sizeof(SOUND_OVERLAPPED));
110 Overlap->SoundDeviceInstance = SoundDeviceInstance;
111 Overlap->Header = Header;
112
113 /* Don't complete this header if it's part of a loop */
114 Overlap->PerformCompletion = TRUE;
115 // ( SoundDeviceInstance->LoopsRemaining > 0 );
116
117 /* Adjust the commit-related counters */
118 HeaderExtension->BytesCommitted += BytesToCommit;
119 ++ SoundDeviceInstance->OutstandingBuffers;
120
121 OK = MMSUCCESS(FunctionTable->CommitWaveBuffer(SoundDeviceInstance,
122 OffsetPtr,
123 BytesToCommit,
124 Overlap,
125 CompleteIO));
126
127 if ( ! OK )
128 {
129 /* Clean-up and try again on the next iteration (is this OK?) */
130 SND_WARN(L"FAILED\n");
131
132 FreeMemory(Overlap);
133 HeaderExtension->BytesCommitted -= BytesToCommit;
134 -- SoundDeviceInstance->OutstandingBuffers;
135 }
136 }
137 }
138 }
139 }
140
141
142 /*
143 CompleteIO
144 An APC called as a result of a call to CommitWaveHeaderToKernelDevice.
145 This will count up the number of bytes which have been dealt with,
146 and when the entire wave header has been dealt with, will call
147 CompleteWaveHeader to have the wave header returned to the client.
148
149 CommitWaveHeaderToKernelDevice
150 Sends portions of the buffer described by the wave header to a kernel
151 device. This must only be called from within the context of the sound
152 thread. The caller supplies either their own commit routine, or uses
153 WriteFileEx_Committer. The committer is called with portions of the
154 buffer specified in the wave header.
155
156 WriteFileEx_Committer
157 Commit buffers using the WriteFileEx API.
158 */
159
160 VOID CALLBACK
161 CompleteIO(
162 IN DWORD dwErrorCode,
163 IN DWORD dwNumberOfBytesTransferred,
164 IN LPOVERLAPPED lpOverlapped)
165 {
166 MMDEVICE_TYPE DeviceType;
167 PSOUND_DEVICE SoundDevice;
168 PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
169 PSOUND_OVERLAPPED SoundOverlapped = (PSOUND_OVERLAPPED) lpOverlapped;
170 PWAVEHDR WaveHdr;
171 PWAVEHDR_EXTENSION HdrExtension;
172 MMRESULT Result;
173 DWORD Bytes;
174
175 WaveHdr = (PWAVEHDR) SoundOverlapped->Header;
176 SND_ASSERT( WaveHdr );
177
178 SND_ASSERT( ERROR_SUCCESS == dwErrorCode );
179
180 HdrExtension = (PWAVEHDR_EXTENSION) WaveHdr->reserved;
181 SND_ASSERT( HdrExtension );
182
183 SoundDeviceInstance = SoundOverlapped->SoundDeviceInstance;
184
185 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
186 SND_ASSERT( MMSUCCESS(Result) );
187
188 Result = GetSoundDeviceType(SoundDevice, &DeviceType);
189 SND_ASSERT( MMSUCCESS(Result) );
190
191 do
192 {
193
194 /* We have an available buffer now */
195 -- SoundDeviceInstance->OutstandingBuffers;
196
197 /* Did we finish a WAVEHDR and aren't looping? */
198 if ( HdrExtension->BytesCompleted + dwNumberOfBytesTransferred >= WaveHdr->dwBufferLength &&
199 SoundOverlapped->PerformCompletion )
200 {
201 /* Wave buffer fully completed */
202 Bytes = WaveHdr->dwBufferLength - HdrExtension->BytesCompleted;
203
204 HdrExtension->BytesCompleted += Bytes;
205 dwNumberOfBytesTransferred -= Bytes;
206
207 CompleteWaveHeader(SoundDeviceInstance, WaveHdr);
208 SND_TRACE(L"%d/%d bytes of wavehdr completed\n", HdrExtension->BytesCompleted, WaveHdr->dwBufferLength);
209 }
210 else
211 {
212 SND_TRACE(L"%d/%d bytes of wavehdr completed\n", HdrExtension->BytesCompleted, WaveHdr->dwBufferLength);
213 /* Partially completed */
214 HdrExtension->BytesCompleted += dwNumberOfBytesTransferred;
215 break;
216 }
217
218 /* Move to next wave header */
219 WaveHdr = WaveHdr->lpNext;
220
221 if (!WaveHdr)
222 {
223 /* No following WaveHdr */
224 SND_ASSERT(dwNumberOfBytesTransferred == 0);
225 break;
226 }
227
228 HdrExtension = (PWAVEHDR_EXTENSION) WaveHdr->reserved;
229 SND_ASSERT( HdrExtension );
230
231
232 }while(dwNumberOfBytesTransferred);
233
234 DoWaveStreaming(SoundDeviceInstance);
235
236 //CompleteWavePortion(SoundDeviceInstance, dwNumberOfBytesTransferred);
237
238 FreeMemory(lpOverlapped);
239 }
240
241 MMRESULT
242 WriteFileEx_Committer(
243 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
244 IN PVOID OffsetPtr,
245 IN DWORD Length,
246 IN PSOUND_OVERLAPPED Overlap,
247 IN LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine)
248 {
249 HANDLE Handle;
250
251 VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
252 VALIDATE_MMSYS_PARAMETER( OffsetPtr );
253 VALIDATE_MMSYS_PARAMETER( Overlap );
254 VALIDATE_MMSYS_PARAMETER( CompletionRoutine );
255
256 GetSoundDeviceInstanceHandle(SoundDeviceInstance, &Handle);
257
258 if ( ! WriteFileEx(Handle, OffsetPtr, Length, (LPOVERLAPPED)Overlap, CompletionRoutine) )
259 {
260 // TODO
261 }
262
263 return MMSYSERR_NOERROR;
264 }
265
266
267 /*
268 Stream control functions
269 (External/internal thread pairs)
270
271 TODO - Move elsewhere as these shouldn't be wave specific!
272 */
273
274 MMRESULT
275 StopStreamingInSoundThread(
276 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
277 IN PVOID Parameter)
278 {
279 /* TODO */
280 return MMSYSERR_NOTSUPPORTED;
281 }
282
283 MMRESULT
284 StopStreaming(
285 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
286 {
287 MMRESULT Result;
288 PSOUND_DEVICE SoundDevice;
289 MMDEVICE_TYPE DeviceType;
290
291 if ( ! IsValidSoundDeviceInstance(SoundDeviceInstance) )
292 return MMSYSERR_INVALHANDLE;
293
294 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
295 if ( ! MMSUCCESS(Result) )
296 return TranslateInternalMmResult(Result);
297
298 Result = GetSoundDeviceType(SoundDevice, &DeviceType);
299 if ( ! MMSUCCESS(Result) )
300 return TranslateInternalMmResult(Result);
301
302 if ( DeviceType != WAVE_OUT_DEVICE_TYPE && DeviceType != WAVE_IN_DEVICE_TYPE )
303 return MMSYSERR_NOTSUPPORTED;
304
305 return CallSoundThread(SoundDeviceInstance,
306 StopStreamingInSoundThread,
307 NULL);
308 }