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