Added version information.
[wine] / dlls / dsound / dsound_main.c
1 /*                      DirectSound
2  *
3  * Copyright 1998 Marcus Meissner
4  * Copyright 1998 Rob Riggs
5  * Copyright 2000-2002 TransGaming Technologies, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21 /*
22  * Most thread locking is complete. There may be a few race
23  * conditions still lurking.
24  *
25  * Tested with a Soundblaster clone, a Gravis UltraSound Classic,
26  * and a Turtle Beach Tropez+.
27  *
28  * TODO:
29  *      Implement SetCooperativeLevel properly (need to address focus issues)
30  *      Implement DirectSound3DBuffers (stubs in place)
31  *      Use hardware 3D support if available
32  *      Add critical section locking inside Release and AddRef methods
33  *      Handle static buffers - put those in hardware, non-static not in hardware
34  *      Hardware DuplicateSoundBuffer
35  *      Proper volume calculation, and setting volume in HEL primary buffer
36  *      Optimize WINMM and negotiate fragment size, decrease DS_HEL_MARGIN
37  */
38
39 #include "config.h"
40 #include "wine/port.h"
41
42 #include <assert.h>
43 #include <stdio.h>
44 #include <sys/types.h>
45 #include <sys/fcntl.h>
46 #ifdef HAVE_UNISTD_H
47 # include <unistd.h>
48 #endif
49 #include <stdlib.h>
50 #include <string.h>
51 #include <math.h>       /* Insomnia - pow() function */
52
53 #include "windef.h"
54 #include "winbase.h"
55 #include "winreg.h"
56 #include "winuser.h"
57 #include "wingdi.h"
58 #include "winuser.h"
59 #include "winerror.h"
60 #include "mmsystem.h"
61 #include "winternl.h"
62 #include "mmddk.h"
63 #include "wine/windef16.h"
64 #include "wine/winbase16.h"
65 #include "wine/debug.h"
66 #include "dsound.h"
67 #include "dsdriver.h"
68 #include "dsound_private.h"
69
70 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
71
72 /* these are eligible for tuning... they must be high on slow machines... */
73 /* some stuff may get more responsive with lower values though... */
74 #define DS_EMULDRIVER 0 /* some games (Quake 2, UT) refuse to accept
75                                 emulated dsound devices. set to 0 ! */
76 #define DS_HEL_MARGIN 5 /* HEL only: number of waveOut fragments ahead to mix in new buffers
77                          * (keep this close or equal to DS_HEL_QUEUE for best results) */
78 #define DS_HEL_QUEUE  5 /* HEL only: number of waveOut fragments ahead to queue to driver
79                          * (this will affect HEL sound reliability and latency) */
80
81 #define DS_SND_QUEUE_MAX 28 /* max number of fragments to prebuffer */
82 #define DS_SND_QUEUE_MIN 12 /* min number of fragments to prebuffer */
83
84 IDirectSoundImpl*       dsound = NULL;
85
86 HRESULT mmErr(UINT err)
87 {
88         switch(err) {
89         case MMSYSERR_NOERROR:
90                 return DS_OK;
91         case MMSYSERR_ALLOCATED:
92                 return DSERR_ALLOCATED;
93         case MMSYSERR_ERROR:
94         case MMSYSERR_INVALHANDLE:
95         case WAVERR_STILLPLAYING:
96                 return DSERR_GENERIC; /* FIXME */
97         case MMSYSERR_NODRIVER:
98                 return DSERR_NODRIVER;
99         case MMSYSERR_NOMEM:
100                 return DSERR_OUTOFMEMORY;
101         case MMSYSERR_INVALPARAM:
102         case WAVERR_BADFORMAT:
103         case WAVERR_UNPREPARED:
104                 return DSERR_INVALIDPARAM;
105         case MMSYSERR_NOTSUPPORTED:
106                 return DSERR_UNSUPPORTED;
107         default:
108                 FIXME("Unknown MMSYS error %d\n",err);
109                 return DSERR_GENERIC;
110         }
111 }
112
113 int ds_emuldriver = DS_EMULDRIVER;
114 int ds_hel_margin = DS_HEL_MARGIN;
115 int ds_hel_queue = DS_HEL_QUEUE;
116 int ds_snd_queue_max = DS_SND_QUEUE_MAX;
117 int ds_snd_queue_min = DS_SND_QUEUE_MIN;
118 int ds_hw_accel = DS_HW_ACCEL_FULL;
119 int ds_default_playback = 0;
120 int ds_default_capture = 0;
121
122 /*
123  * Get a config key from either the app-specific or the default config
124  */
125
126 inline static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
127                                     char *buffer, DWORD size )
128 {
129     if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, buffer, &size )) return 0;
130     return RegQueryValueExA( defkey, name, 0, NULL, buffer, &size );
131 }
132
133
134 /*
135  * Setup the dsound options.
136  */
137
138 void setup_dsound_options(void)
139 {
140     char buffer[MAX_PATH+1];
141     HKEY hkey, appkey = 0;
142
143     buffer[MAX_PATH]='\0';
144
145     if (RegCreateKeyExA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\dsound", 0, NULL,
146                          REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
147     {
148         ERR("Cannot create config registry key\n" );
149         ExitProcess(1);
150     }
151
152     if (GetModuleFileNameA( 0, buffer, MAX_PATH ))
153     {
154         HKEY tmpkey;
155
156         if (!RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\AppDefaults", &tmpkey ))
157         {
158            char appname[MAX_PATH+16];
159            char *p = strrchr( buffer, '\\' );
160            if (p!=NULL) {
161                    appname[MAX_PATH]='\0';
162                    strncpy(appname,p+1,MAX_PATH);
163                    strcat(appname,"\\dsound");
164                    TRACE("appname = [%s] \n",appname);
165                    if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
166                        RegCloseKey( tmpkey );
167            }
168         }
169     }
170
171     /* get options */
172
173     if (!get_config_key( hkey, appkey, "EmulDriver", buffer, MAX_PATH ))
174         ds_emuldriver = strcmp(buffer, "N");
175
176     if (!get_config_key( hkey, appkey, "HELmargin", buffer, MAX_PATH ))
177         ds_hel_margin = atoi(buffer);
178
179     if (!get_config_key( hkey, appkey, "HELqueue", buffer, MAX_PATH ))
180         ds_hel_queue = atoi(buffer);
181
182     if (!get_config_key( hkey, appkey, "SndQueueMax", buffer, MAX_PATH ))
183         ds_snd_queue_max = atoi(buffer);
184
185     if (!get_config_key( hkey, appkey, "SndQueueMin", buffer, MAX_PATH ))
186         ds_snd_queue_min = atoi(buffer);
187
188     if (!get_config_key( hkey, appkey, "HardwareAcceleration", buffer, MAX_PATH )) {
189         if (strcmp(buffer, "Full") == 0)
190             ds_hw_accel = DS_HW_ACCEL_FULL;
191         else if (strcmp(buffer, "Standard") == 0)
192             ds_hw_accel = DS_HW_ACCEL_STANDARD;
193         else if (strcmp(buffer, "Basic") == 0)
194             ds_hw_accel = DS_HW_ACCEL_BASIC;
195         else if (strcmp(buffer, "Emulation") == 0)
196             ds_hw_accel = DS_HW_ACCEL_EMULATION;
197     }
198
199     if (!get_config_key( hkey, appkey, "DefaultPlayback", buffer, MAX_PATH ))
200             ds_default_playback = atoi(buffer);
201
202     if (!get_config_key( hkey, appkey, "DefaultCapture", buffer, MAX_PATH ))
203             ds_default_capture = atoi(buffer);
204
205     if (appkey) RegCloseKey( appkey );
206     RegCloseKey( hkey );
207
208     if (ds_emuldriver != DS_EMULDRIVER )
209        WARN("ds_emuldriver = %d (default=%d)\n",ds_emuldriver, DS_EMULDRIVER);
210     if (ds_hel_margin != DS_HEL_MARGIN )
211        WARN("ds_hel_margin = %d (default=%d)\n",ds_hel_margin, DS_HEL_MARGIN );
212     if (ds_hel_queue != DS_HEL_QUEUE )
213        WARN("ds_hel_queue = %d (default=%d)\n",ds_hel_queue, DS_HEL_QUEUE );
214     if (ds_snd_queue_max != DS_SND_QUEUE_MAX)
215        WARN("ds_snd_queue_max = %d (default=%d)\n",ds_snd_queue_max ,DS_SND_QUEUE_MAX);
216     if (ds_snd_queue_min != DS_SND_QUEUE_MIN)
217        WARN("ds_snd_queue_min = %d (default=%d)\n",ds_snd_queue_min ,DS_SND_QUEUE_MIN);
218     if (ds_hw_accel != DS_HW_ACCEL_FULL)
219         WARN("ds_hw_accel = %s (default=Full)\n", 
220             ds_hw_accel==DS_HW_ACCEL_FULL ? "Full" :
221             ds_hw_accel==DS_HW_ACCEL_STANDARD ? "Standard" :
222             ds_hw_accel==DS_HW_ACCEL_BASIC ? "Basic" :
223             ds_hw_accel==DS_HW_ACCEL_EMULATION ? "Emulation" :
224             "Unknown");
225     if (ds_default_playback != 0)
226         WARN("ds_default_playback = %d (default=0)\n",ds_default_playback);
227     if (ds_default_capture != 0)
228         WARN("ds_default_capture = %d (default=0)\n",ds_default_playback);
229 }
230
231
232
233 /***************************************************************************
234  * GetDeviceID  [DSOUND.9]
235  *
236  * Retrieves unique identifier of default device specified
237  *
238  * PARAMS
239  *    pGuidSrc  [I] Address of device GUID.
240  *    pGuidDest [O] Address to receive unique device GUID.
241  *
242  * RETURNS
243  *    Success: DS_OK
244  *    Failure: DSERR_INVALIDPARAM
245  *
246  * NOTES
247  *    pGuidSrc is a valid device GUID or DSDEVID_DefaultPlayback,
248  *    DSDEVID_DefaultCapture, DSDEVID_DefaultVoicePlayback, or 
249  *    DSDEVID_DefaultVoiceCapture.
250  *    Returns pGuidSrc if pGuidSrc is a valid device or the device
251  *    GUID for the specified constants.
252  */
253 HRESULT WINAPI GetDeviceID(LPCGUID pGuidSrc, LPGUID pGuidDest)
254 {
255     if ( ( pGuidSrc == NULL) || (pGuidDest == NULL) ) {
256         WARN("invalid parameter\n");
257         return DSERR_INVALIDPARAM;
258     }
259
260     if ( IsEqualGUID( &DSDEVID_DefaultPlayback, pGuidSrc ) ||
261         IsEqualGUID( &DSDEVID_DefaultVoicePlayback, pGuidSrc ) ) {
262         GUID guid;
263         int err = mmErr(waveOutMessage((HWAVEOUT)ds_default_playback,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
264         if (err == DS_OK) {
265             memcpy(pGuidDest, &guid, sizeof(GUID));
266             return DS_OK;
267         }
268     }
269
270     if ( IsEqualGUID( &DSDEVID_DefaultCapture, pGuidSrc ) ||
271         IsEqualGUID( &DSDEVID_DefaultVoiceCapture, pGuidSrc ) ) {
272         GUID guid;
273         int err = mmErr(waveInMessage((HWAVEIN)ds_default_capture,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
274         if (err == DS_OK) {
275             memcpy(pGuidDest, &guid, sizeof(GUID));
276             return DS_OK;
277         }
278     }
279
280     memcpy(pGuidDest, pGuidSrc, sizeof(GUID));
281
282     return DS_OK;
283 }
284
285
286 /***************************************************************************
287  * DirectSoundEnumerateA [DSOUND.2]
288  *
289  * Enumerate all DirectSound drivers installed in the system
290  *
291  * PARAMS
292  *    lpDSEnumCallback  [I] Address of callback function.
293  *    lpContext         [I] Address of user defined context passed to callback function.
294  *
295  * RETURNS
296  *    Success: DS_OK
297  *    Failure: DSERR_INVALIDPARAM
298  */
299 HRESULT WINAPI DirectSoundEnumerateA(
300     LPDSENUMCALLBACKA lpDSEnumCallback,
301     LPVOID lpContext)
302 {
303     unsigned devs, wod;
304     DSDRIVERDESC desc;
305     GUID guid;
306     int err;
307
308     TRACE("lpDSEnumCallback = %p, lpContext = %p\n",
309         lpDSEnumCallback, lpContext);
310
311     if (lpDSEnumCallback == NULL) {
312         WARN("invalid parameter: lpDSEnumCallback == NULL\n");
313         return DSERR_INVALIDPARAM;
314     }
315
316     devs = waveOutGetNumDevs();
317     if (devs > 0) {
318         if (GetDeviceID(&DSDEVID_DefaultPlayback, &guid) == DS_OK) {
319             GUID temp;
320             for (wod = 0; wod < devs; ++wod) {
321                 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&temp,0));
322                 if (err == DS_OK) {
323                     if (IsEqualGUID( &guid, &temp ) ) {
324                         err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
325                         if (err == DS_OK) {
326                             TRACE("calling lpDSEnumCallback(%s,\"%s\",\"%s\",%p)\n",
327                                 debugstr_guid(&DSDEVID_DefaultPlayback),"Primary Sound Driver",desc.szDrvName,lpContext);
328                             if (lpDSEnumCallback((LPGUID)&DSDEVID_DefaultPlayback, "Primary Sound Driver", desc.szDrvName, lpContext) == FALSE)
329                                 return DS_OK;
330                         }
331                     }
332                 }
333             }
334         }
335     }
336
337     for (wod = 0; wod < devs; ++wod) {
338         err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
339         if (err == DS_OK) {
340             err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
341             if (err == DS_OK) {
342                 TRACE("calling lpDSEnumCallback(%s,\"%s\",\"%s\",%p)\n",
343                     debugstr_guid(&guid),desc.szDesc,desc.szDrvName,lpContext);
344                 if (lpDSEnumCallback(&guid, desc.szDesc, desc.szDrvName, lpContext) == FALSE)
345                     return DS_OK;
346             }
347         }
348     }
349     return DS_OK;
350 }
351
352 /***************************************************************************
353  * DirectSoundEnumerateW [DSOUND.3]
354  *
355  * Enumerate all DirectSound drivers installed in the system
356  *
357  * PARAMS
358  *    lpDSEnumCallback  [I] Address of callback function.
359  *    lpContext         [I] Address of user defined context passed to callback function.
360  *
361  * RETURNS
362  *    Success: DS_OK
363  *    Failure: DSERR_INVALIDPARAM
364  */
365 HRESULT WINAPI DirectSoundEnumerateW(
366         LPDSENUMCALLBACKW lpDSEnumCallback,
367         LPVOID lpContext )
368 {
369     unsigned devs, wod;
370     DSDRIVERDESC desc;
371     GUID guid;
372     int err;
373     WCHAR wDesc[MAXPNAMELEN];
374     WCHAR wName[MAXPNAMELEN];
375
376     TRACE("lpDSEnumCallback = %p, lpContext = %p\n",
377         lpDSEnumCallback, lpContext);
378
379     if (lpDSEnumCallback == NULL) {
380         WARN("invalid parameter: lpDSEnumCallback == NULL\n");
381         return DSERR_INVALIDPARAM;
382     }
383
384     devs = waveOutGetNumDevs();
385     if (devs > 0) {
386         if (GetDeviceID(&DSDEVID_DefaultPlayback, &guid) == DS_OK) {
387             GUID temp;
388             for (wod = 0; wod < devs; ++wod) {
389                 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&temp,0));
390                 if (err == DS_OK) {
391                     if (IsEqualGUID( &guid, &temp ) ) {
392                         err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
393                         if (err == DS_OK) {
394                             TRACE("calling lpDSEnumCallback(%s,\"%s\",\"%s\",%p)\n",
395                                 debugstr_guid(&DSDEVID_DefaultPlayback),"Primary Sound Driver",desc.szDrvName,lpContext);
396                             MultiByteToWideChar( CP_ACP, 0, "Primary Sound Driver", -1,
397                                 wDesc, sizeof(wDesc)/sizeof(WCHAR) );
398                                 MultiByteToWideChar( CP_ACP, 0, desc.szDrvName, -1,
399                                 wName, sizeof(wName)/sizeof(WCHAR) );
400                             if (lpDSEnumCallback((LPGUID)&DSDEVID_DefaultPlayback, wDesc, wName, lpContext) == FALSE)
401                                 return DS_OK;
402                         }
403                     }
404                 }
405             }
406         }
407     }
408
409     for (wod = 0; wod < devs; ++wod) {
410         err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDDESC,(DWORD)&desc,0));
411         if (err == DS_OK) {
412             err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)&guid,0));
413             if (err == DS_OK) {
414                 TRACE("calling lpDSEnumCallback(%s,\"%s\",\"%s\",%p)\n",
415                     debugstr_guid(&guid),desc.szDesc,desc.szDrvName,lpContext);
416                 MultiByteToWideChar( CP_ACP, 0, desc.szDesc, -1,
417                     wDesc, sizeof(wDesc)/sizeof(WCHAR) );
418                     MultiByteToWideChar( CP_ACP, 0, desc.szDrvName, -1,
419                     wName, sizeof(wName)/sizeof(WCHAR) );
420                 if (lpDSEnumCallback(&guid, wDesc, wName, lpContext) == FALSE)
421                     return DS_OK;
422             }
423         }
424     }
425     return DS_OK;
426 }
427
428
429 static void _dump_DSBCAPS(DWORD xmask) {
430         struct {
431                 DWORD   mask;
432                 char    *name;
433         } flags[] = {
434 #define FE(x) { x, #x },
435                 FE(DSBCAPS_PRIMARYBUFFER)
436                 FE(DSBCAPS_STATIC)
437                 FE(DSBCAPS_LOCHARDWARE)
438                 FE(DSBCAPS_LOCSOFTWARE)
439                 FE(DSBCAPS_CTRL3D)
440                 FE(DSBCAPS_CTRLFREQUENCY)
441                 FE(DSBCAPS_CTRLPAN)
442                 FE(DSBCAPS_CTRLVOLUME)
443                 FE(DSBCAPS_CTRLPOSITIONNOTIFY)
444                 FE(DSBCAPS_CTRLDEFAULT)
445                 FE(DSBCAPS_CTRLALL)
446                 FE(DSBCAPS_STICKYFOCUS)
447                 FE(DSBCAPS_GLOBALFOCUS)
448                 FE(DSBCAPS_GETCURRENTPOSITION2)
449                 FE(DSBCAPS_MUTE3DATMAXDISTANCE)
450 #undef FE
451         };
452         int     i;
453
454         for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
455                 if ((flags[i].mask & xmask) == flags[i].mask)
456                         DPRINTF("%s ",flags[i].name);
457 }
458
459 /*******************************************************************************
460  *              IDirectSound
461  */
462
463 static HRESULT WINAPI IDirectSoundImpl_SetCooperativeLevel(
464         LPDIRECTSOUND8 iface,HWND hwnd,DWORD level
465 ) {
466         ICOM_THIS(IDirectSoundImpl,iface);
467
468         FIXME("(%p,%08lx,%ld):stub\n",This,(DWORD)hwnd,level);
469
470         This->priolevel = level;
471
472         return DS_OK;
473 }
474
475 static HRESULT WINAPI IDirectSoundImpl_CreateSoundBuffer(
476         LPDIRECTSOUND8 iface,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER8 ppdsb,LPUNKNOWN lpunk
477 ) {
478         ICOM_THIS(IDirectSoundImpl,iface);
479         LPWAVEFORMATEX  wfex;
480
481         TRACE("(%p,%p,%p,%p)\n",This,dsbd,ppdsb,lpunk);
482
483         if ((This == NULL) || (dsbd == NULL) || (ppdsb == NULL))
484                 return DSERR_INVALIDPARAM;
485
486         if (TRACE_ON(dsound)) {
487                 TRACE("(structsize=%ld)\n",dsbd->dwSize);
488                 TRACE("(flags=0x%08lx:\n",dsbd->dwFlags);
489                 _dump_DSBCAPS(dsbd->dwFlags);
490                 DPRINTF(")\n");
491                 TRACE("(bufferbytes=%ld)\n",dsbd->dwBufferBytes);
492                 TRACE("(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
493         }
494
495         wfex = dsbd->lpwfxFormat;
496
497         if (wfex)
498                 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
499                    "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
500                    wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
501                    wfex->nAvgBytesPerSec, wfex->nBlockAlign,
502                    wfex->wBitsPerSample, wfex->cbSize);
503
504         if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)
505                 return PrimaryBuffer_Create(This, (PrimaryBufferImpl**)ppdsb, dsbd);
506         else
507                 return SecondaryBuffer_Create(This, (IDirectSoundBufferImpl**)ppdsb, dsbd);
508 }
509
510 static HRESULT WINAPI IDirectSoundImpl_DuplicateSoundBuffer(
511         LPDIRECTSOUND8 iface,LPDIRECTSOUNDBUFFER8 pdsb,LPLPDIRECTSOUNDBUFFER8 ppdsb
512 ) {
513         ICOM_THIS(IDirectSoundImpl,iface);
514         IDirectSoundBufferImpl* ipdsb=(IDirectSoundBufferImpl*)pdsb;
515         IDirectSoundBufferImpl** ippdsb=(IDirectSoundBufferImpl**)ppdsb;
516         TRACE("(%p,%p,%p)\n",This,ipdsb,ippdsb);
517
518         if (ipdsb->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
519                 ERR("trying to duplicate primary buffer\n");
520                 return DSERR_INVALIDCALL;
521         }
522
523         if (ipdsb->hwbuf) {
524                 FIXME("need to duplicate hardware buffer\n");
525         }
526
527         if (ipdsb->dsbd.dwFlags & DSBCAPS_CTRL3D) {
528                 FIXME("need to duplicate 3D buffer\n");
529         }
530
531         *ippdsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBufferImpl));
532
533         IDirectSoundBuffer8_AddRef(pdsb);
534         memcpy(*ippdsb, ipdsb, sizeof(IDirectSoundBufferImpl));
535         (*ippdsb)->ref = 1;
536         (*ippdsb)->state = STATE_STOPPED;
537         (*ippdsb)->playpos = 0;
538         (*ippdsb)->buf_mixpos = 0;
539         (*ippdsb)->dsound = This;
540         (*ippdsb)->parent = ipdsb;
541         (*ippdsb)->hwbuf = NULL;
542         (*ippdsb)->ds3db = NULL; /* FIXME? */
543         (*ippdsb)->iks = NULL; /* FIXME? */
544         memcpy(&((*ippdsb)->wfx), &(ipdsb->wfx), sizeof((*ippdsb)->wfx));
545         InitializeCriticalSection(&(*ippdsb)->lock);
546         /* register buffer */
547         RtlAcquireResourceExclusive(&(This->lock), TRUE);
548         {
549                 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl**)*(This->nrofbuffers+1));
550                 if (newbuffers) {
551                         This->buffers = newbuffers;
552                         This->buffers[This->nrofbuffers] = *ippdsb;
553                         This->nrofbuffers++;
554                         TRACE("buffer count is now %d\n", This->nrofbuffers);
555                 } else {
556                         ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
557                         /* FIXME: release buffer */
558                 }
559         }
560         RtlReleaseResource(&(This->lock));
561         IDirectSound_AddRef(iface);
562         return DS_OK;
563 }
564
565
566 static HRESULT WINAPI IDirectSoundImpl_GetCaps(LPDIRECTSOUND8 iface,LPDSCAPS caps) {
567         ICOM_THIS(IDirectSoundImpl,iface);
568         TRACE("(%p,%p)\n",This,caps);
569
570         if (caps == NULL || caps->dwSize!=sizeof(*caps))
571                 return DSERR_INVALIDPARAM;
572
573         caps->dwFlags = This->drvcaps.dwFlags;
574         TRACE("(flags=0x%08lx)\n",caps->dwFlags);
575
576         /* FIXME: copy caps from This->drvcaps */
577         caps->dwMinSecondarySampleRate          = DSBFREQUENCY_MIN;
578         caps->dwMaxSecondarySampleRate          = DSBFREQUENCY_MAX;
579
580         caps->dwPrimaryBuffers                  = 1;
581
582         caps->dwMaxHwMixingAllBuffers           = 0;
583         caps->dwMaxHwMixingStaticBuffers        = 0;
584         caps->dwMaxHwMixingStreamingBuffers     = 0;
585
586         caps->dwFreeHwMixingAllBuffers          = 0;
587         caps->dwFreeHwMixingStaticBuffers       = 0;
588         caps->dwFreeHwMixingStreamingBuffers    = 0;
589
590         caps->dwMaxHw3DAllBuffers               = 0;
591         caps->dwMaxHw3DStaticBuffers            = 0;
592         caps->dwMaxHw3DStreamingBuffers         = 0;
593
594         caps->dwFreeHw3DAllBuffers              = 0;
595         caps->dwFreeHw3DStaticBuffers           = 0;
596         caps->dwFreeHw3DStreamingBuffers        = 0;
597
598         caps->dwTotalHwMemBytes                 = 0;
599
600         caps->dwFreeHwMemBytes                  = 0;
601
602         caps->dwMaxContigFreeHwMemBytes         = 0;
603
604         caps->dwUnlockTransferRateHwBuffers     = 4096; /* But we have none... */
605
606         caps->dwPlayCpuOverheadSwBuffers        = 1;    /* 1% */
607
608         return DS_OK;
609 }
610
611 static ULONG WINAPI IDirectSoundImpl_AddRef(LPDIRECTSOUND8 iface) {
612         ICOM_THIS(IDirectSoundImpl,iface);
613         return ++(This->ref);
614 }
615
616 static ULONG WINAPI IDirectSoundImpl_Release(LPDIRECTSOUND8 iface) {
617         ICOM_THIS(IDirectSoundImpl,iface);
618         TRACE("(%p), ref was %ld\n",This,This->ref);
619         if (!--(This->ref)) {
620                 UINT i;
621
622                 timeKillEvent(This->timerID);
623                 timeEndPeriod(DS_TIME_RES);
624
625                 if (This->buffers) {
626                         for( i=0;i<This->nrofbuffers;i++)
627                                 IDirectSoundBuffer8_Release((LPDIRECTSOUNDBUFFER8)This->buffers[i]);
628                 }
629
630                 DSOUND_PrimaryDestroy(This);
631
632                 RtlDeleteResource(&This->lock);
633                 DeleteCriticalSection(&This->mixlock);
634                 if (This->driver) {
635                         IDsDriver_Close(This->driver);
636                 }
637                 if (This->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) {
638                         waveOutClose(This->hwo);
639                 }
640                 if (This->driver)
641                         IDsDriver_Release(This->driver);
642
643                 HeapFree(GetProcessHeap(),0,This);
644                 dsound = NULL;
645                 return 0;
646         }
647         return This->ref;
648 }
649
650 static HRESULT WINAPI IDirectSoundImpl_SetSpeakerConfig(
651         LPDIRECTSOUND8 iface,DWORD config
652 ) {
653         ICOM_THIS(IDirectSoundImpl,iface);
654         FIXME("(%p,0x%08lx):stub\n",This,config);
655         return DS_OK;
656 }
657
658 static HRESULT WINAPI IDirectSoundImpl_QueryInterface(
659         LPDIRECTSOUND8 iface,REFIID riid,LPVOID *ppobj
660 ) {
661         ICOM_THIS(IDirectSoundImpl,iface);
662
663         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
664                 ERR("app requested IDirectSound3DListener on dsound object\n");
665                 *ppobj = NULL;
666                 return E_FAIL;
667         }
668
669         FIXME("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
670         return E_NOINTERFACE;
671 }
672
673 static HRESULT WINAPI IDirectSoundImpl_Compact(
674         LPDIRECTSOUND8 iface)
675 {
676         ICOM_THIS(IDirectSoundImpl,iface);
677         TRACE("(%p)\n", This);
678         return DS_OK;
679 }
680
681 static HRESULT WINAPI IDirectSoundImpl_GetSpeakerConfig(
682         LPDIRECTSOUND8 iface,
683         LPDWORD lpdwSpeakerConfig)
684 {
685         ICOM_THIS(IDirectSoundImpl,iface);
686         TRACE("(%p, %p)\n", This, lpdwSpeakerConfig);
687         *lpdwSpeakerConfig = DSSPEAKER_STEREO | (DSSPEAKER_GEOMETRY_NARROW << 16);
688         return DS_OK;
689 }
690
691 static HRESULT WINAPI IDirectSoundImpl_Initialize(
692         LPDIRECTSOUND8 iface,
693         LPCGUID lpcGuid)
694 {
695         ICOM_THIS(IDirectSoundImpl,iface);
696         TRACE("(%p, %p)\n", This, lpcGuid);
697         return DS_OK;
698 }
699
700 static HRESULT WINAPI IDirectSoundImpl_VerifyCertification(
701         LPDIRECTSOUND8 iface,
702         LPDWORD pdwCertified)
703 {
704         ICOM_THIS(IDirectSoundImpl,iface);
705         TRACE("(%p, %p)\n", This, pdwCertified);
706         *pdwCertified = DS_CERTIFIED;
707         return DS_OK;
708 }
709
710 static ICOM_VTABLE(IDirectSound8) dsvt =
711 {
712         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
713         IDirectSoundImpl_QueryInterface,
714         IDirectSoundImpl_AddRef,
715         IDirectSoundImpl_Release,
716         IDirectSoundImpl_CreateSoundBuffer,
717         IDirectSoundImpl_GetCaps,
718         IDirectSoundImpl_DuplicateSoundBuffer,
719         IDirectSoundImpl_SetCooperativeLevel,
720         IDirectSoundImpl_Compact,
721         IDirectSoundImpl_GetSpeakerConfig,
722         IDirectSoundImpl_SetSpeakerConfig,
723         IDirectSoundImpl_Initialize,
724         IDirectSoundImpl_VerifyCertification
725 };
726
727
728 /*******************************************************************************
729  *              DirectSoundCreate (DSOUND.1)
730  *
731  *  Creates and initializes a DirectSound interface.
732  *
733  *  PARAMS
734  *     lpcGUID   [I] Address of the GUID that identifies the sound device.
735  *     ppDS      [O] Address of a variable to receive the interface pointer.
736  *     pUnkOuter [I] Must be NULL.
737  *
738  *  RETURNS
739  *     Success: DS_OK
740  *     Failure: DSERR_ALLOCATED, DSERR_INVALIDPARAM, DSERR_NOAGGREGATION, 
741  *              DSERR_NODRIVER, DSERR_OUTOFMEMORY
742  */
743 HRESULT WINAPI DirectSoundCreate8(LPCGUID lpcGUID,LPDIRECTSOUND8 *ppDS,IUnknown *pUnkOuter )
744 {
745         IDirectSoundImpl** ippDS=(IDirectSoundImpl**)ppDS;
746         PIDSDRIVER drv = NULL;
747         unsigned wod, wodn;
748         HRESULT err = DSERR_INVALIDPARAM;
749         GUID devGuid;
750         BOOLEAN found = FALSE;
751
752         TRACE("(%s,%p,%p)\n",debugstr_guid(lpcGUID),ippDS,pUnkOuter);
753         
754         if (ippDS == NULL) {
755                 WARN("invalid parameter\n");
756                 return DSERR_INVALIDPARAM;
757         }
758
759         /* Get dsound configuration */
760         setup_dsound_options();
761
762         /* Default device? */
763         if (!lpcGUID || IsEqualGUID(lpcGUID, &GUID_NULL))
764                 lpcGUID = &DSDEVID_DefaultPlayback;
765
766         if (GetDeviceID(lpcGUID, &devGuid) != DS_OK) {
767                 WARN("invalid parameter\n");
768                 return DSERR_INVALIDPARAM;
769         }
770
771         if (dsound) {
772                 if (IsEqualGUID(&devGuid, &dsound->guid) ) {
773                         ERR("dsound already opened\n");
774                         IDirectSound_AddRef((LPDIRECTSOUND)dsound);
775                         *ippDS = dsound;
776                         return DS_OK;
777                 } else {
778                         ERR("different dsound already opened\n");
779                 }
780         }
781
782         /* Enumerate WINMM audio devices and find the one we want */
783         wodn = waveOutGetNumDevs();
784         if (!wodn) return DSERR_NODRIVER;
785
786         TRACE(" expecting GUID %s.\n", debugstr_guid(&devGuid));
787         
788         for (wod=0; wod<wodn; wod++) {
789                 GUID guid;
790                 err = mmErr(waveOutMessage((HWAVEOUT)wod,DRV_QUERYDSOUNDGUID,(DWORD)(&guid),0));
791                 if (err != DS_OK) {
792                         WARN("waveOutMessage failed; err=%lx\n",err);
793                         return err;
794                 }
795                 TRACE("got GUID %s for wod %d.\n", debugstr_guid(&guid), wod);
796                 if (IsEqualGUID( &devGuid, &guid) ) {
797                         err = DS_OK;
798                         found = TRUE;
799                         break;
800                 }
801         }
802
803         if (err != DS_OK) {
804                 WARN("invalid parameter\n");
805                 return DSERR_INVALIDPARAM;
806         }
807
808         if (found == FALSE) {
809                 WARN("No device found matching given ID - trying with default one !\n");
810                 wod = ds_default_playback;      
811         }
812         
813         /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
814         waveOutMessage((HWAVEOUT)wod, DRV_QUERYDSOUNDIFACE, (DWORD)&drv, 0);
815
816         /* Disable the direct sound driver to force emulation if requested. */
817         if (ds_hw_accel == DS_HW_ACCEL_EMULATION)
818             drv = NULL;
819         
820         /* Allocate memory */
821         *ippDS = (IDirectSoundImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundImpl));
822         if (*ippDS == NULL)
823                 return DSERR_OUTOFMEMORY;
824
825         (*ippDS)->lpVtbl        = &dsvt;
826         (*ippDS)->ref           = 1;
827
828         (*ippDS)->driver        = drv;
829         (*ippDS)->priolevel     = DSSCL_NORMAL;
830         (*ippDS)->fraglen       = 0;
831         (*ippDS)->hwbuf         = NULL;
832         (*ippDS)->buffer        = NULL;
833         (*ippDS)->buflen        = 0;
834         (*ippDS)->writelead     = 0;
835         (*ippDS)->state         = STATE_STOPPED;
836         (*ippDS)->nrofbuffers   = 0;
837         (*ippDS)->buffers       = NULL;
838         (*ippDS)->listener      = NULL;
839
840         (*ippDS)->prebuf        = ds_snd_queue_max;
841         (*ippDS)->guid          = devGuid;
842
843         /* Get driver description */
844         if (drv) {
845                 IDsDriver_GetDriverDesc(drv,&((*ippDS)->drvdesc));
846         } else {
847                 /* if no DirectSound interface available, use WINMM API instead */
848                 (*ippDS)->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
849         }
850
851         (*ippDS)->drvdesc.dnDevNode = wod;
852
853         /* Set default wave format (may need it for waveOutOpen) */
854         (*ippDS)->wfx.wFormatTag        = WAVE_FORMAT_PCM;
855         /* We rely on the sound driver to return the actual sound format of 
856          * the device if it does not support 22050x8x2 and is given the 
857          * WAVE_DIRECTSOUND flag.
858          */
859         (*ippDS)->wfx.nSamplesPerSec = 22050;
860         (*ippDS)->wfx.wBitsPerSample = 8;
861         (*ippDS)->wfx.nChannels = 2;
862         (*ippDS)->wfx.nBlockAlign = (*ippDS)->wfx.wBitsPerSample * (*ippDS)->wfx.nChannels / 8;
863         (*ippDS)->wfx.nAvgBytesPerSec = (*ippDS)->wfx.nSamplesPerSec * (*ippDS)->wfx.nBlockAlign;
864         (*ippDS)->wfx.cbSize = 0;
865
866         /* If the driver requests being opened through MMSYSTEM
867          * (which is recommended by the DDK), it is supposed to happen
868          * before the DirectSound interface is opened */
869         if ((*ippDS)->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
870         {
871                 DWORD flags = CALLBACK_FUNCTION;
872
873                 /* disable direct sound if requested */
874                 if (ds_hw_accel != DS_HW_ACCEL_EMULATION)
875                     flags |= WAVE_DIRECTSOUND;
876
877                 err = mmErr(waveOutOpen(&((*ippDS)->hwo),
878                                           (*ippDS)->drvdesc.dnDevNode, &((*ippDS)->wfx),
879                                           (DWORD)DSOUND_callback, (DWORD)(*ippDS),
880                                           flags));
881         }
882
883         if (drv && (err == DS_OK))
884                 err = IDsDriver_Open(drv);
885
886         /* FIXME: do we want to handle a temporarily busy device? */
887         if (err != DS_OK) {
888                 HeapFree(GetProcessHeap(),0,*ippDS);
889                 *ippDS = NULL;
890                 return err;
891         }
892
893         /* the driver is now open, so it's now allowed to call GetCaps */
894         if (drv) {
895                 IDsDriver_GetCaps(drv,&((*ippDS)->drvcaps));
896         } else {
897                 /* FIXME: We should check the device capabilities */
898                 (*ippDS)->drvcaps.dwFlags =
899                         DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO;
900                 if (ds_emuldriver)
901                     (*ippDS)->drvcaps.dwFlags |= DSCAPS_EMULDRIVER;
902         }
903
904         DSOUND_RecalcVolPan(&((*ippDS)->volpan));
905
906         InitializeCriticalSection(&((*ippDS)->mixlock));
907         RtlInitializeResource(&((*ippDS)->lock));
908
909         if (!dsound) {
910                 dsound = (*ippDS);
911                 DSOUND_PrimaryCreate(dsound);
912                 timeBeginPeriod(DS_TIME_RES);
913                 dsound->timerID = timeSetEvent(DS_TIME_DEL, DS_TIME_RES, DSOUND_timer,
914                                                (DWORD)dsound, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
915         }
916         return DS_OK;
917 }
918
919
920 /*******************************************************************************
921  * DirectSound ClassFactory
922  */
923 typedef struct
924 {
925     /* IUnknown fields */
926     ICOM_VFIELD(IClassFactory);
927     DWORD                       ref;
928 } IClassFactoryImpl;
929
930 static HRESULT WINAPI
931 DSCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
932         ICOM_THIS(IClassFactoryImpl,iface);
933
934         FIXME("(%p)->(%s,%p),stub!\n",This,debugstr_guid(riid),ppobj);
935         return E_NOINTERFACE;
936 }
937
938 static ULONG WINAPI
939 DSCF_AddRef(LPCLASSFACTORY iface) {
940         ICOM_THIS(IClassFactoryImpl,iface);
941         return ++(This->ref);
942 }
943
944 static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface) {
945         ICOM_THIS(IClassFactoryImpl,iface);
946         /* static class, won't be  freed */
947         return --(This->ref);
948 }
949
950 static HRESULT WINAPI DSCF_CreateInstance(
951         LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
952 ) {
953         ICOM_THIS(IClassFactoryImpl,iface);
954
955         TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
956         if ( IsEqualGUID( &IID_IDirectSound, riid ) ||
957              IsEqualGUID( &IID_IDirectSound8, riid ) ) {
958                 /* FIXME: reuse already created dsound if present? */
959                 return DirectSoundCreate8(0,(LPDIRECTSOUND8*)ppobj,pOuter);
960         }
961         if ( IsEqualGUID( &IID_IDirectSoundCapture, riid ) ||
962              IsEqualGUID( &IID_IDirectSoundCapture8, riid ) ) {
963                 return DirectSoundCaptureCreate8(0,(LPDIRECTSOUNDCAPTURE8*)ppobj,pOuter);
964         }
965         if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
966                 return IKsPropertySetImpl_Create(0,(IKsPropertySetImpl**)ppobj);
967         }
968
969         FIXME("(%p,%p,%s,%p) Interface not found!\n",This,pOuter,debugstr_guid(riid),ppobj);    
970         return E_NOINTERFACE;
971 }
972
973 static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
974         ICOM_THIS(IClassFactoryImpl,iface);
975         FIXME("(%p)->(%d),stub!\n",This,dolock);
976         return S_OK;
977 }
978
979 static ICOM_VTABLE(IClassFactory) DSCF_Vtbl = {
980         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
981         DSCF_QueryInterface,
982         DSCF_AddRef,
983         DSCF_Release,
984         DSCF_CreateInstance,
985         DSCF_LockServer
986 };
987 static IClassFactoryImpl DSOUND_CF = {&DSCF_Vtbl, 1 };
988
989 /*******************************************************************************
990  * DllGetClassObject [DSOUND.5]
991  * Retrieves class object from a DLL object
992  *
993  * NOTES
994  *    Docs say returns STDAPI
995  *
996  * PARAMS
997  *    rclsid [I] CLSID for the class object
998  *    riid   [I] Reference to identifier of interface for class object
999  *    ppv    [O] Address of variable to receive interface pointer for riid
1000  *
1001  * RETURNS
1002  *    Success: S_OK
1003  *    Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
1004  *             E_UNEXPECTED
1005  */
1006 DWORD WINAPI DSOUND_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
1007 {
1008     TRACE("(%s,%s,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
1009     if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
1010         *ppv = (LPVOID)&DSOUND_CF;
1011         IClassFactory_AddRef((IClassFactory*)*ppv);
1012         return S_OK;
1013     }
1014
1015     FIXME("(%s,%s,%p): no interface found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
1016     return CLASS_E_CLASSNOTAVAILABLE;
1017 }
1018
1019
1020 /*******************************************************************************
1021  * DllCanUnloadNow [DSOUND.4]  
1022  * Determines whether the DLL is in use.
1023  *
1024  * RETURNS
1025  *    Success: S_OK
1026  *    Failure: S_FALSE
1027  */
1028 DWORD WINAPI DSOUND_DllCanUnloadNow(void)
1029 {
1030     FIXME("(void): stub\n");
1031     return S_FALSE;
1032 }