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