1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Configuration of network devices
4  * FILE:            dll/directx/dsound_new/directsound.c
5  * PURPOSE:         Handles IDirectSound interface
6  *
7  * PROGRAMMERS:     Johannes Anderwald (johannes.anderwald@reactos.org)
8  */
9 
10 #include "precomp.h"
11 
12 typedef struct
13 {
14     IDirectSound8Vtbl *lpVtbl;
15     LONG ref;
16     GUID DeviceGUID;
17     BOOL bInitialized;
18     BOOL bDirectSound8;
19     DWORD dwLevel;
20     LPFILTERINFO Filter;
21     LPDIRECTSOUNDBUFFER8 PrimaryBuffer;
22 
23 
24 }CDirectSoundImpl, *LPCDirectSoundImpl;
25 
26 HRESULT
27 WINAPI
28 IDirectSound8_fnQueryInterface(
29     LPDIRECTSOUND8 iface,
30     REFIID riid,
31     LPVOID * ppobj)
32 {
33     LPOLESTR pStr;
34     LPCDirectSoundImpl This = (LPCDirectSoundImpl)CONTAINING_RECORD(iface, CDirectSoundImpl, lpVtbl);
35 
36     if ((IsEqualIID(riid, &IID_IDirectSound) && This->bDirectSound8 == FALSE) ||
37         (IsEqualIID(riid, &IID_IDirectSound8) && This->bDirectSound8 != FALSE) ||
38         (IsEqualIID(riid, &IID_IUnknown)))
39     {
40         *ppobj = (LPVOID)&This->lpVtbl;
41         InterlockedIncrement(&This->ref);
42         return S_OK;
43     }
44 
45     if (SUCCEEDED(StringFromIID(riid, &pStr)))
46     {
47         DPRINT("No Interface for class %s\n", pStr);
48         CoTaskMemFree(pStr);
49     }
50     return E_NOINTERFACE;
51 }
52 
53 ULONG
54 WINAPI
55 IDirectSound8_fnAddRef(
56     LPDIRECTSOUND8 iface)
57 {
58     ULONG ref;
59     LPCDirectSoundImpl This = (LPCDirectSoundImpl)CONTAINING_RECORD(iface, CDirectSoundImpl, lpVtbl);
60 
61     ref = InterlockedIncrement(&This->ref);
62 
63     return ref;
64 }
65 
66 ULONG
67 WINAPI
68 IDirectSound8_fnRelease(
69     LPDIRECTSOUND8 iface)
70 {
71     ULONG ref;
72     LPCDirectSoundImpl This = (LPCDirectSoundImpl)CONTAINING_RECORD(iface, CDirectSoundImpl, lpVtbl);
73 
74     ref = InterlockedDecrement(&(This->ref));
75 
76     if (!ref)
77     {
78         HeapFree(GetProcessHeap(), 0, This);
79     }
80 
81     return ref;
82 }
83 
84 HRESULT
85 WINAPI
86 IDirectSound8_fnCreateSoundBuffer(
87     LPDIRECTSOUND8 iface,
88     LPCDSBUFFERDESC lpcDSBufferDesc,
89     LPLPDIRECTSOUNDBUFFER lplpDirectSoundBuffer,
90     IUnknown FAR* pUnkOuter)
91 {
92     HRESULT hResult;
93     LPCDirectSoundImpl This = (LPCDirectSoundImpl)CONTAINING_RECORD(iface, CDirectSoundImpl, lpVtbl);
94 
95     if (!This->bInitialized)
96     {
97         /* object not yet initialized */
98         return DSERR_UNINITIALIZED;
99     }
100 
101     if (!lpcDSBufferDesc  || !lplpDirectSoundBuffer || pUnkOuter != NULL)
102     {
103         DPRINT("Invalid parameter %p %p %p\n", lpcDSBufferDesc, lplpDirectSoundBuffer, pUnkOuter);
104         return DSERR_INVALIDPARAM;
105     }
106 
107     /* check buffer description */
108     if ((lpcDSBufferDesc->dwSize != sizeof(DSBUFFERDESC) && lpcDSBufferDesc->dwSize != sizeof(DSBUFFERDESC1)) || lpcDSBufferDesc->dwReserved != 0)
109     {
110         DPRINT("Invalid buffer description size %u expected %u dwReserved %u\n", lpcDSBufferDesc->dwSize, sizeof(DSBUFFERDESC1), lpcDSBufferDesc->dwReserved);
111         return DSERR_INVALIDPARAM;
112     }
113 
114     DPRINT("This %p dwFlags %x dwBufferBytes %u lpwfxFormat %p dwSize %u\n", This, lpcDSBufferDesc->dwFlags, lpcDSBufferDesc->dwBufferBytes, lpcDSBufferDesc->lpwfxFormat, lpcDSBufferDesc->dwSize);
115 
116     if (lpcDSBufferDesc->dwFlags & DSBCAPS_PRIMARYBUFFER)
117     {
118         if (lpcDSBufferDesc->lpwfxFormat != NULL)
119         {
120             /* format must be null for primary sound buffer */
121             return DSERR_INVALIDPARAM;
122         }
123 
124         if (lpcDSBufferDesc->dwBufferBytes != 0)
125         {
126             /* buffer size must be zero for primary sound buffer */
127             return DSERR_INVALIDPARAM;
128         }
129 
130         if (This->PrimaryBuffer)
131         {
132             /* primary buffer already exists */
133             IDirectSoundBuffer8_AddRef(This->PrimaryBuffer);
134             *lplpDirectSoundBuffer = (LPDIRECTSOUNDBUFFER)This->PrimaryBuffer;
135             return S_OK;
136         }
137 
138         hResult = NewPrimarySoundBuffer((LPLPDIRECTSOUNDBUFFER8)lplpDirectSoundBuffer, This->Filter, This->dwLevel, lpcDSBufferDesc->dwFlags);
139         if (SUCCEEDED(hResult))
140         {
141             /* store primary buffer */
142             This->PrimaryBuffer = (LPDIRECTSOUNDBUFFER8)*lplpDirectSoundBuffer;
143         }
144         return hResult;
145     }
146     else
147     {
148         if (lpcDSBufferDesc->lpwfxFormat == NULL)
149         {
150             /* format must not be null */
151             return DSERR_INVALIDPARAM;
152         }
153 
154         if (lpcDSBufferDesc->dwBufferBytes < DSBSIZE_MIN || lpcDSBufferDesc->dwBufferBytes > DSBSIZE_MAX)
155         {
156             /* buffer size must be within bounds for secondary sound buffer*/
157             return DSERR_INVALIDPARAM;
158         }
159 
160         if (!This->PrimaryBuffer)
161         {
162             hResult = NewPrimarySoundBuffer((LPLPDIRECTSOUNDBUFFER8)lplpDirectSoundBuffer, This->Filter, This->dwLevel, lpcDSBufferDesc->dwFlags);
163             if (SUCCEEDED(hResult))
164             {
165                 /* store primary buffer */
166                 This->PrimaryBuffer = (LPDIRECTSOUNDBUFFER8)*lplpDirectSoundBuffer;
167             }
168             else
169             {
170                 DPRINT("Failed to create primary buffer with %x\n", hResult);
171                 return hResult;
172             }
173 
174         }
175 
176         ASSERT(This->PrimaryBuffer);
177 
178         DPRINT("This %p wFormatTag %x nChannels %u nSamplesPerSec %u nAvgBytesPerSec %u NBlockAlign %u wBitsPerSample %u cbSize %u\n",
179                This, lpcDSBufferDesc->lpwfxFormat->wFormatTag, lpcDSBufferDesc->lpwfxFormat->nChannels, lpcDSBufferDesc->lpwfxFormat->nSamplesPerSec, lpcDSBufferDesc->lpwfxFormat->nAvgBytesPerSec, lpcDSBufferDesc->lpwfxFormat->nBlockAlign, lpcDSBufferDesc->lpwfxFormat->wBitsPerSample, lpcDSBufferDesc->lpwfxFormat->cbSize);
180 
181         hResult = NewSecondarySoundBuffer((LPLPDIRECTSOUNDBUFFER8)lplpDirectSoundBuffer, This->Filter, This->dwLevel, lpcDSBufferDesc, This->PrimaryBuffer);
182         return hResult;
183     }
184 }
185 
186 HRESULT
187 WINAPI
188 IDirectSound8_fnGetCaps(
189     LPDIRECTSOUND8 iface,
190     LPDSCAPS lpDSCaps)
191 {
192     LPCDirectSoundImpl This = (LPCDirectSoundImpl)CONTAINING_RECORD(iface, CDirectSoundImpl, lpVtbl);
193 
194     if (!This->bInitialized)
195     {
196         /* object not yet initialized */
197         return DSERR_UNINITIALIZED;
198     }
199 
200     if (!lpDSCaps)
201     {
202         /* object not yet initialized */
203         return DSERR_INVALIDPARAM;
204     }
205 
206     if (lpDSCaps->dwSize != sizeof(DSCAPS))
207     {
208         /* object not yet initialized */
209         return DSERR_INVALIDPARAM;
210     }
211 
212     UNIMPLEMENTED;
213     return DSERR_GENERIC;
214 }
215 
216 HRESULT
217 WINAPI
218 IDirectSound8_fnDuplicateSoundBuffer(
219     LPDIRECTSOUND8 iface,
220     LPDIRECTSOUNDBUFFER lpDsbOriginal,
221     LPLPDIRECTSOUNDBUFFER lplpDsbDuplicate)
222 {
223     UNIMPLEMENTED;
224     return DSERR_OUTOFMEMORY;
225 }
226 
227 HRESULT
228 WINAPI
229 IDirectSound8_fnSetCooperativeLevel(
230     LPDIRECTSOUND8 iface,
231     HWND hwnd,
232     DWORD dwLevel)
233 {
234     LPCDirectSoundImpl This = (LPCDirectSoundImpl)CONTAINING_RECORD(iface, CDirectSoundImpl, lpVtbl);
235 
236     if (!This->bInitialized)
237     {
238         /* object not yet initialized */
239         return DSERR_UNINITIALIZED;
240     }
241 
242     /* store cooperation level */
243     This->dwLevel = dwLevel;
244     return DS_OK;
245 }
246 
247 HRESULT
248 WINAPI
249 IDirectSound8_fnCompact(
250     LPDIRECTSOUND8 iface)
251 {
252     LPCDirectSoundImpl This = (LPCDirectSoundImpl)CONTAINING_RECORD(iface, CDirectSoundImpl, lpVtbl);
253 
254     if (!This->bInitialized)
255     {
256         /* object not yet initialized */
257         return DSERR_UNINITIALIZED;
258     }
259 
260     if (This->dwLevel != DSSCL_PRIORITY)
261     {
262         /* needs priority level */
263         return DSERR_PRIOLEVELNEEDED;
264     }
265 
266     /* done */
267     return DS_OK;
268 }
269 
270 HRESULT
271 WINAPI
272 IDirectSound8_fnGetSpeakerConfig(
273     LPDIRECTSOUND8 iface,
274     LPDWORD pdwSpeakerConfig)
275 {
276     LPCDirectSoundImpl This = (LPCDirectSoundImpl)CONTAINING_RECORD(iface, CDirectSoundImpl, lpVtbl);
277 
278     if (!This->bInitialized)
279     {
280         /* object not yet initialized */
281         return DSERR_UNINITIALIZED;
282     }
283 
284 
285     UNIMPLEMENTED;
286     return DSERR_INVALIDPARAM;
287 }
288 
289 HRESULT
290 WINAPI
291 IDirectSound8_fnSetSpeakerConfig(
292     LPDIRECTSOUND8 iface,
293     DWORD dwSpeakerConfig)
294 {
295     UNIMPLEMENTED;
296     return DSERR_INVALIDPARAM;
297 }
298 
299 
300 HRESULT
301 WINAPI
302 IDirectSound8_fnInitialize(
303     LPDIRECTSOUND8 iface,
304     LPCGUID pcGuidDevice)
305 {
306     GUID DeviceGuid;
307     LPOLESTR pGuidStr;
308     HRESULT hr;
309     LPCDirectSoundImpl This = (LPCDirectSoundImpl)CONTAINING_RECORD(iface, CDirectSoundImpl, lpVtbl);
310 
311     if (!RootInfo)
312     {
313         EnumAudioDeviceInterfaces(&RootInfo);
314     }
315 
316     /* sanity check */
317     ASSERT(RootInfo);
318 
319     if (This->bInitialized)
320     {
321         /* object has already been initialized */
322         return DSERR_ALREADYINITIALIZED;
323     }
324 
325     /* fixme mutual exclusion */
326 
327     if (pcGuidDevice == NULL || IsEqualGUID(pcGuidDevice, &GUID_NULL))
328     {
329         /* use default playback device id */
330         pcGuidDevice = &DSDEVID_DefaultPlayback;
331     }
332 
333     if (IsEqualIID(pcGuidDevice, &DSDEVID_DefaultCapture) || IsEqualIID(pcGuidDevice, &DSDEVID_DefaultVoiceCapture))
334     {
335         /* this has to be a winetest */
336         return DSERR_NODRIVER;
337     }
338 
339     /* now verify the guid */
340     if (GetDeviceID(pcGuidDevice, &DeviceGuid) != DS_OK)
341     {
342         if (SUCCEEDED(StringFromIID(pcGuidDevice, &pGuidStr)))
343         {
344             DPRINT("IDirectSound8_fnInitialize: Unknown GUID %ws\n", pGuidStr);
345             CoTaskMemFree(pGuidStr);
346         }
347         return DSERR_INVALIDPARAM;
348     }
349 
350     hr = FindDeviceByGuid(&DeviceGuid, &This->Filter);
351 
352     if (SUCCEEDED(hr))
353     {
354         This->bInitialized = TRUE;
355         return DS_OK;
356     }
357 
358     DPRINT("Failed to find device\n");
359     return DSERR_INVALIDPARAM;
360 }
361 
362 HRESULT
363 WINAPI
364 IDirectSound8_fnVerifyCertification(
365     LPDIRECTSOUND8 iface,
366     LPDWORD pdwCertified)
367 {
368     LPCDirectSoundImpl This = (LPCDirectSoundImpl)CONTAINING_RECORD(iface, CDirectSoundImpl, lpVtbl);
369 
370     if (!This->bInitialized)
371     {
372         /* object not yet initialized */
373         return DSERR_UNINITIALIZED;
374     }
375 
376     UNIMPLEMENTED;
377     return DS_CERTIFIED;
378 }
379 
380 static IDirectSound8Vtbl vt_DirectSound8 =
381 {
382     /* IUnknown methods */
383     IDirectSound8_fnQueryInterface,
384     IDirectSound8_fnAddRef,
385     IDirectSound8_fnRelease,
386     /* IDirectSound methods */
387     IDirectSound8_fnCreateSoundBuffer,
388     IDirectSound8_fnGetCaps,
389     IDirectSound8_fnDuplicateSoundBuffer,
390     IDirectSound8_fnSetCooperativeLevel,
391     IDirectSound8_fnCompact,
392     IDirectSound8_fnGetSpeakerConfig,
393     IDirectSound8_fnSetSpeakerConfig,
394     IDirectSound8_fnInitialize,
395     /* IDirectSound8 methods */
396     IDirectSound8_fnVerifyCertification
397 };
398 
399 HRESULT
400 InternalDirectSoundCreate(
401     LPCGUID lpcGUID,
402     LPDIRECTSOUND8 *ppDS,
403     IUnknown *pUnkOuter,
404     BOOL bDirectSound8)
405 {
406     LPCDirectSoundImpl This;
407     HRESULT hr;
408 
409     if (!ppDS || pUnkOuter != NULL)
410     {
411         /* invalid parameter passed */
412         return DSERR_INVALIDPARAM;
413     }
414 
415     /* allocate CDirectSoundImpl struct */
416     This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CDirectSoundImpl));
417     if (!This)
418     {
419         /* not enough memory */
420         return DSERR_OUTOFMEMORY;
421     }
422 
423     /* initialize IDirectSound object */
424     This->ref = 1;
425     This->bDirectSound8 = bDirectSound8;
426     This->lpVtbl = &vt_DirectSound8;
427 
428 
429     /* initialize direct sound interface */
430     hr = IDirectSound8_Initialize((LPDIRECTSOUND8)&This->lpVtbl, lpcGUID);
431 
432     /* check for success */
433     if (!SUCCEEDED(hr))
434     {
435         /* failed */
436         DPRINT("Failed to initialize DirectSound object with %x\n", hr);
437         IDirectSound8_Release((LPDIRECTSOUND8)&This->lpVtbl);
438         return hr;
439     }
440 
441     /* store result */
442     *ppDS = (LPDIRECTSOUND8)&This->lpVtbl;
443     DPRINT("DirectSound object %p\n", *ppDS);
444     return DS_OK;
445 }
446 
447 HRESULT
448 CALLBACK
449 NewDirectSound(
450     IUnknown* pUnkOuter,
451     REFIID riid,
452     LPVOID* ppvObject)
453 {
454     LPOLESTR pStr;
455     LPCDirectSoundImpl This;
456 
457     /* check param */
458     if (!ppvObject)
459     {
460         /* invalid param */
461         return E_INVALIDARG;
462     }
463 
464     /* check requested interface */
465     if (!IsEqualIID(riid, &IID_IUnknown) && !IsEqualIID(riid, &IID_IDirectSound) && !IsEqualIID(riid, &IID_IDirectSound8))
466     {
467         *ppvObject = 0;
468         StringFromIID(riid, &pStr);
469         DPRINT("KsPropertySet does not support Interface %ws\n", pStr);
470         CoTaskMemFree(pStr);
471         return E_NOINTERFACE;
472     }
473 
474     /* allocate CDirectSoundCaptureImpl struct */
475     This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CDirectSoundImpl));
476     if (!This)
477     {
478         /* not enough memory */
479         return DSERR_OUTOFMEMORY;
480     }
481 
482     /* initialize object */
483     This->ref = 1;
484     This->lpVtbl = &vt_DirectSound8;
485     This->bInitialized = FALSE;
486     *ppvObject = (LPVOID)&This->lpVtbl;
487 
488     return S_OK;
489 }
490 
491 
492 HRESULT
493 WINAPI
494 DirectSoundCreate(
495     LPCGUID lpcGUID,
496     LPDIRECTSOUND *ppDS,
497     IUnknown *pUnkOuter)
498 {
499     return InternalDirectSoundCreate(lpcGUID, (LPDIRECTSOUND8*)ppDS, pUnkOuter, FALSE);
500 }
501 
502 HRESULT
503 WINAPI
504 DirectSoundCreate8(
505     LPCGUID lpcGUID,
506     LPDIRECTSOUND8 *ppDS,
507     IUnknown *pUnkOuter)
508 {
509     return InternalDirectSoundCreate(lpcGUID, ppDS, pUnkOuter, TRUE);
510 }
511