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