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