Fixed some issues found by winapi_check.
[wine] / dlls / dsound / dsound_main.c
1 /*                      DirectSound
2  *
3  * Copyright 1998 Marcus Meissner
4  * Copyright 1998 Rob Riggs
5  * Copyright 2000-2002 TransGaming Technologies, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21 /*
22  * Most thread locking is complete. There may be a few race
23  * conditions still lurking.
24  *
25  * Tested with a Soundblaster clone, a Gravis UltraSound Classic,
26  * and a Turtle Beach Tropez+.
27  *
28  * TODO:
29  *      Implement SetCooperativeLevel properly (need to address focus issues)
30  *      Implement DirectSound3DBuffers (stubs in place)
31  *      Use hardware 3D support if available
32  *      Add critical section locking inside Release and AddRef methods
33  *      Handle static buffers - put those in hardware, non-static not in hardware
34  *      Hardware DuplicateSoundBuffer
35  *      Proper volume calculation, and setting volume in HEL primary buffer
36  *      Optimize WINMM and negotiate fragment size, decrease DS_HEL_MARGIN
37  */
38
39 #include "config.h"
40 #include "wine/port.h"
41
42 #include <assert.h>
43 #include <stdio.h>
44 #include <sys/types.h>
45 #include <sys/fcntl.h>
46 #include <unistd.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <math.h>       /* Insomnia - pow() function */
50
51 #include "windef.h"
52 #include "winbase.h"
53 #include "winreg.h"
54 #include "winuser.h"
55 #include "wingdi.h"
56 #include "winuser.h"
57 #include "winerror.h"
58 #include "mmsystem.h"
59 #include "ntddk.h"
60 #include "mmddk.h"
61 #include "wine/windef16.h"
62 #include "wine/winbase16.h"
63 #include "wine/debug.h"
64 #include "dsound.h"
65 #include "dsdriver.h"
66 #include "dsound_private.h"
67
68 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
69
70 /* these are eligible for tuning... they must be high on slow machines... */
71 /* some stuff may get more responsive with lower values though... */
72 #define DS_EMULDRIVER 0 /* some games (Quake 2, UT) refuse to accept
73                                 emulated dsound devices. set to 0 ! */
74 #define DS_HEL_MARGIN 5 /* HEL only: number of waveOut fragments ahead to mix in new buffers
75                          * (keep this close or equal to DS_HEL_QUEUE for best results) */
76 #define DS_HEL_QUEUE  5 /* HEL only: number of waveOut fragments ahead to queue to driver
77                          * (this will affect HEL sound reliability and latency) */
78
79 #define DS_SND_QUEUE_MAX 28 /* max number of fragments to prebuffer */
80 #define DS_SND_QUEUE_MIN 12 /* min number of fragments to prebuffer */
81
82 IDirectSoundImpl*       dsound = NULL;
83
84 static HRESULT mmErr(UINT err)
85 {
86         switch(err) {
87         case MMSYSERR_NOERROR:
88                 return DS_OK;
89         case MMSYSERR_ALLOCATED:
90                 return DSERR_ALLOCATED;
91         case MMSYSERR_INVALHANDLE:
92                 return DSERR_GENERIC; /* FIXME */
93         case MMSYSERR_NODRIVER:
94                 return DSERR_NODRIVER;
95         case MMSYSERR_NOMEM:
96                 return DSERR_OUTOFMEMORY;
97         case MMSYSERR_INVALPARAM:
98                 return DSERR_INVALIDPARAM;
99         default:
100                 FIXME("Unknown MMSYS error %d\n",err);
101                 return DSERR_GENERIC;
102         }
103 }
104
105 int ds_emuldriver = DS_EMULDRIVER;
106 int ds_hel_margin = DS_HEL_MARGIN;
107 int ds_hel_queue = DS_HEL_QUEUE;
108 int ds_snd_queue_max = DS_SND_QUEUE_MAX;
109 int ds_snd_queue_min = DS_SND_QUEUE_MIN;
110
111 /*
112  * Call the callback provided to DirectSoundEnumerateA.
113  */
114
115 inline static void enumerate_devices(LPDSENUMCALLBACKA lpDSEnumCallback,
116                                     LPVOID lpContext)
117 {
118     if (lpDSEnumCallback != NULL)
119        if (lpDSEnumCallback(NULL, "Primary DirectSound Driver",
120                             "sound", lpContext))
121            lpDSEnumCallback((LPGUID)&DSDEVID_WinePlayback,
122                             "WINE DirectSound", "sound",
123                             lpContext);
124 }
125
126
127 /*
128  * Get a config key from either the app-specific or the default config
129  */
130
131 inline static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
132                                     char *buffer, DWORD size )
133 {
134     if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, buffer, &size )) return 0;
135     return RegQueryValueExA( defkey, name, 0, NULL, buffer, &size );
136 }
137
138
139 /*
140  * Setup the dsound options.
141  */
142
143 inline static void setup_dsound_options(void)
144 {
145     char buffer[MAX_PATH+1];
146     HKEY hkey, appkey = 0;
147
148     buffer[MAX_PATH]='\0';
149
150     if (RegCreateKeyExA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\dsound", 0, NULL,
151                          REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
152     {
153         ERR("Cannot create config registry key\n" );
154         ExitProcess(1);
155     }
156
157     if (GetModuleFileNameA( 0, buffer, MAX_PATH ))
158     {
159         HKEY tmpkey;
160
161         if (!RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\AppDefaults", &tmpkey ))
162         {
163            char appname[MAX_PATH+16];
164            char *p = strrchr( buffer, '\\' );
165            if (p!=NULL) {
166                    appname[MAX_PATH]='\0';
167                    strncpy(appname,p+1,MAX_PATH);
168                    strcat(appname,"\\dsound");
169                    TRACE("appname = [%s] \n",appname);
170                    if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
171                        RegCloseKey( tmpkey );
172            }
173         }
174     }
175
176     /* get options */
177
178     if (!get_config_key( hkey, appkey, "EmulDriver", buffer, MAX_PATH ))
179         ds_emuldriver = strcmp(buffer, "N");
180
181     if (!get_config_key( hkey, appkey, "HELmargin", buffer, MAX_PATH ))
182         ds_hel_margin = atoi(buffer);
183
184     if (!get_config_key( hkey, appkey, "HELqueue", buffer, MAX_PATH ))
185         ds_hel_queue = atoi(buffer);
186
187     if (!get_config_key( hkey, appkey, "SndQueueMax", buffer, MAX_PATH ))
188         ds_snd_queue_max = atoi(buffer);
189
190     if (!get_config_key( hkey, appkey, "SndQueueMin", buffer, MAX_PATH ))
191         ds_snd_queue_min = atoi(buffer);
192
193     if (appkey) RegCloseKey( appkey );
194     RegCloseKey( hkey );
195
196     if (ds_emuldriver != DS_EMULDRIVER )
197        WARN("ds_emuldriver = %d (default=%d)\n",ds_emuldriver, DS_EMULDRIVER);
198     if (ds_hel_margin != DS_HEL_MARGIN )
199        WARN("ds_hel_margin = %d (default=%d)\n",ds_hel_margin, DS_HEL_MARGIN );
200     if (ds_hel_queue != DS_HEL_QUEUE )
201        WARN("ds_hel_queue = %d (default=%d)\n",ds_hel_queue, DS_HEL_QUEUE );
202     if (ds_snd_queue_max != DS_SND_QUEUE_MAX)
203        WARN("ds_snd_queue_max = %d (default=%d)\n",ds_snd_queue_max ,DS_SND_QUEUE_MAX);
204     if (ds_snd_queue_min != DS_SND_QUEUE_MIN)
205        WARN("ds_snd_queue_min = %d (default=%d)\n",ds_snd_queue_min ,DS_SND_QUEUE_MIN);
206
207 }
208
209
210
211 /***************************************************************************
212  * DirectSoundEnumerateA [DSOUND.2]
213  *
214  * Enumerate all DirectSound drivers installed in the system
215  *
216  * RETURNS
217  *    Success: DS_OK
218  *    Failure: DSERR_INVALIDPARAM
219  */
220 HRESULT WINAPI DirectSoundEnumerateA(
221         LPDSENUMCALLBACKA lpDSEnumCallback,
222         LPVOID lpContext)
223 {
224         WAVEOUTCAPSA wcaps;
225         unsigned devs, wod;
226
227         TRACE("lpDSEnumCallback = %p, lpContext = %p\n",
228                 lpDSEnumCallback, lpContext);
229
230         devs = waveOutGetNumDevs();
231         for (wod = 0; wod < devs; ++wod) {
232                 waveOutGetDevCapsA(wod, &wcaps, sizeof(wcaps));
233                 if (wcaps.dwSupport & WAVECAPS_DIRECTSOUND) {
234                         TRACE("- Device %u supports DirectSound\n", wod);
235                         enumerate_devices(lpDSEnumCallback, lpContext);
236                         return DS_OK;
237                 }
238         }
239         return DS_OK;
240 }
241
242 /***************************************************************************
243  * DirectSoundEnumerateW [DSOUND.3]
244  *
245  * Enumerate all DirectSound drivers installed in the system
246  *
247  * RETURNS
248  *    Success: DS_OK
249  *    Failure: DSERR_INVALIDPARAM
250  */
251 HRESULT WINAPI DirectSoundEnumerateW(
252         LPDSENUMCALLBACKW lpDSEnumCallback,
253         LPVOID lpContext )
254 {
255         FIXME("lpDSEnumCallback = %p, lpContext = %p: stub\n",
256                 lpDSEnumCallback, lpContext);
257
258         return DS_OK;
259 }
260
261
262 static void _dump_DSBCAPS(DWORD xmask) {
263         struct {
264                 DWORD   mask;
265                 char    *name;
266         } flags[] = {
267 #define FE(x) { x, #x },
268                 FE(DSBCAPS_PRIMARYBUFFER)
269                 FE(DSBCAPS_STATIC)
270                 FE(DSBCAPS_LOCHARDWARE)
271                 FE(DSBCAPS_LOCSOFTWARE)
272                 FE(DSBCAPS_CTRL3D)
273                 FE(DSBCAPS_CTRLFREQUENCY)
274                 FE(DSBCAPS_CTRLPAN)
275                 FE(DSBCAPS_CTRLVOLUME)
276                 FE(DSBCAPS_CTRLPOSITIONNOTIFY)
277                 FE(DSBCAPS_CTRLDEFAULT)
278                 FE(DSBCAPS_CTRLALL)
279                 FE(DSBCAPS_STICKYFOCUS)
280                 FE(DSBCAPS_GLOBALFOCUS)
281                 FE(DSBCAPS_GETCURRENTPOSITION2)
282                 FE(DSBCAPS_MUTE3DATMAXDISTANCE)
283 #undef FE
284         };
285         int     i;
286
287         for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
288                 if ((flags[i].mask & xmask) == flags[i].mask)
289                         DPRINTF("%s ",flags[i].name);
290 }
291
292 /*******************************************************************************
293  *              IDirectSound
294  */
295
296 static HRESULT WINAPI IDirectSoundImpl_SetCooperativeLevel(
297         LPDIRECTSOUND8 iface,HWND hwnd,DWORD level
298 ) {
299         ICOM_THIS(IDirectSoundImpl,iface);
300
301         FIXME("(%p,%08lx,%ld):stub\n",This,(DWORD)hwnd,level);
302
303         This->priolevel = level;
304
305         return DS_OK;
306 }
307
308 static HRESULT WINAPI IDirectSoundImpl_CreateSoundBuffer(
309         LPDIRECTSOUND8 iface,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER8 ppdsb,LPUNKNOWN lpunk
310 ) {
311         ICOM_THIS(IDirectSoundImpl,iface);
312         LPWAVEFORMATEX  wfex;
313
314         TRACE("(%p,%p,%p,%p)\n",This,dsbd,ppdsb,lpunk);
315
316         if ((This == NULL) || (dsbd == NULL) || (ppdsb == NULL))
317                 return DSERR_INVALIDPARAM;
318
319         if (TRACE_ON(dsound)) {
320                 TRACE("(structsize=%ld)\n",dsbd->dwSize);
321                 TRACE("(flags=0x%08lx:\n",dsbd->dwFlags);
322                 _dump_DSBCAPS(dsbd->dwFlags);
323                 DPRINTF(")\n");
324                 TRACE("(bufferbytes=%ld)\n",dsbd->dwBufferBytes);
325                 TRACE("(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
326         }
327
328         wfex = dsbd->lpwfxFormat;
329
330         if (wfex)
331                 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
332                    "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
333                    wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
334                    wfex->nAvgBytesPerSec, wfex->nBlockAlign,
335                    wfex->wBitsPerSample, wfex->cbSize);
336
337         if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)
338                 return PrimaryBuffer_Create(This, (PrimaryBufferImpl**)ppdsb, dsbd);
339         else
340                 return SecondaryBuffer_Create(This, (IDirectSoundBufferImpl**)ppdsb, dsbd);
341 }
342
343 static HRESULT WINAPI IDirectSoundImpl_DuplicateSoundBuffer(
344         LPDIRECTSOUND8 iface,LPDIRECTSOUNDBUFFER8 pdsb,LPLPDIRECTSOUNDBUFFER8 ppdsb
345 ) {
346         ICOM_THIS(IDirectSoundImpl,iface);
347         IDirectSoundBufferImpl* ipdsb=(IDirectSoundBufferImpl*)pdsb;
348         IDirectSoundBufferImpl** ippdsb=(IDirectSoundBufferImpl**)ppdsb;
349         TRACE("(%p,%p,%p)\n",This,ipdsb,ippdsb);
350
351         if (ipdsb->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
352                 ERR("trying to duplicate primary buffer\n");
353                 return DSERR_INVALIDCALL;
354         }
355
356         if (ipdsb->hwbuf) {
357                 FIXME("need to duplicate hardware buffer\n");
358         }
359
360         if (ipdsb->dsbd.dwFlags & DSBCAPS_CTRL3D) {
361                 FIXME("need to duplicate 3D buffer\n");
362         }
363
364         *ippdsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBufferImpl));
365
366         IDirectSoundBuffer8_AddRef(pdsb);
367         memcpy(*ippdsb, ipdsb, sizeof(IDirectSoundBufferImpl));
368         (*ippdsb)->ref = 1;
369         (*ippdsb)->state = STATE_STOPPED;
370         (*ippdsb)->playpos = 0;
371         (*ippdsb)->buf_mixpos = 0;
372         (*ippdsb)->dsound = This;
373         (*ippdsb)->parent = ipdsb;
374         (*ippdsb)->hwbuf = NULL;
375         (*ippdsb)->ds3db = NULL; /* FIXME? */
376         (*ippdsb)->iks = NULL; /* FIXME? */
377         memcpy(&((*ippdsb)->wfx), &(ipdsb->wfx), sizeof((*ippdsb)->wfx));
378         InitializeCriticalSection(&(*ippdsb)->lock);
379         /* register buffer */
380         RtlAcquireResourceExclusive(&(This->lock), TRUE);
381         {
382                 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl**)*(This->nrofbuffers+1));
383                 if (newbuffers) {
384                         This->buffers = newbuffers;
385                         This->buffers[This->nrofbuffers] = *ippdsb;
386                         This->nrofbuffers++;
387                         TRACE("buffer count is now %d\n", This->nrofbuffers);
388                 } else {
389                         ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
390                         /* FIXME: release buffer */
391                 }
392         }
393         RtlReleaseResource(&(This->lock));
394         IDirectSound_AddRef(iface);
395         return DS_OK;
396 }
397
398
399 static HRESULT WINAPI IDirectSoundImpl_GetCaps(LPDIRECTSOUND8 iface,LPDSCAPS caps) {
400         ICOM_THIS(IDirectSoundImpl,iface);
401         TRACE("(%p,%p)\n",This,caps);
402         TRACE("(flags=0x%08lx)\n",caps->dwFlags);
403
404         if (caps == NULL)
405                 return DSERR_INVALIDPARAM;
406
407         /* We should check this value, not set it. See Inside DirectX, p215. */
408         caps->dwSize = sizeof(*caps);
409
410         caps->dwFlags = This->drvcaps.dwFlags;
411
412         /* FIXME: copy caps from This->drvcaps */
413         caps->dwMinSecondarySampleRate          = DSBFREQUENCY_MIN;
414         caps->dwMaxSecondarySampleRate          = DSBFREQUENCY_MAX;
415
416         caps->dwPrimaryBuffers                  = 1;
417
418         caps->dwMaxHwMixingAllBuffers           = 0;
419         caps->dwMaxHwMixingStaticBuffers        = 0;
420         caps->dwMaxHwMixingStreamingBuffers     = 0;
421
422         caps->dwFreeHwMixingAllBuffers          = 0;
423         caps->dwFreeHwMixingStaticBuffers       = 0;
424         caps->dwFreeHwMixingStreamingBuffers    = 0;
425
426         caps->dwMaxHw3DAllBuffers               = 0;
427         caps->dwMaxHw3DStaticBuffers            = 0;
428         caps->dwMaxHw3DStreamingBuffers         = 0;
429
430         caps->dwFreeHw3DAllBuffers              = 0;
431         caps->dwFreeHw3DStaticBuffers           = 0;
432         caps->dwFreeHw3DStreamingBuffers        = 0;
433
434         caps->dwTotalHwMemBytes                 = 0;
435
436         caps->dwFreeHwMemBytes                  = 0;
437
438         caps->dwMaxContigFreeHwMemBytes         = 0;
439
440         caps->dwUnlockTransferRateHwBuffers     = 4096; /* But we have none... */
441
442         caps->dwPlayCpuOverheadSwBuffers        = 1;    /* 1% */
443
444         return DS_OK;
445 }
446
447 static ULONG WINAPI IDirectSoundImpl_AddRef(LPDIRECTSOUND8 iface) {
448         ICOM_THIS(IDirectSoundImpl,iface);
449         return ++(This->ref);
450 }
451
452 static ULONG WINAPI IDirectSoundImpl_Release(LPDIRECTSOUND8 iface) {
453         ICOM_THIS(IDirectSoundImpl,iface);
454         TRACE("(%p), ref was %ld\n",This,This->ref);
455         if (!--(This->ref)) {
456                 UINT i;
457
458                 timeKillEvent(This->timerID);
459                 timeEndPeriod(DS_TIME_RES);
460
461                 if (This->buffers) {
462                         for( i=0;i<This->nrofbuffers;i++)
463                                 IDirectSoundBuffer8_Release((LPDIRECTSOUNDBUFFER8)This->buffers[i]);
464                 }
465
466                 DSOUND_PrimaryDestroy(This);
467
468                 RtlDeleteResource(&This->lock);
469                 DeleteCriticalSection(&This->mixlock);
470                 if (This->driver) {
471                         IDsDriver_Close(This->driver);
472                 } else {
473                         unsigned c;
474                         for (c=0; c<DS_HEL_FRAGS; c++)
475                                 HeapFree(GetProcessHeap(),0,This->pwave[c]);
476                 }
477                 if (This->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) {
478                         waveOutClose(This->hwo);
479                 }
480                 if (This->driver)
481                         IDsDriver_Release(This->driver);
482
483                 HeapFree(GetProcessHeap(),0,This);
484                 dsound = NULL;
485                 return 0;
486         }
487         return This->ref;
488 }
489
490 static HRESULT WINAPI IDirectSoundImpl_SetSpeakerConfig(
491         LPDIRECTSOUND8 iface,DWORD config
492 ) {
493         ICOM_THIS(IDirectSoundImpl,iface);
494         FIXME("(%p,0x%08lx):stub\n",This,config);
495         return DS_OK;
496 }
497
498 static HRESULT WINAPI IDirectSoundImpl_QueryInterface(
499         LPDIRECTSOUND8 iface,REFIID riid,LPVOID *ppobj
500 ) {
501         ICOM_THIS(IDirectSoundImpl,iface);
502
503         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
504                 ERR("app requested IDirectSound3DListener on dsound object\n");
505                 *ppobj = NULL;
506                 return E_FAIL;
507         }
508
509         FIXME("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
510         return E_NOINTERFACE;
511 }
512
513 static HRESULT WINAPI IDirectSoundImpl_Compact(
514         LPDIRECTSOUND8 iface)
515 {
516         ICOM_THIS(IDirectSoundImpl,iface);
517         TRACE("(%p)\n", This);
518         return DS_OK;
519 }
520
521 static HRESULT WINAPI IDirectSoundImpl_GetSpeakerConfig(
522         LPDIRECTSOUND8 iface,
523         LPDWORD lpdwSpeakerConfig)
524 {
525         ICOM_THIS(IDirectSoundImpl,iface);
526         TRACE("(%p, %p)\n", This, lpdwSpeakerConfig);
527         *lpdwSpeakerConfig = DSSPEAKER_STEREO | (DSSPEAKER_GEOMETRY_NARROW << 16);
528         return DS_OK;
529 }
530
531 static HRESULT WINAPI IDirectSoundImpl_Initialize(
532         LPDIRECTSOUND8 iface,
533         LPCGUID lpcGuid)
534 {
535         ICOM_THIS(IDirectSoundImpl,iface);
536         TRACE("(%p, %p)\n", This, lpcGuid);
537         return DS_OK;
538 }
539
540 static HRESULT WINAPI IDirectSoundImpl_VerifyCertification(
541         LPDIRECTSOUND8 iface,
542         LPDWORD pdwCertified)
543 {
544         ICOM_THIS(IDirectSoundImpl,iface);
545         TRACE("(%p, %p)\n", This, pdwCertified);
546         *pdwCertified = DS_CERTIFIED;
547         return DS_OK;
548 }
549
550 static ICOM_VTABLE(IDirectSound8) dsvt =
551 {
552         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
553         IDirectSoundImpl_QueryInterface,
554         IDirectSoundImpl_AddRef,
555         IDirectSoundImpl_Release,
556         IDirectSoundImpl_CreateSoundBuffer,
557         IDirectSoundImpl_GetCaps,
558         IDirectSoundImpl_DuplicateSoundBuffer,
559         IDirectSoundImpl_SetCooperativeLevel,
560         IDirectSoundImpl_Compact,
561         IDirectSoundImpl_GetSpeakerConfig,
562         IDirectSoundImpl_SetSpeakerConfig,
563         IDirectSoundImpl_Initialize,
564         IDirectSoundImpl_VerifyCertification
565 };
566
567
568 /*******************************************************************************
569  *              DirectSoundCreate (DSOUND.1)
570  */
571 HRESULT WINAPI DirectSoundCreate8(REFGUID lpGUID,LPDIRECTSOUND8 *ppDS,IUnknown *pUnkOuter )
572 {
573         IDirectSoundImpl** ippDS=(IDirectSoundImpl**)ppDS;
574         PIDSDRIVER drv = NULL;
575         WAVEOUTCAPSA wcaps;
576         unsigned wod, wodn;
577         HRESULT err = DS_OK;
578
579         if (lpGUID)
580                 TRACE("(%p,%p,%p)\n",lpGUID,ippDS,pUnkOuter);
581         else
582                 TRACE("DirectSoundCreate (%p)\n", ippDS);
583
584         if (ippDS == NULL)
585                 return DSERR_INVALIDPARAM;
586
587         if (dsound) {
588                 IDirectSound_AddRef((LPDIRECTSOUND)dsound);
589                 *ippDS = dsound;
590                 return DS_OK;
591         }
592
593         /* Get dsound configuration */
594         setup_dsound_options();
595
596         /* Enumerate WINMM audio devices and find the one we want */
597         wodn = waveOutGetNumDevs();
598         if (!wodn) return DSERR_NODRIVER;
599
600         /* FIXME: How do we find the GUID of an audio device? */
601         wod = 0;  /* start at the first audio device */
602
603         /* Get output device caps */
604         waveOutGetDevCapsA(wod, &wcaps, sizeof(wcaps));
605         /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
606         waveOutMessage((HWAVEOUT)wod, DRV_QUERYDSOUNDIFACE, (DWORD)&drv, 0);
607
608         /* Allocate memory */
609         *ippDS = (IDirectSoundImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundImpl));
610         if (*ippDS == NULL)
611                 return DSERR_OUTOFMEMORY;
612
613         ICOM_VTBL(*ippDS)       = &dsvt;
614         (*ippDS)->ref           = 1;
615
616         (*ippDS)->driver        = drv;
617         (*ippDS)->priolevel     = DSSCL_NORMAL;
618         (*ippDS)->fraglen       = 0;
619         (*ippDS)->hwbuf         = NULL;
620         (*ippDS)->buffer        = NULL;
621         (*ippDS)->buflen        = 0;
622         (*ippDS)->writelead     = 0;
623         (*ippDS)->state         = STATE_STOPPED;
624         (*ippDS)->nrofbuffers   = 0;
625         (*ippDS)->buffers       = NULL;
626 /*      (*ippDS)->primary       = NULL; */
627         (*ippDS)->listener      = NULL;
628
629         (*ippDS)->prebuf        = ds_snd_queue_max;
630
631         /* Get driver description */
632         if (drv) {
633                 IDsDriver_GetDriverDesc(drv,&((*ippDS)->drvdesc));
634         } else {
635                 /* if no DirectSound interface available, use WINMM API instead */
636                 (*ippDS)->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
637                 (*ippDS)->drvdesc.dnDevNode = wod; /* FIXME? */
638         }
639
640         /* Set default wave format (may need it for waveOutOpen) */
641         (*ippDS)->wfx.wFormatTag        = WAVE_FORMAT_PCM;
642         (*ippDS)->wfx.nChannels         = 2;
643         (*ippDS)->wfx.nSamplesPerSec    = 22050;
644         (*ippDS)->wfx.nAvgBytesPerSec   = 44100;
645         (*ippDS)->wfx.nBlockAlign       = 2;
646         (*ippDS)->wfx.wBitsPerSample    = 8;
647
648         /* If the driver requests being opened through MMSYSTEM
649          * (which is recommended by the DDK), it is supposed to happen
650          * before the DirectSound interface is opened */
651         if ((*ippDS)->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
652         {
653                 /* FIXME: is this right? */
654                 (*ippDS)->drvdesc.dnDevNode = 0;
655                 err = DSERR_ALLOCATED;
656
657                 /* if this device is busy try the next one */
658                 while((err == DSERR_ALLOCATED) &&
659                         ((*ippDS)->drvdesc.dnDevNode < wodn))
660                 {
661                   err = mmErr(waveOutOpen(&((*ippDS)->hwo),
662                                           (*ippDS)->drvdesc.dnDevNode, &((*ippDS)->wfx),
663                                           (DWORD)DSOUND_callback, (DWORD)(*ippDS),
664                                           CALLBACK_FUNCTION | WAVE_DIRECTSOUND));
665                   (*ippDS)->drvdesc.dnDevNode++; /* next wave device */
666                 }
667
668                 (*ippDS)->drvdesc.dnDevNode--; /* take away last increment */
669         }
670
671         if (drv && (err == DS_OK))
672                 err = IDsDriver_Open(drv);
673
674         /* FIXME: do we want to handle a temporarily busy device? */
675         if (err != DS_OK) {
676                 HeapFree(GetProcessHeap(),0,*ippDS);
677                 *ippDS = NULL;
678                 return err;
679         }
680
681         /* the driver is now open, so it's now allowed to call GetCaps */
682         if (drv) {
683                 IDsDriver_GetCaps(drv,&((*ippDS)->drvcaps));
684         } else {
685                 unsigned c;
686
687                 /* FIXME: look at wcaps */
688                 (*ippDS)->drvcaps.dwFlags =
689                         DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO;
690                 if (ds_emuldriver)
691                     (*ippDS)->drvcaps.dwFlags |= DSCAPS_EMULDRIVER;
692
693                 /* Allocate memory for HEL buffer headers */
694                 for (c=0; c<DS_HEL_FRAGS; c++) {
695                         (*ippDS)->pwave[c] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEHDR));
696                         if (!(*ippDS)->pwave[c]) {
697                                 /* Argh, out of memory */
698                                 while (c--) {
699                                         HeapFree(GetProcessHeap(),0,(*ippDS)->pwave[c]);
700                                         waveOutClose((*ippDS)->hwo);
701                                         HeapFree(GetProcessHeap(),0,*ippDS);
702                                         *ippDS = NULL;
703                                         return DSERR_OUTOFMEMORY;
704                                 }
705                         }
706                 }
707         }
708
709         DSOUND_RecalcVolPan(&((*ippDS)->volpan));
710
711         InitializeCriticalSection(&((*ippDS)->mixlock));
712         RtlInitializeResource(&((*ippDS)->lock));
713
714         if (!dsound) {
715                 dsound = (*ippDS);
716                 DSOUND_PrimaryCreate(dsound);
717                 timeBeginPeriod(DS_TIME_RES);
718                 dsound->timerID = timeSetEvent(DS_TIME_DEL, DS_TIME_RES, DSOUND_timer,
719                                                (DWORD)dsound, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
720         }
721         return DS_OK;
722 }
723
724
725 /*******************************************************************************
726  * DirectSound ClassFactory
727  */
728 typedef struct
729 {
730     /* IUnknown fields */
731     ICOM_VFIELD(IClassFactory);
732     DWORD                       ref;
733 } IClassFactoryImpl;
734
735 static HRESULT WINAPI
736 DSCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
737         ICOM_THIS(IClassFactoryImpl,iface);
738
739         FIXME("(%p)->(%s,%p),stub!\n",This,debugstr_guid(riid),ppobj);
740         return E_NOINTERFACE;
741 }
742
743 static ULONG WINAPI
744 DSCF_AddRef(LPCLASSFACTORY iface) {
745         ICOM_THIS(IClassFactoryImpl,iface);
746         return ++(This->ref);
747 }
748
749 static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface) {
750         ICOM_THIS(IClassFactoryImpl,iface);
751         /* static class, won't be  freed */
752         return --(This->ref);
753 }
754
755 static HRESULT WINAPI DSCF_CreateInstance(
756         LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
757 ) {
758         ICOM_THIS(IClassFactoryImpl,iface);
759
760         TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
761         if ( IsEqualGUID( &IID_IDirectSound, riid ) ||
762              IsEqualGUID( &IID_IDirectSound8, riid ) ) {
763                 /* FIXME: reuse already created dsound if present? */
764                 return DirectSoundCreate8(riid,(LPDIRECTSOUND8*)ppobj,pOuter);
765         }
766         return E_NOINTERFACE;
767 }
768
769 static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
770         ICOM_THIS(IClassFactoryImpl,iface);
771         FIXME("(%p)->(%d),stub!\n",This,dolock);
772         return S_OK;
773 }
774
775 static ICOM_VTABLE(IClassFactory) DSCF_Vtbl = {
776         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
777         DSCF_QueryInterface,
778         DSCF_AddRef,
779         DSCF_Release,
780         DSCF_CreateInstance,
781         DSCF_LockServer
782 };
783 static IClassFactoryImpl DSOUND_CF = {&DSCF_Vtbl, 1 };
784
785 /*******************************************************************************
786  * DllGetClassObject [DSOUND.5]
787  * Retrieves class object from a DLL object
788  *
789  * NOTES
790  *    Docs say returns STDAPI
791  *
792  * PARAMS
793  *    rclsid [I] CLSID for the class object
794  *    riid   [I] Reference to identifier of interface for class object
795  *    ppv    [O] Address of variable to receive interface pointer for riid
796  *
797  * RETURNS
798  *    Success: S_OK
799  *    Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
800  *             E_UNEXPECTED
801  */
802 DWORD WINAPI DSOUND_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
803 {
804     TRACE("(%p,%p,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
805     if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
806         *ppv = (LPVOID)&DSOUND_CF;
807         IClassFactory_AddRef((IClassFactory*)*ppv);
808     return S_OK;
809     }
810
811     FIXME("(%p,%p,%p): no interface found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
812     return CLASS_E_CLASSNOTAVAILABLE;
813 }
814
815
816 /*******************************************************************************
817  * DllCanUnloadNow [DSOUND.4]  Determines whether the DLL is in use.
818  *
819  * RETURNS
820  *    Success: S_OK
821  *    Failure: S_FALSE
822  */
823 DWORD WINAPI DSOUND_DllCanUnloadNow(void)
824 {
825     FIXME("(void): stub\n");
826     return S_FALSE;
827 }