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
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
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
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
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
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
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
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
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