Remove Get/SetBeepActive from USER driver and manage it locally inside
[wine] / dlls / dsound / dsound_main.c
1 /*                      DirectSound
2  * 
3  * Copyright 1998 Marcus Meissner
4  * Copyright 1998 Rob Riggs
5  * Copyright 2000-2001 TransGaming Technologies, Inc.
6  */
7 /*
8  * Most thread locking is complete. There may be a few race
9  * conditions still lurking.
10  *
11  * Tested with a Soundblaster clone, a Gravis UltraSound Classic,
12  * and a Turtle Beach Tropez+.
13  *
14  * TODO:
15  *      Implement DirectSoundCapture API
16  *      Implement SetCooperativeLevel properly (need to address focus issues)
17  *      Implement DirectSound3DBuffers (stubs in place)
18  *      Use hardware 3D support if available
19  *      Add critical section locking inside Release and AddRef methods
20  *      Handle static buffers - put those in hardware, non-static not in hardware
21  *      Hardware DuplicateSoundBuffer
22  *      Proper volume calculation, and setting volume in HEL primary buffer
23  *      Optimize WINMM and negotiate fragment size, decrease DS_HEL_MARGIN
24  */
25
26 #include "config.h"
27 #include <assert.h>
28 #include <stdio.h>
29 #include <sys/types.h>
30 #include <sys/fcntl.h>
31 #include <unistd.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <math.h>       /* Insomnia - pow() function */
35
36 #include "windef.h"
37 #include "winbase.h"
38 #include "wingdi.h"
39 #include "winuser.h"
40 #include "winerror.h"
41 #include "mmsystem.h"
42 #include "mmddk.h"
43 #include "wine/windef16.h"
44 #include "debugtools.h"
45 #include "dsound.h"
46 #include "dsdriver.h"
47
48 DEFAULT_DEBUG_CHANNEL(dsound);
49
50 /* these are eligible for tuning... they must be high on slow machines... */
51 /* especially since the WINMM overhead is pretty high, and could be improved quite a bit;
52  * the high DS_HEL_MARGIN reflects the currently high wineoss/HEL latency
53  * some settings here should probably get ported to wine.conf */
54 #define DS_EMULDRIVER 1 /* some games (Quake 2, UT) refuse to accept
55                                 emulated dsound devices. set to 0 ! */ 
56 #define DS_HEL_FRAGS 48 /* HEL only: number of waveOut fragments in primary buffer */
57 #define DS_HEL_MARGIN 4 /* HEL only: number of waveOut fragments ahead to mix in new buffers */
58
59 #define DS_SND_QUEUE 28 /* max number of fragments to prebuffer */
60
61 /* Linux does not support better timing than 10ms */
62 #define DS_TIME_RES 10  /* Resolution of multimedia timer */
63 #define DS_TIME_DEL 10  /* Delay of multimedia timer callback, and duration of HEL fragment */
64
65 /*****************************************************************************
66  * Predeclare the interface implementation structures
67  */
68 typedef struct IDirectSoundImpl IDirectSoundImpl;
69 typedef struct IDirectSoundBufferImpl IDirectSoundBufferImpl;
70 typedef struct IDirectSoundNotifyImpl IDirectSoundNotifyImpl;
71 typedef struct IDirectSound3DListenerImpl IDirectSound3DListenerImpl;
72 typedef struct IDirectSound3DBufferImpl IDirectSound3DBufferImpl;
73 typedef struct IDirectSoundCaptureImpl IDirectSoundCaptureImpl;
74 typedef struct IDirectSoundCaptureBufferImpl IDirectSoundCaptureBufferImpl;
75
76 /*****************************************************************************
77  * IDirectSound implementation structure
78  */
79 struct IDirectSoundImpl
80 {
81     /* IUnknown fields */
82     ICOM_VFIELD(IDirectSound);
83     DWORD                      ref;
84     /* IDirectSoundImpl fields */
85     PIDSDRIVER                  driver;
86     DSDRIVERDESC                drvdesc;
87     DSDRIVERCAPS                drvcaps;
88     HWAVEOUT                    hwo;
89     LPWAVEHDR                   pwave[DS_HEL_FRAGS];
90     UINT                        timerID, pwplay, pwwrite, pwqueue;
91     DWORD                       fraglen;
92     DWORD                       priolevel;
93     int                         nrofbuffers;
94     IDirectSoundBufferImpl**    buffers;
95     IDirectSoundBufferImpl*     primary;
96     IDirectSound3DListenerImpl* listener;
97     WAVEFORMATEX                wfx; /* current main waveformat */
98     CRITICAL_SECTION            lock;
99 };
100
101 /*****************************************************************************
102  * IDirectSoundBuffer implementation structure
103  */
104 struct IDirectSoundBufferImpl
105 {
106     /* IUnknown fields */
107     ICOM_VFIELD(IDirectSoundBuffer);
108     DWORD                            ref;
109     /* IDirectSoundBufferImpl fields */
110     PIDSDRIVERBUFFER          hwbuf;
111     WAVEFORMATEX              wfx;
112     LPBYTE                    buffer;
113     IDirectSound3DBufferImpl* ds3db;
114     DWORD                     playflags,state,leadin;
115     DWORD                     playpos,startpos,writelead,buflen;
116     DWORD                     nAvgBytesPerSec;
117     DWORD                     freq;
118     DSVOLUMEPAN               volpan;
119     IDirectSoundBufferImpl*   parent;         /* for duplicates */
120     IDirectSoundImpl*         dsound;
121     DSBUFFERDESC              dsbd;
122     LPDSBPOSITIONNOTIFY       notifies;
123     int                       nrofnotifies;
124     CRITICAL_SECTION          lock;
125     /* used for frequency conversion (PerfectPitch) */
126     ULONG                     freqAdjust, freqAcc;
127     /* used for intelligent (well, sort of) prebuffering */
128     DWORD                     probably_valid_to;
129     DWORD                     primary_mixpos, buf_mixpos;
130 };
131
132 #define STATE_STOPPED  0
133 #define STATE_STARTING 1
134 #define STATE_PLAYING  2
135 #define STATE_STOPPING 3
136
137 /*****************************************************************************
138  * IDirectSoundNotify implementation structure
139  */
140 struct IDirectSoundNotifyImpl
141 {
142     /* IUnknown fields */
143     ICOM_VFIELD(IDirectSoundNotify);
144     DWORD                            ref;
145     /* IDirectSoundNotifyImpl fields */
146     IDirectSoundBufferImpl* dsb;
147 };
148
149 /*****************************************************************************
150  *  IDirectSound3DListener implementation structure
151  */
152 struct IDirectSound3DListenerImpl
153 {
154     /* IUnknown fields */
155     ICOM_VFIELD(IDirectSound3DListener);
156     DWORD                                ref;
157     /* IDirectSound3DListenerImpl fields */
158     IDirectSoundBufferImpl* dsb;
159     DS3DLISTENER            ds3dl;
160     CRITICAL_SECTION        lock;   
161 };
162
163 /*****************************************************************************
164  * IDirectSound3DBuffer implementation structure
165  */
166 struct IDirectSound3DBufferImpl
167 {
168     /* IUnknown fields */
169     ICOM_VFIELD(IDirectSound3DBuffer);
170     DWORD                              ref;
171     /* IDirectSound3DBufferImpl fields */
172     IDirectSoundBufferImpl* dsb;
173     DS3DBUFFER              ds3db;
174     LPBYTE                  buffer;
175     DWORD                   buflen;
176     CRITICAL_SECTION        lock;
177 };
178
179
180 /*****************************************************************************
181  * IDirectSoundCapture implementation structure
182  */
183 struct IDirectSoundCaptureImpl
184 {
185     /* IUnknown fields */
186     ICOM_VFIELD(IDirectSoundCapture);
187     DWORD                              ref;
188
189     /* IDirectSoundCaptureImpl fields */
190     CRITICAL_SECTION        lock;
191 };
192
193 /*****************************************************************************
194  * IDirectSoundCapture implementation structure
195  */
196 struct IDirectSoundCaptureBufferImpl
197 {
198     /* IUnknown fields */
199     ICOM_VFIELD(IDirectSoundCaptureBuffer);
200     DWORD                              ref;
201
202     /* IDirectSoundCaptureBufferImpl fields */
203     CRITICAL_SECTION        lock;
204 };
205
206
207 /* #define USE_DSOUND3D 1 */
208
209 #define DSOUND_FREQSHIFT (14)
210
211 static IDirectSoundImpl*        dsound = NULL;
212
213 static IDirectSoundBufferImpl*  primarybuf = NULL;
214
215 static void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len);
216
217 static HRESULT DSOUND_CreateDirectSoundCapture( LPVOID* ppobj );
218 static HRESULT DSOUND_CreateDirectSoundCaptureBuffer( LPCDSCBUFFERDESC lpcDSCBufferDesc, LPVOID* ppobj );
219
220 static ICOM_VTABLE(IDirectSoundCapture) dscvt;
221 static ICOM_VTABLE(IDirectSoundCaptureBuffer) dscbvt;
222
223 static HRESULT mmErr(UINT err)
224 {
225         switch(err) {
226         case MMSYSERR_NOERROR:
227                 return DS_OK;
228         case MMSYSERR_ALLOCATED:
229                 return DSERR_ALLOCATED;
230         case MMSYSERR_INVALHANDLE:
231                 return DSERR_GENERIC; /* FIXME */
232         case MMSYSERR_NODRIVER:
233                 return DSERR_NODRIVER;
234         case MMSYSERR_NOMEM:
235                 return DSERR_OUTOFMEMORY;
236         case MMSYSERR_INVALPARAM:
237                 return DSERR_INVALIDPARAM;
238         default:
239                 FIXME("Unknown MMSYS error %d\n",err);
240                 return DSERR_GENERIC;
241         }
242 }
243
244 /***************************************************************************
245  * DirectSoundEnumerateA [DSOUND.2]  
246  *
247  * Enumerate all DirectSound drivers installed in the system
248  *
249  * RETURNS
250  *    Success: DS_OK
251  *    Failure: DSERR_INVALIDPARAM
252  */
253 HRESULT WINAPI DirectSoundEnumerateA(
254         LPDSENUMCALLBACKA lpDSEnumCallback,
255         LPVOID lpContext)
256 {
257         TRACE("lpDSEnumCallback = %p, lpContext = %p\n", 
258                 lpDSEnumCallback, lpContext);
259
260 #ifdef HAVE_OSS
261         if (lpDSEnumCallback != NULL)
262                 lpDSEnumCallback(NULL,"WINE DirectSound using Open Sound System",
263                     "sound",lpContext);
264 #endif
265
266         return DS_OK;
267 }
268
269 /***************************************************************************
270  * DirectSoundEnumerateW [DSOUND.3]  
271  *
272  * Enumerate all DirectSound drivers installed in the system
273  *
274  * RETURNS
275  *    Success: DS_OK
276  *    Failure: DSERR_INVALIDPARAM
277  */
278 HRESULT WINAPI DirectSoundEnumerateW(
279         LPDSENUMCALLBACKW lpDSEnumCallback, 
280         LPVOID lpContext )
281 {
282         FIXME("lpDSEnumCallback = %p, lpContext = %p: stub\n", 
283                 lpDSEnumCallback, lpContext);
284
285         return DS_OK;
286 }
287
288
289 static void _dump_DSBCAPS(DWORD xmask) {
290         struct {
291                 DWORD   mask;
292                 char    *name;
293         } flags[] = {
294 #define FE(x) { x, #x },
295                 FE(DSBCAPS_PRIMARYBUFFER)
296                 FE(DSBCAPS_STATIC)
297                 FE(DSBCAPS_LOCHARDWARE)
298                 FE(DSBCAPS_LOCSOFTWARE)
299                 FE(DSBCAPS_CTRL3D)
300                 FE(DSBCAPS_CTRLFREQUENCY)
301                 FE(DSBCAPS_CTRLPAN)
302                 FE(DSBCAPS_CTRLVOLUME)
303                 FE(DSBCAPS_CTRLPOSITIONNOTIFY)
304                 FE(DSBCAPS_CTRLDEFAULT)
305                 FE(DSBCAPS_CTRLALL)
306                 FE(DSBCAPS_STICKYFOCUS)
307                 FE(DSBCAPS_GLOBALFOCUS)
308                 FE(DSBCAPS_GETCURRENTPOSITION2)
309                 FE(DSBCAPS_MUTE3DATMAXDISTANCE)
310 #undef FE
311         };
312         int     i;
313
314         for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
315                 if ((flags[i].mask & xmask) == flags[i].mask)
316                         DPRINTF("%s ",flags[i].name);
317 }
318
319 /*******************************************************************************
320  *              IDirectSound3DBuffer
321  */
322
323 /* IUnknown methods */
324 #ifdef USE_DSOUND3D
325 static HRESULT WINAPI IDirectSound3DBufferImpl_QueryInterface(
326         LPDIRECTSOUND3DBUFFER iface, REFIID riid, LPVOID *ppobj)
327 {
328         ICOM_THIS(IDirectSound3DBufferImpl,iface);
329
330         TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
331         return E_FAIL;
332 }
333 #endif
334
335 #ifdef USE_DSOUND3D
336 static ULONG WINAPI IDirectSound3DBufferImpl_AddRef(LPDIRECTSOUND3DBUFFER iface)
337 {
338         ICOM_THIS(IDirectSound3DBufferImpl,iface);
339         This->ref++;
340         return This->ref;
341 }
342 #endif
343
344 #ifdef USE_DSOUND3D
345 static ULONG WINAPI IDirectSound3DBufferImpl_Release(LPDIRECTSOUND3DBUFFER iface)
346 {
347         ICOM_THIS(IDirectSound3DBufferImpl,iface);
348
349         TRACE("(%p) ref was %ld\n", This, This->ref);
350
351         if(--This->ref)
352                 return This->ref;
353
354         if (This->dsb)
355                 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
356
357         DeleteCriticalSection(&This->lock);
358
359         HeapFree(GetProcessHeap(),0,This->buffer);
360         HeapFree(GetProcessHeap(),0,This);
361
362         return 0;
363 }
364 #endif
365
366 /* IDirectSound3DBuffer methods */
367 #ifdef USE_DSOUND3D
368 static HRESULT WINAPI IDirectSound3DBufferImpl_GetAllParameters(
369         LPDIRECTSOUND3DBUFFER iface,
370         LPDS3DBUFFER lpDs3dBuffer)
371 {
372         FIXME("stub\n");
373         return DS_OK;
374 }
375 #endif
376
377 #ifdef USE_DSOUND3D
378 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeAngles(
379         LPDIRECTSOUND3DBUFFER iface,
380         LPDWORD lpdwInsideConeAngle,
381         LPDWORD lpdwOutsideConeAngle)
382 {
383         FIXME("stub\n");
384         return DS_OK;
385 }
386 #endif
387
388 #ifdef USE_DSOUND3D
389 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeOrientation(
390         LPDIRECTSOUND3DBUFFER iface,
391         LPD3DVECTOR lpvConeOrientation)
392 {
393         FIXME("stub\n");
394         return DS_OK;
395 }
396 #endif
397
398 #ifdef USE_DSOUND3D
399 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeOutsideVolume(
400         LPDIRECTSOUND3DBUFFER iface,
401         LPLONG lplConeOutsideVolume)
402 {
403         FIXME("stub\n");
404         return DS_OK;
405 }
406 #endif
407
408 #ifdef USE_DSOUND3D
409 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMaxDistance(
410         LPDIRECTSOUND3DBUFFER iface,
411         LPD3DVALUE lpfMaxDistance)
412 {
413         FIXME("stub\n");
414         return DS_OK;
415 }
416 #endif
417
418 #ifdef USE_DSOUND3D
419 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMinDistance(
420         LPDIRECTSOUND3DBUFFER iface,
421         LPD3DVALUE lpfMinDistance)
422 {
423         FIXME("stub\n");
424         return DS_OK;
425 }
426 #endif
427
428 #ifdef USE_DSOUND3D
429 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMode(
430         LPDIRECTSOUND3DBUFFER iface,
431         LPDWORD lpdwMode)
432 {
433         FIXME("stub\n");
434         return DS_OK;
435 }
436 #endif
437
438 #ifdef USE_DSOUND3D
439 static HRESULT WINAPI IDirectSound3DBufferImpl_GetPosition(
440         LPDIRECTSOUND3DBUFFER iface,
441         LPD3DVECTOR lpvPosition)
442 {
443         FIXME("stub\n");
444         return DS_OK;
445 }
446 #endif
447
448 #ifdef USE_DSOUND3D
449 static HRESULT WINAPI IDirectSound3DBufferImpl_GetVelocity(
450         LPDIRECTSOUND3DBUFFER iface,
451         LPD3DVECTOR lpvVelocity)
452 {
453         FIXME("stub\n");
454         return DS_OK;
455 }
456 #endif
457
458 #ifdef USE_DSOUND3D
459 static HRESULT WINAPI IDirectSound3DBufferImpl_SetAllParameters(
460         LPDIRECTSOUND3DBUFFER iface,
461         LPCDS3DBUFFER lpcDs3dBuffer,
462         DWORD dwApply)
463 {
464         FIXME("stub\n");
465         return DS_OK;
466 }
467 #endif
468
469 #ifdef USE_DSOUND3D
470 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeAngles(
471         LPDIRECTSOUND3DBUFFER iface,
472         DWORD dwInsideConeAngle,
473         DWORD dwOutsideConeAngle,
474         DWORD dwApply)
475 {
476         FIXME("stub\n");
477         return DS_OK;
478 }
479 #endif
480
481 #ifdef USE_DSOUND3D
482 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeOrientation(
483         LPDIRECTSOUND3DBUFFER iface,
484         D3DVALUE x, D3DVALUE y, D3DVALUE z,
485         DWORD dwApply)
486 {
487         FIXME("stub\n");
488         return DS_OK;
489 }
490 #endif
491
492 #ifdef USE_DSOUND3D
493 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeOutsideVolume(
494         LPDIRECTSOUND3DBUFFER iface,
495         LONG lConeOutsideVolume,
496         DWORD dwApply)
497 {
498         FIXME("stub\n");
499         return DS_OK;
500 }
501 #endif
502
503 #ifdef USE_DSOUND3D
504 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMaxDistance(
505         LPDIRECTSOUND3DBUFFER iface,
506         D3DVALUE fMaxDistance,
507         DWORD dwApply)
508 {
509         FIXME("stub\n");
510         return DS_OK;
511 }
512 #endif
513
514 #ifdef USE_DSOUND3D
515 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMinDistance(
516         LPDIRECTSOUND3DBUFFER iface,
517         D3DVALUE fMinDistance,
518         DWORD dwApply)
519 {
520         FIXME("stub\n");
521         return DS_OK;
522 }
523 #endif
524
525 #ifdef USE_DSOUND3D
526 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMode(
527         LPDIRECTSOUND3DBUFFER iface,
528         DWORD dwMode,
529         DWORD dwApply)
530 {
531         ICOM_THIS(IDirectSound3DBufferImpl,iface);
532         TRACE("mode = %lx\n", dwMode);
533         This->ds3db.dwMode = dwMode;
534         return DS_OK;
535 }
536 #endif
537
538 #ifdef USE_DSOUND3D
539 static HRESULT WINAPI IDirectSound3DBufferImpl_SetPosition(
540         LPDIRECTSOUND3DBUFFER iface,
541         D3DVALUE x, D3DVALUE y, D3DVALUE z,
542         DWORD dwApply)
543 {
544         FIXME("stub\n");
545         return DS_OK;
546 }
547 #endif
548
549 #ifdef USE_DSOUND3D
550 static HRESULT WINAPI IDirectSound3DBufferImpl_SetVelocity(
551         LPDIRECTSOUND3DBUFFER iface,
552         D3DVALUE x, D3DVALUE y, D3DVALUE z,
553         DWORD dwApply)
554 {
555         FIXME("stub\n");
556         return DS_OK;
557 }
558 #endif
559
560 #ifdef USE_DSOUND3D
561 static ICOM_VTABLE(IDirectSound3DBuffer) ds3dbvt = 
562 {
563         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
564         /* IUnknown methods */
565         IDirectSound3DBufferImpl_QueryInterface,
566         IDirectSound3DBufferImpl_AddRef,
567         IDirectSound3DBufferImpl_Release,
568         /* IDirectSound3DBuffer methods */
569         IDirectSound3DBufferImpl_GetAllParameters,
570         IDirectSound3DBufferImpl_GetConeAngles,
571         IDirectSound3DBufferImpl_GetConeOrientation,
572         IDirectSound3DBufferImpl_GetConeOutsideVolume,
573         IDirectSound3DBufferImpl_GetMaxDistance,
574         IDirectSound3DBufferImpl_GetMinDistance,
575         IDirectSound3DBufferImpl_GetMode,
576         IDirectSound3DBufferImpl_GetPosition,
577         IDirectSound3DBufferImpl_GetVelocity,
578         IDirectSound3DBufferImpl_SetAllParameters,
579         IDirectSound3DBufferImpl_SetConeAngles,
580         IDirectSound3DBufferImpl_SetConeOrientation,
581         IDirectSound3DBufferImpl_SetConeOutsideVolume,
582         IDirectSound3DBufferImpl_SetMaxDistance,
583         IDirectSound3DBufferImpl_SetMinDistance,
584         IDirectSound3DBufferImpl_SetMode,
585         IDirectSound3DBufferImpl_SetPosition,
586         IDirectSound3DBufferImpl_SetVelocity,
587 };
588 #endif
589
590 #ifdef USE_DSOUND3D
591 static int DSOUND_Create3DBuffer(IDirectSoundBufferImpl* dsb)
592 {
593         DWORD   i, temp, iSize, oSize, offset;
594         LPBYTE  bIbuf, bObuf, bTbuf = NULL;
595         LPWORD  wIbuf, wObuf, wTbuf = NULL;
596
597         /* Inside DirectX says it's stupid but allowed */
598         if (dsb->wfx.nChannels == 2) {
599                 /* Convert to mono */
600                 if (dsb->wfx.wBitsPerSample == 16) {
601                         iSize = dsb->buflen / 4;
602                         wTbuf = malloc(dsb->buflen / 2);
603                         if (wTbuf == NULL)
604                                 return DSERR_OUTOFMEMORY;
605                         for (i = 0; i < iSize; i++)
606                                 wTbuf[i] = (dsb->buffer[i * 2] + dsb->buffer[(i * 2) + 1]) / 2;
607                         wIbuf = wTbuf;
608                 } else {
609                         iSize = dsb->buflen / 2;
610                         bTbuf = malloc(dsb->buflen / 2);
611                         if (bTbuf == NULL)
612                                 return DSERR_OUTOFMEMORY;
613                         for (i = 0; i < iSize; i++)
614                                 bTbuf[i] = (dsb->buffer[i * 2] + dsb->buffer[(i * 2) + 1]) / 2;
615                         bIbuf = bTbuf;
616                 }
617         } else {
618                 if (dsb->wfx.wBitsPerSample == 16) {
619                         iSize = dsb->buflen / 2;
620                         wIbuf = (LPWORD) dsb->buffer;
621                 } else {
622                         bIbuf = (LPBYTE) dsb->buffer;
623                         iSize = dsb->buflen;
624                 }
625         }
626
627         if (primarybuf->wfx.wBitsPerSample == 16) {
628                 wObuf = (LPWORD) dsb->ds3db->buffer;
629                 oSize = dsb->ds3db->buflen / 2;
630         } else {
631                 bObuf = (LPBYTE) dsb->ds3db->buffer;
632                 oSize = dsb->ds3db->buflen;
633         }
634
635         offset = primarybuf->wfx.nSamplesPerSec / 100;          /* 10ms */
636         if (primarybuf->wfx.wBitsPerSample == 16 && dsb->wfx.wBitsPerSample == 16)
637                 for (i = 0; i < iSize; i++) {
638                         temp = wIbuf[i];
639                         if (i >= offset)
640                                 temp += wIbuf[i - offset] >> 9;
641                         else
642                                 temp += wIbuf[i + iSize - offset] >> 9;
643                         wObuf[i * 2] = temp;
644                         wObuf[(i * 2) + 1] = temp;
645                 }
646         else if (primarybuf->wfx.wBitsPerSample == 8 && dsb->wfx.wBitsPerSample == 8)
647                 for (i = 0; i < iSize; i++) {
648                         temp = bIbuf[i];
649                         if (i >= offset)
650                                 temp += bIbuf[i - offset] >> 5;
651                         else
652                                 temp += bIbuf[i + iSize - offset] >> 5;
653                         bObuf[i * 2] = temp;
654                         bObuf[(i * 2) + 1] = temp;
655                 }
656         
657         if (wTbuf)
658                 free(wTbuf);
659         if (bTbuf)
660                 free(bTbuf);
661
662         return DS_OK;
663 }
664 #endif
665 /*******************************************************************************
666  *              IDirectSound3DListener
667  */
668
669 /* IUnknown methods */
670 static HRESULT WINAPI IDirectSound3DListenerImpl_QueryInterface(
671         LPDIRECTSOUND3DLISTENER iface, REFIID riid, LPVOID *ppobj)
672 {
673         ICOM_THIS(IDirectSound3DListenerImpl,iface);
674
675         TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
676         return E_FAIL;
677 }
678         
679 static ULONG WINAPI IDirectSound3DListenerImpl_AddRef(LPDIRECTSOUND3DLISTENER iface)
680 {
681         ICOM_THIS(IDirectSound3DListenerImpl,iface);
682         This->ref++;
683         return This->ref;
684 }
685
686 static ULONG WINAPI IDirectSound3DListenerImpl_Release(LPDIRECTSOUND3DLISTENER iface)
687 {
688         ULONG ulReturn;
689         ICOM_THIS(IDirectSound3DListenerImpl,iface);
690
691         TRACE("(%p) ref was %ld\n", This, This->ref);
692
693         ulReturn = --This->ref;
694
695         /* Free all resources */
696         if( ulReturn == 0 ) {
697                 if(This->dsb)
698                         IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
699                 DeleteCriticalSection(&This->lock);
700                 HeapFree(GetProcessHeap(),0,This);
701         }
702
703         return ulReturn;
704 }
705
706 /* IDirectSound3DListener methods */
707 static HRESULT WINAPI IDirectSound3DListenerImpl_GetAllParameter(
708         LPDIRECTSOUND3DLISTENER iface,
709         LPDS3DLISTENER lpDS3DL)
710 {
711         FIXME("stub\n");
712         return DS_OK;
713 }
714
715 static HRESULT WINAPI IDirectSound3DListenerImpl_GetDistanceFactor(
716         LPDIRECTSOUND3DLISTENER iface,
717         LPD3DVALUE lpfDistanceFactor)
718 {
719         FIXME("stub\n");
720         return DS_OK;
721 }
722
723 static HRESULT WINAPI IDirectSound3DListenerImpl_GetDopplerFactor(
724         LPDIRECTSOUND3DLISTENER iface,
725         LPD3DVALUE lpfDopplerFactor)
726 {
727         FIXME("stub\n");
728         return DS_OK;
729 }
730
731 static HRESULT WINAPI IDirectSound3DListenerImpl_GetOrientation(
732         LPDIRECTSOUND3DLISTENER iface,
733         LPD3DVECTOR lpvOrientFront,
734         LPD3DVECTOR lpvOrientTop)
735 {
736         FIXME("stub\n");
737         return DS_OK;
738 }
739
740 static HRESULT WINAPI IDirectSound3DListenerImpl_GetPosition(
741         LPDIRECTSOUND3DLISTENER iface,
742         LPD3DVECTOR lpvPosition)
743 {
744         FIXME("stub\n");
745         return DS_OK;
746 }
747
748 static HRESULT WINAPI IDirectSound3DListenerImpl_GetRolloffFactor(
749         LPDIRECTSOUND3DLISTENER iface,
750         LPD3DVALUE lpfRolloffFactor)
751 {
752         FIXME("stub\n");
753         return DS_OK;
754 }
755
756 static HRESULT WINAPI IDirectSound3DListenerImpl_GetVelocity(
757         LPDIRECTSOUND3DLISTENER iface,
758         LPD3DVECTOR lpvVelocity)
759 {
760         FIXME("stub\n");
761         return DS_OK;
762 }
763
764 static HRESULT WINAPI IDirectSound3DListenerImpl_SetAllParameters(
765         LPDIRECTSOUND3DLISTENER iface,
766         LPCDS3DLISTENER lpcDS3DL,
767         DWORD dwApply)
768 {
769         FIXME("stub\n");
770         return DS_OK;
771 }
772
773 static HRESULT WINAPI IDirectSound3DListenerImpl_SetDistanceFactor(
774         LPDIRECTSOUND3DLISTENER iface,
775         D3DVALUE fDistanceFactor,
776         DWORD dwApply)
777 {
778         FIXME("stub\n");
779         return DS_OK;
780 }
781
782 static HRESULT WINAPI IDirectSound3DListenerImpl_SetDopplerFactor(
783         LPDIRECTSOUND3DLISTENER iface,
784         D3DVALUE fDopplerFactor,
785         DWORD dwApply)
786 {
787         FIXME("stub\n");
788         return DS_OK;
789 }
790
791 static HRESULT WINAPI IDirectSound3DListenerImpl_SetOrientation(
792         LPDIRECTSOUND3DLISTENER iface,
793         D3DVALUE xFront, D3DVALUE yFront, D3DVALUE zFront,
794         D3DVALUE xTop, D3DVALUE yTop, D3DVALUE zTop,
795         DWORD dwApply)
796 {
797         FIXME("stub\n");
798         return DS_OK;
799 }
800
801 static HRESULT WINAPI IDirectSound3DListenerImpl_SetPosition(
802         LPDIRECTSOUND3DLISTENER iface,
803         D3DVALUE x, D3DVALUE y, D3DVALUE z,
804         DWORD dwApply)
805 {
806         FIXME("stub\n");
807         return DS_OK;
808 }
809
810 static HRESULT WINAPI IDirectSound3DListenerImpl_SetRolloffFactor(
811         LPDIRECTSOUND3DLISTENER iface,
812         D3DVALUE fRolloffFactor,
813         DWORD dwApply)
814 {
815         FIXME("stub\n");
816         return DS_OK;
817 }
818
819 static HRESULT WINAPI IDirectSound3DListenerImpl_SetVelocity(
820         LPDIRECTSOUND3DLISTENER iface,
821         D3DVALUE x, D3DVALUE y, D3DVALUE z,
822         DWORD dwApply)
823 {
824         FIXME("stub\n");
825         return DS_OK;
826 }
827
828 static HRESULT WINAPI IDirectSound3DListenerImpl_CommitDeferredSettings(
829         LPDIRECTSOUND3DLISTENER iface)
830
831 {
832         FIXME("stub\n");
833         return DS_OK;
834 }
835
836 static ICOM_VTABLE(IDirectSound3DListener) ds3dlvt = 
837 {
838         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
839         /* IUnknown methods */
840         IDirectSound3DListenerImpl_QueryInterface,
841         IDirectSound3DListenerImpl_AddRef,
842         IDirectSound3DListenerImpl_Release,
843         /* IDirectSound3DListener methods */
844         IDirectSound3DListenerImpl_GetAllParameter,
845         IDirectSound3DListenerImpl_GetDistanceFactor,
846         IDirectSound3DListenerImpl_GetDopplerFactor,
847         IDirectSound3DListenerImpl_GetOrientation,
848         IDirectSound3DListenerImpl_GetPosition,
849         IDirectSound3DListenerImpl_GetRolloffFactor,
850         IDirectSound3DListenerImpl_GetVelocity,
851         IDirectSound3DListenerImpl_SetAllParameters,
852         IDirectSound3DListenerImpl_SetDistanceFactor,
853         IDirectSound3DListenerImpl_SetDopplerFactor,
854         IDirectSound3DListenerImpl_SetOrientation,
855         IDirectSound3DListenerImpl_SetPosition,
856         IDirectSound3DListenerImpl_SetRolloffFactor,
857         IDirectSound3DListenerImpl_SetVelocity,
858         IDirectSound3DListenerImpl_CommitDeferredSettings,
859 };
860
861 /*******************************************************************************
862  *              IDirectSoundNotify
863  */
864 static HRESULT WINAPI IDirectSoundNotifyImpl_QueryInterface(
865         LPDIRECTSOUNDNOTIFY iface,REFIID riid,LPVOID *ppobj
866 ) {
867         ICOM_THIS(IDirectSoundNotifyImpl,iface);
868
869         TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
870         return E_FAIL;
871 }
872
873 static ULONG WINAPI IDirectSoundNotifyImpl_AddRef(LPDIRECTSOUNDNOTIFY iface) {
874         ICOM_THIS(IDirectSoundNotifyImpl,iface);
875         return ++(This->ref);
876 }
877
878 static ULONG WINAPI IDirectSoundNotifyImpl_Release(LPDIRECTSOUNDNOTIFY iface) {
879         ICOM_THIS(IDirectSoundNotifyImpl,iface);
880
881         TRACE("(%p) ref was %ld\n", This, This->ref);
882
883         This->ref--;
884         if (!This->ref) {
885                 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
886                 HeapFree(GetProcessHeap(),0,This);
887                 return 0;
888         }
889         return This->ref;
890 }
891
892 static HRESULT WINAPI IDirectSoundNotifyImpl_SetNotificationPositions(
893         LPDIRECTSOUNDNOTIFY iface,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify
894 ) {
895         ICOM_THIS(IDirectSoundNotifyImpl,iface);
896         int     i;
897
898         if (TRACE_ON(dsound)) {
899             TRACE("(%p,0x%08lx,%p)\n",This,howmuch,notify);
900             for (i=0;i<howmuch;i++)
901                     TRACE("notify at %ld to 0x%08lx\n",
902                             notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
903         }
904         This->dsb->notifies = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,This->dsb->notifies,(This->dsb->nrofnotifies+howmuch)*sizeof(DSBPOSITIONNOTIFY));
905         memcpy( This->dsb->notifies+This->dsb->nrofnotifies,
906                 notify,
907                 howmuch*sizeof(DSBPOSITIONNOTIFY)
908         );
909         This->dsb->nrofnotifies+=howmuch;
910
911         return S_OK;
912 }
913
914 static ICOM_VTABLE(IDirectSoundNotify) dsnvt = 
915 {
916         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
917         IDirectSoundNotifyImpl_QueryInterface,
918         IDirectSoundNotifyImpl_AddRef,
919         IDirectSoundNotifyImpl_Release,
920         IDirectSoundNotifyImpl_SetNotificationPositions,
921 };
922
923 /*******************************************************************************
924  *              IDirectSoundBuffer
925  */
926
927 static void DSOUND_RecalcVolPan(PDSVOLUMEPAN volpan)
928 {
929         double temp;
930
931         /* the AmpFactors are expressed in 16.16 fixed point */
932         volpan->dwVolAmpFactor = (ULONG) (pow(2.0, volpan->lVolume / 600.0) * 65536);
933         /* FIXME: dwPan{Left|Right}AmpFactor */
934
935         /* FIXME: use calculated vol and pan ampfactors */
936         temp = (double) (volpan->lVolume - (volpan->lPan > 0 ? volpan->lPan : 0));
937         volpan->dwTotalLeftAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 65536);
938         temp = (double) (volpan->lVolume + (volpan->lPan < 0 ? volpan->lPan : 0));
939         volpan->dwTotalRightAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 65536);
940
941         TRACE("left = %lx, right = %lx\n", volpan->dwTotalLeftAmpFactor, volpan->dwTotalRightAmpFactor);
942 }
943
944 static void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
945 {
946         DWORD sw;
947
948         sw = dsb->wfx.nChannels * (dsb->wfx.wBitsPerSample / 8);
949         if ((dsb->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && dsb->hwbuf) {
950                 DWORD fraglen;
951                 /* let fragment size approximate the timer delay */
952                 fraglen = (dsb->freq * DS_TIME_DEL / 1000) * sw;
953                 /* reduce fragment size until an integer number of them fits in the buffer */
954                 /* (FIXME: this may or may not be a good idea) */
955                 while (dsb->buflen % fraglen) fraglen -= sw;
956                 dsb->dsound->fraglen = fraglen;
957                 TRACE("fraglen=%ld\n", dsb->dsound->fraglen);
958         }
959         /* calculate the 10ms write lead */
960         dsb->writelead = (dsb->freq / 100) * sw;
961 }
962
963 static HRESULT DSOUND_PrimaryOpen(IDirectSoundBufferImpl *dsb)
964 {
965         HRESULT err = DS_OK;
966
967         /* are we using waveOut stuff? */
968         if (!dsb->hwbuf) {
969                 LPBYTE newbuf;
970                 DWORD buflen;
971                 HRESULT merr = DS_OK;
972                 /* Start in pause mode, to allow buffers to get filled */
973                 waveOutPause(dsb->dsound->hwo);
974                 if (dsb->state == STATE_PLAYING) dsb->state = STATE_STARTING;
975                 else if (dsb->state == STATE_STOPPING) dsb->state = STATE_STOPPED;
976                 /* use fragments of 10ms (1/100s) each (which should get us within
977                  * the documented write cursor lead of 10-15ms) */
978                 buflen = ((dsb->wfx.nAvgBytesPerSec / 100) & ~3) * DS_HEL_FRAGS;
979                 TRACE("desired buflen=%ld, old buffer=%p\n", buflen, dsb->buffer);
980                 /* reallocate emulated primary buffer */
981                 newbuf = (LPBYTE)HeapReAlloc(GetProcessHeap(),0,dsb->buffer,buflen);
982                 if (newbuf == NULL) {
983                         ERR("failed to allocate primary buffer\n");
984                         merr = DSERR_OUTOFMEMORY;
985                         /* but the old buffer might still exists and must be re-prepared */
986                 } else {
987                         dsb->buffer = newbuf;
988                         dsb->buflen = buflen;
989                 }
990                 if (dsb->buffer) {
991                         unsigned c;
992                         IDirectSoundImpl *ds = dsb->dsound;
993
994                         ds->fraglen = dsb->buflen / DS_HEL_FRAGS;
995
996                         /* prepare fragment headers */
997                         for (c=0; c<DS_HEL_FRAGS; c++) {
998                                 ds->pwave[c]->lpData = dsb->buffer + c*ds->fraglen;
999                                 ds->pwave[c]->dwBufferLength = ds->fraglen;
1000                                 ds->pwave[c]->dwUser = (DWORD)dsb;
1001                                 ds->pwave[c]->dwFlags = 0;
1002                                 ds->pwave[c]->dwLoops = 0;
1003                                 err = mmErr(waveOutPrepareHeader(ds->hwo,ds->pwave[c],sizeof(WAVEHDR)));
1004                                 if (err != DS_OK) {
1005                                         while (c--)
1006                                                 waveOutUnprepareHeader(ds->hwo,ds->pwave[c],sizeof(WAVEHDR));
1007                                         break;
1008                                 }
1009                         }
1010
1011                         ds->pwplay = 0;
1012                         ds->pwwrite = 0;
1013                         ds->pwqueue = 0;
1014                         memset(dsb->buffer, (dsb->wfx.wBitsPerSample == 16) ? 0 : 128, dsb->buflen);
1015                         TRACE("fraglen=%ld\n", ds->fraglen);
1016                 }
1017                 if ((err == DS_OK) && (merr != DS_OK))
1018                         err = merr;
1019         }
1020         return err;
1021 }
1022
1023
1024 static void DSOUND_PrimaryClose(IDirectSoundBufferImpl *dsb)
1025 {
1026         /* are we using waveOut stuff? */
1027         if (!dsb->hwbuf) {
1028                 unsigned c;
1029                 IDirectSoundImpl *ds = dsb->dsound;
1030
1031                 waveOutReset(ds->hwo);
1032                 for (c=0; c<DS_HEL_FRAGS; c++)
1033                         waveOutUnprepareHeader(ds->hwo, ds->pwave[c], sizeof(WAVEHDR));
1034         }
1035 }
1036
1037 static HRESULT DSOUND_PrimaryPlay(IDirectSoundBufferImpl *dsb)
1038 {
1039         HRESULT err = DS_OK;
1040         if (dsb->hwbuf)
1041                 err = IDsDriverBuffer_Play(dsb->hwbuf, 0, 0, DSBPLAY_LOOPING);
1042         return err;
1043 }
1044
1045 static HRESULT DSOUND_PrimaryStop(IDirectSoundBufferImpl *dsb)
1046 {
1047         HRESULT err = DS_OK;
1048         if (dsb->hwbuf) {
1049                 err = IDsDriverBuffer_Stop(dsb->hwbuf);
1050                 if (err == DSERR_BUFFERLOST) {
1051                         /* Wine-only: the driver wants us to reopen the device */
1052                         /* FIXME: check for errors */
1053                         IDsDriverBuffer_Release(primarybuf->hwbuf);
1054                         waveOutClose(dsb->dsound->hwo);
1055                         dsb->dsound->hwo = 0;
1056                         waveOutOpen(&(dsb->dsound->hwo), dsb->dsound->drvdesc.dnDevNode,
1057                                     &(primarybuf->wfx), 0, 0, CALLBACK_NULL | WAVE_DIRECTSOUND);
1058                         err = IDsDriver_CreateSoundBuffer(dsb->dsound->driver,&(dsb->wfx),dsb->dsbd.dwFlags,0,
1059                                                           &(dsb->buflen),&(dsb->buffer),
1060                                                           (LPVOID)&(dsb->hwbuf));
1061                 }
1062         }
1063         return err;
1064 }
1065
1066 /* This sets this format for the <em>Primary Buffer Only</em> */
1067 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
1068 static HRESULT WINAPI IDirectSoundBufferImpl_SetFormat(
1069         LPDIRECTSOUNDBUFFER iface,LPWAVEFORMATEX wfex
1070 ) {
1071         ICOM_THIS(IDirectSoundBufferImpl,iface);
1072         IDirectSoundBufferImpl** dsb;
1073         HRESULT err = DS_OK;
1074         int                     i;
1075
1076         /* Let's be pedantic! */
1077         if ((wfex == NULL) ||
1078             (wfex->wFormatTag != WAVE_FORMAT_PCM) ||
1079             (wfex->nChannels < 1) || (wfex->nChannels > 2) ||
1080             (wfex->nSamplesPerSec < 1) ||
1081             (wfex->nBlockAlign < 1) || (wfex->nChannels > 4) ||
1082             ((wfex->wBitsPerSample != 8) && (wfex->wBitsPerSample != 16))) {
1083                 TRACE("failed pedantic check!\n");
1084                 return DSERR_INVALIDPARAM;
1085         }
1086
1087         /* **** */
1088         EnterCriticalSection(&(This->dsound->lock));
1089
1090         if (primarybuf->wfx.nSamplesPerSec != wfex->nSamplesPerSec) {
1091                 dsb = dsound->buffers;
1092                 for (i = 0; i < dsound->nrofbuffers; i++, dsb++) {
1093                         /* **** */
1094                         EnterCriticalSection(&((*dsb)->lock));
1095
1096                         (*dsb)->freqAdjust = ((*dsb)->freq << DSOUND_FREQSHIFT) /
1097                                 wfex->nSamplesPerSec;
1098
1099                         LeaveCriticalSection(&((*dsb)->lock));
1100                         /* **** */
1101                 }
1102         }
1103
1104         memcpy(&(primarybuf->wfx), wfex, sizeof(primarybuf->wfx));
1105
1106         TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
1107                    "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1108                    wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
1109                    wfex->nAvgBytesPerSec, wfex->nBlockAlign, 
1110                    wfex->wBitsPerSample, wfex->cbSize);
1111
1112         primarybuf->wfx.nAvgBytesPerSec =
1113                 This->wfx.nSamplesPerSec * This->wfx.nBlockAlign;
1114         if (primarybuf->dsound->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) {
1115                 /* FIXME: check for errors */
1116                 DSOUND_PrimaryClose(primarybuf);
1117                 waveOutClose(This->dsound->hwo);
1118                 This->dsound->hwo = 0;
1119                 waveOutOpen(&(This->dsound->hwo), This->dsound->drvdesc.dnDevNode,
1120                             &(primarybuf->wfx), 0, 0, CALLBACK_NULL | WAVE_DIRECTSOUND);
1121                 DSOUND_PrimaryOpen(primarybuf);
1122         }
1123         if (primarybuf->hwbuf) {
1124                 err = IDsDriverBuffer_SetFormat(primarybuf->hwbuf, &(primarybuf->wfx));
1125                 if (err == DSERR_BUFFERLOST) {
1126                         /* Wine-only: the driver wants us to recreate the HW buffer */
1127                         IDsDriverBuffer_Release(primarybuf->hwbuf);
1128                         err = IDsDriver_CreateSoundBuffer(primarybuf->dsound->driver,&(primarybuf->wfx),primarybuf->dsbd.dwFlags,0,
1129                                                           &(primarybuf->buflen),&(primarybuf->buffer),
1130                                                           (LPVOID)&(primarybuf->hwbuf));
1131                         if (primarybuf->state == STATE_PLAYING) primarybuf->state = STATE_STARTING;
1132                         else if (primarybuf->state == STATE_STOPPING) primarybuf->state = STATE_STOPPED;
1133                 }
1134         }
1135         DSOUND_RecalcFormat(primarybuf);
1136
1137         LeaveCriticalSection(&(This->dsound->lock));
1138         /* **** */
1139
1140         return DS_OK;
1141 }
1142
1143 static HRESULT WINAPI IDirectSoundBufferImpl_SetVolume(
1144         LPDIRECTSOUNDBUFFER iface,LONG vol
1145 ) {
1146         ICOM_THIS(IDirectSoundBufferImpl,iface);
1147
1148         TRACE("(%p,%ld)\n",This,vol);
1149
1150         /* I'm not sure if we need this for primary buffer */
1151         if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
1152                 return DSERR_CONTROLUNAVAIL;
1153
1154         if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN))
1155                 return DSERR_INVALIDPARAM;
1156
1157         /* **** */
1158         EnterCriticalSection(&(This->lock));
1159
1160         This->volpan.lVolume = vol;
1161
1162         DSOUND_RecalcVolPan(&(This->volpan));
1163
1164         if (This->hwbuf) {
1165                 IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
1166         }
1167         else if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
1168 #if 0 /* should we really do this? */
1169                 /* the DS volume ranges from 0 (max, 0dB attenuation) to -10000 (min, 100dB attenuation) */
1170                 /* the MM volume ranges from 0 to 0xffff in an unspecified logarithmic scale */
1171                 WORD cvol = 0xffff + vol*6 + vol/2;
1172                 DWORD vol = cvol | ((DWORD)cvol << 16)
1173                 waveOutSetVolume(This->dsound->hwo, vol);
1174 #endif
1175         }
1176
1177         LeaveCriticalSection(&(This->lock));
1178         /* **** */
1179
1180         return DS_OK;
1181 }
1182
1183 static HRESULT WINAPI IDirectSoundBufferImpl_GetVolume(
1184         LPDIRECTSOUNDBUFFER iface,LPLONG vol
1185 ) {
1186         ICOM_THIS(IDirectSoundBufferImpl,iface);
1187         TRACE("(%p,%p)\n",This,vol);
1188
1189         if (vol == NULL)
1190                 return DSERR_INVALIDPARAM;
1191
1192         *vol = This->volpan.lVolume;
1193         return DS_OK;
1194 }
1195
1196 static HRESULT WINAPI IDirectSoundBufferImpl_SetFrequency(
1197         LPDIRECTSOUNDBUFFER iface,DWORD freq
1198 ) {
1199         ICOM_THIS(IDirectSoundBufferImpl,iface);
1200         TRACE("(%p,%ld)\n",This,freq);
1201
1202         /* You cannot set the frequency of the primary buffer */
1203         if (!(This->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY) ||
1204             (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
1205                 return DSERR_CONTROLUNAVAIL;
1206
1207         if (!freq) freq = This->wfx.nSamplesPerSec;
1208
1209         if ((freq < DSBFREQUENCY_MIN) || (freq > DSBFREQUENCY_MAX))
1210                 return DSERR_INVALIDPARAM;
1211
1212         /* **** */
1213         EnterCriticalSection(&(This->lock));
1214
1215         This->freq = freq;
1216         This->freqAdjust = (freq << DSOUND_FREQSHIFT) / primarybuf->wfx.nSamplesPerSec;
1217         This->nAvgBytesPerSec = freq * This->wfx.nBlockAlign;
1218         DSOUND_RecalcFormat(This);
1219
1220         LeaveCriticalSection(&(This->lock));
1221         /* **** */
1222
1223         return DS_OK;
1224 }
1225
1226 static HRESULT WINAPI IDirectSoundBufferImpl_Play(
1227         LPDIRECTSOUNDBUFFER iface,DWORD reserved1,DWORD reserved2,DWORD flags
1228 ) {
1229         ICOM_THIS(IDirectSoundBufferImpl,iface);
1230         TRACE("(%p,%08lx,%08lx,%08lx)\n",
1231                 This,reserved1,reserved2,flags
1232         );
1233
1234         /* **** */
1235         EnterCriticalSection(&(This->lock));
1236
1237         This->playflags = flags;
1238         if (This->state == STATE_STOPPED) {
1239                 This->leadin = TRUE;
1240                 This->startpos = This->buf_mixpos;
1241                 This->state = STATE_STARTING;
1242         } else if (This->state == STATE_STOPPING)
1243                 This->state = STATE_PLAYING;
1244         if (!(This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && This->hwbuf) {
1245                 IDsDriverBuffer_Play(This->hwbuf, 0, 0, This->playflags);
1246                 This->state = STATE_PLAYING;
1247         }
1248
1249         LeaveCriticalSection(&(This->lock));
1250         /* **** */
1251
1252         return DS_OK;
1253 }
1254
1255 static HRESULT WINAPI IDirectSoundBufferImpl_Stop(LPDIRECTSOUNDBUFFER iface)
1256 {
1257         ICOM_THIS(IDirectSoundBufferImpl,iface);
1258         TRACE("(%p)\n",This);
1259
1260         /* **** */
1261         EnterCriticalSection(&(This->lock));
1262
1263         if (This->state == STATE_PLAYING)
1264                 This->state = STATE_STOPPING;
1265         else if (This->state == STATE_STARTING)
1266                 This->state = STATE_STOPPED;
1267         if (!(This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && This->hwbuf) {
1268                 IDsDriverBuffer_Stop(This->hwbuf);
1269                 This->state = STATE_STOPPED;
1270         }
1271         DSOUND_CheckEvent(This, 0);
1272
1273         LeaveCriticalSection(&(This->lock));
1274         /* **** */
1275
1276         return DS_OK;
1277 }
1278
1279 static DWORD WINAPI IDirectSoundBufferImpl_AddRef(LPDIRECTSOUNDBUFFER iface) {
1280         ICOM_THIS(IDirectSoundBufferImpl,iface);
1281         DWORD ref;
1282
1283         TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId());
1284
1285         ref = InterlockedIncrement(&(This->ref));
1286         if (!ref) {
1287                 FIXME("thread-safety alert! AddRef-ing with a zero refcount!\n");
1288         }
1289         return ref;
1290 }
1291 static DWORD WINAPI IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER iface) {
1292         ICOM_THIS(IDirectSoundBufferImpl,iface);
1293         int     i;
1294         DWORD ref;
1295
1296         TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId());
1297
1298         ref = InterlockedDecrement(&(This->ref));
1299         if (ref) return ref;
1300
1301         EnterCriticalSection(&(This->dsound->lock));
1302         for (i=0;i<This->dsound->nrofbuffers;i++)
1303                 if (This->dsound->buffers[i] == This)
1304                         break;
1305
1306         if (i < This->dsound->nrofbuffers) {
1307                 /* Put the last buffer of the list in the (now empty) position */
1308                 This->dsound->buffers[i] = This->dsound->buffers[This->dsound->nrofbuffers - 1];
1309                 This->dsound->nrofbuffers--;
1310                 This->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,This->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER)*This->dsound->nrofbuffers);
1311                 TRACE("buffer count is now %d\n", This->dsound->nrofbuffers);
1312                 IDirectSound_Release((LPDIRECTSOUND)This->dsound);
1313         }
1314         LeaveCriticalSection(&(This->dsound->lock));
1315
1316         DeleteCriticalSection(&(This->lock));
1317         if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1318                 DSOUND_PrimaryClose(This);
1319         if (This->hwbuf) {
1320                 IDsDriverBuffer_Release(This->hwbuf);
1321         }
1322         if (This->ds3db)
1323                 IDirectSound3DBuffer_Release((LPDIRECTSOUND3DBUFFER)This->ds3db);
1324         if (This->parent)
1325                 /* this is a duplicate buffer */
1326                 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->parent);
1327         else
1328                 /* this is a toplevel buffer */
1329                 HeapFree(GetProcessHeap(),0,This->buffer);
1330
1331         HeapFree(GetProcessHeap(),0,This);
1332         
1333         if (This == primarybuf)
1334                 primarybuf = NULL;
1335
1336         return 0;
1337 }
1338
1339 static DWORD DSOUND_CalcPlayPosition(IDirectSoundBufferImpl *This,
1340                                      DWORD state, DWORD pplay, DWORD pwrite, DWORD pmix, DWORD bmix)
1341 {
1342         DWORD bplay;
1343
1344         TRACE("primary playpos=%ld, mixpos=%ld\n", pplay, pmix);
1345         TRACE("this mixpos=%ld\n", bmix);
1346
1347         /* the actual primary play position (pplay) is always behind last mixed (pmix),
1348          * unless the computer is too slow or something */
1349         /* we need to know how far away we are from there */
1350         if (pmix == pplay) {
1351                 if ((state == STATE_PLAYING) || (state == STATE_STOPPING)) {
1352                         /* wow, the software mixer is really doing well,
1353                          * seems the entire primary buffer is filled! */
1354                         pmix += primarybuf->buflen;
1355                 }
1356                 /* else: the primary buffer is not playing, so probably empty */
1357         }
1358         if (pmix < pplay) pmix += primarybuf->buflen; /* wraparound */
1359         pmix -= pplay;
1360         /* detect buffer underrun */
1361         if (pwrite < pplay) pwrite += primarybuf->buflen; /* wraparound */
1362         pwrite -= pplay;
1363         if (pmix > (DS_SND_QUEUE * primarybuf->dsound->fraglen + pwrite + primarybuf->writelead)) {
1364                 TRACE("detected an underrun: primary queue was %ld\n",pmix);
1365                 pmix = 0;
1366         }
1367         /* divide the offset by its sample size */
1368         pmix /= primarybuf->wfx.nBlockAlign;
1369         TRACE("primary back-samples=%ld\n",pmix);
1370         /* adjust for our frequency */
1371         pmix = (pmix * This->freqAdjust) >> DSOUND_FREQSHIFT;
1372         /* multiply by our own sample size */
1373         pmix *= This->wfx.nBlockAlign;
1374         TRACE("this back-offset=%ld\n", pmix);
1375         /* subtract from our last mixed position */
1376         bplay = bmix;
1377         while (bplay < pmix) bplay += This->buflen; /* wraparound */
1378         bplay -= pmix;
1379         if (This->leadin && ((bplay < This->startpos) || (bplay > bmix))) {
1380                 /* seems we haven't started playing yet */
1381                 TRACE("this still in lead-in phase\n");
1382                 bplay = This->startpos;
1383         }
1384         /* return the result */
1385         return bplay;
1386 }
1387
1388 static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
1389         LPDIRECTSOUNDBUFFER iface,LPDWORD playpos,LPDWORD writepos
1390 ) {
1391         HRESULT hres; 
1392         ICOM_THIS(IDirectSoundBufferImpl,iface);
1393         TRACE("(%p,%p,%p)\n",This,playpos,writepos);
1394         if (This->hwbuf) {
1395                 hres=IDsDriverBuffer_GetPosition(This->hwbuf,playpos,writepos);
1396                 if (hres)
1397                     return hres;
1398
1399         }
1400         else if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
1401                 if (playpos) {
1402                         MMTIME mtime;
1403                         mtime.wType = TIME_BYTES;
1404                         waveOutGetPosition(This->dsound->hwo, &mtime, sizeof(mtime));
1405                         mtime.u.cb = mtime.u.cb % This->buflen;
1406                         *playpos = mtime.u.cb;
1407                 }
1408                 if (writepos) {
1409                         /* the writepos should only be used by apps with WRITEPRIMARY priority,
1410                          * in which case our software mixer is disabled anyway */
1411                         *writepos = This->playpos + DS_HEL_MARGIN * This->dsound->fraglen;
1412                         while (*writepos >= This->buflen)
1413                                 *writepos -= This->buflen;
1414                 }
1415         } else {
1416                 if (playpos && (This->state != STATE_PLAYING)) {
1417                         /* we haven't been merged into the primary buffer (yet) */
1418                         *playpos = This->buf_mixpos;
1419                 }
1420                 else if (playpos) {
1421                         DWORD pplay, pwrite, lplay, splay, pstate;
1422                         /* let's get this exact; first, recursively call GetPosition on the primary */
1423                         EnterCriticalSection(&(primarybuf->lock));
1424                         if ((This->dsbd.dwFlags & DSBCAPS_GETCURRENTPOSITION2) || primarybuf->hwbuf || !DS_EMULDRIVER) {
1425                                 IDirectSoundBufferImpl_GetCurrentPosition((LPDIRECTSOUNDBUFFER)primarybuf, &pplay, &pwrite);
1426                                 /* detect HEL mode underrun */
1427                                 pstate = primarybuf->state;
1428                                 if (!(primarybuf->hwbuf || primarybuf->dsound->pwqueue)) {
1429                                         TRACE("detected an underrun\n");
1430                                         /* pplay = ? */
1431                                         if (pstate == STATE_PLAYING)
1432                                                 pstate = STATE_STARTING;
1433                                         else if (pstate == STATE_STOPPING)
1434                                                 pstate = STATE_STOPPED;
1435                                 }
1436                                 /* get data for ourselves while we still have the lock */
1437                                 pstate &= This->state;
1438                                 lplay = This->primary_mixpos;
1439                                 splay = This->buf_mixpos;
1440                                 /* calculate play position using this */
1441                                 *playpos = DSOUND_CalcPlayPosition(This, pstate, pplay, pwrite, lplay, splay);
1442                         } else {
1443                                 /* (unless the app isn't using GETCURRENTPOSITION2) */
1444                                 /* don't know exactly how this should be handled...
1445                                  * the docs says that play cursor is reported as directly
1446                                  * behind write cursor, hmm... */
1447                                 *playpos = This->playpos;
1448                         }
1449                         LeaveCriticalSection(&(primarybuf->lock));
1450                 }
1451                 if (writepos) *writepos = This->buf_mixpos;
1452         }
1453         if (writepos) {
1454                 if (This->state != STATE_STOPPED)
1455                         /* apply the documented 10ms lead to writepos */
1456                         *writepos += This->writelead;
1457                 while (*writepos >= This->buflen) *writepos -= This->buflen;
1458         }
1459         TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
1460         return DS_OK;
1461 }
1462
1463 static HRESULT WINAPI IDirectSoundBufferImpl_GetStatus(
1464         LPDIRECTSOUNDBUFFER iface,LPDWORD status
1465 ) {
1466         ICOM_THIS(IDirectSoundBufferImpl,iface);
1467         TRACE("(%p,%p), thread is %lx\n",This,status,GetCurrentThreadId());
1468
1469         if (status == NULL)
1470                 return DSERR_INVALIDPARAM;
1471
1472         *status = 0;
1473         if ((This->state == STATE_STARTING) || (This->state == STATE_PLAYING))
1474                 *status |= DSBSTATUS_PLAYING;
1475         if (This->playflags & DSBPLAY_LOOPING)
1476                 *status |= DSBSTATUS_LOOPING;
1477
1478         TRACE("status=%lx\n", *status);
1479         return DS_OK;
1480 }
1481
1482
1483 static HRESULT WINAPI IDirectSoundBufferImpl_GetFormat(
1484         LPDIRECTSOUNDBUFFER iface,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
1485 ) {
1486         ICOM_THIS(IDirectSoundBufferImpl,iface);
1487         TRACE("(%p,%p,%ld,%p)\n",This,lpwf,wfsize,wfwritten);
1488
1489         if (wfsize>sizeof(This->wfx))
1490                 wfsize = sizeof(This->wfx);
1491         if (lpwf) {     /* NULL is valid */
1492                 memcpy(lpwf,&(This->wfx),wfsize);
1493                 if (wfwritten)
1494                         *wfwritten = wfsize;
1495         } else
1496                 if (wfwritten)
1497                         *wfwritten = sizeof(This->wfx);
1498                 else
1499                         return DSERR_INVALIDPARAM;
1500
1501         return DS_OK;
1502 }
1503
1504 static HRESULT WINAPI IDirectSoundBufferImpl_Lock(
1505         LPDIRECTSOUNDBUFFER iface,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
1506 ) {
1507         ICOM_THIS(IDirectSoundBufferImpl,iface);
1508         DWORD capf;
1509
1510         TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx)\n",
1511                 This,
1512                 writecursor,
1513                 writebytes,
1514                 lplpaudioptr1,
1515                 audiobytes1,
1516                 lplpaudioptr2,
1517                 audiobytes2,
1518                 flags
1519         );
1520
1521         if (flags & DSBLOCK_FROMWRITECURSOR) {
1522                 DWORD writepos;
1523                 /* GetCurrentPosition does too much magic to duplicate here */
1524                 IDirectSoundBufferImpl_GetCurrentPosition(iface, NULL, &writepos);
1525                 writecursor += writepos;
1526         }
1527         if (flags & DSBLOCK_ENTIREBUFFER)
1528                 writebytes = This->buflen;
1529         if (writebytes > This->buflen)
1530                 writebytes = This->buflen;
1531
1532         assert(audiobytes1!=audiobytes2);
1533         assert(lplpaudioptr1!=lplpaudioptr2);
1534
1535         if ((writebytes == This->buflen) &&
1536             ((This->state == STATE_STARTING) ||
1537              (This->state == STATE_PLAYING)))
1538                 /* some games, like Half-Life, tries to be clever (not) and
1539                  * keeps one secondary buffer, and mixes sounds into it itself,
1540                  * locking the entire buffer every time... so we can just forget
1541                  * about tracking the last-written-to-position... */
1542                 This->probably_valid_to = (DWORD)-1;
1543         else
1544                 This->probably_valid_to = writecursor;
1545
1546         if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1547                 capf = DSDDESC_DONTNEEDPRIMARYLOCK;
1548         else
1549                 capf = DSDDESC_DONTNEEDSECONDARYLOCK;
1550         if (!(This->dsound->drvdesc.dwFlags & capf) && This->hwbuf) {
1551                 IDsDriverBuffer_Lock(This->hwbuf,
1552                                      lplpaudioptr1, audiobytes1,
1553                                      lplpaudioptr2, audiobytes2,
1554                                      writecursor, writebytes,
1555                                      0);
1556         } else
1557         if (writecursor+writebytes <= This->buflen) {
1558                 *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor;
1559                 *audiobytes1 = writebytes;
1560                 if (lplpaudioptr2)
1561                         *(LPBYTE*)lplpaudioptr2 = NULL;
1562                 if (audiobytes2)
1563                         *audiobytes2 = 0;
1564                 TRACE("->%ld.0\n",writebytes);
1565         } else {
1566                 *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor;
1567                 *audiobytes1 = This->buflen-writecursor;
1568                 if (lplpaudioptr2)
1569                         *(LPBYTE*)lplpaudioptr2 = This->buffer;
1570                 if (audiobytes2)
1571                         *audiobytes2 = writebytes-(This->buflen-writecursor);
1572                 TRACE("->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
1573         }
1574         return DS_OK;
1575 }
1576
1577 static HRESULT WINAPI IDirectSoundBufferImpl_SetCurrentPosition(
1578         LPDIRECTSOUNDBUFFER iface,DWORD newpos
1579 ) {
1580         ICOM_THIS(IDirectSoundBufferImpl,iface);
1581         TRACE("(%p,%ld)\n",This,newpos);
1582
1583         /* **** */
1584         EnterCriticalSection(&(This->lock));
1585
1586         This->buf_mixpos = newpos;
1587         if (This->hwbuf)
1588                 IDsDriverBuffer_SetPosition(This->hwbuf, This->buf_mixpos);
1589
1590         LeaveCriticalSection(&(This->lock));
1591         /* **** */
1592
1593         return DS_OK;
1594 }
1595
1596 static HRESULT WINAPI IDirectSoundBufferImpl_SetPan(
1597         LPDIRECTSOUNDBUFFER iface,LONG pan
1598 ) {
1599         ICOM_THIS(IDirectSoundBufferImpl,iface);
1600
1601         TRACE("(%p,%ld)\n",This,pan);
1602
1603         if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT))
1604                 return DSERR_INVALIDPARAM;
1605
1606         /* You cannot set the pan of the primary buffer */
1607         /* and you cannot use both pan and 3D controls */
1608         if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
1609             (This->dsbd.dwFlags & DSBCAPS_CTRL3D) ||
1610             (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
1611                 return DSERR_CONTROLUNAVAIL;
1612
1613         /* **** */
1614         EnterCriticalSection(&(This->lock));
1615
1616         This->volpan.lPan = pan;
1617
1618         DSOUND_RecalcVolPan(&(This->volpan));
1619
1620         if (This->hwbuf) {
1621                 IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
1622         }
1623
1624         LeaveCriticalSection(&(This->lock));
1625         /* **** */
1626
1627         return DS_OK;
1628 }
1629
1630 static HRESULT WINAPI IDirectSoundBufferImpl_GetPan(
1631         LPDIRECTSOUNDBUFFER iface,LPLONG pan
1632 ) {
1633         ICOM_THIS(IDirectSoundBufferImpl,iface);
1634         TRACE("(%p,%p)\n",This,pan);
1635
1636         if (pan == NULL)
1637                 return DSERR_INVALIDPARAM;
1638
1639         *pan = This->volpan.lPan;
1640
1641         return DS_OK;
1642 }
1643
1644 static HRESULT WINAPI IDirectSoundBufferImpl_Unlock(
1645         LPDIRECTSOUNDBUFFER iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
1646 ) {
1647         ICOM_THIS(IDirectSoundBufferImpl,iface);
1648         DWORD capf, probably_valid_to;
1649
1650         TRACE("(%p,%p,%ld,%p,%ld):stub\n", This,p1,x1,p2,x2);
1651
1652 #if 0
1653         /* Preprocess 3D buffers... */
1654
1655         /* This is highly experimental and liable to break things */
1656         if (This->dsbd.dwFlags & DSBCAPS_CTRL3D)
1657                 DSOUND_Create3DBuffer(This);
1658 #endif
1659
1660         if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1661                 capf = DSDDESC_DONTNEEDPRIMARYLOCK;
1662         else
1663                 capf = DSDDESC_DONTNEEDSECONDARYLOCK;
1664         if (!(This->dsound->drvdesc.dwFlags & capf) && This->hwbuf) {
1665                 IDsDriverBuffer_Unlock(This->hwbuf, p1, x1, p2, x2);
1666         }
1667
1668         if (p2) probably_valid_to = (((LPBYTE)p2)-This->buffer) + x2;
1669         else probably_valid_to = (((LPBYTE)p1)-This->buffer) + x1;
1670         while (probably_valid_to >= This->buflen)
1671                 probably_valid_to -= This->buflen;
1672         if ((probably_valid_to == 0) && ((x1+x2) == This->buflen) &&
1673             ((This->state == STATE_STARTING) ||
1674              (This->state == STATE_PLAYING)))
1675                 /* see IDirectSoundBufferImpl_Lock */
1676                 probably_valid_to = (DWORD)-1;
1677         This->probably_valid_to = probably_valid_to;
1678
1679         return DS_OK;
1680 }
1681
1682 static HRESULT WINAPI IDirectSoundBufferImpl_Restore(
1683         LPDIRECTSOUNDBUFFER iface
1684 ) {
1685         ICOM_THIS(IDirectSoundBufferImpl,iface);
1686         FIXME("(%p):stub\n",This);
1687         return DS_OK;
1688 }
1689
1690 static HRESULT WINAPI IDirectSoundBufferImpl_GetFrequency(
1691         LPDIRECTSOUNDBUFFER iface,LPDWORD freq
1692 ) {
1693         ICOM_THIS(IDirectSoundBufferImpl,iface);
1694         TRACE("(%p,%p)\n",This,freq);
1695
1696         if (freq == NULL)
1697                 return DSERR_INVALIDPARAM;
1698
1699         *freq = This->freq;
1700         TRACE("-> %ld\n", *freq);
1701
1702         return DS_OK;
1703 }
1704
1705 static HRESULT WINAPI IDirectSoundBufferImpl_Initialize(
1706         LPDIRECTSOUNDBUFFER iface,LPDIRECTSOUND dsound,LPDSBUFFERDESC dbsd
1707 ) {
1708         ICOM_THIS(IDirectSoundBufferImpl,iface);
1709         FIXME("(%p,%p,%p):stub\n",This,dsound,dbsd);
1710         DPRINTF("Re-Init!!!\n");
1711         return DSERR_ALREADYINITIALIZED;
1712 }
1713
1714 static HRESULT WINAPI IDirectSoundBufferImpl_GetCaps(
1715         LPDIRECTSOUNDBUFFER iface,LPDSBCAPS caps
1716 ) {
1717         ICOM_THIS(IDirectSoundBufferImpl,iface);
1718         TRACE("(%p)->(%p)\n",This,caps);
1719   
1720         if (caps == NULL)
1721                 return DSERR_INVALIDPARAM;
1722
1723         /* I think we should check this value, not set it. See */
1724         /* Inside DirectX, p215. That should apply here, too. */
1725         caps->dwSize = sizeof(*caps);
1726
1727         caps->dwFlags = This->dsbd.dwFlags;
1728         if (This->hwbuf) caps->dwFlags |= DSBCAPS_LOCHARDWARE;
1729         else caps->dwFlags |= DSBCAPS_LOCSOFTWARE;
1730
1731         caps->dwBufferBytes = This->dsbd.dwBufferBytes;
1732
1733         /* This value represents the speed of the "unlock" command.
1734            As unlock is quite fast (it does not do anything), I put
1735            4096 ko/s = 4 Mo / s */
1736         /* FIXME: hwbuf speed */
1737         caps->dwUnlockTransferRate = 4096;
1738         caps->dwPlayCpuOverhead = 0;
1739
1740         return DS_OK;
1741 }
1742
1743 static HRESULT WINAPI IDirectSoundBufferImpl_QueryInterface(
1744         LPDIRECTSOUNDBUFFER iface,REFIID riid,LPVOID *ppobj
1745 ) {
1746         ICOM_THIS(IDirectSoundBufferImpl,iface);
1747
1748         TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
1749
1750         if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
1751                 IDirectSoundNotifyImpl  *dsn;
1752
1753                 dsn = (IDirectSoundNotifyImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsn));
1754                 dsn->ref = 1;
1755                 dsn->dsb = This;
1756                 IDirectSoundBuffer_AddRef(iface);
1757                 ICOM_VTBL(dsn) = &dsnvt;
1758                 *ppobj = (LPVOID)dsn;
1759                 return S_OK;
1760         }
1761
1762 #if USE_DSOUND3D
1763         if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1764                 IDirectSound3DBufferImpl        *ds3db;
1765
1766                 *ppobj = This->ds3db;
1767                 if (*ppobj) {
1768                         IDirectSound3DBuffer_AddRef((LPDIRECTSOUND3DBUFFER)This->ds3db);
1769                         return S_OK;
1770                 }
1771
1772                 ds3db = (IDirectSound3DBufferImpl*)HeapAlloc(GetProcessHeap(),
1773                         0,sizeof(*ds3db));
1774                 ds3db->ref = 1;
1775                 ds3db->dsb = This;
1776                 ICOM_VTBL(ds3db) = &ds3dbvt;
1777                 InitializeCriticalSection(&ds3db->lock);
1778
1779                 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)This);
1780
1781                 ds3db->ds3db.dwSize = sizeof(DS3DBUFFER);
1782                 ds3db->ds3db.vPosition.u1.x = 0.0;
1783                 ds3db->ds3db.vPosition.u2.y = 0.0;
1784                 ds3db->ds3db.vPosition.u3.z = 0.0;
1785                 ds3db->ds3db.vVelocity.u1.x = 0.0;
1786                 ds3db->ds3db.vVelocity.u2.y = 0.0;
1787                 ds3db->ds3db.vVelocity.u3.z = 0.0;
1788                 ds3db->ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
1789                 ds3db->ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
1790                 ds3db->ds3db.vConeOrientation.u1.x = 0.0;
1791                 ds3db->ds3db.vConeOrientation.u2.y = 0.0;
1792                 ds3db->ds3db.vConeOrientation.u3.z = 0.0;
1793                 ds3db->ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME;                ds3db->ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE;
1794                 ds3db->ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
1795                 ds3db->ds3db.dwMode = DS3DMODE_NORMAL;
1796                 ds3db->buflen = (This->buflen * primarybuf->wfx.nBlockAlign) /
1797                         This->wfx.nBlockAlign;
1798                 ds3db->buffer = HeapAlloc(GetProcessHeap(), 0, ds3db->buflen);
1799                 if (ds3db->buffer == NULL) {
1800                         ds3db->buflen = 0;
1801                         ds3db->ds3db.dwMode = DS3DMODE_DISABLE;
1802                 }
1803
1804                 return S_OK;
1805         }
1806 #else
1807         if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1808                 FIXME("%s: I know about this GUID, but don't support it yet\n",
1809                       debugstr_guid( riid ));
1810                 *ppobj = NULL;
1811                 return E_FAIL;
1812         }
1813 #endif
1814
1815         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
1816                 IDirectSound3DListenerImpl* dsl;
1817
1818                 if (This->dsound->listener) {
1819                         *ppobj = This->dsound->listener;
1820                         IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)This->dsound->listener);
1821                         return DS_OK;
1822                 }
1823
1824                 dsl = (IDirectSound3DListenerImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsl));
1825                 dsl->ref = 1;
1826                 ICOM_VTBL(dsl) = &ds3dlvt;
1827                 *ppobj = (LPVOID)dsl;
1828
1829                 dsl->ds3dl.dwSize = sizeof(DS3DLISTENER);
1830                 dsl->ds3dl.vPosition.u1.x = 0.0;
1831                 dsl->ds3dl.vPosition.u2.y = 0.0;
1832                 dsl->ds3dl.vPosition.u3.z = 0.0;
1833                 dsl->ds3dl.vVelocity.u1.x = 0.0;
1834                 dsl->ds3dl.vVelocity.u2.y = 0.0;
1835                 dsl->ds3dl.vVelocity.u3.z = 0.0;
1836                 dsl->ds3dl.vOrientFront.u1.x = 0.0;
1837                 dsl->ds3dl.vOrientFront.u2.y = 0.0;
1838                 dsl->ds3dl.vOrientFront.u3.z = 1.0;
1839                 dsl->ds3dl.vOrientTop.u1.x = 0.0;
1840                 dsl->ds3dl.vOrientTop.u2.y = 1.0;
1841                 dsl->ds3dl.vOrientTop.u3.z = 0.0;
1842                 dsl->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
1843                 dsl->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
1844
1845                 InitializeCriticalSection(&dsl->lock);
1846
1847                 dsl->dsb = This;
1848                 IDirectSoundBuffer_AddRef(iface);
1849
1850                 This->dsound->listener = dsl;
1851                 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)dsl);
1852
1853                 return S_OK;
1854         }
1855
1856         FIXME( "Unknown GUID %s\n", debugstr_guid( riid ) );
1857
1858         *ppobj = NULL;
1859
1860         return E_FAIL;
1861 }
1862
1863 static ICOM_VTABLE(IDirectSoundBuffer) dsbvt = 
1864 {
1865         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1866         IDirectSoundBufferImpl_QueryInterface,
1867         IDirectSoundBufferImpl_AddRef,
1868         IDirectSoundBufferImpl_Release,
1869         IDirectSoundBufferImpl_GetCaps,
1870         IDirectSoundBufferImpl_GetCurrentPosition,
1871         IDirectSoundBufferImpl_GetFormat,
1872         IDirectSoundBufferImpl_GetVolume,
1873         IDirectSoundBufferImpl_GetPan,
1874         IDirectSoundBufferImpl_GetFrequency,
1875         IDirectSoundBufferImpl_GetStatus,
1876         IDirectSoundBufferImpl_Initialize,
1877         IDirectSoundBufferImpl_Lock,
1878         IDirectSoundBufferImpl_Play,
1879         IDirectSoundBufferImpl_SetCurrentPosition,
1880         IDirectSoundBufferImpl_SetFormat,
1881         IDirectSoundBufferImpl_SetVolume,
1882         IDirectSoundBufferImpl_SetPan,
1883         IDirectSoundBufferImpl_SetFrequency,
1884         IDirectSoundBufferImpl_Stop,
1885         IDirectSoundBufferImpl_Unlock,
1886         IDirectSoundBufferImpl_Restore
1887 };
1888
1889 /*******************************************************************************
1890  *              IDirectSound
1891  */
1892
1893 static HRESULT WINAPI IDirectSoundImpl_SetCooperativeLevel(
1894         LPDIRECTSOUND iface,HWND hwnd,DWORD level
1895 ) {
1896         ICOM_THIS(IDirectSoundImpl,iface);
1897
1898         FIXME("(%p,%08lx,%ld):stub\n",This,(DWORD)hwnd,level);
1899
1900         This->priolevel = level;
1901
1902         return DS_OK;
1903 }
1904
1905 static HRESULT WINAPI IDirectSoundImpl_CreateSoundBuffer(
1906         LPDIRECTSOUND iface,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER ppdsb,LPUNKNOWN lpunk
1907 ) {
1908         ICOM_THIS(IDirectSoundImpl,iface);
1909         IDirectSoundBufferImpl** ippdsb=(IDirectSoundBufferImpl**)ppdsb;
1910         LPWAVEFORMATEX  wfex;
1911         HRESULT err = DS_OK;
1912
1913         TRACE("(%p,%p,%p,%p)\n",This,dsbd,ippdsb,lpunk);
1914         
1915         if ((This == NULL) || (dsbd == NULL) || (ippdsb == NULL))
1916                 return DSERR_INVALIDPARAM;
1917         
1918         if (TRACE_ON(dsound)) {
1919                 TRACE("(structsize=%ld)\n",dsbd->dwSize);
1920                 TRACE("(flags=0x%08lx:\n",dsbd->dwFlags);
1921                 _dump_DSBCAPS(dsbd->dwFlags);
1922                 DPRINTF(")\n");
1923                 TRACE("(bufferbytes=%ld)\n",dsbd->dwBufferBytes);
1924                 TRACE("(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
1925         }
1926
1927         wfex = dsbd->lpwfxFormat;
1928
1929         if (wfex)
1930                 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
1931                    "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1932                    wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
1933                    wfex->nAvgBytesPerSec, wfex->nBlockAlign, 
1934                    wfex->wBitsPerSample, wfex->cbSize);
1935
1936         if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
1937                 if (primarybuf) {
1938                         IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)primarybuf);
1939                         *ippdsb = primarybuf;
1940                         primarybuf->dsbd.dwFlags = dsbd->dwFlags;
1941                         return DS_OK;
1942                 } /* Else create primary buffer */
1943         }
1944
1945         *ippdsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBufferImpl));
1946         if (*ippdsb == NULL)
1947                 return DSERR_OUTOFMEMORY;
1948         ICOM_VTBL(*ippdsb) = &dsbvt;
1949         (*ippdsb)->ref = 1;
1950         (*ippdsb)->dsound = This;
1951         (*ippdsb)->parent = NULL;
1952         (*ippdsb)->buffer = NULL;
1953
1954         memcpy(&((*ippdsb)->dsbd),dsbd,sizeof(*dsbd));
1955         if (dsbd->lpwfxFormat)
1956                 memcpy(&((*ippdsb)->wfx), dsbd->lpwfxFormat, sizeof((*ippdsb)->wfx));
1957
1958         TRACE("Created buffer at %p\n", *ippdsb);
1959
1960         if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
1961                 (*ippdsb)->buflen = dsound->wfx.nAvgBytesPerSec;
1962                 (*ippdsb)->freq = dsound->wfx.nSamplesPerSec;
1963
1964                 /* FIXME: verify that hardware capabilities (DSCAPS_PRIMARY flags) match */
1965
1966                 if (This->driver) {
1967                         err = IDsDriver_CreateSoundBuffer(This->driver,wfex,dsbd->dwFlags,0,
1968                                                           &((*ippdsb)->buflen),&((*ippdsb)->buffer),
1969                                                           (LPVOID*)&((*ippdsb)->hwbuf));
1970                 }
1971                 if (err == DS_OK)
1972                         err = DSOUND_PrimaryOpen(*ippdsb);
1973         } else {
1974                 DWORD capf = 0;
1975                 int use_hw;
1976
1977                 (*ippdsb)->buflen = dsbd->dwBufferBytes;
1978                 (*ippdsb)->freq = dsbd->lpwfxFormat->nSamplesPerSec;
1979
1980                 /* Check necessary hardware mixing capabilities */
1981                 if (wfex->nChannels==2) capf |= DSCAPS_SECONDARYSTEREO;
1982                 else capf |= DSCAPS_SECONDARYMONO;
1983                 if (wfex->wBitsPerSample==16) capf |= DSCAPS_SECONDARY16BIT;
1984                 else capf |= DSCAPS_SECONDARY8BIT;
1985                 use_hw = (This->drvcaps.dwFlags & capf) == capf;
1986
1987                 /* FIXME: check hardware sample rate mixing capabilities */
1988                 /* FIXME: check app hints for software/hardware buffer (STATIC, LOCHARDWARE, etc) */
1989                 /* FIXME: check whether any hardware buffers are left */
1990                 /* FIXME: handle DSDHEAP_CREATEHEAP for hardware buffers */
1991
1992                 /* Allocate system memory if applicable */
1993                 if ((This->drvdesc.dwFlags & DSDDESC_USESYSTEMMEMORY) || !use_hw) {
1994                         (*ippdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,(*ippdsb)->buflen);
1995                         if ((*ippdsb)->buffer == NULL)
1996                                 err = DSERR_OUTOFMEMORY;
1997                 }
1998
1999                 /* Allocate the hardware buffer */
2000                 if (use_hw && (err == DS_OK)) {
2001                         err = IDsDriver_CreateSoundBuffer(This->driver,wfex,dsbd->dwFlags,0,
2002                                                           &((*ippdsb)->buflen),&((*ippdsb)->buffer),
2003                                                           (LPVOID*)&((*ippdsb)->hwbuf));
2004                 }
2005         }
2006
2007         if (err != DS_OK) {
2008                 if ((*ippdsb)->buffer)
2009                         HeapFree(GetProcessHeap(),0,(*ippdsb)->buffer);
2010                 HeapFree(GetProcessHeap(),0,(*ippdsb));
2011                 *ippdsb = NULL;
2012                 return err;
2013         }
2014         /* calculate fragment size and write lead */
2015         DSOUND_RecalcFormat(*ippdsb);
2016
2017         /* It's not necessary to initialize values to zero since */
2018         /* we allocated this structure with HEAP_ZERO_MEMORY... */
2019         (*ippdsb)->playpos = 0;
2020         (*ippdsb)->buf_mixpos = 0;
2021         (*ippdsb)->state = STATE_STOPPED;
2022         DSOUND_RecalcVolPan(&((*ippdsb)->volpan));
2023
2024         if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
2025                 (*ippdsb)->freqAdjust = ((*ippdsb)->freq << DSOUND_FREQSHIFT) /
2026                         primarybuf->wfx.nSamplesPerSec;
2027                 (*ippdsb)->nAvgBytesPerSec = (*ippdsb)->freq *
2028                         dsbd->lpwfxFormat->nBlockAlign;
2029         }
2030
2031         EnterCriticalSection(&(This->lock));
2032         /* register buffer */
2033         if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
2034                 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl*)*(This->nrofbuffers+1));
2035                 if (newbuffers) {
2036                         This->buffers = newbuffers;
2037                         This->buffers[This->nrofbuffers] = *ippdsb;
2038                         This->nrofbuffers++;
2039                         TRACE("buffer count is now %d\n", This->nrofbuffers);
2040                 } else {
2041                         ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
2042                         err = DSERR_OUTOFMEMORY;
2043                 }
2044         }
2045         LeaveCriticalSection(&(This->lock));
2046
2047         IDirectSound_AddRef(iface);
2048
2049         InitializeCriticalSection(&((*ippdsb)->lock));
2050
2051         if (err != DS_OK) {
2052                 /* oops... */
2053                 IDirectSoundBuffer_Release(*ppdsb);
2054                 *ippdsb = NULL;
2055                 return err;
2056         }
2057         
2058 #if USE_DSOUND3D
2059         if (dsbd->dwFlags & DSBCAPS_CTRL3D) {
2060                 IDirectSound3DBufferImpl        *ds3db;
2061
2062                 ds3db = (IDirectSound3DBufferImpl*)HeapAlloc(GetProcessHeap(),
2063                         0,sizeof(*ds3db));
2064                 ICOM_VTBL(ds3db) = &ds3dbvt;
2065                 ds3db->ref = 1;
2066                 (*ippdsb)->ds3db = ds3db;
2067
2068                 ds3db->dsb = (*ippdsb);
2069                 IDirectSoundBufferImpl_AddRef((LPDIRECTSOUNDBUFFER)ippdsb);
2070
2071                 InitializeCriticalSection(&ds3db->lock);
2072
2073                 ds3db->ds3db.dwSize = sizeof(DS3DBUFFER);
2074                 ds3db->ds3db.vPosition.u1.x = 0.0;
2075                 ds3db->ds3db.vPosition.u2.y = 0.0;
2076                 ds3db->ds3db.vPosition.u3.z = 0.0;
2077                 ds3db->ds3db.vVelocity.u1.x = 0.0;
2078                 ds3db->ds3db.vVelocity.u2.y = 0.0;
2079                 ds3db->ds3db.vVelocity.u3.z = 0.0;
2080                 ds3db->ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
2081                 ds3db->ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
2082                 ds3db->ds3db.vConeOrientation.u1.x = 0.0;
2083                 ds3db->ds3db.vConeOrientation.u2.y = 0.0;
2084                 ds3db->ds3db.vConeOrientation.u3.z = 0.0;
2085                 ds3db->ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME;
2086                 ds3db->ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE;
2087                 ds3db->ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
2088                 ds3db->ds3db.dwMode = DS3DMODE_NORMAL;
2089                 ds3db->buflen = ((*ippdsb)->buflen * primarybuf->wfx.nBlockAlign) /
2090                         (*ippdsb)->wfx.nBlockAlign;
2091                 ds3db->buffer = HeapAlloc(GetProcessHeap(), 0, ds3db->buflen);
2092                 if (ds3db->buffer == NULL) {
2093                         ds3db->buflen = 0;
2094                         ds3db->ds3db.dwMode = DS3DMODE_DISABLE;
2095                 }
2096         }
2097 #endif
2098         return DS_OK;
2099 }
2100
2101 static HRESULT WINAPI IDirectSoundImpl_DuplicateSoundBuffer(
2102         LPDIRECTSOUND iface,LPDIRECTSOUNDBUFFER pdsb,LPLPDIRECTSOUNDBUFFER ppdsb
2103 ) {
2104         ICOM_THIS(IDirectSoundImpl,iface);
2105         IDirectSoundBufferImpl* ipdsb=(IDirectSoundBufferImpl*)pdsb;
2106         IDirectSoundBufferImpl** ippdsb=(IDirectSoundBufferImpl**)ppdsb;
2107         TRACE("(%p,%p,%p)\n",This,ipdsb,ippdsb);
2108
2109         if (ipdsb->hwbuf) {
2110                 FIXME("need to duplicate hardware buffer\n");
2111         }
2112
2113         *ippdsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBufferImpl));
2114
2115         IDirectSoundBuffer_AddRef(pdsb);
2116         memcpy(*ippdsb, ipdsb, sizeof(IDirectSoundBufferImpl));
2117         (*ippdsb)->ref = 1;
2118         (*ippdsb)->state = STATE_STOPPED;
2119         (*ippdsb)->playpos = 0;
2120         (*ippdsb)->buf_mixpos = 0;
2121         (*ippdsb)->dsound = This;
2122         (*ippdsb)->parent = ipdsb;
2123         memcpy(&((*ippdsb)->wfx), &(ipdsb->wfx), sizeof((*ippdsb)->wfx));
2124         InitializeCriticalSection(&(*ippdsb)->lock);
2125         /* register buffer */
2126         EnterCriticalSection(&(This->lock));
2127         {
2128                 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl**)*(This->nrofbuffers+1));
2129                 if (newbuffers) {
2130                         This->buffers = newbuffers;
2131                         This->buffers[This->nrofbuffers] = *ippdsb;
2132                         This->nrofbuffers++;
2133                         TRACE("buffer count is now %d\n", This->nrofbuffers);
2134                 } else {
2135                         ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
2136                         /* FIXME: release buffer */
2137                 }
2138         }
2139         LeaveCriticalSection(&(This->lock));
2140         IDirectSound_AddRef(iface);
2141         return DS_OK;
2142 }
2143
2144
2145 static HRESULT WINAPI IDirectSoundImpl_GetCaps(LPDIRECTSOUND iface,LPDSCAPS caps) {
2146         ICOM_THIS(IDirectSoundImpl,iface);
2147         TRACE("(%p,%p)\n",This,caps);
2148         TRACE("(flags=0x%08lx)\n",caps->dwFlags);
2149
2150         if (caps == NULL)
2151                 return DSERR_INVALIDPARAM;
2152
2153         /* We should check this value, not set it. See Inside DirectX, p215. */
2154         caps->dwSize = sizeof(*caps);
2155
2156         caps->dwFlags = This->drvcaps.dwFlags;
2157
2158         /* FIXME: copy caps from This->drvcaps */
2159         caps->dwMinSecondarySampleRate          = DSBFREQUENCY_MIN;
2160         caps->dwMaxSecondarySampleRate          = DSBFREQUENCY_MAX;
2161
2162         caps->dwPrimaryBuffers                  = 1;
2163
2164         caps->dwMaxHwMixingAllBuffers           = 0;
2165         caps->dwMaxHwMixingStaticBuffers        = 0;
2166         caps->dwMaxHwMixingStreamingBuffers     = 0;
2167
2168         caps->dwFreeHwMixingAllBuffers          = 0;
2169         caps->dwFreeHwMixingStaticBuffers       = 0;
2170         caps->dwFreeHwMixingStreamingBuffers    = 0;
2171
2172         caps->dwMaxHw3DAllBuffers               = 0;
2173         caps->dwMaxHw3DStaticBuffers            = 0;
2174         caps->dwMaxHw3DStreamingBuffers         = 0;
2175
2176         caps->dwFreeHw3DAllBuffers              = 0;
2177         caps->dwFreeHw3DStaticBuffers           = 0;
2178         caps->dwFreeHw3DStreamingBuffers        = 0;
2179
2180         caps->dwTotalHwMemBytes                 = 0;
2181
2182         caps->dwFreeHwMemBytes                  = 0;
2183
2184         caps->dwMaxContigFreeHwMemBytes         = 0;
2185
2186         caps->dwUnlockTransferRateHwBuffers     = 4096; /* But we have none... */
2187
2188         caps->dwPlayCpuOverheadSwBuffers        = 1;    /* 1% */
2189
2190         return DS_OK;
2191 }
2192
2193 static ULONG WINAPI IDirectSoundImpl_AddRef(LPDIRECTSOUND iface) {
2194         ICOM_THIS(IDirectSoundImpl,iface);
2195         return ++(This->ref);
2196 }
2197
2198 static ULONG WINAPI IDirectSoundImpl_Release(LPDIRECTSOUND iface) {
2199         ICOM_THIS(IDirectSoundImpl,iface);
2200         TRACE("(%p), ref was %ld\n",This,This->ref);
2201         if (!--(This->ref)) {
2202                 UINT i;
2203
2204                 timeKillEvent(This->timerID);
2205                 timeEndPeriod(DS_TIME_RES);
2206
2207                 if (primarybuf)
2208                         IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)primarybuf);
2209
2210                 if (This->buffers) {
2211                         for( i=0;i<This->nrofbuffers;i++)       
2212                                 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->buffers[i]);
2213                 }
2214
2215                 if (This->primary)
2216                         IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->primary);
2217
2218                 DeleteCriticalSection(&This->lock);
2219                 if (This->driver) {
2220                         IDsDriver_Close(This->driver);
2221                 } else {
2222                         unsigned c;
2223                         for (c=0; c<DS_HEL_FRAGS; c++)
2224                                 HeapFree(GetProcessHeap(),0,This->pwave[c]);
2225                 }
2226                 if (This->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) {
2227                         waveOutClose(This->hwo);
2228                 }
2229                 if (This->driver)
2230                         IDsDriver_Release(This->driver);
2231
2232                 HeapFree(GetProcessHeap(),0,This);
2233                 dsound = NULL;
2234                 return 0;
2235         }
2236         return This->ref;
2237 }
2238
2239 static HRESULT WINAPI IDirectSoundImpl_SetSpeakerConfig(
2240         LPDIRECTSOUND iface,DWORD config
2241 ) {
2242         ICOM_THIS(IDirectSoundImpl,iface);
2243         FIXME("(%p,0x%08lx):stub\n",This,config);
2244         return DS_OK;
2245 }
2246
2247 static HRESULT WINAPI IDirectSoundImpl_QueryInterface(
2248         LPDIRECTSOUND iface,REFIID riid,LPVOID *ppobj
2249 ) {
2250         ICOM_THIS(IDirectSoundImpl,iface);
2251
2252         if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
2253
2254                 if (This->listener) {
2255                         *ppobj = This->listener;
2256                         IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)This->listener);
2257                         return DS_OK;
2258                 }
2259
2260                 This->listener = (IDirectSound3DListenerImpl*)HeapAlloc(
2261                         GetProcessHeap(), 0, sizeof(*(This->listener)));
2262                 This->listener->ref = 1;
2263                 ICOM_VTBL(This->listener) = &ds3dlvt;
2264                 *ppobj = (LPVOID)This->listener;
2265                 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj); 
2266
2267                 This->listener->dsb = NULL; 
2268
2269                 This->listener->ds3dl.dwSize = sizeof(DS3DLISTENER);
2270                 This->listener->ds3dl.vPosition.u1.x = 0.0;
2271                 This->listener->ds3dl.vPosition.u2.y = 0.0;
2272                 This->listener->ds3dl.vPosition.u3.z = 0.0;
2273                 This->listener->ds3dl.vVelocity.u1.x = 0.0;
2274                 This->listener->ds3dl.vVelocity.u2.y = 0.0;
2275                 This->listener->ds3dl.vVelocity.u3.z = 0.0;
2276                 This->listener->ds3dl.vOrientFront.u1.x = 0.0;
2277                 This->listener->ds3dl.vOrientFront.u2.y = 0.0;
2278                 This->listener->ds3dl.vOrientFront.u3.z = 1.0;
2279                 This->listener->ds3dl.vOrientTop.u1.x = 0.0;
2280                 This->listener->ds3dl.vOrientTop.u2.y = 1.0;
2281                 This->listener->ds3dl.vOrientTop.u3.z = 0.0;
2282                 This->listener->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
2283                 This->listener->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
2284                 This->listener->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
2285
2286                 InitializeCriticalSection(&This->listener->lock);
2287
2288                 return DS_OK;
2289         }
2290
2291         FIXME("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
2292         return E_FAIL;
2293 }
2294
2295 static HRESULT WINAPI IDirectSoundImpl_Compact(
2296         LPDIRECTSOUND iface)
2297 {
2298         ICOM_THIS(IDirectSoundImpl,iface);
2299         TRACE("(%p)\n", This);
2300         return DS_OK;
2301 }
2302
2303 static HRESULT WINAPI IDirectSoundImpl_GetSpeakerConfig(
2304         LPDIRECTSOUND iface,
2305         LPDWORD lpdwSpeakerConfig)
2306 {
2307         ICOM_THIS(IDirectSoundImpl,iface);
2308         TRACE("(%p, %p)\n", This, lpdwSpeakerConfig);
2309         *lpdwSpeakerConfig = DSSPEAKER_STEREO | (DSSPEAKER_GEOMETRY_NARROW << 16);
2310         return DS_OK;
2311 }
2312
2313 static HRESULT WINAPI IDirectSoundImpl_Initialize(
2314         LPDIRECTSOUND iface,
2315         LPCGUID lpcGuid)
2316 {
2317         ICOM_THIS(IDirectSoundImpl,iface);
2318         TRACE("(%p, %p)\n", This, lpcGuid);
2319         return DS_OK;
2320 }
2321
2322 static ICOM_VTABLE(IDirectSound) dsvt = 
2323 {
2324         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2325         IDirectSoundImpl_QueryInterface,
2326         IDirectSoundImpl_AddRef,
2327         IDirectSoundImpl_Release,
2328         IDirectSoundImpl_CreateSoundBuffer,
2329         IDirectSoundImpl_GetCaps,
2330         IDirectSoundImpl_DuplicateSoundBuffer,
2331         IDirectSoundImpl_SetCooperativeLevel,
2332         IDirectSoundImpl_Compact,
2333         IDirectSoundImpl_GetSpeakerConfig,
2334         IDirectSoundImpl_SetSpeakerConfig,
2335         IDirectSoundImpl_Initialize
2336 };
2337
2338
2339 static void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len)
2340 {
2341         int                     i;
2342         DWORD                   offset;
2343         LPDSBPOSITIONNOTIFY     event;
2344
2345         if (dsb->nrofnotifies == 0)
2346                 return;
2347
2348         TRACE("(%p) buflen = %ld, playpos = %ld, len = %d\n",
2349                 dsb, dsb->buflen, dsb->playpos, len);
2350         for (i = 0; i < dsb->nrofnotifies ; i++) {
2351                 event = dsb->notifies + i;
2352                 offset = event->dwOffset;
2353                 TRACE("checking %d, position %ld, event = %d\n",
2354                         i, offset, event->hEventNotify);
2355                 /* DSBPN_OFFSETSTOP has to be the last element. So this is */
2356                 /* OK. [Inside DirectX, p274] */
2357                 /*  */
2358                 /* This also means we can't sort the entries by offset, */
2359                 /* because DSBPN_OFFSETSTOP == -1 */
2360                 if (offset == DSBPN_OFFSETSTOP) {
2361                         if (dsb->state == STATE_STOPPED) {
2362                                 SetEvent(event->hEventNotify);
2363                                 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2364                                 return;
2365                         } else
2366                                 return;
2367                 }
2368                 if ((dsb->playpos + len) >= dsb->buflen) {
2369                         if ((offset < ((dsb->playpos + len) % dsb->buflen)) ||
2370                             (offset >= dsb->playpos)) {
2371                                 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2372                                 SetEvent(event->hEventNotify);
2373                         }
2374                 } else {
2375                         if ((offset >= dsb->playpos) && (offset < (dsb->playpos + len))) {
2376                                 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2377                                 SetEvent(event->hEventNotify);
2378                         }
2379                 }
2380         }
2381 }
2382
2383 /* WAV format info can be found at: */
2384 /* */
2385 /*      http://www.cwi.nl/ftp/audio/AudioFormats.part2 */
2386 /*      ftp://ftp.cwi.nl/pub/audio/RIFF-format */
2387 /* */
2388 /* Import points to remember: */
2389 /* */
2390 /*      8-bit WAV is unsigned */
2391 /*      16-bit WAV is signed */
2392
2393 static inline INT16 cvtU8toS16(BYTE byte)
2394 {
2395         INT16   s = (byte - 128) << 8;
2396
2397         return s;
2398 }
2399
2400 static inline BYTE cvtS16toU8(INT16 word)
2401 {
2402         BYTE    b = (word + 32768) >> 8;
2403         
2404         return b;
2405 }
2406
2407
2408 /* We should be able to optimize these two inline functions */
2409 /* so that we aren't doing 8->16->8 conversions when it is */
2410 /* not necessary. But this is still a WIP. Optimize later. */
2411 static inline void get_fields(const IDirectSoundBufferImpl *dsb, BYTE *buf, INT *fl, INT *fr)
2412 {
2413         INT16   *bufs = (INT16 *) buf;
2414
2415         /* TRACE("(%p)", buf); */
2416         if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 2) {
2417                 *fl = cvtU8toS16(*buf);
2418                 *fr = cvtU8toS16(*(buf + 1));
2419                 return;
2420         }
2421
2422         if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 2) {
2423                 *fl = *bufs;
2424                 *fr = *(bufs + 1);
2425                 return;
2426         }
2427
2428         if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 1) {
2429                 *fl = cvtU8toS16(*buf);
2430                 *fr = *fl;
2431                 return;
2432         }
2433
2434         if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 1) {
2435                 *fl = *bufs;
2436                 *fr = *bufs;
2437                 return;
2438         }
2439
2440         FIXME("get_fields found an unsupported configuration\n");
2441         return;
2442 }
2443
2444 static inline void set_fields(BYTE *buf, INT fl, INT fr)
2445 {
2446         INT16 *bufs = (INT16 *) buf;
2447
2448         if ((primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 2)) {
2449                 *buf = cvtS16toU8(fl);
2450                 *(buf + 1) = cvtS16toU8(fr);
2451                 return;
2452         }
2453
2454         if ((primarybuf->wfx.wBitsPerSample == 16) && (primarybuf->wfx.nChannels == 2)) {
2455                 *bufs = fl;
2456                 *(bufs + 1) = fr;
2457                 return;
2458         }
2459
2460         if ((primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 1)) {
2461                 *buf = cvtS16toU8((fl + fr) >> 1);
2462                 return;
2463         }
2464
2465         if ((primarybuf->wfx.wBitsPerSample == 16) && (primarybuf->wfx.nChannels == 1)) {
2466                 *bufs = (fl + fr) >> 1;
2467                 return;
2468         }
2469         FIXME("set_fields found an unsupported configuration\n");
2470         return;
2471 }
2472
2473 /* Now with PerfectPitch (tm) technology */
2474 static INT DSOUND_MixerNorm(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2475 {
2476         INT     i, size, ipos, ilen, fieldL, fieldR;
2477         BYTE    *ibp, *obp;
2478         INT     iAdvance = dsb->wfx.nBlockAlign;
2479         INT     oAdvance = primarybuf->wfx.nBlockAlign;
2480
2481         ibp = dsb->buffer + dsb->buf_mixpos;
2482         obp = buf;
2483
2484         TRACE("(%p, %p, %p), buf_mixpos=%ld\n", dsb, ibp, obp, dsb->buf_mixpos);
2485         /* Check for the best case */
2486         if ((dsb->freq == primarybuf->wfx.nSamplesPerSec) &&
2487             (dsb->wfx.wBitsPerSample == primarybuf->wfx.wBitsPerSample) &&
2488             (dsb->wfx.nChannels == primarybuf->wfx.nChannels)) {
2489                 DWORD bytesleft = dsb->buflen - dsb->buf_mixpos;
2490                 TRACE("(%p) Best case\n", dsb);
2491                 if (len <= bytesleft )
2492                         memcpy(obp, ibp, len);
2493                 else { /* wrap */
2494                         memcpy(obp, ibp, bytesleft );
2495                         memcpy(obp + bytesleft, dsb->buffer, len - bytesleft);
2496                 }
2497                 return len;
2498         }
2499         
2500         /* Check for same sample rate */
2501         if (dsb->freq == primarybuf->wfx.nSamplesPerSec) {
2502                 TRACE("(%p) Same sample rate %ld = primary %ld\n", dsb,
2503                         dsb->freq, primarybuf->wfx.nSamplesPerSec);
2504                 ilen = 0;
2505                 for (i = 0; i < len; i += oAdvance) {
2506                         get_fields(dsb, ibp, &fieldL, &fieldR);
2507                         ibp += iAdvance;
2508                         ilen += iAdvance;
2509                         set_fields(obp, fieldL, fieldR);
2510                         obp += oAdvance;
2511                         if (ibp >= (BYTE *)(dsb->buffer + dsb->buflen))
2512                                 ibp = dsb->buffer;      /* wrap */
2513                 }
2514                 return (ilen);  
2515         }
2516
2517         /* Mix in different sample rates */
2518         /* */
2519         /* New PerfectPitch(tm) Technology (c) 1998 Rob Riggs */
2520         /* Patent Pending :-] */
2521
2522         /* Patent enhancements (c) 2000 Ove KÃ¥ven,
2523          * TransGaming Technologies Inc. */
2524
2525         TRACE("(%p) Adjusting frequency: %ld -> %ld\n",
2526                 dsb, dsb->freq, primarybuf->wfx.nSamplesPerSec);
2527
2528         size = len / oAdvance;
2529         ilen = 0;
2530         ipos = dsb->buf_mixpos;
2531         for (i = 0; i < size; i++) {
2532                 get_fields(dsb, (dsb->buffer + ipos), &fieldL, &fieldR);
2533                 set_fields(obp, fieldL, fieldR);
2534                 obp += oAdvance;
2535
2536                 dsb->freqAcc += dsb->freqAdjust;
2537                 if (dsb->freqAcc >= (1<<DSOUND_FREQSHIFT)) {
2538                         ULONG adv = (dsb->freqAcc>>DSOUND_FREQSHIFT) * iAdvance;
2539                         dsb->freqAcc &= (1<<DSOUND_FREQSHIFT)-1;
2540                         ipos += adv; ilen += adv;
2541                         while (ipos >= dsb->buflen)
2542                                 ipos -= dsb->buflen;
2543                 }
2544         }
2545         return ilen;
2546 }
2547
2548 static void DSOUND_MixerVol(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2549 {
2550         INT     i, inc = primarybuf->wfx.wBitsPerSample >> 3;
2551         BYTE    *bpc = buf;
2552         INT16   *bps = (INT16 *) buf;
2553         
2554         TRACE("(%p) left = %lx, right = %lx\n", dsb,
2555                 dsb->volpan.dwTotalLeftAmpFactor, dsb->volpan.dwTotalRightAmpFactor);
2556         if ((!(dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->volpan.lPan == 0)) &&
2557             (!(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->volpan.lVolume == 0)) &&
2558             !(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
2559                 return;         /* Nothing to do */
2560
2561         /* If we end up with some bozo coder using panning or 3D sound */
2562         /* with a mono primary buffer, it could sound very weird using */
2563         /* this method. Oh well, tough patooties. */
2564
2565         for (i = 0; i < len; i += inc) {
2566                 INT     val;
2567
2568                 switch (inc) {
2569
2570                 case 1:
2571                         /* 8-bit WAV is unsigned, but we need to operate */
2572                         /* on signed data for this to work properly */
2573                         val = *bpc - 128;
2574                         val = ((val * (i & inc ? dsb->volpan.dwTotalRightAmpFactor : dsb->volpan.dwTotalLeftAmpFactor)) >> 16);
2575                         *bpc = val + 128;
2576                         bpc++;
2577                         break;
2578                 case 2:
2579                         /* 16-bit WAV is signed -- much better */
2580                         val = *bps;
2581                         val = ((val * ((i & inc) ? dsb->volpan.dwTotalRightAmpFactor : dsb->volpan.dwTotalLeftAmpFactor)) >> 16);
2582                         *bps = val;
2583                         bps++;
2584                         break;
2585                 default:
2586                         /* Very ugly! */
2587                         FIXME("MixerVol had a nasty error\n");
2588                 }
2589         }               
2590 }
2591
2592 #ifdef USE_DSOUND3D
2593 static void DSOUND_Mixer3D(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2594 {
2595         BYTE    *ibp, *obp;
2596         DWORD   buflen, buf_mixpos;
2597
2598         buflen = dsb->ds3db->buflen;
2599         buf_mixpos = (dsb->buf_mixpos * primarybuf->wfx.nBlockAlign) / dsb->wfx.nBlockAlign;
2600         ibp = dsb->ds3db->buffer + buf_mixpos;
2601         obp = buf;
2602
2603         if (buf_mixpos > buflen) {
2604                 FIXME("Major breakage");
2605                 return;
2606         }
2607
2608         if (len <= (buf_mixpos + buflen))
2609                 memcpy(obp, ibp, len);
2610         else { /* wrap */
2611                 memcpy(obp, ibp, buflen - buf_mixpos);
2612                 memcpy(obp + (buflen - buf_mixpos),
2613                     dsb->buffer,
2614                     len - (buflen - buf_mixpos));
2615         }
2616         return;
2617 }
2618 #endif
2619
2620 static void *tmp_buffer;
2621 static size_t tmp_buffer_len = 0;
2622
2623 static void *DSOUND_tmpbuffer(size_t len)
2624 {
2625   if (len>tmp_buffer_len) {
2626     void *new_buffer = realloc(tmp_buffer, len);
2627     if (new_buffer) {
2628       tmp_buffer = new_buffer;
2629       tmp_buffer_len = len;
2630     }
2631     return new_buffer;
2632   }
2633   return tmp_buffer;
2634 }
2635
2636 static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD fraglen)
2637 {
2638         INT     i, len, ilen, temp, field;
2639         INT     advance = primarybuf->wfx.wBitsPerSample >> 3;
2640         BYTE    *buf, *ibuf, *obuf;
2641         INT16   *ibufs, *obufs;
2642
2643         len = fraglen;
2644         if (!(dsb->playflags & DSBPLAY_LOOPING)) {
2645                 temp = MulDiv(primarybuf->wfx.nAvgBytesPerSec, dsb->buflen,
2646                         dsb->nAvgBytesPerSec) -
2647                        MulDiv(primarybuf->wfx.nAvgBytesPerSec, dsb->buf_mixpos,
2648                         dsb->nAvgBytesPerSec);
2649                 len = (len > temp) ? temp : len;
2650         }
2651         len &= ~3;                              /* 4 byte alignment */
2652
2653         if (len == 0) {
2654                 /* This should only happen if we aren't looping and temp < 4 */
2655
2656                 /* We skip the remainder, so check for possible events */
2657                 DSOUND_CheckEvent(dsb, dsb->buflen - dsb->buf_mixpos);
2658                 /* Stop */
2659                 dsb->state = STATE_STOPPED;
2660                 dsb->playpos = 0;
2661                 dsb->buf_mixpos = 0;
2662                 dsb->leadin = FALSE;
2663                 /* Check for DSBPN_OFFSETSTOP */
2664                 DSOUND_CheckEvent(dsb, 0);
2665                 return 0;
2666         }
2667
2668         /* Been seeing segfaults in malloc() for some reason... */
2669         TRACE("allocating buffer (size = %d)\n", len);
2670         if ((buf = ibuf = (BYTE *) DSOUND_tmpbuffer(len)) == NULL)
2671                 return 0;
2672
2673         TRACE("MixInBuffer (%p) len = %d, dest = %ld\n", dsb, len, writepos);
2674
2675         ilen = DSOUND_MixerNorm(dsb, ibuf, len);
2676         if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
2677             (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
2678                 DSOUND_MixerVol(dsb, ibuf, len);
2679
2680         obuf = primarybuf->buffer + writepos;
2681         for (i = 0; i < len; i += advance) {
2682                 obufs = (INT16 *) obuf;
2683                 ibufs = (INT16 *) ibuf;
2684                 if (primarybuf->wfx.wBitsPerSample == 8) {
2685                         /* 8-bit WAV is unsigned */
2686                         field = (*ibuf - 128);
2687                         field += (*obuf - 128);
2688                         field = field > 127 ? 127 : field;
2689                         field = field < -128 ? -128 : field;
2690                         *obuf = field + 128;
2691                 } else {
2692                         /* 16-bit WAV is signed */
2693                         field = *ibufs;
2694                         field += *obufs;
2695                         field = field > 32767 ? 32767 : field;
2696                         field = field < -32768 ? -32768 : field;
2697                         *obufs = field;
2698                 }
2699                 ibuf += advance;
2700                 obuf += advance;
2701                 if (obuf >= (BYTE *)(primarybuf->buffer + primarybuf->buflen))
2702                         obuf = primarybuf->buffer;
2703         }
2704         /* free(buf); */
2705
2706         if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY)
2707                 DSOUND_CheckEvent(dsb, ilen);
2708
2709         if (dsb->leadin && (dsb->startpos > dsb->buf_mixpos) && (dsb->startpos <= dsb->buf_mixpos + ilen)) {
2710                 /* HACK... leadin should be reset when the PLAY position reaches the startpos,
2711                  * not the MIX position... but if the sound buffer is bigger than our prebuffering
2712                  * (which must be the case for the streaming buffers that need this hack anyway)
2713                  * plus DS_HEL_MARGIN or equivalent, then this ought to work anyway. */
2714                 dsb->leadin = FALSE;
2715         }
2716
2717         dsb->buf_mixpos += ilen;
2718         
2719         if (dsb->buf_mixpos >= dsb->buflen) {
2720                 if (!(dsb->playflags & DSBPLAY_LOOPING)) {
2721                         dsb->state = STATE_STOPPED;
2722                         dsb->playpos = 0;
2723                         dsb->buf_mixpos = 0;
2724                         dsb->leadin = FALSE;
2725                         DSOUND_CheckEvent(dsb, 0);              /* For DSBPN_OFFSETSTOP */
2726                 } else {
2727                         /* wrap */
2728                         while (dsb->buf_mixpos >= dsb->buflen)
2729                                 dsb->buf_mixpos -= dsb->buflen;
2730                         if (dsb->leadin && (dsb->startpos <= dsb->buf_mixpos))
2731                                 dsb->leadin = FALSE; /* HACK: see above */
2732                 }
2733         }
2734
2735         return len;
2736 }
2737
2738 static void DSOUND_MixCancel(IDirectSoundBufferImpl *dsb, DWORD writepos)
2739 {
2740         FIXME("prebuffer cancel not implemented yet\n");
2741 }
2742
2743 static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD playpos, DWORD writepos, DWORD mixlen)
2744 {
2745         DWORD len, slen;
2746         /* determine this buffer's write position */
2747         DWORD buf_writepos = DSOUND_CalcPlayPosition(dsb, dsb->state & primarybuf->state, writepos,
2748                                                      writepos, dsb->primary_mixpos, dsb->buf_mixpos);
2749         /* determine how much already-mixed data exist */
2750         DWORD buf_done =
2751                 ((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) +
2752                 dsb->buf_mixpos - buf_writepos;
2753         DWORD primary_done =
2754                 ((dsb->primary_mixpos < writepos) ? primarybuf->buflen : 0) +
2755                 dsb->primary_mixpos - writepos;
2756         DWORD adv_done =
2757                 ((primarybuf->buf_mixpos < writepos) ? primarybuf->buflen : 0) +
2758                 primarybuf->buf_mixpos - writepos;
2759         int still_behind;
2760
2761         TRACE("buf_writepos=%ld, primary_writepos=%ld\n", buf_writepos, writepos);
2762         TRACE("buf_done=%ld, primary_done=%ld\n", buf_done, primary_done);
2763         TRACE("buf_mixpos=%ld, primary_mixpos=%ld, mixlen=%ld\n", dsb->buf_mixpos, dsb->primary_mixpos,
2764               mixlen);
2765         TRACE("looping=%ld, startpos=%ld, leadin=%ld\n", dsb->playflags, dsb->startpos, dsb->leadin);
2766
2767         /* save write position for non-GETCURRENTPOSITION2... */
2768         dsb->playpos = buf_writepos;
2769
2770         /* check whether CalcPlayPosition detected a mixing underrun */
2771         if ((buf_done == 0) && (dsb->primary_mixpos != writepos)) {
2772                 /* it did, but did we have more to play? */
2773                 if ((dsb->playflags & DSBPLAY_LOOPING) ||
2774                     (dsb->buf_mixpos < dsb->buflen)) {
2775                         /* yes, have to recover */
2776                         ERR("underrun on sound buffer %p\n", dsb);
2777                         TRACE("recovering from underrun: primary_mixpos=%ld\n", writepos);
2778                 }
2779                 dsb->primary_mixpos = writepos;
2780                 primary_done = 0;
2781         }
2782         /* determine how far ahead we should mix */
2783         if (((dsb->playflags & DSBPLAY_LOOPING) ||
2784              (dsb->leadin && (dsb->probably_valid_to != 0))) &&
2785             !(dsb->dsbd.dwFlags & DSBCAPS_STATIC)) {
2786                 /* if this is a streaming buffer, it typically means that
2787                  * we should defer mixing past probably_valid_to as long
2788                  * as we can, to avoid unnecessary remixing */
2789                 /* the heavy-looking calculations shouldn't be that bad,
2790                  * as any game isn't likely to be have more than 1 or 2
2791                  * streaming buffers in use at any time anyway... */
2792                 DWORD probably_valid_left =
2793                         (dsb->probably_valid_to == (DWORD)-1) ? dsb->buflen :
2794                         ((dsb->probably_valid_to < buf_writepos) ? dsb->buflen : 0) +
2795                         dsb->probably_valid_to - buf_writepos;
2796                 /* check for leadin condition */
2797                 if ((probably_valid_left == 0) &&
2798                     (dsb->probably_valid_to == dsb->startpos) &&
2799                     dsb->leadin)
2800                         probably_valid_left = dsb->buflen;
2801                 TRACE("streaming buffer probably_valid_to=%ld, probably_valid_left=%ld\n",
2802                       dsb->probably_valid_to, probably_valid_left);
2803                 /* check whether the app's time is already up */
2804                 if (probably_valid_left < dsb->writelead) {
2805                         WARN("probably_valid_to now within writelead, possible streaming underrun\n");
2806                         /* once we pass the point of no return,
2807                          * no reason to hold back anymore */
2808                         dsb->probably_valid_to = (DWORD)-1;
2809                         /* we just have to go ahead and mix what we have,
2810                          * there's no telling what the app is thinking anyway */
2811                 } else {
2812                         /* divide valid length by our sample size */
2813                         probably_valid_left /= dsb->wfx.nBlockAlign;
2814                         /* adjust for our frequency */
2815                         probably_valid_left = (probably_valid_left << DSOUND_FREQSHIFT) / dsb->freqAdjust;
2816                         /* multiply by primary sample size */
2817                         probably_valid_left *= primarybuf->wfx.nBlockAlign;
2818                         /* check whether to clip mix_len */
2819                         if (probably_valid_left < mixlen) {
2820                                 TRACE("clipping to probably_valid_left=%ld\n", probably_valid_left);
2821                                 mixlen = probably_valid_left;
2822                         }
2823                 }
2824         }
2825         /* cut mixlen with what's already been mixed */
2826         if (mixlen < primary_done) {
2827                 /* huh? and still CalcPlayPosition didn't
2828                  * detect an underrun? */
2829                 FIXME("problem with underrun detection (mixlen=%ld < primary_done=%ld)\n", mixlen, primary_done);
2830                 return 0;
2831         }
2832         len = mixlen - primary_done;
2833         TRACE("remaining mixlen=%ld\n", len);
2834
2835         if (len < primarybuf->dsound->fraglen) {
2836                 /* smaller than a fragment, wait until it gets larger
2837                  * before we take the mixing overhead */
2838                 TRACE("mixlen not worth it, deferring mixing\n");
2839                 return 0;
2840         }
2841
2842         /* ok, we know how much to mix, let's go */
2843         still_behind = (adv_done > primary_done);
2844         while (len) {
2845                 slen = primarybuf->buflen - dsb->primary_mixpos;
2846                 if (slen > len) slen = len;
2847                 slen = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, slen);
2848
2849                 if ((dsb->primary_mixpos < primarybuf->buf_mixpos) &&
2850                     (dsb->primary_mixpos + slen >= primarybuf->buf_mixpos))
2851                         still_behind = FALSE;
2852
2853                 dsb->primary_mixpos += slen; len -= slen;
2854                 while (dsb->primary_mixpos >= primarybuf->buflen)
2855                         dsb->primary_mixpos -= primarybuf->buflen;
2856
2857                 if ((dsb->state == STATE_STOPPED) || !slen) break;
2858         }
2859         TRACE("new primary_mixpos=%ld, primary_advbase=%ld\n", dsb->primary_mixpos, primarybuf->buf_mixpos);
2860         TRACE("mixed data len=%ld, still_behind=%d\n", mixlen-len, still_behind);
2861         /* return how far we think the primary buffer can
2862          * advance its underrun detector...*/
2863         if (still_behind) return 0;
2864         if ((mixlen - len) < primary_done) return 0;
2865         slen = ((dsb->primary_mixpos < primarybuf->buf_mixpos) ?
2866                 primarybuf->buflen : 0) + dsb->primary_mixpos -
2867                 primarybuf->buf_mixpos;
2868         if (slen > mixlen) {
2869                 /* the primary_done and still_behind checks above should have worked */
2870                 FIXME("problem with advancement calculation (advlen=%ld > mixlen=%ld)\n", slen, mixlen);
2871                 slen = 0;
2872         }
2873         return slen;
2874 }
2875
2876 static DWORD DSOUND_MixToPrimary(DWORD playpos, DWORD writepos, DWORD mixlen, BOOL recover)
2877 {
2878         INT                     i, len, maxlen = 0;
2879         IDirectSoundBufferImpl  *dsb;
2880
2881         TRACE("(%ld,%ld,%ld)\n", playpos, writepos, mixlen);
2882         for (i = dsound->nrofbuffers - 1; i >= 0; i--) {
2883                 dsb = dsound->buffers[i];
2884
2885                 if (!dsb || !(ICOM_VTBL(dsb)))
2886                         continue;
2887                 if (dsb->buflen && dsb->state && !dsb->hwbuf) {
2888                         TRACE("Checking %p, mixlen=%ld\n", dsb, mixlen);
2889                         EnterCriticalSection(&(dsb->lock));
2890                         if (dsb->state == STATE_STOPPING) {
2891                                 DSOUND_MixCancel(dsb, writepos);
2892                                 dsb->state = STATE_STOPPED;
2893                         } else {
2894                                 if ((dsb->state == STATE_STARTING) || recover)
2895                                         dsb->primary_mixpos = writepos;
2896                                 len = DSOUND_MixOne(dsb, playpos, writepos, mixlen);
2897                                 if (dsb->state == STATE_STARTING)
2898                                         dsb->state = STATE_PLAYING;
2899                                 maxlen = (len > maxlen) ? len : maxlen;
2900                         }
2901                         LeaveCriticalSection(&(dsb->lock));
2902                 }
2903         }
2904         
2905         return maxlen;
2906 }
2907
2908 static void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
2909 {
2910         int nfiller;
2911         BOOL forced;
2912         HRESULT hres;
2913
2914         if (!dsound || !primarybuf) {
2915                 ERR("dsound died without killing us?\n");
2916                 timeKillEvent(timerID);
2917                 timeEndPeriod(DS_TIME_RES);
2918                 return;
2919         }
2920
2921         EnterCriticalSection(&(dsound->lock));
2922
2923         if (!primarybuf || !primarybuf->ref) {
2924                 /* seems the primary buffer is currently being released */
2925                 LeaveCriticalSection(&(dsound->lock));
2926                 return;
2927         }
2928
2929         /* the sound of silence */
2930         nfiller = primarybuf->wfx.wBitsPerSample == 8 ? 128 : 0;
2931
2932         /* whether the primary is forced to play even without secondary buffers */
2933         forced = ((primarybuf->state == STATE_PLAYING) || (primarybuf->state == STATE_STARTING));
2934
2935         if (primarybuf->hwbuf) {
2936                 if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
2937                         BOOL paused = ((primarybuf->state == STATE_STOPPED) || (primarybuf->state == STATE_STARTING));
2938                         DWORD playpos, writepos, inq, maxq, frag;
2939                         hres = IDsDriverBuffer_GetPosition(primarybuf->hwbuf, &playpos, &writepos);
2940                         if (hres) {
2941                             LeaveCriticalSection(&(dsound->lock));
2942                             return;
2943                         }
2944                         /* Well, we *could* do Just-In-Time mixing using the writepos,
2945                          * but that's a little bit ambitious and unnecessary... */
2946                         /* rather add our safety margin to the writepos, if we're playing */
2947                         if (!paused) {
2948                                 writepos += primarybuf->writelead;
2949                                 while (writepos >= primarybuf->buflen)
2950                                         writepos -= primarybuf->buflen;
2951                         } else writepos = playpos;
2952                         TRACE("primary playpos=%ld, writepos=%ld, clrpos=%ld, mixpos=%ld\n",
2953                               playpos,writepos,primarybuf->playpos,primarybuf->buf_mixpos);
2954                         /* wipe out just-played sound data */
2955                         if (playpos < primarybuf->playpos) {
2956                                 memset(primarybuf->buffer + primarybuf->playpos, nfiller, primarybuf->buflen - primarybuf->playpos);
2957                                 memset(primarybuf->buffer, nfiller, playpos);
2958                         } else {
2959                                 memset(primarybuf->buffer + primarybuf->playpos, nfiller, playpos - primarybuf->playpos);
2960                         }
2961                         primarybuf->playpos = playpos;
2962
2963                         /* check how much prebuffering is left */
2964                         inq = primarybuf->buf_mixpos;
2965                         if (inq < writepos)
2966                                 inq += primarybuf->buflen;
2967                         inq -= writepos;
2968
2969                         /* find the maximum we can prebuffer */
2970                         if (!paused) {
2971                                 maxq = playpos;
2972                                 if (maxq < writepos)
2973                                         maxq += primarybuf->buflen;
2974                                 maxq -= writepos;
2975                         } else maxq = primarybuf->buflen;
2976
2977                         /* clip maxq to DS_SND_QUEUE */
2978                         frag = DS_SND_QUEUE * dsound->fraglen;
2979                         if (maxq > frag) maxq = frag;
2980
2981                         EnterCriticalSection(&(primarybuf->lock));
2982
2983                         /* check for consistency */
2984                         if (inq > maxq) {
2985                                 /* the playback position must have passed our last
2986                                  * mixed position, i.e. it's an underrun, or we have
2987                                  * nothing more to play */
2988                                 TRACE("reached end of mixed data (inq=%ld, maxq=%ld)\n", inq, maxq);
2989                                 inq = 0;
2990                                 /* stop the playback now, to allow buffers to refill */
2991                                 DSOUND_PrimaryStop(primarybuf);
2992                                 if (primarybuf->state == STATE_PLAYING) {
2993                                         primarybuf->state = STATE_STARTING;
2994                                 }
2995                                 else if (primarybuf->state == STATE_STOPPING) {
2996                                         primarybuf->state = STATE_STOPPED;
2997                                 }
2998                                 else {
2999                                         /* how can we have an underrun if we aren't playing? */
3000                                         WARN("unexpected primary state (%ld)\n", primarybuf->state);
3001                                 }
3002                                 /* the Stop is supposed to reset play position to beginning of buffer */
3003                                 /* unfortunately, OSS is not able to do so, so get current pointer */
3004                                 hres = IDsDriverBuffer_GetPosition(primarybuf->hwbuf, &playpos, NULL);
3005                                 if (hres) {
3006                                         LeaveCriticalSection(&(dsound->lock));
3007                                         LeaveCriticalSection(&(primarybuf->lock));
3008                                         return;
3009                                 }
3010                                 writepos = playpos;
3011                                 primarybuf->playpos = playpos;
3012                                 primarybuf->buf_mixpos = writepos;
3013                                 inq = 0;
3014                                 maxq = primarybuf->buflen;
3015                                 if (maxq > frag) maxq = frag;
3016                                 memset(primarybuf->buffer, nfiller, primarybuf->buflen);
3017                                 paused = TRUE;
3018                         }
3019
3020                         /* do the mixing */
3021                         frag = DSOUND_MixToPrimary(playpos, writepos, maxq, paused);
3022                         if (forced) frag = maxq - inq;
3023                         primarybuf->buf_mixpos += frag;
3024                         while (primarybuf->buf_mixpos >= primarybuf->buflen)
3025                                 primarybuf->buf_mixpos -= primarybuf->buflen;
3026
3027                         if (frag) {
3028                                 /* buffers have been filled, restart playback */
3029                                 if (primarybuf->state == STATE_STARTING) {
3030                                         DSOUND_PrimaryPlay(primarybuf);
3031                                         primarybuf->state = STATE_PLAYING;
3032                                         TRACE("starting playback\n");
3033                                 }
3034                                 else if (primarybuf->state == STATE_STOPPED) {
3035                                         /* the primarybuf is supposed to play if there's something to play
3036                                          * even if it is reported as stopped, so don't let this confuse you */
3037                                         DSOUND_PrimaryPlay(primarybuf);
3038                                         primarybuf->state = STATE_STOPPING;
3039                                         TRACE("starting playback\n");
3040                                 }
3041                         }
3042                         LeaveCriticalSection(&(primarybuf->lock));
3043                 } else {
3044                         /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
3045                         if (primarybuf->state == STATE_STARTING) {
3046                                 DSOUND_PrimaryPlay(primarybuf);
3047                                 primarybuf->state = STATE_PLAYING;
3048                         } 
3049                         else if (primarybuf->state == STATE_STOPPING) {
3050                                 DSOUND_PrimaryStop(primarybuf);
3051                                 primarybuf->state = STATE_STOPPED;
3052                         }
3053                 }
3054         } else {
3055                 /* using waveOut stuff */
3056                 /* if no buffers are playing, we should be in pause mode now */
3057                 DWORD writepos, mixq;
3058                 /* clean out completed fragments */
3059                 while (dsound->pwqueue && (dsound->pwave[dsound->pwplay]->dwFlags & WHDR_DONE)) {
3060                         if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
3061                                 DWORD pos = dsound->pwplay * dsound->fraglen;
3062                                 TRACE("done playing primary pos=%ld\n",pos);
3063                                 memset(primarybuf->buffer + pos, nfiller, dsound->fraglen);
3064                         }
3065                         dsound->pwplay++;
3066                         if (dsound->pwplay >= DS_HEL_FRAGS) dsound->pwplay = 0;
3067                         dsound->pwqueue--;
3068                 }
3069                 primarybuf->playpos = dsound->pwplay * dsound->fraglen;
3070                 TRACE("primary playpos=%ld, mixpos=%ld\n",primarybuf->playpos,primarybuf->buf_mixpos);
3071                 EnterCriticalSection(&(primarybuf->lock));
3072
3073                 if (!dsound->pwqueue) {
3074                         /* this is either an underrun or we have nothing more to play...
3075                          * since playback has already stopped now, we can enter pause mode,
3076                          * in order to allow buffers to refill */
3077                         if (primarybuf->state == STATE_PLAYING) {
3078                                 TRACE("no more fragments ready to play\n");
3079                                 waveOutPause(dsound->hwo);
3080                                 primarybuf->state = STATE_STARTING;
3081                         }
3082                         else if (primarybuf->state == STATE_STOPPING) {
3083                                 TRACE("no more fragments ready to play\n");
3084                                 waveOutPause(dsound->hwo);
3085                                 primarybuf->state = STATE_STOPPED;
3086                         }
3087                 }
3088
3089                 /* find next write position, plus some extra margin, if necessary */
3090                 mixq = DS_HEL_MARGIN;
3091                 if (mixq > dsound->pwqueue) mixq = dsound->pwqueue;
3092                 writepos = primarybuf->playpos + mixq * dsound->fraglen;
3093                 while (writepos >= primarybuf->buflen) writepos -= primarybuf->buflen;
3094
3095                 /* do the mixing */
3096                 if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
3097                         DWORD frag, maxq = DS_SND_QUEUE * dsound->fraglen;
3098                         frag = DSOUND_MixToPrimary(primarybuf->playpos, writepos, maxq, FALSE);
3099                         mixq = frag / dsound->fraglen;
3100                         if (frag - (mixq * dsound->fraglen))
3101                                 mixq++;
3102                 } else mixq = 0;
3103                 if (forced) mixq = DS_SND_QUEUE;
3104
3105                 /* output it */
3106                 for (; mixq; mixq--) {
3107                         waveOutWrite(dsound->hwo, dsound->pwave[dsound->pwwrite], sizeof(WAVEHDR));
3108                         dsound->pwwrite++;
3109                         if (dsound->pwwrite >= DS_HEL_FRAGS) dsound->pwwrite = 0;
3110                         dsound->pwqueue++;
3111                 }
3112                 primarybuf->buf_mixpos = dsound->pwwrite * dsound->fraglen;
3113
3114                 if (dsound->pwqueue) {
3115                         /* buffers have been filled, restart playback */
3116                         if (primarybuf->state == STATE_STARTING) {
3117                                 waveOutRestart(dsound->hwo);
3118                                 primarybuf->state = STATE_PLAYING;
3119                                 TRACE("starting playback\n");
3120                         }
3121                         else if (primarybuf->state == STATE_STOPPED) {
3122                                 /* the primarybuf is supposed to play if there's something to play
3123                                  * even if it is reported as stopped, so don't let this confuse you */
3124                                 waveOutRestart(dsound->hwo);
3125                                 primarybuf->state = STATE_STOPPING;
3126                                 TRACE("starting playback\n");
3127                         }
3128                 }
3129                 LeaveCriticalSection(&(primarybuf->lock));
3130         }
3131         TRACE("completed processing\n");
3132         LeaveCriticalSection(&(dsound->lock));
3133 }
3134
3135 /*******************************************************************************
3136  *              DirectSoundCreate
3137  */
3138 HRESULT WINAPI DirectSoundCreate(REFGUID lpGUID,LPDIRECTSOUND *ppDS,IUnknown *pUnkOuter )
3139 {
3140         IDirectSoundImpl** ippDS=(IDirectSoundImpl**)ppDS;
3141         PIDSDRIVER drv = NULL;
3142         WAVEOUTCAPSA wcaps;
3143         unsigned wod, wodn;
3144         HRESULT err = DS_OK;
3145
3146         if (lpGUID)
3147                 TRACE("(%p,%p,%p)\n",lpGUID,ippDS,pUnkOuter);
3148         else
3149                 TRACE("DirectSoundCreate (%p)\n", ippDS);
3150
3151         if (ippDS == NULL)
3152                 return DSERR_INVALIDPARAM;
3153
3154         if (primarybuf) {
3155                 IDirectSound_AddRef((LPDIRECTSOUND)dsound);
3156                 *ippDS = dsound;
3157                 return DS_OK;
3158         }
3159
3160         /* Enumerate WINMM audio devices and find the one we want */
3161         wodn = waveOutGetNumDevs();
3162         if (!wodn) return DSERR_NODRIVER;
3163
3164         /* FIXME: How do we find the GUID of an audio device? */
3165         /* Well, just use the first available device for now... */
3166         wod = 0;
3167         /* Get output device caps */
3168         waveOutGetDevCapsA(wod, &wcaps, sizeof(wcaps));
3169         /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
3170         waveOutMessage(wod, DRV_QUERYDSOUNDIFACE, (DWORD)&drv, 0);
3171
3172         /* Allocate memory */
3173         *ippDS = (IDirectSoundImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSoundImpl));
3174         if (*ippDS == NULL)
3175                 return DSERR_OUTOFMEMORY;
3176
3177         ICOM_VTBL(*ippDS)       = &dsvt;
3178         (*ippDS)->ref           = 1;
3179
3180         (*ippDS)->driver        = drv;
3181         (*ippDS)->fraglen       = 0;
3182         (*ippDS)->priolevel     = DSSCL_NORMAL; 
3183         (*ippDS)->nrofbuffers   = 0;
3184         (*ippDS)->buffers       = NULL;
3185         (*ippDS)->primary       = NULL; 
3186         (*ippDS)->listener      = NULL; 
3187
3188         /* Get driver description */
3189         if (drv) {
3190                 IDsDriver_GetDriverDesc(drv,&((*ippDS)->drvdesc));
3191         } else {
3192                 /* if no DirectSound interface available, use WINMM API instead */
3193                 (*ippDS)->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
3194                 (*ippDS)->drvdesc.dnDevNode = wod; /* FIXME? */
3195         }
3196
3197         /* Set default wave format (may need it for waveOutOpen) */
3198         (*ippDS)->wfx.wFormatTag        = WAVE_FORMAT_PCM;
3199         (*ippDS)->wfx.nChannels         = 2;
3200         (*ippDS)->wfx.nSamplesPerSec    = 22050;
3201         (*ippDS)->wfx.nAvgBytesPerSec   = 44100;
3202         (*ippDS)->wfx.nBlockAlign       = 2;
3203         (*ippDS)->wfx.wBitsPerSample    = 8;
3204
3205         /* If the driver requests being opened through MMSYSTEM
3206          * (which is recommended by the DDK), it is supposed to happen
3207          * before the DirectSound interface is opened */
3208         if ((*ippDS)->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) {
3209                 /* FIXME: is this right? */
3210                 err = mmErr(waveOutOpen(&((*ippDS)->hwo), (*ippDS)->drvdesc.dnDevNode, &((*ippDS)->wfx),
3211                                         0, 0, CALLBACK_NULL | WAVE_DIRECTSOUND));
3212         }
3213
3214         if (drv && (err == DS_OK))
3215                 err = IDsDriver_Open(drv);
3216
3217         /* FIXME: do we want to handle a temporarily busy device? */
3218         if (err != DS_OK) {
3219                 HeapFree(GetProcessHeap(),0,*ippDS);
3220                 *ippDS = NULL;
3221                 return err;
3222         }
3223
3224         /* the driver is now open, so it's now allowed to call GetCaps */
3225         if (drv) {
3226                 IDsDriver_GetCaps(drv,&((*ippDS)->drvcaps));
3227         } else {
3228                 unsigned c;
3229
3230                 /* FIXME: look at wcaps */
3231                 (*ippDS)->drvcaps.dwFlags =
3232                         DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO;
3233                 if (DS_EMULDRIVER)
3234                     (*ippDS)->drvcaps.dwFlags |= DSCAPS_EMULDRIVER;
3235
3236                 /* Allocate memory for HEL buffer headers */
3237                 for (c=0; c<DS_HEL_FRAGS; c++) {
3238                         (*ippDS)->pwave[c] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEHDR));
3239                         if (!(*ippDS)->pwave[c]) {
3240                                 /* Argh, out of memory */
3241                                 while (c--) {
3242                                         HeapFree(GetProcessHeap(),0,(*ippDS)->pwave[c]);
3243                                         waveOutClose((*ippDS)->hwo);
3244                                         HeapFree(GetProcessHeap(),0,*ippDS);
3245                                         *ippDS = NULL;
3246                                         return DSERR_OUTOFMEMORY;
3247                                 }
3248                         }
3249                 }
3250         }
3251
3252         InitializeCriticalSection(&((*ippDS)->lock));
3253
3254         if (!dsound) {
3255                 dsound = (*ippDS);
3256                 if (primarybuf == NULL) {
3257                         DSBUFFERDESC    dsbd;
3258                         HRESULT         hr;
3259
3260                         dsbd.dwSize = sizeof(DSBUFFERDESC);
3261                         dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
3262                         dsbd.dwBufferBytes = 0;
3263                         dsbd.lpwfxFormat = &(dsound->wfx);
3264                         hr = IDirectSound_CreateSoundBuffer(*ppDS, &dsbd, (LPDIRECTSOUNDBUFFER*)&primarybuf, NULL);
3265                         if (hr != DS_OK)
3266                                 return hr;
3267
3268                         /* dsound->primary is NULL - don't need to Release */
3269                         dsound->primary = primarybuf;
3270                         IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)primarybuf);
3271                 }
3272                 timeBeginPeriod(DS_TIME_RES);
3273                 dsound->timerID = timeSetEvent(DS_TIME_DEL, DS_TIME_RES, DSOUND_timer,
3274                                                (DWORD)dsound, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
3275         }
3276         return DS_OK;
3277 }
3278
3279 /***************************************************************************
3280  * DirectSoundCaptureCreate [DSOUND.6]
3281  *
3282  * Create and initialize a DirectSoundCapture interface
3283  *
3284  * RETURNS
3285  *    Success: DS_OK
3286  *    Failure: DSERR_NOAGGREGATION, DSERR_ALLOCATED, DSERR_INVALIDPARAM,
3287  *             DSERR_OUTOFMEMORY
3288  */
3289 HRESULT WINAPI DirectSoundCaptureCreate(
3290         LPCGUID lpcGUID,
3291         LPDIRECTSOUNDCAPTURE* lplpDSC,
3292         LPUNKNOWN pUnkOuter )
3293 {
3294         TRACE("(%s,%p,%p)\n", debugstr_guid(lpcGUID), lplpDSC, pUnkOuter);
3295
3296         if( pUnkOuter ) {
3297                 return DSERR_NOAGGREGATION;
3298         }
3299
3300         /* Default device? */
3301         if ( !lpcGUID ) {
3302                 return DSOUND_CreateDirectSoundCapture( (LPVOID*)lplpDSC );
3303         }
3304
3305         FIXME( "Unknown GUID %s\n", debugstr_guid(lpcGUID) );
3306         *lplpDSC = NULL;
3307
3308         return DSERR_OUTOFMEMORY;
3309 }
3310
3311 /***************************************************************************
3312  * DirectSoundCaptureEnumerateA [DSOUND.7]
3313  *
3314  * Enumerate all DirectSound drivers installed in the system
3315  *
3316  * RETURNS
3317  *    Success: DS_OK
3318  *    Failure: DSERR_INVALIDPARAM
3319  */
3320 HRESULT WINAPI DirectSoundCaptureEnumerateA(
3321         LPDSENUMCALLBACKA lpDSEnumCallback,
3322         LPVOID lpContext)
3323 {
3324         TRACE("(%p,%p)\n", lpDSEnumCallback, lpContext );
3325
3326         if ( lpDSEnumCallback )
3327                 lpDSEnumCallback(NULL,"WINE Primary Sound Capture Driver",
3328                     "SoundCap",lpContext);
3329
3330
3331         return DS_OK;
3332 }
3333
3334 /***************************************************************************
3335  * DirectSoundCaptureEnumerateW [DSOUND.8]
3336  *
3337  * Enumerate all DirectSound drivers installed in the system
3338  *
3339  * RETURNS
3340  *    Success: DS_OK
3341  *    Failure: DSERR_INVALIDPARAM
3342  */
3343 HRESULT WINAPI DirectSoundCaptureEnumerateW(
3344         LPDSENUMCALLBACKW lpDSEnumCallback,
3345         LPVOID lpContext)
3346 {
3347         FIXME("(%p,%p):stub\n", lpDSEnumCallback, lpContext );
3348         return DS_OK;
3349
3350
3351 static HRESULT
3352 DSOUND_CreateDirectSoundCapture( LPVOID* ppobj )
3353 {
3354         *ppobj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( IDirectSoundCaptureImpl ) );
3355
3356         if ( *ppobj == NULL ) {
3357                 return DSERR_OUTOFMEMORY;
3358         }
3359
3360         {
3361                 ICOM_THIS(IDirectSoundCaptureImpl,*ppobj);
3362
3363                 This->ref = 1;
3364                 ICOM_VTBL(This) = &dscvt;
3365
3366                 InitializeCriticalSection( &This->lock );
3367         }
3368
3369         return S_OK;
3370 }
3371
3372 static HRESULT WINAPI
3373 IDirectSoundCaptureImpl_QueryInterface(
3374         LPDIRECTSOUNDCAPTURE iface,
3375         REFIID riid,
3376         LPVOID* ppobj )
3377 {
3378         ICOM_THIS(IDirectSoundCaptureImpl,iface);
3379
3380         FIXME( "(%p)->(%s,%p): stub\n", This, debugstr_guid(riid), ppobj );
3381
3382         return E_FAIL;
3383 }
3384
3385 static ULONG WINAPI
3386 IDirectSoundCaptureImpl_AddRef( LPDIRECTSOUNDCAPTURE iface )
3387 {
3388         ULONG uRef;
3389         ICOM_THIS(IDirectSoundCaptureImpl,iface);
3390
3391         EnterCriticalSection( &This->lock );
3392
3393         TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3394         uRef = ++(This->ref);
3395
3396         LeaveCriticalSection( &This->lock );
3397
3398         return uRef;
3399 }
3400
3401 static ULONG WINAPI
3402 IDirectSoundCaptureImpl_Release( LPDIRECTSOUNDCAPTURE iface )
3403 {
3404         ULONG uRef;
3405         ICOM_THIS(IDirectSoundCaptureImpl,iface);
3406
3407         EnterCriticalSection( &This->lock );
3408
3409         TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3410         uRef = --(This->ref);
3411
3412         LeaveCriticalSection( &This->lock );
3413
3414         if ( uRef == 0 ) {
3415                 DeleteCriticalSection( &This->lock );
3416                 HeapFree( GetProcessHeap(), 0, This );
3417         }
3418
3419         return uRef;
3420 }
3421
3422 static HRESULT WINAPI
3423 IDirectSoundCaptureImpl_CreateCaptureBuffer(
3424         LPDIRECTSOUNDCAPTURE iface,
3425         LPCDSCBUFFERDESC lpcDSCBufferDesc,
3426         LPDIRECTSOUNDCAPTUREBUFFER* lplpDSCaptureBuffer, 
3427         LPUNKNOWN pUnk )
3428 {
3429         HRESULT hr;
3430         ICOM_THIS(IDirectSoundCaptureImpl,iface);
3431
3432         TRACE( "(%p)->(%p,%p,%p)\n", This, lpcDSCBufferDesc, lplpDSCaptureBuffer, pUnk );
3433
3434         if ( pUnk ) {
3435                 return DSERR_INVALIDPARAM;
3436         }
3437
3438         hr = DSOUND_CreateDirectSoundCaptureBuffer( lpcDSCBufferDesc, (LPVOID*)lplpDSCaptureBuffer );
3439
3440         return hr;
3441 }
3442
3443 static HRESULT WINAPI
3444 IDirectSoundCaptureImpl_GetCaps(
3445         LPDIRECTSOUNDCAPTURE iface,
3446         LPDSCCAPS lpDSCCaps )
3447 {
3448         ICOM_THIS(IDirectSoundCaptureImpl,iface);
3449
3450         FIXME( "(%p)->(%p): stub\n", This, lpDSCCaps );
3451
3452         return DS_OK;
3453 }
3454
3455 static HRESULT WINAPI
3456 IDirectSoundCaptureImpl_Initialize(
3457         LPDIRECTSOUNDCAPTURE iface,
3458         LPCGUID lpcGUID )
3459 {
3460         ICOM_THIS(IDirectSoundCaptureImpl,iface);
3461
3462         FIXME( "(%p)->(%p): stub\n", This, lpcGUID );
3463
3464         return DS_OK;
3465 }
3466
3467
3468 static ICOM_VTABLE(IDirectSoundCapture) dscvt =
3469 {
3470         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3471         /* IUnknown methods */
3472         IDirectSoundCaptureImpl_QueryInterface,
3473         IDirectSoundCaptureImpl_AddRef,
3474         IDirectSoundCaptureImpl_Release,
3475
3476         /* IDirectSoundCapture methods */
3477         IDirectSoundCaptureImpl_CreateCaptureBuffer,
3478         IDirectSoundCaptureImpl_GetCaps,
3479         IDirectSoundCaptureImpl_Initialize 
3480 };
3481
3482 static HRESULT
3483 DSOUND_CreateDirectSoundCaptureBuffer( LPCDSCBUFFERDESC lpcDSCBufferDesc, LPVOID* ppobj )
3484 {
3485
3486         FIXME( "(%p,%p): ignoring lpcDSCBufferDesc\n", lpcDSCBufferDesc, ppobj );
3487
3488         *ppobj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( IDirectSoundCaptureBufferImpl ) );
3489
3490         if ( *ppobj == NULL ) {
3491                 return DSERR_OUTOFMEMORY;
3492         }
3493
3494         {
3495                 ICOM_THIS(IDirectSoundCaptureBufferImpl,*ppobj);
3496
3497                 This->ref = 1;
3498                 ICOM_VTBL(This) = &dscbvt;
3499
3500                 InitializeCriticalSection( &This->lock );
3501         }
3502
3503         return S_OK;
3504 }
3505
3506
3507 static HRESULT WINAPI
3508 IDirectSoundCaptureBufferImpl_QueryInterface(
3509         LPDIRECTSOUNDCAPTUREBUFFER iface,
3510         REFIID riid,
3511         LPVOID* ppobj )
3512 {
3513         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3514
3515         FIXME( "(%p)->(%s,%p): stub\n", This, debugstr_guid(riid), ppobj );
3516
3517         return E_FAIL;
3518 }
3519
3520 static ULONG WINAPI
3521 IDirectSoundCaptureBufferImpl_AddRef( LPDIRECTSOUNDCAPTUREBUFFER iface )
3522 {
3523         ULONG uRef;
3524         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3525
3526         EnterCriticalSection( &This->lock );
3527
3528         TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3529         uRef = ++(This->ref);
3530
3531         LeaveCriticalSection( &This->lock );
3532
3533         return uRef;
3534 }
3535
3536 static ULONG WINAPI
3537 IDirectSoundCaptureBufferImpl_Release( LPDIRECTSOUNDCAPTUREBUFFER iface )
3538 {
3539         ULONG uRef;
3540         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3541
3542         EnterCriticalSection( &This->lock );
3543
3544         TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3545         uRef = --(This->ref);
3546
3547         LeaveCriticalSection( &This->lock );
3548
3549         if ( uRef == 0 ) {
3550                 DeleteCriticalSection( &This->lock );
3551                 HeapFree( GetProcessHeap(), 0, This );
3552         }
3553
3554         return uRef;
3555 }
3556
3557 static HRESULT WINAPI
3558 IDirectSoundCaptureBufferImpl_GetCaps(
3559         LPDIRECTSOUNDCAPTUREBUFFER iface,
3560         LPDSCBCAPS lpDSCBCaps )
3561 {
3562         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3563
3564         FIXME( "(%p)->(%p): stub\n", This, lpDSCBCaps );
3565
3566         return DS_OK;
3567 }
3568
3569 static HRESULT WINAPI
3570 IDirectSoundCaptureBufferImpl_GetCurrentPosition(
3571         LPDIRECTSOUNDCAPTUREBUFFER iface,
3572         LPDWORD lpdwCapturePosition,
3573         LPDWORD lpdwReadPosition )
3574 {
3575         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3576
3577         FIXME( "(%p)->(%p,%p): stub\n", This, lpdwCapturePosition, lpdwReadPosition );
3578
3579         return DS_OK;
3580 }
3581
3582 static HRESULT WINAPI
3583 IDirectSoundCaptureBufferImpl_GetFormat(
3584         LPDIRECTSOUNDCAPTUREBUFFER iface,
3585         LPWAVEFORMATEX lpwfxFormat, 
3586         DWORD dwSizeAllocated, 
3587         LPDWORD lpdwSizeWritten )
3588 {
3589         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3590
3591         FIXME( "(%p)->(%p,0x%08lx,%p): stub\n", This, lpwfxFormat, dwSizeAllocated, lpdwSizeWritten );
3592
3593         return DS_OK;
3594 }
3595
3596 static HRESULT WINAPI
3597 IDirectSoundCaptureBufferImpl_GetStatus(
3598         LPDIRECTSOUNDCAPTUREBUFFER iface,
3599         LPDWORD lpdwStatus )
3600 {
3601         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3602
3603         FIXME( "(%p)->(%p): stub\n", This, lpdwStatus );
3604
3605         return DS_OK;
3606 }
3607
3608 static HRESULT WINAPI
3609 IDirectSoundCaptureBufferImpl_Initialize(
3610         LPDIRECTSOUNDCAPTUREBUFFER iface,
3611         LPDIRECTSOUNDCAPTURE lpDSC, 
3612         LPCDSCBUFFERDESC lpcDSCBDesc )
3613 {
3614         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3615
3616         FIXME( "(%p)->(%p,%p): stub\n", This, lpDSC, lpcDSCBDesc );
3617
3618         return DS_OK;
3619 }
3620
3621 static HRESULT WINAPI
3622 IDirectSoundCaptureBufferImpl_Lock(
3623         LPDIRECTSOUNDCAPTUREBUFFER iface,
3624         DWORD dwReadCusor, 
3625         DWORD dwReadBytes, 
3626         LPVOID* lplpvAudioPtr1, 
3627         LPDWORD lpdwAudioBytes1, 
3628         LPVOID* lplpvAudioPtr2, 
3629         LPDWORD lpdwAudioBytes2, 
3630         DWORD dwFlags )
3631 {
3632         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3633
3634         FIXME( "(%p)->(%08lu,%08lu,%p,%p,%p,%p,0x%08lx): stub\n", This, dwReadCusor, dwReadBytes, lplpvAudioPtr1, lpdwAudioBytes1, lplpvAudioPtr2, lpdwAudioBytes2, dwFlags );
3635
3636         return DS_OK;
3637 }
3638
3639 static HRESULT WINAPI
3640 IDirectSoundCaptureBufferImpl_Start(
3641         LPDIRECTSOUNDCAPTUREBUFFER iface,
3642         DWORD dwFlags )
3643 {
3644         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3645
3646         FIXME( "(%p)->(0x%08lx): stub\n", This, dwFlags );
3647
3648         return DS_OK;
3649 }
3650
3651 static HRESULT WINAPI
3652 IDirectSoundCaptureBufferImpl_Stop( LPDIRECTSOUNDCAPTUREBUFFER iface )
3653 {
3654         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3655
3656         FIXME( "(%p): stub\n", This );
3657
3658         return DS_OK;
3659 }
3660
3661 static HRESULT WINAPI
3662 IDirectSoundCaptureBufferImpl_Unlock(
3663         LPDIRECTSOUNDCAPTUREBUFFER iface,
3664         LPVOID lpvAudioPtr1, 
3665         DWORD dwAudioBytes1, 
3666         LPVOID lpvAudioPtr2, 
3667         DWORD dwAudioBytes2 )
3668 {
3669         ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3670
3671         FIXME( "(%p)->(%p,%08lu,%p,%08lu): stub\n", This, lpvAudioPtr1, dwAudioBytes1, lpvAudioPtr2, dwAudioBytes2 );
3672
3673         return DS_OK;
3674 }
3675
3676
3677 static ICOM_VTABLE(IDirectSoundCaptureBuffer) dscbvt =
3678 {
3679         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3680         /* IUnknown methods */
3681         IDirectSoundCaptureBufferImpl_QueryInterface,
3682         IDirectSoundCaptureBufferImpl_AddRef,
3683         IDirectSoundCaptureBufferImpl_Release,
3684
3685         /* IDirectSoundCaptureBuffer methods */
3686         IDirectSoundCaptureBufferImpl_GetCaps,
3687         IDirectSoundCaptureBufferImpl_GetCurrentPosition,
3688         IDirectSoundCaptureBufferImpl_GetFormat,
3689         IDirectSoundCaptureBufferImpl_GetStatus,
3690         IDirectSoundCaptureBufferImpl_Initialize,
3691         IDirectSoundCaptureBufferImpl_Lock,
3692         IDirectSoundCaptureBufferImpl_Start,
3693         IDirectSoundCaptureBufferImpl_Stop,
3694         IDirectSoundCaptureBufferImpl_Unlock
3695 };
3696
3697 /*******************************************************************************
3698  * DirectSound ClassFactory
3699  */
3700 typedef struct
3701 {
3702     /* IUnknown fields */
3703     ICOM_VFIELD(IClassFactory);
3704     DWORD                       ref;
3705 } IClassFactoryImpl;
3706
3707 static HRESULT WINAPI 
3708 DSCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
3709         ICOM_THIS(IClassFactoryImpl,iface);
3710
3711         FIXME("(%p)->(%s,%p),stub!\n",This,debugstr_guid(riid),ppobj);
3712         return E_NOINTERFACE;
3713 }
3714
3715 static ULONG WINAPI
3716 DSCF_AddRef(LPCLASSFACTORY iface) {
3717         ICOM_THIS(IClassFactoryImpl,iface);
3718         return ++(This->ref);
3719 }
3720
3721 static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface) {
3722         ICOM_THIS(IClassFactoryImpl,iface);
3723         /* static class, won't be  freed */
3724         return --(This->ref);
3725 }
3726
3727 static HRESULT WINAPI DSCF_CreateInstance(
3728         LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
3729 ) {
3730         ICOM_THIS(IClassFactoryImpl,iface);
3731
3732         TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
3733         if ( IsEqualGUID( &IID_IDirectSound, riid ) ) {
3734                 /* FIXME: reuse already created dsound if present? */
3735                 return DirectSoundCreate(riid,(LPDIRECTSOUND*)ppobj,pOuter);
3736         }
3737         return E_NOINTERFACE;
3738 }
3739
3740 static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
3741         ICOM_THIS(IClassFactoryImpl,iface);
3742         FIXME("(%p)->(%d),stub!\n",This,dolock);
3743         return S_OK;
3744 }
3745
3746 static ICOM_VTABLE(IClassFactory) DSCF_Vtbl = {
3747         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3748         DSCF_QueryInterface,
3749         DSCF_AddRef,
3750         DSCF_Release,
3751         DSCF_CreateInstance,
3752         DSCF_LockServer
3753 };
3754 static IClassFactoryImpl DSOUND_CF = {&DSCF_Vtbl, 1 };
3755
3756 /*******************************************************************************
3757  * DllGetClassObject [DSOUND.4]
3758  * Retrieves class object from a DLL object
3759  *
3760  * NOTES
3761  *    Docs say returns STDAPI
3762  *
3763  * PARAMS
3764  *    rclsid [I] CLSID for the class object
3765  *    riid   [I] Reference to identifier of interface for class object
3766  *    ppv    [O] Address of variable to receive interface pointer for riid
3767  *
3768  * RETURNS
3769  *    Success: S_OK
3770  *    Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
3771  *             E_UNEXPECTED
3772  */
3773 DWORD WINAPI DSOUND_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
3774 {
3775     TRACE("(%p,%p,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
3776     if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
3777         *ppv = (LPVOID)&DSOUND_CF;
3778         IClassFactory_AddRef((IClassFactory*)*ppv);
3779     return S_OK;
3780     }
3781
3782     FIXME("(%p,%p,%p): no interface found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
3783     return CLASS_E_CLASSNOTAVAILABLE;
3784 }
3785
3786
3787 /*******************************************************************************
3788  * DllCanUnloadNow [DSOUND.3]  Determines whether the DLL is in use.
3789  *
3790  * RETURNS
3791  *    Success: S_OK
3792  *    Failure: S_FALSE
3793  */
3794 DWORD WINAPI DSOUND_DllCanUnloadNow(void)
3795 {
3796     FIXME("(void): stub\n");
3797     return S_FALSE;
3798 }