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