Use environment variables instead of config file entries to specify
[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 <stdarg.h>
44 #include <stdio.h>
45 #include <sys/types.h>
46 #include <sys/fcntl.h>
47 #ifdef HAVE_UNISTD_H
48 # include <unistd.h>
49 #endif
50 #include <stdlib.h>
51 #include <string.h>
52 #include <math.h>
53
54 #define NONAMELESSSTRUCT
55 #define NONAMELESSUNION
56 #include "windef.h"
57 #include "winbase.h"
58 #include "winreg.h"
59 #include "winuser.h"
60 #include "wingdi.h"
61 #include "winuser.h"
62 #include "winerror.h"
63 #include "mmsystem.h"
64 #include "winternl.h"
65 #include "mmddk.h"
66 #include "wine/windef16.h"
67 #include "wine/winbase16.h"
68 #include "wine/debug.h"
69 #include "dsound.h"
70 #include "dsdriver.h"
71 #include "dsound_private.h"
72 #include "dsconf.h"
73
74 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
75
76 /* these are eligible for tuning... they must be high on slow machines... */
77 /* some stuff may get more responsive with lower values though... */
78 #define DS_EMULDRIVER 0 /* some games (Quake 2, UT) refuse to accept
79                                 emulated dsound devices. set to 0 ! */
80 #define DS_HEL_MARGIN 5 /* HEL only: number of waveOut fragments ahead to mix in new buffers
81                          * (keep this close or equal to DS_HEL_QUEUE for best results) */
82 #define DS_HEL_QUEUE  5 /* HEL only: number of waveOut fragments ahead to queue to driver
83                          * (this will affect HEL sound reliability and latency) */
84
85 #define DS_SND_QUEUE_MAX 28 /* max number of fragments to prebuffer */
86 #define DS_SND_QUEUE_MIN 12 /* min number of fragments to prebuffer */
87
88 IDirectSoundImpl*       dsound = NULL;
89
90 HRESULT mmErr(UINT err)
91 {
92         switch(err) {
93         case MMSYSERR_NOERROR:
94                 return DS_OK;
95         case MMSYSERR_ALLOCATED:
96                 return DSERR_ALLOCATED;
97         case MMSYSERR_ERROR:
98         case MMSYSERR_INVALHANDLE:
99         case WAVERR_STILLPLAYING:
100                 return DSERR_GENERIC; /* FIXME */
101         case MMSYSERR_NODRIVER:
102                 return DSERR_NODRIVER;
103         case MMSYSERR_NOMEM:
104                 return DSERR_OUTOFMEMORY;
105         case MMSYSERR_INVALPARAM:
106         case WAVERR_BADFORMAT:
107         case WAVERR_UNPREPARED:
108                 return DSERR_INVALIDPARAM;
109         case MMSYSERR_NOTSUPPORTED:
110                 return DSERR_UNSUPPORTED;
111         default:
112                 FIXME("Unknown MMSYS error %d\n",err);
113                 return DSERR_GENERIC;
114         }
115 }
116
117 int ds_emuldriver = DS_EMULDRIVER;
118 int ds_hel_margin = DS_HEL_MARGIN;
119 int ds_hel_queue = DS_HEL_QUEUE;
120 int ds_snd_queue_max = DS_SND_QUEUE_MAX;
121 int ds_snd_queue_min = DS_SND_QUEUE_MIN;
122 int ds_hw_accel = DS_HW_ACCEL_FULL;
123 int ds_default_playback = 0;
124 int ds_default_capture = 0;
125
126 /*
127  * Get a config key from either the app-specific or the default config
128  */
129
130 inline static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
131                                     char *buffer, DWORD size )
132 {
133     if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, buffer, &size )) return 0;
134     return RegQueryValueExA( defkey, name, 0, NULL, buffer, &size );
135 }
136
137
138 /*
139  * Setup the dsound options.
140  */
141
142 void setup_dsound_options(void)
143 {
144     char buffer[MAX_PATH+1];
145     HKEY hkey, appkey = 0;
146
147     buffer[MAX_PATH]='\0';
148
149     if (RegCreateKeyExA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\dsound", 0, NULL,
150                          REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
151     {
152         ERR("Cannot create config registry key\n" );
153         ExitProcess(1);
154     }
155
156     if (GetModuleFileNameA( 0, buffer, MAX_PATH ))
157     {
158         HKEY tmpkey;
159
160         if (!RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\AppDefaults", &tmpkey ))
161         {
162            char appname[MAX_PATH+16];
163            char *p = strrchr( buffer, '\\' );
164            if (p!=NULL) {
165                    appname[MAX_PATH]='\0';
166                    strncpy(appname,p+1,MAX_PATH);
167                    strcat(appname,"\\dsound");
168                    TRACE("appname = [%s] \n",appname);
169                    if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
170                        RegCloseKey( tmpkey );
171            }
172         }
173     }
174
175     /* get options */
176
177     if (!get_config_key( hkey, appkey, "EmulDriver", buffer, MAX_PATH ))
178         ds_emuldriver = strcmp(buffer, "N");
179
180     if (!get_config_key( hkey, appkey, "HELmargin", buffer, MAX_PATH ))
181         ds_hel_margin = atoi(buffer);
182
183     if (!get_config_key( hkey, appkey, "HELqueue", buffer, MAX_PATH ))
184         ds_hel_queue = atoi(buffer);
185
186     if (!get_config_key( hkey, appkey, "SndQueueMax", buffer, MAX_PATH ))
187         ds_snd_queue_max = atoi(buffer);
188
189     if (!get_config_key( hkey, appkey, "SndQueueMin", buffer, MAX_PATH ))
190         ds_snd_queue_min = atoi(buffer);
191
192     if (!get_config_key( hkey, appkey, "HardwareAcceleration", buffer, MAX_PATH )) {
193         if (strcmp(buffer, "Full") == 0)
194             ds_hw_accel = DS_HW_ACCEL_FULL;
195         else if (strcmp(buffer, "Standard") == 0)
196             ds_hw_accel = DS_HW_ACCEL_STANDARD;
197         else if (strcmp(buffer, "Basic") == 0)
198             ds_hw_accel = DS_HW_ACCEL_BASIC;
199         else if (strcmp(buffer, "Emulation") == 0)
200             ds_hw_accel = DS_HW_ACCEL_EMULATION;
201     }
202
203     if (!get_config_key( hkey, appkey, "DefaultPlayback", buffer, MAX_PATH ))
204             ds_default_playback = atoi(buffer);
205
206     if (!get_config_key( hkey, appkey, "DefaultCapture", buffer, MAX_PATH ))
207             ds_default_capture = atoi(buffer);
208
209     if (appkey) RegCloseKey( appkey );
210     RegCloseKey( hkey );
211
212     if (ds_emuldriver != DS_EMULDRIVER )
213        WARN("ds_emuldriver = %d (default=%d)\n",ds_emuldriver, DS_EMULDRIVER);
214     if (ds_hel_margin != DS_HEL_MARGIN )
215        WARN("ds_hel_margin = %d (default=%d)\n",ds_hel_margin, DS_HEL_MARGIN );
216     if (ds_hel_queue != DS_HEL_QUEUE )
217        WARN("ds_hel_queue = %d (default=%d)\n",ds_hel_queue, DS_HEL_QUEUE );
218     if (ds_snd_queue_max != DS_SND_QUEUE_MAX)
219        WARN("ds_snd_queue_max = %d (default=%d)\n",ds_snd_queue_max ,DS_SND_QUEUE_MAX);
220     if (ds_snd_queue_min != DS_SND_QUEUE_MIN)
221        WARN("ds_snd_queue_min = %d (default=%d)\n",ds_snd_queue_min ,DS_SND_QUEUE_MIN);
222     if (ds_hw_accel != DS_HW_ACCEL_FULL)
223         WARN("ds_hw_accel = %s (default=Full)\n", 
224             ds_hw_accel==DS_HW_ACCEL_FULL ? "Full" :
225             ds_hw_accel==DS_HW_ACCEL_STANDARD ? "Standard" :
226             ds_hw_accel==DS_HW_ACCEL_BASIC ? "Basic" :
227             ds_hw_accel==DS_HW_ACCEL_EMULATION ? "Emulation" :
228             "Unknown");
229     if (ds_default_playback != 0)
230         WARN("ds_default_playback = %d (default=0)\n",ds_default_playback);
231     if (ds_default_capture != 0)
232         WARN("ds_default_capture = %d (default=0)\n",ds_default_playback);
233 }
234
235
236
237 /***************************************************************************
238  * GetDeviceID  [DSOUND.9]
239  *
240  * Retrieves unique identifier of default device specified
241  *
242  * PARAMS
243  *    pGuidSrc  [I] Address of device GUID.
244  *    pGuidDest [O] Address to receive unique device GUID.
245  *
246  * RETURNS
247  *    Success: DS_OK
248  *    Failure: DSERR_INVALIDPARAM
249  *
250  * NOTES
251  *    pGuidSrc is a valid device GUID or DSDEVID_DefaultPlayback,
252  *    DSDEVID_DefaultCapture, DSDEVID_DefaultVoicePlayback, or 
253  *    DSDEVID_DefaultVoiceCapture.
254  *    Returns pGuidSrc if pGuidSrc is a valid device or the device
255  *    GUID for the specified constants.
256  */
257 HRESULT WINAPI GetDeviceID(LPCGUID pGuidSrc, LPGUID pGuidDest)
258 {
259     TRACE("(%p,%p)\n",pGuidSrc,pGuidDest);
260
261     if ( pGuidSrc == NULL) {
262         WARN("invalid parameter: pGuidSrc == NULL\n");
263         return DSERR_INVALIDPARAM;
264     }
265
266     if ( pGuidDest == NULL ) {
267         WARN("invalid parameter: pGuidDest == NULL\n");
268         return DSERR_INVALIDPARAM;
269     }
270
271     if ( IsEqualGUID( &DSDEVID_DefaultPlayback, pGuidSrc ) ||
272         IsEqualGUID( &DSDEVID_DefaultVoicePlayback, pGuidSrc ) ) {
273         GUID guid;
274         int err = mmErr(waveOutMessage((HWAVEOUT)ds_default_playback,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
275         if (err == DS_OK) {
276             memcpy(pGuidDest, &guid, sizeof(GUID));
277             return DS_OK;
278         }
279     }
280
281     if ( IsEqualGUID( &DSDEVID_DefaultCapture, pGuidSrc ) ||
282         IsEqualGUID( &DSDEVID_DefaultVoiceCapture, pGuidSrc ) ) {
283         GUID guid;
284         int err = mmErr(waveInMessage((HWAVEIN)ds_default_capture,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
285         if (err == DS_OK) {
286             memcpy(pGuidDest, &guid, sizeof(GUID));
287             return DS_OK;
288         }
289     }
290
291     memcpy(pGuidDest, pGuidSrc, sizeof(GUID));
292
293     return DS_OK;
294 }
295
296
297 /***************************************************************************
298  * DirectSoundEnumerateA [DSOUND.2]
299  *
300  * Enumerate all DirectSound drivers installed in the system
301  *
302  * PARAMS
303  *    lpDSEnumCallback  [I] Address of callback function.
304  *    lpContext         [I] Address of user defined context passed to callback function.
305  *
306  * RETURNS
307  *    Success: DS_OK
308  *    Failure: DSERR_INVALIDPARAM
309  */
310 HRESULT WINAPI DirectSoundEnumerateA(
311     LPDSENUMCALLBACKA lpDSEnumCallback,
312     LPVOID lpContext)
313 {
314     unsigned devs, wod;
315     DSDRIVERDESC desc;
316     GUID guid;
317     int err;
318
319     TRACE("lpDSEnumCallback = %p, lpContext = %p\n",
320         lpDSEnumCallback, lpContext);
321
322     if (lpDSEnumCallback == NULL) {
323         WARN("invalid parameter: lpDSEnumCallback == NULL\n");
324         return DSERR_INVALIDPARAM;
325     }
326
327     devs = waveOutGetNumDevs();
328     if (devs > 0) {
329         if (GetDeviceID(&DSDEVID_DefaultPlayback, &guid) == DS_OK) {
330             GUID temp;
331             for (wod = 0; wod < devs; ++wod) {
332                 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&temp,0));
333                 if (err == DS_OK) {
334                     if (IsEqualGUID( &guid, &temp ) ) {
335                         err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
336                         if (err == DS_OK) {
337                             TRACE("calling lpDSEnumCallback(NULL,\"%s\",\"%s\",%p)\n",
338                                 "Primary Sound Driver",desc.szDrvName,lpContext);
339                             if (lpDSEnumCallback(NULL, "Primary Sound Driver", desc.szDrvName, lpContext) == FALSE)
340                                 return DS_OK;
341                         }
342                     }
343                 }
344             }
345         }
346     }
347
348     for (wod = 0; wod < devs; ++wod) {
349         err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
350         if (err == DS_OK) {
351             err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
352             if (err == DS_OK) {
353                 TRACE("calling lpDSEnumCallback(%s,\"%s\",\"%s\",%p)\n",
354                     debugstr_guid(&guid),desc.szDesc,desc.szDrvName,lpContext);
355                 if (lpDSEnumCallback(&guid, desc.szDesc, desc.szDrvName, lpContext) == FALSE)
356                     return DS_OK;
357             }
358         }
359     }
360     return DS_OK;
361 }
362
363 /***************************************************************************
364  * DirectSoundEnumerateW [DSOUND.3]
365  *
366  * Enumerate all DirectSound drivers installed in the system
367  *
368  * PARAMS
369  *    lpDSEnumCallback  [I] Address of callback function.
370  *    lpContext         [I] Address of user defined context passed to callback function.
371  *
372  * RETURNS
373  *    Success: DS_OK
374  *    Failure: DSERR_INVALIDPARAM
375  */
376 HRESULT WINAPI DirectSoundEnumerateW(
377         LPDSENUMCALLBACKW lpDSEnumCallback,
378         LPVOID lpContext )
379 {
380     unsigned devs, wod;
381     DSDRIVERDESC desc;
382     GUID guid;
383     int err;
384     WCHAR wDesc[MAXPNAMELEN];
385     WCHAR wName[MAXPNAMELEN];
386
387     TRACE("lpDSEnumCallback = %p, lpContext = %p\n",
388         lpDSEnumCallback, lpContext);
389
390     if (lpDSEnumCallback == NULL) {
391         WARN("invalid parameter: lpDSEnumCallback == NULL\n");
392         return DSERR_INVALIDPARAM;
393     }
394
395     devs = waveOutGetNumDevs();
396     if (devs > 0) {
397         if (GetDeviceID(&DSDEVID_DefaultPlayback, &guid) == DS_OK) {
398             GUID temp;
399             for (wod = 0; wod < devs; ++wod) {
400                 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&temp,0));
401                 if (err == DS_OK) {
402                     if (IsEqualGUID( &guid, &temp ) ) {
403                         err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
404                         if (err == DS_OK) {
405                             TRACE("calling lpDSEnumCallback(NULL,\"%s\",\"%s\",%p)\n",
406                                 "Primary Sound Driver",desc.szDrvName,lpContext);
407                             MultiByteToWideChar( CP_ACP, 0, "Primary Sound Driver", -1,
408                                 wDesc, sizeof(wDesc)/sizeof(WCHAR) );
409                                 MultiByteToWideChar( CP_ACP, 0, desc.szDrvName, -1,
410                                 wName, sizeof(wName)/sizeof(WCHAR) );
411                             if (lpDSEnumCallback(NULL, wDesc, wName, lpContext) == FALSE)
412                                 return DS_OK;
413                         }
414                     }
415                 }
416             }
417         }
418     }
419
420     for (wod = 0; wod < devs; ++wod) {
421         err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
422         if (err == DS_OK) {
423             err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
424             if (err == DS_OK) {
425                 TRACE("calling lpDSEnumCallback(%s,\"%s\",\"%s\",%p)\n",
426                     debugstr_guid(&guid),desc.szDesc,desc.szDrvName,lpContext);
427                 MultiByteToWideChar( CP_ACP, 0, desc.szDesc, -1,
428                     wDesc, sizeof(wDesc)/sizeof(WCHAR) );
429                     MultiByteToWideChar( CP_ACP, 0, desc.szDrvName, -1,
430                     wName, sizeof(wName)/sizeof(WCHAR) );
431                 if (lpDSEnumCallback(&guid, wDesc, wName, lpContext) == FALSE)
432                     return DS_OK;
433             }
434         }
435     }
436     return DS_OK;
437 }
438
439
440 static void _dump_DSBCAPS(DWORD xmask) {
441         struct {
442                 DWORD   mask;
443                 char    *name;
444         } flags[] = {
445 #define FE(x) { x, #x },
446                 FE(DSBCAPS_PRIMARYBUFFER)
447                 FE(DSBCAPS_STATIC)
448                 FE(DSBCAPS_LOCHARDWARE)
449                 FE(DSBCAPS_LOCSOFTWARE)
450                 FE(DSBCAPS_CTRL3D)
451                 FE(DSBCAPS_CTRLFREQUENCY)
452                 FE(DSBCAPS_CTRLPAN)
453                 FE(DSBCAPS_CTRLVOLUME)
454                 FE(DSBCAPS_CTRLPOSITIONNOTIFY)
455                 FE(DSBCAPS_CTRLDEFAULT)
456                 FE(DSBCAPS_CTRLALL)
457                 FE(DSBCAPS_STICKYFOCUS)
458                 FE(DSBCAPS_GLOBALFOCUS)
459                 FE(DSBCAPS_GETCURRENTPOSITION2)
460                 FE(DSBCAPS_MUTE3DATMAXDISTANCE)
461 #undef FE
462         };
463         int     i;
464
465         for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
466                 if ((flags[i].mask & xmask) == flags[i].mask)
467                         DPRINTF("%s ",flags[i].name);
468 }
469
470 /*******************************************************************************
471  *              IDirectSound
472  */
473
474 static HRESULT WINAPI IDirectSoundImpl_SetCooperativeLevel(
475         LPDIRECTSOUND8 iface,HWND hwnd,DWORD level
476 ) {
477         ICOM_THIS(IDirectSoundImpl,iface);
478         TRACE("(%p,%08lx,%ld(%s))\n",This,(DWORD)hwnd,level,
479                 level == DSSCL_NORMAL ? "DSSCL_NORMAL" :
480                 level == DSSCL_PRIORITY ? "DSSCL_PRIORITY" :
481                 level == DSSCL_EXCLUSIVE ? "DSSCL_EXCLUSIVE" :
482                 level == DSSCL_WRITEPRIMARY ? "DSSCL_WRITEPRIMARY" : "Unknown");
483
484         if (level==DSSCL_PRIORITY || level==DSSCL_EXCLUSIVE) {
485                 FIXME("level=%s not fully supported\n",
486                         level==DSSCL_PRIORITY ? "DSSCL_PRIORITY" : "DSSCL_EXCLUSIVE");
487         }
488
489         This->priolevel = level;
490
491         return DS_OK;
492 }
493
494 static HRESULT WINAPI IDirectSoundImpl_CreateSoundBuffer(
495         LPDIRECTSOUND8 iface,LPCDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER8 ppdsb,LPUNKNOWN lpunk
496 ) {
497         ICOM_THIS(IDirectSoundImpl,iface);
498         LPWAVEFORMATEX  wfex;
499         HRESULT hres = DS_OK;
500
501         TRACE("(%p,%p,%p,%p)\n",This,dsbd,ppdsb,lpunk);
502
503         if (This == NULL) {
504                 WARN("invalid parameter: This == NULL\n");
505                 return DSERR_INVALIDPARAM;
506         }
507
508         if (dsbd == NULL) {
509                 WARN("invalid parameter: dsbd == NULL\n");
510                 return DSERR_INVALIDPARAM;
511         }
512
513         if (dsbd->dwSize != sizeof(DSBUFFERDESC) && dsbd->dwSize != sizeof(DSBUFFERDESC1)) {
514                 WARN("invalid parameter: dsbd\n");
515                 return DSERR_INVALIDPARAM;
516         }
517
518         if (ppdsb == NULL) {
519                 WARN("invalid parameter: ppdsb == NULL\n");
520                 return DSERR_INVALIDPARAM;
521         }
522
523         if (TRACE_ON(dsound)) {
524                 TRACE("(structsize=%ld)\n",dsbd->dwSize);
525                 TRACE("(flags=0x%08lx:\n",dsbd->dwFlags);
526                 _dump_DSBCAPS(dsbd->dwFlags);
527                 DPRINTF(")\n");
528                 TRACE("(bufferbytes=%ld)\n",dsbd->dwBufferBytes);
529                 TRACE("(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
530         }
531
532         wfex = dsbd->lpwfxFormat;
533
534         if (wfex)
535                 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
536                    "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
537                    wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
538                    wfex->nAvgBytesPerSec, wfex->nBlockAlign,
539                    wfex->wBitsPerSample, wfex->cbSize);
540
541         if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
542                 if (This->primary) {
543                         WARN("Primary Buffer already created\n");
544                         IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER8)(This->primary));
545                         *ppdsb = (LPDIRECTSOUNDBUFFER8)(This->primary);
546                 } else {
547                         This->dsbd = *dsbd;
548                         hres = PrimaryBufferImpl_Create(This, (PrimaryBufferImpl**)&(This->primary), &(This->dsbd));
549                         if (This->primary) {
550                                 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER8)(This->primary));
551                                 *ppdsb = (LPDIRECTSOUNDBUFFER8)(This->primary);
552                         } else 
553                                 WARN("PrimaryBufferImpl_Create failed\n");
554                 }
555         } else {
556                 IDirectSoundBufferImpl * dsb;
557                 hres = IDirectSoundBufferImpl_Create(This, (IDirectSoundBufferImpl**)&dsb, dsbd);
558                 if (dsb) {
559                         hres = SecondaryBufferImpl_Create(dsb, (SecondaryBufferImpl**)ppdsb);
560                         if (*ppdsb) {
561                                 dsb->dsb = (SecondaryBufferImpl*)*ppdsb;
562                                 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER8)*ppdsb);
563                         } else
564                                 WARN("SecondaryBufferImpl_Create failed\n");
565                 } else 
566                         WARN("IDirectSoundBufferImpl_Create failed\n");
567         }
568
569         return hres;
570 }
571
572 static HRESULT WINAPI IDirectSoundImpl_DuplicateSoundBuffer(
573         LPDIRECTSOUND8 iface,LPDIRECTSOUNDBUFFER8 psb,LPLPDIRECTSOUNDBUFFER8 ppdsb
574 ) {
575         ICOM_THIS(IDirectSoundImpl,iface);
576         IDirectSoundBufferImpl* pdsb;
577         IDirectSoundBufferImpl* dsb;
578         HRESULT hres = DS_OK;
579         TRACE("(%p,%p,%p)\n",This,psb,ppdsb);
580
581         if (This == NULL) {
582                 WARN("invalid parameter: This == NULL\n");
583                 return DSERR_INVALIDPARAM;
584         }
585
586         if (psb == NULL) {
587                 WARN("invalid parameter: psb == NULL\n");
588                 return DSERR_INVALIDPARAM;
589         }
590
591         if (ppdsb == NULL) {
592                 WARN("invalid parameter: ppdsb == NULL\n");
593                 return DSERR_INVALIDPARAM;
594         }
595
596         /* FIXME: hack to make sure we have a secondary buffer */
597         if ((DWORD)((SecondaryBufferImpl *)psb)->dsb == (DWORD)This) {
598                 ERR("trying to duplicate primary buffer\n");
599                 *ppdsb = NULL;
600                 return DSERR_INVALIDCALL;
601         }
602
603         pdsb = ((SecondaryBufferImpl *)psb)->dsb;
604
605         dsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(*dsb));
606
607         if (dsb == NULL) {
608                 WARN("out of memory\n");
609                 *ppdsb = NULL;
610                 return DSERR_OUTOFMEMORY;
611         }
612
613         memcpy(dsb, pdsb, sizeof(IDirectSoundBufferImpl));
614
615         if (pdsb->hwbuf) {
616                 TRACE("duplicating hardware buffer\n");
617
618                 hres = IDsDriver_DuplicateSoundBuffer(This->driver, pdsb->hwbuf, (LPVOID *)&dsb->hwbuf);
619                 if (hres != DS_OK) {
620                         TRACE("IDsDriver_DuplicateSoundBuffer failed, falling back to software buffer\n");
621                         dsb->hwbuf = NULL;
622                         /* allocate buffer */
623                         if (This->drvdesc.dwFlags & DSDDESC_USESYSTEMMEMORY) {
624                                 dsb->buffer = HeapAlloc(GetProcessHeap(),0,sizeof(*(dsb->buffer)));
625                                 if (dsb->buffer == NULL) {
626                                         WARN("out of memory\n");
627                                         HeapFree(GetProcessHeap(),0,dsb);
628                                         *ppdsb = NULL;
629                                         return DSERR_OUTOFMEMORY;
630                                 }
631
632                                 dsb->buffer->memory = (LPBYTE)HeapAlloc(GetProcessHeap(),0,dsb->buflen);
633                                 if (dsb->buffer->memory == NULL) {
634                                         WARN("out of memory\n");
635                                         HeapFree(GetProcessHeap(),0,dsb->buffer);
636                                         HeapFree(GetProcessHeap(),0,dsb);
637                                         *ppdsb = NULL;
638                                         return DSERR_OUTOFMEMORY;
639                                 }
640                                 dsb->buffer->ref = 1;
641
642                                 /* FIXME: copy buffer ? */
643                         }
644                 }
645         } else {
646                 dsb->hwbuf = NULL;
647                 dsb->buffer->ref++;
648         }
649
650         dsb->ref = 0;
651         dsb->state = STATE_STOPPED;
652         dsb->playpos = 0;
653         dsb->buf_mixpos = 0;
654         dsb->dsound = This;
655         dsb->ds3db = NULL;
656         dsb->iks = NULL; /* FIXME? */
657         dsb->dsb = NULL;
658         memcpy(&(dsb->wfx), &(pdsb->wfx), sizeof(dsb->wfx));
659         InitializeCriticalSection(&(dsb->lock));
660         /* register buffer */
661         RtlAcquireResourceExclusive(&(This->lock), TRUE);
662         {
663                 IDirectSoundBufferImpl **newbuffers;
664                 if (This->buffers)
665                         newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl**)*(This->nrofbuffers+1));
666                 else
667                         newbuffers = (IDirectSoundBufferImpl**)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSoundBufferImpl**)*(This->nrofbuffers+1));
668
669                 if (newbuffers) {
670                         This->buffers = newbuffers;
671                         This->buffers[This->nrofbuffers] = dsb;
672                         This->nrofbuffers++;
673                         TRACE("buffer count is now %d\n", This->nrofbuffers);
674                 } else {
675                         ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
676                         IDirectSoundBuffer8_Release(psb);
677                         DeleteCriticalSection(&(dsb->lock));
678                         RtlReleaseResource(&(This->lock));
679                         HeapFree(GetProcessHeap(),0,dsb);
680                         *ppdsb = 0;
681                         return DSERR_OUTOFMEMORY;
682                 }
683         }
684         RtlReleaseResource(&(This->lock));
685         IDirectSound_AddRef(iface);
686         hres = SecondaryBufferImpl_Create(dsb, (SecondaryBufferImpl**)ppdsb);
687         if (*ppdsb) {
688                 dsb->dsb = (SecondaryBufferImpl*)*ppdsb;
689                 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER8)*ppdsb);
690         } else
691                 WARN("SecondaryBufferImpl_Create failed\n");
692
693         return hres;
694 }
695
696 static HRESULT WINAPI IDirectSoundImpl_GetCaps(LPDIRECTSOUND8 iface,LPDSCAPS lpDSCaps) {
697         ICOM_THIS(IDirectSoundImpl,iface);
698         TRACE("(%p,%p)\n",This,lpDSCaps);
699
700         if (This == NULL) {
701                 WARN("invalid parameter: This == NULL\n");
702                 return DSERR_INVALIDPARAM;
703         }
704
705         if (lpDSCaps == NULL) {
706                 WARN("invalid parameter: lpDSCaps = NULL\n");
707                 return DSERR_INVALIDPARAM;
708         }
709
710         /* check is there is enough room */
711         if (lpDSCaps->dwSize < sizeof(*lpDSCaps)) {
712                 WARN("invalid parameter: lpDSCaps->dwSize = %ld < %d\n",
713                         lpDSCaps->dwSize, sizeof(*lpDSCaps));
714                 return DSERR_INVALIDPARAM;
715         }
716
717         lpDSCaps->dwFlags                               = This->drvcaps.dwFlags;
718         TRACE("(flags=0x%08lx)\n",lpDSCaps->dwFlags);
719
720         lpDSCaps->dwMinSecondarySampleRate              = This->drvcaps.dwMinSecondarySampleRate;
721         lpDSCaps->dwMaxSecondarySampleRate              = This->drvcaps.dwMaxSecondarySampleRate;
722
723         lpDSCaps->dwPrimaryBuffers                      = This->drvcaps.dwPrimaryBuffers;
724
725         lpDSCaps->dwMaxHwMixingAllBuffers               = This->drvcaps.dwMaxHwMixingAllBuffers;
726         lpDSCaps->dwMaxHwMixingStaticBuffers            = This->drvcaps.dwMaxHwMixingStaticBuffers;
727         lpDSCaps->dwMaxHwMixingStreamingBuffers         = This->drvcaps.dwMaxHwMixingStreamingBuffers;
728
729         lpDSCaps->dwFreeHwMixingAllBuffers              = This->drvcaps.dwFreeHwMixingAllBuffers;
730         lpDSCaps->dwFreeHwMixingStaticBuffers           = This->drvcaps.dwFreeHwMixingStaticBuffers;
731         lpDSCaps->dwFreeHwMixingStreamingBuffers        = This->drvcaps.dwFreeHwMixingStreamingBuffers;
732
733         lpDSCaps->dwMaxHw3DAllBuffers                   = This->drvcaps.dwMaxHw3DAllBuffers;
734         lpDSCaps->dwMaxHw3DStaticBuffers                = This->drvcaps.dwMaxHw3DStaticBuffers;
735         lpDSCaps->dwMaxHw3DStreamingBuffers             = This->drvcaps.dwMaxHw3DStreamingBuffers;
736
737         lpDSCaps->dwFreeHw3DAllBuffers                  = This->drvcaps.dwFreeHw3DAllBuffers;
738         lpDSCaps->dwFreeHw3DStaticBuffers               = This->drvcaps.dwFreeHw3DStaticBuffers;
739         lpDSCaps->dwFreeHw3DStreamingBuffers            = This->drvcaps.dwFreeHw3DStreamingBuffers;
740
741         lpDSCaps->dwTotalHwMemBytes                     = This->drvcaps.dwTotalHwMemBytes;
742
743         lpDSCaps->dwFreeHwMemBytes                      = This->drvcaps.dwFreeHwMemBytes;
744
745         lpDSCaps->dwMaxContigFreeHwMemBytes             = This->drvcaps.dwMaxContigFreeHwMemBytes;
746
747         /* driver doesn't have these */
748         lpDSCaps->dwUnlockTransferRateHwBuffers         = 4096; /* But we have none... */
749
750         lpDSCaps->dwPlayCpuOverheadSwBuffers            = 1;    /* 1% */
751
752         return DS_OK;
753 }
754
755 static ULONG WINAPI IDirectSoundImpl_AddRef(LPDIRECTSOUND8 iface) {
756         ICOM_THIS(IDirectSoundImpl,iface);
757         TRACE("(%p) ref was %ld, thread is %04lx\n", This, This->ref, GetCurrentThreadId());
758         return InterlockedIncrement(&This->ref);
759 }
760
761 static ULONG WINAPI IDirectSoundImpl_Release(LPDIRECTSOUND8 iface) {
762         ICOM_THIS(IDirectSoundImpl,iface);
763         ULONG ulReturn;
764
765         TRACE("(%p) ref was %ld, thread is %04lx\n", This, This->ref, GetCurrentThreadId());
766         ulReturn = InterlockedDecrement(&This->ref);
767         if (ulReturn == 0) {
768                 HRESULT hres;
769                 UINT i;
770
771                 timeKillEvent(This->timerID);
772                 timeEndPeriod(DS_TIME_RES);
773                 /* wait for timer to expire */
774                 Sleep(DS_TIME_RES+1);
775
776                 RtlAcquireResourceShared(&(This->lock), TRUE);
777
778                 if (This->buffers) {
779                         for( i=0;i<This->nrofbuffers;i++)
780                                 IDirectSoundBuffer8_Release((LPDIRECTSOUNDBUFFER8)This->buffers[i]);
781                 }
782
783                 RtlReleaseResource(&(This->lock));
784
785                 if (This->primary) {
786                         WARN("primary buffer not released\n");
787                         IDirectSoundBuffer8_Release((LPDIRECTSOUNDBUFFER8)This->primary);
788                 }
789
790                 hres = DSOUND_PrimaryDestroy(This);
791                 if (hres != DS_OK)
792                         WARN("DSOUND_PrimaryDestroy failed\n");
793
794                 if (This->driver)
795                         IDsDriver_Close(This->driver);
796
797                 if (This->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
798                         waveOutClose(This->hwo);
799
800                 if (This->driver)
801                         IDsDriver_Release(This->driver);
802
803                 RtlDeleteResource(&This->lock);
804                 DeleteCriticalSection(&This->mixlock);
805                 HeapFree(GetProcessHeap(),0,This);
806                 dsound = NULL;
807                 TRACE("(%p) released\n",This);
808         }
809
810         return ulReturn;
811 }
812
813 static HRESULT WINAPI IDirectSoundImpl_SetSpeakerConfig(
814         LPDIRECTSOUND8 iface,DWORD config
815 ) {
816         ICOM_THIS(IDirectSoundImpl,iface);
817         TRACE("(%p,0x%08lx)\n",This,config);
818
819         This->speaker_config = config;
820
821         WARN("not fully functional\n");
822         return DS_OK;
823 }
824
825 static HRESULT WINAPI IDirectSoundImpl_QueryInterface(
826         LPDIRECTSOUND8 iface,REFIID riid,LPVOID *ppobj
827 ) {
828         ICOM_THIS(IDirectSoundImpl,iface);
829         TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
830
831         if (ppobj == NULL) {
832                 WARN("invalid parameter\n");
833                 return E_INVALIDARG;
834         }
835
836         *ppobj = NULL;  /* assume failure */
837
838         if ( IsEqualGUID(riid, &IID_IUnknown) || 
839              IsEqualGUID(riid, &IID_IDirectSound) || 
840              IsEqualGUID(riid, &IID_IDirectSound8) ) {
841                 IDirectSound8_AddRef((LPDIRECTSOUND8)This);
842                 *ppobj = This;
843                 return S_OK;
844         }
845
846         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
847                 WARN("app requested IDirectSound3DListener on dsound object\n");
848                 return E_NOINTERFACE;
849         }
850
851         FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
852         return E_NOINTERFACE;
853 }
854
855 static HRESULT WINAPI IDirectSoundImpl_Compact(
856         LPDIRECTSOUND8 iface)
857 {
858         ICOM_THIS(IDirectSoundImpl,iface);
859         TRACE("(%p)\n", This);
860         return DS_OK;
861 }
862
863 static HRESULT WINAPI IDirectSoundImpl_GetSpeakerConfig(
864         LPDIRECTSOUND8 iface,
865         LPDWORD lpdwSpeakerConfig)
866 {
867         ICOM_THIS(IDirectSoundImpl,iface);
868         TRACE("(%p, %p)\n", This, lpdwSpeakerConfig);
869
870         if (lpdwSpeakerConfig == NULL) {
871                 WARN("invalid parameter\n");
872                 return DSERR_INVALIDPARAM;
873         }
874
875         WARN("not fully functional\n");
876
877         *lpdwSpeakerConfig = This->speaker_config;
878
879         return DS_OK;
880 }
881
882 static HRESULT WINAPI IDirectSoundImpl_Initialize(
883         LPDIRECTSOUND8 iface,
884         LPCGUID lpcGuid)
885 {
886         ICOM_THIS(IDirectSoundImpl,iface);
887         TRACE("(%p, %s)\n", This, debugstr_guid(lpcGuid));
888         return DS_OK;
889 }
890
891 static HRESULT WINAPI IDirectSoundImpl_VerifyCertification(
892         LPDIRECTSOUND8 iface,
893         LPDWORD pdwCertified)
894 {
895         ICOM_THIS(IDirectSoundImpl,iface);
896         TRACE("(%p, %p)\n", This, pdwCertified);
897         *pdwCertified = DS_CERTIFIED;
898         return DS_OK;
899 }
900
901 static ICOM_VTABLE(IDirectSound8) dsvt =
902 {
903         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
904         IDirectSoundImpl_QueryInterface,
905         IDirectSoundImpl_AddRef,
906         IDirectSoundImpl_Release,
907         IDirectSoundImpl_CreateSoundBuffer,
908         IDirectSoundImpl_GetCaps,
909         IDirectSoundImpl_DuplicateSoundBuffer,
910         IDirectSoundImpl_SetCooperativeLevel,
911         IDirectSoundImpl_Compact,
912         IDirectSoundImpl_GetSpeakerConfig,
913         IDirectSoundImpl_SetSpeakerConfig,
914         IDirectSoundImpl_Initialize,
915         IDirectSoundImpl_VerifyCertification
916 };
917
918
919 /*******************************************************************************
920  *              DirectSoundCreate (DSOUND.1)
921  *
922  *  Creates and initializes a DirectSound interface.
923  *
924  *  PARAMS
925  *     lpcGUID   [I] Address of the GUID that identifies the sound device.
926  *     ppDS      [O] Address of a variable to receive the interface pointer.
927  *     pUnkOuter [I] Must be NULL.
928  *
929  *  RETURNS
930  *     Success: DS_OK
931  *     Failure: DSERR_ALLOCATED, DSERR_INVALIDPARAM, DSERR_NOAGGREGATION, 
932  *              DSERR_NODRIVER, DSERR_OUTOFMEMORY
933  */
934 HRESULT WINAPI DirectSoundCreate8(LPCGUID lpcGUID,LPDIRECTSOUND8 *ppDS,IUnknown *pUnkOuter )
935 {
936         IDirectSoundImpl** ippDS=(IDirectSoundImpl**)ppDS;
937         PIDSDRIVER drv = NULL;
938         unsigned wod, wodn;
939         HRESULT err = DSERR_INVALIDPARAM;
940         GUID devGuid;
941         BOOLEAN found = FALSE;
942
943         TRACE("(%s,%p,%p)\n",debugstr_guid(lpcGUID),ippDS,pUnkOuter);
944         
945         if (ippDS == NULL) {
946                 WARN("invalid parameter: ippDS == NULL\n");
947                 return DSERR_INVALIDPARAM;
948         }
949
950         /* Get dsound configuration */
951         setup_dsound_options();
952
953         /* Default device? */
954         if (!lpcGUID || IsEqualGUID(lpcGUID, &GUID_NULL))
955                 lpcGUID = &DSDEVID_DefaultPlayback;
956
957         if (GetDeviceID(lpcGUID, &devGuid) != DS_OK) {
958                 WARN("invalid parameter: lpcGUID\n");
959                 *ippDS = NULL;
960                 return DSERR_INVALIDPARAM;
961         }
962
963         if (dsound) {
964                 if (IsEqualGUID(&devGuid, &dsound->guid) ) {
965                         /* FIXME: this is wrong, need to create a new instance */
966                         ERR("dsound already opened\n");
967                         IDirectSound_AddRef((LPDIRECTSOUND)dsound);
968                         *ippDS = dsound;
969                         return DS_OK;
970                 } else {
971                         ERR("different dsound already opened\n");
972                 }
973         }
974
975         /* Enumerate WINMM audio devices and find the one we want */
976         wodn = waveOutGetNumDevs();
977         if (!wodn) {
978                 WARN("no driver\n");
979                 *ippDS = NULL;
980                 return DSERR_NODRIVER;
981         }
982
983         TRACE(" expecting GUID %s.\n", debugstr_guid(&devGuid));
984         
985         for (wod=0; wod<wodn; wod++) {
986                 GUID guid;
987                 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)(&guid),0));
988                 if (err != DS_OK) {
989                         WARN("waveOutMessage failed; err=%lx\n",err);
990                         *ippDS = NULL;
991                         return err;
992                 }
993                 TRACE("got GUID %s for wod %d.\n", debugstr_guid(&guid), wod);
994                 if (IsEqualGUID( &devGuid, &guid) ) {
995                         err = DS_OK;
996                         found = TRUE;
997                         break;
998                 }
999         }
1000
1001         if (err != DS_OK) {
1002                 WARN("invalid parameter\n");
1003                 *ippDS = NULL;
1004                 return DSERR_INVALIDPARAM;
1005         }
1006
1007         if (found == FALSE) {
1008                 WARN("No device found matching given ID - trying with default one !\n");
1009                 wod = ds_default_playback;      
1010         }
1011         
1012         /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
1013         waveOutMessage((HWAVEOUT)wod, DRV_QUERYDSOUNDIFACE, (DWORD)&drv, 0);
1014
1015         /* Disable the direct sound driver to force emulation if requested. */
1016         if (ds_hw_accel == DS_HW_ACCEL_EMULATION)
1017             drv = NULL;
1018         
1019         /* Allocate memory */
1020         *ippDS = (IDirectSoundImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundImpl));
1021         if (*ippDS == NULL) {
1022                 WARN("out of memory\n");
1023                 return DSERR_OUTOFMEMORY;
1024         }
1025
1026         (*ippDS)->lpVtbl        = &dsvt;
1027         (*ippDS)->ref           = 1;
1028
1029         (*ippDS)->driver        = drv;
1030         (*ippDS)->priolevel     = DSSCL_NORMAL;
1031         (*ippDS)->fraglen       = 0;
1032         (*ippDS)->hwbuf         = NULL;
1033         (*ippDS)->buffer        = NULL;
1034         (*ippDS)->buflen        = 0;
1035         (*ippDS)->writelead     = 0;
1036         (*ippDS)->state         = STATE_STOPPED;
1037         (*ippDS)->nrofbuffers   = 0;
1038         (*ippDS)->buffers       = NULL;
1039         (*ippDS)->primary       = NULL;
1040         (*ippDS)->speaker_config = DSSPEAKER_STEREO | (DSSPEAKER_GEOMETRY_NARROW << 16);
1041         
1042         /* 3D listener initial parameters */
1043         (*ippDS)->listener      = NULL;
1044         (*ippDS)->ds3dl.dwSize = sizeof(DS3DLISTENER);
1045         (*ippDS)->ds3dl.vPosition.x = 0.0;
1046         (*ippDS)->ds3dl.vPosition.y = 0.0;
1047         (*ippDS)->ds3dl.vPosition.z = 0.0;
1048         (*ippDS)->ds3dl.vVelocity.x = 0.0;
1049         (*ippDS)->ds3dl.vVelocity.y = 0.0;
1050         (*ippDS)->ds3dl.vVelocity.z = 0.0;
1051         (*ippDS)->ds3dl.vOrientFront.x = 0.0;
1052         (*ippDS)->ds3dl.vOrientFront.y = 0.0;
1053         (*ippDS)->ds3dl.vOrientFront.z = 1.0;
1054         (*ippDS)->ds3dl.vOrientTop.x = 0.0;
1055         (*ippDS)->ds3dl.vOrientTop.y = 1.0;
1056         (*ippDS)->ds3dl.vOrientTop.z = 0.0;
1057         (*ippDS)->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
1058         (*ippDS)->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
1059         (*ippDS)->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
1060
1061         (*ippDS)->prebuf        = ds_snd_queue_max;
1062         (*ippDS)->guid          = devGuid;
1063
1064         /* Get driver description */
1065         if (drv) {
1066                 err = IDsDriver_GetDriverDesc(drv,&((*ippDS)->drvdesc));
1067                 if (err != DS_OK) {
1068                         WARN("IDsDriver_GetDriverDesc failed\n");
1069                         HeapFree(GetProcessHeap(),0,*ippDS);
1070                         *ippDS = NULL;
1071                         return err;
1072                 }
1073         } else {
1074                 /* if no DirectSound interface available, use WINMM API instead */
1075                 (*ippDS)->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
1076         }
1077
1078         (*ippDS)->drvdesc.dnDevNode = wod;
1079
1080         /* Set default wave format (may need it for waveOutOpen) */
1081         (*ippDS)->wfx.wFormatTag        = WAVE_FORMAT_PCM;
1082         /* We rely on the sound driver to return the actual sound format of 
1083          * the device if it does not support 22050x8x2 and is given the 
1084          * WAVE_DIRECTSOUND flag.
1085          */
1086         (*ippDS)->wfx.nSamplesPerSec = 22050;
1087         (*ippDS)->wfx.wBitsPerSample = 8;
1088         (*ippDS)->wfx.nChannels = 2;
1089         (*ippDS)->wfx.nBlockAlign = (*ippDS)->wfx.wBitsPerSample * (*ippDS)->wfx.nChannels / 8;
1090         (*ippDS)->wfx.nAvgBytesPerSec = (*ippDS)->wfx.nSamplesPerSec * (*ippDS)->wfx.nBlockAlign;
1091         (*ippDS)->wfx.cbSize = 0;
1092
1093         /* If the driver requests being opened through MMSYSTEM
1094          * (which is recommended by the DDK), it is supposed to happen
1095          * before the DirectSound interface is opened */
1096         if ((*ippDS)->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
1097         {
1098                 DWORD flags = CALLBACK_FUNCTION;
1099
1100                 /* disable direct sound if requested */
1101                 if (ds_hw_accel != DS_HW_ACCEL_EMULATION)
1102                     flags |= WAVE_DIRECTSOUND;
1103
1104                 err = mmErr(waveOutOpen(&((*ippDS)->hwo),
1105                                           (*ippDS)->drvdesc.dnDevNode, &((*ippDS)->wfx),
1106                                           (DWORD)DSOUND_callback, (DWORD)(*ippDS),
1107                                           flags));
1108                 if (err != DS_OK) {
1109                         WARN("waveOutOpen failed\n");
1110                         HeapFree(GetProcessHeap(),0,*ippDS);
1111                         *ippDS = NULL;
1112                         return err;
1113                 }
1114         }
1115
1116         if (drv) {
1117                 err = IDsDriver_Open(drv);
1118                 if (err != DS_OK) {
1119                         WARN("IDsDriver_Open failed\n");
1120                         HeapFree(GetProcessHeap(),0,*ippDS);
1121                         *ippDS = NULL;
1122                         return err;
1123                 }
1124
1125                 /* the driver is now open, so it's now allowed to call GetCaps */
1126                 err = IDsDriver_GetCaps(drv,&((*ippDS)->drvcaps));
1127                 if (err != DS_OK) {
1128                         WARN("IDsDriver_GetCaps failed\n");
1129                         HeapFree(GetProcessHeap(),0,*ippDS);
1130                         *ippDS = NULL;
1131                         return err;
1132                 }
1133         } else {
1134                 WAVEOUTCAPSA woc;
1135                 err = mmErr(waveOutGetDevCapsA((*ippDS)->drvdesc.dnDevNode, &woc, sizeof(woc)));
1136                 if (err != DS_OK) {
1137                         WARN("waveOutGetDevCaps failed\n");
1138                         HeapFree(GetProcessHeap(),0,*ippDS);
1139                         *ippDS = NULL;
1140                         return err;
1141                 }
1142                 ZeroMemory(&(*ippDS)->drvcaps, sizeof((*ippDS)->drvcaps));
1143                 if ((woc.dwFormats & WAVE_FORMAT_1M08) ||
1144                     (woc.dwFormats & WAVE_FORMAT_2M08) ||
1145                     (woc.dwFormats & WAVE_FORMAT_4M08) ||
1146                     (woc.dwFormats & WAVE_FORMAT_48M08) ||
1147                     (woc.dwFormats & WAVE_FORMAT_96M08)) {
1148                         (*ippDS)->drvcaps.dwFlags |= DSCAPS_PRIMARY8BIT;
1149                         (*ippDS)->drvcaps.dwFlags |= DSCAPS_PRIMARYMONO;
1150                 }
1151                 if ((woc.dwFormats & WAVE_FORMAT_1M16) ||
1152                     (woc.dwFormats & WAVE_FORMAT_2M16) ||
1153                     (woc.dwFormats & WAVE_FORMAT_4M16) ||
1154                     (woc.dwFormats & WAVE_FORMAT_48M16) ||
1155                     (woc.dwFormats & WAVE_FORMAT_96M16)) {
1156                         (*ippDS)->drvcaps.dwFlags |= DSCAPS_PRIMARY16BIT;
1157                         (*ippDS)->drvcaps.dwFlags |= DSCAPS_PRIMARYMONO;
1158                 }
1159                 if ((woc.dwFormats & WAVE_FORMAT_1S08) ||
1160                     (woc.dwFormats & WAVE_FORMAT_2S08) ||
1161                     (woc.dwFormats & WAVE_FORMAT_4S08) ||
1162                     (woc.dwFormats & WAVE_FORMAT_48S08) ||
1163                     (woc.dwFormats & WAVE_FORMAT_96S08)) {
1164                         (*ippDS)->drvcaps.dwFlags |= DSCAPS_PRIMARY8BIT;
1165                         (*ippDS)->drvcaps.dwFlags |= DSCAPS_PRIMARYSTEREO;
1166                 }
1167                 if ((woc.dwFormats & WAVE_FORMAT_1S16) ||
1168                     (woc.dwFormats & WAVE_FORMAT_2S16) ||
1169                     (woc.dwFormats & WAVE_FORMAT_4S16) ||
1170                     (woc.dwFormats & WAVE_FORMAT_48S16) ||
1171                     (woc.dwFormats & WAVE_FORMAT_96S16)) {
1172                         (*ippDS)->drvcaps.dwFlags |= DSCAPS_PRIMARY16BIT;
1173                         (*ippDS)->drvcaps.dwFlags |= DSCAPS_PRIMARYSTEREO;
1174                 }
1175                 if (ds_emuldriver)
1176                     (*ippDS)->drvcaps.dwFlags |= DSCAPS_EMULDRIVER;
1177                 (*ippDS)->drvcaps.dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
1178                 (*ippDS)->drvcaps.dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
1179                 (*ippDS)->drvcaps.dwPrimaryBuffers = 1;
1180         }
1181
1182         (*ippDS)->volpan.lVolume = 0;
1183         (*ippDS)->volpan.lPan = 0;
1184         DSOUND_RecalcVolPan(&((*ippDS)->volpan));
1185
1186         InitializeCriticalSection(&((*ippDS)->mixlock));
1187         RtlInitializeResource(&((*ippDS)->lock));
1188
1189         if (!dsound) {
1190                 HRESULT hres;
1191                 dsound = (*ippDS);
1192                 hres = DSOUND_PrimaryCreate(dsound);
1193                 if (hres != DS_OK) {
1194                         WARN("DSOUND_PrimaryCreate failed\n");
1195                         return hres;
1196                 }
1197                 timeBeginPeriod(DS_TIME_RES);
1198                 dsound->timerID = timeSetEvent(DS_TIME_DEL, DS_TIME_RES, DSOUND_timer,
1199                                                (DWORD)dsound, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
1200         }
1201
1202         return DS_OK;
1203 }
1204
1205
1206 /*******************************************************************************
1207  * DirectSound ClassFactory
1208  */
1209
1210 static HRESULT WINAPI
1211 DSCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
1212         ICOM_THIS(IClassFactoryImpl,iface);
1213
1214         FIXME("(%p)->(%s,%p),stub!\n",This,debugstr_guid(riid),ppobj);
1215         return E_NOINTERFACE;
1216 }
1217
1218 static ULONG WINAPI
1219 DSCF_AddRef(LPCLASSFACTORY iface) {
1220         ICOM_THIS(IClassFactoryImpl,iface);
1221         TRACE("(%p) ref was %ld\n", This, This->ref);
1222         return ++(This->ref);
1223 }
1224
1225 static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface) {
1226         ICOM_THIS(IClassFactoryImpl,iface);
1227         /* static class, won't be  freed */
1228         TRACE("(%p) ref was %ld\n", This, This->ref);
1229         return --(This->ref);
1230 }
1231
1232 static HRESULT WINAPI DSCF_CreateInstance(
1233         LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
1234 ) {
1235         ICOM_THIS(IClassFactoryImpl,iface);
1236         TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
1237
1238         if (ppobj == NULL) {
1239                 WARN("invalid parameter\n");
1240                 return DSERR_INVALIDPARAM;
1241         }
1242
1243         *ppobj = NULL;
1244
1245         if ( IsEqualGUID( &IID_IDirectSound, riid ) ||
1246              IsEqualGUID( &IID_IDirectSound8, riid ) ) {
1247                 /* FIXME: reuse already created dsound if present? */
1248                 return DirectSoundCreate8(0,(LPDIRECTSOUND8*)ppobj,pOuter);
1249         }
1250
1251         WARN("(%p,%p,%s,%p) Interface not found!\n",This,pOuter,debugstr_guid(riid),ppobj);     
1252         return E_NOINTERFACE;
1253 }
1254
1255 static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
1256         ICOM_THIS(IClassFactoryImpl,iface);
1257         FIXME("(%p)->(%d),stub!\n",This,dolock);
1258         return S_OK;
1259 }
1260
1261 static ICOM_VTABLE(IClassFactory) DSCF_Vtbl = {
1262         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1263         DSCF_QueryInterface,
1264         DSCF_AddRef,
1265         DSCF_Release,
1266         DSCF_CreateInstance,
1267         DSCF_LockServer
1268 };
1269
1270 static IClassFactoryImpl DSOUND_CF = { &DSCF_Vtbl, 1 };
1271
1272 /*******************************************************************************
1273  * DirectSoundPrivate ClassFactory
1274  */
1275
1276 static HRESULT WINAPI
1277 DSPCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
1278         ICOM_THIS(IClassFactoryImpl,iface);
1279
1280         FIXME("(%p)->(%s,%p),stub!\n",This,debugstr_guid(riid),ppobj);
1281         return E_NOINTERFACE;
1282 }
1283
1284 static ULONG WINAPI
1285 DSPCF_AddRef(LPCLASSFACTORY iface) {
1286         ICOM_THIS(IClassFactoryImpl,iface);
1287         TRACE("(%p) ref was %ld\n", This, This->ref);
1288         return ++(This->ref);
1289 }
1290
1291 static ULONG WINAPI 
1292 DSPCF_Release(LPCLASSFACTORY iface) {
1293         ICOM_THIS(IClassFactoryImpl,iface);
1294         /* static class, won't be  freed */
1295         TRACE("(%p) ref was %ld\n", This, This->ref);
1296         return --(This->ref);
1297 }
1298
1299 static HRESULT WINAPI 
1300 DSPCF_CreateInstance(
1301         LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
1302 ) {
1303         ICOM_THIS(IClassFactoryImpl,iface);
1304         TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
1305
1306         if (ppobj == NULL) {
1307                 WARN("invalid parameter\n");
1308                 return DSERR_INVALIDPARAM;
1309         }
1310
1311         *ppobj = NULL;
1312
1313         if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
1314                 return IKsPrivatePropertySetImpl_Create((IKsPrivatePropertySetImpl**)ppobj);
1315         }
1316
1317         WARN("(%p,%p,%s,%p) Interface not found!\n",This,pOuter,debugstr_guid(riid),ppobj);     
1318         return E_NOINTERFACE;
1319 }
1320
1321 static HRESULT WINAPI 
1322 DSPCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
1323         ICOM_THIS(IClassFactoryImpl,iface);
1324         FIXME("(%p)->(%d),stub!\n",This,dolock);
1325         return S_OK;
1326 }
1327
1328 static ICOM_VTABLE(IClassFactory) DSPCF_Vtbl = {
1329         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1330         DSPCF_QueryInterface,
1331         DSPCF_AddRef,
1332         DSPCF_Release,
1333         DSPCF_CreateInstance,
1334         DSPCF_LockServer
1335 };
1336
1337 static IClassFactoryImpl DSOUND_PRIVATE_CF = { &DSPCF_Vtbl, 1 };
1338
1339 /*******************************************************************************
1340  * DllGetClassObject [DSOUND.5]
1341  * Retrieves class object from a DLL object
1342  *
1343  * NOTES
1344  *    Docs say returns STDAPI
1345  *
1346  * PARAMS
1347  *    rclsid [I] CLSID for the class object
1348  *    riid   [I] Reference to identifier of interface for class object
1349  *    ppv    [O] Address of variable to receive interface pointer for riid
1350  *
1351  * RETURNS
1352  *    Success: S_OK
1353  *    Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
1354  *             E_UNEXPECTED
1355  */
1356 DWORD WINAPI DSOUND_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
1357 {
1358     TRACE("(%s,%s,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
1359
1360     if (ppv == NULL) {
1361         WARN("invalid parameter\n");
1362         return E_INVALIDARG;
1363     }
1364
1365     *ppv = NULL;
1366
1367     if ( IsEqualCLSID( &CLSID_DirectSound, rclsid ) ||
1368          IsEqualCLSID( &CLSID_DirectSound8, rclsid ) ) {
1369         if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
1370             *ppv = (LPVOID)&DSOUND_CF;
1371             IClassFactory_AddRef((IClassFactory*)*ppv);
1372             return S_OK;
1373         }
1374         WARN("(%s,%s,%p): no interface found.\n",
1375             debugstr_guid(rclsid), debugstr_guid(riid), ppv);
1376         return S_FALSE;
1377     }
1378     
1379     if ( IsEqualCLSID( &CLSID_DirectSoundCapture, rclsid ) ||
1380          IsEqualCLSID( &CLSID_DirectSoundCapture8, rclsid ) ) {
1381         if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
1382             *ppv = (LPVOID)&DSOUND_CAPTURE_CF;
1383             IClassFactory_AddRef((IClassFactory*)*ppv);
1384             return S_OK;
1385         }
1386         WARN("(%s,%s,%p): no interface found.\n",
1387             debugstr_guid(rclsid), debugstr_guid(riid), ppv);
1388         return S_FALSE;
1389     }
1390     
1391     if ( IsEqualCLSID( &CLSID_DirectSoundFullDuplex, rclsid ) ) {
1392         if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
1393             *ppv = (LPVOID)&DSOUND_FULLDUPLEX_CF;
1394             IClassFactory_AddRef((IClassFactory*)*ppv);
1395             return S_OK;
1396         }
1397         WARN("(%s,%s,%p): no interface found.\n",
1398             debugstr_guid(rclsid), debugstr_guid(riid), ppv);
1399         return S_FALSE;
1400     }
1401     
1402     if ( IsEqualCLSID( &CLSID_DirectSoundPrivate, rclsid ) ) {
1403         if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
1404             *ppv = (LPVOID)&DSOUND_PRIVATE_CF;
1405             IClassFactory_AddRef((IClassFactory*)*ppv);
1406             return S_OK;
1407         }
1408         WARN("(%s,%s,%p): no interface found.\n",
1409             debugstr_guid(rclsid), debugstr_guid(riid), ppv);
1410         return S_FALSE;
1411     }
1412
1413     WARN("(%s,%s,%p): no class found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
1414     return CLASS_E_CLASSNOTAVAILABLE;
1415 }
1416
1417
1418 /*******************************************************************************
1419  * DllCanUnloadNow [DSOUND.4]  
1420  * Determines whether the DLL is in use.
1421  *
1422  * RETURNS
1423  *    Success: S_OK
1424  *    Failure: S_FALSE
1425  */
1426 DWORD WINAPI DSOUND_DllCanUnloadNow(void)
1427 {
1428     FIXME("(void): stub\n");
1429     return S_FALSE;
1430 }