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