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