[MMEBUDDY]
[reactos.git] / reactos / lib / drivers / sound / mmebuddy / deviceinstance.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/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
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
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
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
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
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
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
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
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
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
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
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 }