Added regedit unit test, a couple minor changes to regedit.
[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 (GetModuleFileName16( GetCurrentTask(), buffer, MAX_PATH ) ||
158         GetModuleFileNameA( 0, buffer, MAX_PATH ))
159     {
160         HKEY tmpkey;
161
162         if (!RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\AppDefaults", &tmpkey ))
163         {
164            char appname[MAX_PATH+16];
165            char *p = strrchr( buffer, '\\' );
166            if (p!=NULL) {
167                    appname[MAX_PATH]='\0';
168                    strncpy(appname,p+1,MAX_PATH);
169                    strcat(appname,"\\dsound");
170                    TRACE("appname = [%s] \n",appname);
171                    if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
172                        RegCloseKey( tmpkey );
173            }
174         }
175     }
176
177     /* get options */
178
179     if (!get_config_key( hkey, appkey, "EmulDriver", buffer, MAX_PATH ))
180         ds_emuldriver = strcmp(buffer, "N");
181
182     if (!get_config_key( hkey, appkey, "HELmargin", buffer, MAX_PATH ))
183         ds_hel_margin = atoi(buffer);
184
185     if (!get_config_key( hkey, appkey, "HELqueue", buffer, MAX_PATH ))
186         ds_hel_queue = atoi(buffer);
187
188     if (!get_config_key( hkey, appkey, "SndQueueMax", buffer, MAX_PATH ))
189         ds_snd_queue_max = atoi(buffer);
190
191     if (!get_config_key( hkey, appkey, "SndQueueMin", buffer, MAX_PATH ))
192         ds_snd_queue_min = atoi(buffer);
193
194     if (appkey) RegCloseKey( appkey );
195     RegCloseKey( hkey );
196
197     if (ds_emuldriver != DS_EMULDRIVER )
198        WARN("ds_emuldriver = %d (default=%d)\n",ds_emuldriver, DS_EMULDRIVER);
199     if (ds_hel_margin != DS_HEL_MARGIN )
200        WARN("ds_hel_margin = %d (default=%d)\n",ds_hel_margin, DS_HEL_MARGIN );
201     if (ds_hel_queue != DS_HEL_QUEUE )
202        WARN("ds_hel_queue = %d (default=%d)\n",ds_hel_queue, DS_HEL_QUEUE );
203     if (ds_snd_queue_max != DS_SND_QUEUE_MAX)
204        WARN("ds_snd_queue_max = %d (default=%d)\n",ds_snd_queue_max ,DS_SND_QUEUE_MAX);
205     if (ds_snd_queue_min != DS_SND_QUEUE_MIN)
206        WARN("ds_snd_queue_min = %d (default=%d)\n",ds_snd_queue_min ,DS_SND_QUEUE_MIN);
207
208 }
209
210
211
212 /***************************************************************************
213  * DirectSoundEnumerateA [DSOUND.2]
214  *
215  * Enumerate all DirectSound drivers installed in the system
216  *
217  * RETURNS
218  *    Success: DS_OK
219  *    Failure: DSERR_INVALIDPARAM
220  */
221 HRESULT WINAPI DirectSoundEnumerateA(
222         LPDSENUMCALLBACKA lpDSEnumCallback,
223         LPVOID lpContext)
224 {
225         WAVEOUTCAPSA wcaps;
226         unsigned devs, wod;
227
228         TRACE("lpDSEnumCallback = %p, lpContext = %p\n",
229                 lpDSEnumCallback, lpContext);
230
231         devs = waveOutGetNumDevs();
232         for (wod = 0; wod < devs; ++wod) {
233                 waveOutGetDevCapsA(wod, &wcaps, sizeof(wcaps));
234                 if (wcaps.dwSupport & WAVECAPS_DIRECTSOUND) {
235                         TRACE("- Device %u supports DirectSound\n", wod);
236                         enumerate_devices(lpDSEnumCallback, lpContext);
237                         return DS_OK;
238                 }
239         }
240         return DS_OK;
241 }
242
243 /***************************************************************************
244  * DirectSoundEnumerateW [DSOUND.3]
245  *
246  * Enumerate all DirectSound drivers installed in the system
247  *
248  * RETURNS
249  *    Success: DS_OK
250  *    Failure: DSERR_INVALIDPARAM
251  */
252 HRESULT WINAPI DirectSoundEnumerateW(
253         LPDSENUMCALLBACKW lpDSEnumCallback,
254         LPVOID lpContext )
255 {
256         FIXME("lpDSEnumCallback = %p, lpContext = %p: stub\n",
257                 lpDSEnumCallback, lpContext);
258
259         return DS_OK;
260 }
261
262
263 static void _dump_DSBCAPS(DWORD xmask) {
264         struct {
265                 DWORD   mask;
266                 char    *name;
267         } flags[] = {
268 #define FE(x) { x, #x },
269                 FE(DSBCAPS_PRIMARYBUFFER)
270                 FE(DSBCAPS_STATIC)
271                 FE(DSBCAPS_LOCHARDWARE)
272                 FE(DSBCAPS_LOCSOFTWARE)
273                 FE(DSBCAPS_CTRL3D)
274                 FE(DSBCAPS_CTRLFREQUENCY)
275                 FE(DSBCAPS_CTRLPAN)
276                 FE(DSBCAPS_CTRLVOLUME)
277                 FE(DSBCAPS_CTRLPOSITIONNOTIFY)
278                 FE(DSBCAPS_CTRLDEFAULT)
279                 FE(DSBCAPS_CTRLALL)
280                 FE(DSBCAPS_STICKYFOCUS)
281                 FE(DSBCAPS_GLOBALFOCUS)
282                 FE(DSBCAPS_GETCURRENTPOSITION2)
283                 FE(DSBCAPS_MUTE3DATMAXDISTANCE)
284 #undef FE
285         };
286         int     i;
287
288         for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
289                 if ((flags[i].mask & xmask) == flags[i].mask)
290                         DPRINTF("%s ",flags[i].name);
291 }
292
293 /*******************************************************************************
294  *              IDirectSound
295  */
296
297 static HRESULT WINAPI IDirectSoundImpl_SetCooperativeLevel(
298         LPDIRECTSOUND8 iface,HWND hwnd,DWORD level
299 ) {
300         ICOM_THIS(IDirectSoundImpl,iface);
301
302         FIXME("(%p,%08lx,%ld):stub\n",This,(DWORD)hwnd,level);
303
304         This->priolevel = level;
305
306         return DS_OK;
307 }
308
309 static HRESULT WINAPI IDirectSoundImpl_CreateSoundBuffer(
310         LPDIRECTSOUND8 iface,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER8 ppdsb,LPUNKNOWN lpunk
311 ) {
312         ICOM_THIS(IDirectSoundImpl,iface);
313         LPWAVEFORMATEX  wfex;
314
315         TRACE("(%p,%p,%p,%p)\n",This,dsbd,ppdsb,lpunk);
316
317         if ((This == NULL) || (dsbd == NULL) || (ppdsb == NULL))
318                 return DSERR_INVALIDPARAM;
319
320         if (TRACE_ON(dsound)) {
321                 TRACE("(structsize=%ld)\n",dsbd->dwSize);
322                 TRACE("(flags=0x%08lx:\n",dsbd->dwFlags);
323                 _dump_DSBCAPS(dsbd->dwFlags);
324                 DPRINTF(")\n");
325                 TRACE("(bufferbytes=%ld)\n",dsbd->dwBufferBytes);
326                 TRACE("(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
327         }
328
329         wfex = dsbd->lpwfxFormat;
330
331         if (wfex)
332                 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
333                    "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
334                    wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
335                    wfex->nAvgBytesPerSec, wfex->nBlockAlign,
336                    wfex->wBitsPerSample, wfex->cbSize);
337
338         if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)
339                 return PrimaryBuffer_Create(This, (PrimaryBufferImpl**)ppdsb, dsbd);
340         else
341                 return SecondaryBuffer_Create(This, (IDirectSoundBufferImpl**)ppdsb, dsbd);
342 }
343
344 static HRESULT WINAPI IDirectSoundImpl_DuplicateSoundBuffer(
345         LPDIRECTSOUND8 iface,LPDIRECTSOUNDBUFFER8 pdsb,LPLPDIRECTSOUNDBUFFER8 ppdsb
346 ) {
347         ICOM_THIS(IDirectSoundImpl,iface);
348         IDirectSoundBufferImpl* ipdsb=(IDirectSoundBufferImpl*)pdsb;
349         IDirectSoundBufferImpl** ippdsb=(IDirectSoundBufferImpl**)ppdsb;
350         TRACE("(%p,%p,%p)\n",This,ipdsb,ippdsb);
351
352         if (ipdsb->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
353                 ERR("trying to duplicate primary buffer\n");
354                 return DSERR_INVALIDCALL;
355         }
356
357         if (ipdsb->hwbuf) {
358                 FIXME("need to duplicate hardware buffer\n");
359         }
360
361         if (ipdsb->dsbd.dwFlags & DSBCAPS_CTRL3D) {
362                 FIXME("need to duplicate 3D buffer\n");
363         }
364
365         *ippdsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBufferImpl));
366
367         IDirectSoundBuffer8_AddRef(pdsb);
368         memcpy(*ippdsb, ipdsb, sizeof(IDirectSoundBufferImpl));
369         (*ippdsb)->ref = 1;
370         (*ippdsb)->state = STATE_STOPPED;
371         (*ippdsb)->playpos = 0;
372         (*ippdsb)->buf_mixpos = 0;
373         (*ippdsb)->dsound = This;
374         (*ippdsb)->parent = ipdsb;
375         (*ippdsb)->hwbuf = NULL;
376         (*ippdsb)->ds3db = NULL; /* FIXME? */
377         (*ippdsb)->iks = NULL; /* FIXME? */
378         memcpy(&((*ippdsb)->wfx), &(ipdsb->wfx), sizeof((*ippdsb)->wfx));
379         InitializeCriticalSection(&(*ippdsb)->lock);
380         /* register buffer */
381         RtlAcquireResourceExclusive(&(This->lock), TRUE);
382         {
383                 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl**)*(This->nrofbuffers+1));
384                 if (newbuffers) {
385                         This->buffers = newbuffers;
386                         This->buffers[This->nrofbuffers] = *ippdsb;
387                         This->nrofbuffers++;
388                         TRACE("buffer count is now %d\n", This->nrofbuffers);
389                 } else {
390                         ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
391                         /* FIXME: release buffer */
392                 }
393         }
394         RtlReleaseResource(&(This->lock));
395         IDirectSound_AddRef(iface);
396         return DS_OK;
397 }
398
399
400 static HRESULT WINAPI IDirectSoundImpl_GetCaps(LPDIRECTSOUND8 iface,LPDSCAPS caps) {
401         ICOM_THIS(IDirectSoundImpl,iface);
402         TRACE("(%p,%p)\n",This,caps);
403         TRACE("(flags=0x%08lx)\n",caps->dwFlags);
404
405         if (caps == NULL)
406                 return DSERR_INVALIDPARAM;
407
408         /* We should check this value, not set it. See Inside DirectX, p215. */
409         caps->dwSize = sizeof(*caps);
410
411         caps->dwFlags = This->drvcaps.dwFlags;
412
413         /* FIXME: copy caps from This->drvcaps */
414         caps->dwMinSecondarySampleRate          = DSBFREQUENCY_MIN;
415         caps->dwMaxSecondarySampleRate          = DSBFREQUENCY_MAX;
416
417         caps->dwPrimaryBuffers                  = 1;
418
419         caps->dwMaxHwMixingAllBuffers           = 0;
420         caps->dwMaxHwMixingStaticBuffers        = 0;
421         caps->dwMaxHwMixingStreamingBuffers     = 0;
422
423         caps->dwFreeHwMixingAllBuffers          = 0;
424         caps->dwFreeHwMixingStaticBuffers       = 0;
425         caps->dwFreeHwMixingStreamingBuffers    = 0;
426
427         caps->dwMaxHw3DAllBuffers               = 0;
428         caps->dwMaxHw3DStaticBuffers            = 0;
429         caps->dwMaxHw3DStreamingBuffers         = 0;
430
431         caps->dwFreeHw3DAllBuffers              = 0;
432         caps->dwFreeHw3DStaticBuffers           = 0;
433         caps->dwFreeHw3DStreamingBuffers        = 0;
434
435         caps->dwTotalHwMemBytes                 = 0;
436
437         caps->dwFreeHwMemBytes                  = 0;
438
439         caps->dwMaxContigFreeHwMemBytes         = 0;
440
441         caps->dwUnlockTransferRateHwBuffers     = 4096; /* But we have none... */
442
443         caps->dwPlayCpuOverheadSwBuffers        = 1;    /* 1% */
444
445         return DS_OK;
446 }
447
448 static ULONG WINAPI IDirectSoundImpl_AddRef(LPDIRECTSOUND8 iface) {
449         ICOM_THIS(IDirectSoundImpl,iface);
450         return ++(This->ref);
451 }
452
453 static ULONG WINAPI IDirectSoundImpl_Release(LPDIRECTSOUND8 iface) {
454         ICOM_THIS(IDirectSoundImpl,iface);
455         TRACE("(%p), ref was %ld\n",This,This->ref);
456         if (!--(This->ref)) {
457                 UINT i;
458
459                 timeKillEvent(This->timerID);
460                 timeEndPeriod(DS_TIME_RES);
461
462                 if (This->buffers) {
463                         for( i=0;i<This->nrofbuffers;i++)
464                                 IDirectSoundBuffer8_Release((LPDIRECTSOUNDBUFFER8)This->buffers[i]);
465                 }
466
467                 DSOUND_PrimaryDestroy(This);
468
469                 RtlDeleteResource(&This->lock);
470                 DeleteCriticalSection(&This->mixlock);
471                 if (This->driver) {
472                         IDsDriver_Close(This->driver);
473                 } else {
474                         unsigned c;
475                         for (c=0; c<DS_HEL_FRAGS; c++)
476                                 HeapFree(GetProcessHeap(),0,This->pwave[c]);
477                 }
478                 if (This->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) {
479                         waveOutClose(This->hwo);
480                 }
481                 if (This->driver)
482                         IDsDriver_Release(This->driver);
483
484                 HeapFree(GetProcessHeap(),0,This);
485                 dsound = NULL;
486                 return 0;
487         }
488         return This->ref;
489 }
490
491 static HRESULT WINAPI IDirectSoundImpl_SetSpeakerConfig(
492         LPDIRECTSOUND8 iface,DWORD config
493 ) {
494         ICOM_THIS(IDirectSoundImpl,iface);
495         FIXME("(%p,0x%08lx):stub\n",This,config);
496         return DS_OK;
497 }
498
499 static HRESULT WINAPI IDirectSoundImpl_QueryInterface(
500         LPDIRECTSOUND8 iface,REFIID riid,LPVOID *ppobj
501 ) {
502         ICOM_THIS(IDirectSoundImpl,iface);
503
504         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
505                 ERR("app requested IDirectSound3DListener on dsound object\n");
506                 *ppobj = NULL;
507                 return E_FAIL;
508         }
509
510         FIXME("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
511         return E_NOINTERFACE;
512 }
513
514 static HRESULT WINAPI IDirectSoundImpl_Compact(
515         LPDIRECTSOUND8 iface)
516 {
517         ICOM_THIS(IDirectSoundImpl,iface);
518         TRACE("(%p)\n", This);
519         return DS_OK;
520 }
521
522 static HRESULT WINAPI IDirectSoundImpl_GetSpeakerConfig(
523         LPDIRECTSOUND8 iface,
524         LPDWORD lpdwSpeakerConfig)
525 {
526         ICOM_THIS(IDirectSoundImpl,iface);
527         TRACE("(%p, %p)\n", This, lpdwSpeakerConfig);
528         *lpdwSpeakerConfig = DSSPEAKER_STEREO | (DSSPEAKER_GEOMETRY_NARROW << 16);
529         return DS_OK;
530 }
531
532 static HRESULT WINAPI IDirectSoundImpl_Initialize(
533         LPDIRECTSOUND8 iface,
534         LPCGUID lpcGuid)
535 {
536         ICOM_THIS(IDirectSoundImpl,iface);
537         TRACE("(%p, %p)\n", This, lpcGuid);
538         return DS_OK;
539 }
540
541 static HRESULT WINAPI IDirectSoundImpl_VerifyCertification(
542         LPDIRECTSOUND8 iface,
543         LPDWORD pdwCertified)
544 {
545         ICOM_THIS(IDirectSoundImpl,iface);
546         TRACE("(%p, %p)\n", This, pdwCertified);
547         *pdwCertified = DS_CERTIFIED;
548         return DS_OK;
549 }
550
551 static ICOM_VTABLE(IDirectSound8) dsvt =
552 {
553         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
554         IDirectSoundImpl_QueryInterface,
555         IDirectSoundImpl_AddRef,
556         IDirectSoundImpl_Release,
557         IDirectSoundImpl_CreateSoundBuffer,
558         IDirectSoundImpl_GetCaps,
559         IDirectSoundImpl_DuplicateSoundBuffer,
560         IDirectSoundImpl_SetCooperativeLevel,
561         IDirectSoundImpl_Compact,
562         IDirectSoundImpl_GetSpeakerConfig,
563         IDirectSoundImpl_SetSpeakerConfig,
564         IDirectSoundImpl_Initialize,
565         IDirectSoundImpl_VerifyCertification
566 };
567
568
569 /*******************************************************************************
570  *              DirectSoundCreate (DSOUND.1)
571  */
572 HRESULT WINAPI DirectSoundCreate8(REFGUID lpGUID,LPDIRECTSOUND8 *ppDS,IUnknown *pUnkOuter )
573 {
574         IDirectSoundImpl** ippDS=(IDirectSoundImpl**)ppDS;
575         PIDSDRIVER drv = NULL;
576         WAVEOUTCAPSA wcaps;
577         unsigned wod, wodn;
578         HRESULT err = DS_OK;
579
580         if (lpGUID)
581                 TRACE("(%p,%p,%p)\n",lpGUID,ippDS,pUnkOuter);
582         else
583                 TRACE("DirectSoundCreate (%p)\n", ippDS);
584
585         if (ippDS == NULL)
586                 return DSERR_INVALIDPARAM;
587
588         if (dsound) {
589                 IDirectSound_AddRef((LPDIRECTSOUND)dsound);
590                 *ippDS = dsound;
591                 return DS_OK;
592         }
593
594         /* Get dsound configuration */
595         setup_dsound_options();
596
597         /* Enumerate WINMM audio devices and find the one we want */
598         wodn = waveOutGetNumDevs();
599         if (!wodn) return DSERR_NODRIVER;
600
601         /* FIXME: How do we find the GUID of an audio device? */
602         wod = 0;  /* start at the first audio device */
603
604         /* Get output device caps */
605         waveOutGetDevCapsA(wod, &wcaps, sizeof(wcaps));
606         /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
607         waveOutMessage(wod, DRV_QUERYDSOUNDIFACE, (DWORD)&drv, 0);
608
609         /* Allocate memory */
610         *ippDS = (IDirectSoundImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundImpl));
611         if (*ippDS == NULL)
612                 return DSERR_OUTOFMEMORY;
613
614         ICOM_VTBL(*ippDS)       = &dsvt;
615         (*ippDS)->ref           = 1;
616
617         (*ippDS)->driver        = drv;
618         (*ippDS)->priolevel     = DSSCL_NORMAL;
619         (*ippDS)->fraglen       = 0;
620         (*ippDS)->hwbuf         = NULL;
621         (*ippDS)->buffer        = NULL;
622         (*ippDS)->buflen        = 0;
623         (*ippDS)->writelead     = 0;
624         (*ippDS)->state         = STATE_STOPPED;
625         (*ippDS)->nrofbuffers   = 0;
626         (*ippDS)->buffers       = NULL;
627 /*      (*ippDS)->primary       = NULL; */
628         (*ippDS)->listener      = NULL;
629
630         (*ippDS)->prebuf        = ds_snd_queue_max;
631
632         /* Get driver description */
633         if (drv) {
634                 IDsDriver_GetDriverDesc(drv,&((*ippDS)->drvdesc));
635         } else {
636                 /* if no DirectSound interface available, use WINMM API instead */
637                 (*ippDS)->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
638                 (*ippDS)->drvdesc.dnDevNode = wod; /* FIXME? */
639         }
640
641         /* Set default wave format (may need it for waveOutOpen) */
642         (*ippDS)->wfx.wFormatTag        = WAVE_FORMAT_PCM;
643         (*ippDS)->wfx.nChannels         = 2;
644         (*ippDS)->wfx.nSamplesPerSec    = 22050;
645         (*ippDS)->wfx.nAvgBytesPerSec   = 44100;
646         (*ippDS)->wfx.nBlockAlign       = 2;
647         (*ippDS)->wfx.wBitsPerSample    = 8;
648
649         /* If the driver requests being opened through MMSYSTEM
650          * (which is recommended by the DDK), it is supposed to happen
651          * before the DirectSound interface is opened */
652         if ((*ippDS)->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
653         {
654                 /* FIXME: is this right? */
655                 (*ippDS)->drvdesc.dnDevNode = 0;
656                 err = DSERR_ALLOCATED;
657
658                 /* if this device is busy try the next one */
659                 while((err == DSERR_ALLOCATED) &&
660                         ((*ippDS)->drvdesc.dnDevNode < wodn))
661                 {
662                   err = mmErr(waveOutOpen(&((*ippDS)->hwo),
663                                           (*ippDS)->drvdesc.dnDevNode, &((*ippDS)->wfx),
664                                           (DWORD)DSOUND_callback, (DWORD)(*ippDS),
665                                           CALLBACK_FUNCTION | WAVE_DIRECTSOUND));
666                   (*ippDS)->drvdesc.dnDevNode++; /* next wave device */
667                 }
668
669                 (*ippDS)->drvdesc.dnDevNode--; /* take away last increment */
670         }
671
672         if (drv && (err == DS_OK))
673                 err = IDsDriver_Open(drv);
674
675         /* FIXME: do we want to handle a temporarily busy device? */
676         if (err != DS_OK) {
677                 HeapFree(GetProcessHeap(),0,*ippDS);
678                 *ippDS = NULL;
679                 return err;
680         }
681
682         /* the driver is now open, so it's now allowed to call GetCaps */
683         if (drv) {
684                 IDsDriver_GetCaps(drv,&((*ippDS)->drvcaps));
685         } else {
686                 unsigned c;
687
688                 /* FIXME: look at wcaps */
689                 (*ippDS)->drvcaps.dwFlags =
690                         DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO;
691                 if (ds_emuldriver)
692                     (*ippDS)->drvcaps.dwFlags |= DSCAPS_EMULDRIVER;
693
694                 /* Allocate memory for HEL buffer headers */
695                 for (c=0; c<DS_HEL_FRAGS; c++) {
696                         (*ippDS)->pwave[c] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEHDR));
697                         if (!(*ippDS)->pwave[c]) {
698                                 /* Argh, out of memory */
699                                 while (c--) {
700                                         HeapFree(GetProcessHeap(),0,(*ippDS)->pwave[c]);
701                                         waveOutClose((*ippDS)->hwo);
702                                         HeapFree(GetProcessHeap(),0,*ippDS);
703                                         *ippDS = NULL;
704                                         return DSERR_OUTOFMEMORY;
705                                 }
706                         }
707                 }
708         }
709
710         DSOUND_RecalcVolPan(&((*ippDS)->volpan));
711
712         InitializeCriticalSection(&((*ippDS)->mixlock));
713         RtlInitializeResource(&((*ippDS)->lock));
714
715         if (!dsound) {
716                 dsound = (*ippDS);
717                 DSOUND_PrimaryCreate(dsound);
718                 timeBeginPeriod(DS_TIME_RES);
719                 dsound->timerID = timeSetEvent(DS_TIME_DEL, DS_TIME_RES, DSOUND_timer,
720                                                (DWORD)dsound, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
721         }
722         return DS_OK;
723 }
724
725
726 /*******************************************************************************
727  * DirectSound ClassFactory
728  */
729 typedef struct
730 {
731     /* IUnknown fields */
732     ICOM_VFIELD(IClassFactory);
733     DWORD                       ref;
734 } IClassFactoryImpl;
735
736 static HRESULT WINAPI
737 DSCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
738         ICOM_THIS(IClassFactoryImpl,iface);
739
740         FIXME("(%p)->(%s,%p),stub!\n",This,debugstr_guid(riid),ppobj);
741         return E_NOINTERFACE;
742 }
743
744 static ULONG WINAPI
745 DSCF_AddRef(LPCLASSFACTORY iface) {
746         ICOM_THIS(IClassFactoryImpl,iface);
747         return ++(This->ref);
748 }
749
750 static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface) {
751         ICOM_THIS(IClassFactoryImpl,iface);
752         /* static class, won't be  freed */
753         return --(This->ref);
754 }
755
756 static HRESULT WINAPI DSCF_CreateInstance(
757         LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
758 ) {
759         ICOM_THIS(IClassFactoryImpl,iface);
760
761         TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
762         if ( IsEqualGUID( &IID_IDirectSound, riid ) ||
763              IsEqualGUID( &IID_IDirectSound8, riid ) ) {
764                 /* FIXME: reuse already created dsound if present? */
765                 return DirectSoundCreate8(riid,(LPDIRECTSOUND8*)ppobj,pOuter);
766         }
767         return E_NOINTERFACE;
768 }
769
770 static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
771         ICOM_THIS(IClassFactoryImpl,iface);
772         FIXME("(%p)->(%d),stub!\n",This,dolock);
773         return S_OK;
774 }
775
776 static ICOM_VTABLE(IClassFactory) DSCF_Vtbl = {
777         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
778         DSCF_QueryInterface,
779         DSCF_AddRef,
780         DSCF_Release,
781         DSCF_CreateInstance,
782         DSCF_LockServer
783 };
784 static IClassFactoryImpl DSOUND_CF = {&DSCF_Vtbl, 1 };
785
786 /*******************************************************************************
787  * DllGetClassObject [DSOUND.5]
788  * Retrieves class object from a DLL object
789  *
790  * NOTES
791  *    Docs say returns STDAPI
792  *
793  * PARAMS
794  *    rclsid [I] CLSID for the class object
795  *    riid   [I] Reference to identifier of interface for class object
796  *    ppv    [O] Address of variable to receive interface pointer for riid
797  *
798  * RETURNS
799  *    Success: S_OK
800  *    Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
801  *             E_UNEXPECTED
802  */
803 DWORD WINAPI DSOUND_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
804 {
805     TRACE("(%p,%p,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
806     if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
807         *ppv = (LPVOID)&DSOUND_CF;
808         IClassFactory_AddRef((IClassFactory*)*ppv);
809     return S_OK;
810     }
811
812     FIXME("(%p,%p,%p): no interface found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
813     return CLASS_E_CLASSNOTAVAILABLE;
814 }
815
816
817 /*******************************************************************************
818  * DllCanUnloadNow [DSOUND.4]  Determines whether the DLL is in use.
819  *
820  * RETURNS
821  *    Success: S_OK
822  *    Failure: S_FALSE
823  */
824 DWORD WINAPI DSOUND_DllCanUnloadNow(void)
825 {
826     FIXME("(void): stub\n");
827     return S_FALSE;
828 }