Don't use old_spacing-1 if old_spacing already has the minimum value.
[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 "winternl.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
405         if (caps == NULL || caps->dwSize!=sizeof(*caps))
406                 return DSERR_INVALIDPARAM;
407
408         caps->dwFlags = This->drvcaps.dwFlags;
409         TRACE("(flags=0x%08lx)\n",caps->dwFlags);
410
411         /* FIXME: copy caps from This->drvcaps */
412         caps->dwMinSecondarySampleRate          = DSBFREQUENCY_MIN;
413         caps->dwMaxSecondarySampleRate          = DSBFREQUENCY_MAX;
414
415         caps->dwPrimaryBuffers                  = 1;
416
417         caps->dwMaxHwMixingAllBuffers           = 0;
418         caps->dwMaxHwMixingStaticBuffers        = 0;
419         caps->dwMaxHwMixingStreamingBuffers     = 0;
420
421         caps->dwFreeHwMixingAllBuffers          = 0;
422         caps->dwFreeHwMixingStaticBuffers       = 0;
423         caps->dwFreeHwMixingStreamingBuffers    = 0;
424
425         caps->dwMaxHw3DAllBuffers               = 0;
426         caps->dwMaxHw3DStaticBuffers            = 0;
427         caps->dwMaxHw3DStreamingBuffers         = 0;
428
429         caps->dwFreeHw3DAllBuffers              = 0;
430         caps->dwFreeHw3DStaticBuffers           = 0;
431         caps->dwFreeHw3DStreamingBuffers        = 0;
432
433         caps->dwTotalHwMemBytes                 = 0;
434
435         caps->dwFreeHwMemBytes                  = 0;
436
437         caps->dwMaxContigFreeHwMemBytes         = 0;
438
439         caps->dwUnlockTransferRateHwBuffers     = 4096; /* But we have none... */
440
441         caps->dwPlayCpuOverheadSwBuffers        = 1;    /* 1% */
442
443         return DS_OK;
444 }
445
446 static ULONG WINAPI IDirectSoundImpl_AddRef(LPDIRECTSOUND8 iface) {
447         ICOM_THIS(IDirectSoundImpl,iface);
448         return ++(This->ref);
449 }
450
451 static ULONG WINAPI IDirectSoundImpl_Release(LPDIRECTSOUND8 iface) {
452         ICOM_THIS(IDirectSoundImpl,iface);
453         TRACE("(%p), ref was %ld\n",This,This->ref);
454         if (!--(This->ref)) {
455                 UINT i;
456
457                 timeKillEvent(This->timerID);
458                 timeEndPeriod(DS_TIME_RES);
459
460                 if (This->buffers) {
461                         for( i=0;i<This->nrofbuffers;i++)
462                                 IDirectSoundBuffer8_Release((LPDIRECTSOUNDBUFFER8)This->buffers[i]);
463                 }
464
465                 DSOUND_PrimaryDestroy(This);
466
467                 RtlDeleteResource(&This->lock);
468                 DeleteCriticalSection(&This->mixlock);
469                 if (This->driver) {
470                         IDsDriver_Close(This->driver);
471                 }
472                 if (This->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) {
473                         waveOutClose(This->hwo);
474                 }
475                 if (This->driver)
476                         IDsDriver_Release(This->driver);
477
478                 HeapFree(GetProcessHeap(),0,This);
479                 dsound = NULL;
480                 return 0;
481         }
482         return This->ref;
483 }
484
485 static HRESULT WINAPI IDirectSoundImpl_SetSpeakerConfig(
486         LPDIRECTSOUND8 iface,DWORD config
487 ) {
488         ICOM_THIS(IDirectSoundImpl,iface);
489         FIXME("(%p,0x%08lx):stub\n",This,config);
490         return DS_OK;
491 }
492
493 static HRESULT WINAPI IDirectSoundImpl_QueryInterface(
494         LPDIRECTSOUND8 iface,REFIID riid,LPVOID *ppobj
495 ) {
496         ICOM_THIS(IDirectSoundImpl,iface);
497
498         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
499                 ERR("app requested IDirectSound3DListener on dsound object\n");
500                 *ppobj = NULL;
501                 return E_FAIL;
502         }
503
504         FIXME("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
505         return E_NOINTERFACE;
506 }
507
508 static HRESULT WINAPI IDirectSoundImpl_Compact(
509         LPDIRECTSOUND8 iface)
510 {
511         ICOM_THIS(IDirectSoundImpl,iface);
512         TRACE("(%p)\n", This);
513         return DS_OK;
514 }
515
516 static HRESULT WINAPI IDirectSoundImpl_GetSpeakerConfig(
517         LPDIRECTSOUND8 iface,
518         LPDWORD lpdwSpeakerConfig)
519 {
520         ICOM_THIS(IDirectSoundImpl,iface);
521         TRACE("(%p, %p)\n", This, lpdwSpeakerConfig);
522         *lpdwSpeakerConfig = DSSPEAKER_STEREO | (DSSPEAKER_GEOMETRY_NARROW << 16);
523         return DS_OK;
524 }
525
526 static HRESULT WINAPI IDirectSoundImpl_Initialize(
527         LPDIRECTSOUND8 iface,
528         LPCGUID lpcGuid)
529 {
530         ICOM_THIS(IDirectSoundImpl,iface);
531         TRACE("(%p, %p)\n", This, lpcGuid);
532         return DS_OK;
533 }
534
535 static HRESULT WINAPI IDirectSoundImpl_VerifyCertification(
536         LPDIRECTSOUND8 iface,
537         LPDWORD pdwCertified)
538 {
539         ICOM_THIS(IDirectSoundImpl,iface);
540         TRACE("(%p, %p)\n", This, pdwCertified);
541         *pdwCertified = DS_CERTIFIED;
542         return DS_OK;
543 }
544
545 static ICOM_VTABLE(IDirectSound8) dsvt =
546 {
547         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
548         IDirectSoundImpl_QueryInterface,
549         IDirectSoundImpl_AddRef,
550         IDirectSoundImpl_Release,
551         IDirectSoundImpl_CreateSoundBuffer,
552         IDirectSoundImpl_GetCaps,
553         IDirectSoundImpl_DuplicateSoundBuffer,
554         IDirectSoundImpl_SetCooperativeLevel,
555         IDirectSoundImpl_Compact,
556         IDirectSoundImpl_GetSpeakerConfig,
557         IDirectSoundImpl_SetSpeakerConfig,
558         IDirectSoundImpl_Initialize,
559         IDirectSoundImpl_VerifyCertification
560 };
561
562
563 /*******************************************************************************
564  *              DirectSoundCreate (DSOUND.1)
565  */
566 HRESULT WINAPI DirectSoundCreate8(REFGUID lpGUID,LPDIRECTSOUND8 *ppDS,IUnknown *pUnkOuter )
567 {
568         IDirectSoundImpl** ippDS=(IDirectSoundImpl**)ppDS;
569         PIDSDRIVER drv = NULL;
570         unsigned wod, wodn;
571         HRESULT err = DS_OK;
572
573         if (lpGUID)
574                 TRACE("(%p,%p,%p)\n",lpGUID,ippDS,pUnkOuter);
575         else
576                 TRACE("DirectSoundCreate (%p)\n", ippDS);
577
578         if (ippDS == NULL)
579                 return DSERR_INVALIDPARAM;
580
581         if (dsound) {
582                 IDirectSound_AddRef((LPDIRECTSOUND)dsound);
583                 *ippDS = dsound;
584                 return DS_OK;
585         }
586
587         /* Get dsound configuration */
588         setup_dsound_options();
589
590         /* Enumerate WINMM audio devices and find the one we want */
591         wodn = waveOutGetNumDevs();
592         if (!wodn) return DSERR_NODRIVER;
593
594         /* FIXME: How do we find the GUID of an audio device? */
595         wod = 0;  /* start at the first audio device */
596
597         /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
598         waveOutMessage((HWAVEOUT)wod, DRV_QUERYDSOUNDIFACE, (DWORD)&drv, 0);
599
600         /* Allocate memory */
601         *ippDS = (IDirectSoundImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundImpl));
602         if (*ippDS == NULL)
603                 return DSERR_OUTOFMEMORY;
604
605         ICOM_VTBL(*ippDS)       = &dsvt;
606         (*ippDS)->ref           = 1;
607
608         (*ippDS)->driver        = drv;
609         (*ippDS)->priolevel     = DSSCL_NORMAL;
610         (*ippDS)->fraglen       = 0;
611         (*ippDS)->hwbuf         = NULL;
612         (*ippDS)->buffer        = NULL;
613         (*ippDS)->buflen        = 0;
614         (*ippDS)->writelead     = 0;
615         (*ippDS)->state         = STATE_STOPPED;
616         (*ippDS)->nrofbuffers   = 0;
617         (*ippDS)->buffers       = NULL;
618 /*      (*ippDS)->primary       = NULL; */
619         (*ippDS)->listener      = NULL;
620
621         (*ippDS)->prebuf        = ds_snd_queue_max;
622
623         /* Get driver description */
624         if (drv) {
625                 IDsDriver_GetDriverDesc(drv,&((*ippDS)->drvdesc));
626         } else {
627                 /* if no DirectSound interface available, use WINMM API instead */
628                 (*ippDS)->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
629                 (*ippDS)->drvdesc.dnDevNode = wod; /* FIXME? */
630         }
631
632         /* Set default wave format (may need it for waveOutOpen) */
633         (*ippDS)->wfx.wFormatTag        = WAVE_FORMAT_PCM;
634         /* We rely on the sound driver to return the actual sound format of 
635          * the device if it does not support 22050x8x2 and is given the 
636          * WAVE_DIRECTSOUND flag.
637          */
638         (*ippDS)->wfx.nSamplesPerSec = 22050;
639         (*ippDS)->wfx.wBitsPerSample = 8;
640         (*ippDS)->wfx.nChannels = 2;
641         (*ippDS)->wfx.nBlockAlign = (*ippDS)->wfx.wBitsPerSample * (*ippDS)->wfx.nChannels / 8;
642         (*ippDS)->wfx.nAvgBytesPerSec = (*ippDS)->wfx.nSamplesPerSec * (*ippDS)->wfx.nBlockAlign;
643         (*ippDS)->wfx.cbSize = 0;
644
645         /* If the driver requests being opened through MMSYSTEM
646          * (which is recommended by the DDK), it is supposed to happen
647          * before the DirectSound interface is opened */
648         if ((*ippDS)->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
649         {
650                 /* FIXME: is this right? */
651                 (*ippDS)->drvdesc.dnDevNode = 0;
652                 err = DSERR_ALLOCATED;
653
654                 /* if this device is busy try the next one */
655                 while((err == DSERR_ALLOCATED) &&
656                         ((*ippDS)->drvdesc.dnDevNode < wodn))
657                 {
658                   err = mmErr(waveOutOpen(&((*ippDS)->hwo),
659                                           (*ippDS)->drvdesc.dnDevNode, &((*ippDS)->wfx),
660                                           (DWORD)DSOUND_callback, (DWORD)(*ippDS),
661                                           CALLBACK_FUNCTION | WAVE_DIRECTSOUND));
662                   (*ippDS)->drvdesc.dnDevNode++; /* next wave device */
663                 }
664
665                 (*ippDS)->drvdesc.dnDevNode--; /* take away last increment */
666         }
667
668         if (drv && (err == DS_OK))
669                 err = IDsDriver_Open(drv);
670
671         /* FIXME: do we want to handle a temporarily busy device? */
672         if (err != DS_OK) {
673                 HeapFree(GetProcessHeap(),0,*ippDS);
674                 *ippDS = NULL;
675                 return err;
676         }
677
678         /* the driver is now open, so it's now allowed to call GetCaps */
679         if (drv) {
680                 IDsDriver_GetCaps(drv,&((*ippDS)->drvcaps));
681         } else {
682                 /* FIXME: We should check the device capabilities */
683                 (*ippDS)->drvcaps.dwFlags =
684                         DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO;
685                 if (ds_emuldriver)
686                     (*ippDS)->drvcaps.dwFlags |= DSCAPS_EMULDRIVER;
687         }
688
689         DSOUND_RecalcVolPan(&((*ippDS)->volpan));
690
691         InitializeCriticalSection(&((*ippDS)->mixlock));
692         RtlInitializeResource(&((*ippDS)->lock));
693
694         if (!dsound) {
695                 dsound = (*ippDS);
696                 DSOUND_PrimaryCreate(dsound);
697                 timeBeginPeriod(DS_TIME_RES);
698                 dsound->timerID = timeSetEvent(DS_TIME_DEL, DS_TIME_RES, DSOUND_timer,
699                                                (DWORD)dsound, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
700         }
701         return DS_OK;
702 }
703
704
705 /*******************************************************************************
706  * DirectSound ClassFactory
707  */
708 typedef struct
709 {
710     /* IUnknown fields */
711     ICOM_VFIELD(IClassFactory);
712     DWORD                       ref;
713 } IClassFactoryImpl;
714
715 static HRESULT WINAPI
716 DSCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
717         ICOM_THIS(IClassFactoryImpl,iface);
718
719         FIXME("(%p)->(%s,%p),stub!\n",This,debugstr_guid(riid),ppobj);
720         return E_NOINTERFACE;
721 }
722
723 static ULONG WINAPI
724 DSCF_AddRef(LPCLASSFACTORY iface) {
725         ICOM_THIS(IClassFactoryImpl,iface);
726         return ++(This->ref);
727 }
728
729 static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface) {
730         ICOM_THIS(IClassFactoryImpl,iface);
731         /* static class, won't be  freed */
732         return --(This->ref);
733 }
734
735 static HRESULT WINAPI DSCF_CreateInstance(
736         LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
737 ) {
738         ICOM_THIS(IClassFactoryImpl,iface);
739
740         TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
741         if ( IsEqualGUID( &IID_IDirectSound, riid ) ||
742              IsEqualGUID( &IID_IDirectSound8, riid ) ) {
743                 /* FIXME: reuse already created dsound if present? */
744                 return DirectSoundCreate8(riid,(LPDIRECTSOUND8*)ppobj,pOuter);
745         }
746         return E_NOINTERFACE;
747 }
748
749 static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
750         ICOM_THIS(IClassFactoryImpl,iface);
751         FIXME("(%p)->(%d),stub!\n",This,dolock);
752         return S_OK;
753 }
754
755 static ICOM_VTABLE(IClassFactory) DSCF_Vtbl = {
756         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
757         DSCF_QueryInterface,
758         DSCF_AddRef,
759         DSCF_Release,
760         DSCF_CreateInstance,
761         DSCF_LockServer
762 };
763 static IClassFactoryImpl DSOUND_CF = {&DSCF_Vtbl, 1 };
764
765 /*******************************************************************************
766  * DllGetClassObject [DSOUND.5]
767  * Retrieves class object from a DLL object
768  *
769  * NOTES
770  *    Docs say returns STDAPI
771  *
772  * PARAMS
773  *    rclsid [I] CLSID for the class object
774  *    riid   [I] Reference to identifier of interface for class object
775  *    ppv    [O] Address of variable to receive interface pointer for riid
776  *
777  * RETURNS
778  *    Success: S_OK
779  *    Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
780  *             E_UNEXPECTED
781  */
782 DWORD WINAPI DSOUND_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
783 {
784     TRACE("(%p,%p,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
785     if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
786         *ppv = (LPVOID)&DSOUND_CF;
787         IClassFactory_AddRef((IClassFactory*)*ppv);
788     return S_OK;
789     }
790
791     FIXME("(%p,%p,%p): no interface found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
792     return CLASS_E_CLASSNOTAVAILABLE;
793 }
794
795
796 /*******************************************************************************
797  * DllCanUnloadNow [DSOUND.4]  Determines whether the DLL is in use.
798  *
799  * RETURNS
800  *    Success: S_OK
801  *    Failure: S_FALSE
802  */
803 DWORD WINAPI DSOUND_DllCanUnloadNow(void)
804 {
805     FIXME("(void): stub\n");
806     return S_FALSE;
807 }