3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998 Rob Riggs
5 * Copyright 2000-2001 TransGaming Technologies, Inc.
8 * Most thread locking is complete. There may be a few race
9 * conditions still lurking.
11 * Tested with a Soundblaster clone, a Gravis UltraSound Classic,
12 * and a Turtle Beach Tropez+.
15 * Implement DirectSoundCapture API
16 * Implement SetCooperativeLevel properly (need to address focus issues)
17 * Implement DirectSound3DBuffers (stubs in place)
18 * Use hardware 3D support if available
19 * Add critical section locking inside Release and AddRef methods
20 * Handle static buffers - put those in hardware, non-static not in hardware
21 * Hardware DuplicateSoundBuffer
22 * Proper volume calculation, and setting volume in HEL primary buffer
23 * Optimize WINMM and negotiate fragment size, decrease DS_HEL_MARGIN
29 #include <sys/types.h>
30 #include <sys/fcntl.h>
34 #include <math.h> /* Insomnia - pow() function */
43 #include "wine/windef16.h"
44 #include "debugtools.h"
48 DEFAULT_DEBUG_CHANNEL(dsound
);
50 /* these are eligible for tuning... they must be high on slow machines... */
51 /* some stuff may get more responsive with lower values though... */
52 #define DS_EMULDRIVER 1 /* some games (Quake 2, UT) refuse to accept
53 emulated dsound devices. set to 0 ! */
54 #define DS_HEL_FRAGS 48 /* HEL only: number of waveOut fragments in primary buffer
55 * (changing this won't help you) */
56 #define DS_HEL_MARGIN 5 /* HEL only: number of waveOut fragments ahead to mix in new buffers
57 * (keep this close or equal to DS_HEL_QUEUE for best results) */
58 #define DS_HEL_QUEUE 5 /* HEL only: number of waveOut fragments ahead to queue to driver
59 * (this will affect HEL sound reliability and latency) */
61 #define DS_SND_QUEUE_MAX 28 /* max number of fragments to prebuffer */
62 #define DS_SND_QUEUE_MIN 12 /* min number of fragments to prebuffer */
64 /* Linux does not support better timing than 10ms */
65 #define DS_TIME_RES 10 /* Resolution of multimedia timer */
66 #define DS_TIME_DEL 10 /* Delay of multimedia timer callback, and duration of HEL fragment */
68 /*****************************************************************************
69 * Predeclare the interface implementation structures
71 typedef struct IDirectSoundImpl IDirectSoundImpl
;
72 typedef struct IDirectSoundBufferImpl IDirectSoundBufferImpl
;
73 typedef struct IDirectSoundNotifyImpl IDirectSoundNotifyImpl
;
74 typedef struct IDirectSound3DListenerImpl IDirectSound3DListenerImpl
;
75 typedef struct IDirectSound3DBufferImpl IDirectSound3DBufferImpl
;
76 typedef struct IDirectSoundCaptureImpl IDirectSoundCaptureImpl
;
77 typedef struct IDirectSoundCaptureBufferImpl IDirectSoundCaptureBufferImpl
;
78 typedef struct IKsPropertySetImpl IKsPropertySetImpl
;
80 /*****************************************************************************
81 * IDirectSound implementation structure
83 struct IDirectSoundImpl
86 ICOM_VFIELD(IDirectSound
);
88 /* IDirectSoundImpl fields */
93 LPWAVEHDR pwave
[DS_HEL_FRAGS
];
94 UINT timerID
, pwplay
, pwwrite
, pwqueue
, prebuf
;
98 IDirectSoundBufferImpl
** buffers
;
99 IDirectSoundBufferImpl
* primary
;
100 IDirectSound3DListenerImpl
* listener
;
101 WAVEFORMATEX wfx
; /* current main waveformat */
102 CRITICAL_SECTION lock
;
105 /*****************************************************************************
106 * IDirectSoundBuffer implementation structure
108 struct IDirectSoundBufferImpl
110 /* FIXME: document */
111 /* IUnknown fields */
112 ICOM_VFIELD(IDirectSoundBuffer
);
114 /* IDirectSoundBufferImpl fields */
115 PIDSDRIVERBUFFER hwbuf
;
118 IDirectSound3DBufferImpl
* ds3db
;
119 DWORD playflags
,state
,leadin
;
120 DWORD playpos
,startpos
,writelead
,buflen
;
121 DWORD nAvgBytesPerSec
;
124 IDirectSoundBufferImpl
* parent
; /* for duplicates */
125 IDirectSoundImpl
* dsound
;
127 LPDSBPOSITIONNOTIFY notifies
;
129 CRITICAL_SECTION lock
;
130 /* used for frequency conversion (PerfectPitch) */
131 ULONG freqAdjust
, freqAcc
;
132 /* used for intelligent (well, sort of) prebuffering */
133 DWORD probably_valid_to
;
134 DWORD primary_mixpos
, buf_mixpos
;
138 #define STATE_STOPPED 0
139 #define STATE_STARTING 1
140 #define STATE_PLAYING 2
141 #define STATE_STOPPING 3
143 /*****************************************************************************
144 * IDirectSoundNotify implementation structure
146 struct IDirectSoundNotifyImpl
148 /* IUnknown fields */
149 ICOM_VFIELD(IDirectSoundNotify
);
151 /* IDirectSoundNotifyImpl fields */
152 IDirectSoundBufferImpl
* dsb
;
155 /*****************************************************************************
156 * IDirectSound3DListener implementation structure
158 struct IDirectSound3DListenerImpl
160 /* IUnknown fields */
161 ICOM_VFIELD(IDirectSound3DListener
);
163 /* IDirectSound3DListenerImpl fields */
164 IDirectSoundBufferImpl
* dsb
;
166 CRITICAL_SECTION lock
;
169 struct IKsPropertySetImpl
171 /* IUnknown fields */
172 ICOM_VFIELD(IKsPropertySet
);
174 /* IKsPropertySetImpl fields */
175 IDirectSound3DBufferImpl
*ds3db
; /* backptr, no ref */
178 /*****************************************************************************
179 * IDirectSound3DBuffer implementation structure
181 struct IDirectSound3DBufferImpl
183 /* IUnknown fields */
184 ICOM_VFIELD(IDirectSound3DBuffer
);
186 /* IDirectSound3DBufferImpl fields */
187 IDirectSoundBufferImpl
* dsb
;
191 CRITICAL_SECTION lock
;
192 IKsPropertySetImpl
* iks
;
196 /*****************************************************************************
197 * IDirectSoundCapture implementation structure
199 struct IDirectSoundCaptureImpl
201 /* IUnknown fields */
202 ICOM_VFIELD(IDirectSoundCapture
);
205 /* IDirectSoundCaptureImpl fields */
206 CRITICAL_SECTION lock
;
209 /*****************************************************************************
210 * IDirectSoundCapture implementation structure
212 struct IDirectSoundCaptureBufferImpl
214 /* IUnknown fields */
215 ICOM_VFIELD(IDirectSoundCaptureBuffer
);
218 /* IDirectSoundCaptureBufferImpl fields */
219 CRITICAL_SECTION lock
;
223 /* #define USE_DSOUND3D 1 */
225 #define DSOUND_FREQSHIFT (14)
227 static IDirectSoundImpl
* dsound
= NULL
;
229 static IDirectSoundBufferImpl
* primarybuf
= NULL
;
231 static void DSOUND_CheckEvent(IDirectSoundBufferImpl
*dsb
, int len
);
232 static void DSOUND_MixCancelAt(IDirectSoundBufferImpl
*dsb
, DWORD buf_writepos
);
234 static void DSOUND_WaveQueue(IDirectSoundImpl
*dsound
, DWORD mixq
);
235 static void DSOUND_PerformMix(void);
236 static void CALLBACK
DSOUND_callback(HWAVEOUT hwo
, UINT msg
, DWORD dwUser
, DWORD dw1
, DWORD dw2
);
238 static HRESULT
DSOUND_CreateDirectSoundCapture( LPVOID
* ppobj
);
239 static HRESULT
DSOUND_CreateDirectSoundCaptureBuffer( LPCDSCBUFFERDESC lpcDSCBufferDesc
, LPVOID
* ppobj
);
241 static ICOM_VTABLE(IDirectSoundCapture
) dscvt
;
242 static ICOM_VTABLE(IDirectSoundCaptureBuffer
) dscbvt
;
244 static HRESULT
mmErr(UINT err
)
247 case MMSYSERR_NOERROR
:
249 case MMSYSERR_ALLOCATED
:
250 return DSERR_ALLOCATED
;
251 case MMSYSERR_INVALHANDLE
:
252 return DSERR_GENERIC
; /* FIXME */
253 case MMSYSERR_NODRIVER
:
254 return DSERR_NODRIVER
;
256 return DSERR_OUTOFMEMORY
;
257 case MMSYSERR_INVALPARAM
:
258 return DSERR_INVALIDPARAM
;
260 FIXME("Unknown MMSYS error %d\n",err
);
261 return DSERR_GENERIC
;
265 /***************************************************************************
266 * DirectSoundEnumerateA [DSOUND.2]
268 * Enumerate all DirectSound drivers installed in the system
272 * Failure: DSERR_INVALIDPARAM
274 HRESULT WINAPI
DirectSoundEnumerateA(
275 LPDSENUMCALLBACKA lpDSEnumCallback
,
278 TRACE("lpDSEnumCallback = %p, lpContext = %p\n",
279 lpDSEnumCallback
, lpContext
);
282 if (lpDSEnumCallback
!= NULL
)
283 lpDSEnumCallback(NULL
,"WINE DirectSound using Open Sound System",
290 /***************************************************************************
291 * DirectSoundEnumerateW [DSOUND.3]
293 * Enumerate all DirectSound drivers installed in the system
297 * Failure: DSERR_INVALIDPARAM
299 HRESULT WINAPI
DirectSoundEnumerateW(
300 LPDSENUMCALLBACKW lpDSEnumCallback
,
303 FIXME("lpDSEnumCallback = %p, lpContext = %p: stub\n",
304 lpDSEnumCallback
, lpContext
);
310 static void _dump_DSBCAPS(DWORD xmask
) {
315 #define FE(x) { x, #x },
316 FE(DSBCAPS_PRIMARYBUFFER
)
318 FE(DSBCAPS_LOCHARDWARE
)
319 FE(DSBCAPS_LOCSOFTWARE
)
321 FE(DSBCAPS_CTRLFREQUENCY
)
323 FE(DSBCAPS_CTRLVOLUME
)
324 FE(DSBCAPS_CTRLPOSITIONNOTIFY
)
325 FE(DSBCAPS_CTRLDEFAULT
)
327 FE(DSBCAPS_STICKYFOCUS
)
328 FE(DSBCAPS_GLOBALFOCUS
)
329 FE(DSBCAPS_GETCURRENTPOSITION2
)
330 FE(DSBCAPS_MUTE3DATMAXDISTANCE
)
335 for (i
=0;i
<sizeof(flags
)/sizeof(flags
[0]);i
++)
336 if ((flags
[i
].mask
& xmask
) == flags
[i
].mask
)
337 DPRINTF("%s ",flags
[i
].name
);
340 /*******************************************************************************
344 /* IUnknown methods */
346 static HRESULT WINAPI
IKsPropertySetImpl_QueryInterface(
347 LPKSPROPERTYSET iface
, REFIID riid
, LPVOID
*ppobj
349 ICOM_THIS(IKsPropertySetImpl
,iface
);
351 FIXME("(%p,%s,%p), stub!\n",This
,debugstr_guid(riid
),ppobj
);
357 static ULONG WINAPI
IKsPropertySetImpl_AddRef(LPKSPROPERTYSET iface
) {
358 ICOM_THIS(IKsPropertySetImpl
,iface
);
366 static ULONG WINAPI
IKsPropertySetImpl_Release(LPKSPROPERTYSET iface
) {
367 ICOM_THIS(IKsPropertySetImpl
,iface
);
375 static HRESULT WINAPI
IKsPropertySetImpl_Get(LPKSPROPERTYSET iface
,
376 REFGUID guidPropSet
, ULONG dwPropID
,
377 LPVOID pInstanceData
, ULONG cbInstanceData
,
378 LPVOID pPropData
, ULONG cbPropData
,
381 ICOM_THIS(IKsPropertySetImpl
,iface
);
383 FIXME("(%p,%s,%ld,%p,%ld,%p,%ld,%p), stub!\n",This
,debugstr_guid(guidPropSet
),dwPropID
,pInstanceData
,cbInstanceData
,pPropData
,cbPropData
,pcbReturned
);
384 return E_PROP_ID_UNSUPPORTED
;
389 static HRESULT WINAPI
IKsPropertySetImpl_Set(LPKSPROPERTYSET iface
,
390 REFGUID guidPropSet
, ULONG dwPropID
,
391 LPVOID pInstanceData
, ULONG cbInstanceData
,
392 LPVOID pPropData
, ULONG cbPropData
394 ICOM_THIS(IKsPropertySetImpl
,iface
);
396 FIXME("(%p,%s,%ld,%p,%ld,%p,%ld), stub!\n",This
,debugstr_guid(guidPropSet
),dwPropID
,pInstanceData
,cbInstanceData
,pPropData
,cbPropData
);
397 return E_PROP_ID_UNSUPPORTED
;
402 static HRESULT WINAPI
IKsPropertySetImpl_QuerySupport(LPKSPROPERTYSET iface
,
403 REFGUID guidPropSet
, ULONG dwPropID
, PULONG pTypeSupport
405 ICOM_THIS(IKsPropertySetImpl
,iface
);
407 FIXME("(%p,%s,%ld,%p), stub!\n",This
,debugstr_guid(guidPropSet
),dwPropID
,pTypeSupport
);
408 return E_PROP_ID_UNSUPPORTED
;
413 static ICOM_VTABLE(IKsPropertySet
) iksvt
= {
414 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
415 IKsPropertySetImpl_QueryInterface
,
416 IKsPropertySetImpl_AddRef
,
417 IKsPropertySetImpl_Release
,
418 IKsPropertySetImpl_Get
,
419 IKsPropertySetImpl_Set
,
420 IKsPropertySetImpl_QuerySupport
424 /*******************************************************************************
425 * IDirectSound3DBuffer
428 /* IUnknown methods */
430 static HRESULT WINAPI
IDirectSound3DBufferImpl_QueryInterface(
431 LPDIRECTSOUND3DBUFFER iface
, REFIID riid
, LPVOID
*ppobj
)
433 ICOM_THIS(IDirectSound3DBufferImpl
,iface
);
435 if ( IsEqualGUID( &IID_IKsPropertySet
, riid
) ) {
436 IDirectSound3DBuffer_AddRef(iface
);
441 FIXME("(%p,%s,%p), no such interface.\n",This
,debugstr_guid(riid
),ppobj
);
447 static ULONG WINAPI
IDirectSound3DBufferImpl_AddRef(LPDIRECTSOUND3DBUFFER iface
)
449 ICOM_THIS(IDirectSound3DBufferImpl
,iface
);
456 static ULONG WINAPI
IDirectSound3DBufferImpl_Release(LPDIRECTSOUND3DBUFFER iface
)
458 ICOM_THIS(IDirectSound3DBufferImpl
,iface
);
460 TRACE("(%p) ref was %ld\n", This
, This
->ref
);
466 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER
)This
->dsb
);
468 DeleteCriticalSection(&This
->lock
);
470 HeapFree(GetProcessHeap(),0,This
->buffer
);
471 HeapFree(GetProcessHeap(),0,This
);
477 /* IDirectSound3DBuffer methods */
479 static HRESULT WINAPI
IDirectSound3DBufferImpl_GetAllParameters(
480 LPDIRECTSOUND3DBUFFER iface
,
481 LPDS3DBUFFER lpDs3dBuffer
)
489 static HRESULT WINAPI
IDirectSound3DBufferImpl_GetConeAngles(
490 LPDIRECTSOUND3DBUFFER iface
,
491 LPDWORD lpdwInsideConeAngle
,
492 LPDWORD lpdwOutsideConeAngle
)
500 static HRESULT WINAPI
IDirectSound3DBufferImpl_GetConeOrientation(
501 LPDIRECTSOUND3DBUFFER iface
,
502 LPD3DVECTOR lpvConeOrientation
)
510 static HRESULT WINAPI
IDirectSound3DBufferImpl_GetConeOutsideVolume(
511 LPDIRECTSOUND3DBUFFER iface
,
512 LPLONG lplConeOutsideVolume
)
520 static HRESULT WINAPI
IDirectSound3DBufferImpl_GetMaxDistance(
521 LPDIRECTSOUND3DBUFFER iface
,
522 LPD3DVALUE lpfMaxDistance
)
530 static HRESULT WINAPI
IDirectSound3DBufferImpl_GetMinDistance(
531 LPDIRECTSOUND3DBUFFER iface
,
532 LPD3DVALUE lpfMinDistance
)
540 static HRESULT WINAPI
IDirectSound3DBufferImpl_GetMode(
541 LPDIRECTSOUND3DBUFFER iface
,
550 static HRESULT WINAPI
IDirectSound3DBufferImpl_GetPosition(
551 LPDIRECTSOUND3DBUFFER iface
,
552 LPD3DVECTOR lpvPosition
)
560 static HRESULT WINAPI
IDirectSound3DBufferImpl_GetVelocity(
561 LPDIRECTSOUND3DBUFFER iface
,
562 LPD3DVECTOR lpvVelocity
)
570 static HRESULT WINAPI
IDirectSound3DBufferImpl_SetAllParameters(
571 LPDIRECTSOUND3DBUFFER iface
,
572 LPCDS3DBUFFER lpcDs3dBuffer
,
581 static HRESULT WINAPI
IDirectSound3DBufferImpl_SetConeAngles(
582 LPDIRECTSOUND3DBUFFER iface
,
583 DWORD dwInsideConeAngle
,
584 DWORD dwOutsideConeAngle
,
593 static HRESULT WINAPI
IDirectSound3DBufferImpl_SetConeOrientation(
594 LPDIRECTSOUND3DBUFFER iface
,
595 D3DVALUE x
, D3DVALUE y
, D3DVALUE z
,
604 static HRESULT WINAPI
IDirectSound3DBufferImpl_SetConeOutsideVolume(
605 LPDIRECTSOUND3DBUFFER iface
,
606 LONG lConeOutsideVolume
,
615 static HRESULT WINAPI
IDirectSound3DBufferImpl_SetMaxDistance(
616 LPDIRECTSOUND3DBUFFER iface
,
617 D3DVALUE fMaxDistance
,
626 static HRESULT WINAPI
IDirectSound3DBufferImpl_SetMinDistance(
627 LPDIRECTSOUND3DBUFFER iface
,
628 D3DVALUE fMinDistance
,
637 static HRESULT WINAPI
IDirectSound3DBufferImpl_SetMode(
638 LPDIRECTSOUND3DBUFFER iface
,
642 ICOM_THIS(IDirectSound3DBufferImpl
,iface
);
643 TRACE("mode = %lx\n", dwMode
);
644 This
->ds3db
.dwMode
= dwMode
;
650 static HRESULT WINAPI
IDirectSound3DBufferImpl_SetPosition(
651 LPDIRECTSOUND3DBUFFER iface
,
652 D3DVALUE x
, D3DVALUE y
, D3DVALUE z
,
661 static HRESULT WINAPI
IDirectSound3DBufferImpl_SetVelocity(
662 LPDIRECTSOUND3DBUFFER iface
,
663 D3DVALUE x
, D3DVALUE y
, D3DVALUE z
,
672 static ICOM_VTABLE(IDirectSound3DBuffer
) ds3dbvt
=
674 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
675 /* IUnknown methods */
676 IDirectSound3DBufferImpl_QueryInterface
,
677 IDirectSound3DBufferImpl_AddRef
,
678 IDirectSound3DBufferImpl_Release
,
679 /* IDirectSound3DBuffer methods */
680 IDirectSound3DBufferImpl_GetAllParameters
,
681 IDirectSound3DBufferImpl_GetConeAngles
,
682 IDirectSound3DBufferImpl_GetConeOrientation
,
683 IDirectSound3DBufferImpl_GetConeOutsideVolume
,
684 IDirectSound3DBufferImpl_GetMaxDistance
,
685 IDirectSound3DBufferImpl_GetMinDistance
,
686 IDirectSound3DBufferImpl_GetMode
,
687 IDirectSound3DBufferImpl_GetPosition
,
688 IDirectSound3DBufferImpl_GetVelocity
,
689 IDirectSound3DBufferImpl_SetAllParameters
,
690 IDirectSound3DBufferImpl_SetConeAngles
,
691 IDirectSound3DBufferImpl_SetConeOrientation
,
692 IDirectSound3DBufferImpl_SetConeOutsideVolume
,
693 IDirectSound3DBufferImpl_SetMaxDistance
,
694 IDirectSound3DBufferImpl_SetMinDistance
,
695 IDirectSound3DBufferImpl_SetMode
,
696 IDirectSound3DBufferImpl_SetPosition
,
697 IDirectSound3DBufferImpl_SetVelocity
,
702 static int DSOUND_Create3DBuffer(IDirectSoundBufferImpl
* dsb
)
704 DWORD i
, temp
, iSize
, oSize
, offset
;
705 LPBYTE bIbuf
, bObuf
, bTbuf
= NULL
;
706 LPWORD wIbuf
, wObuf
, wTbuf
= NULL
;
708 /* Inside DirectX says it's stupid but allowed */
709 if (dsb
->wfx
.nChannels
== 2) {
710 /* Convert to mono */
711 if (dsb
->wfx
.wBitsPerSample
== 16) {
712 iSize
= dsb
->buflen
/ 4;
713 wTbuf
= malloc(dsb
->buflen
/ 2);
715 return DSERR_OUTOFMEMORY
;
716 for (i
= 0; i
< iSize
; i
++)
717 wTbuf
[i
] = (dsb
->buffer
[i
* 2] + dsb
->buffer
[(i
* 2) + 1]) / 2;
720 iSize
= dsb
->buflen
/ 2;
721 bTbuf
= malloc(dsb
->buflen
/ 2);
723 return DSERR_OUTOFMEMORY
;
724 for (i
= 0; i
< iSize
; i
++)
725 bTbuf
[i
] = (dsb
->buffer
[i
* 2] + dsb
->buffer
[(i
* 2) + 1]) / 2;
729 if (dsb
->wfx
.wBitsPerSample
== 16) {
730 iSize
= dsb
->buflen
/ 2;
731 wIbuf
= (LPWORD
) dsb
->buffer
;
733 bIbuf
= (LPBYTE
) dsb
->buffer
;
738 if (primarybuf
->wfx
.wBitsPerSample
== 16) {
739 wObuf
= (LPWORD
) dsb
->ds3db
->buffer
;
740 oSize
= dsb
->ds3db
->buflen
/ 2;
742 bObuf
= (LPBYTE
) dsb
->ds3db
->buffer
;
743 oSize
= dsb
->ds3db
->buflen
;
746 offset
= primarybuf
->wfx
.nSamplesPerSec
/ 100; /* 10ms */
747 if (primarybuf
->wfx
.wBitsPerSample
== 16 && dsb
->wfx
.wBitsPerSample
== 16)
748 for (i
= 0; i
< iSize
; i
++) {
751 temp
+= wIbuf
[i
- offset
] >> 9;
753 temp
+= wIbuf
[i
+ iSize
- offset
] >> 9;
755 wObuf
[(i
* 2) + 1] = temp
;
757 else if (primarybuf
->wfx
.wBitsPerSample
== 8 && dsb
->wfx
.wBitsPerSample
== 8)
758 for (i
= 0; i
< iSize
; i
++) {
761 temp
+= bIbuf
[i
- offset
] >> 5;
763 temp
+= bIbuf
[i
+ iSize
- offset
] >> 5;
765 bObuf
[(i
* 2) + 1] = temp
;
776 /*******************************************************************************
777 * IDirectSound3DListener
780 /* IUnknown methods */
781 static HRESULT WINAPI
IDirectSound3DListenerImpl_QueryInterface(
782 LPDIRECTSOUND3DLISTENER iface
, REFIID riid
, LPVOID
*ppobj
)
784 ICOM_THIS(IDirectSound3DListenerImpl
,iface
);
786 TRACE("(%p,%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
790 static ULONG WINAPI
IDirectSound3DListenerImpl_AddRef(LPDIRECTSOUND3DLISTENER iface
)
792 ICOM_THIS(IDirectSound3DListenerImpl
,iface
);
797 static ULONG WINAPI
IDirectSound3DListenerImpl_Release(LPDIRECTSOUND3DLISTENER iface
)
800 ICOM_THIS(IDirectSound3DListenerImpl
,iface
);
802 TRACE("(%p) ref was %ld\n", This
, This
->ref
);
804 ulReturn
= --This
->ref
;
806 /* Free all resources */
807 if( ulReturn
== 0 ) {
809 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER
)This
->dsb
);
810 DeleteCriticalSection(&This
->lock
);
811 HeapFree(GetProcessHeap(),0,This
);
817 /* IDirectSound3DListener methods */
818 static HRESULT WINAPI
IDirectSound3DListenerImpl_GetAllParameter(
819 LPDIRECTSOUND3DLISTENER iface
,
820 LPDS3DLISTENER lpDS3DL
)
826 static HRESULT WINAPI
IDirectSound3DListenerImpl_GetDistanceFactor(
827 LPDIRECTSOUND3DLISTENER iface
,
828 LPD3DVALUE lpfDistanceFactor
)
834 static HRESULT WINAPI
IDirectSound3DListenerImpl_GetDopplerFactor(
835 LPDIRECTSOUND3DLISTENER iface
,
836 LPD3DVALUE lpfDopplerFactor
)
842 static HRESULT WINAPI
IDirectSound3DListenerImpl_GetOrientation(
843 LPDIRECTSOUND3DLISTENER iface
,
844 LPD3DVECTOR lpvOrientFront
,
845 LPD3DVECTOR lpvOrientTop
)
851 static HRESULT WINAPI
IDirectSound3DListenerImpl_GetPosition(
852 LPDIRECTSOUND3DLISTENER iface
,
853 LPD3DVECTOR lpvPosition
)
859 static HRESULT WINAPI
IDirectSound3DListenerImpl_GetRolloffFactor(
860 LPDIRECTSOUND3DLISTENER iface
,
861 LPD3DVALUE lpfRolloffFactor
)
867 static HRESULT WINAPI
IDirectSound3DListenerImpl_GetVelocity(
868 LPDIRECTSOUND3DLISTENER iface
,
869 LPD3DVECTOR lpvVelocity
)
875 static HRESULT WINAPI
IDirectSound3DListenerImpl_SetAllParameters(
876 LPDIRECTSOUND3DLISTENER iface
,
877 LPCDS3DLISTENER lpcDS3DL
,
884 static HRESULT WINAPI
IDirectSound3DListenerImpl_SetDistanceFactor(
885 LPDIRECTSOUND3DLISTENER iface
,
886 D3DVALUE fDistanceFactor
,
893 static HRESULT WINAPI
IDirectSound3DListenerImpl_SetDopplerFactor(
894 LPDIRECTSOUND3DLISTENER iface
,
895 D3DVALUE fDopplerFactor
,
902 static HRESULT WINAPI
IDirectSound3DListenerImpl_SetOrientation(
903 LPDIRECTSOUND3DLISTENER iface
,
904 D3DVALUE xFront
, D3DVALUE yFront
, D3DVALUE zFront
,
905 D3DVALUE xTop
, D3DVALUE yTop
, D3DVALUE zTop
,
912 static HRESULT WINAPI
IDirectSound3DListenerImpl_SetPosition(
913 LPDIRECTSOUND3DLISTENER iface
,
914 D3DVALUE x
, D3DVALUE y
, D3DVALUE z
,
921 static HRESULT WINAPI
IDirectSound3DListenerImpl_SetRolloffFactor(
922 LPDIRECTSOUND3DLISTENER iface
,
923 D3DVALUE fRolloffFactor
,
930 static HRESULT WINAPI
IDirectSound3DListenerImpl_SetVelocity(
931 LPDIRECTSOUND3DLISTENER iface
,
932 D3DVALUE x
, D3DVALUE y
, D3DVALUE z
,
939 static HRESULT WINAPI
IDirectSound3DListenerImpl_CommitDeferredSettings(
940 LPDIRECTSOUND3DLISTENER iface
)
947 static ICOM_VTABLE(IDirectSound3DListener
) ds3dlvt
=
949 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
950 /* IUnknown methods */
951 IDirectSound3DListenerImpl_QueryInterface
,
952 IDirectSound3DListenerImpl_AddRef
,
953 IDirectSound3DListenerImpl_Release
,
954 /* IDirectSound3DListener methods */
955 IDirectSound3DListenerImpl_GetAllParameter
,
956 IDirectSound3DListenerImpl_GetDistanceFactor
,
957 IDirectSound3DListenerImpl_GetDopplerFactor
,
958 IDirectSound3DListenerImpl_GetOrientation
,
959 IDirectSound3DListenerImpl_GetPosition
,
960 IDirectSound3DListenerImpl_GetRolloffFactor
,
961 IDirectSound3DListenerImpl_GetVelocity
,
962 IDirectSound3DListenerImpl_SetAllParameters
,
963 IDirectSound3DListenerImpl_SetDistanceFactor
,
964 IDirectSound3DListenerImpl_SetDopplerFactor
,
965 IDirectSound3DListenerImpl_SetOrientation
,
966 IDirectSound3DListenerImpl_SetPosition
,
967 IDirectSound3DListenerImpl_SetRolloffFactor
,
968 IDirectSound3DListenerImpl_SetVelocity
,
969 IDirectSound3DListenerImpl_CommitDeferredSettings
,
972 /*******************************************************************************
975 static HRESULT WINAPI
IDirectSoundNotifyImpl_QueryInterface(
976 LPDIRECTSOUNDNOTIFY iface
,REFIID riid
,LPVOID
*ppobj
978 ICOM_THIS(IDirectSoundNotifyImpl
,iface
);
980 TRACE("(%p,%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
984 static ULONG WINAPI
IDirectSoundNotifyImpl_AddRef(LPDIRECTSOUNDNOTIFY iface
) {
985 ICOM_THIS(IDirectSoundNotifyImpl
,iface
);
986 return ++(This
->ref
);
989 static ULONG WINAPI
IDirectSoundNotifyImpl_Release(LPDIRECTSOUNDNOTIFY iface
) {
990 ICOM_THIS(IDirectSoundNotifyImpl
,iface
);
992 TRACE("(%p) ref was %ld\n", This
, This
->ref
);
996 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER
)This
->dsb
);
997 HeapFree(GetProcessHeap(),0,This
);
1003 static HRESULT WINAPI
IDirectSoundNotifyImpl_SetNotificationPositions(
1004 LPDIRECTSOUNDNOTIFY iface
,DWORD howmuch
,LPCDSBPOSITIONNOTIFY notify
1006 ICOM_THIS(IDirectSoundNotifyImpl
,iface
);
1009 if (TRACE_ON(dsound
)) {
1010 TRACE("(%p,0x%08lx,%p)\n",This
,howmuch
,notify
);
1011 for (i
=0;i
<howmuch
;i
++)
1012 TRACE("notify at %ld to 0x%08lx\n",
1013 notify
[i
].dwOffset
,(DWORD
)notify
[i
].hEventNotify
);
1015 This
->dsb
->notifies
= HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,This
->dsb
->notifies
,(This
->dsb
->nrofnotifies
+howmuch
)*sizeof(DSBPOSITIONNOTIFY
));
1016 memcpy( This
->dsb
->notifies
+This
->dsb
->nrofnotifies
,
1018 howmuch
*sizeof(DSBPOSITIONNOTIFY
)
1020 This
->dsb
->nrofnotifies
+=howmuch
;
1025 static ICOM_VTABLE(IDirectSoundNotify
) dsnvt
=
1027 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1028 IDirectSoundNotifyImpl_QueryInterface
,
1029 IDirectSoundNotifyImpl_AddRef
,
1030 IDirectSoundNotifyImpl_Release
,
1031 IDirectSoundNotifyImpl_SetNotificationPositions
,
1034 /*******************************************************************************
1035 * IDirectSoundBuffer
1038 static void DSOUND_RecalcVolPan(PDSVOLUMEPAN volpan
)
1042 /* the AmpFactors are expressed in 16.16 fixed point */
1043 volpan
->dwVolAmpFactor
= (ULONG
) (pow(2.0, volpan
->lVolume
/ 600.0) * 65536);
1044 /* FIXME: dwPan{Left|Right}AmpFactor */
1046 /* FIXME: use calculated vol and pan ampfactors */
1047 temp
= (double) (volpan
->lVolume
- (volpan
->lPan
> 0 ? volpan
->lPan
: 0));
1048 volpan
->dwTotalLeftAmpFactor
= (ULONG
) (pow(2.0, temp
/ 600.0) * 65536);
1049 temp
= (double) (volpan
->lVolume
+ (volpan
->lPan
< 0 ? volpan
->lPan
: 0));
1050 volpan
->dwTotalRightAmpFactor
= (ULONG
) (pow(2.0, temp
/ 600.0) * 65536);
1052 TRACE("left = %lx, right = %lx\n", volpan
->dwTotalLeftAmpFactor
, volpan
->dwTotalRightAmpFactor
);
1055 static void DSOUND_RecalcFormat(IDirectSoundBufferImpl
*dsb
)
1059 sw
= dsb
->wfx
.nChannels
* (dsb
->wfx
.wBitsPerSample
/ 8);
1060 if ((dsb
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
) && dsb
->hwbuf
) {
1062 /* let fragment size approximate the timer delay */
1063 fraglen
= (dsb
->freq
* DS_TIME_DEL
/ 1000) * sw
;
1064 /* reduce fragment size until an integer number of them fits in the buffer */
1065 /* (FIXME: this may or may not be a good idea) */
1066 while (dsb
->buflen
% fraglen
) fraglen
-= sw
;
1067 dsb
->dsound
->fraglen
= fraglen
;
1068 TRACE("fraglen=%ld\n", dsb
->dsound
->fraglen
);
1070 /* calculate the 10ms write lead */
1071 dsb
->writelead
= (dsb
->freq
/ 100) * sw
;
1074 static HRESULT
DSOUND_PrimaryOpen(IDirectSoundBufferImpl
*dsb
)
1076 HRESULT err
= DS_OK
;
1078 /* are we using waveOut stuff? */
1082 HRESULT merr
= DS_OK
;
1083 /* Start in pause mode, to allow buffers to get filled */
1084 waveOutPause(dsb
->dsound
->hwo
);
1085 if (dsb
->state
== STATE_PLAYING
) dsb
->state
= STATE_STARTING
;
1086 else if (dsb
->state
== STATE_STOPPING
) dsb
->state
= STATE_STOPPED
;
1087 /* use fragments of 10ms (1/100s) each (which should get us within
1088 * the documented write cursor lead of 10-15ms) */
1089 buflen
= ((dsb
->wfx
.nAvgBytesPerSec
/ 100) & ~3) * DS_HEL_FRAGS
;
1090 TRACE("desired buflen=%ld, old buffer=%p\n", buflen
, dsb
->buffer
);
1091 /* reallocate emulated primary buffer */
1092 newbuf
= (LPBYTE
)HeapReAlloc(GetProcessHeap(),0,dsb
->buffer
,buflen
);
1093 if (newbuf
== NULL
) {
1094 ERR("failed to allocate primary buffer\n");
1095 merr
= DSERR_OUTOFMEMORY
;
1096 /* but the old buffer might still exists and must be re-prepared */
1098 dsb
->buffer
= newbuf
;
1099 dsb
->buflen
= buflen
;
1103 IDirectSoundImpl
*ds
= dsb
->dsound
;
1105 ds
->fraglen
= dsb
->buflen
/ DS_HEL_FRAGS
;
1107 /* prepare fragment headers */
1108 for (c
=0; c
<DS_HEL_FRAGS
; c
++) {
1109 ds
->pwave
[c
]->lpData
= dsb
->buffer
+ c
*ds
->fraglen
;
1110 ds
->pwave
[c
]->dwBufferLength
= ds
->fraglen
;
1111 ds
->pwave
[c
]->dwUser
= (DWORD
)dsb
;
1112 ds
->pwave
[c
]->dwFlags
= 0;
1113 ds
->pwave
[c
]->dwLoops
= 0;
1114 err
= mmErr(waveOutPrepareHeader(ds
->hwo
,ds
->pwave
[c
],sizeof(WAVEHDR
)));
1117 waveOutUnprepareHeader(ds
->hwo
,ds
->pwave
[c
],sizeof(WAVEHDR
));
1125 memset(dsb
->buffer
, (dsb
->wfx
.wBitsPerSample
== 16) ? 0 : 128, dsb
->buflen
);
1126 TRACE("fraglen=%ld\n", ds
->fraglen
);
1127 DSOUND_WaveQueue(dsb
->dsound
, (DWORD
)-1);
1129 if ((err
== DS_OK
) && (merr
!= DS_OK
))
1136 static void DSOUND_PrimaryClose(IDirectSoundBufferImpl
*dsb
)
1138 /* are we using waveOut stuff? */
1141 IDirectSoundImpl
*ds
= dsb
->dsound
;
1143 ds
->pwqueue
= (DWORD
)-1; /* resetting queues */
1144 waveOutReset(ds
->hwo
);
1145 for (c
=0; c
<DS_HEL_FRAGS
; c
++)
1146 waveOutUnprepareHeader(ds
->hwo
, ds
->pwave
[c
], sizeof(WAVEHDR
));
1151 static HRESULT
DSOUND_PrimaryPlay(IDirectSoundBufferImpl
*dsb
)
1153 HRESULT err
= DS_OK
;
1155 err
= IDsDriverBuffer_Play(dsb
->hwbuf
, 0, 0, DSBPLAY_LOOPING
);
1157 err
= mmErr(waveOutRestart(dsb
->dsound
->hwo
));
1161 static HRESULT
DSOUND_PrimaryStop(IDirectSoundBufferImpl
*dsb
)
1163 HRESULT err
= DS_OK
;
1165 err
= IDsDriverBuffer_Stop(dsb
->hwbuf
);
1166 if (err
== DSERR_BUFFERLOST
) {
1167 /* Wine-only: the driver wants us to reopen the device */
1168 /* FIXME: check for errors */
1169 IDsDriverBuffer_Release(primarybuf
->hwbuf
);
1170 waveOutClose(dsb
->dsound
->hwo
);
1171 dsb
->dsound
->hwo
= 0;
1172 waveOutOpen(&(dsb
->dsound
->hwo
), dsb
->dsound
->drvdesc
.dnDevNode
,
1173 &(primarybuf
->wfx
), (DWORD
)DSOUND_callback
, (DWORD
)dsb
->dsound
,
1174 CALLBACK_FUNCTION
| WAVE_DIRECTSOUND
);
1175 err
= IDsDriver_CreateSoundBuffer(dsb
->dsound
->driver
,&(dsb
->wfx
),dsb
->dsbd
.dwFlags
,0,
1176 &(dsb
->buflen
),&(dsb
->buffer
),
1177 (LPVOID
)&(dsb
->hwbuf
));
1181 err
= mmErr(waveOutPause(dsb
->dsound
->hwo
));
1185 /* This sets this format for the <em>Primary Buffer Only</em> */
1186 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
1187 static HRESULT WINAPI
IDirectSoundBufferImpl_SetFormat(
1188 LPDIRECTSOUNDBUFFER iface
,LPWAVEFORMATEX wfex
1190 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1191 IDirectSoundBufferImpl
** dsb
;
1192 HRESULT err
= DS_OK
;
1195 /* Let's be pedantic! */
1196 if ((wfex
== NULL
) ||
1197 (wfex
->wFormatTag
!= WAVE_FORMAT_PCM
) ||
1198 (wfex
->nChannels
< 1) || (wfex
->nChannels
> 2) ||
1199 (wfex
->nSamplesPerSec
< 1) ||
1200 (wfex
->nBlockAlign
< 1) || (wfex
->nChannels
> 4) ||
1201 ((wfex
->wBitsPerSample
!= 8) && (wfex
->wBitsPerSample
!= 16))) {
1202 TRACE("failed pedantic check!\n");
1203 return DSERR_INVALIDPARAM
;
1207 EnterCriticalSection(&(This
->dsound
->lock
));
1209 if (primarybuf
->wfx
.nSamplesPerSec
!= wfex
->nSamplesPerSec
) {
1210 dsb
= dsound
->buffers
;
1211 for (i
= 0; i
< dsound
->nrofbuffers
; i
++, dsb
++) {
1213 EnterCriticalSection(&((*dsb
)->lock
));
1215 (*dsb
)->freqAdjust
= ((*dsb
)->freq
<< DSOUND_FREQSHIFT
) /
1216 wfex
->nSamplesPerSec
;
1218 LeaveCriticalSection(&((*dsb
)->lock
));
1223 memcpy(&(primarybuf
->wfx
), wfex
, sizeof(primarybuf
->wfx
));
1225 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
1226 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1227 wfex
->wFormatTag
, wfex
->nChannels
, wfex
->nSamplesPerSec
,
1228 wfex
->nAvgBytesPerSec
, wfex
->nBlockAlign
,
1229 wfex
->wBitsPerSample
, wfex
->cbSize
);
1231 primarybuf
->wfx
.nAvgBytesPerSec
=
1232 This
->wfx
.nSamplesPerSec
* This
->wfx
.nBlockAlign
;
1234 if (primarybuf
->dsound
->drvdesc
.dwFlags
& DSDDESC_DOMMSYSTEMSETFORMAT
) {
1235 /* FIXME: check for errors */
1236 DSOUND_PrimaryClose(primarybuf
);
1237 waveOutClose(This
->dsound
->hwo
);
1238 This
->dsound
->hwo
= 0;
1239 waveOutOpen(&(This
->dsound
->hwo
), This
->dsound
->drvdesc
.dnDevNode
,
1240 &(primarybuf
->wfx
), (DWORD
)DSOUND_callback
, (DWORD
)This
->dsound
,
1241 CALLBACK_FUNCTION
| WAVE_DIRECTSOUND
);
1242 DSOUND_PrimaryOpen(primarybuf
);
1244 if (primarybuf
->hwbuf
) {
1245 err
= IDsDriverBuffer_SetFormat(primarybuf
->hwbuf
, &(primarybuf
->wfx
));
1246 if (err
== DSERR_BUFFERLOST
) {
1247 /* Wine-only: the driver wants us to recreate the HW buffer */
1248 IDsDriverBuffer_Release(primarybuf
->hwbuf
);
1249 err
= IDsDriver_CreateSoundBuffer(primarybuf
->dsound
->driver
,&(primarybuf
->wfx
),primarybuf
->dsbd
.dwFlags
,0,
1250 &(primarybuf
->buflen
),&(primarybuf
->buffer
),
1251 (LPVOID
)&(primarybuf
->hwbuf
));
1252 if (primarybuf
->state
== STATE_PLAYING
) primarybuf
->state
= STATE_STARTING
;
1253 else if (primarybuf
->state
== STATE_STOPPING
) primarybuf
->state
= STATE_STOPPED
;
1256 DSOUND_RecalcFormat(primarybuf
);
1258 LeaveCriticalSection(&(This
->dsound
->lock
));
1264 static HRESULT WINAPI
IDirectSoundBufferImpl_SetVolume(
1265 LPDIRECTSOUNDBUFFER iface
,LONG vol
1267 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1269 TRACE("(%p,%ld)\n",This
,vol
);
1271 /* I'm not sure if we need this for primary buffer */
1272 if (!(This
->dsbd
.dwFlags
& DSBCAPS_CTRLVOLUME
))
1273 return DSERR_CONTROLUNAVAIL
;
1275 if ((vol
> DSBVOLUME_MAX
) || (vol
< DSBVOLUME_MIN
))
1276 return DSERR_INVALIDPARAM
;
1279 EnterCriticalSection(&(This
->lock
));
1281 This
->volpan
.lVolume
= vol
;
1283 DSOUND_RecalcVolPan(&(This
->volpan
));
1286 IDsDriverBuffer_SetVolumePan(This
->hwbuf
, &(This
->volpan
));
1288 else if (This
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
) {
1289 #if 0 /* should we really do this? */
1290 /* the DS volume ranges from 0 (max, 0dB attenuation) to -10000 (min, 100dB attenuation) */
1291 /* the MM volume ranges from 0 to 0xffff in an unspecified logarithmic scale */
1292 WORD cvol
= 0xffff + vol
*6 + vol
/2;
1293 DWORD vol
= cvol
| ((DWORD
)cvol
<< 16)
1294 waveOutSetVolume(This
->dsound
->hwo
, vol
);
1298 LeaveCriticalSection(&(This
->lock
));
1304 static HRESULT WINAPI
IDirectSoundBufferImpl_GetVolume(
1305 LPDIRECTSOUNDBUFFER iface
,LPLONG vol
1307 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1308 TRACE("(%p,%p)\n",This
,vol
);
1311 return DSERR_INVALIDPARAM
;
1313 *vol
= This
->volpan
.lVolume
;
1317 static HRESULT WINAPI
IDirectSoundBufferImpl_SetFrequency(
1318 LPDIRECTSOUNDBUFFER iface
,DWORD freq
1320 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1321 TRACE("(%p,%ld)\n",This
,freq
);
1323 /* You cannot set the frequency of the primary buffer */
1324 if (!(This
->dsbd
.dwFlags
& DSBCAPS_CTRLFREQUENCY
) ||
1325 (This
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
))
1326 return DSERR_CONTROLUNAVAIL
;
1328 if (!freq
) freq
= This
->wfx
.nSamplesPerSec
;
1330 if ((freq
< DSBFREQUENCY_MIN
) || (freq
> DSBFREQUENCY_MAX
))
1331 return DSERR_INVALIDPARAM
;
1334 EnterCriticalSection(&(This
->lock
));
1337 This
->freqAdjust
= (freq
<< DSOUND_FREQSHIFT
) / primarybuf
->wfx
.nSamplesPerSec
;
1338 This
->nAvgBytesPerSec
= freq
* This
->wfx
.nBlockAlign
;
1339 DSOUND_RecalcFormat(This
);
1341 LeaveCriticalSection(&(This
->lock
));
1347 static HRESULT WINAPI
IDirectSoundBufferImpl_Play(
1348 LPDIRECTSOUNDBUFFER iface
,DWORD reserved1
,DWORD reserved2
,DWORD flags
1350 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1351 TRACE("(%p,%08lx,%08lx,%08lx)\n",
1352 This
,reserved1
,reserved2
,flags
1356 EnterCriticalSection(&(This
->lock
));
1358 This
->playflags
= flags
;
1359 if (This
->state
== STATE_STOPPED
) {
1360 This
->leadin
= TRUE
;
1361 This
->startpos
= This
->buf_mixpos
;
1362 This
->state
= STATE_STARTING
;
1363 } else if (This
->state
== STATE_STOPPING
)
1364 This
->state
= STATE_PLAYING
;
1365 if (!(This
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
) && This
->hwbuf
) {
1366 IDsDriverBuffer_Play(This
->hwbuf
, 0, 0, This
->playflags
);
1367 This
->state
= STATE_PLAYING
;
1370 LeaveCriticalSection(&(This
->lock
));
1376 static HRESULT WINAPI
IDirectSoundBufferImpl_Stop(LPDIRECTSOUNDBUFFER iface
)
1378 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1379 TRACE("(%p)\n",This
);
1382 EnterCriticalSection(&(This
->lock
));
1384 if (This
->state
== STATE_PLAYING
)
1385 This
->state
= STATE_STOPPING
;
1386 else if (This
->state
== STATE_STARTING
)
1387 This
->state
= STATE_STOPPED
;
1388 if (!(This
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
) && This
->hwbuf
) {
1389 IDsDriverBuffer_Stop(This
->hwbuf
);
1390 This
->state
= STATE_STOPPED
;
1392 DSOUND_CheckEvent(This
, 0);
1394 LeaveCriticalSection(&(This
->lock
));
1400 static DWORD WINAPI
IDirectSoundBufferImpl_AddRef(LPDIRECTSOUNDBUFFER iface
) {
1401 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1404 TRACE("(%p) ref was %ld, thread is %lx\n",This
, This
->ref
, GetCurrentThreadId());
1406 ref
= InterlockedIncrement(&(This
->ref
));
1408 FIXME("thread-safety alert! AddRef-ing with a zero refcount!\n");
1412 static DWORD WINAPI
IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER iface
) {
1413 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1417 TRACE("(%p) ref was %ld, thread is %lx\n",This
, This
->ref
, GetCurrentThreadId());
1419 ref
= InterlockedDecrement(&(This
->ref
));
1420 if (ref
) return ref
;
1422 EnterCriticalSection(&(This
->dsound
->lock
));
1423 for (i
=0;i
<This
->dsound
->nrofbuffers
;i
++)
1424 if (This
->dsound
->buffers
[i
] == This
)
1427 if (i
< This
->dsound
->nrofbuffers
) {
1428 /* Put the last buffer of the list in the (now empty) position */
1429 This
->dsound
->buffers
[i
] = This
->dsound
->buffers
[This
->dsound
->nrofbuffers
- 1];
1430 This
->dsound
->nrofbuffers
--;
1431 This
->dsound
->buffers
= HeapReAlloc(GetProcessHeap(),0,This
->dsound
->buffers
,sizeof(LPDIRECTSOUNDBUFFER
)*This
->dsound
->nrofbuffers
);
1432 TRACE("buffer count is now %d\n", This
->dsound
->nrofbuffers
);
1433 IDirectSound_Release((LPDIRECTSOUND
)This
->dsound
);
1435 LeaveCriticalSection(&(This
->dsound
->lock
));
1437 DeleteCriticalSection(&(This
->lock
));
1438 if (This
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
)
1439 DSOUND_PrimaryClose(This
);
1441 IDsDriverBuffer_Release(This
->hwbuf
);
1444 IDirectSound3DBuffer_Release((LPDIRECTSOUND3DBUFFER
)This
->ds3db
);
1446 /* this is a duplicate buffer */
1447 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER
)This
->parent
);
1449 /* this is a toplevel buffer */
1450 HeapFree(GetProcessHeap(),0,This
->buffer
);
1452 HeapFree(GetProcessHeap(),0,This
);
1454 if (This
== primarybuf
)
1460 static DWORD
DSOUND_CalcPlayPosition(IDirectSoundBufferImpl
*This
,
1461 DWORD state
, DWORD pplay
, DWORD pwrite
, DWORD pmix
, DWORD bmix
)
1465 TRACE("primary playpos=%ld, mixpos=%ld\n", pplay
, pmix
);
1466 TRACE("this mixpos=%ld, time=%ld\n", bmix
, GetTickCount());
1468 /* the actual primary play position (pplay) is always behind last mixed (pmix),
1469 * unless the computer is too slow or something */
1470 /* we need to know how far away we are from there */
1471 #if 0 /* we'll never fill the primary entirely */
1472 if (pmix
== pplay
) {
1473 if ((state
== STATE_PLAYING
) || (state
== STATE_STOPPING
)) {
1474 /* wow, the software mixer is really doing well,
1475 * seems the entire primary buffer is filled! */
1476 pmix
+= primarybuf
->buflen
;
1478 /* else: the primary buffer is not playing, so probably empty */
1481 if (pmix
< pplay
) pmix
+= primarybuf
->buflen
; /* wraparound */
1483 /* detect buffer underrun */
1484 if (pwrite
< pplay
) pwrite
+= primarybuf
->buflen
; /* wraparound */
1486 if (pmix
> (DS_SND_QUEUE_MAX
* primarybuf
->dsound
->fraglen
+ pwrite
+ primarybuf
->writelead
)) {
1487 WARN("detected an underrun: primary queue was %ld\n",pmix
);
1490 /* divide the offset by its sample size */
1491 pmix
/= primarybuf
->wfx
.nBlockAlign
;
1492 TRACE("primary back-samples=%ld\n",pmix
);
1493 /* adjust for our frequency */
1494 pmix
= (pmix
* This
->freqAdjust
) >> DSOUND_FREQSHIFT
;
1495 /* multiply by our own sample size */
1496 pmix
*= This
->wfx
.nBlockAlign
;
1497 TRACE("this back-offset=%ld\n", pmix
);
1498 /* subtract from our last mixed position */
1500 while (bplay
< pmix
) bplay
+= This
->buflen
; /* wraparound */
1502 if (This
->leadin
&& ((bplay
< This
->startpos
) || (bplay
> bmix
))) {
1503 /* seems we haven't started playing yet */
1504 TRACE("this still in lead-in phase\n");
1505 bplay
= This
->startpos
;
1507 /* return the result */
1511 static HRESULT WINAPI
IDirectSoundBufferImpl_GetCurrentPosition(
1512 LPDIRECTSOUNDBUFFER iface
,LPDWORD playpos
,LPDWORD writepos
1515 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1516 TRACE("(%p,%p,%p)\n",This
,playpos
,writepos
);
1518 hres
=IDsDriverBuffer_GetPosition(This
->hwbuf
,playpos
,writepos
);
1522 else if (This
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
) {
1525 mtime
.wType
= TIME_BYTES
;
1526 waveOutGetPosition(This
->dsound
->hwo
, &mtime
, sizeof(mtime
));
1527 mtime
.u
.cb
= mtime
.u
.cb
% This
->buflen
;
1528 *playpos
= mtime
.u
.cb
;
1531 /* the writepos should only be used by apps with WRITEPRIMARY priority,
1532 * in which case our software mixer is disabled anyway */
1533 *writepos
= (This
->dsound
->pwplay
+ DS_HEL_MARGIN
) * This
->dsound
->fraglen
;
1534 while (*writepos
>= This
->buflen
)
1535 *writepos
-= This
->buflen
;
1538 if (playpos
&& (This
->state
!= STATE_PLAYING
)) {
1539 /* we haven't been merged into the primary buffer (yet) */
1540 *playpos
= This
->buf_mixpos
;
1543 DWORD pplay
, pwrite
, lplay
, splay
, pstate
;
1544 /* let's get this exact; first, recursively call GetPosition on the primary */
1545 EnterCriticalSection(&(primarybuf
->lock
));
1546 IDirectSoundBufferImpl_GetCurrentPosition((LPDIRECTSOUNDBUFFER
)primarybuf
, &pplay
, &pwrite
);
1547 /* detect HEL mode underrun */
1548 pstate
= primarybuf
->state
;
1549 if (!(primarybuf
->hwbuf
|| primarybuf
->dsound
->pwqueue
)) {
1550 TRACE("detected an underrun\n");
1552 if (pstate
== STATE_PLAYING
)
1553 pstate
= STATE_STARTING
;
1554 else if (pstate
== STATE_STOPPING
)
1555 pstate
= STATE_STOPPED
;
1557 /* get data for ourselves while we still have the lock */
1558 pstate
&= This
->state
;
1559 lplay
= This
->primary_mixpos
;
1560 splay
= This
->buf_mixpos
;
1561 if ((This
->dsbd
.dwFlags
& DSBCAPS_GETCURRENTPOSITION2
) || primarybuf
->hwbuf
) {
1562 /* calculate play position using this */
1563 *playpos
= DSOUND_CalcPlayPosition(This
, pstate
, pplay
, pwrite
, lplay
, splay
);
1565 /* (unless the app isn't using GETCURRENTPOSITION2) */
1566 /* don't know exactly how this should be handled...
1567 * the docs says that play cursor is reported as directly
1568 * behind write cursor, hmm... */
1569 /* let's just do what might work for Half-Life */
1571 wp
= (This
->dsound
->pwplay
+ DS_HEL_MARGIN
) * This
->dsound
->fraglen
;
1572 while (wp
>= primarybuf
->buflen
)
1573 wp
-= primarybuf
->buflen
;
1574 *playpos
= DSOUND_CalcPlayPosition(This
, pstate
, wp
, pwrite
, lplay
, splay
);
1576 LeaveCriticalSection(&(primarybuf
->lock
));
1578 if (writepos
) *writepos
= This
->buf_mixpos
;
1581 if (This
->state
!= STATE_STOPPED
)
1582 /* apply the documented 10ms lead to writepos */
1583 *writepos
+= This
->writelead
;
1584 while (*writepos
>= This
->buflen
) *writepos
-= This
->buflen
;
1586 TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos
?*playpos
:0, writepos
?*writepos
:0, This
, GetTickCount());
1590 static HRESULT WINAPI
IDirectSoundBufferImpl_GetStatus(
1591 LPDIRECTSOUNDBUFFER iface
,LPDWORD status
1593 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1594 TRACE("(%p,%p), thread is %lx\n",This
,status
,GetCurrentThreadId());
1597 return DSERR_INVALIDPARAM
;
1600 if ((This
->state
== STATE_STARTING
) || (This
->state
== STATE_PLAYING
)) {
1601 *status
|= DSBSTATUS_PLAYING
;
1602 if (This
->playflags
& DSBPLAY_LOOPING
)
1603 *status
|= DSBSTATUS_LOOPING
;
1606 TRACE("status=%lx\n", *status
);
1611 static HRESULT WINAPI
IDirectSoundBufferImpl_GetFormat(
1612 LPDIRECTSOUNDBUFFER iface
,LPWAVEFORMATEX lpwf
,DWORD wfsize
,LPDWORD wfwritten
1614 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1615 TRACE("(%p,%p,%ld,%p)\n",This
,lpwf
,wfsize
,wfwritten
);
1617 if (wfsize
>sizeof(This
->wfx
))
1618 wfsize
= sizeof(This
->wfx
);
1619 if (lpwf
) { /* NULL is valid */
1620 memcpy(lpwf
,&(This
->wfx
),wfsize
);
1622 *wfwritten
= wfsize
;
1625 *wfwritten
= sizeof(This
->wfx
);
1627 return DSERR_INVALIDPARAM
;
1632 static HRESULT WINAPI
IDirectSoundBufferImpl_Lock(
1633 LPDIRECTSOUNDBUFFER iface
,DWORD writecursor
,DWORD writebytes
,LPVOID lplpaudioptr1
,LPDWORD audiobytes1
,LPVOID lplpaudioptr2
,LPDWORD audiobytes2
,DWORD flags
1635 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1638 TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx) at %ld\n",
1650 if (flags
& DSBLOCK_FROMWRITECURSOR
) {
1652 /* GetCurrentPosition does too much magic to duplicate here */
1653 IDirectSoundBufferImpl_GetCurrentPosition(iface
, NULL
, &writepos
);
1654 writecursor
+= writepos
;
1656 if (flags
& DSBLOCK_ENTIREBUFFER
)
1657 writebytes
= This
->buflen
;
1658 if (writebytes
> This
->buflen
)
1659 writebytes
= This
->buflen
;
1661 assert(audiobytes1
!=audiobytes2
);
1662 assert(lplpaudioptr1
!=lplpaudioptr2
);
1664 if ((writebytes
== This
->buflen
) &&
1665 ((This
->state
== STATE_STARTING
) ||
1666 (This
->state
== STATE_PLAYING
)))
1667 /* some games, like Half-Life, try to be clever (not) and
1668 * keep one secondary buffer, and mix sounds into it itself,
1669 * locking the entire buffer every time... so we can just forget
1670 * about tracking the last-written-to-position... */
1671 This
->probably_valid_to
= (DWORD
)-1;
1673 This
->probably_valid_to
= writecursor
;
1675 if (This
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
)
1676 capf
= DSDDESC_DONTNEEDPRIMARYLOCK
;
1678 capf
= DSDDESC_DONTNEEDSECONDARYLOCK
;
1679 if (!(This
->dsound
->drvdesc
.dwFlags
& capf
) && This
->hwbuf
) {
1680 IDsDriverBuffer_Lock(This
->hwbuf
,
1681 lplpaudioptr1
, audiobytes1
,
1682 lplpaudioptr2
, audiobytes2
,
1683 writecursor
, writebytes
,
1688 if (writecursor
+writebytes
<= This
->buflen
) {
1689 *(LPBYTE
*)lplpaudioptr1
= This
->buffer
+writecursor
;
1690 *audiobytes1
= writebytes
;
1692 *(LPBYTE
*)lplpaudioptr2
= NULL
;
1695 TRACE("->%ld.0\n",writebytes
);
1697 *(LPBYTE
*)lplpaudioptr1
= This
->buffer
+writecursor
;
1698 *audiobytes1
= This
->buflen
-writecursor
;
1700 *(LPBYTE
*)lplpaudioptr2
= This
->buffer
;
1702 *audiobytes2
= writebytes
-(This
->buflen
-writecursor
);
1703 TRACE("->%ld.%ld\n",*audiobytes1
,audiobytes2
?*audiobytes2
:0);
1705 /* if the segment between playpos and buf_mixpos is touched,
1706 * we need to cancel some mixing */
1707 if (This
->buf_mixpos
>= This
->playpos
) {
1708 if (This
->buf_mixpos
> writecursor
&&
1709 This
->playpos
<= writecursor
+writebytes
)
1713 if (This
->buf_mixpos
> writecursor
||
1714 This
->playpos
<= writecursor
+writebytes
)
1718 TRACE("locking prebuffered region, ouch\n");
1719 DSOUND_MixCancelAt(This
, writecursor
);
1725 static HRESULT WINAPI
IDirectSoundBufferImpl_SetCurrentPosition(
1726 LPDIRECTSOUNDBUFFER iface
,DWORD newpos
1728 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1729 TRACE("(%p,%ld)\n",This
,newpos
);
1732 EnterCriticalSection(&(This
->lock
));
1734 This
->buf_mixpos
= newpos
;
1736 IDsDriverBuffer_SetPosition(This
->hwbuf
, This
->buf_mixpos
);
1738 LeaveCriticalSection(&(This
->lock
));
1744 static HRESULT WINAPI
IDirectSoundBufferImpl_SetPan(
1745 LPDIRECTSOUNDBUFFER iface
,LONG pan
1747 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1749 TRACE("(%p,%ld)\n",This
,pan
);
1751 if ((pan
> DSBPAN_RIGHT
) || (pan
< DSBPAN_LEFT
))
1752 return DSERR_INVALIDPARAM
;
1754 /* You cannot set the pan of the primary buffer */
1755 /* and you cannot use both pan and 3D controls */
1756 if (!(This
->dsbd
.dwFlags
& DSBCAPS_CTRLPAN
) ||
1757 (This
->dsbd
.dwFlags
& DSBCAPS_CTRL3D
) ||
1758 (This
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
))
1759 return DSERR_CONTROLUNAVAIL
;
1762 EnterCriticalSection(&(This
->lock
));
1764 This
->volpan
.lPan
= pan
;
1766 DSOUND_RecalcVolPan(&(This
->volpan
));
1769 IDsDriverBuffer_SetVolumePan(This
->hwbuf
, &(This
->volpan
));
1772 LeaveCriticalSection(&(This
->lock
));
1778 static HRESULT WINAPI
IDirectSoundBufferImpl_GetPan(
1779 LPDIRECTSOUNDBUFFER iface
,LPLONG pan
1781 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1782 TRACE("(%p,%p)\n",This
,pan
);
1785 return DSERR_INVALIDPARAM
;
1787 *pan
= This
->volpan
.lPan
;
1792 static HRESULT WINAPI
IDirectSoundBufferImpl_Unlock(
1793 LPDIRECTSOUNDBUFFER iface
,LPVOID p1
,DWORD x1
,LPVOID p2
,DWORD x2
1795 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1796 DWORD capf
, probably_valid_to
;
1798 TRACE("(%p,%p,%ld,%p,%ld):stub\n", This
,p1
,x1
,p2
,x2
);
1801 /* Preprocess 3D buffers... */
1803 /* This is highly experimental and liable to break things */
1804 if (This
->dsbd
.dwFlags
& DSBCAPS_CTRL3D
)
1805 DSOUND_Create3DBuffer(This
);
1808 if (This
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
)
1809 capf
= DSDDESC_DONTNEEDPRIMARYLOCK
;
1811 capf
= DSDDESC_DONTNEEDSECONDARYLOCK
;
1812 if (!(This
->dsound
->drvdesc
.dwFlags
& capf
) && This
->hwbuf
) {
1813 IDsDriverBuffer_Unlock(This
->hwbuf
, p1
, x1
, p2
, x2
);
1816 if (p2
) probably_valid_to
= (((LPBYTE
)p2
)-This
->buffer
) + x2
;
1817 else probably_valid_to
= (((LPBYTE
)p1
)-This
->buffer
) + x1
;
1818 while (probably_valid_to
>= This
->buflen
)
1819 probably_valid_to
-= This
->buflen
;
1820 if ((probably_valid_to
== 0) && ((x1
+x2
) == This
->buflen
) &&
1821 ((This
->state
== STATE_STARTING
) ||
1822 (This
->state
== STATE_PLAYING
)))
1823 /* see IDirectSoundBufferImpl_Lock */
1824 probably_valid_to
= (DWORD
)-1;
1825 This
->probably_valid_to
= probably_valid_to
;
1830 static HRESULT WINAPI
IDirectSoundBufferImpl_Restore(
1831 LPDIRECTSOUNDBUFFER iface
1833 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1834 FIXME("(%p):stub\n",This
);
1838 static HRESULT WINAPI
IDirectSoundBufferImpl_GetFrequency(
1839 LPDIRECTSOUNDBUFFER iface
,LPDWORD freq
1841 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1842 TRACE("(%p,%p)\n",This
,freq
);
1845 return DSERR_INVALIDPARAM
;
1848 TRACE("-> %ld\n", *freq
);
1853 static HRESULT WINAPI
IDirectSoundBufferImpl_Initialize(
1854 LPDIRECTSOUNDBUFFER iface
,LPDIRECTSOUND dsound
,LPDSBUFFERDESC dbsd
1856 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1857 FIXME("(%p,%p,%p):stub\n",This
,dsound
,dbsd
);
1858 DPRINTF("Re-Init!!!\n");
1859 return DSERR_ALREADYINITIALIZED
;
1862 static HRESULT WINAPI
IDirectSoundBufferImpl_GetCaps(
1863 LPDIRECTSOUNDBUFFER iface
,LPDSBCAPS caps
1865 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1866 TRACE("(%p)->(%p)\n",This
,caps
);
1869 return DSERR_INVALIDPARAM
;
1871 /* I think we should check this value, not set it. See */
1872 /* Inside DirectX, p215. That should apply here, too. */
1873 caps
->dwSize
= sizeof(*caps
);
1875 caps
->dwFlags
= This
->dsbd
.dwFlags
;
1876 if (This
->hwbuf
) caps
->dwFlags
|= DSBCAPS_LOCHARDWARE
;
1877 else caps
->dwFlags
|= DSBCAPS_LOCSOFTWARE
;
1879 caps
->dwBufferBytes
= This
->buflen
;
1881 /* This value represents the speed of the "unlock" command.
1882 As unlock is quite fast (it does not do anything), I put
1883 4096 ko/s = 4 Mo / s */
1884 /* FIXME: hwbuf speed */
1885 caps
->dwUnlockTransferRate
= 4096;
1886 caps
->dwPlayCpuOverhead
= 0;
1891 static HRESULT WINAPI
IDirectSoundBufferImpl_QueryInterface(
1892 LPDIRECTSOUNDBUFFER iface
,REFIID riid
,LPVOID
*ppobj
1894 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1896 TRACE("(%p,%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
1898 if ( IsEqualGUID( &IID_IDirectSoundNotify
, riid
) ) {
1899 IDirectSoundNotifyImpl
*dsn
;
1901 dsn
= (IDirectSoundNotifyImpl
*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsn
));
1904 IDirectSoundBuffer_AddRef(iface
);
1905 ICOM_VTBL(dsn
) = &dsnvt
;
1906 *ppobj
= (LPVOID
)dsn
;
1911 if ( IsEqualGUID( &IID_IDirectSound3DBuffer
, riid
) ) {
1912 IDirectSound3DBufferImpl
*ds3db
;
1914 *ppobj
= This
->ds3db
;
1916 IDirectSound3DBuffer_AddRef((LPDIRECTSOUND3DBUFFER
)This
->ds3db
);
1920 ds3db
= (IDirectSound3DBufferImpl
*)HeapAlloc(GetProcessHeap(),
1924 ICOM_VTBL(ds3db
) = &ds3dbvt
;
1925 InitializeCriticalSection(&ds3db
->lock
);
1927 IDirectSoundBuffer_AddRef(iface
);
1929 ds3db
->ds3db
.dwSize
= sizeof(DS3DBUFFER
);
1930 ds3db
->ds3db
.vPosition
.u1
.x
= 0.0;
1931 ds3db
->ds3db
.vPosition
.u2
.y
= 0.0;
1932 ds3db
->ds3db
.vPosition
.u3
.z
= 0.0;
1933 ds3db
->ds3db
.vVelocity
.u1
.x
= 0.0;
1934 ds3db
->ds3db
.vVelocity
.u2
.y
= 0.0;
1935 ds3db
->ds3db
.vVelocity
.u3
.z
= 0.0;
1936 ds3db
->ds3db
.dwInsideConeAngle
= DS3D_DEFAULTCONEANGLE
;
1937 ds3db
->ds3db
.dwOutsideConeAngle
= DS3D_DEFAULTCONEANGLE
;
1938 ds3db
->ds3db
.vConeOrientation
.u1
.x
= 0.0;
1939 ds3db
->ds3db
.vConeOrientation
.u2
.y
= 0.0;
1940 ds3db
->ds3db
.vConeOrientation
.u3
.z
= 0.0;
1941 ds3db
->ds3db
.lConeOutsideVolume
= DS3D_DEFAULTCONEOUTSIDEVOLUME
; ds3db
->ds3db
.flMinDistance
= DS3D_DEFAULTMINDISTANCE
;
1942 ds3db
->ds3db
.flMaxDistance
= DS3D_DEFAULTMAXDISTANCE
;
1943 ds3db
->ds3db
.dwMode
= DS3DMODE_NORMAL
;
1944 ds3db
->buflen
= (This
->buflen
* primarybuf
->wfx
.nBlockAlign
) /
1945 This
->wfx
.nBlockAlign
;
1946 ds3db
->buffer
= HeapAlloc(GetProcessHeap(), 0, ds3db
->buflen
);
1947 if (ds3db
->buffer
== NULL
) {
1949 ds3db
->ds3db
.dwMode
= DS3DMODE_DISABLE
;
1952 ds3db
->iks
= (IKsPropertySetImpl
*)HeapAlloc(GetProcessHeap(),0,sizeof(*(ds3db
->iks
)));
1953 ds3db
->iks
->ref
= 1;
1954 ds3db
->iks
->ds3db
= ds3db
;
1955 ICOM_VTBL(ds3db
->iks
) = &iksvt
;
1960 if ( IsEqualGUID( &IID_IDirectSound3DBuffer
, riid
) ) {
1961 FIXME("%s: I know about this GUID, but don't support it yet\n",
1962 debugstr_guid( riid
));
1969 if ( IsEqualGUID( &IID_IDirectSound3DListener
, riid
) ) {
1970 IDirectSound3DListenerImpl
* dsl
;
1972 if (This
->dsound
->listener
) {
1973 *ppobj
= This
->dsound
->listener
;
1974 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER
)This
->dsound
->listener
);
1978 dsl
= (IDirectSound3DListenerImpl
*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsl
));
1980 ICOM_VTBL(dsl
) = &ds3dlvt
;
1981 *ppobj
= (LPVOID
)dsl
;
1983 dsl
->ds3dl
.dwSize
= sizeof(DS3DLISTENER
);
1984 dsl
->ds3dl
.vPosition
.u1
.x
= 0.0;
1985 dsl
->ds3dl
.vPosition
.u2
.y
= 0.0;
1986 dsl
->ds3dl
.vPosition
.u3
.z
= 0.0;
1987 dsl
->ds3dl
.vVelocity
.u1
.x
= 0.0;
1988 dsl
->ds3dl
.vVelocity
.u2
.y
= 0.0;
1989 dsl
->ds3dl
.vVelocity
.u3
.z
= 0.0;
1990 dsl
->ds3dl
.vOrientFront
.u1
.x
= 0.0;
1991 dsl
->ds3dl
.vOrientFront
.u2
.y
= 0.0;
1992 dsl
->ds3dl
.vOrientFront
.u3
.z
= 1.0;
1993 dsl
->ds3dl
.vOrientTop
.u1
.x
= 0.0;
1994 dsl
->ds3dl
.vOrientTop
.u2
.y
= 1.0;
1995 dsl
->ds3dl
.vOrientTop
.u3
.z
= 0.0;
1996 dsl
->ds3dl
.flDistanceFactor
= DS3D_DEFAULTDISTANCEFACTOR
;
1997 dsl
->ds3dl
.flRolloffFactor
= DS3D_DEFAULTROLLOFFFACTOR
;
1999 InitializeCriticalSection(&dsl
->lock
);
2002 IDirectSoundBuffer_AddRef(iface
);
2004 This
->dsound
->listener
= dsl
;
2005 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER
)dsl
);
2010 if ( IsEqualGUID( &IID_IDirectSound3DListener
, riid
) ) {
2011 FIXME("%s: I know about this GUID, but don't support it yet\n",
2012 debugstr_guid( riid
));
2018 FIXME( "Unknown GUID %s\n", debugstr_guid( riid
) );
2025 static ICOM_VTABLE(IDirectSoundBuffer
) dsbvt
=
2027 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2028 IDirectSoundBufferImpl_QueryInterface
,
2029 IDirectSoundBufferImpl_AddRef
,
2030 IDirectSoundBufferImpl_Release
,
2031 IDirectSoundBufferImpl_GetCaps
,
2032 IDirectSoundBufferImpl_GetCurrentPosition
,
2033 IDirectSoundBufferImpl_GetFormat
,
2034 IDirectSoundBufferImpl_GetVolume
,
2035 IDirectSoundBufferImpl_GetPan
,
2036 IDirectSoundBufferImpl_GetFrequency
,
2037 IDirectSoundBufferImpl_GetStatus
,
2038 IDirectSoundBufferImpl_Initialize
,
2039 IDirectSoundBufferImpl_Lock
,
2040 IDirectSoundBufferImpl_Play
,
2041 IDirectSoundBufferImpl_SetCurrentPosition
,
2042 IDirectSoundBufferImpl_SetFormat
,
2043 IDirectSoundBufferImpl_SetVolume
,
2044 IDirectSoundBufferImpl_SetPan
,
2045 IDirectSoundBufferImpl_SetFrequency
,
2046 IDirectSoundBufferImpl_Stop
,
2047 IDirectSoundBufferImpl_Unlock
,
2048 IDirectSoundBufferImpl_Restore
2051 /*******************************************************************************
2055 static HRESULT WINAPI
IDirectSoundImpl_SetCooperativeLevel(
2056 LPDIRECTSOUND iface
,HWND hwnd
,DWORD level
2058 ICOM_THIS(IDirectSoundImpl
,iface
);
2060 FIXME("(%p,%08lx,%ld):stub\n",This
,(DWORD
)hwnd
,level
);
2062 This
->priolevel
= level
;
2067 static HRESULT WINAPI
IDirectSoundImpl_CreateSoundBuffer(
2068 LPDIRECTSOUND iface
,LPDSBUFFERDESC dsbd
,LPLPDIRECTSOUNDBUFFER ppdsb
,LPUNKNOWN lpunk
2070 ICOM_THIS(IDirectSoundImpl
,iface
);
2071 IDirectSoundBufferImpl
** ippdsb
=(IDirectSoundBufferImpl
**)ppdsb
;
2072 LPWAVEFORMATEX wfex
;
2073 HRESULT err
= DS_OK
;
2075 TRACE("(%p,%p,%p,%p)\n",This
,dsbd
,ippdsb
,lpunk
);
2077 if ((This
== NULL
) || (dsbd
== NULL
) || (ippdsb
== NULL
))
2078 return DSERR_INVALIDPARAM
;
2080 if (TRACE_ON(dsound
)) {
2081 TRACE("(structsize=%ld)\n",dsbd
->dwSize
);
2082 TRACE("(flags=0x%08lx:\n",dsbd
->dwFlags
);
2083 _dump_DSBCAPS(dsbd
->dwFlags
);
2085 TRACE("(bufferbytes=%ld)\n",dsbd
->dwBufferBytes
);
2086 TRACE("(lpwfxFormat=%p)\n",dsbd
->lpwfxFormat
);
2089 wfex
= dsbd
->lpwfxFormat
;
2092 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
2093 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
2094 wfex
->wFormatTag
, wfex
->nChannels
, wfex
->nSamplesPerSec
,
2095 wfex
->nAvgBytesPerSec
, wfex
->nBlockAlign
,
2096 wfex
->wBitsPerSample
, wfex
->cbSize
);
2098 if (dsbd
->dwFlags
& DSBCAPS_PRIMARYBUFFER
) {
2100 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER
)primarybuf
);
2101 *ippdsb
= primarybuf
;
2102 primarybuf
->dsbd
.dwFlags
= dsbd
->dwFlags
;
2104 } /* Else create primary buffer */
2106 if (dsbd
->dwBufferBytes
< DSBSIZE_MIN
|| dsbd
->dwBufferBytes
> DSBSIZE_MAX
) {
2107 ERR("invalid sound buffer size %ld\n", dsbd
->dwBufferBytes
);
2108 return DSERR_INVALIDPARAM
; /* FIXME: which error? */
2112 *ippdsb
= (IDirectSoundBufferImpl
*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(IDirectSoundBufferImpl
));
2113 if (*ippdsb
== NULL
)
2114 return DSERR_OUTOFMEMORY
;
2115 ICOM_VTBL(*ippdsb
) = &dsbvt
;
2117 (*ippdsb
)->dsound
= This
;
2118 (*ippdsb
)->parent
= NULL
;
2119 (*ippdsb
)->buffer
= NULL
;
2121 memcpy(&((*ippdsb
)->dsbd
),dsbd
,sizeof(*dsbd
));
2122 if (dsbd
->lpwfxFormat
)
2123 memcpy(&((*ippdsb
)->wfx
), dsbd
->lpwfxFormat
, sizeof((*ippdsb
)->wfx
));
2125 TRACE("Created buffer at %p\n", *ippdsb
);
2127 if (dsbd
->dwFlags
& DSBCAPS_PRIMARYBUFFER
) {
2128 (*ippdsb
)->buflen
= dsound
->wfx
.nAvgBytesPerSec
;
2129 (*ippdsb
)->freq
= dsound
->wfx
.nSamplesPerSec
;
2131 /* FIXME: verify that hardware capabilities (DSCAPS_PRIMARY flags) match */
2134 err
= IDsDriver_CreateSoundBuffer(This
->driver
,wfex
,dsbd
->dwFlags
,0,
2135 &((*ippdsb
)->buflen
),&((*ippdsb
)->buffer
),
2136 (LPVOID
*)&((*ippdsb
)->hwbuf
));
2139 err
= DSOUND_PrimaryOpen(*ippdsb
);
2144 (*ippdsb
)->buflen
= dsbd
->dwBufferBytes
;
2145 (*ippdsb
)->freq
= dsbd
->lpwfxFormat
->nSamplesPerSec
;
2147 /* Check necessary hardware mixing capabilities */
2148 if (wfex
->nChannels
==2) capf
|= DSCAPS_SECONDARYSTEREO
;
2149 else capf
|= DSCAPS_SECONDARYMONO
;
2150 if (wfex
->wBitsPerSample
==16) capf
|= DSCAPS_SECONDARY16BIT
;
2151 else capf
|= DSCAPS_SECONDARY8BIT
;
2152 use_hw
= (This
->drvcaps
.dwFlags
& capf
) == capf
;
2154 /* FIXME: check hardware sample rate mixing capabilities */
2155 /* FIXME: check app hints for software/hardware buffer (STATIC, LOCHARDWARE, etc) */
2156 /* FIXME: check whether any hardware buffers are left */
2157 /* FIXME: handle DSDHEAP_CREATEHEAP for hardware buffers */
2159 /* Allocate system memory if applicable */
2160 if ((This
->drvdesc
.dwFlags
& DSDDESC_USESYSTEMMEMORY
) || !use_hw
) {
2161 (*ippdsb
)->buffer
= (LPBYTE
)HeapAlloc(GetProcessHeap(),0,(*ippdsb
)->buflen
);
2162 if ((*ippdsb
)->buffer
== NULL
)
2163 err
= DSERR_OUTOFMEMORY
;
2166 /* Allocate the hardware buffer */
2167 if (use_hw
&& (err
== DS_OK
)) {
2168 err
= IDsDriver_CreateSoundBuffer(This
->driver
,wfex
,dsbd
->dwFlags
,0,
2169 &((*ippdsb
)->buflen
),&((*ippdsb
)->buffer
),
2170 (LPVOID
*)&((*ippdsb
)->hwbuf
));
2175 if ((*ippdsb
)->buffer
)
2176 HeapFree(GetProcessHeap(),0,(*ippdsb
)->buffer
);
2177 HeapFree(GetProcessHeap(),0,(*ippdsb
));
2181 /* calculate fragment size and write lead */
2182 DSOUND_RecalcFormat(*ippdsb
);
2184 /* It's not necessary to initialize values to zero since */
2185 /* we allocated this structure with HEAP_ZERO_MEMORY... */
2186 (*ippdsb
)->playpos
= 0;
2187 (*ippdsb
)->buf_mixpos
= 0;
2188 (*ippdsb
)->state
= STATE_STOPPED
;
2189 DSOUND_RecalcVolPan(&((*ippdsb
)->volpan
));
2191 if (!(dsbd
->dwFlags
& DSBCAPS_PRIMARYBUFFER
)) {
2192 (*ippdsb
)->freqAdjust
= ((*ippdsb
)->freq
<< DSOUND_FREQSHIFT
) /
2193 primarybuf
->wfx
.nSamplesPerSec
;
2194 (*ippdsb
)->nAvgBytesPerSec
= (*ippdsb
)->freq
*
2195 dsbd
->lpwfxFormat
->nBlockAlign
;
2198 EnterCriticalSection(&(This
->lock
));
2199 /* register buffer */
2200 if (!(dsbd
->dwFlags
& DSBCAPS_PRIMARYBUFFER
)) {
2201 IDirectSoundBufferImpl
**newbuffers
= (IDirectSoundBufferImpl
**)HeapReAlloc(GetProcessHeap(),0,This
->buffers
,sizeof(IDirectSoundBufferImpl
*)*(This
->nrofbuffers
+1));
2203 This
->buffers
= newbuffers
;
2204 This
->buffers
[This
->nrofbuffers
] = *ippdsb
;
2205 This
->nrofbuffers
++;
2206 TRACE("buffer count is now %d\n", This
->nrofbuffers
);
2208 ERR("out of memory for buffer list! Current buffer count is %d\n", This
->nrofbuffers
);
2209 err
= DSERR_OUTOFMEMORY
;
2212 LeaveCriticalSection(&(This
->lock
));
2214 IDirectSound_AddRef(iface
);
2216 InitializeCriticalSection(&((*ippdsb
)->lock
));
2220 IDirectSoundBuffer_Release(*ppdsb
);
2226 if (dsbd
->dwFlags
& DSBCAPS_CTRL3D
) {
2227 IDirectSound3DBufferImpl
*ds3db
;
2229 ds3db
= (IDirectSound3DBufferImpl
*)HeapAlloc(GetProcessHeap(),
2231 ICOM_VTBL(ds3db
) = &ds3dbvt
;
2233 (*ippdsb
)->ds3db
= ds3db
;
2235 ds3db
->dsb
= (*ippdsb
);
2236 IDirectSoundBufferImpl_AddRef((LPDIRECTSOUNDBUFFER
)(*ippdsb
));
2238 InitializeCriticalSection(&ds3db
->lock
);
2240 ds3db
->ds3db
.dwSize
= sizeof(DS3DBUFFER
);
2241 ds3db
->ds3db
.vPosition
.u1
.x
= 0.0;
2242 ds3db
->ds3db
.vPosition
.u2
.y
= 0.0;
2243 ds3db
->ds3db
.vPosition
.u3
.z
= 0.0;
2244 ds3db
->ds3db
.vVelocity
.u1
.x
= 0.0;
2245 ds3db
->ds3db
.vVelocity
.u2
.y
= 0.0;
2246 ds3db
->ds3db
.vVelocity
.u3
.z
= 0.0;
2247 ds3db
->ds3db
.dwInsideConeAngle
= DS3D_DEFAULTCONEANGLE
;
2248 ds3db
->ds3db
.dwOutsideConeAngle
= DS3D_DEFAULTCONEANGLE
;
2249 ds3db
->ds3db
.vConeOrientation
.u1
.x
= 0.0;
2250 ds3db
->ds3db
.vConeOrientation
.u2
.y
= 0.0;
2251 ds3db
->ds3db
.vConeOrientation
.u3
.z
= 0.0;
2252 ds3db
->ds3db
.lConeOutsideVolume
= DS3D_DEFAULTCONEOUTSIDEVOLUME
;
2253 ds3db
->ds3db
.flMinDistance
= DS3D_DEFAULTMINDISTANCE
;
2254 ds3db
->ds3db
.flMaxDistance
= DS3D_DEFAULTMAXDISTANCE
;
2255 ds3db
->ds3db
.dwMode
= DS3DMODE_NORMAL
;
2256 ds3db
->buflen
= ((*ippdsb
)->buflen
* primarybuf
->wfx
.nBlockAlign
) /
2257 (*ippdsb
)->wfx
.nBlockAlign
;
2258 ds3db
->buffer
= HeapAlloc(GetProcessHeap(), 0, ds3db
->buflen
);
2259 if (ds3db
->buffer
== NULL
) {
2261 ds3db
->ds3db
.dwMode
= DS3DMODE_DISABLE
;
2263 ds3db
->iks
= (IKsPropertySetImpl
*)HeapAlloc(GetProcessHeap(),0,sizeof(*(ds3db
->iks
)));
2264 ds3db
->iks
->ref
= 1;
2265 ds3db
->iks
->ds3db
= ds3db
;
2266 ICOM_VTBL(ds3db
->iks
) = &iksvt
;
2273 static HRESULT WINAPI
IDirectSoundImpl_DuplicateSoundBuffer(
2274 LPDIRECTSOUND iface
,LPDIRECTSOUNDBUFFER pdsb
,LPLPDIRECTSOUNDBUFFER ppdsb
2276 ICOM_THIS(IDirectSoundImpl
,iface
);
2277 IDirectSoundBufferImpl
* ipdsb
=(IDirectSoundBufferImpl
*)pdsb
;
2278 IDirectSoundBufferImpl
** ippdsb
=(IDirectSoundBufferImpl
**)ppdsb
;
2279 TRACE("(%p,%p,%p)\n",This
,ipdsb
,ippdsb
);
2282 FIXME("need to duplicate hardware buffer\n");
2285 *ippdsb
= (IDirectSoundBufferImpl
*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(IDirectSoundBufferImpl
));
2287 IDirectSoundBuffer_AddRef(pdsb
);
2288 memcpy(*ippdsb
, ipdsb
, sizeof(IDirectSoundBufferImpl
));
2290 (*ippdsb
)->state
= STATE_STOPPED
;
2291 (*ippdsb
)->playpos
= 0;
2292 (*ippdsb
)->buf_mixpos
= 0;
2293 (*ippdsb
)->dsound
= This
;
2294 (*ippdsb
)->parent
= ipdsb
;
2295 memcpy(&((*ippdsb
)->wfx
), &(ipdsb
->wfx
), sizeof((*ippdsb
)->wfx
));
2296 InitializeCriticalSection(&(*ippdsb
)->lock
);
2297 /* register buffer */
2298 EnterCriticalSection(&(This
->lock
));
2300 IDirectSoundBufferImpl
**newbuffers
= (IDirectSoundBufferImpl
**)HeapReAlloc(GetProcessHeap(),0,This
->buffers
,sizeof(IDirectSoundBufferImpl
**)*(This
->nrofbuffers
+1));
2302 This
->buffers
= newbuffers
;
2303 This
->buffers
[This
->nrofbuffers
] = *ippdsb
;
2304 This
->nrofbuffers
++;
2305 TRACE("buffer count is now %d\n", This
->nrofbuffers
);
2307 ERR("out of memory for buffer list! Current buffer count is %d\n", This
->nrofbuffers
);
2308 /* FIXME: release buffer */
2311 LeaveCriticalSection(&(This
->lock
));
2312 IDirectSound_AddRef(iface
);
2317 static HRESULT WINAPI
IDirectSoundImpl_GetCaps(LPDIRECTSOUND iface
,LPDSCAPS caps
) {
2318 ICOM_THIS(IDirectSoundImpl
,iface
);
2319 TRACE("(%p,%p)\n",This
,caps
);
2320 TRACE("(flags=0x%08lx)\n",caps
->dwFlags
);
2323 return DSERR_INVALIDPARAM
;
2325 /* We should check this value, not set it. See Inside DirectX, p215. */
2326 caps
->dwSize
= sizeof(*caps
);
2328 caps
->dwFlags
= This
->drvcaps
.dwFlags
;
2330 /* FIXME: copy caps from This->drvcaps */
2331 caps
->dwMinSecondarySampleRate
= DSBFREQUENCY_MIN
;
2332 caps
->dwMaxSecondarySampleRate
= DSBFREQUENCY_MAX
;
2334 caps
->dwPrimaryBuffers
= 1;
2336 caps
->dwMaxHwMixingAllBuffers
= 0;
2337 caps
->dwMaxHwMixingStaticBuffers
= 0;
2338 caps
->dwMaxHwMixingStreamingBuffers
= 0;
2340 caps
->dwFreeHwMixingAllBuffers
= 0;
2341 caps
->dwFreeHwMixingStaticBuffers
= 0;
2342 caps
->dwFreeHwMixingStreamingBuffers
= 0;
2344 caps
->dwMaxHw3DAllBuffers
= 0;
2345 caps
->dwMaxHw3DStaticBuffers
= 0;
2346 caps
->dwMaxHw3DStreamingBuffers
= 0;
2348 caps
->dwFreeHw3DAllBuffers
= 0;
2349 caps
->dwFreeHw3DStaticBuffers
= 0;
2350 caps
->dwFreeHw3DStreamingBuffers
= 0;
2352 caps
->dwTotalHwMemBytes
= 0;
2354 caps
->dwFreeHwMemBytes
= 0;
2356 caps
->dwMaxContigFreeHwMemBytes
= 0;
2358 caps
->dwUnlockTransferRateHwBuffers
= 4096; /* But we have none... */
2360 caps
->dwPlayCpuOverheadSwBuffers
= 1; /* 1% */
2365 static ULONG WINAPI
IDirectSoundImpl_AddRef(LPDIRECTSOUND iface
) {
2366 ICOM_THIS(IDirectSoundImpl
,iface
);
2367 return ++(This
->ref
);
2370 static ULONG WINAPI
IDirectSoundImpl_Release(LPDIRECTSOUND iface
) {
2371 ICOM_THIS(IDirectSoundImpl
,iface
);
2372 TRACE("(%p), ref was %ld\n",This
,This
->ref
);
2373 if (!--(This
->ref
)) {
2376 timeKillEvent(This
->timerID
);
2377 timeEndPeriod(DS_TIME_RES
);
2380 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER
)primarybuf
);
2382 if (This
->buffers
) {
2383 for( i
=0;i
<This
->nrofbuffers
;i
++)
2384 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER
)This
->buffers
[i
]);
2388 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER
)This
->primary
);
2390 DeleteCriticalSection(&This
->lock
);
2392 IDsDriver_Close(This
->driver
);
2395 for (c
=0; c
<DS_HEL_FRAGS
; c
++)
2396 HeapFree(GetProcessHeap(),0,This
->pwave
[c
]);
2398 if (This
->drvdesc
.dwFlags
& DSDDESC_DOMMSYSTEMOPEN
) {
2399 waveOutClose(This
->hwo
);
2402 IDsDriver_Release(This
->driver
);
2404 HeapFree(GetProcessHeap(),0,This
);
2411 static HRESULT WINAPI
IDirectSoundImpl_SetSpeakerConfig(
2412 LPDIRECTSOUND iface
,DWORD config
2414 ICOM_THIS(IDirectSoundImpl
,iface
);
2415 FIXME("(%p,0x%08lx):stub\n",This
,config
);
2419 static HRESULT WINAPI
IDirectSoundImpl_QueryInterface(
2420 LPDIRECTSOUND iface
,REFIID riid
,LPVOID
*ppobj
2422 ICOM_THIS(IDirectSoundImpl
,iface
);
2424 if ( IsEqualGUID( &IID_IDirectSound3DListener
, riid
) ) {
2426 if (This
->listener
) {
2427 *ppobj
= This
->listener
;
2428 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER
)This
->listener
);
2432 This
->listener
= (IDirectSound3DListenerImpl
*)HeapAlloc(
2433 GetProcessHeap(), 0, sizeof(*(This
->listener
)));
2434 This
->listener
->ref
= 1;
2435 ICOM_VTBL(This
->listener
) = &ds3dlvt
;
2436 *ppobj
= (LPVOID
)This
->listener
;
2437 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER
)*ppobj
);
2439 This
->listener
->dsb
= NULL
;
2441 This
->listener
->ds3dl
.dwSize
= sizeof(DS3DLISTENER
);
2442 This
->listener
->ds3dl
.vPosition
.u1
.x
= 0.0;
2443 This
->listener
->ds3dl
.vPosition
.u2
.y
= 0.0;
2444 This
->listener
->ds3dl
.vPosition
.u3
.z
= 0.0;
2445 This
->listener
->ds3dl
.vVelocity
.u1
.x
= 0.0;
2446 This
->listener
->ds3dl
.vVelocity
.u2
.y
= 0.0;
2447 This
->listener
->ds3dl
.vVelocity
.u3
.z
= 0.0;
2448 This
->listener
->ds3dl
.vOrientFront
.u1
.x
= 0.0;
2449 This
->listener
->ds3dl
.vOrientFront
.u2
.y
= 0.0;
2450 This
->listener
->ds3dl
.vOrientFront
.u3
.z
= 1.0;
2451 This
->listener
->ds3dl
.vOrientTop
.u1
.x
= 0.0;
2452 This
->listener
->ds3dl
.vOrientTop
.u2
.y
= 1.0;
2453 This
->listener
->ds3dl
.vOrientTop
.u3
.z
= 0.0;
2454 This
->listener
->ds3dl
.flDistanceFactor
= DS3D_DEFAULTDISTANCEFACTOR
;
2455 This
->listener
->ds3dl
.flRolloffFactor
= DS3D_DEFAULTROLLOFFFACTOR
;
2456 This
->listener
->ds3dl
.flDopplerFactor
= DS3D_DEFAULTDOPPLERFACTOR
;
2458 InitializeCriticalSection(&This
->listener
->lock
);
2463 FIXME("(%p,%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
2467 static HRESULT WINAPI
IDirectSoundImpl_Compact(
2468 LPDIRECTSOUND iface
)
2470 ICOM_THIS(IDirectSoundImpl
,iface
);
2471 TRACE("(%p)\n", This
);
2475 static HRESULT WINAPI
IDirectSoundImpl_GetSpeakerConfig(
2476 LPDIRECTSOUND iface
,
2477 LPDWORD lpdwSpeakerConfig
)
2479 ICOM_THIS(IDirectSoundImpl
,iface
);
2480 TRACE("(%p, %p)\n", This
, lpdwSpeakerConfig
);
2481 *lpdwSpeakerConfig
= DSSPEAKER_STEREO
| (DSSPEAKER_GEOMETRY_NARROW
<< 16);
2485 static HRESULT WINAPI
IDirectSoundImpl_Initialize(
2486 LPDIRECTSOUND iface
,
2489 ICOM_THIS(IDirectSoundImpl
,iface
);
2490 TRACE("(%p, %p)\n", This
, lpcGuid
);
2494 static ICOM_VTABLE(IDirectSound
) dsvt
=
2496 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2497 IDirectSoundImpl_QueryInterface
,
2498 IDirectSoundImpl_AddRef
,
2499 IDirectSoundImpl_Release
,
2500 IDirectSoundImpl_CreateSoundBuffer
,
2501 IDirectSoundImpl_GetCaps
,
2502 IDirectSoundImpl_DuplicateSoundBuffer
,
2503 IDirectSoundImpl_SetCooperativeLevel
,
2504 IDirectSoundImpl_Compact
,
2505 IDirectSoundImpl_GetSpeakerConfig
,
2506 IDirectSoundImpl_SetSpeakerConfig
,
2507 IDirectSoundImpl_Initialize
2511 static void DSOUND_CheckEvent(IDirectSoundBufferImpl
*dsb
, int len
)
2515 LPDSBPOSITIONNOTIFY event
;
2517 if (dsb
->nrofnotifies
== 0)
2520 TRACE("(%p) buflen = %ld, playpos = %ld, len = %d\n",
2521 dsb
, dsb
->buflen
, dsb
->playpos
, len
);
2522 for (i
= 0; i
< dsb
->nrofnotifies
; i
++) {
2523 event
= dsb
->notifies
+ i
;
2524 offset
= event
->dwOffset
;
2525 TRACE("checking %d, position %ld, event = %d\n",
2526 i
, offset
, event
->hEventNotify
);
2527 /* DSBPN_OFFSETSTOP has to be the last element. So this is */
2528 /* OK. [Inside DirectX, p274] */
2530 /* This also means we can't sort the entries by offset, */
2531 /* because DSBPN_OFFSETSTOP == -1 */
2532 if (offset
== DSBPN_OFFSETSTOP
) {
2533 if (dsb
->state
== STATE_STOPPED
) {
2534 SetEvent(event
->hEventNotify
);
2535 TRACE("signalled event %d (%d)\n", event
->hEventNotify
, i
);
2540 if ((dsb
->playpos
+ len
) >= dsb
->buflen
) {
2541 if ((offset
< ((dsb
->playpos
+ len
) % dsb
->buflen
)) ||
2542 (offset
>= dsb
->playpos
)) {
2543 TRACE("signalled event %d (%d)\n", event
->hEventNotify
, i
);
2544 SetEvent(event
->hEventNotify
);
2547 if ((offset
>= dsb
->playpos
) && (offset
< (dsb
->playpos
+ len
))) {
2548 TRACE("signalled event %d (%d)\n", event
->hEventNotify
, i
);
2549 SetEvent(event
->hEventNotify
);
2555 /* WAV format info can be found at: */
2557 /* http://www.cwi.nl/ftp/audio/AudioFormats.part2 */
2558 /* ftp://ftp.cwi.nl/pub/audio/RIFF-format */
2560 /* Import points to remember: */
2562 /* 8-bit WAV is unsigned */
2563 /* 16-bit WAV is signed */
2565 static inline INT16
cvtU8toS16(BYTE byte
)
2567 INT16 s
= (byte
- 128) << 8;
2572 static inline BYTE
cvtS16toU8(INT16 word
)
2574 BYTE b
= (word
+ 32768) >> 8;
2580 /* We should be able to optimize these two inline functions */
2581 /* so that we aren't doing 8->16->8 conversions when it is */
2582 /* not necessary. But this is still a WIP. Optimize later. */
2583 static inline void get_fields(const IDirectSoundBufferImpl
*dsb
, BYTE
*buf
, INT
*fl
, INT
*fr
)
2585 INT16
*bufs
= (INT16
*) buf
;
2587 /* TRACE("(%p)\n", buf); */
2588 if ((dsb
->wfx
.wBitsPerSample
== 8) && dsb
->wfx
.nChannels
== 2) {
2589 *fl
= cvtU8toS16(*buf
);
2590 *fr
= cvtU8toS16(*(buf
+ 1));
2594 if ((dsb
->wfx
.wBitsPerSample
== 16) && dsb
->wfx
.nChannels
== 2) {
2600 if ((dsb
->wfx
.wBitsPerSample
== 8) && dsb
->wfx
.nChannels
== 1) {
2601 *fl
= cvtU8toS16(*buf
);
2606 if ((dsb
->wfx
.wBitsPerSample
== 16) && dsb
->wfx
.nChannels
== 1) {
2612 FIXME("get_fields found an unsupported configuration\n");
2616 static inline void set_fields(BYTE
*buf
, INT fl
, INT fr
)
2618 INT16
*bufs
= (INT16
*) buf
;
2620 if ((primarybuf
->wfx
.wBitsPerSample
== 8) && (primarybuf
->wfx
.nChannels
== 2)) {
2621 *buf
= cvtS16toU8(fl
);
2622 *(buf
+ 1) = cvtS16toU8(fr
);
2626 if ((primarybuf
->wfx
.wBitsPerSample
== 16) && (primarybuf
->wfx
.nChannels
== 2)) {
2632 if ((primarybuf
->wfx
.wBitsPerSample
== 8) && (primarybuf
->wfx
.nChannels
== 1)) {
2633 *buf
= cvtS16toU8((fl
+ fr
) >> 1);
2637 if ((primarybuf
->wfx
.wBitsPerSample
== 16) && (primarybuf
->wfx
.nChannels
== 1)) {
2638 *bufs
= (fl
+ fr
) >> 1;
2641 FIXME("set_fields found an unsupported configuration\n");
2645 /* Now with PerfectPitch (tm) technology */
2646 static INT
DSOUND_MixerNorm(IDirectSoundBufferImpl
*dsb
, BYTE
*buf
, INT len
)
2648 INT i
, size
, ipos
, ilen
, fieldL
, fieldR
;
2650 INT iAdvance
= dsb
->wfx
.nBlockAlign
;
2651 INT oAdvance
= primarybuf
->wfx
.nBlockAlign
;
2653 ibp
= dsb
->buffer
+ dsb
->buf_mixpos
;
2656 TRACE("(%p, %p, %p), buf_mixpos=%ld\n", dsb
, ibp
, obp
, dsb
->buf_mixpos
);
2657 /* Check for the best case */
2658 if ((dsb
->freq
== primarybuf
->wfx
.nSamplesPerSec
) &&
2659 (dsb
->wfx
.wBitsPerSample
== primarybuf
->wfx
.wBitsPerSample
) &&
2660 (dsb
->wfx
.nChannels
== primarybuf
->wfx
.nChannels
)) {
2661 DWORD bytesleft
= dsb
->buflen
- dsb
->buf_mixpos
;
2662 TRACE("(%p) Best case\n", dsb
);
2663 if (len
<= bytesleft
)
2664 memcpy(obp
, ibp
, len
);
2666 memcpy(obp
, ibp
, bytesleft
);
2667 memcpy(obp
+ bytesleft
, dsb
->buffer
, len
- bytesleft
);
2672 /* Check for same sample rate */
2673 if (dsb
->freq
== primarybuf
->wfx
.nSamplesPerSec
) {
2674 TRACE("(%p) Same sample rate %ld = primary %ld\n", dsb
,
2675 dsb
->freq
, primarybuf
->wfx
.nSamplesPerSec
);
2677 for (i
= 0; i
< len
; i
+= oAdvance
) {
2678 get_fields(dsb
, ibp
, &fieldL
, &fieldR
);
2681 set_fields(obp
, fieldL
, fieldR
);
2683 if (ibp
>= (BYTE
*)(dsb
->buffer
+ dsb
->buflen
))
2684 ibp
= dsb
->buffer
; /* wrap */
2689 /* Mix in different sample rates */
2691 /* New PerfectPitch(tm) Technology (c) 1998 Rob Riggs */
2692 /* Patent Pending :-] */
2694 /* Patent enhancements (c) 2000 Ove KÃ¥ven,
2695 * TransGaming Technologies Inc. */
2697 FIXME("(%p) Adjusting frequency: %ld -> %ld (need optimization)\n",
2698 dsb
, dsb
->freq
, primarybuf
->wfx
.nSamplesPerSec
);
2700 size
= len
/ oAdvance
;
2702 ipos
= dsb
->buf_mixpos
;
2703 for (i
= 0; i
< size
; i
++) {
2704 get_fields(dsb
, (dsb
->buffer
+ ipos
), &fieldL
, &fieldR
);
2705 set_fields(obp
, fieldL
, fieldR
);
2708 dsb
->freqAcc
+= dsb
->freqAdjust
;
2709 if (dsb
->freqAcc
>= (1<<DSOUND_FREQSHIFT
)) {
2710 ULONG adv
= (dsb
->freqAcc
>>DSOUND_FREQSHIFT
) * iAdvance
;
2711 dsb
->freqAcc
&= (1<<DSOUND_FREQSHIFT
)-1;
2712 ipos
+= adv
; ilen
+= adv
;
2713 while (ipos
>= dsb
->buflen
)
2714 ipos
-= dsb
->buflen
;
2720 static void DSOUND_MixerVol(IDirectSoundBufferImpl
*dsb
, BYTE
*buf
, INT len
)
2722 INT i
, inc
= primarybuf
->wfx
.wBitsPerSample
>> 3;
2724 INT16
*bps
= (INT16
*) buf
;
2726 TRACE("(%p) left = %lx, right = %lx\n", dsb
,
2727 dsb
->volpan
.dwTotalLeftAmpFactor
, dsb
->volpan
.dwTotalRightAmpFactor
);
2728 if ((!(dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLPAN
) || (dsb
->volpan
.lPan
== 0)) &&
2729 (!(dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLVOLUME
) || (dsb
->volpan
.lVolume
== 0)) &&
2730 !(dsb
->dsbd
.dwFlags
& DSBCAPS_CTRL3D
))
2731 return; /* Nothing to do */
2733 /* If we end up with some bozo coder using panning or 3D sound */
2734 /* with a mono primary buffer, it could sound very weird using */
2735 /* this method. Oh well, tough patooties. */
2737 for (i
= 0; i
< len
; i
+= inc
) {
2743 /* 8-bit WAV is unsigned, but we need to operate */
2744 /* on signed data for this to work properly */
2746 val
= ((val
* (i
& inc
? dsb
->volpan
.dwTotalRightAmpFactor
: dsb
->volpan
.dwTotalLeftAmpFactor
)) >> 16);
2751 /* 16-bit WAV is signed -- much better */
2753 val
= ((val
* ((i
& inc
) ? dsb
->volpan
.dwTotalRightAmpFactor
: dsb
->volpan
.dwTotalLeftAmpFactor
)) >> 16);
2759 FIXME("MixerVol had a nasty error\n");
2765 static void DSOUND_Mixer3D(IDirectSoundBufferImpl
*dsb
, BYTE
*buf
, INT len
)
2768 DWORD buflen
, buf_mixpos
;
2770 buflen
= dsb
->ds3db
->buflen
;
2771 buf_mixpos
= (dsb
->buf_mixpos
* primarybuf
->wfx
.nBlockAlign
) / dsb
->wfx
.nBlockAlign
;
2772 ibp
= dsb
->ds3db
->buffer
+ buf_mixpos
;
2775 if (buf_mixpos
> buflen
) {
2776 FIXME("Major breakage\n");
2780 if (len
<= (buf_mixpos
+ buflen
))
2781 memcpy(obp
, ibp
, len
);
2783 memcpy(obp
, ibp
, buflen
- buf_mixpos
);
2784 memcpy(obp
+ (buflen
- buf_mixpos
),
2786 len
- (buflen
- buf_mixpos
));
2792 static void *tmp_buffer
;
2793 static size_t tmp_buffer_len
= 0;
2795 static void *DSOUND_tmpbuffer(size_t len
)
2797 if (len
>tmp_buffer_len
) {
2798 void *new_buffer
= realloc(tmp_buffer
, len
);
2800 tmp_buffer
= new_buffer
;
2801 tmp_buffer_len
= len
;
2808 static DWORD
DSOUND_MixInBuffer(IDirectSoundBufferImpl
*dsb
, DWORD writepos
, DWORD fraglen
)
2810 INT i
, len
, ilen
, temp
, field
;
2811 INT advance
= primarybuf
->wfx
.wBitsPerSample
>> 3;
2812 BYTE
*buf
, *ibuf
, *obuf
;
2813 INT16
*ibufs
, *obufs
;
2816 if (!(dsb
->playflags
& DSBPLAY_LOOPING
)) {
2817 temp
= MulDiv(primarybuf
->wfx
.nAvgBytesPerSec
, dsb
->buflen
,
2818 dsb
->nAvgBytesPerSec
) -
2819 MulDiv(primarybuf
->wfx
.nAvgBytesPerSec
, dsb
->buf_mixpos
,
2820 dsb
->nAvgBytesPerSec
);
2821 len
= (len
> temp
) ? temp
: len
;
2823 len
&= ~3; /* 4 byte alignment */
2826 /* This should only happen if we aren't looping and temp < 4 */
2828 /* We skip the remainder, so check for possible events */
2829 DSOUND_CheckEvent(dsb
, dsb
->buflen
- dsb
->buf_mixpos
);
2831 dsb
->state
= STATE_STOPPED
;
2833 dsb
->buf_mixpos
= 0;
2834 dsb
->leadin
= FALSE
;
2835 /* Check for DSBPN_OFFSETSTOP */
2836 DSOUND_CheckEvent(dsb
, 0);
2840 /* Been seeing segfaults in malloc() for some reason... */
2841 TRACE("allocating buffer (size = %d)\n", len
);
2842 if ((buf
= ibuf
= (BYTE
*) DSOUND_tmpbuffer(len
)) == NULL
)
2845 TRACE("MixInBuffer (%p) len = %d, dest = %ld\n", dsb
, len
, writepos
);
2847 ilen
= DSOUND_MixerNorm(dsb
, ibuf
, len
);
2848 if ((dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLPAN
) ||
2849 (dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLVOLUME
))
2850 DSOUND_MixerVol(dsb
, ibuf
, len
);
2852 obuf
= primarybuf
->buffer
+ writepos
;
2853 for (i
= 0; i
< len
; i
+= advance
) {
2854 obufs
= (INT16
*) obuf
;
2855 ibufs
= (INT16
*) ibuf
;
2856 if (primarybuf
->wfx
.wBitsPerSample
== 8) {
2857 /* 8-bit WAV is unsigned */
2858 field
= (*ibuf
- 128);
2859 field
+= (*obuf
- 128);
2860 field
= field
> 127 ? 127 : field
;
2861 field
= field
< -128 ? -128 : field
;
2862 *obuf
= field
+ 128;
2864 /* 16-bit WAV is signed */
2867 field
= field
> 32767 ? 32767 : field
;
2868 field
= field
< -32768 ? -32768 : field
;
2873 if (obuf
>= (BYTE
*)(primarybuf
->buffer
+ primarybuf
->buflen
))
2874 obuf
= primarybuf
->buffer
;
2878 if (dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLPOSITIONNOTIFY
)
2879 DSOUND_CheckEvent(dsb
, ilen
);
2881 if (dsb
->leadin
&& (dsb
->startpos
> dsb
->buf_mixpos
) && (dsb
->startpos
<= dsb
->buf_mixpos
+ ilen
)) {
2882 /* HACK... leadin should be reset when the PLAY position reaches the startpos,
2883 * not the MIX position... but if the sound buffer is bigger than our prebuffering
2884 * (which must be the case for the streaming buffers that need this hack anyway)
2885 * plus DS_HEL_MARGIN or equivalent, then this ought to work anyway. */
2886 dsb
->leadin
= FALSE
;
2889 dsb
->buf_mixpos
+= ilen
;
2891 if (dsb
->buf_mixpos
>= dsb
->buflen
) {
2892 if (!(dsb
->playflags
& DSBPLAY_LOOPING
)) {
2893 dsb
->state
= STATE_STOPPED
;
2895 dsb
->buf_mixpos
= 0;
2896 dsb
->leadin
= FALSE
;
2897 DSOUND_CheckEvent(dsb
, 0); /* For DSBPN_OFFSETSTOP */
2900 while (dsb
->buf_mixpos
>= dsb
->buflen
)
2901 dsb
->buf_mixpos
-= dsb
->buflen
;
2902 if (dsb
->leadin
&& (dsb
->startpos
<= dsb
->buf_mixpos
))
2903 dsb
->leadin
= FALSE
; /* HACK: see above */
2910 static void DSOUND_PhaseCancel(IDirectSoundBufferImpl
*dsb
, DWORD writepos
, DWORD len
)
2913 INT advance
= primarybuf
->wfx
.wBitsPerSample
>> 3;
2914 BYTE
*buf
, *ibuf
, *obuf
;
2915 INT16
*ibufs
, *obufs
;
2917 len
&= ~3; /* 4 byte alignment */
2919 TRACE("allocating buffer (size = %ld)\n", len
);
2920 if ((buf
= ibuf
= (BYTE
*) DSOUND_tmpbuffer(len
)) == NULL
)
2923 TRACE("PhaseCancel (%p) len = %ld, dest = %ld\n", dsb
, len
, writepos
);
2925 ilen
= DSOUND_MixerNorm(dsb
, ibuf
, len
);
2926 if ((dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLPAN
) ||
2927 (dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLVOLUME
))
2928 DSOUND_MixerVol(dsb
, ibuf
, len
);
2930 /* subtract instead of add, to phase out premixed data */
2931 obuf
= primarybuf
->buffer
+ writepos
;
2932 for (i
= 0; i
< len
; i
+= advance
) {
2933 obufs
= (INT16
*) obuf
;
2934 ibufs
= (INT16
*) ibuf
;
2935 if (primarybuf
->wfx
.wBitsPerSample
== 8) {
2936 /* 8-bit WAV is unsigned */
2937 field
= (*ibuf
- 128);
2938 field
-= (*obuf
- 128);
2939 field
= field
> 127 ? 127 : field
;
2940 field
= field
< -128 ? -128 : field
;
2941 *obuf
= field
+ 128;
2943 /* 16-bit WAV is signed */
2946 field
= field
> 32767 ? 32767 : field
;
2947 field
= field
< -32768 ? -32768 : field
;
2952 if (obuf
>= (BYTE
*)(primarybuf
->buffer
+ primarybuf
->buflen
))
2953 obuf
= primarybuf
->buffer
;
2958 static void DSOUND_MixCancel(IDirectSoundBufferImpl
*dsb
, DWORD writepos
, BOOL cancel
)
2960 DWORD size
, flen
, len
, npos
, nlen
;
2961 INT iAdvance
= dsb
->wfx
.nBlockAlign
;
2962 INT oAdvance
= primarybuf
->wfx
.nBlockAlign
;
2963 /* determine amount of premixed data to cancel */
2964 DWORD primary_done
=
2965 ((dsb
->primary_mixpos
< writepos
) ? primarybuf
->buflen
: 0) +
2966 dsb
->primary_mixpos
- writepos
;
2968 TRACE("(%p, %ld), buf_mixpos=%ld\n", dsb
, writepos
, dsb
->buf_mixpos
);
2970 /* backtrack the mix position */
2971 size
= primary_done
/ oAdvance
;
2972 flen
= size
* dsb
->freqAdjust
;
2973 len
= (flen
>> DSOUND_FREQSHIFT
) * iAdvance
;
2974 flen
&= (1<<DSOUND_FREQSHIFT
)-1;
2975 while (dsb
->freqAcc
< flen
) {
2977 dsb
->freqAcc
+= 1<<DSOUND_FREQSHIFT
;
2980 npos
= ((dsb
->buf_mixpos
< len
) ? dsb
->buflen
: 0) +
2981 dsb
->buf_mixpos
- len
;
2982 if (dsb
->leadin
&& (dsb
->startpos
> npos
) && (dsb
->startpos
<= npos
+ len
)) {
2983 /* stop backtracking at startpos */
2984 npos
= dsb
->startpos
;
2985 len
= ((dsb
->buf_mixpos
< npos
) ? dsb
->buflen
: 0) +
2986 dsb
->buf_mixpos
- npos
;
2987 flen
= dsb
->freqAcc
;
2988 nlen
= len
/ dsb
->wfx
.nBlockAlign
;
2989 nlen
= ((nlen
<< DSOUND_FREQSHIFT
) + flen
) / dsb
->freqAdjust
;
2990 nlen
*= primarybuf
->wfx
.nBlockAlign
;
2992 ((dsb
->primary_mixpos
< nlen
) ? primarybuf
->buflen
: 0) +
2993 dsb
->primary_mixpos
- nlen
;
2996 dsb
->freqAcc
-= flen
;
2997 dsb
->buf_mixpos
= npos
;
2998 dsb
->primary_mixpos
= writepos
;
3000 TRACE("new buf_mixpos=%ld, primary_mixpos=%ld (len=%ld)\n",
3001 dsb
->buf_mixpos
, dsb
->primary_mixpos
, len
);
3003 if (cancel
) DSOUND_PhaseCancel(dsb
, writepos
, len
);
3006 static void DSOUND_MixCancelAt(IDirectSoundBufferImpl
*dsb
, DWORD buf_writepos
)
3009 DWORD i
, size
, flen
, len
, npos
, nlen
;
3010 INT iAdvance
= dsb
->wfx
.nBlockAlign
;
3011 INT oAdvance
= primarybuf
->wfx
.nBlockAlign
;
3012 /* determine amount of premixed data to cancel */
3014 ((dsb
->buf_mixpos
< buf_writepos
) ? dsb
->buflen
: 0) +
3015 dsb
->buf_mixpos
- buf_writepos
;
3018 WARN("(%p, %ld), buf_mixpos=%ld\n", dsb
, buf_writepos
, dsb
->buf_mixpos
);
3019 /* since this is not implemented yet, just cancel *ALL* prebuffering for now
3020 * (which is faster anyway when there's one a single secondary buffer) */
3021 primarybuf
->need_remix
= TRUE
;
3024 static DWORD
DSOUND_MixOne(IDirectSoundBufferImpl
*dsb
, DWORD playpos
, DWORD writepos
, DWORD mixlen
)
3027 /* determine this buffer's write position */
3028 DWORD buf_writepos
= DSOUND_CalcPlayPosition(dsb
, dsb
->state
& primarybuf
->state
, writepos
,
3029 writepos
, dsb
->primary_mixpos
, dsb
->buf_mixpos
);
3030 /* determine how much already-mixed data exists */
3032 ((dsb
->buf_mixpos
< buf_writepos
) ? dsb
->buflen
: 0) +
3033 dsb
->buf_mixpos
- buf_writepos
;
3034 DWORD primary_done
=
3035 ((dsb
->primary_mixpos
< writepos
) ? primarybuf
->buflen
: 0) +
3036 dsb
->primary_mixpos
- writepos
;
3038 ((primarybuf
->buf_mixpos
< writepos
) ? primarybuf
->buflen
: 0) +
3039 primarybuf
->buf_mixpos
- writepos
;
3042 TRACE("buf_writepos=%ld, primary_writepos=%ld\n", buf_writepos
, writepos
);
3043 TRACE("buf_done=%ld, primary_done=%ld\n", buf_done
, primary_done
);
3044 TRACE("buf_mixpos=%ld, primary_mixpos=%ld, mixlen=%ld\n", dsb
->buf_mixpos
, dsb
->primary_mixpos
,
3046 TRACE("looping=%ld, startpos=%ld, leadin=%ld\n", dsb
->playflags
, dsb
->startpos
, dsb
->leadin
);
3048 /* save write position for non-GETCURRENTPOSITION2... */
3049 dsb
->playpos
= buf_writepos
;
3051 /* check whether CalcPlayPosition detected a mixing underrun */
3052 if ((buf_done
== 0) && (dsb
->primary_mixpos
!= writepos
)) {
3053 /* it did, but did we have more to play? */
3054 if ((dsb
->playflags
& DSBPLAY_LOOPING
) ||
3055 (dsb
->buf_mixpos
< dsb
->buflen
)) {
3056 /* yes, have to recover */
3057 ERR("underrun on sound buffer %p\n", dsb
);
3058 TRACE("recovering from underrun: primary_mixpos=%ld\n", writepos
);
3060 dsb
->primary_mixpos
= writepos
;
3063 /* determine how far ahead we should mix */
3064 if (((dsb
->playflags
& DSBPLAY_LOOPING
) ||
3065 (dsb
->leadin
&& (dsb
->probably_valid_to
!= 0))) &&
3066 !(dsb
->dsbd
.dwFlags
& DSBCAPS_STATIC
)) {
3067 /* if this is a streaming buffer, it typically means that
3068 * we should defer mixing past probably_valid_to as long
3069 * as we can, to avoid unnecessary remixing */
3070 /* the heavy-looking calculations shouldn't be that bad,
3071 * as any game isn't likely to be have more than 1 or 2
3072 * streaming buffers in use at any time anyway... */
3073 DWORD probably_valid_left
=
3074 (dsb
->probably_valid_to
== (DWORD
)-1) ? dsb
->buflen
:
3075 ((dsb
->probably_valid_to
< buf_writepos
) ? dsb
->buflen
: 0) +
3076 dsb
->probably_valid_to
- buf_writepos
;
3077 /* check for leadin condition */
3078 if ((probably_valid_left
== 0) &&
3079 (dsb
->probably_valid_to
== dsb
->startpos
) &&
3081 probably_valid_left
= dsb
->buflen
;
3082 TRACE("streaming buffer probably_valid_to=%ld, probably_valid_left=%ld\n",
3083 dsb
->probably_valid_to
, probably_valid_left
);
3084 /* check whether the app's time is already up */
3085 if (probably_valid_left
< dsb
->writelead
) {
3086 WARN("probably_valid_to now within writelead, possible streaming underrun\n");
3087 /* once we pass the point of no return,
3088 * no reason to hold back anymore */
3089 dsb
->probably_valid_to
= (DWORD
)-1;
3090 /* we just have to go ahead and mix what we have,
3091 * there's no telling what the app is thinking anyway */
3093 /* adjust for our frequency and our sample size */
3094 probably_valid_left
= MulDiv(probably_valid_left
,
3095 1 << DSOUND_FREQSHIFT
,
3096 dsb
->wfx
.nBlockAlign
* dsb
->freqAdjust
) *
3097 primarybuf
->wfx
.nBlockAlign
;
3098 /* check whether to clip mix_len */
3099 if (probably_valid_left
< mixlen
) {
3100 TRACE("clipping to probably_valid_left=%ld\n", probably_valid_left
);
3101 mixlen
= probably_valid_left
;
3105 /* cut mixlen with what's already been mixed */
3106 if (mixlen
< primary_done
) {
3107 /* huh? and still CalcPlayPosition didn't
3108 * detect an underrun? */
3109 FIXME("problem with underrun detection (mixlen=%ld < primary_done=%ld)\n", mixlen
, primary_done
);
3112 len
= mixlen
- primary_done
;
3113 TRACE("remaining mixlen=%ld\n", len
);
3115 if (len
< primarybuf
->dsound
->fraglen
) {
3116 /* smaller than a fragment, wait until it gets larger
3117 * before we take the mixing overhead */
3118 TRACE("mixlen not worth it, deferring mixing\n");
3122 /* ok, we know how much to mix, let's go */
3123 still_behind
= (adv_done
> primary_done
);
3125 slen
= primarybuf
->buflen
- dsb
->primary_mixpos
;
3126 if (slen
> len
) slen
= len
;
3127 slen
= DSOUND_MixInBuffer(dsb
, dsb
->primary_mixpos
, slen
);
3129 if ((dsb
->primary_mixpos
< primarybuf
->buf_mixpos
) &&
3130 (dsb
->primary_mixpos
+ slen
>= primarybuf
->buf_mixpos
))
3131 still_behind
= FALSE
;
3133 dsb
->primary_mixpos
+= slen
; len
-= slen
;
3134 while (dsb
->primary_mixpos
>= primarybuf
->buflen
)
3135 dsb
->primary_mixpos
-= primarybuf
->buflen
;
3137 if ((dsb
->state
== STATE_STOPPED
) || !slen
) break;
3139 TRACE("new primary_mixpos=%ld, primary_advbase=%ld\n", dsb
->primary_mixpos
, primarybuf
->buf_mixpos
);
3140 TRACE("mixed data len=%ld, still_behind=%d\n", mixlen
-len
, still_behind
);
3141 /* return how far we think the primary buffer can
3142 * advance its underrun detector...*/
3143 if (still_behind
) return 0;
3144 if ((mixlen
- len
) < primary_done
) return 0;
3145 slen
= ((dsb
->primary_mixpos
< primarybuf
->buf_mixpos
) ?
3146 primarybuf
->buflen
: 0) + dsb
->primary_mixpos
-
3147 primarybuf
->buf_mixpos
;
3148 if (slen
> mixlen
) {
3149 /* the primary_done and still_behind checks above should have worked */
3150 FIXME("problem with advancement calculation (advlen=%ld > mixlen=%ld)\n", slen
, mixlen
);
3156 static DWORD
DSOUND_MixToPrimary(DWORD playpos
, DWORD writepos
, DWORD mixlen
, BOOL recover
)
3158 INT i
, len
, maxlen
= 0;
3159 IDirectSoundBufferImpl
*dsb
;
3161 TRACE("(%ld,%ld,%ld)\n", playpos
, writepos
, mixlen
);
3162 for (i
= dsound
->nrofbuffers
- 1; i
>= 0; i
--) {
3163 dsb
= dsound
->buffers
[i
];
3165 if (!dsb
|| !(ICOM_VTBL(dsb
)))
3167 if (dsb
->buflen
&& dsb
->state
&& !dsb
->hwbuf
) {
3168 TRACE("Checking %p, mixlen=%ld\n", dsb
, mixlen
);
3169 EnterCriticalSection(&(dsb
->lock
));
3170 if (dsb
->state
== STATE_STOPPING
) {
3171 DSOUND_MixCancel(dsb
, writepos
, TRUE
);
3172 dsb
->state
= STATE_STOPPED
;
3174 if ((dsb
->state
== STATE_STARTING
) || recover
)
3175 dsb
->primary_mixpos
= writepos
;
3176 len
= DSOUND_MixOne(dsb
, playpos
, writepos
, mixlen
);
3177 if (dsb
->state
== STATE_STARTING
)
3178 dsb
->state
= STATE_PLAYING
;
3179 maxlen
= (len
> maxlen
) ? len
: maxlen
;
3181 LeaveCriticalSection(&(dsb
->lock
));
3188 static void DSOUND_MixReset(DWORD writepos
)
3191 IDirectSoundBufferImpl
*dsb
;
3194 TRACE("(%ld)\n", writepos
);
3196 /* the sound of silence */
3197 nfiller
= primarybuf
->wfx
.wBitsPerSample
== 8 ? 128 : 0;
3199 /* reset all buffer mix positions */
3200 for (i
= dsound
->nrofbuffers
- 1; i
>= 0; i
--) {
3201 dsb
= dsound
->buffers
[i
];
3203 if (!dsb
|| !(ICOM_VTBL(dsb
)))
3205 if (dsb
->buflen
&& dsb
->state
&& !dsb
->hwbuf
) {
3206 TRACE("Resetting %p\n", dsb
);
3207 EnterCriticalSection(&(dsb
->lock
));
3208 if (dsb
->state
== STATE_STOPPING
) {
3209 dsb
->state
= STATE_STOPPED
;
3211 else if (dsb
->state
== STATE_STARTING
) {
3214 DSOUND_MixCancel(dsb
, writepos
, FALSE
);
3216 LeaveCriticalSection(&(dsb
->lock
));
3220 /* wipe out premixed data */
3221 if (primarybuf
->buf_mixpos
< writepos
) {
3222 memset(primarybuf
->buffer
+ writepos
, nfiller
, primarybuf
->buflen
- writepos
);
3223 memset(primarybuf
->buffer
, nfiller
, primarybuf
->buf_mixpos
);
3225 memset(primarybuf
->buffer
+ writepos
, nfiller
, primarybuf
->buf_mixpos
- writepos
);
3228 /* reset primary mix position */
3229 primarybuf
->buf_mixpos
= writepos
;
3232 static void DSOUND_CheckReset(IDirectSoundImpl
*dsound
, DWORD writepos
)
3234 if (primarybuf
->need_remix
) {
3235 DSOUND_MixReset(writepos
);
3236 primarybuf
->need_remix
= FALSE
;
3237 /* maximize Half-Life performance */
3238 dsound
->prebuf
= DS_SND_QUEUE_MIN
;
3240 /* if (dsound->prebuf < DS_SND_QUEUE_MAX) dsound->prebuf++; */
3242 TRACE("premix adjust: %d\n", dsound
->prebuf
);
3245 static void DSOUND_WaveQueue(IDirectSoundImpl
*dsound
, DWORD mixq
)
3247 if (mixq
+ dsound
->pwqueue
> DS_HEL_QUEUE
) mixq
= DS_HEL_QUEUE
- dsound
->pwqueue
;
3248 TRACE("queueing %ld buffers, starting at %d\n", mixq
, dsound
->pwwrite
);
3249 for (; mixq
; mixq
--) {
3250 waveOutWrite(dsound
->hwo
, dsound
->pwave
[dsound
->pwwrite
], sizeof(WAVEHDR
));
3252 if (dsound
->pwwrite
>= DS_HEL_FRAGS
) dsound
->pwwrite
= 0;
3257 /* #define SYNC_CALLBACK */
3259 static void DSOUND_PerformMix(void)
3265 EnterCriticalSection(&(dsound
->lock
));
3267 if (!primarybuf
|| !primarybuf
->ref
) {
3268 /* seems the primary buffer is currently being released */
3269 LeaveCriticalSection(&(dsound
->lock
));
3273 /* the sound of silence */
3274 nfiller
= primarybuf
->wfx
.wBitsPerSample
== 8 ? 128 : 0;
3276 /* whether the primary is forced to play even without secondary buffers */
3277 forced
= ((primarybuf
->state
== STATE_PLAYING
) || (primarybuf
->state
== STATE_STARTING
));
3279 TRACE("entering at %ld\n", GetTickCount());
3280 if (dsound
->priolevel
!= DSSCL_WRITEPRIMARY
) {
3281 BOOL paused
= ((primarybuf
->state
== STATE_STOPPED
) || (primarybuf
->state
== STATE_STARTING
));
3282 /* FIXME: document variables */
3283 DWORD playpos
, writepos
, inq
, maxq
, frag
;
3284 if (primarybuf
->hwbuf
) {
3285 hres
= IDsDriverBuffer_GetPosition(primarybuf
->hwbuf
, &playpos
, &writepos
);
3287 LeaveCriticalSection(&(dsound
->lock
));
3290 /* Well, we *could* do Just-In-Time mixing using the writepos,
3291 * but that's a little bit ambitious and unnecessary... */
3292 /* rather add our safety margin to the writepos, if we're playing */
3294 writepos
+= primarybuf
->writelead
;
3295 while (writepos
>= primarybuf
->buflen
)
3296 writepos
-= primarybuf
->buflen
;
3297 } else writepos
= playpos
;
3300 playpos
= dsound
->pwplay
* dsound
->fraglen
;
3303 writepos
+= DS_HEL_MARGIN
* dsound
->fraglen
;
3304 while (writepos
>= primarybuf
->buflen
)
3305 writepos
-= primarybuf
->buflen
;
3308 TRACE("primary playpos=%ld, writepos=%ld, clrpos=%ld, mixpos=%ld\n",
3309 playpos
,writepos
,primarybuf
->playpos
,primarybuf
->buf_mixpos
);
3310 /* wipe out just-played sound data */
3311 if (playpos
< primarybuf
->playpos
) {
3312 memset(primarybuf
->buffer
+ primarybuf
->playpos
, nfiller
, primarybuf
->buflen
- primarybuf
->playpos
);
3313 memset(primarybuf
->buffer
, nfiller
, playpos
);
3315 memset(primarybuf
->buffer
+ primarybuf
->playpos
, nfiller
, playpos
- primarybuf
->playpos
);
3317 primarybuf
->playpos
= playpos
;
3319 EnterCriticalSection(&(primarybuf
->lock
));
3321 /* reset mixing if necessary */
3322 DSOUND_CheckReset(dsound
, writepos
);
3324 /* check how much prebuffering is left */
3325 inq
= primarybuf
->buf_mixpos
;
3327 inq
+= primarybuf
->buflen
;
3330 /* find the maximum we can prebuffer */
3333 if (maxq
< writepos
)
3334 maxq
+= primarybuf
->buflen
;
3336 } else maxq
= primarybuf
->buflen
;
3338 /* clip maxq to dsound->prebuf */
3339 frag
= dsound
->prebuf
* dsound
->fraglen
;
3340 if (maxq
> frag
) maxq
= frag
;
3342 /* check for consistency */
3344 /* the playback position must have passed our last
3345 * mixed position, i.e. it's an underrun, or we have
3346 * nothing more to play */
3347 TRACE("reached end of mixed data (inq=%ld, maxq=%ld)\n", inq
, maxq
);
3349 /* stop the playback now, to allow buffers to refill */
3350 if (primarybuf
->state
== STATE_PLAYING
) {
3351 primarybuf
->state
= STATE_STARTING
;
3353 else if (primarybuf
->state
== STATE_STOPPING
) {
3354 primarybuf
->state
= STATE_STOPPED
;
3357 /* how can we have an underrun if we aren't playing? */
3358 WARN("unexpected primary state (%ld)\n", primarybuf
->state
);
3360 #ifdef SYNC_CALLBACK
3361 /* DSOUND_callback may need this lock */
3362 LeaveCriticalSection(&(primarybuf
->lock
));
3364 DSOUND_PrimaryStop(primarybuf
);
3365 #ifdef SYNC_CALLBACK
3366 EnterCriticalSection(&(primarybuf
->lock
));
3368 if (primarybuf
->hwbuf
) {
3369 /* the Stop is supposed to reset play position to beginning of buffer */
3370 /* unfortunately, OSS is not able to do so, so get current pointer */
3371 hres
= IDsDriverBuffer_GetPosition(primarybuf
->hwbuf
, &playpos
, NULL
);
3373 LeaveCriticalSection(&(dsound
->lock
));
3374 LeaveCriticalSection(&(primarybuf
->lock
));
3378 playpos
= dsound
->pwplay
* dsound
->fraglen
;
3381 primarybuf
->playpos
= playpos
;
3382 primarybuf
->buf_mixpos
= writepos
;
3384 maxq
= primarybuf
->buflen
;
3385 if (maxq
> frag
) maxq
= frag
;
3386 memset(primarybuf
->buffer
, nfiller
, primarybuf
->buflen
);
3391 frag
= DSOUND_MixToPrimary(playpos
, writepos
, maxq
, paused
);
3392 if (forced
) frag
= maxq
- inq
;
3393 primarybuf
->buf_mixpos
+= frag
;
3394 while (primarybuf
->buf_mixpos
>= primarybuf
->buflen
)
3395 primarybuf
->buf_mixpos
-= primarybuf
->buflen
;
3398 /* buffers have been filled, restart playback */
3399 if (primarybuf
->state
== STATE_STARTING
) {
3400 primarybuf
->state
= STATE_PLAYING
;
3402 else if (primarybuf
->state
== STATE_STOPPED
) {
3403 /* the primarybuf is supposed to play if there's something to play
3404 * even if it is reported as stopped, so don't let this confuse you */
3405 primarybuf
->state
= STATE_STOPPING
;
3407 LeaveCriticalSection(&(primarybuf
->lock
));
3409 DSOUND_PrimaryPlay(primarybuf
);
3410 TRACE("starting playback\n");
3414 LeaveCriticalSection(&(primarybuf
->lock
));
3416 /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
3417 if (primarybuf
->state
== STATE_STARTING
) {
3418 DSOUND_PrimaryPlay(primarybuf
);
3419 primarybuf
->state
= STATE_PLAYING
;
3421 else if (primarybuf
->state
== STATE_STOPPING
) {
3422 DSOUND_PrimaryStop(primarybuf
);
3423 primarybuf
->state
= STATE_STOPPED
;
3426 TRACE("completed processing at %ld\n", GetTickCount());
3427 LeaveCriticalSection(&(dsound
->lock
));
3430 static void CALLBACK
DSOUND_timer(UINT timerID
, UINT msg
, DWORD dwUser
, DWORD dw1
, DWORD dw2
)
3432 if (!dsound
|| !primarybuf
) {
3433 ERR("dsound died without killing us?\n");
3434 timeKillEvent(timerID
);
3435 timeEndPeriod(DS_TIME_RES
);
3440 DSOUND_PerformMix();
3443 static void CALLBACK
DSOUND_callback(HWAVEOUT hwo
, UINT msg
, DWORD dwUser
, DWORD dw1
, DWORD dw2
)
3445 IDirectSoundImpl
* This
= (IDirectSoundImpl
*)dwUser
;
3446 TRACE("entering at %ld, msg=%08x\n", GetTickCount(), msg
);
3447 if (msg
== MM_WOM_DONE
) {
3448 DWORD inq
, mixq
, fraglen
, buflen
, pwplay
, playpos
, mixpos
;
3449 if (This
->pwqueue
== (DWORD
)-1) {
3450 TRACE("completed due to reset\n");
3453 /* it could be a bad idea to enter critical section here... if there's lock contention,
3454 * the resulting scheduling delays might obstruct the winmm player thread */
3455 #ifdef SYNC_CALLBACK
3456 EnterCriticalSection(&(primarybuf
->lock
));
3458 /* retrieve current values */
3459 fraglen
= dsound
->fraglen
;
3460 buflen
= primarybuf
->buflen
;
3461 pwplay
= dsound
->pwplay
;
3462 playpos
= pwplay
* fraglen
;
3463 mixpos
= primarybuf
->buf_mixpos
;
3464 /* check remaining mixed data */
3465 inq
= ((mixpos
< playpos
) ? buflen
: 0) + mixpos
- playpos
;
3466 mixq
= inq
/ fraglen
;
3467 if ((inq
- (mixq
* fraglen
)) > 0) mixq
++;
3468 /* complete the playing buffer */
3469 TRACE("done playing primary pos=%ld\n", playpos
);
3471 if (pwplay
>= DS_HEL_FRAGS
) pwplay
= 0;
3472 /* write new values */
3473 dsound
->pwplay
= pwplay
;
3475 /* queue new buffer if we have data for it */
3476 if (inq
>1) DSOUND_WaveQueue(This
, inq
-1);
3477 #ifdef SYNC_CALLBACK
3478 LeaveCriticalSection(&(primarybuf
->lock
));
3481 TRACE("completed\n");
3484 /*******************************************************************************
3485 * DirectSoundCreate (DSOUND.1)
3487 HRESULT WINAPI
DirectSoundCreate(REFGUID lpGUID
,LPDIRECTSOUND
*ppDS
,IUnknown
*pUnkOuter
)
3489 IDirectSoundImpl
** ippDS
=(IDirectSoundImpl
**)ppDS
;
3490 PIDSDRIVER drv
= NULL
;
3493 HRESULT err
= DS_OK
;
3496 TRACE("(%p,%p,%p)\n",lpGUID
,ippDS
,pUnkOuter
);
3498 TRACE("DirectSoundCreate (%p)\n", ippDS
);
3501 return DSERR_INVALIDPARAM
;
3504 IDirectSound_AddRef((LPDIRECTSOUND
)dsound
);
3509 /* Enumerate WINMM audio devices and find the one we want */
3510 wodn
= waveOutGetNumDevs();
3511 if (!wodn
) return DSERR_NODRIVER
;
3513 /* FIXME: How do we find the GUID of an audio device? */
3514 /* Well, just use the first available device for now... */
3516 /* Get output device caps */
3517 waveOutGetDevCapsA(wod
, &wcaps
, sizeof(wcaps
));
3518 /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
3519 waveOutMessage(wod
, DRV_QUERYDSOUNDIFACE
, (DWORD
)&drv
, 0);
3521 /* Allocate memory */
3522 *ippDS
= (IDirectSoundImpl
*)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSoundImpl
));
3524 return DSERR_OUTOFMEMORY
;
3526 ICOM_VTBL(*ippDS
) = &dsvt
;
3529 (*ippDS
)->driver
= drv
;
3530 (*ippDS
)->fraglen
= 0;
3531 (*ippDS
)->priolevel
= DSSCL_NORMAL
;
3532 (*ippDS
)->nrofbuffers
= 0;
3533 (*ippDS
)->buffers
= NULL
;
3534 (*ippDS
)->primary
= NULL
;
3535 (*ippDS
)->listener
= NULL
;
3537 (*ippDS
)->prebuf
= DS_SND_QUEUE_MAX
;
3539 /* Get driver description */
3541 IDsDriver_GetDriverDesc(drv
,&((*ippDS
)->drvdesc
));
3543 /* if no DirectSound interface available, use WINMM API instead */
3544 (*ippDS
)->drvdesc
.dwFlags
= DSDDESC_DOMMSYSTEMOPEN
| DSDDESC_DOMMSYSTEMSETFORMAT
;
3545 (*ippDS
)->drvdesc
.dnDevNode
= wod
; /* FIXME? */
3548 /* Set default wave format (may need it for waveOutOpen) */
3549 (*ippDS
)->wfx
.wFormatTag
= WAVE_FORMAT_PCM
;
3550 (*ippDS
)->wfx
.nChannels
= 2;
3551 (*ippDS
)->wfx
.nSamplesPerSec
= 22050;
3552 (*ippDS
)->wfx
.nAvgBytesPerSec
= 44100;
3553 (*ippDS
)->wfx
.nBlockAlign
= 2;
3554 (*ippDS
)->wfx
.wBitsPerSample
= 8;
3556 /* If the driver requests being opened through MMSYSTEM
3557 * (which is recommended by the DDK), it is supposed to happen
3558 * before the DirectSound interface is opened */
3559 if ((*ippDS
)->drvdesc
.dwFlags
& DSDDESC_DOMMSYSTEMOPEN
) {
3560 /* FIXME: is this right? */
3561 err
= mmErr(waveOutOpen(&((*ippDS
)->hwo
), (*ippDS
)->drvdesc
.dnDevNode
, &((*ippDS
)->wfx
),
3562 (DWORD
)DSOUND_callback
, (DWORD
)(*ippDS
),
3563 CALLBACK_FUNCTION
| WAVE_DIRECTSOUND
));
3566 if (drv
&& (err
== DS_OK
))
3567 err
= IDsDriver_Open(drv
);
3569 /* FIXME: do we want to handle a temporarily busy device? */
3571 HeapFree(GetProcessHeap(),0,*ippDS
);
3576 /* the driver is now open, so it's now allowed to call GetCaps */
3578 IDsDriver_GetCaps(drv
,&((*ippDS
)->drvcaps
));
3582 /* FIXME: look at wcaps */
3583 (*ippDS
)->drvcaps
.dwFlags
=
3584 DSCAPS_PRIMARY16BIT
| DSCAPS_PRIMARYSTEREO
;
3586 (*ippDS
)->drvcaps
.dwFlags
|= DSCAPS_EMULDRIVER
;
3588 /* Allocate memory for HEL buffer headers */
3589 for (c
=0; c
<DS_HEL_FRAGS
; c
++) {
3590 (*ippDS
)->pwave
[c
] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(WAVEHDR
));
3591 if (!(*ippDS
)->pwave
[c
]) {
3592 /* Argh, out of memory */
3594 HeapFree(GetProcessHeap(),0,(*ippDS
)->pwave
[c
]);
3595 waveOutClose((*ippDS
)->hwo
);
3596 HeapFree(GetProcessHeap(),0,*ippDS
);
3598 return DSERR_OUTOFMEMORY
;
3604 InitializeCriticalSection(&((*ippDS
)->lock
));
3608 if (primarybuf
== NULL
) {
3612 dsbd
.dwSize
= sizeof(DSBUFFERDESC
);
3613 dsbd
.dwFlags
= DSBCAPS_PRIMARYBUFFER
;
3614 dsbd
.dwBufferBytes
= 0;
3615 dsbd
.lpwfxFormat
= &(dsound
->wfx
);
3616 hr
= IDirectSound_CreateSoundBuffer(*ppDS
, &dsbd
, (LPDIRECTSOUNDBUFFER
*)&primarybuf
, NULL
);
3620 /* dsound->primary is NULL - don't need to Release */
3621 dsound
->primary
= primarybuf
;
3622 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER
)primarybuf
);
3624 timeBeginPeriod(DS_TIME_RES
);
3625 dsound
->timerID
= timeSetEvent(DS_TIME_DEL
, DS_TIME_RES
, DSOUND_timer
,
3626 (DWORD
)dsound
, TIME_PERIODIC
| TIME_CALLBACK_FUNCTION
);
3631 /***************************************************************************
3632 * DirectSoundCaptureCreate [DSOUND.6]
3634 * Create and initialize a DirectSoundCapture interface
3638 * Failure: DSERR_NOAGGREGATION, DSERR_ALLOCATED, DSERR_INVALIDPARAM,
3641 HRESULT WINAPI
DirectSoundCaptureCreate(
3643 LPDIRECTSOUNDCAPTURE
* lplpDSC
,
3644 LPUNKNOWN pUnkOuter
)
3646 TRACE("(%s,%p,%p)\n", debugstr_guid(lpcGUID
), lplpDSC
, pUnkOuter
);
3649 return DSERR_NOAGGREGATION
;
3652 /* Default device? */
3654 return DSOUND_CreateDirectSoundCapture( (LPVOID
*)lplpDSC
);
3657 FIXME( "Unknown GUID %s\n", debugstr_guid(lpcGUID
) );
3660 return DSERR_OUTOFMEMORY
;
3663 /***************************************************************************
3664 * DirectSoundCaptureEnumerateA [DSOUND.7]
3666 * Enumerate all DirectSound drivers installed in the system
3670 * Failure: DSERR_INVALIDPARAM
3672 HRESULT WINAPI
DirectSoundCaptureEnumerateA(
3673 LPDSENUMCALLBACKA lpDSEnumCallback
,
3676 TRACE("(%p,%p)\n", lpDSEnumCallback
, lpContext
);
3678 if ( lpDSEnumCallback
)
3679 lpDSEnumCallback(NULL
,"WINE Primary Sound Capture Driver",
3680 "SoundCap",lpContext
);
3686 /***************************************************************************
3687 * DirectSoundCaptureEnumerateW [DSOUND.8]
3689 * Enumerate all DirectSound drivers installed in the system
3693 * Failure: DSERR_INVALIDPARAM
3695 HRESULT WINAPI
DirectSoundCaptureEnumerateW(
3696 LPDSENUMCALLBACKW lpDSEnumCallback
,
3699 FIXME("(%p,%p):stub\n", lpDSEnumCallback
, lpContext
);
3704 DSOUND_CreateDirectSoundCapture( LPVOID
* ppobj
)
3706 *ppobj
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof( IDirectSoundCaptureImpl
) );
3708 if ( *ppobj
== NULL
) {
3709 return DSERR_OUTOFMEMORY
;
3713 ICOM_THIS(IDirectSoundCaptureImpl
,*ppobj
);
3716 ICOM_VTBL(This
) = &dscvt
;
3718 InitializeCriticalSection( &This
->lock
);
3724 static HRESULT WINAPI
3725 IDirectSoundCaptureImpl_QueryInterface(
3726 LPDIRECTSOUNDCAPTURE iface
,
3730 ICOM_THIS(IDirectSoundCaptureImpl
,iface
);
3732 FIXME( "(%p)->(%s,%p): stub\n", This
, debugstr_guid(riid
), ppobj
);
3738 IDirectSoundCaptureImpl_AddRef( LPDIRECTSOUNDCAPTURE iface
)
3741 ICOM_THIS(IDirectSoundCaptureImpl
,iface
);
3743 EnterCriticalSection( &This
->lock
);
3745 TRACE( "(%p) was 0x%08lx\n", This
, This
->ref
);
3746 uRef
= ++(This
->ref
);
3748 LeaveCriticalSection( &This
->lock
);
3754 IDirectSoundCaptureImpl_Release( LPDIRECTSOUNDCAPTURE iface
)
3757 ICOM_THIS(IDirectSoundCaptureImpl
,iface
);
3759 EnterCriticalSection( &This
->lock
);
3761 TRACE( "(%p) was 0x%08lx\n", This
, This
->ref
);
3762 uRef
= --(This
->ref
);
3764 LeaveCriticalSection( &This
->lock
);
3767 DeleteCriticalSection( &This
->lock
);
3768 HeapFree( GetProcessHeap(), 0, This
);
3774 static HRESULT WINAPI
3775 IDirectSoundCaptureImpl_CreateCaptureBuffer(
3776 LPDIRECTSOUNDCAPTURE iface
,
3777 LPCDSCBUFFERDESC lpcDSCBufferDesc
,
3778 LPDIRECTSOUNDCAPTUREBUFFER
* lplpDSCaptureBuffer
,
3782 ICOM_THIS(IDirectSoundCaptureImpl
,iface
);
3784 TRACE( "(%p)->(%p,%p,%p)\n", This
, lpcDSCBufferDesc
, lplpDSCaptureBuffer
, pUnk
);
3787 return DSERR_INVALIDPARAM
;
3790 hr
= DSOUND_CreateDirectSoundCaptureBuffer( lpcDSCBufferDesc
, (LPVOID
*)lplpDSCaptureBuffer
);
3795 static HRESULT WINAPI
3796 IDirectSoundCaptureImpl_GetCaps(
3797 LPDIRECTSOUNDCAPTURE iface
,
3798 LPDSCCAPS lpDSCCaps
)
3800 ICOM_THIS(IDirectSoundCaptureImpl
,iface
);
3802 FIXME( "(%p)->(%p): stub\n", This
, lpDSCCaps
);
3807 static HRESULT WINAPI
3808 IDirectSoundCaptureImpl_Initialize(
3809 LPDIRECTSOUNDCAPTURE iface
,
3812 ICOM_THIS(IDirectSoundCaptureImpl
,iface
);
3814 FIXME( "(%p)->(%p): stub\n", This
, lpcGUID
);
3820 static ICOM_VTABLE(IDirectSoundCapture
) dscvt
=
3822 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3823 /* IUnknown methods */
3824 IDirectSoundCaptureImpl_QueryInterface
,
3825 IDirectSoundCaptureImpl_AddRef
,
3826 IDirectSoundCaptureImpl_Release
,
3828 /* IDirectSoundCapture methods */
3829 IDirectSoundCaptureImpl_CreateCaptureBuffer
,
3830 IDirectSoundCaptureImpl_GetCaps
,
3831 IDirectSoundCaptureImpl_Initialize
3835 DSOUND_CreateDirectSoundCaptureBuffer( LPCDSCBUFFERDESC lpcDSCBufferDesc
, LPVOID
* ppobj
)
3838 FIXME( "(%p,%p): ignoring lpcDSCBufferDesc\n", lpcDSCBufferDesc
, ppobj
);
3840 *ppobj
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof( IDirectSoundCaptureBufferImpl
) );
3842 if ( *ppobj
== NULL
) {
3843 return DSERR_OUTOFMEMORY
;
3847 ICOM_THIS(IDirectSoundCaptureBufferImpl
,*ppobj
);
3850 ICOM_VTBL(This
) = &dscbvt
;
3852 InitializeCriticalSection( &This
->lock
);
3859 static HRESULT WINAPI
3860 IDirectSoundCaptureBufferImpl_QueryInterface(
3861 LPDIRECTSOUNDCAPTUREBUFFER iface
,
3865 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
3867 FIXME( "(%p)->(%s,%p): stub\n", This
, debugstr_guid(riid
), ppobj
);
3873 IDirectSoundCaptureBufferImpl_AddRef( LPDIRECTSOUNDCAPTUREBUFFER iface
)
3876 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
3878 EnterCriticalSection( &This
->lock
);
3880 TRACE( "(%p) was 0x%08lx\n", This
, This
->ref
);
3881 uRef
= ++(This
->ref
);
3883 LeaveCriticalSection( &This
->lock
);
3889 IDirectSoundCaptureBufferImpl_Release( LPDIRECTSOUNDCAPTUREBUFFER iface
)
3892 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
3894 EnterCriticalSection( &This
->lock
);
3896 TRACE( "(%p) was 0x%08lx\n", This
, This
->ref
);
3897 uRef
= --(This
->ref
);
3899 LeaveCriticalSection( &This
->lock
);
3902 DeleteCriticalSection( &This
->lock
);
3903 HeapFree( GetProcessHeap(), 0, This
);
3909 static HRESULT WINAPI
3910 IDirectSoundCaptureBufferImpl_GetCaps(
3911 LPDIRECTSOUNDCAPTUREBUFFER iface
,
3912 LPDSCBCAPS lpDSCBCaps
)
3914 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
3916 FIXME( "(%p)->(%p): stub\n", This
, lpDSCBCaps
);
3921 static HRESULT WINAPI
3922 IDirectSoundCaptureBufferImpl_GetCurrentPosition(
3923 LPDIRECTSOUNDCAPTUREBUFFER iface
,
3924 LPDWORD lpdwCapturePosition
,
3925 LPDWORD lpdwReadPosition
)
3927 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
3929 FIXME( "(%p)->(%p,%p): stub\n", This
, lpdwCapturePosition
, lpdwReadPosition
);
3934 static HRESULT WINAPI
3935 IDirectSoundCaptureBufferImpl_GetFormat(
3936 LPDIRECTSOUNDCAPTUREBUFFER iface
,
3937 LPWAVEFORMATEX lpwfxFormat
,
3938 DWORD dwSizeAllocated
,
3939 LPDWORD lpdwSizeWritten
)
3941 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
3943 FIXME( "(%p)->(%p,0x%08lx,%p): stub\n", This
, lpwfxFormat
, dwSizeAllocated
, lpdwSizeWritten
);
3948 static HRESULT WINAPI
3949 IDirectSoundCaptureBufferImpl_GetStatus(
3950 LPDIRECTSOUNDCAPTUREBUFFER iface
,
3951 LPDWORD lpdwStatus
)
3953 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
3955 FIXME( "(%p)->(%p): stub\n", This
, lpdwStatus
);
3960 static HRESULT WINAPI
3961 IDirectSoundCaptureBufferImpl_Initialize(
3962 LPDIRECTSOUNDCAPTUREBUFFER iface
,
3963 LPDIRECTSOUNDCAPTURE lpDSC
,
3964 LPCDSCBUFFERDESC lpcDSCBDesc
)
3966 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
3968 FIXME( "(%p)->(%p,%p): stub\n", This
, lpDSC
, lpcDSCBDesc
);
3973 static HRESULT WINAPI
3974 IDirectSoundCaptureBufferImpl_Lock(
3975 LPDIRECTSOUNDCAPTUREBUFFER iface
,
3978 LPVOID
* lplpvAudioPtr1
,
3979 LPDWORD lpdwAudioBytes1
,
3980 LPVOID
* lplpvAudioPtr2
,
3981 LPDWORD lpdwAudioBytes2
,
3984 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
3986 FIXME( "(%p)->(%08lu,%08lu,%p,%p,%p,%p,0x%08lx): stub\n", This
, dwReadCusor
, dwReadBytes
, lplpvAudioPtr1
, lpdwAudioBytes1
, lplpvAudioPtr2
, lpdwAudioBytes2
, dwFlags
);
3991 static HRESULT WINAPI
3992 IDirectSoundCaptureBufferImpl_Start(
3993 LPDIRECTSOUNDCAPTUREBUFFER iface
,
3996 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
3998 FIXME( "(%p)->(0x%08lx): stub\n", This
, dwFlags
);
4003 static HRESULT WINAPI
4004 IDirectSoundCaptureBufferImpl_Stop( LPDIRECTSOUNDCAPTUREBUFFER iface
)
4006 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
4008 FIXME( "(%p): stub\n", This
);
4013 static HRESULT WINAPI
4014 IDirectSoundCaptureBufferImpl_Unlock(
4015 LPDIRECTSOUNDCAPTUREBUFFER iface
,
4016 LPVOID lpvAudioPtr1
,
4017 DWORD dwAudioBytes1
,
4018 LPVOID lpvAudioPtr2
,
4019 DWORD dwAudioBytes2
)
4021 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
4023 FIXME( "(%p)->(%p,%08lu,%p,%08lu): stub\n", This
, lpvAudioPtr1
, dwAudioBytes1
, lpvAudioPtr2
, dwAudioBytes2
);
4029 static ICOM_VTABLE(IDirectSoundCaptureBuffer
) dscbvt
=
4031 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
4032 /* IUnknown methods */
4033 IDirectSoundCaptureBufferImpl_QueryInterface
,
4034 IDirectSoundCaptureBufferImpl_AddRef
,
4035 IDirectSoundCaptureBufferImpl_Release
,
4037 /* IDirectSoundCaptureBuffer methods */
4038 IDirectSoundCaptureBufferImpl_GetCaps
,
4039 IDirectSoundCaptureBufferImpl_GetCurrentPosition
,
4040 IDirectSoundCaptureBufferImpl_GetFormat
,
4041 IDirectSoundCaptureBufferImpl_GetStatus
,
4042 IDirectSoundCaptureBufferImpl_Initialize
,
4043 IDirectSoundCaptureBufferImpl_Lock
,
4044 IDirectSoundCaptureBufferImpl_Start
,
4045 IDirectSoundCaptureBufferImpl_Stop
,
4046 IDirectSoundCaptureBufferImpl_Unlock
4049 /*******************************************************************************
4050 * DirectSound ClassFactory
4054 /* IUnknown fields */
4055 ICOM_VFIELD(IClassFactory
);
4057 } IClassFactoryImpl
;
4059 static HRESULT WINAPI
4060 DSCF_QueryInterface(LPCLASSFACTORY iface
,REFIID riid
,LPVOID
*ppobj
) {
4061 ICOM_THIS(IClassFactoryImpl
,iface
);
4063 FIXME("(%p)->(%s,%p),stub!\n",This
,debugstr_guid(riid
),ppobj
);
4064 return E_NOINTERFACE
;
4068 DSCF_AddRef(LPCLASSFACTORY iface
) {
4069 ICOM_THIS(IClassFactoryImpl
,iface
);
4070 return ++(This
->ref
);
4073 static ULONG WINAPI
DSCF_Release(LPCLASSFACTORY iface
) {
4074 ICOM_THIS(IClassFactoryImpl
,iface
);
4075 /* static class, won't be freed */
4076 return --(This
->ref
);
4079 static HRESULT WINAPI
DSCF_CreateInstance(
4080 LPCLASSFACTORY iface
,LPUNKNOWN pOuter
,REFIID riid
,LPVOID
*ppobj
4082 ICOM_THIS(IClassFactoryImpl
,iface
);
4084 TRACE("(%p)->(%p,%s,%p)\n",This
,pOuter
,debugstr_guid(riid
),ppobj
);
4085 if ( IsEqualGUID( &IID_IDirectSound
, riid
) ) {
4086 /* FIXME: reuse already created dsound if present? */
4087 return DirectSoundCreate(riid
,(LPDIRECTSOUND
*)ppobj
,pOuter
);
4089 return E_NOINTERFACE
;
4092 static HRESULT WINAPI
DSCF_LockServer(LPCLASSFACTORY iface
,BOOL dolock
) {
4093 ICOM_THIS(IClassFactoryImpl
,iface
);
4094 FIXME("(%p)->(%d),stub!\n",This
,dolock
);
4098 static ICOM_VTABLE(IClassFactory
) DSCF_Vtbl
= {
4099 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
4100 DSCF_QueryInterface
,
4103 DSCF_CreateInstance
,
4106 static IClassFactoryImpl DSOUND_CF
= {&DSCF_Vtbl
, 1 };
4108 /*******************************************************************************
4109 * DllGetClassObject [DSOUND.5]
4110 * Retrieves class object from a DLL object
4113 * Docs say returns STDAPI
4116 * rclsid [I] CLSID for the class object
4117 * riid [I] Reference to identifier of interface for class object
4118 * ppv [O] Address of variable to receive interface pointer for riid
4122 * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
4125 DWORD WINAPI
DSOUND_DllGetClassObject(REFCLSID rclsid
,REFIID riid
,LPVOID
*ppv
)
4127 TRACE("(%p,%p,%p)\n", debugstr_guid(rclsid
), debugstr_guid(riid
), ppv
);
4128 if ( IsEqualCLSID( &IID_IClassFactory
, riid
) ) {
4129 *ppv
= (LPVOID
)&DSOUND_CF
;
4130 IClassFactory_AddRef((IClassFactory
*)*ppv
);
4134 FIXME("(%p,%p,%p): no interface found.\n", debugstr_guid(rclsid
), debugstr_guid(riid
), ppv
);
4135 return CLASS_E_CLASSNOTAVAILABLE
;
4139 /*******************************************************************************
4140 * DllCanUnloadNow [DSOUND.4] Determines whether the DLL is in use.
4146 DWORD WINAPI
DSOUND_DllCanUnloadNow(void)
4148 FIXME("(void): stub\n");