- Quick & dirty implementation of WIDM_RESET / WODM_RESET
[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
174 WaveHdr = (PWAVEHDR) SoundOverlapped->Header;
175 SND_ASSERT( WaveHdr );
176
177 SND_ASSERT( ERROR_SUCCESS == dwErrorCode );
178
179 HdrExtension = (PWAVEHDR_EXTENSION) WaveHdr->reserved;
180 SND_ASSERT( HdrExtension );
181
182 SoundDeviceInstance = SoundOverlapped->SoundDeviceInstance;
183
184 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
185 SND_ASSERT( MMSUCCESS(Result) );
186
187 Result = GetSoundDeviceType(SoundDevice, &DeviceType);
188 SND_ASSERT( MMSUCCESS(Result) );
189
190 HdrExtension->BytesCompleted += dwNumberOfBytesTransferred;
191 SND_TRACE(L"%d/%d bytes of wavehdr completed\n", HdrExtension->BytesCompleted, WaveHdr->dwBufferLength);
192
193 /* We have an available buffer now */
194 -- SoundDeviceInstance->OutstandingBuffers;
195
196 /* Did we finish a WAVEHDR and aren't looping? */
197 if ( HdrExtension->BytesCompleted == WaveHdr->dwBufferLength &&
198 SoundOverlapped->PerformCompletion )
199 {
200 CompleteWaveHeader(SoundDeviceInstance, WaveHdr);
201 }
202
203 DoWaveStreaming(SoundDeviceInstance);
204
205 //CompleteWavePortion(SoundDeviceInstance, dwNumberOfBytesTransferred);
206
207 FreeMemory(lpOverlapped);
208 }
209
210 MMRESULT
211 WriteFileEx_Committer(
212 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
213 IN PVOID OffsetPtr,
214 IN DWORD Length,
215 IN PSOUND_OVERLAPPED Overlap,
216 IN LPOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine)
217 {
218 HANDLE Handle;
219
220 VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
221 VALIDATE_MMSYS_PARAMETER( OffsetPtr );
222 VALIDATE_MMSYS_PARAMETER( Overlap );
223 VALIDATE_MMSYS_PARAMETER( CompletionRoutine );
224
225 GetSoundDeviceInstanceHandle(SoundDeviceInstance, &Handle);
226
227 if ( ! WriteFileEx(Handle, OffsetPtr, Length, (LPOVERLAPPED)Overlap, CompletionRoutine) )
228 {
229 // TODO
230 }
231
232 return MMSYSERR_NOERROR;
233 }
234
235
236 /*
237 Stream control functions
238 (External/internal thread pairs)
239
240 TODO - Move elsewhere as these shouldn't be wave specific!
241 */
242
243 MMRESULT
244 StopStreamingInSoundThread(
245 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
246 IN PVOID Parameter)
247 {
248 /* complete all pending headers */
249 while(SoundDeviceInstance->HeadWaveHeader)
250 CompleteWaveHeader(SoundDeviceInstance, SoundDeviceInstance->HeadWaveHeader);
251
252 SND_ASSERT( NULL == SoundDeviceInstance->HeadWaveHeader );
253 SND_ASSERT( NULL == SoundDeviceInstance->TailWaveHeader );
254 return MMSYSERR_NOERROR;
255 }
256
257 MMRESULT
258 StopStreaming(
259 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
260 {
261 MMRESULT Result;
262 PSOUND_DEVICE SoundDevice;
263 MMDEVICE_TYPE DeviceType;
264
265 if ( ! IsValidSoundDeviceInstance(SoundDeviceInstance) )
266 return MMSYSERR_INVALHANDLE;
267
268 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
269 if ( ! MMSUCCESS(Result) )
270 return TranslateInternalMmResult(Result);
271
272 Result = GetSoundDeviceType(SoundDevice, &DeviceType);
273 if ( ! MMSUCCESS(Result) )
274 return TranslateInternalMmResult(Result);
275
276 if ( DeviceType != WAVE_OUT_DEVICE_TYPE && DeviceType != WAVE_IN_DEVICE_TYPE )
277 return MMSYSERR_NOTSUPPORTED;
278
279 return CallSoundThread(SoundDeviceInstance,
280 StopStreamingInSoundThread,
281 NULL);
282 }