[MMEBUDDY]
[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 PWAVEHDR WaveHeader = (PWAVEHDR) Parameter;
228
229 VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
230 VALIDATE_MMSYS_PARAMETER( 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
252 /* FIXME - Make sure that the buffer has not already been added to the list */
253 if ( SoundDeviceInstance->TailWaveHeader != WaveHeader )
254 {
255 SND_ASSERT(SoundDeviceInstance->TailWaveHeader != WaveHeader);
256
257 SoundDeviceInstance->TailWaveHeader->lpNext = WaveHeader;
258 SoundDeviceInstance->TailWaveHeader = WaveHeader;
259 DUMP_WAVEHDR_QUEUE(SoundDeviceInstance);
260
261 DoWaveStreaming(SoundDeviceInstance);
262 }
263 }
264
265 DUMP_WAVEHDR_QUEUE(SoundDeviceInstance);
266
267 return MMSYSERR_NOERROR;
268 }
269
270 VOID
271 CompleteWaveHeader(
272 IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
273 IN PWAVEHDR Header)
274 {
275 PWAVEHDR PrevHdr = NULL, CurrHdr = NULL;
276 PWAVEHDR_EXTENSION Extension;
277 PSOUND_DEVICE SoundDevice;
278 MMDEVICE_TYPE DeviceType;
279 MMRESULT Result;
280
281 SND_TRACE(L"BUFFER COMPLETE :)\n");
282
283 // TODO: Set header flags?
284 // TODO: Call client
285 // TODO: Streaming
286
287 //DoWaveStreaming(SoundDeviceInstance);
288
289 Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
290 SND_ASSERT( MMSUCCESS(Result) );
291 Result = GetSoundDeviceType(SoundDevice, &DeviceType);
292 SND_ASSERT( MMSUCCESS(Result) );
293
294 Extension = (PWAVEHDR_EXTENSION)Header->reserved;
295 SND_ASSERT( Extension );
296
297 /* Remove the header from the queue, like so */
298 if ( SoundDeviceInstance->HeadWaveHeader == Header )
299 {
300 SoundDeviceInstance->HeadWaveHeader = Header->lpNext;
301
302 SND_TRACE(L"Dropping head node\n");
303
304 /* If nothing after the head, then there is no tail */
305 if ( Header->lpNext == NULL )
306 {
307 SND_TRACE(L"Dropping tail node\n");
308 SoundDeviceInstance->TailWaveHeader = NULL;
309 }
310 }
311 else
312 {
313 PrevHdr = NULL;
314 CurrHdr = SoundDeviceInstance->HeadWaveHeader;
315
316 SND_TRACE(L"Relinking nodes\n");
317
318 while ( CurrHdr != Header )
319 {
320 PrevHdr = CurrHdr;
321 CurrHdr = CurrHdr->lpNext;
322 SND_ASSERT( CurrHdr );
323 }
324
325 SND_ASSERT( PrevHdr );
326
327 PrevHdr->lpNext = CurrHdr->lpNext;
328
329 /* If this is the tail node, update the tail */
330 if ( Header->lpNext == NULL )
331 {
332 SND_TRACE(L"Updating tail node\n");
333 SoundDeviceInstance->TailWaveHeader = PrevHdr;
334 }
335 }
336
337 /* Make sure we're not using this as the current buffer any more, either! */
338 /*
339 if ( SoundDeviceInstance->CurrentWaveHeader == Header )
340 {
341 SoundDeviceInstance->CurrentWaveHeader = Header->lpNext;
342 }
343 */
344
345 DUMP_WAVEHDR_QUEUE(SoundDeviceInstance);
346
347 SND_TRACE(L"Returning buffer to client...\n");
348
349 /* Update the header */
350 Header->dwFlags &= ~WHDR_INQUEUE;
351 Header->dwFlags |= WHDR_DONE;
352
353 if ( DeviceType == WAVE_IN_DEVICE_TYPE )
354 {
355 // FIXME: We won't be called on incomplete buffer!
356 Header->dwBytesRecorded = Extension->BytesCompleted;
357 }
358
359 /* Safe to do this without thread protection, as we're done with the header */
360 NotifyMmeClient(SoundDeviceInstance,
361 DeviceType == WAVE_OUT_DEVICE_TYPE ? WOM_DONE : WIM_DATA,
362 (DWORD_PTR)Header);
363 }