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/deviceinstance.c
5  *
6  * PURPOSE:     Manages instances of sound devices.
7  *
8  * PROGRAMMERS: Andrew Greenwood (silverblade@reactos.org)
9 */
10 
11 #include "precomp.h"
12 
13 /*
14     Restrain ourselves from flooding the kernel device!
15 */
16 
17 #define SOUND_KERNEL_BUFFER_COUNT       10
18 #define SOUND_KERNEL_BUFFER_SIZE        16384
19 
20 MMRESULT
AllocateSoundDeviceInstance(OUT PSOUND_DEVICE_INSTANCE * SoundDeviceInstance)21 AllocateSoundDeviceInstance(
22     OUT PSOUND_DEVICE_INSTANCE* SoundDeviceInstance)
23 {
24     PSOUND_DEVICE_INSTANCE NewInstance;
25 
26     VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
27 
28     /* Allocate memory for the new instance */
29     NewInstance = AllocateStruct(SOUND_DEVICE_INSTANCE);
30 
31     if ( ! NewInstance )
32         return MMSYSERR_NOMEM;
33 
34     /* Use default frame size */
35     NewInstance->FrameSize = SOUND_KERNEL_BUFFER_SIZE;
36     /* Use default buffer count */
37     NewInstance->BufferCount = SOUND_KERNEL_BUFFER_COUNT;
38 
39     /* Provide the caller with the new instance pointer */
40     *SoundDeviceInstance = NewInstance;
41 
42     return MMSYSERR_NOERROR;
43 }
44 
45 VOID
FreeSoundDeviceInstance(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)46 FreeSoundDeviceInstance(
47     IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
48 {
49     /*
50         Device is marked as invalid by now, but we can still do some sanity
51         checking.
52     */
53     SND_ASSERT( SoundDeviceInstance->Thread == NULL );
54 
55     ZeroMemory(SoundDeviceInstance, sizeof(SOUND_DEVICE_INSTANCE));
56     FreeMemory(SoundDeviceInstance);
57 }
58 
59 BOOLEAN
IsValidSoundDeviceInstance(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)60 IsValidSoundDeviceInstance(
61     IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
62 {
63     PSOUND_DEVICE SoundDevice;
64     PSOUND_DEVICE_INSTANCE CurrentInstance;
65 
66     if ( ! SoundDeviceInstance )
67         return FALSE;
68 
69     /* GetSoundDeviceFromInstance would send us into a recursive loop... */
70     SoundDevice = SoundDeviceInstance->Device;
71     SND_ASSERT(SoundDevice);
72 
73     CurrentInstance = SoundDevice->HeadInstance;
74 
75     while ( CurrentInstance )
76     {
77         if ( CurrentInstance == SoundDeviceInstance )
78             return TRUE;
79 
80         CurrentInstance = CurrentInstance->Next;
81     }
82 
83     return FALSE;
84 }
85 
86 MMRESULT
ListSoundDeviceInstance(IN PSOUND_DEVICE SoundDevice,IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)87 ListSoundDeviceInstance(
88     IN  PSOUND_DEVICE SoundDevice,
89     IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
90 {
91     VALIDATE_MMSYS_PARAMETER( IsValidSoundDevice(SoundDevice) );
92     VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance );
93 
94     SND_TRACE(L"Listing sound device instance\n");
95 
96     if ( ! SoundDevice->HeadInstance )
97     {
98         /* First entry - assign to head and tail */
99         SoundDevice->HeadInstance = SoundDeviceInstance;
100         SoundDevice->TailInstance = SoundDeviceInstance;
101     }
102     else
103     {
104         /* Attach to the end */
105         SoundDevice->TailInstance->Next = SoundDeviceInstance;
106         SoundDevice->TailInstance = SoundDeviceInstance;
107     }
108 
109     return MMSYSERR_NOERROR;
110 }
111 
112 MMRESULT
UnlistSoundDeviceInstance(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)113 UnlistSoundDeviceInstance(
114     IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
115 {
116     MMRESULT Result;
117     PSOUND_DEVICE SoundDevice;
118     PSOUND_DEVICE_INSTANCE CurrentInstance, PreviousInstance;
119 
120     VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
121 
122     SND_TRACE(L"Unlisting sound device instance\n");
123 
124     Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
125     SND_ASSERT( MMSUCCESS(Result) );
126     if ( ! MMSUCCESS(Result) )
127         return TranslateInternalMmResult(Result);
128 
129     PreviousInstance = NULL;
130     CurrentInstance = SoundDevice->HeadInstance;
131 
132     while ( CurrentInstance )
133     {
134         if ( CurrentInstance == SoundDeviceInstance )
135         {
136             if ( ! PreviousInstance )
137             {
138                 /* This is the head node */
139                 SoundDevice->HeadInstance = SoundDevice->HeadInstance->Next;
140             }
141             else
142             {
143                 /* There are nodes before this one - cut ours out */
144                 PreviousInstance->Next = CurrentInstance->Next;
145             }
146 
147             if ( ! CurrentInstance->Next )
148             {
149                 /* This is the tail node */
150                 SoundDevice->TailInstance = PreviousInstance;
151             }
152         }
153 
154         PreviousInstance = CurrentInstance;
155         CurrentInstance = CurrentInstance->Next;
156     }
157 
158     return MMSYSERR_NOERROR;
159 }
160 
161 MMRESULT
CreateSoundDeviceInstance(IN PSOUND_DEVICE SoundDevice,OUT PSOUND_DEVICE_INSTANCE * SoundDeviceInstance)162 CreateSoundDeviceInstance(
163     IN  PSOUND_DEVICE SoundDevice,
164     OUT PSOUND_DEVICE_INSTANCE* SoundDeviceInstance)
165 {
166     MMRESULT Result;
167     PMMFUNCTION_TABLE FunctionTable;
168 
169     SND_TRACE(L"Creating a sound device instance\n");
170 
171     VALIDATE_MMSYS_PARAMETER( IsValidSoundDevice(SoundDevice) );
172     VALIDATE_MMSYS_PARAMETER( SoundDeviceInstance != NULL );
173 
174     Result = AllocateSoundDeviceInstance(SoundDeviceInstance);
175 
176     if ( ! MMSUCCESS(Result) )
177         return TranslateInternalMmResult(Result);
178 
179     /* Get the "open" routine from the function table, and validate it */
180     Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
181     if ( ! MMSUCCESS(Result) )
182     {
183         FreeSoundDeviceInstance(*SoundDeviceInstance);
184         return TranslateInternalMmResult(Result);
185     }
186 
187     if ( FunctionTable->Open == NULL )
188     {
189         FreeSoundDeviceInstance(*SoundDeviceInstance);
190         return MMSYSERR_NOTSUPPORTED;
191     }
192 
193     /* Set up the members of the structure */
194     (*SoundDeviceInstance)->Next = NULL;
195     (*SoundDeviceInstance)->Device = SoundDevice;
196     (*SoundDeviceInstance)->Handle = NULL;
197     (*SoundDeviceInstance)->Thread = NULL;
198 
199     (*SoundDeviceInstance)->WinMM.Handle = INVALID_HANDLE_VALUE;
200     (*SoundDeviceInstance)->WinMM.ClientCallback = 0;
201     (*SoundDeviceInstance)->WinMM.ClientCallbackInstanceData = 0;
202     (*SoundDeviceInstance)->WinMM.Flags = 0;
203 
204     /* Initialise the members of the struct used by the sound thread */
205     (*SoundDeviceInstance)->HeadWaveHeader = NULL;
206     (*SoundDeviceInstance)->TailWaveHeader = NULL;
207 
208     (*SoundDeviceInstance)->OutstandingBuffers = 0;
209 
210     (*SoundDeviceInstance)->LoopsRemaining = 0;
211 
212     /* Create the streaming thread (TODO - is this for wave only?) */
213     Result = CreateSoundThread(&(*SoundDeviceInstance)->Thread);
214     if ( ! MMSUCCESS(Result) )
215     {
216         FreeSoundDeviceInstance(*SoundDeviceInstance);
217         return TranslateInternalMmResult(Result);
218     }
219 
220     /* Add the instance to the list */
221     Result = ListSoundDeviceInstance(SoundDevice, *SoundDeviceInstance);
222     if ( ! MMSUCCESS(Result) )
223     {
224         FreeSoundDeviceInstance(*SoundDeviceInstance);
225         return TranslateInternalMmResult(Result);
226     }
227 
228     /* Try and open the device */
229     Result = FunctionTable->Open(SoundDevice, (&(*SoundDeviceInstance)->Handle));
230     if ( ! MMSUCCESS(Result) )
231     {
232         UnlistSoundDeviceInstance(*SoundDeviceInstance);
233         FreeSoundDeviceInstance(*SoundDeviceInstance);
234         return TranslateInternalMmResult(Result);
235     }
236 
237     return MMSYSERR_NOERROR;
238 }
239 
240 MMRESULT
DestroySoundDeviceInstance(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance)241 DestroySoundDeviceInstance(
242     IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance)
243 {
244     MMRESULT Result;
245     PMMFUNCTION_TABLE FunctionTable;
246     PSOUND_DEVICE SoundDevice;
247     PVOID Handle;
248 
249     SND_TRACE(L"Destroying a sound device instance\n");
250 
251     VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
252 
253     Result = GetSoundDeviceFromInstance(SoundDeviceInstance, &SoundDevice);
254     if ( ! MMSUCCESS(Result) )
255         return TranslateInternalMmResult(Result);
256 
257     Result = GetSoundDeviceInstanceHandle(SoundDeviceInstance, &Handle);
258     if ( ! MMSUCCESS(Result) )
259         return TranslateInternalMmResult(Result);
260 
261     /* Get the "close" routine from the function table, and validate it */
262     Result = GetSoundDeviceFunctionTable(SoundDevice, &FunctionTable);
263     if ( ! MMSUCCESS(Result) )
264         return TranslateInternalMmResult(Result);
265 
266     SND_ASSERT( FunctionTable->Close );
267     if ( FunctionTable->Close == NULL )
268     {
269         /* This indicates bad practice, really! If you can open, why not close?! */
270         return MMSYSERR_NOTSUPPORTED;
271     }
272 
273     /* Stop the streaming thread */
274     if ( SoundDeviceInstance->Thread )
275     {
276         Result = DestroySoundThread(SoundDeviceInstance->Thread);
277         SND_ASSERT( MMSUCCESS(Result) );    /* It should succeed! */
278         if ( ! MMSUCCESS(Result ) )
279         {
280             return TranslateInternalMmResult(Result);
281         }
282     }
283 
284     /* Blank this out here */
285     SoundDeviceInstance->Thread = NULL;
286 
287     /* Try and close the device */
288     Result = FunctionTable->Close(SoundDeviceInstance, Handle);
289     SND_ASSERT( MMSUCCESS(Result) );    /* It should succeed! */
290     if ( ! MMSUCCESS(Result) )
291         return TranslateInternalMmResult(Result);
292 
293     /* Drop it from the list */
294     Result = UnlistSoundDeviceInstance(SoundDeviceInstance);
295     SND_ASSERT( MMSUCCESS(Result) );    /* It should succeed! */
296     if ( ! MMSUCCESS(Result) )
297         return TranslateInternalMmResult(Result);
298 
299     FreeSoundDeviceInstance(SoundDeviceInstance);
300 
301     return MMSYSERR_NOERROR;
302 }
303 
304 MMRESULT
DestroyAllSoundDeviceInstances(IN PSOUND_DEVICE SoundDevice)305 DestroyAllSoundDeviceInstances(
306     IN  PSOUND_DEVICE SoundDevice)
307 {
308     MMRESULT Result;
309     PSOUND_DEVICE_INSTANCE SoundDeviceInstance, NextDeviceInstance;
310 
311     SoundDeviceInstance = SoundDevice->HeadInstance;
312 
313     while ( SoundDeviceInstance )
314     {
315         NextDeviceInstance = SoundDeviceInstance->Next;
316         Result = DestroySoundDeviceInstance(SoundDeviceInstance);
317         SND_ASSERT( MMSUCCESS(Result) );
318         SoundDeviceInstance = NextDeviceInstance;
319     }
320 
321     return MMSYSERR_NOERROR;
322 }
323 
324 MMRESULT
GetSoundDeviceFromInstance(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,OUT PSOUND_DEVICE * SoundDevice)325 GetSoundDeviceFromInstance(
326     IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
327     OUT PSOUND_DEVICE* SoundDevice)
328 {
329     VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
330     VALIDATE_MMSYS_PARAMETER( SoundDevice );
331 
332     *SoundDevice = SoundDeviceInstance->Device;
333 
334     return MMSYSERR_NOERROR;
335 }
336 
337 MMRESULT
GetSoundDeviceInstanceHandle(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,OUT PVOID * Handle)338 GetSoundDeviceInstanceHandle(
339     IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
340     OUT PVOID* Handle)
341 {
342     VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
343     VALIDATE_MMSYS_PARAMETER( Handle );
344 
345     *Handle = SoundDeviceInstance->Handle;
346 
347     return MMSYSERR_NOERROR;
348 }
349 
350 MMRESULT
SetSoundDeviceInstanceMmeData(IN PSOUND_DEVICE_INSTANCE SoundDeviceInstance,IN HDRVR MmeHandle,IN DWORD_PTR ClientCallback,IN DWORD_PTR ClientCallbackData,IN DWORD Flags)351 SetSoundDeviceInstanceMmeData(
352     IN  PSOUND_DEVICE_INSTANCE SoundDeviceInstance,
353     IN  HDRVR MmeHandle,
354     IN  DWORD_PTR ClientCallback,
355     IN  DWORD_PTR ClientCallbackData,
356     IN  DWORD Flags)
357 {
358     VALIDATE_MMSYS_PARAMETER( IsValidSoundDeviceInstance(SoundDeviceInstance) );
359 
360     SND_TRACE(L"Setting MME data - handle %x, callback %x, instance data %x, flags %x\n",
361               MmeHandle, ClientCallback, ClientCallbackData, Flags);
362 
363     SoundDeviceInstance->WinMM.Handle = MmeHandle;
364     SoundDeviceInstance->WinMM.ClientCallback = ClientCallback;
365     SoundDeviceInstance->WinMM.ClientCallbackInstanceData = ClientCallbackData;
366     SoundDeviceInstance->WinMM.Flags = Flags;
367 
368     return MMSYSERR_NOERROR;
369 }
370