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/header.c
5 *
6 * PURPOSE: Wave header preparation and submission routines
7 *
8 * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
9 */
10
11 #include "precomp.h"
12
13
14 /*
15 This structure gets used locally within functions as a way to shuttle data
16 to the sound thread. It's safe to use locally since CallSoundThread will
17 not return until the operation has been carried out.
18 */
19
20 typedef struct
21 {
22 MMWAVEHEADER_FUNC Function;
23 PWAVEHDR Header;
24 } THREADED_WAVEHEADER_PARAMETERS;
25
26
27 /*
28 Helper routines to simplify the call to the sound thread for the header
29 functions.
30 */
31
32 MMRESULT
WaveHeaderOperationInSoundThread(PSOUND_DEVICE_INSTANCE SoundDeviceInstance,IN PVOID Parameter)33 WaveHeaderOperationInSoundThread(
34 PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
35 IN PVOID Parameter)
36 {
37 THREADED_WAVEHEADER_PARAMETERS* Parameters = (THREADED_WAVEHEADER_PARAMETERS*) Parameter;
38 return Parameters->Function(SoundDeviceInstance, Parameters->Header);
39 }
40
41 MMRESULT
WaveHeaderOperation(MMWAVEHEADER_FUNC Function,PSOUND_DEVICE_INSTANCE SoundDeviceInstance,PWAVEHDR Header)42 WaveHeaderOperation(
43 MMWAVEHEADER_FUNC Function,
44 PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
45 PWAVEHDR Header)
46 {
47 THREADED_WAVEHEADER_PARAMETERS Parameters;
48
49 Parameters.Function = Function;
50 Parameters.Header = Header;
51
52 return CallSoundThread(SoundDeviceInstance,
53 WaveHeaderOperationInSoundThread,
54 &Parameters);
55 }
56
57
58 /*
59 SanitizeWaveHeader
60 Clean up a header / reinitialize
61 */
62
63 VOID
SanitizeWaveHeader(PWAVEHDR Header)64 SanitizeWaveHeader(
65 PWAVEHDR Header)
66 {
67 PWAVEHDR_EXTENSION Extension = (PWAVEHDR_EXTENSION) Header->reserved;
68 SND_ASSERT( Extension );
69
70 Header->dwBytesRecorded = 0;
71
72 Extension->BytesCommitted = 0;
73 Extension->BytesCompleted = 0;
74 }
75
76
77 /*
78 The following routines are basically handlers for:
79 - WODM_PREPARE
80 - WODM_UNPREPARE
81 - WODM_WRITE
82
83 All of these calls are ultimately dealt with in the context of the
84 appropriate sound thread, so the implementation should expect itself to
85 be running in this other thread when any of these operations take place.
86 */
87
88 MMRESULT
PrepareWaveHeader(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,IN PWAVEHDR Header)89 PrepareWaveHeader(
90 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
91 IN PWAVEHDR Header)
92 {
93 MMRESULT Result;
94 PSOUND_DEVICE SoundDevice;
95 PMMFUNCTION_TABLE FunctionTable;
96 PWAVEHDR_EXTENSION Extension;
97
98 VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
99 VALIDATE_MMSYS_PARAMETER( Header );
100
101 SND_TRACE(L"Preparing wave header\n");
102
103 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
104 if ( ! MMSUCCESS(Result) )
105 return TranslateInternalMmResult(Result);
106
107 Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
108 if ( ! MMSUCCESS(Result) )
109 return TranslateInternalMmResult(Result);
110
111 Extension = AllocateStruct(WAVEHDR_EXTENSION);
112 if ( ! Extension )
113 return MMSYSERR_NOMEM;
114
115 Header->reserved = (DWORD_PTR) Extension;
116 Extension->BytesCommitted = 0;
117 Extension->BytesCompleted = 0;
118
119 /* Configure the flags */
120 Header->dwFlags |= WHDR_PREPARED;
121
122 return MMSYSERR_NOERROR;
123 }
124
125 MMRESULT
UnprepareWaveHeader(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,IN PWAVEHDR Header)126 UnprepareWaveHeader(
127 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
128 IN PWAVEHDR Header)
129 {
130 MMRESULT Result;
131 PSOUND_DEVICE SoundDevice;
132 PMMFUNCTION_TABLE FunctionTable;
133 PWAVEHDR_EXTENSION Extension;
134
135 VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
136 VALIDATE_MMSYS_PARAMETER( Header );
137
138 SND_TRACE(L"Un-preparing wave header\n");
139
140 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
141 if ( ! MMSUCCESS(Result) )
142 return TranslateInternalMmResult(Result);
143
144 Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
145 if ( ! MMSUCCESS(Result) )
146 return TranslateInternalMmResult(Result);
147
148 SND_ASSERT( Header->reserved );
149 Extension = (PWAVEHDR_EXTENSION) Header->reserved;
150 FreeMemory(Extension);
151
152 /* Configure the flags */
153 Header->dwFlags &= ~WHDR_PREPARED;
154
155 return MMSYSERR_NOERROR;
156 }
157
158 MMRESULT
WriteWaveHeader(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,IN PWAVEHDR Header)159 WriteWaveHeader(
160 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
161 IN PWAVEHDR Header)
162 {
163 MMRESULT Result;
164 PSOUND_DEVICE SoundDevice;
165 PMMFUNCTION_TABLE FunctionTable;
166
167 VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
168 VALIDATE_MMSYS_PARAMETER( Header );
169
170 SND_TRACE(L"Submitting wave header\n");
171
172 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
173 if ( ! MMSUCCESS(Result) )
174 return TranslateInternalMmResult(Result);
175
176 Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
177 if ( ! MMSUCCESS(Result) )
178 return TranslateInternalMmResult(Result);
179
180 if ( ! FunctionTable->CommitWaveBuffer )
181 return MMSYSERR_NOTSUPPORTED;
182
183 /*
184 A few minor sanity checks - any custom checks should've been carried
185 out during wave header preparation etc.
186 */
187 VALIDATE_MMSYS_PARAMETER( Header->lpData != NULL );
188 VALIDATE_MMSYS_PARAMETER( Header->dwBufferLength > 0 );
189 VALIDATE_MMSYS_PARAMETER( Header->dwFlags & WHDR_PREPARED );
190 VALIDATE_MMSYS_PARAMETER( ! (Header->dwFlags & WHDR_INQUEUE) );
191
192 SanitizeWaveHeader(Header);
193
194 /* Clear the "done" flag for the buffer */
195 Header->dwFlags &= ~WHDR_DONE;
196
197 Result = CallSoundThread(SoundDeviceInstance,
198 EnqueueWaveHeader,
199 Header);
200
201 return Result;
202 }
203
204
205 /*
206 EnqueueWaveHeader
207 Put the header in the record/playback queue. This is performed within
208 the context of the sound thread, it must NEVER be called from another
209 thread.
210
211 CompleteWaveHeader
212 Set the header information to indicate that it has finished playing,
213 and return it to the client application. This again must be called
214 within the context of the sound thread.
215 */
216
217 MMRESULT
EnqueueWaveHeader(PSOUND_DEVICE_INSTANCE SoundDeviceInstance,IN PVOID Parameter)218 EnqueueWaveHeader(
219 PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
220 IN PVOID Parameter)
221 {
222 PWAVEHDR WaveHeader = (PWAVEHDR) Parameter;
223
224 VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
225 VALIDATE_MMSYS_PARAMETER( Parameter );
226
227 /* Initialise */
228 WaveHeader->lpNext = NULL;
229
230 /* Set the "in queue" flag */
231 WaveHeader->dwFlags |= WHDR_INQUEUE;
232
233 if ( ! SoundDeviceInstance->HeadWaveHeader )
234 {
235 /* This is the first header in the queue */
236 SND_TRACE(L"Enqueued first wave header\n");
237 SoundDeviceInstance->HeadWaveHeader = WaveHeader;
238 SoundDeviceInstance->TailWaveHeader = WaveHeader;
239
240 /* Only do wave streaming when the stream has not been paused */
241 if (SoundDeviceInstance->bPaused == FALSE)
242 {
243 DoWaveStreaming(SoundDeviceInstance);
244 }
245 }
246 else
247 {
248 /* There are already queued headers - make this one the tail */
249 SND_TRACE(L"Enqueued next wave header\n");
250
251 /* FIXME - Make sure that the buffer has not already been added to the list */
252 if ( SoundDeviceInstance->TailWaveHeader != WaveHeader )
253 {
254 SND_ASSERT(SoundDeviceInstance->TailWaveHeader != WaveHeader);
255
256 SoundDeviceInstance->TailWaveHeader->lpNext = WaveHeader;
257 SoundDeviceInstance->TailWaveHeader = WaveHeader;
258 DUMP_WAVEHDR_QUEUE(SoundDeviceInstance);
259
260 /* Only do wave streaming when the stream has not been paused */
261 if ( SoundDeviceInstance->bPaused == FALSE )
262 {
263 DoWaveStreaming(SoundDeviceInstance);
264 }
265 }
266 }
267
268 DUMP_WAVEHDR_QUEUE(SoundDeviceInstance);
269
270 return MMSYSERR_NOERROR;
271 }
272
273 VOID
CompleteWaveHeader(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,IN PWAVEHDR Header)274 CompleteWaveHeader(
275 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
276 IN PWAVEHDR Header)
277 {
278 PWAVEHDR PrevHdr = NULL, CurrHdr = NULL;
279 PWAVEHDR_EXTENSION Extension;
280 PSOUND_DEVICE SoundDevice;
281 MMDEVICE_TYPE DeviceType;
282 MMRESULT Result;
283
284 SND_TRACE(L"BUFFER COMPLETE :)\n");
285
286 // TODO: Set header flags?
287 // TODO: Call client
288 // TODO: Streaming
289
290 //DoWaveStreaming(SoundDeviceInstance);
291
292 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
293 SND_ASSERT( MMSUCCESS(Result) );
294 Result = GetSoundDeviceType(SoundDevice, &DeviceType);
295 SND_ASSERT( MMSUCCESS(Result) );
296
297 Extension = (PWAVEHDR_EXTENSION)Header->reserved;
298 SND_ASSERT( Extension );
299
300 /* Remove the header from the queue, like so */
301 if ( SoundDeviceInstance->HeadWaveHeader == Header )
302 {
303 SoundDeviceInstance->HeadWaveHeader = Header->lpNext;
304
305 SND_TRACE(L"Dropping head node\n");
306
307 /* If nothing after the head, then there is no tail */
308 if ( Header->lpNext == NULL )
309 {
310 SND_TRACE(L"Dropping tail node\n");
311 SoundDeviceInstance->TailWaveHeader = NULL;
312 }
313 }
314 else
315 {
316 PrevHdr = NULL;
317 CurrHdr = SoundDeviceInstance->HeadWaveHeader;
318
319 SND_TRACE(L"Relinking nodes\n");
320
321 while ( CurrHdr != Header )
322 {
323 PrevHdr = CurrHdr;
324 CurrHdr = CurrHdr->lpNext;
325 SND_ASSERT( CurrHdr );
326 }
327
328 SND_ASSERT( PrevHdr );
329
330 PrevHdr->lpNext = CurrHdr->lpNext;
331
332 /* If this is the tail node, update the tail */
333 if ( Header->lpNext == NULL )
334 {
335 SND_TRACE(L"Updating tail node\n");
336 SoundDeviceInstance->TailWaveHeader = PrevHdr;
337 }
338 }
339
340 /* Make sure we're not using this as the current buffer any more, either! */
341 /*
342 if ( SoundDeviceInstance->CurrentWaveHeader == Header )
343 {
344 SoundDeviceInstance->CurrentWaveHeader = Header->lpNext;
345 }
346 */
347
348 DUMP_WAVEHDR_QUEUE(SoundDeviceInstance);
349
350 SND_TRACE(L"Returning buffer to client...\n");
351
352 /* Update the header */
353 Header->dwFlags &= ~WHDR_INQUEUE;
354 Header->dwFlags |= WHDR_DONE;
355
356 if ( DeviceType == WAVE_IN_DEVICE_TYPE )
357 {
358 // FIXME: We won't be called on incomplete buffer!
359 Header->dwBytesRecorded = Extension->BytesCompleted;
360 }
361
362 /* Safe to do this without thread protection, as we're done with the header */
363 NotifyMmeClient(SoundDeviceInstance,
364 DeviceType == WAVE_OUT_DEVICE_TYPE ? WOM_DONE : WIM_DATA,
365 (DWORD_PTR)Header);
366 }
367