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