sync with trunk r46493
[reactos.git] / 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 SND_ASSERT(Header != Header->lpNext);
81 Header = Header->lpNext;
82 }
83 }
84 else
85 {
86 PSOUND_OVERLAPPED Overlap;
87 LPVOID OffsetPtr;
88 DWORD BytesRemaining, BytesToCommit;
89 BOOL OK;
90
91 /* Where within the header buffer to stream from */
92 OffsetPtr = Header->lpData + HeaderExtension->BytesCommitted;
93
94 /* How much of this header has not been committed */
95 BytesRemaining = Header->dwBufferLength - HeaderExtension->BytesCommitted;
96
97 /* We can commit anything up to the buffer size limit */
98 BytesToCommit = BytesRemaining > SoundDeviceInstance->FrameSize ?
99 SoundDeviceInstance->FrameSize :
100 BytesRemaining;
101
102 /* Should always have something to commit by this point */
103 SND_ASSERT( BytesToCommit > 0 );
104
105 /* We need a new overlapped info structure for each buffer */
106 Overlap = AllocateStruct(SOUND_OVERLAPPED);
107
108 if ( Overlap )
109 {
110 ZeroMemory(Overlap, sizeof(SOUND_OVERLAPPED));
111 Overlap->SoundDeviceInstance = SoundDeviceInstance;
112 Overlap->Header = Header;
113
114 /* Don't complete this header if it's part of a loop */
115 Overlap->PerformCompletion = TRUE;
116 // ( SoundDeviceInstance->LoopsRemaining > 0 );
117
118 /* Adjust the commit-related counters */
119 HeaderExtension->BytesCommitted += BytesToCommit;
120 ++ SoundDeviceInstance->OutstandingBuffers;
121
122 OK = MMSUCCESS(FunctionTable->CommitWaveBuffer(SoundDeviceInstance,
123 OffsetPtr,
124 BytesToCommit,
125 Overlap,
126 CompleteIO));
127
128 if ( ! OK )
129 {
130 /* Clean-up and try again on the next iteration (is this OK?) */
131 SND_WARN(L"FAILED\n");
132
133 FreeMemory(Overlap);
134 HeaderExtension->BytesCommitted -= BytesToCommit;
135 -- SoundDeviceInstance->OutstandingBuffers;
136 }
137 }
138 }
139 }
140 }
141
142
143 /*
144 CompleteIO
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.
149
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.
156
157 WriteFileEx_Committer
158 Commit buffers using the WriteFileEx API.
159 */
160
161 VOID CALLBACK
162 CompleteIO(
163 IN DWORD dwErrorCode,
164 IN DWORD dwNumberOfBytesTransferred,
165 IN LPOVERLAPPED lpOverlapped)
166 {
167 MMDEVICE_TYPE DeviceType;
168 PSOUND_DEVICE SoundDevice;
169 PSOUND_DEVICE_INSTANCE SoundDeviceInstance;
170 PSOUND_OVERLAPPED SoundOverlapped = (PSOUND_OVERLAPPED) lpOverlapped;
171 PWAVEHDR WaveHdr;
172 PWAVEHDR_EXTENSION HdrExtension;
173 MMRESULT Result;
174 DWORD Bytes;
175
176 WaveHdr = (PWAVEHDR) SoundOverlapped->Header;
177 SND_ASSERT( WaveHdr );
178
179 SND_ASSERT( ERROR_SUCCESS == dwErrorCode );
180
181 HdrExtension = (PWAVEHDR_EXTENSION) WaveHdr->reserved;
182 SND_ASSERT( HdrExtension );
183
184 SoundDeviceInstance = SoundOverlapped->SoundDeviceInstance;
185
186 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
187 SND_ASSERT( MMSUCCESS(Result) );
188
189 Result = GetSoundDeviceType(SoundDevice, &DeviceType);
190 SND_ASSERT( MMSUCCESS(Result) );
191
192 do
193 {
194
195 /* We have an available buffer now */
196 -- SoundDeviceInstance->OutstandingBuffers;
197
198 /* Did we finish a WAVEHDR and aren't looping? */
199 if ( HdrExtension->BytesCompleted + dwNumberOfBytesTransferred >= WaveHdr->dwBufferLength &&
200 SoundOverlapped->PerformCompletion )
201 {
202 /* Wave buffer fully completed */
203 Bytes = WaveHdr->dwBufferLength - HdrExtension->BytesCompleted;
204
205 HdrExtension->BytesCompleted += Bytes;
206 dwNumberOfBytesTransferred -= Bytes;
207
208 CompleteWaveHeader(SoundDeviceInstance, WaveHdr);
209 SND_TRACE(L"%d/%d bytes of wavehdr completed\n", HdrExtension->BytesCompleted, WaveHdr->dwBufferLength);
210 }
211 else
212 {
213 /* Partially completed */
214 HdrExtension->BytesCompleted += dwNumberOfBytesTransferred;
215 SND_TRACE(L"%d/%d bytes of wavehdr completed\n", HdrExtension->BytesCompleted, WaveHdr->dwBufferLength);
216 break;
217 }
218
219 /* Move to next wave header */
220 WaveHdr = WaveHdr->lpNext;
221
222 if (!WaveHdr)
223 {
224 /* No following WaveHdr */
225 SND_ASSERT(dwNumberOfBytesTransferred == 0);
226 break;
227 }
228
229 HdrExtension = (PWAVEHDR_EXTENSION) WaveHdr->reserved;
230 SND_ASSERT( HdrExtension );
231
232
233 }while(dwNumberOfBytesTransferred);
234
235 DoWaveStreaming(SoundDeviceInstance);
236
237 //CompleteWavePortion(SoundDeviceInstance, dwNumberOfBytesTransferred);
238
239 FreeMemory(lpOverlapped);
240 }
241
242 MMRESULT
243 WriteFileEx_Committer(
244 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
245 IN PVOID OffsetPtr,
246 IN DWORD Length,
247 IN PSOUND_OVERLAPPED Overlap,
248 IN LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine)
249 {
250 HANDLE Handle;
251
252 VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
253 VALIDATE_MMSYS_PARAMETER( OffsetPtr );
254 VALIDATE_MMSYS_PARAMETER( Overlap );
255 VALIDATE_MMSYS_PARAMETER( CompletionRoutine );
256
257 GetSoundDeviceInstanceHandle(SoundDeviceInstance, &Handle);
258
259 if ( ! WriteFileEx(Handle, OffsetPtr, Length, (LPOVERLAPPED)Overlap, CompletionRoutine) )
260 {
261 // TODO
262 }
263
264 return MMSYSERR_NOERROR;
265 }
266
267
268 /*
269 Stream control functions
270 (External/internal thread pairs)
271
272 TODO - Move elsewhere as these shouldn't be wave specific!
273 */
274
275 MMRESULT
276 StopStreamingInSoundThread(
277 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
278 IN PVOID Parameter)
279 {
280 /* TODO */
281 return MMSYSERR_NOTSUPPORTED;
282 }
283
284 MMRESULT
285 StopStreaming(
286 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
287 {
288 MMRESULT Result;
289 PSOUND_DEVICE SoundDevice;
290 MMDEVICE_TYPE DeviceType;
291
292 if ( ! IsValidSoundDeviceInstance(SoundDeviceInstance) )
293 return MMSYSERR_INVALHANDLE;
294
295 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
296 if ( ! MMSUCCESS(Result) )
297 return TranslateInternalMmResult(Result);
298
299 Result = GetSoundDeviceType(SoundDevice, &DeviceType);
300 if ( ! MMSUCCESS(Result) )
301 return TranslateInternalMmResult(Result);
302
303 if ( DeviceType != WAVE_OUT_DEVICE_TYPE && DeviceType != WAVE_IN_DEVICE_TYPE )
304 return MMSYSERR_NOTSUPPORTED;
305
306 return CallSoundThread(SoundDeviceInstance,
307 StopStreamingInSoundThread,
308 NULL);
309 }