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