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