3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998 Rob Riggs
5 * Copyright 2000-2001 TransGaming Technologies, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * Most thread locking is complete. There may be a few race
23 * conditions still lurking.
25 * Tested with a Soundblaster clone, a Gravis UltraSound Classic,
26 * and a Turtle Beach Tropez+.
29 * Implement DirectSoundCapture API
30 * Implement SetCooperativeLevel properly (need to address focus issues)
31 * Implement DirectSound3DBuffers (stubs in place)
32 * Use hardware 3D support if available
33 * Add critical section locking inside Release and AddRef methods
34 * Handle static buffers - put those in hardware, non-static not in hardware
35 * Hardware DuplicateSoundBuffer
36 * Proper volume calculation, and setting volume in HEL primary buffer
37 * Optimize WINMM and negotiate fragment size, decrease DS_HEL_MARGIN
43 #include <sys/types.h>
44 #include <sys/fcntl.h>
48 #include <math.h> /* Insomnia - pow() function */
57 #include "wine/windef16.h"
58 #include "wine/debug.h"
62 WINE_DEFAULT_DEBUG_CHANNEL(dsound
);
64 /* these are eligible for tuning... they must be high on slow machines... */
65 /* some stuff may get more responsive with lower values though... */
66 #define DS_EMULDRIVER 1 /* some games (Quake 2, UT) refuse to accept
67 emulated dsound devices. set to 0 ! */
68 #define DS_HEL_FRAGS 48 /* HEL only: number of waveOut fragments in primary buffer
69 * (changing this won't help you) */
70 #define DS_HEL_MARGIN 5 /* HEL only: number of waveOut fragments ahead to mix in new buffers
71 * (keep this close or equal to DS_HEL_QUEUE for best results) */
72 #define DS_HEL_QUEUE 5 /* HEL only: number of waveOut fragments ahead to queue to driver
73 * (this will affect HEL sound reliability and latency) */
75 #define DS_SND_QUEUE_MAX 28 /* max number of fragments to prebuffer */
76 #define DS_SND_QUEUE_MIN 12 /* min number of fragments to prebuffer */
78 /* Linux does not support better timing than 10ms */
79 #define DS_TIME_RES 10 /* Resolution of multimedia timer */
80 #define DS_TIME_DEL 10 /* Delay of multimedia timer callback, and duration of HEL fragment */
82 /*****************************************************************************
83 * Predeclare the interface implementation structures
85 typedef struct IDirectSoundImpl IDirectSoundImpl
;
86 typedef struct IDirectSoundBufferImpl IDirectSoundBufferImpl
;
87 typedef struct IDirectSoundNotifyImpl IDirectSoundNotifyImpl
;
88 typedef struct IDirectSound3DListenerImpl IDirectSound3DListenerImpl
;
89 typedef struct IDirectSound3DBufferImpl IDirectSound3DBufferImpl
;
90 typedef struct IDirectSoundCaptureImpl IDirectSoundCaptureImpl
;
91 typedef struct IDirectSoundCaptureBufferImpl IDirectSoundCaptureBufferImpl
;
92 typedef struct IKsPropertySetImpl IKsPropertySetImpl
;
94 /*****************************************************************************
95 * IDirectSound implementation structure
97 struct IDirectSoundImpl
100 ICOM_VFIELD(IDirectSound
);
102 /* IDirectSoundImpl fields */
104 DSDRIVERDESC drvdesc
;
105 DSDRIVERCAPS drvcaps
;
107 LPWAVEHDR pwave
[DS_HEL_FRAGS
];
108 UINT timerID
, pwplay
, pwwrite
, pwqueue
, prebuf
;
112 IDirectSoundBufferImpl
** buffers
;
113 IDirectSoundBufferImpl
* primary
;
114 IDirectSound3DListenerImpl
* listener
;
115 WAVEFORMATEX wfx
; /* current main waveformat */
116 CRITICAL_SECTION lock
;
119 /*****************************************************************************
120 * IDirectSoundBuffer implementation structure
122 struct IDirectSoundBufferImpl
124 /* FIXME: document */
125 /* IUnknown fields */
126 ICOM_VFIELD(IDirectSoundBuffer
);
128 /* IDirectSoundBufferImpl fields */
129 PIDSDRIVERBUFFER hwbuf
;
132 IDirectSound3DBufferImpl
* ds3db
;
133 DWORD playflags
,state
,leadin
;
134 DWORD playpos
,startpos
,writelead
,buflen
;
135 DWORD nAvgBytesPerSec
;
138 IDirectSoundBufferImpl
* parent
; /* for duplicates */
139 IDirectSoundImpl
* dsound
;
141 LPDSBPOSITIONNOTIFY notifies
;
143 CRITICAL_SECTION lock
;
144 /* used for frequency conversion (PerfectPitch) */
145 ULONG freqAdjust
, freqAcc
;
146 /* used for intelligent (well, sort of) prebuffering */
147 DWORD probably_valid_to
;
148 DWORD primary_mixpos
, buf_mixpos
;
152 #define STATE_STOPPED 0
153 #define STATE_STARTING 1
154 #define STATE_PLAYING 2
155 #define STATE_STOPPING 3
157 /*****************************************************************************
158 * IDirectSoundNotify implementation structure
160 struct IDirectSoundNotifyImpl
162 /* IUnknown fields */
163 ICOM_VFIELD(IDirectSoundNotify
);
165 /* IDirectSoundNotifyImpl fields */
166 IDirectSoundBufferImpl
* dsb
;
169 /*****************************************************************************
170 * IDirectSound3DListener implementation structure
172 struct IDirectSound3DListenerImpl
174 /* IUnknown fields */
175 ICOM_VFIELD(IDirectSound3DListener
);
177 /* IDirectSound3DListenerImpl fields */
178 IDirectSoundBufferImpl
* dsb
;
180 CRITICAL_SECTION lock
;
183 struct IKsPropertySetImpl
185 /* IUnknown fields */
186 ICOM_VFIELD(IKsPropertySet
);
188 /* IKsPropertySetImpl fields */
189 IDirectSound3DBufferImpl
*ds3db
; /* backptr, no ref */
192 /*****************************************************************************
193 * IDirectSound3DBuffer implementation structure
195 struct IDirectSound3DBufferImpl
197 /* IUnknown fields */
198 ICOM_VFIELD(IDirectSound3DBuffer
);
200 /* IDirectSound3DBufferImpl fields */
201 IDirectSoundBufferImpl
* dsb
;
205 CRITICAL_SECTION lock
;
206 IKsPropertySetImpl
* iks
;
210 /*****************************************************************************
211 * IDirectSoundCapture implementation structure
213 struct IDirectSoundCaptureImpl
215 /* IUnknown fields */
216 ICOM_VFIELD(IDirectSoundCapture
);
219 /* IDirectSoundCaptureImpl fields */
220 CRITICAL_SECTION lock
;
223 /*****************************************************************************
224 * IDirectSoundCapture implementation structure
226 struct IDirectSoundCaptureBufferImpl
228 /* IUnknown fields */
229 ICOM_VFIELD(IDirectSoundCaptureBuffer
);
232 /* IDirectSoundCaptureBufferImpl fields */
233 CRITICAL_SECTION lock
;
237 /* #define USE_DSOUND3D 1 */
239 #define DSOUND_FREQSHIFT (14)
241 static IDirectSoundImpl
* dsound
= NULL
;
243 static IDirectSoundBufferImpl
* primarybuf
= NULL
;
245 static void DSOUND_CheckEvent(IDirectSoundBufferImpl
*dsb
, int len
);
246 static void DSOUND_MixCancelAt(IDirectSoundBufferImpl
*dsb
, DWORD buf_writepos
);
248 static void DSOUND_WaveQueue(IDirectSoundImpl
*dsound
, DWORD mixq
);
249 static void DSOUND_PerformMix(void);
250 static void CALLBACK
DSOUND_callback(HWAVEOUT hwo
, UINT msg
, DWORD dwUser
, DWORD dw1
, DWORD dw2
);
252 static HRESULT
DSOUND_CreateDirectSoundCapture( LPVOID
* ppobj
);
253 static HRESULT
DSOUND_CreateDirectSoundCaptureBuffer( LPCDSCBUFFERDESC lpcDSCBufferDesc
, LPVOID
* ppobj
);
255 static ICOM_VTABLE(IDirectSoundCapture
) dscvt
;
256 static ICOM_VTABLE(IDirectSoundCaptureBuffer
) dscbvt
;
258 static HRESULT
mmErr(UINT err
)
261 case MMSYSERR_NOERROR
:
263 case MMSYSERR_ALLOCATED
:
264 return DSERR_ALLOCATED
;
265 case MMSYSERR_INVALHANDLE
:
266 return DSERR_GENERIC
; /* FIXME */
267 case MMSYSERR_NODRIVER
:
268 return DSERR_NODRIVER
;
270 return DSERR_OUTOFMEMORY
;
271 case MMSYSERR_INVALPARAM
:
272 return DSERR_INVALIDPARAM
;
274 FIXME("Unknown MMSYS error %d\n",err
);
275 return DSERR_GENERIC
;
279 /***************************************************************************
280 * DirectSoundEnumerateA [DSOUND.2]
282 * Enumerate all DirectSound drivers installed in the system
286 * Failure: DSERR_INVALIDPARAM
288 HRESULT WINAPI
DirectSoundEnumerateA(
289 LPDSENUMCALLBACKA lpDSEnumCallback
,
292 TRACE("lpDSEnumCallback = %p, lpContext = %p\n",
293 lpDSEnumCallback
, lpContext
);
296 if (lpDSEnumCallback
!= NULL
)
297 lpDSEnumCallback(NULL
,"WINE DirectSound using Open Sound System",
304 /***************************************************************************
305 * DirectSoundEnumerateW [DSOUND.3]
307 * Enumerate all DirectSound drivers installed in the system
311 * Failure: DSERR_INVALIDPARAM
313 HRESULT WINAPI
DirectSoundEnumerateW(
314 LPDSENUMCALLBACKW lpDSEnumCallback
,
317 FIXME("lpDSEnumCallback = %p, lpContext = %p: stub\n",
318 lpDSEnumCallback
, lpContext
);
324 static void _dump_DSBCAPS(DWORD xmask
) {
329 #define FE(x) { x, #x },
330 FE(DSBCAPS_PRIMARYBUFFER
)
332 FE(DSBCAPS_LOCHARDWARE
)
333 FE(DSBCAPS_LOCSOFTWARE
)
335 FE(DSBCAPS_CTRLFREQUENCY
)
337 FE(DSBCAPS_CTRLVOLUME
)
338 FE(DSBCAPS_CTRLPOSITIONNOTIFY
)
339 FE(DSBCAPS_CTRLDEFAULT
)
341 FE(DSBCAPS_STICKYFOCUS
)
342 FE(DSBCAPS_GLOBALFOCUS
)
343 FE(DSBCAPS_GETCURRENTPOSITION2
)
344 FE(DSBCAPS_MUTE3DATMAXDISTANCE
)
349 for (i
=0;i
<sizeof(flags
)/sizeof(flags
[0]);i
++)
350 if ((flags
[i
].mask
& xmask
) == flags
[i
].mask
)
351 DPRINTF("%s ",flags
[i
].name
);
354 /*******************************************************************************
358 /* IUnknown methods */
360 static HRESULT WINAPI
IKsPropertySetImpl_QueryInterface(
361 LPKSPROPERTYSET iface
, REFIID riid
, LPVOID
*ppobj
363 ICOM_THIS(IKsPropertySetImpl
,iface
);
365 FIXME("(%p,%s,%p), stub!\n",This
,debugstr_guid(riid
),ppobj
);
371 static ULONG WINAPI
IKsPropertySetImpl_AddRef(LPKSPROPERTYSET iface
) {
372 ICOM_THIS(IKsPropertySetImpl
,iface
);
380 static ULONG WINAPI
IKsPropertySetImpl_Release(LPKSPROPERTYSET iface
) {
381 ICOM_THIS(IKsPropertySetImpl
,iface
);
389 static HRESULT WINAPI
IKsPropertySetImpl_Get(LPKSPROPERTYSET iface
,
390 REFGUID guidPropSet
, ULONG dwPropID
,
391 LPVOID pInstanceData
, ULONG cbInstanceData
,
392 LPVOID pPropData
, ULONG cbPropData
,
395 ICOM_THIS(IKsPropertySetImpl
,iface
);
397 FIXME("(%p,%s,%ld,%p,%ld,%p,%ld,%p), stub!\n",This
,debugstr_guid(guidPropSet
),dwPropID
,pInstanceData
,cbInstanceData
,pPropData
,cbPropData
,pcbReturned
);
398 return E_PROP_ID_UNSUPPORTED
;
403 static HRESULT WINAPI
IKsPropertySetImpl_Set(LPKSPROPERTYSET iface
,
404 REFGUID guidPropSet
, ULONG dwPropID
,
405 LPVOID pInstanceData
, ULONG cbInstanceData
,
406 LPVOID pPropData
, ULONG cbPropData
408 ICOM_THIS(IKsPropertySetImpl
,iface
);
410 FIXME("(%p,%s,%ld,%p,%ld,%p,%ld), stub!\n",This
,debugstr_guid(guidPropSet
),dwPropID
,pInstanceData
,cbInstanceData
,pPropData
,cbPropData
);
411 return E_PROP_ID_UNSUPPORTED
;
416 static HRESULT WINAPI
IKsPropertySetImpl_QuerySupport(LPKSPROPERTYSET iface
,
417 REFGUID guidPropSet
, ULONG dwPropID
, PULONG pTypeSupport
419 ICOM_THIS(IKsPropertySetImpl
,iface
);
421 FIXME("(%p,%s,%ld,%p), stub!\n",This
,debugstr_guid(guidPropSet
),dwPropID
,pTypeSupport
);
422 return E_PROP_ID_UNSUPPORTED
;
427 static ICOM_VTABLE(IKsPropertySet
) iksvt
= {
428 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
429 IKsPropertySetImpl_QueryInterface
,
430 IKsPropertySetImpl_AddRef
,
431 IKsPropertySetImpl_Release
,
432 IKsPropertySetImpl_Get
,
433 IKsPropertySetImpl_Set
,
434 IKsPropertySetImpl_QuerySupport
438 /*******************************************************************************
439 * IDirectSound3DBuffer
442 /* IUnknown methods */
444 static HRESULT WINAPI
IDirectSound3DBufferImpl_QueryInterface(
445 LPDIRECTSOUND3DBUFFER iface
, REFIID riid
, LPVOID
*ppobj
)
447 ICOM_THIS(IDirectSound3DBufferImpl
,iface
);
449 if ( IsEqualGUID( &IID_IKsPropertySet
, riid
) ) {
450 IDirectSound3DBuffer_AddRef(iface
);
455 FIXME("(%p,%s,%p), no such interface.\n",This
,debugstr_guid(riid
),ppobj
);
461 static ULONG WINAPI
IDirectSound3DBufferImpl_AddRef(LPDIRECTSOUND3DBUFFER iface
)
463 ICOM_THIS(IDirectSound3DBufferImpl
,iface
);
470 static ULONG WINAPI
IDirectSound3DBufferImpl_Release(LPDIRECTSOUND3DBUFFER iface
)
472 ICOM_THIS(IDirectSound3DBufferImpl
,iface
);
474 TRACE("(%p) ref was %ld\n", This
, This
->ref
);
480 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER
)This
->dsb
);
482 DeleteCriticalSection(&This
->lock
);
484 HeapFree(GetProcessHeap(),0,This
->buffer
);
485 HeapFree(GetProcessHeap(),0,This
);
491 /* IDirectSound3DBuffer methods */
493 static HRESULT WINAPI
IDirectSound3DBufferImpl_GetAllParameters(
494 LPDIRECTSOUND3DBUFFER iface
,
495 LPDS3DBUFFER lpDs3dBuffer
)
503 static HRESULT WINAPI
IDirectSound3DBufferImpl_GetConeAngles(
504 LPDIRECTSOUND3DBUFFER iface
,
505 LPDWORD lpdwInsideConeAngle
,
506 LPDWORD lpdwOutsideConeAngle
)
514 static HRESULT WINAPI
IDirectSound3DBufferImpl_GetConeOrientation(
515 LPDIRECTSOUND3DBUFFER iface
,
516 LPD3DVECTOR lpvConeOrientation
)
524 static HRESULT WINAPI
IDirectSound3DBufferImpl_GetConeOutsideVolume(
525 LPDIRECTSOUND3DBUFFER iface
,
526 LPLONG lplConeOutsideVolume
)
534 static HRESULT WINAPI
IDirectSound3DBufferImpl_GetMaxDistance(
535 LPDIRECTSOUND3DBUFFER iface
,
536 LPD3DVALUE lpfMaxDistance
)
544 static HRESULT WINAPI
IDirectSound3DBufferImpl_GetMinDistance(
545 LPDIRECTSOUND3DBUFFER iface
,
546 LPD3DVALUE lpfMinDistance
)
554 static HRESULT WINAPI
IDirectSound3DBufferImpl_GetMode(
555 LPDIRECTSOUND3DBUFFER iface
,
564 static HRESULT WINAPI
IDirectSound3DBufferImpl_GetPosition(
565 LPDIRECTSOUND3DBUFFER iface
,
566 LPD3DVECTOR lpvPosition
)
574 static HRESULT WINAPI
IDirectSound3DBufferImpl_GetVelocity(
575 LPDIRECTSOUND3DBUFFER iface
,
576 LPD3DVECTOR lpvVelocity
)
584 static HRESULT WINAPI
IDirectSound3DBufferImpl_SetAllParameters(
585 LPDIRECTSOUND3DBUFFER iface
,
586 LPCDS3DBUFFER lpcDs3dBuffer
,
595 static HRESULT WINAPI
IDirectSound3DBufferImpl_SetConeAngles(
596 LPDIRECTSOUND3DBUFFER iface
,
597 DWORD dwInsideConeAngle
,
598 DWORD dwOutsideConeAngle
,
607 static HRESULT WINAPI
IDirectSound3DBufferImpl_SetConeOrientation(
608 LPDIRECTSOUND3DBUFFER iface
,
609 D3DVALUE x
, D3DVALUE y
, D3DVALUE z
,
618 static HRESULT WINAPI
IDirectSound3DBufferImpl_SetConeOutsideVolume(
619 LPDIRECTSOUND3DBUFFER iface
,
620 LONG lConeOutsideVolume
,
629 static HRESULT WINAPI
IDirectSound3DBufferImpl_SetMaxDistance(
630 LPDIRECTSOUND3DBUFFER iface
,
631 D3DVALUE fMaxDistance
,
640 static HRESULT WINAPI
IDirectSound3DBufferImpl_SetMinDistance(
641 LPDIRECTSOUND3DBUFFER iface
,
642 D3DVALUE fMinDistance
,
651 static HRESULT WINAPI
IDirectSound3DBufferImpl_SetMode(
652 LPDIRECTSOUND3DBUFFER iface
,
656 ICOM_THIS(IDirectSound3DBufferImpl
,iface
);
657 TRACE("mode = %lx\n", dwMode
);
658 This
->ds3db
.dwMode
= dwMode
;
664 static HRESULT WINAPI
IDirectSound3DBufferImpl_SetPosition(
665 LPDIRECTSOUND3DBUFFER iface
,
666 D3DVALUE x
, D3DVALUE y
, D3DVALUE z
,
675 static HRESULT WINAPI
IDirectSound3DBufferImpl_SetVelocity(
676 LPDIRECTSOUND3DBUFFER iface
,
677 D3DVALUE x
, D3DVALUE y
, D3DVALUE z
,
686 static ICOM_VTABLE(IDirectSound3DBuffer
) ds3dbvt
=
688 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
689 /* IUnknown methods */
690 IDirectSound3DBufferImpl_QueryInterface
,
691 IDirectSound3DBufferImpl_AddRef
,
692 IDirectSound3DBufferImpl_Release
,
693 /* IDirectSound3DBuffer methods */
694 IDirectSound3DBufferImpl_GetAllParameters
,
695 IDirectSound3DBufferImpl_GetConeAngles
,
696 IDirectSound3DBufferImpl_GetConeOrientation
,
697 IDirectSound3DBufferImpl_GetConeOutsideVolume
,
698 IDirectSound3DBufferImpl_GetMaxDistance
,
699 IDirectSound3DBufferImpl_GetMinDistance
,
700 IDirectSound3DBufferImpl_GetMode
,
701 IDirectSound3DBufferImpl_GetPosition
,
702 IDirectSound3DBufferImpl_GetVelocity
,
703 IDirectSound3DBufferImpl_SetAllParameters
,
704 IDirectSound3DBufferImpl_SetConeAngles
,
705 IDirectSound3DBufferImpl_SetConeOrientation
,
706 IDirectSound3DBufferImpl_SetConeOutsideVolume
,
707 IDirectSound3DBufferImpl_SetMaxDistance
,
708 IDirectSound3DBufferImpl_SetMinDistance
,
709 IDirectSound3DBufferImpl_SetMode
,
710 IDirectSound3DBufferImpl_SetPosition
,
711 IDirectSound3DBufferImpl_SetVelocity
,
716 static int DSOUND_Create3DBuffer(IDirectSoundBufferImpl
* dsb
)
718 DWORD i
, temp
, iSize
, oSize
, offset
;
719 LPBYTE bIbuf
, bObuf
, bTbuf
= NULL
;
720 LPWORD wIbuf
, wObuf
, wTbuf
= NULL
;
722 /* Inside DirectX says it's stupid but allowed */
723 if (dsb
->wfx
.nChannels
== 2) {
724 /* Convert to mono */
725 if (dsb
->wfx
.wBitsPerSample
== 16) {
726 iSize
= dsb
->buflen
/ 4;
727 wTbuf
= malloc(dsb
->buflen
/ 2);
729 return DSERR_OUTOFMEMORY
;
730 for (i
= 0; i
< iSize
; i
++)
731 wTbuf
[i
] = (dsb
->buffer
[i
* 2] + dsb
->buffer
[(i
* 2) + 1]) / 2;
734 iSize
= dsb
->buflen
/ 2;
735 bTbuf
= malloc(dsb
->buflen
/ 2);
737 return DSERR_OUTOFMEMORY
;
738 for (i
= 0; i
< iSize
; i
++)
739 bTbuf
[i
] = (dsb
->buffer
[i
* 2] + dsb
->buffer
[(i
* 2) + 1]) / 2;
743 if (dsb
->wfx
.wBitsPerSample
== 16) {
744 iSize
= dsb
->buflen
/ 2;
745 wIbuf
= (LPWORD
) dsb
->buffer
;
747 bIbuf
= (LPBYTE
) dsb
->buffer
;
752 if (primarybuf
->wfx
.wBitsPerSample
== 16) {
753 wObuf
= (LPWORD
) dsb
->ds3db
->buffer
;
754 oSize
= dsb
->ds3db
->buflen
/ 2;
756 bObuf
= (LPBYTE
) dsb
->ds3db
->buffer
;
757 oSize
= dsb
->ds3db
->buflen
;
760 offset
= primarybuf
->wfx
.nSamplesPerSec
/ 100; /* 10ms */
761 if (primarybuf
->wfx
.wBitsPerSample
== 16 && dsb
->wfx
.wBitsPerSample
== 16)
762 for (i
= 0; i
< iSize
; i
++) {
765 temp
+= wIbuf
[i
- offset
] >> 9;
767 temp
+= wIbuf
[i
+ iSize
- offset
] >> 9;
769 wObuf
[(i
* 2) + 1] = temp
;
771 else if (primarybuf
->wfx
.wBitsPerSample
== 8 && dsb
->wfx
.wBitsPerSample
== 8)
772 for (i
= 0; i
< iSize
; i
++) {
775 temp
+= bIbuf
[i
- offset
] >> 5;
777 temp
+= bIbuf
[i
+ iSize
- offset
] >> 5;
779 bObuf
[(i
* 2) + 1] = temp
;
790 /*******************************************************************************
791 * IDirectSound3DListener
794 /* IUnknown methods */
795 static HRESULT WINAPI
IDirectSound3DListenerImpl_QueryInterface(
796 LPDIRECTSOUND3DLISTENER iface
, REFIID riid
, LPVOID
*ppobj
)
798 ICOM_THIS(IDirectSound3DListenerImpl
,iface
);
800 TRACE("(%p,%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
804 static ULONG WINAPI
IDirectSound3DListenerImpl_AddRef(LPDIRECTSOUND3DLISTENER iface
)
806 ICOM_THIS(IDirectSound3DListenerImpl
,iface
);
811 static ULONG WINAPI
IDirectSound3DListenerImpl_Release(LPDIRECTSOUND3DLISTENER iface
)
814 ICOM_THIS(IDirectSound3DListenerImpl
,iface
);
816 TRACE("(%p) ref was %ld\n", This
, This
->ref
);
818 ulReturn
= --This
->ref
;
820 /* Free all resources */
821 if( ulReturn
== 0 ) {
823 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER
)This
->dsb
);
824 DeleteCriticalSection(&This
->lock
);
825 HeapFree(GetProcessHeap(),0,This
);
831 /* IDirectSound3DListener methods */
832 static HRESULT WINAPI
IDirectSound3DListenerImpl_GetAllParameter(
833 LPDIRECTSOUND3DLISTENER iface
,
834 LPDS3DLISTENER lpDS3DL
)
840 static HRESULT WINAPI
IDirectSound3DListenerImpl_GetDistanceFactor(
841 LPDIRECTSOUND3DLISTENER iface
,
842 LPD3DVALUE lpfDistanceFactor
)
848 static HRESULT WINAPI
IDirectSound3DListenerImpl_GetDopplerFactor(
849 LPDIRECTSOUND3DLISTENER iface
,
850 LPD3DVALUE lpfDopplerFactor
)
856 static HRESULT WINAPI
IDirectSound3DListenerImpl_GetOrientation(
857 LPDIRECTSOUND3DLISTENER iface
,
858 LPD3DVECTOR lpvOrientFront
,
859 LPD3DVECTOR lpvOrientTop
)
865 static HRESULT WINAPI
IDirectSound3DListenerImpl_GetPosition(
866 LPDIRECTSOUND3DLISTENER iface
,
867 LPD3DVECTOR lpvPosition
)
873 static HRESULT WINAPI
IDirectSound3DListenerImpl_GetRolloffFactor(
874 LPDIRECTSOUND3DLISTENER iface
,
875 LPD3DVALUE lpfRolloffFactor
)
881 static HRESULT WINAPI
IDirectSound3DListenerImpl_GetVelocity(
882 LPDIRECTSOUND3DLISTENER iface
,
883 LPD3DVECTOR lpvVelocity
)
889 static HRESULT WINAPI
IDirectSound3DListenerImpl_SetAllParameters(
890 LPDIRECTSOUND3DLISTENER iface
,
891 LPCDS3DLISTENER lpcDS3DL
,
898 static HRESULT WINAPI
IDirectSound3DListenerImpl_SetDistanceFactor(
899 LPDIRECTSOUND3DLISTENER iface
,
900 D3DVALUE fDistanceFactor
,
907 static HRESULT WINAPI
IDirectSound3DListenerImpl_SetDopplerFactor(
908 LPDIRECTSOUND3DLISTENER iface
,
909 D3DVALUE fDopplerFactor
,
916 static HRESULT WINAPI
IDirectSound3DListenerImpl_SetOrientation(
917 LPDIRECTSOUND3DLISTENER iface
,
918 D3DVALUE xFront
, D3DVALUE yFront
, D3DVALUE zFront
,
919 D3DVALUE xTop
, D3DVALUE yTop
, D3DVALUE zTop
,
926 static HRESULT WINAPI
IDirectSound3DListenerImpl_SetPosition(
927 LPDIRECTSOUND3DLISTENER iface
,
928 D3DVALUE x
, D3DVALUE y
, D3DVALUE z
,
935 static HRESULT WINAPI
IDirectSound3DListenerImpl_SetRolloffFactor(
936 LPDIRECTSOUND3DLISTENER iface
,
937 D3DVALUE fRolloffFactor
,
944 static HRESULT WINAPI
IDirectSound3DListenerImpl_SetVelocity(
945 LPDIRECTSOUND3DLISTENER iface
,
946 D3DVALUE x
, D3DVALUE y
, D3DVALUE z
,
953 static HRESULT WINAPI
IDirectSound3DListenerImpl_CommitDeferredSettings(
954 LPDIRECTSOUND3DLISTENER iface
)
961 static ICOM_VTABLE(IDirectSound3DListener
) ds3dlvt
=
963 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
964 /* IUnknown methods */
965 IDirectSound3DListenerImpl_QueryInterface
,
966 IDirectSound3DListenerImpl_AddRef
,
967 IDirectSound3DListenerImpl_Release
,
968 /* IDirectSound3DListener methods */
969 IDirectSound3DListenerImpl_GetAllParameter
,
970 IDirectSound3DListenerImpl_GetDistanceFactor
,
971 IDirectSound3DListenerImpl_GetDopplerFactor
,
972 IDirectSound3DListenerImpl_GetOrientation
,
973 IDirectSound3DListenerImpl_GetPosition
,
974 IDirectSound3DListenerImpl_GetRolloffFactor
,
975 IDirectSound3DListenerImpl_GetVelocity
,
976 IDirectSound3DListenerImpl_SetAllParameters
,
977 IDirectSound3DListenerImpl_SetDistanceFactor
,
978 IDirectSound3DListenerImpl_SetDopplerFactor
,
979 IDirectSound3DListenerImpl_SetOrientation
,
980 IDirectSound3DListenerImpl_SetPosition
,
981 IDirectSound3DListenerImpl_SetRolloffFactor
,
982 IDirectSound3DListenerImpl_SetVelocity
,
983 IDirectSound3DListenerImpl_CommitDeferredSettings
,
986 /*******************************************************************************
989 static HRESULT WINAPI
IDirectSoundNotifyImpl_QueryInterface(
990 LPDIRECTSOUNDNOTIFY iface
,REFIID riid
,LPVOID
*ppobj
992 ICOM_THIS(IDirectSoundNotifyImpl
,iface
);
994 TRACE("(%p,%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
998 static ULONG WINAPI
IDirectSoundNotifyImpl_AddRef(LPDIRECTSOUNDNOTIFY iface
) {
999 ICOM_THIS(IDirectSoundNotifyImpl
,iface
);
1000 return ++(This
->ref
);
1003 static ULONG WINAPI
IDirectSoundNotifyImpl_Release(LPDIRECTSOUNDNOTIFY iface
) {
1004 ICOM_THIS(IDirectSoundNotifyImpl
,iface
);
1006 TRACE("(%p) ref was %ld\n", This
, This
->ref
);
1010 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER
)This
->dsb
);
1011 HeapFree(GetProcessHeap(),0,This
);
1017 static HRESULT WINAPI
IDirectSoundNotifyImpl_SetNotificationPositions(
1018 LPDIRECTSOUNDNOTIFY iface
,DWORD howmuch
,LPCDSBPOSITIONNOTIFY notify
1020 ICOM_THIS(IDirectSoundNotifyImpl
,iface
);
1023 if (TRACE_ON(dsound
)) {
1024 TRACE("(%p,0x%08lx,%p)\n",This
,howmuch
,notify
);
1025 for (i
=0;i
<howmuch
;i
++)
1026 TRACE("notify at %ld to 0x%08lx\n",
1027 notify
[i
].dwOffset
,(DWORD
)notify
[i
].hEventNotify
);
1029 This
->dsb
->notifies
= HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,This
->dsb
->notifies
,(This
->dsb
->nrofnotifies
+howmuch
)*sizeof(DSBPOSITIONNOTIFY
));
1030 memcpy( This
->dsb
->notifies
+This
->dsb
->nrofnotifies
,
1032 howmuch
*sizeof(DSBPOSITIONNOTIFY
)
1034 This
->dsb
->nrofnotifies
+=howmuch
;
1039 static ICOM_VTABLE(IDirectSoundNotify
) dsnvt
=
1041 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1042 IDirectSoundNotifyImpl_QueryInterface
,
1043 IDirectSoundNotifyImpl_AddRef
,
1044 IDirectSoundNotifyImpl_Release
,
1045 IDirectSoundNotifyImpl_SetNotificationPositions
,
1048 /*******************************************************************************
1049 * IDirectSoundBuffer
1052 static void DSOUND_RecalcVolPan(PDSVOLUMEPAN volpan
)
1056 /* the AmpFactors are expressed in 16.16 fixed point */
1057 volpan
->dwVolAmpFactor
= (ULONG
) (pow(2.0, volpan
->lVolume
/ 600.0) * 65536);
1058 /* FIXME: dwPan{Left|Right}AmpFactor */
1060 /* FIXME: use calculated vol and pan ampfactors */
1061 temp
= (double) (volpan
->lVolume
- (volpan
->lPan
> 0 ? volpan
->lPan
: 0));
1062 volpan
->dwTotalLeftAmpFactor
= (ULONG
) (pow(2.0, temp
/ 600.0) * 65536);
1063 temp
= (double) (volpan
->lVolume
+ (volpan
->lPan
< 0 ? volpan
->lPan
: 0));
1064 volpan
->dwTotalRightAmpFactor
= (ULONG
) (pow(2.0, temp
/ 600.0) * 65536);
1066 TRACE("left = %lx, right = %lx\n", volpan
->dwTotalLeftAmpFactor
, volpan
->dwTotalRightAmpFactor
);
1069 static void DSOUND_RecalcFormat(IDirectSoundBufferImpl
*dsb
)
1073 sw
= dsb
->wfx
.nChannels
* (dsb
->wfx
.wBitsPerSample
/ 8);
1074 if ((dsb
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
) && dsb
->hwbuf
) {
1076 /* let fragment size approximate the timer delay */
1077 fraglen
= (dsb
->freq
* DS_TIME_DEL
/ 1000) * sw
;
1078 /* reduce fragment size until an integer number of them fits in the buffer */
1079 /* (FIXME: this may or may not be a good idea) */
1080 while (dsb
->buflen
% fraglen
) fraglen
-= sw
;
1081 dsb
->dsound
->fraglen
= fraglen
;
1082 TRACE("fraglen=%ld\n", dsb
->dsound
->fraglen
);
1084 /* calculate the 10ms write lead */
1085 dsb
->writelead
= (dsb
->freq
/ 100) * sw
;
1088 static HRESULT
DSOUND_PrimaryOpen(IDirectSoundBufferImpl
*dsb
)
1090 HRESULT err
= DS_OK
;
1092 /* are we using waveOut stuff? */
1096 HRESULT merr
= DS_OK
;
1097 /* Start in pause mode, to allow buffers to get filled */
1098 waveOutPause(dsb
->dsound
->hwo
);
1099 if (dsb
->state
== STATE_PLAYING
) dsb
->state
= STATE_STARTING
;
1100 else if (dsb
->state
== STATE_STOPPING
) dsb
->state
= STATE_STOPPED
;
1101 /* use fragments of 10ms (1/100s) each (which should get us within
1102 * the documented write cursor lead of 10-15ms) */
1103 buflen
= ((dsb
->wfx
.nAvgBytesPerSec
/ 100) & ~3) * DS_HEL_FRAGS
;
1104 TRACE("desired buflen=%ld, old buffer=%p\n", buflen
, dsb
->buffer
);
1105 /* reallocate emulated primary buffer */
1106 newbuf
= (LPBYTE
)HeapReAlloc(GetProcessHeap(),0,dsb
->buffer
,buflen
);
1107 if (newbuf
== NULL
) {
1108 ERR("failed to allocate primary buffer\n");
1109 merr
= DSERR_OUTOFMEMORY
;
1110 /* but the old buffer might still exists and must be re-prepared */
1112 dsb
->buffer
= newbuf
;
1113 dsb
->buflen
= buflen
;
1117 IDirectSoundImpl
*ds
= dsb
->dsound
;
1119 ds
->fraglen
= dsb
->buflen
/ DS_HEL_FRAGS
;
1121 /* prepare fragment headers */
1122 for (c
=0; c
<DS_HEL_FRAGS
; c
++) {
1123 ds
->pwave
[c
]->lpData
= dsb
->buffer
+ c
*ds
->fraglen
;
1124 ds
->pwave
[c
]->dwBufferLength
= ds
->fraglen
;
1125 ds
->pwave
[c
]->dwUser
= (DWORD
)dsb
;
1126 ds
->pwave
[c
]->dwFlags
= 0;
1127 ds
->pwave
[c
]->dwLoops
= 0;
1128 err
= mmErr(waveOutPrepareHeader(ds
->hwo
,ds
->pwave
[c
],sizeof(WAVEHDR
)));
1131 waveOutUnprepareHeader(ds
->hwo
,ds
->pwave
[c
],sizeof(WAVEHDR
));
1139 memset(dsb
->buffer
, (dsb
->wfx
.wBitsPerSample
== 16) ? 0 : 128, dsb
->buflen
);
1140 TRACE("fraglen=%ld\n", ds
->fraglen
);
1141 DSOUND_WaveQueue(dsb
->dsound
, (DWORD
)-1);
1143 if ((err
== DS_OK
) && (merr
!= DS_OK
))
1150 static void DSOUND_PrimaryClose(IDirectSoundBufferImpl
*dsb
)
1152 /* are we using waveOut stuff? */
1155 IDirectSoundImpl
*ds
= dsb
->dsound
;
1157 ds
->pwqueue
= (DWORD
)-1; /* resetting queues */
1158 waveOutReset(ds
->hwo
);
1159 for (c
=0; c
<DS_HEL_FRAGS
; c
++)
1160 waveOutUnprepareHeader(ds
->hwo
, ds
->pwave
[c
], sizeof(WAVEHDR
));
1165 static HRESULT
DSOUND_PrimaryPlay(IDirectSoundBufferImpl
*dsb
)
1167 HRESULT err
= DS_OK
;
1169 err
= IDsDriverBuffer_Play(dsb
->hwbuf
, 0, 0, DSBPLAY_LOOPING
);
1171 err
= mmErr(waveOutRestart(dsb
->dsound
->hwo
));
1175 static HRESULT
DSOUND_PrimaryStop(IDirectSoundBufferImpl
*dsb
)
1177 HRESULT err
= DS_OK
;
1179 err
= IDsDriverBuffer_Stop(dsb
->hwbuf
);
1180 if (err
== DSERR_BUFFERLOST
) {
1181 /* Wine-only: the driver wants us to reopen the device */
1182 /* FIXME: check for errors */
1183 IDsDriverBuffer_Release(primarybuf
->hwbuf
);
1184 waveOutClose(dsb
->dsound
->hwo
);
1185 dsb
->dsound
->hwo
= 0;
1186 waveOutOpen(&(dsb
->dsound
->hwo
), dsb
->dsound
->drvdesc
.dnDevNode
,
1187 &(primarybuf
->wfx
), (DWORD
)DSOUND_callback
, (DWORD
)dsb
->dsound
,
1188 CALLBACK_FUNCTION
| WAVE_DIRECTSOUND
);
1189 err
= IDsDriver_CreateSoundBuffer(dsb
->dsound
->driver
,&(dsb
->wfx
),dsb
->dsbd
.dwFlags
,0,
1190 &(dsb
->buflen
),&(dsb
->buffer
),
1191 (LPVOID
)&(dsb
->hwbuf
));
1195 err
= mmErr(waveOutPause(dsb
->dsound
->hwo
));
1199 /* This sets this format for the <em>Primary Buffer Only</em> */
1200 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
1201 static HRESULT WINAPI
IDirectSoundBufferImpl_SetFormat(
1202 LPDIRECTSOUNDBUFFER iface
,LPWAVEFORMATEX wfex
1204 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1205 IDirectSoundBufferImpl
** dsb
;
1206 HRESULT err
= DS_OK
;
1209 /* Let's be pedantic! */
1210 if ((wfex
== NULL
) ||
1211 (wfex
->wFormatTag
!= WAVE_FORMAT_PCM
) ||
1212 (wfex
->nChannels
< 1) || (wfex
->nChannels
> 2) ||
1213 (wfex
->nSamplesPerSec
< 1) ||
1214 (wfex
->nBlockAlign
< 1) || (wfex
->nChannels
> 4) ||
1215 ((wfex
->wBitsPerSample
!= 8) && (wfex
->wBitsPerSample
!= 16))) {
1216 TRACE("failed pedantic check!\n");
1217 return DSERR_INVALIDPARAM
;
1221 EnterCriticalSection(&(This
->dsound
->lock
));
1223 if (primarybuf
->wfx
.nSamplesPerSec
!= wfex
->nSamplesPerSec
) {
1224 dsb
= dsound
->buffers
;
1225 for (i
= 0; i
< dsound
->nrofbuffers
; i
++, dsb
++) {
1227 EnterCriticalSection(&((*dsb
)->lock
));
1229 (*dsb
)->freqAdjust
= ((*dsb
)->freq
<< DSOUND_FREQSHIFT
) /
1230 wfex
->nSamplesPerSec
;
1232 LeaveCriticalSection(&((*dsb
)->lock
));
1237 memcpy(&(primarybuf
->wfx
), wfex
, sizeof(primarybuf
->wfx
));
1239 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
1240 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1241 wfex
->wFormatTag
, wfex
->nChannels
, wfex
->nSamplesPerSec
,
1242 wfex
->nAvgBytesPerSec
, wfex
->nBlockAlign
,
1243 wfex
->wBitsPerSample
, wfex
->cbSize
);
1245 primarybuf
->wfx
.nAvgBytesPerSec
=
1246 This
->wfx
.nSamplesPerSec
* This
->wfx
.nBlockAlign
;
1248 if (primarybuf
->dsound
->drvdesc
.dwFlags
& DSDDESC_DOMMSYSTEMSETFORMAT
) {
1249 /* FIXME: check for errors */
1250 DSOUND_PrimaryClose(primarybuf
);
1251 waveOutClose(This
->dsound
->hwo
);
1252 This
->dsound
->hwo
= 0;
1253 waveOutOpen(&(This
->dsound
->hwo
), This
->dsound
->drvdesc
.dnDevNode
,
1254 &(primarybuf
->wfx
), (DWORD
)DSOUND_callback
, (DWORD
)This
->dsound
,
1255 CALLBACK_FUNCTION
| WAVE_DIRECTSOUND
);
1256 DSOUND_PrimaryOpen(primarybuf
);
1258 if (primarybuf
->hwbuf
) {
1259 err
= IDsDriverBuffer_SetFormat(primarybuf
->hwbuf
, &(primarybuf
->wfx
));
1260 if (err
== DSERR_BUFFERLOST
) {
1261 /* Wine-only: the driver wants us to recreate the HW buffer */
1262 IDsDriverBuffer_Release(primarybuf
->hwbuf
);
1263 err
= IDsDriver_CreateSoundBuffer(primarybuf
->dsound
->driver
,&(primarybuf
->wfx
),primarybuf
->dsbd
.dwFlags
,0,
1264 &(primarybuf
->buflen
),&(primarybuf
->buffer
),
1265 (LPVOID
)&(primarybuf
->hwbuf
));
1266 if (primarybuf
->state
== STATE_PLAYING
) primarybuf
->state
= STATE_STARTING
;
1267 else if (primarybuf
->state
== STATE_STOPPING
) primarybuf
->state
= STATE_STOPPED
;
1270 DSOUND_RecalcFormat(primarybuf
);
1272 LeaveCriticalSection(&(This
->dsound
->lock
));
1278 static HRESULT WINAPI
IDirectSoundBufferImpl_SetVolume(
1279 LPDIRECTSOUNDBUFFER iface
,LONG vol
1281 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1283 TRACE("(%p,%ld)\n",This
,vol
);
1285 /* I'm not sure if we need this for primary buffer */
1286 if (!(This
->dsbd
.dwFlags
& DSBCAPS_CTRLVOLUME
))
1287 return DSERR_CONTROLUNAVAIL
;
1289 if ((vol
> DSBVOLUME_MAX
) || (vol
< DSBVOLUME_MIN
))
1290 return DSERR_INVALIDPARAM
;
1293 EnterCriticalSection(&(This
->lock
));
1295 This
->volpan
.lVolume
= vol
;
1297 DSOUND_RecalcVolPan(&(This
->volpan
));
1300 IDsDriverBuffer_SetVolumePan(This
->hwbuf
, &(This
->volpan
));
1302 else if (This
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
) {
1303 #if 0 /* should we really do this? */
1304 /* the DS volume ranges from 0 (max, 0dB attenuation) to -10000 (min, 100dB attenuation) */
1305 /* the MM volume ranges from 0 to 0xffff in an unspecified logarithmic scale */
1306 WORD cvol
= 0xffff + vol
*6 + vol
/2;
1307 DWORD vol
= cvol
| ((DWORD
)cvol
<< 16)
1308 waveOutSetVolume(This
->dsound
->hwo
, vol
);
1312 LeaveCriticalSection(&(This
->lock
));
1318 static HRESULT WINAPI
IDirectSoundBufferImpl_GetVolume(
1319 LPDIRECTSOUNDBUFFER iface
,LPLONG vol
1321 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1322 TRACE("(%p,%p)\n",This
,vol
);
1325 return DSERR_INVALIDPARAM
;
1327 *vol
= This
->volpan
.lVolume
;
1331 static HRESULT WINAPI
IDirectSoundBufferImpl_SetFrequency(
1332 LPDIRECTSOUNDBUFFER iface
,DWORD freq
1334 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1335 TRACE("(%p,%ld)\n",This
,freq
);
1337 /* You cannot set the frequency of the primary buffer */
1338 if (!(This
->dsbd
.dwFlags
& DSBCAPS_CTRLFREQUENCY
) ||
1339 (This
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
))
1340 return DSERR_CONTROLUNAVAIL
;
1342 if (!freq
) freq
= This
->wfx
.nSamplesPerSec
;
1344 if ((freq
< DSBFREQUENCY_MIN
) || (freq
> DSBFREQUENCY_MAX
))
1345 return DSERR_INVALIDPARAM
;
1348 EnterCriticalSection(&(This
->lock
));
1351 This
->freqAdjust
= (freq
<< DSOUND_FREQSHIFT
) / primarybuf
->wfx
.nSamplesPerSec
;
1352 This
->nAvgBytesPerSec
= freq
* This
->wfx
.nBlockAlign
;
1353 DSOUND_RecalcFormat(This
);
1355 LeaveCriticalSection(&(This
->lock
));
1361 static HRESULT WINAPI
IDirectSoundBufferImpl_Play(
1362 LPDIRECTSOUNDBUFFER iface
,DWORD reserved1
,DWORD reserved2
,DWORD flags
1364 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1365 TRACE("(%p,%08lx,%08lx,%08lx)\n",
1366 This
,reserved1
,reserved2
,flags
1370 EnterCriticalSection(&(This
->lock
));
1372 This
->playflags
= flags
;
1373 if (This
->state
== STATE_STOPPED
) {
1374 This
->leadin
= TRUE
;
1375 This
->startpos
= This
->buf_mixpos
;
1376 This
->state
= STATE_STARTING
;
1377 } else if (This
->state
== STATE_STOPPING
)
1378 This
->state
= STATE_PLAYING
;
1379 if (!(This
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
) && This
->hwbuf
) {
1380 IDsDriverBuffer_Play(This
->hwbuf
, 0, 0, This
->playflags
);
1381 This
->state
= STATE_PLAYING
;
1384 LeaveCriticalSection(&(This
->lock
));
1390 static HRESULT WINAPI
IDirectSoundBufferImpl_Stop(LPDIRECTSOUNDBUFFER iface
)
1392 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1393 TRACE("(%p)\n",This
);
1396 EnterCriticalSection(&(This
->lock
));
1398 if (This
->state
== STATE_PLAYING
)
1399 This
->state
= STATE_STOPPING
;
1400 else if (This
->state
== STATE_STARTING
)
1401 This
->state
= STATE_STOPPED
;
1402 if (!(This
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
) && This
->hwbuf
) {
1403 IDsDriverBuffer_Stop(This
->hwbuf
);
1404 This
->state
= STATE_STOPPED
;
1406 DSOUND_CheckEvent(This
, 0);
1408 LeaveCriticalSection(&(This
->lock
));
1414 static DWORD WINAPI
IDirectSoundBufferImpl_AddRef(LPDIRECTSOUNDBUFFER iface
) {
1415 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1418 TRACE("(%p) ref was %ld, thread is %lx\n",This
, This
->ref
, GetCurrentThreadId());
1420 ref
= InterlockedIncrement(&(This
->ref
));
1422 FIXME("thread-safety alert! AddRef-ing with a zero refcount!\n");
1426 static DWORD WINAPI
IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER iface
) {
1427 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1431 TRACE("(%p) ref was %ld, thread is %lx\n",This
, This
->ref
, GetCurrentThreadId());
1433 ref
= InterlockedDecrement(&(This
->ref
));
1434 if (ref
) return ref
;
1436 EnterCriticalSection(&(This
->dsound
->lock
));
1437 for (i
=0;i
<This
->dsound
->nrofbuffers
;i
++)
1438 if (This
->dsound
->buffers
[i
] == This
)
1441 if (i
< This
->dsound
->nrofbuffers
) {
1442 /* Put the last buffer of the list in the (now empty) position */
1443 This
->dsound
->buffers
[i
] = This
->dsound
->buffers
[This
->dsound
->nrofbuffers
- 1];
1444 This
->dsound
->nrofbuffers
--;
1445 This
->dsound
->buffers
= HeapReAlloc(GetProcessHeap(),0,This
->dsound
->buffers
,sizeof(LPDIRECTSOUNDBUFFER
)*This
->dsound
->nrofbuffers
);
1446 TRACE("buffer count is now %d\n", This
->dsound
->nrofbuffers
);
1447 IDirectSound_Release((LPDIRECTSOUND
)This
->dsound
);
1449 LeaveCriticalSection(&(This
->dsound
->lock
));
1451 DeleteCriticalSection(&(This
->lock
));
1452 if (This
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
)
1453 DSOUND_PrimaryClose(This
);
1455 IDsDriverBuffer_Release(This
->hwbuf
);
1458 IDirectSound3DBuffer_Release((LPDIRECTSOUND3DBUFFER
)This
->ds3db
);
1460 /* this is a duplicate buffer */
1461 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER
)This
->parent
);
1463 /* this is a toplevel buffer */
1464 HeapFree(GetProcessHeap(),0,This
->buffer
);
1466 HeapFree(GetProcessHeap(),0,This
);
1468 if (This
== primarybuf
)
1474 static DWORD
DSOUND_CalcPlayPosition(IDirectSoundBufferImpl
*This
,
1475 DWORD state
, DWORD pplay
, DWORD pwrite
, DWORD pmix
, DWORD bmix
)
1479 TRACE("primary playpos=%ld, mixpos=%ld\n", pplay
, pmix
);
1480 TRACE("this mixpos=%ld, time=%ld\n", bmix
, GetTickCount());
1482 /* the actual primary play position (pplay) is always behind last mixed (pmix),
1483 * unless the computer is too slow or something */
1484 /* we need to know how far away we are from there */
1485 #if 0 /* we'll never fill the primary entirely */
1486 if (pmix
== pplay
) {
1487 if ((state
== STATE_PLAYING
) || (state
== STATE_STOPPING
)) {
1488 /* wow, the software mixer is really doing well,
1489 * seems the entire primary buffer is filled! */
1490 pmix
+= primarybuf
->buflen
;
1492 /* else: the primary buffer is not playing, so probably empty */
1495 if (pmix
< pplay
) pmix
+= primarybuf
->buflen
; /* wraparound */
1497 /* detect buffer underrun */
1498 if (pwrite
< pplay
) pwrite
+= primarybuf
->buflen
; /* wraparound */
1500 if (pmix
> (DS_SND_QUEUE_MAX
* primarybuf
->dsound
->fraglen
+ pwrite
+ primarybuf
->writelead
)) {
1501 WARN("detected an underrun: primary queue was %ld\n",pmix
);
1504 /* divide the offset by its sample size */
1505 pmix
/= primarybuf
->wfx
.nBlockAlign
;
1506 TRACE("primary back-samples=%ld\n",pmix
);
1507 /* adjust for our frequency */
1508 pmix
= (pmix
* This
->freqAdjust
) >> DSOUND_FREQSHIFT
;
1509 /* multiply by our own sample size */
1510 pmix
*= This
->wfx
.nBlockAlign
;
1511 TRACE("this back-offset=%ld\n", pmix
);
1512 /* subtract from our last mixed position */
1514 while (bplay
< pmix
) bplay
+= This
->buflen
; /* wraparound */
1516 if (This
->leadin
&& ((bplay
< This
->startpos
) || (bplay
> bmix
))) {
1517 /* seems we haven't started playing yet */
1518 TRACE("this still in lead-in phase\n");
1519 bplay
= This
->startpos
;
1521 /* return the result */
1525 static HRESULT WINAPI
IDirectSoundBufferImpl_GetCurrentPosition(
1526 LPDIRECTSOUNDBUFFER iface
,LPDWORD playpos
,LPDWORD writepos
1529 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1530 TRACE("(%p,%p,%p)\n",This
,playpos
,writepos
);
1532 hres
=IDsDriverBuffer_GetPosition(This
->hwbuf
,playpos
,writepos
);
1536 else if (This
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
) {
1539 mtime
.wType
= TIME_BYTES
;
1540 waveOutGetPosition(This
->dsound
->hwo
, &mtime
, sizeof(mtime
));
1541 mtime
.u
.cb
= mtime
.u
.cb
% This
->buflen
;
1542 *playpos
= mtime
.u
.cb
;
1545 /* the writepos should only be used by apps with WRITEPRIMARY priority,
1546 * in which case our software mixer is disabled anyway */
1547 *writepos
= (This
->dsound
->pwplay
+ DS_HEL_MARGIN
) * This
->dsound
->fraglen
;
1548 while (*writepos
>= This
->buflen
)
1549 *writepos
-= This
->buflen
;
1552 if (playpos
&& (This
->state
!= STATE_PLAYING
)) {
1553 /* we haven't been merged into the primary buffer (yet) */
1554 *playpos
= This
->buf_mixpos
;
1557 DWORD pplay
, pwrite
, lplay
, splay
, pstate
;
1558 /* let's get this exact; first, recursively call GetPosition on the primary */
1559 EnterCriticalSection(&(primarybuf
->lock
));
1560 IDirectSoundBufferImpl_GetCurrentPosition((LPDIRECTSOUNDBUFFER
)primarybuf
, &pplay
, &pwrite
);
1561 /* detect HEL mode underrun */
1562 pstate
= primarybuf
->state
;
1563 if (!(primarybuf
->hwbuf
|| primarybuf
->dsound
->pwqueue
)) {
1564 TRACE("detected an underrun\n");
1566 if (pstate
== STATE_PLAYING
)
1567 pstate
= STATE_STARTING
;
1568 else if (pstate
== STATE_STOPPING
)
1569 pstate
= STATE_STOPPED
;
1571 /* get data for ourselves while we still have the lock */
1572 pstate
&= This
->state
;
1573 lplay
= This
->primary_mixpos
;
1574 splay
= This
->buf_mixpos
;
1575 if ((This
->dsbd
.dwFlags
& DSBCAPS_GETCURRENTPOSITION2
) || primarybuf
->hwbuf
) {
1576 /* calculate play position using this */
1577 *playpos
= DSOUND_CalcPlayPosition(This
, pstate
, pplay
, pwrite
, lplay
, splay
);
1579 /* (unless the app isn't using GETCURRENTPOSITION2) */
1580 /* don't know exactly how this should be handled...
1581 * the docs says that play cursor is reported as directly
1582 * behind write cursor, hmm... */
1583 /* let's just do what might work for Half-Life */
1585 wp
= (This
->dsound
->pwplay
+ DS_HEL_MARGIN
) * This
->dsound
->fraglen
;
1586 while (wp
>= primarybuf
->buflen
)
1587 wp
-= primarybuf
->buflen
;
1588 *playpos
= DSOUND_CalcPlayPosition(This
, pstate
, wp
, pwrite
, lplay
, splay
);
1590 LeaveCriticalSection(&(primarybuf
->lock
));
1592 if (writepos
) *writepos
= This
->buf_mixpos
;
1595 if (This
->state
!= STATE_STOPPED
)
1596 /* apply the documented 10ms lead to writepos */
1597 *writepos
+= This
->writelead
;
1598 while (*writepos
>= This
->buflen
) *writepos
-= This
->buflen
;
1600 TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos
?*playpos
:0, writepos
?*writepos
:0, This
, GetTickCount());
1604 static HRESULT WINAPI
IDirectSoundBufferImpl_GetStatus(
1605 LPDIRECTSOUNDBUFFER iface
,LPDWORD status
1607 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1608 TRACE("(%p,%p), thread is %lx\n",This
,status
,GetCurrentThreadId());
1611 return DSERR_INVALIDPARAM
;
1614 if ((This
->state
== STATE_STARTING
) || (This
->state
== STATE_PLAYING
)) {
1615 *status
|= DSBSTATUS_PLAYING
;
1616 if (This
->playflags
& DSBPLAY_LOOPING
)
1617 *status
|= DSBSTATUS_LOOPING
;
1620 TRACE("status=%lx\n", *status
);
1625 static HRESULT WINAPI
IDirectSoundBufferImpl_GetFormat(
1626 LPDIRECTSOUNDBUFFER iface
,LPWAVEFORMATEX lpwf
,DWORD wfsize
,LPDWORD wfwritten
1628 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1629 TRACE("(%p,%p,%ld,%p)\n",This
,lpwf
,wfsize
,wfwritten
);
1631 if (wfsize
>sizeof(This
->wfx
))
1632 wfsize
= sizeof(This
->wfx
);
1633 if (lpwf
) { /* NULL is valid */
1634 memcpy(lpwf
,&(This
->wfx
),wfsize
);
1636 *wfwritten
= wfsize
;
1639 *wfwritten
= sizeof(This
->wfx
);
1641 return DSERR_INVALIDPARAM
;
1646 static HRESULT WINAPI
IDirectSoundBufferImpl_Lock(
1647 LPDIRECTSOUNDBUFFER iface
,DWORD writecursor
,DWORD writebytes
,LPVOID lplpaudioptr1
,LPDWORD audiobytes1
,LPVOID lplpaudioptr2
,LPDWORD audiobytes2
,DWORD flags
1649 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1652 TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx) at %ld\n",
1664 if (flags
& DSBLOCK_FROMWRITECURSOR
) {
1666 /* GetCurrentPosition does too much magic to duplicate here */
1667 IDirectSoundBufferImpl_GetCurrentPosition(iface
, NULL
, &writepos
);
1668 writecursor
+= writepos
;
1670 if (flags
& DSBLOCK_ENTIREBUFFER
)
1671 writebytes
= This
->buflen
;
1672 if (writebytes
> This
->buflen
)
1673 writebytes
= This
->buflen
;
1675 assert(audiobytes1
!=audiobytes2
);
1676 assert(lplpaudioptr1
!=lplpaudioptr2
);
1678 if ((writebytes
== This
->buflen
) &&
1679 ((This
->state
== STATE_STARTING
) ||
1680 (This
->state
== STATE_PLAYING
)))
1681 /* some games, like Half-Life, try to be clever (not) and
1682 * keep one secondary buffer, and mix sounds into it itself,
1683 * locking the entire buffer every time... so we can just forget
1684 * about tracking the last-written-to-position... */
1685 This
->probably_valid_to
= (DWORD
)-1;
1687 This
->probably_valid_to
= writecursor
;
1689 if (This
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
)
1690 capf
= DSDDESC_DONTNEEDPRIMARYLOCK
;
1692 capf
= DSDDESC_DONTNEEDSECONDARYLOCK
;
1693 if (!(This
->dsound
->drvdesc
.dwFlags
& capf
) && This
->hwbuf
) {
1694 IDsDriverBuffer_Lock(This
->hwbuf
,
1695 lplpaudioptr1
, audiobytes1
,
1696 lplpaudioptr2
, audiobytes2
,
1697 writecursor
, writebytes
,
1702 if (writecursor
+writebytes
<= This
->buflen
) {
1703 *(LPBYTE
*)lplpaudioptr1
= This
->buffer
+writecursor
;
1704 *audiobytes1
= writebytes
;
1706 *(LPBYTE
*)lplpaudioptr2
= NULL
;
1709 TRACE("->%ld.0\n",writebytes
);
1711 *(LPBYTE
*)lplpaudioptr1
= This
->buffer
+writecursor
;
1712 *audiobytes1
= This
->buflen
-writecursor
;
1714 *(LPBYTE
*)lplpaudioptr2
= This
->buffer
;
1716 *audiobytes2
= writebytes
-(This
->buflen
-writecursor
);
1717 TRACE("->%ld.%ld\n",*audiobytes1
,audiobytes2
?*audiobytes2
:0);
1719 /* if the segment between playpos and buf_mixpos is touched,
1720 * we need to cancel some mixing */
1721 if (This
->buf_mixpos
>= This
->playpos
) {
1722 if (This
->buf_mixpos
> writecursor
&&
1723 This
->playpos
<= writecursor
+writebytes
)
1727 if (This
->buf_mixpos
> writecursor
||
1728 This
->playpos
<= writecursor
+writebytes
)
1732 TRACE("locking prebuffered region, ouch\n");
1733 DSOUND_MixCancelAt(This
, writecursor
);
1739 static HRESULT WINAPI
IDirectSoundBufferImpl_SetCurrentPosition(
1740 LPDIRECTSOUNDBUFFER iface
,DWORD newpos
1742 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1743 TRACE("(%p,%ld)\n",This
,newpos
);
1746 EnterCriticalSection(&(This
->lock
));
1748 This
->buf_mixpos
= newpos
;
1750 IDsDriverBuffer_SetPosition(This
->hwbuf
, This
->buf_mixpos
);
1752 LeaveCriticalSection(&(This
->lock
));
1758 static HRESULT WINAPI
IDirectSoundBufferImpl_SetPan(
1759 LPDIRECTSOUNDBUFFER iface
,LONG pan
1761 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1763 TRACE("(%p,%ld)\n",This
,pan
);
1765 if ((pan
> DSBPAN_RIGHT
) || (pan
< DSBPAN_LEFT
))
1766 return DSERR_INVALIDPARAM
;
1768 /* You cannot set the pan of the primary buffer */
1769 /* and you cannot use both pan and 3D controls */
1770 if (!(This
->dsbd
.dwFlags
& DSBCAPS_CTRLPAN
) ||
1771 (This
->dsbd
.dwFlags
& DSBCAPS_CTRL3D
) ||
1772 (This
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
))
1773 return DSERR_CONTROLUNAVAIL
;
1776 EnterCriticalSection(&(This
->lock
));
1778 This
->volpan
.lPan
= pan
;
1780 DSOUND_RecalcVolPan(&(This
->volpan
));
1783 IDsDriverBuffer_SetVolumePan(This
->hwbuf
, &(This
->volpan
));
1786 LeaveCriticalSection(&(This
->lock
));
1792 static HRESULT WINAPI
IDirectSoundBufferImpl_GetPan(
1793 LPDIRECTSOUNDBUFFER iface
,LPLONG pan
1795 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1796 TRACE("(%p,%p)\n",This
,pan
);
1799 return DSERR_INVALIDPARAM
;
1801 *pan
= This
->volpan
.lPan
;
1806 static HRESULT WINAPI
IDirectSoundBufferImpl_Unlock(
1807 LPDIRECTSOUNDBUFFER iface
,LPVOID p1
,DWORD x1
,LPVOID p2
,DWORD x2
1809 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1810 DWORD capf
, probably_valid_to
;
1812 TRACE("(%p,%p,%ld,%p,%ld):stub\n", This
,p1
,x1
,p2
,x2
);
1815 /* Preprocess 3D buffers... */
1817 /* This is highly experimental and liable to break things */
1818 if (This
->dsbd
.dwFlags
& DSBCAPS_CTRL3D
)
1819 DSOUND_Create3DBuffer(This
);
1822 if (This
->dsbd
.dwFlags
& DSBCAPS_PRIMARYBUFFER
)
1823 capf
= DSDDESC_DONTNEEDPRIMARYLOCK
;
1825 capf
= DSDDESC_DONTNEEDSECONDARYLOCK
;
1826 if (!(This
->dsound
->drvdesc
.dwFlags
& capf
) && This
->hwbuf
) {
1827 IDsDriverBuffer_Unlock(This
->hwbuf
, p1
, x1
, p2
, x2
);
1830 if (p2
) probably_valid_to
= (((LPBYTE
)p2
)-This
->buffer
) + x2
;
1831 else probably_valid_to
= (((LPBYTE
)p1
)-This
->buffer
) + x1
;
1832 while (probably_valid_to
>= This
->buflen
)
1833 probably_valid_to
-= This
->buflen
;
1834 if ((probably_valid_to
== 0) && ((x1
+x2
) == This
->buflen
) &&
1835 ((This
->state
== STATE_STARTING
) ||
1836 (This
->state
== STATE_PLAYING
)))
1837 /* see IDirectSoundBufferImpl_Lock */
1838 probably_valid_to
= (DWORD
)-1;
1839 This
->probably_valid_to
= probably_valid_to
;
1844 static HRESULT WINAPI
IDirectSoundBufferImpl_Restore(
1845 LPDIRECTSOUNDBUFFER iface
1847 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1848 FIXME("(%p):stub\n",This
);
1852 static HRESULT WINAPI
IDirectSoundBufferImpl_GetFrequency(
1853 LPDIRECTSOUNDBUFFER iface
,LPDWORD freq
1855 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1856 TRACE("(%p,%p)\n",This
,freq
);
1859 return DSERR_INVALIDPARAM
;
1862 TRACE("-> %ld\n", *freq
);
1867 static HRESULT WINAPI
IDirectSoundBufferImpl_Initialize(
1868 LPDIRECTSOUNDBUFFER iface
,LPDIRECTSOUND dsound
,LPDSBUFFERDESC dbsd
1870 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1871 FIXME("(%p,%p,%p):stub\n",This
,dsound
,dbsd
);
1872 DPRINTF("Re-Init!!!\n");
1873 return DSERR_ALREADYINITIALIZED
;
1876 static HRESULT WINAPI
IDirectSoundBufferImpl_GetCaps(
1877 LPDIRECTSOUNDBUFFER iface
,LPDSBCAPS caps
1879 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1880 TRACE("(%p)->(%p)\n",This
,caps
);
1883 return DSERR_INVALIDPARAM
;
1885 /* I think we should check this value, not set it. See */
1886 /* Inside DirectX, p215. That should apply here, too. */
1887 caps
->dwSize
= sizeof(*caps
);
1889 caps
->dwFlags
= This
->dsbd
.dwFlags
;
1890 if (This
->hwbuf
) caps
->dwFlags
|= DSBCAPS_LOCHARDWARE
;
1891 else caps
->dwFlags
|= DSBCAPS_LOCSOFTWARE
;
1893 caps
->dwBufferBytes
= This
->buflen
;
1895 /* This value represents the speed of the "unlock" command.
1896 As unlock is quite fast (it does not do anything), I put
1897 4096 ko/s = 4 Mo / s */
1898 /* FIXME: hwbuf speed */
1899 caps
->dwUnlockTransferRate
= 4096;
1900 caps
->dwPlayCpuOverhead
= 0;
1905 static HRESULT WINAPI
IDirectSoundBufferImpl_QueryInterface(
1906 LPDIRECTSOUNDBUFFER iface
,REFIID riid
,LPVOID
*ppobj
1908 ICOM_THIS(IDirectSoundBufferImpl
,iface
);
1910 TRACE("(%p,%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
1912 if ( IsEqualGUID( &IID_IDirectSoundNotify
, riid
) ) {
1913 IDirectSoundNotifyImpl
*dsn
;
1915 dsn
= (IDirectSoundNotifyImpl
*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsn
));
1918 IDirectSoundBuffer_AddRef(iface
);
1919 ICOM_VTBL(dsn
) = &dsnvt
;
1920 *ppobj
= (LPVOID
)dsn
;
1925 if ( IsEqualGUID( &IID_IDirectSound3DBuffer
, riid
) ) {
1926 IDirectSound3DBufferImpl
*ds3db
;
1928 *ppobj
= This
->ds3db
;
1930 IDirectSound3DBuffer_AddRef((LPDIRECTSOUND3DBUFFER
)This
->ds3db
);
1934 ds3db
= (IDirectSound3DBufferImpl
*)HeapAlloc(GetProcessHeap(),
1938 ICOM_VTBL(ds3db
) = &ds3dbvt
;
1939 InitializeCriticalSection(&ds3db
->lock
);
1941 IDirectSoundBuffer_AddRef(iface
);
1943 ds3db
->ds3db
.dwSize
= sizeof(DS3DBUFFER
);
1944 ds3db
->ds3db
.vPosition
.u1
.x
= 0.0;
1945 ds3db
->ds3db
.vPosition
.u2
.y
= 0.0;
1946 ds3db
->ds3db
.vPosition
.u3
.z
= 0.0;
1947 ds3db
->ds3db
.vVelocity
.u1
.x
= 0.0;
1948 ds3db
->ds3db
.vVelocity
.u2
.y
= 0.0;
1949 ds3db
->ds3db
.vVelocity
.u3
.z
= 0.0;
1950 ds3db
->ds3db
.dwInsideConeAngle
= DS3D_DEFAULTCONEANGLE
;
1951 ds3db
->ds3db
.dwOutsideConeAngle
= DS3D_DEFAULTCONEANGLE
;
1952 ds3db
->ds3db
.vConeOrientation
.u1
.x
= 0.0;
1953 ds3db
->ds3db
.vConeOrientation
.u2
.y
= 0.0;
1954 ds3db
->ds3db
.vConeOrientation
.u3
.z
= 0.0;
1955 ds3db
->ds3db
.lConeOutsideVolume
= DS3D_DEFAULTCONEOUTSIDEVOLUME
; ds3db
->ds3db
.flMinDistance
= DS3D_DEFAULTMINDISTANCE
;
1956 ds3db
->ds3db
.flMaxDistance
= DS3D_DEFAULTMAXDISTANCE
;
1957 ds3db
->ds3db
.dwMode
= DS3DMODE_NORMAL
;
1958 ds3db
->buflen
= (This
->buflen
* primarybuf
->wfx
.nBlockAlign
) /
1959 This
->wfx
.nBlockAlign
;
1960 ds3db
->buffer
= HeapAlloc(GetProcessHeap(), 0, ds3db
->buflen
);
1961 if (ds3db
->buffer
== NULL
) {
1963 ds3db
->ds3db
.dwMode
= DS3DMODE_DISABLE
;
1966 ds3db
->iks
= (IKsPropertySetImpl
*)HeapAlloc(GetProcessHeap(),0,sizeof(*(ds3db
->iks
)));
1967 ds3db
->iks
->ref
= 1;
1968 ds3db
->iks
->ds3db
= ds3db
;
1969 ICOM_VTBL(ds3db
->iks
) = &iksvt
;
1974 if ( IsEqualGUID( &IID_IDirectSound3DBuffer
, riid
) ) {
1975 FIXME("%s: I know about this GUID, but don't support it yet\n",
1976 debugstr_guid( riid
));
1983 if ( IsEqualGUID( &IID_IDirectSound3DListener
, riid
) ) {
1984 IDirectSound3DListenerImpl
* dsl
;
1986 if (This
->dsound
->listener
) {
1987 *ppobj
= This
->dsound
->listener
;
1988 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER
)This
->dsound
->listener
);
1992 dsl
= (IDirectSound3DListenerImpl
*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsl
));
1994 ICOM_VTBL(dsl
) = &ds3dlvt
;
1995 *ppobj
= (LPVOID
)dsl
;
1997 dsl
->ds3dl
.dwSize
= sizeof(DS3DLISTENER
);
1998 dsl
->ds3dl
.vPosition
.u1
.x
= 0.0;
1999 dsl
->ds3dl
.vPosition
.u2
.y
= 0.0;
2000 dsl
->ds3dl
.vPosition
.u3
.z
= 0.0;
2001 dsl
->ds3dl
.vVelocity
.u1
.x
= 0.0;
2002 dsl
->ds3dl
.vVelocity
.u2
.y
= 0.0;
2003 dsl
->ds3dl
.vVelocity
.u3
.z
= 0.0;
2004 dsl
->ds3dl
.vOrientFront
.u1
.x
= 0.0;
2005 dsl
->ds3dl
.vOrientFront
.u2
.y
= 0.0;
2006 dsl
->ds3dl
.vOrientFront
.u3
.z
= 1.0;
2007 dsl
->ds3dl
.vOrientTop
.u1
.x
= 0.0;
2008 dsl
->ds3dl
.vOrientTop
.u2
.y
= 1.0;
2009 dsl
->ds3dl
.vOrientTop
.u3
.z
= 0.0;
2010 dsl
->ds3dl
.flDistanceFactor
= DS3D_DEFAULTDISTANCEFACTOR
;
2011 dsl
->ds3dl
.flRolloffFactor
= DS3D_DEFAULTROLLOFFFACTOR
;
2013 InitializeCriticalSection(&dsl
->lock
);
2016 IDirectSoundBuffer_AddRef(iface
);
2018 This
->dsound
->listener
= dsl
;
2019 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER
)dsl
);
2024 if ( IsEqualGUID( &IID_IDirectSound3DListener
, riid
) ) {
2025 FIXME("%s: I know about this GUID, but don't support it yet\n",
2026 debugstr_guid( riid
));
2032 FIXME( "Unknown GUID %s\n", debugstr_guid( riid
) );
2039 static ICOM_VTABLE(IDirectSoundBuffer
) dsbvt
=
2041 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2042 IDirectSoundBufferImpl_QueryInterface
,
2043 IDirectSoundBufferImpl_AddRef
,
2044 IDirectSoundBufferImpl_Release
,
2045 IDirectSoundBufferImpl_GetCaps
,
2046 IDirectSoundBufferImpl_GetCurrentPosition
,
2047 IDirectSoundBufferImpl_GetFormat
,
2048 IDirectSoundBufferImpl_GetVolume
,
2049 IDirectSoundBufferImpl_GetPan
,
2050 IDirectSoundBufferImpl_GetFrequency
,
2051 IDirectSoundBufferImpl_GetStatus
,
2052 IDirectSoundBufferImpl_Initialize
,
2053 IDirectSoundBufferImpl_Lock
,
2054 IDirectSoundBufferImpl_Play
,
2055 IDirectSoundBufferImpl_SetCurrentPosition
,
2056 IDirectSoundBufferImpl_SetFormat
,
2057 IDirectSoundBufferImpl_SetVolume
,
2058 IDirectSoundBufferImpl_SetPan
,
2059 IDirectSoundBufferImpl_SetFrequency
,
2060 IDirectSoundBufferImpl_Stop
,
2061 IDirectSoundBufferImpl_Unlock
,
2062 IDirectSoundBufferImpl_Restore
2065 /*******************************************************************************
2069 static HRESULT WINAPI
IDirectSoundImpl_SetCooperativeLevel(
2070 LPDIRECTSOUND iface
,HWND hwnd
,DWORD level
2072 ICOM_THIS(IDirectSoundImpl
,iface
);
2074 FIXME("(%p,%08lx,%ld):stub\n",This
,(DWORD
)hwnd
,level
);
2076 This
->priolevel
= level
;
2081 static HRESULT WINAPI
IDirectSoundImpl_CreateSoundBuffer(
2082 LPDIRECTSOUND iface
,LPDSBUFFERDESC dsbd
,LPLPDIRECTSOUNDBUFFER ppdsb
,LPUNKNOWN lpunk
2084 ICOM_THIS(IDirectSoundImpl
,iface
);
2085 IDirectSoundBufferImpl
** ippdsb
=(IDirectSoundBufferImpl
**)ppdsb
;
2086 LPWAVEFORMATEX wfex
;
2087 HRESULT err
= DS_OK
;
2089 TRACE("(%p,%p,%p,%p)\n",This
,dsbd
,ippdsb
,lpunk
);
2091 if ((This
== NULL
) || (dsbd
== NULL
) || (ippdsb
== NULL
))
2092 return DSERR_INVALIDPARAM
;
2094 if (TRACE_ON(dsound
)) {
2095 TRACE("(structsize=%ld)\n",dsbd
->dwSize
);
2096 TRACE("(flags=0x%08lx:\n",dsbd
->dwFlags
);
2097 _dump_DSBCAPS(dsbd
->dwFlags
);
2099 TRACE("(bufferbytes=%ld)\n",dsbd
->dwBufferBytes
);
2100 TRACE("(lpwfxFormat=%p)\n",dsbd
->lpwfxFormat
);
2103 wfex
= dsbd
->lpwfxFormat
;
2106 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
2107 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
2108 wfex
->wFormatTag
, wfex
->nChannels
, wfex
->nSamplesPerSec
,
2109 wfex
->nAvgBytesPerSec
, wfex
->nBlockAlign
,
2110 wfex
->wBitsPerSample
, wfex
->cbSize
);
2112 if (dsbd
->dwFlags
& DSBCAPS_PRIMARYBUFFER
) {
2114 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER
)primarybuf
);
2115 *ippdsb
= primarybuf
;
2116 primarybuf
->dsbd
.dwFlags
= dsbd
->dwFlags
;
2118 } /* Else create primary buffer */
2120 if (dsbd
->dwBufferBytes
< DSBSIZE_MIN
|| dsbd
->dwBufferBytes
> DSBSIZE_MAX
) {
2121 ERR("invalid sound buffer size %ld\n", dsbd
->dwBufferBytes
);
2122 return DSERR_INVALIDPARAM
; /* FIXME: which error? */
2126 *ippdsb
= (IDirectSoundBufferImpl
*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(IDirectSoundBufferImpl
));
2127 if (*ippdsb
== NULL
)
2128 return DSERR_OUTOFMEMORY
;
2129 ICOM_VTBL(*ippdsb
) = &dsbvt
;
2131 (*ippdsb
)->dsound
= This
;
2132 (*ippdsb
)->parent
= NULL
;
2133 (*ippdsb
)->buffer
= NULL
;
2135 memcpy(&((*ippdsb
)->dsbd
),dsbd
,sizeof(*dsbd
));
2136 if (dsbd
->lpwfxFormat
)
2137 memcpy(&((*ippdsb
)->wfx
), dsbd
->lpwfxFormat
, sizeof((*ippdsb
)->wfx
));
2139 TRACE("Created buffer at %p\n", *ippdsb
);
2141 if (dsbd
->dwFlags
& DSBCAPS_PRIMARYBUFFER
) {
2142 (*ippdsb
)->buflen
= dsound
->wfx
.nAvgBytesPerSec
;
2143 (*ippdsb
)->freq
= dsound
->wfx
.nSamplesPerSec
;
2145 /* FIXME: verify that hardware capabilities (DSCAPS_PRIMARY flags) match */
2148 err
= IDsDriver_CreateSoundBuffer(This
->driver
,wfex
,dsbd
->dwFlags
,0,
2149 &((*ippdsb
)->buflen
),&((*ippdsb
)->buffer
),
2150 (LPVOID
*)&((*ippdsb
)->hwbuf
));
2153 err
= DSOUND_PrimaryOpen(*ippdsb
);
2158 (*ippdsb
)->buflen
= dsbd
->dwBufferBytes
;
2159 (*ippdsb
)->freq
= dsbd
->lpwfxFormat
->nSamplesPerSec
;
2161 /* Check necessary hardware mixing capabilities */
2162 if (wfex
->nChannels
==2) capf
|= DSCAPS_SECONDARYSTEREO
;
2163 else capf
|= DSCAPS_SECONDARYMONO
;
2164 if (wfex
->wBitsPerSample
==16) capf
|= DSCAPS_SECONDARY16BIT
;
2165 else capf
|= DSCAPS_SECONDARY8BIT
;
2166 use_hw
= (This
->drvcaps
.dwFlags
& capf
) == capf
;
2168 /* FIXME: check hardware sample rate mixing capabilities */
2169 /* FIXME: check app hints for software/hardware buffer (STATIC, LOCHARDWARE, etc) */
2170 /* FIXME: check whether any hardware buffers are left */
2171 /* FIXME: handle DSDHEAP_CREATEHEAP for hardware buffers */
2173 /* Allocate system memory if applicable */
2174 if ((This
->drvdesc
.dwFlags
& DSDDESC_USESYSTEMMEMORY
) || !use_hw
) {
2175 (*ippdsb
)->buffer
= (LPBYTE
)HeapAlloc(GetProcessHeap(),0,(*ippdsb
)->buflen
);
2176 if ((*ippdsb
)->buffer
== NULL
)
2177 err
= DSERR_OUTOFMEMORY
;
2180 /* Allocate the hardware buffer */
2181 if (use_hw
&& (err
== DS_OK
)) {
2182 err
= IDsDriver_CreateSoundBuffer(This
->driver
,wfex
,dsbd
->dwFlags
,0,
2183 &((*ippdsb
)->buflen
),&((*ippdsb
)->buffer
),
2184 (LPVOID
*)&((*ippdsb
)->hwbuf
));
2189 if ((*ippdsb
)->buffer
)
2190 HeapFree(GetProcessHeap(),0,(*ippdsb
)->buffer
);
2191 HeapFree(GetProcessHeap(),0,(*ippdsb
));
2195 /* calculate fragment size and write lead */
2196 DSOUND_RecalcFormat(*ippdsb
);
2198 /* It's not necessary to initialize values to zero since */
2199 /* we allocated this structure with HEAP_ZERO_MEMORY... */
2200 (*ippdsb
)->playpos
= 0;
2201 (*ippdsb
)->buf_mixpos
= 0;
2202 (*ippdsb
)->state
= STATE_STOPPED
;
2203 DSOUND_RecalcVolPan(&((*ippdsb
)->volpan
));
2205 if (!(dsbd
->dwFlags
& DSBCAPS_PRIMARYBUFFER
)) {
2206 (*ippdsb
)->freqAdjust
= ((*ippdsb
)->freq
<< DSOUND_FREQSHIFT
) /
2207 primarybuf
->wfx
.nSamplesPerSec
;
2208 (*ippdsb
)->nAvgBytesPerSec
= (*ippdsb
)->freq
*
2209 dsbd
->lpwfxFormat
->nBlockAlign
;
2212 EnterCriticalSection(&(This
->lock
));
2213 /* register buffer */
2214 if (!(dsbd
->dwFlags
& DSBCAPS_PRIMARYBUFFER
)) {
2215 IDirectSoundBufferImpl
**newbuffers
= (IDirectSoundBufferImpl
**)HeapReAlloc(GetProcessHeap(),0,This
->buffers
,sizeof(IDirectSoundBufferImpl
*)*(This
->nrofbuffers
+1));
2217 This
->buffers
= newbuffers
;
2218 This
->buffers
[This
->nrofbuffers
] = *ippdsb
;
2219 This
->nrofbuffers
++;
2220 TRACE("buffer count is now %d\n", This
->nrofbuffers
);
2222 ERR("out of memory for buffer list! Current buffer count is %d\n", This
->nrofbuffers
);
2223 err
= DSERR_OUTOFMEMORY
;
2226 LeaveCriticalSection(&(This
->lock
));
2228 IDirectSound_AddRef(iface
);
2230 InitializeCriticalSection(&((*ippdsb
)->lock
));
2234 IDirectSoundBuffer_Release(*ppdsb
);
2240 if (dsbd
->dwFlags
& DSBCAPS_CTRL3D
) {
2241 IDirectSound3DBufferImpl
*ds3db
;
2243 ds3db
= (IDirectSound3DBufferImpl
*)HeapAlloc(GetProcessHeap(),
2245 ICOM_VTBL(ds3db
) = &ds3dbvt
;
2247 (*ippdsb
)->ds3db
= ds3db
;
2249 ds3db
->dsb
= (*ippdsb
);
2250 IDirectSoundBufferImpl_AddRef((LPDIRECTSOUNDBUFFER
)(*ippdsb
));
2252 InitializeCriticalSection(&ds3db
->lock
);
2254 ds3db
->ds3db
.dwSize
= sizeof(DS3DBUFFER
);
2255 ds3db
->ds3db
.vPosition
.u1
.x
= 0.0;
2256 ds3db
->ds3db
.vPosition
.u2
.y
= 0.0;
2257 ds3db
->ds3db
.vPosition
.u3
.z
= 0.0;
2258 ds3db
->ds3db
.vVelocity
.u1
.x
= 0.0;
2259 ds3db
->ds3db
.vVelocity
.u2
.y
= 0.0;
2260 ds3db
->ds3db
.vVelocity
.u3
.z
= 0.0;
2261 ds3db
->ds3db
.dwInsideConeAngle
= DS3D_DEFAULTCONEANGLE
;
2262 ds3db
->ds3db
.dwOutsideConeAngle
= DS3D_DEFAULTCONEANGLE
;
2263 ds3db
->ds3db
.vConeOrientation
.u1
.x
= 0.0;
2264 ds3db
->ds3db
.vConeOrientation
.u2
.y
= 0.0;
2265 ds3db
->ds3db
.vConeOrientation
.u3
.z
= 0.0;
2266 ds3db
->ds3db
.lConeOutsideVolume
= DS3D_DEFAULTCONEOUTSIDEVOLUME
;
2267 ds3db
->ds3db
.flMinDistance
= DS3D_DEFAULTMINDISTANCE
;
2268 ds3db
->ds3db
.flMaxDistance
= DS3D_DEFAULTMAXDISTANCE
;
2269 ds3db
->ds3db
.dwMode
= DS3DMODE_NORMAL
;
2270 ds3db
->buflen
= ((*ippdsb
)->buflen
* primarybuf
->wfx
.nBlockAlign
) /
2271 (*ippdsb
)->wfx
.nBlockAlign
;
2272 ds3db
->buffer
= HeapAlloc(GetProcessHeap(), 0, ds3db
->buflen
);
2273 if (ds3db
->buffer
== NULL
) {
2275 ds3db
->ds3db
.dwMode
= DS3DMODE_DISABLE
;
2277 ds3db
->iks
= (IKsPropertySetImpl
*)HeapAlloc(GetProcessHeap(),0,sizeof(*(ds3db
->iks
)));
2278 ds3db
->iks
->ref
= 1;
2279 ds3db
->iks
->ds3db
= ds3db
;
2280 ICOM_VTBL(ds3db
->iks
) = &iksvt
;
2287 static HRESULT WINAPI
IDirectSoundImpl_DuplicateSoundBuffer(
2288 LPDIRECTSOUND iface
,LPDIRECTSOUNDBUFFER pdsb
,LPLPDIRECTSOUNDBUFFER ppdsb
2290 ICOM_THIS(IDirectSoundImpl
,iface
);
2291 IDirectSoundBufferImpl
* ipdsb
=(IDirectSoundBufferImpl
*)pdsb
;
2292 IDirectSoundBufferImpl
** ippdsb
=(IDirectSoundBufferImpl
**)ppdsb
;
2293 TRACE("(%p,%p,%p)\n",This
,ipdsb
,ippdsb
);
2296 FIXME("need to duplicate hardware buffer\n");
2299 *ippdsb
= (IDirectSoundBufferImpl
*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(IDirectSoundBufferImpl
));
2301 IDirectSoundBuffer_AddRef(pdsb
);
2302 memcpy(*ippdsb
, ipdsb
, sizeof(IDirectSoundBufferImpl
));
2304 (*ippdsb
)->state
= STATE_STOPPED
;
2305 (*ippdsb
)->playpos
= 0;
2306 (*ippdsb
)->buf_mixpos
= 0;
2307 (*ippdsb
)->dsound
= This
;
2308 (*ippdsb
)->parent
= ipdsb
;
2309 memcpy(&((*ippdsb
)->wfx
), &(ipdsb
->wfx
), sizeof((*ippdsb
)->wfx
));
2310 InitializeCriticalSection(&(*ippdsb
)->lock
);
2311 /* register buffer */
2312 EnterCriticalSection(&(This
->lock
));
2314 IDirectSoundBufferImpl
**newbuffers
= (IDirectSoundBufferImpl
**)HeapReAlloc(GetProcessHeap(),0,This
->buffers
,sizeof(IDirectSoundBufferImpl
**)*(This
->nrofbuffers
+1));
2316 This
->buffers
= newbuffers
;
2317 This
->buffers
[This
->nrofbuffers
] = *ippdsb
;
2318 This
->nrofbuffers
++;
2319 TRACE("buffer count is now %d\n", This
->nrofbuffers
);
2321 ERR("out of memory for buffer list! Current buffer count is %d\n", This
->nrofbuffers
);
2322 /* FIXME: release buffer */
2325 LeaveCriticalSection(&(This
->lock
));
2326 IDirectSound_AddRef(iface
);
2331 static HRESULT WINAPI
IDirectSoundImpl_GetCaps(LPDIRECTSOUND iface
,LPDSCAPS caps
) {
2332 ICOM_THIS(IDirectSoundImpl
,iface
);
2333 TRACE("(%p,%p)\n",This
,caps
);
2334 TRACE("(flags=0x%08lx)\n",caps
->dwFlags
);
2337 return DSERR_INVALIDPARAM
;
2339 /* We should check this value, not set it. See Inside DirectX, p215. */
2340 caps
->dwSize
= sizeof(*caps
);
2342 caps
->dwFlags
= This
->drvcaps
.dwFlags
;
2344 /* FIXME: copy caps from This->drvcaps */
2345 caps
->dwMinSecondarySampleRate
= DSBFREQUENCY_MIN
;
2346 caps
->dwMaxSecondarySampleRate
= DSBFREQUENCY_MAX
;
2348 caps
->dwPrimaryBuffers
= 1;
2350 caps
->dwMaxHwMixingAllBuffers
= 0;
2351 caps
->dwMaxHwMixingStaticBuffers
= 0;
2352 caps
->dwMaxHwMixingStreamingBuffers
= 0;
2354 caps
->dwFreeHwMixingAllBuffers
= 0;
2355 caps
->dwFreeHwMixingStaticBuffers
= 0;
2356 caps
->dwFreeHwMixingStreamingBuffers
= 0;
2358 caps
->dwMaxHw3DAllBuffers
= 0;
2359 caps
->dwMaxHw3DStaticBuffers
= 0;
2360 caps
->dwMaxHw3DStreamingBuffers
= 0;
2362 caps
->dwFreeHw3DAllBuffers
= 0;
2363 caps
->dwFreeHw3DStaticBuffers
= 0;
2364 caps
->dwFreeHw3DStreamingBuffers
= 0;
2366 caps
->dwTotalHwMemBytes
= 0;
2368 caps
->dwFreeHwMemBytes
= 0;
2370 caps
->dwMaxContigFreeHwMemBytes
= 0;
2372 caps
->dwUnlockTransferRateHwBuffers
= 4096; /* But we have none... */
2374 caps
->dwPlayCpuOverheadSwBuffers
= 1; /* 1% */
2379 static ULONG WINAPI
IDirectSoundImpl_AddRef(LPDIRECTSOUND iface
) {
2380 ICOM_THIS(IDirectSoundImpl
,iface
);
2381 return ++(This
->ref
);
2384 static ULONG WINAPI
IDirectSoundImpl_Release(LPDIRECTSOUND iface
) {
2385 ICOM_THIS(IDirectSoundImpl
,iface
);
2386 TRACE("(%p), ref was %ld\n",This
,This
->ref
);
2387 if (!--(This
->ref
)) {
2390 timeKillEvent(This
->timerID
);
2391 timeEndPeriod(DS_TIME_RES
);
2394 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER
)primarybuf
);
2396 if (This
->buffers
) {
2397 for( i
=0;i
<This
->nrofbuffers
;i
++)
2398 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER
)This
->buffers
[i
]);
2402 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER
)This
->primary
);
2404 DeleteCriticalSection(&This
->lock
);
2406 IDsDriver_Close(This
->driver
);
2409 for (c
=0; c
<DS_HEL_FRAGS
; c
++)
2410 HeapFree(GetProcessHeap(),0,This
->pwave
[c
]);
2412 if (This
->drvdesc
.dwFlags
& DSDDESC_DOMMSYSTEMOPEN
) {
2413 waveOutClose(This
->hwo
);
2416 IDsDriver_Release(This
->driver
);
2418 HeapFree(GetProcessHeap(),0,This
);
2425 static HRESULT WINAPI
IDirectSoundImpl_SetSpeakerConfig(
2426 LPDIRECTSOUND iface
,DWORD config
2428 ICOM_THIS(IDirectSoundImpl
,iface
);
2429 FIXME("(%p,0x%08lx):stub\n",This
,config
);
2433 static HRESULT WINAPI
IDirectSoundImpl_QueryInterface(
2434 LPDIRECTSOUND iface
,REFIID riid
,LPVOID
*ppobj
2436 ICOM_THIS(IDirectSoundImpl
,iface
);
2438 if ( IsEqualGUID( &IID_IDirectSound3DListener
, riid
) ) {
2440 if (This
->listener
) {
2441 *ppobj
= This
->listener
;
2442 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER
)This
->listener
);
2446 This
->listener
= (IDirectSound3DListenerImpl
*)HeapAlloc(
2447 GetProcessHeap(), 0, sizeof(*(This
->listener
)));
2448 This
->listener
->ref
= 1;
2449 ICOM_VTBL(This
->listener
) = &ds3dlvt
;
2450 *ppobj
= (LPVOID
)This
->listener
;
2451 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER
)*ppobj
);
2453 This
->listener
->dsb
= NULL
;
2455 This
->listener
->ds3dl
.dwSize
= sizeof(DS3DLISTENER
);
2456 This
->listener
->ds3dl
.vPosition
.u1
.x
= 0.0;
2457 This
->listener
->ds3dl
.vPosition
.u2
.y
= 0.0;
2458 This
->listener
->ds3dl
.vPosition
.u3
.z
= 0.0;
2459 This
->listener
->ds3dl
.vVelocity
.u1
.x
= 0.0;
2460 This
->listener
->ds3dl
.vVelocity
.u2
.y
= 0.0;
2461 This
->listener
->ds3dl
.vVelocity
.u3
.z
= 0.0;
2462 This
->listener
->ds3dl
.vOrientFront
.u1
.x
= 0.0;
2463 This
->listener
->ds3dl
.vOrientFront
.u2
.y
= 0.0;
2464 This
->listener
->ds3dl
.vOrientFront
.u3
.z
= 1.0;
2465 This
->listener
->ds3dl
.vOrientTop
.u1
.x
= 0.0;
2466 This
->listener
->ds3dl
.vOrientTop
.u2
.y
= 1.0;
2467 This
->listener
->ds3dl
.vOrientTop
.u3
.z
= 0.0;
2468 This
->listener
->ds3dl
.flDistanceFactor
= DS3D_DEFAULTDISTANCEFACTOR
;
2469 This
->listener
->ds3dl
.flRolloffFactor
= DS3D_DEFAULTROLLOFFFACTOR
;
2470 This
->listener
->ds3dl
.flDopplerFactor
= DS3D_DEFAULTDOPPLERFACTOR
;
2472 InitializeCriticalSection(&This
->listener
->lock
);
2477 FIXME("(%p,%s,%p)\n",This
,debugstr_guid(riid
),ppobj
);
2481 static HRESULT WINAPI
IDirectSoundImpl_Compact(
2482 LPDIRECTSOUND iface
)
2484 ICOM_THIS(IDirectSoundImpl
,iface
);
2485 TRACE("(%p)\n", This
);
2489 static HRESULT WINAPI
IDirectSoundImpl_GetSpeakerConfig(
2490 LPDIRECTSOUND iface
,
2491 LPDWORD lpdwSpeakerConfig
)
2493 ICOM_THIS(IDirectSoundImpl
,iface
);
2494 TRACE("(%p, %p)\n", This
, lpdwSpeakerConfig
);
2495 *lpdwSpeakerConfig
= DSSPEAKER_STEREO
| (DSSPEAKER_GEOMETRY_NARROW
<< 16);
2499 static HRESULT WINAPI
IDirectSoundImpl_Initialize(
2500 LPDIRECTSOUND iface
,
2503 ICOM_THIS(IDirectSoundImpl
,iface
);
2504 TRACE("(%p, %p)\n", This
, lpcGuid
);
2508 static ICOM_VTABLE(IDirectSound
) dsvt
=
2510 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2511 IDirectSoundImpl_QueryInterface
,
2512 IDirectSoundImpl_AddRef
,
2513 IDirectSoundImpl_Release
,
2514 IDirectSoundImpl_CreateSoundBuffer
,
2515 IDirectSoundImpl_GetCaps
,
2516 IDirectSoundImpl_DuplicateSoundBuffer
,
2517 IDirectSoundImpl_SetCooperativeLevel
,
2518 IDirectSoundImpl_Compact
,
2519 IDirectSoundImpl_GetSpeakerConfig
,
2520 IDirectSoundImpl_SetSpeakerConfig
,
2521 IDirectSoundImpl_Initialize
2525 static void DSOUND_CheckEvent(IDirectSoundBufferImpl
*dsb
, int len
)
2529 LPDSBPOSITIONNOTIFY event
;
2531 if (dsb
->nrofnotifies
== 0)
2534 TRACE("(%p) buflen = %ld, playpos = %ld, len = %d\n",
2535 dsb
, dsb
->buflen
, dsb
->playpos
, len
);
2536 for (i
= 0; i
< dsb
->nrofnotifies
; i
++) {
2537 event
= dsb
->notifies
+ i
;
2538 offset
= event
->dwOffset
;
2539 TRACE("checking %d, position %ld, event = %d\n",
2540 i
, offset
, event
->hEventNotify
);
2541 /* DSBPN_OFFSETSTOP has to be the last element. So this is */
2542 /* OK. [Inside DirectX, p274] */
2544 /* This also means we can't sort the entries by offset, */
2545 /* because DSBPN_OFFSETSTOP == -1 */
2546 if (offset
== DSBPN_OFFSETSTOP
) {
2547 if (dsb
->state
== STATE_STOPPED
) {
2548 SetEvent(event
->hEventNotify
);
2549 TRACE("signalled event %d (%d)\n", event
->hEventNotify
, i
);
2554 if ((dsb
->playpos
+ len
) >= dsb
->buflen
) {
2555 if ((offset
< ((dsb
->playpos
+ len
) % dsb
->buflen
)) ||
2556 (offset
>= dsb
->playpos
)) {
2557 TRACE("signalled event %d (%d)\n", event
->hEventNotify
, i
);
2558 SetEvent(event
->hEventNotify
);
2561 if ((offset
>= dsb
->playpos
) && (offset
< (dsb
->playpos
+ len
))) {
2562 TRACE("signalled event %d (%d)\n", event
->hEventNotify
, i
);
2563 SetEvent(event
->hEventNotify
);
2569 /* WAV format info can be found at: */
2571 /* http://www.cwi.nl/ftp/audio/AudioFormats.part2 */
2572 /* ftp://ftp.cwi.nl/pub/audio/RIFF-format */
2574 /* Import points to remember: */
2576 /* 8-bit WAV is unsigned */
2577 /* 16-bit WAV is signed */
2579 static inline INT16
cvtU8toS16(BYTE byte
)
2581 INT16 s
= (byte
- 128) << 8;
2586 static inline BYTE
cvtS16toU8(INT16 word
)
2588 BYTE b
= (word
+ 32768) >> 8;
2594 /* We should be able to optimize these two inline functions */
2595 /* so that we aren't doing 8->16->8 conversions when it is */
2596 /* not necessary. But this is still a WIP. Optimize later. */
2597 static inline void get_fields(const IDirectSoundBufferImpl
*dsb
, BYTE
*buf
, INT
*fl
, INT
*fr
)
2599 INT16
*bufs
= (INT16
*) buf
;
2601 /* TRACE("(%p)\n", buf); */
2602 if ((dsb
->wfx
.wBitsPerSample
== 8) && dsb
->wfx
.nChannels
== 2) {
2603 *fl
= cvtU8toS16(*buf
);
2604 *fr
= cvtU8toS16(*(buf
+ 1));
2608 if ((dsb
->wfx
.wBitsPerSample
== 16) && dsb
->wfx
.nChannels
== 2) {
2614 if ((dsb
->wfx
.wBitsPerSample
== 8) && dsb
->wfx
.nChannels
== 1) {
2615 *fl
= cvtU8toS16(*buf
);
2620 if ((dsb
->wfx
.wBitsPerSample
== 16) && dsb
->wfx
.nChannels
== 1) {
2626 FIXME("get_fields found an unsupported configuration\n");
2630 static inline void set_fields(BYTE
*buf
, INT fl
, INT fr
)
2632 INT16
*bufs
= (INT16
*) buf
;
2634 if ((primarybuf
->wfx
.wBitsPerSample
== 8) && (primarybuf
->wfx
.nChannels
== 2)) {
2635 *buf
= cvtS16toU8(fl
);
2636 *(buf
+ 1) = cvtS16toU8(fr
);
2640 if ((primarybuf
->wfx
.wBitsPerSample
== 16) && (primarybuf
->wfx
.nChannels
== 2)) {
2646 if ((primarybuf
->wfx
.wBitsPerSample
== 8) && (primarybuf
->wfx
.nChannels
== 1)) {
2647 *buf
= cvtS16toU8((fl
+ fr
) >> 1);
2651 if ((primarybuf
->wfx
.wBitsPerSample
== 16) && (primarybuf
->wfx
.nChannels
== 1)) {
2652 *bufs
= (fl
+ fr
) >> 1;
2655 FIXME("set_fields found an unsupported configuration\n");
2659 /* Now with PerfectPitch (tm) technology */
2660 static INT
DSOUND_MixerNorm(IDirectSoundBufferImpl
*dsb
, BYTE
*buf
, INT len
)
2662 INT i
, size
, ipos
, ilen
, fieldL
, fieldR
;
2664 INT iAdvance
= dsb
->wfx
.nBlockAlign
;
2665 INT oAdvance
= primarybuf
->wfx
.nBlockAlign
;
2667 ibp
= dsb
->buffer
+ dsb
->buf_mixpos
;
2670 TRACE("(%p, %p, %p), buf_mixpos=%ld\n", dsb
, ibp
, obp
, dsb
->buf_mixpos
);
2671 /* Check for the best case */
2672 if ((dsb
->freq
== primarybuf
->wfx
.nSamplesPerSec
) &&
2673 (dsb
->wfx
.wBitsPerSample
== primarybuf
->wfx
.wBitsPerSample
) &&
2674 (dsb
->wfx
.nChannels
== primarybuf
->wfx
.nChannels
)) {
2675 DWORD bytesleft
= dsb
->buflen
- dsb
->buf_mixpos
;
2676 TRACE("(%p) Best case\n", dsb
);
2677 if (len
<= bytesleft
)
2678 memcpy(obp
, ibp
, len
);
2680 memcpy(obp
, ibp
, bytesleft
);
2681 memcpy(obp
+ bytesleft
, dsb
->buffer
, len
- bytesleft
);
2686 /* Check for same sample rate */
2687 if (dsb
->freq
== primarybuf
->wfx
.nSamplesPerSec
) {
2688 TRACE("(%p) Same sample rate %ld = primary %ld\n", dsb
,
2689 dsb
->freq
, primarybuf
->wfx
.nSamplesPerSec
);
2691 for (i
= 0; i
< len
; i
+= oAdvance
) {
2692 get_fields(dsb
, ibp
, &fieldL
, &fieldR
);
2695 set_fields(obp
, fieldL
, fieldR
);
2697 if (ibp
>= (BYTE
*)(dsb
->buffer
+ dsb
->buflen
))
2698 ibp
= dsb
->buffer
; /* wrap */
2703 /* Mix in different sample rates */
2705 /* New PerfectPitch(tm) Technology (c) 1998 Rob Riggs */
2706 /* Patent Pending :-] */
2708 /* Patent enhancements (c) 2000 Ove KÃ¥ven,
2709 * TransGaming Technologies Inc. */
2711 FIXME("(%p) Adjusting frequency: %ld -> %ld (need optimization)\n",
2712 dsb
, dsb
->freq
, primarybuf
->wfx
.nSamplesPerSec
);
2714 size
= len
/ oAdvance
;
2716 ipos
= dsb
->buf_mixpos
;
2717 for (i
= 0; i
< size
; i
++) {
2718 get_fields(dsb
, (dsb
->buffer
+ ipos
), &fieldL
, &fieldR
);
2719 set_fields(obp
, fieldL
, fieldR
);
2722 dsb
->freqAcc
+= dsb
->freqAdjust
;
2723 if (dsb
->freqAcc
>= (1<<DSOUND_FREQSHIFT
)) {
2724 ULONG adv
= (dsb
->freqAcc
>>DSOUND_FREQSHIFT
) * iAdvance
;
2725 dsb
->freqAcc
&= (1<<DSOUND_FREQSHIFT
)-1;
2726 ipos
+= adv
; ilen
+= adv
;
2727 while (ipos
>= dsb
->buflen
)
2728 ipos
-= dsb
->buflen
;
2734 static void DSOUND_MixerVol(IDirectSoundBufferImpl
*dsb
, BYTE
*buf
, INT len
)
2736 INT i
, inc
= primarybuf
->wfx
.wBitsPerSample
>> 3;
2738 INT16
*bps
= (INT16
*) buf
;
2740 TRACE("(%p) left = %lx, right = %lx\n", dsb
,
2741 dsb
->volpan
.dwTotalLeftAmpFactor
, dsb
->volpan
.dwTotalRightAmpFactor
);
2742 if ((!(dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLPAN
) || (dsb
->volpan
.lPan
== 0)) &&
2743 (!(dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLVOLUME
) || (dsb
->volpan
.lVolume
== 0)) &&
2744 !(dsb
->dsbd
.dwFlags
& DSBCAPS_CTRL3D
))
2745 return; /* Nothing to do */
2747 /* If we end up with some bozo coder using panning or 3D sound */
2748 /* with a mono primary buffer, it could sound very weird using */
2749 /* this method. Oh well, tough patooties. */
2751 for (i
= 0; i
< len
; i
+= inc
) {
2757 /* 8-bit WAV is unsigned, but we need to operate */
2758 /* on signed data for this to work properly */
2760 val
= ((val
* (i
& inc
? dsb
->volpan
.dwTotalRightAmpFactor
: dsb
->volpan
.dwTotalLeftAmpFactor
)) >> 16);
2765 /* 16-bit WAV is signed -- much better */
2767 val
= ((val
* ((i
& inc
) ? dsb
->volpan
.dwTotalRightAmpFactor
: dsb
->volpan
.dwTotalLeftAmpFactor
)) >> 16);
2773 FIXME("MixerVol had a nasty error\n");
2779 static void DSOUND_Mixer3D(IDirectSoundBufferImpl
*dsb
, BYTE
*buf
, INT len
)
2782 DWORD buflen
, buf_mixpos
;
2784 buflen
= dsb
->ds3db
->buflen
;
2785 buf_mixpos
= (dsb
->buf_mixpos
* primarybuf
->wfx
.nBlockAlign
) / dsb
->wfx
.nBlockAlign
;
2786 ibp
= dsb
->ds3db
->buffer
+ buf_mixpos
;
2789 if (buf_mixpos
> buflen
) {
2790 FIXME("Major breakage\n");
2794 if (len
<= (buf_mixpos
+ buflen
))
2795 memcpy(obp
, ibp
, len
);
2797 memcpy(obp
, ibp
, buflen
- buf_mixpos
);
2798 memcpy(obp
+ (buflen
- buf_mixpos
),
2800 len
- (buflen
- buf_mixpos
));
2806 static void *tmp_buffer
;
2807 static size_t tmp_buffer_len
= 0;
2809 static void *DSOUND_tmpbuffer(size_t len
)
2811 if (len
>tmp_buffer_len
) {
2812 void *new_buffer
= realloc(tmp_buffer
, len
);
2814 tmp_buffer
= new_buffer
;
2815 tmp_buffer_len
= len
;
2822 static DWORD
DSOUND_MixInBuffer(IDirectSoundBufferImpl
*dsb
, DWORD writepos
, DWORD fraglen
)
2824 INT i
, len
, ilen
, temp
, field
;
2825 INT advance
= primarybuf
->wfx
.wBitsPerSample
>> 3;
2826 BYTE
*buf
, *ibuf
, *obuf
;
2827 INT16
*ibufs
, *obufs
;
2830 if (!(dsb
->playflags
& DSBPLAY_LOOPING
)) {
2831 temp
= MulDiv(primarybuf
->wfx
.nAvgBytesPerSec
, dsb
->buflen
,
2832 dsb
->nAvgBytesPerSec
) -
2833 MulDiv(primarybuf
->wfx
.nAvgBytesPerSec
, dsb
->buf_mixpos
,
2834 dsb
->nAvgBytesPerSec
);
2835 len
= (len
> temp
) ? temp
: len
;
2837 len
&= ~3; /* 4 byte alignment */
2840 /* This should only happen if we aren't looping and temp < 4 */
2842 /* We skip the remainder, so check for possible events */
2843 DSOUND_CheckEvent(dsb
, dsb
->buflen
- dsb
->buf_mixpos
);
2845 dsb
->state
= STATE_STOPPED
;
2847 dsb
->buf_mixpos
= 0;
2848 dsb
->leadin
= FALSE
;
2849 /* Check for DSBPN_OFFSETSTOP */
2850 DSOUND_CheckEvent(dsb
, 0);
2854 /* Been seeing segfaults in malloc() for some reason... */
2855 TRACE("allocating buffer (size = %d)\n", len
);
2856 if ((buf
= ibuf
= (BYTE
*) DSOUND_tmpbuffer(len
)) == NULL
)
2859 TRACE("MixInBuffer (%p) len = %d, dest = %ld\n", dsb
, len
, writepos
);
2861 ilen
= DSOUND_MixerNorm(dsb
, ibuf
, len
);
2862 if ((dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLPAN
) ||
2863 (dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLVOLUME
))
2864 DSOUND_MixerVol(dsb
, ibuf
, len
);
2866 obuf
= primarybuf
->buffer
+ writepos
;
2867 for (i
= 0; i
< len
; i
+= advance
) {
2868 obufs
= (INT16
*) obuf
;
2869 ibufs
= (INT16
*) ibuf
;
2870 if (primarybuf
->wfx
.wBitsPerSample
== 8) {
2871 /* 8-bit WAV is unsigned */
2872 field
= (*ibuf
- 128);
2873 field
+= (*obuf
- 128);
2874 field
= field
> 127 ? 127 : field
;
2875 field
= field
< -128 ? -128 : field
;
2876 *obuf
= field
+ 128;
2878 /* 16-bit WAV is signed */
2881 field
= field
> 32767 ? 32767 : field
;
2882 field
= field
< -32768 ? -32768 : field
;
2887 if (obuf
>= (BYTE
*)(primarybuf
->buffer
+ primarybuf
->buflen
))
2888 obuf
= primarybuf
->buffer
;
2892 if (dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLPOSITIONNOTIFY
)
2893 DSOUND_CheckEvent(dsb
, ilen
);
2895 if (dsb
->leadin
&& (dsb
->startpos
> dsb
->buf_mixpos
) && (dsb
->startpos
<= dsb
->buf_mixpos
+ ilen
)) {
2896 /* HACK... leadin should be reset when the PLAY position reaches the startpos,
2897 * not the MIX position... but if the sound buffer is bigger than our prebuffering
2898 * (which must be the case for the streaming buffers that need this hack anyway)
2899 * plus DS_HEL_MARGIN or equivalent, then this ought to work anyway. */
2900 dsb
->leadin
= FALSE
;
2903 dsb
->buf_mixpos
+= ilen
;
2905 if (dsb
->buf_mixpos
>= dsb
->buflen
) {
2906 if (!(dsb
->playflags
& DSBPLAY_LOOPING
)) {
2907 dsb
->state
= STATE_STOPPED
;
2909 dsb
->buf_mixpos
= 0;
2910 dsb
->leadin
= FALSE
;
2911 DSOUND_CheckEvent(dsb
, 0); /* For DSBPN_OFFSETSTOP */
2914 while (dsb
->buf_mixpos
>= dsb
->buflen
)
2915 dsb
->buf_mixpos
-= dsb
->buflen
;
2916 if (dsb
->leadin
&& (dsb
->startpos
<= dsb
->buf_mixpos
))
2917 dsb
->leadin
= FALSE
; /* HACK: see above */
2924 static void DSOUND_PhaseCancel(IDirectSoundBufferImpl
*dsb
, DWORD writepos
, DWORD len
)
2927 INT advance
= primarybuf
->wfx
.wBitsPerSample
>> 3;
2928 BYTE
*buf
, *ibuf
, *obuf
;
2929 INT16
*ibufs
, *obufs
;
2931 len
&= ~3; /* 4 byte alignment */
2933 TRACE("allocating buffer (size = %ld)\n", len
);
2934 if ((buf
= ibuf
= (BYTE
*) DSOUND_tmpbuffer(len
)) == NULL
)
2937 TRACE("PhaseCancel (%p) len = %ld, dest = %ld\n", dsb
, len
, writepos
);
2939 ilen
= DSOUND_MixerNorm(dsb
, ibuf
, len
);
2940 if ((dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLPAN
) ||
2941 (dsb
->dsbd
.dwFlags
& DSBCAPS_CTRLVOLUME
))
2942 DSOUND_MixerVol(dsb
, ibuf
, len
);
2944 /* subtract instead of add, to phase out premixed data */
2945 obuf
= primarybuf
->buffer
+ writepos
;
2946 for (i
= 0; i
< len
; i
+= advance
) {
2947 obufs
= (INT16
*) obuf
;
2948 ibufs
= (INT16
*) ibuf
;
2949 if (primarybuf
->wfx
.wBitsPerSample
== 8) {
2950 /* 8-bit WAV is unsigned */
2951 field
= (*ibuf
- 128);
2952 field
-= (*obuf
- 128);
2953 field
= field
> 127 ? 127 : field
;
2954 field
= field
< -128 ? -128 : field
;
2955 *obuf
= field
+ 128;
2957 /* 16-bit WAV is signed */
2960 field
= field
> 32767 ? 32767 : field
;
2961 field
= field
< -32768 ? -32768 : field
;
2966 if (obuf
>= (BYTE
*)(primarybuf
->buffer
+ primarybuf
->buflen
))
2967 obuf
= primarybuf
->buffer
;
2972 static void DSOUND_MixCancel(IDirectSoundBufferImpl
*dsb
, DWORD writepos
, BOOL cancel
)
2974 DWORD size
, flen
, len
, npos
, nlen
;
2975 INT iAdvance
= dsb
->wfx
.nBlockAlign
;
2976 INT oAdvance
= primarybuf
->wfx
.nBlockAlign
;
2977 /* determine amount of premixed data to cancel */
2978 DWORD primary_done
=
2979 ((dsb
->primary_mixpos
< writepos
) ? primarybuf
->buflen
: 0) +
2980 dsb
->primary_mixpos
- writepos
;
2982 TRACE("(%p, %ld), buf_mixpos=%ld\n", dsb
, writepos
, dsb
->buf_mixpos
);
2984 /* backtrack the mix position */
2985 size
= primary_done
/ oAdvance
;
2986 flen
= size
* dsb
->freqAdjust
;
2987 len
= (flen
>> DSOUND_FREQSHIFT
) * iAdvance
;
2988 flen
&= (1<<DSOUND_FREQSHIFT
)-1;
2989 while (dsb
->freqAcc
< flen
) {
2991 dsb
->freqAcc
+= 1<<DSOUND_FREQSHIFT
;
2994 npos
= ((dsb
->buf_mixpos
< len
) ? dsb
->buflen
: 0) +
2995 dsb
->buf_mixpos
- len
;
2996 if (dsb
->leadin
&& (dsb
->startpos
> npos
) && (dsb
->startpos
<= npos
+ len
)) {
2997 /* stop backtracking at startpos */
2998 npos
= dsb
->startpos
;
2999 len
= ((dsb
->buf_mixpos
< npos
) ? dsb
->buflen
: 0) +
3000 dsb
->buf_mixpos
- npos
;
3001 flen
= dsb
->freqAcc
;
3002 nlen
= len
/ dsb
->wfx
.nBlockAlign
;
3003 nlen
= ((nlen
<< DSOUND_FREQSHIFT
) + flen
) / dsb
->freqAdjust
;
3004 nlen
*= primarybuf
->wfx
.nBlockAlign
;
3006 ((dsb
->primary_mixpos
< nlen
) ? primarybuf
->buflen
: 0) +
3007 dsb
->primary_mixpos
- nlen
;
3010 dsb
->freqAcc
-= flen
;
3011 dsb
->buf_mixpos
= npos
;
3012 dsb
->primary_mixpos
= writepos
;
3014 TRACE("new buf_mixpos=%ld, primary_mixpos=%ld (len=%ld)\n",
3015 dsb
->buf_mixpos
, dsb
->primary_mixpos
, len
);
3017 if (cancel
) DSOUND_PhaseCancel(dsb
, writepos
, len
);
3020 static void DSOUND_MixCancelAt(IDirectSoundBufferImpl
*dsb
, DWORD buf_writepos
)
3023 DWORD i
, size
, flen
, len
, npos
, nlen
;
3024 INT iAdvance
= dsb
->wfx
.nBlockAlign
;
3025 INT oAdvance
= primarybuf
->wfx
.nBlockAlign
;
3026 /* determine amount of premixed data to cancel */
3028 ((dsb
->buf_mixpos
< buf_writepos
) ? dsb
->buflen
: 0) +
3029 dsb
->buf_mixpos
- buf_writepos
;
3032 WARN("(%p, %ld), buf_mixpos=%ld\n", dsb
, buf_writepos
, dsb
->buf_mixpos
);
3033 /* since this is not implemented yet, just cancel *ALL* prebuffering for now
3034 * (which is faster anyway when there's one a single secondary buffer) */
3035 primarybuf
->need_remix
= TRUE
;
3038 static DWORD
DSOUND_MixOne(IDirectSoundBufferImpl
*dsb
, DWORD playpos
, DWORD writepos
, DWORD mixlen
)
3041 /* determine this buffer's write position */
3042 DWORD buf_writepos
= DSOUND_CalcPlayPosition(dsb
, dsb
->state
& primarybuf
->state
, writepos
,
3043 writepos
, dsb
->primary_mixpos
, dsb
->buf_mixpos
);
3044 /* determine how much already-mixed data exists */
3046 ((dsb
->buf_mixpos
< buf_writepos
) ? dsb
->buflen
: 0) +
3047 dsb
->buf_mixpos
- buf_writepos
;
3048 DWORD primary_done
=
3049 ((dsb
->primary_mixpos
< writepos
) ? primarybuf
->buflen
: 0) +
3050 dsb
->primary_mixpos
- writepos
;
3052 ((primarybuf
->buf_mixpos
< writepos
) ? primarybuf
->buflen
: 0) +
3053 primarybuf
->buf_mixpos
- writepos
;
3056 TRACE("buf_writepos=%ld, primary_writepos=%ld\n", buf_writepos
, writepos
);
3057 TRACE("buf_done=%ld, primary_done=%ld\n", buf_done
, primary_done
);
3058 TRACE("buf_mixpos=%ld, primary_mixpos=%ld, mixlen=%ld\n", dsb
->buf_mixpos
, dsb
->primary_mixpos
,
3060 TRACE("looping=%ld, startpos=%ld, leadin=%ld\n", dsb
->playflags
, dsb
->startpos
, dsb
->leadin
);
3062 /* save write position for non-GETCURRENTPOSITION2... */
3063 dsb
->playpos
= buf_writepos
;
3065 /* check whether CalcPlayPosition detected a mixing underrun */
3066 if ((buf_done
== 0) && (dsb
->primary_mixpos
!= writepos
)) {
3067 /* it did, but did we have more to play? */
3068 if ((dsb
->playflags
& DSBPLAY_LOOPING
) ||
3069 (dsb
->buf_mixpos
< dsb
->buflen
)) {
3070 /* yes, have to recover */
3071 ERR("underrun on sound buffer %p\n", dsb
);
3072 TRACE("recovering from underrun: primary_mixpos=%ld\n", writepos
);
3074 dsb
->primary_mixpos
= writepos
;
3077 /* determine how far ahead we should mix */
3078 if (((dsb
->playflags
& DSBPLAY_LOOPING
) ||
3079 (dsb
->leadin
&& (dsb
->probably_valid_to
!= 0))) &&
3080 !(dsb
->dsbd
.dwFlags
& DSBCAPS_STATIC
)) {
3081 /* if this is a streaming buffer, it typically means that
3082 * we should defer mixing past probably_valid_to as long
3083 * as we can, to avoid unnecessary remixing */
3084 /* the heavy-looking calculations shouldn't be that bad,
3085 * as any game isn't likely to be have more than 1 or 2
3086 * streaming buffers in use at any time anyway... */
3087 DWORD probably_valid_left
=
3088 (dsb
->probably_valid_to
== (DWORD
)-1) ? dsb
->buflen
:
3089 ((dsb
->probably_valid_to
< buf_writepos
) ? dsb
->buflen
: 0) +
3090 dsb
->probably_valid_to
- buf_writepos
;
3091 /* check for leadin condition */
3092 if ((probably_valid_left
== 0) &&
3093 (dsb
->probably_valid_to
== dsb
->startpos
) &&
3095 probably_valid_left
= dsb
->buflen
;
3096 TRACE("streaming buffer probably_valid_to=%ld, probably_valid_left=%ld\n",
3097 dsb
->probably_valid_to
, probably_valid_left
);
3098 /* check whether the app's time is already up */
3099 if (probably_valid_left
< dsb
->writelead
) {
3100 WARN("probably_valid_to now within writelead, possible streaming underrun\n");
3101 /* once we pass the point of no return,
3102 * no reason to hold back anymore */
3103 dsb
->probably_valid_to
= (DWORD
)-1;
3104 /* we just have to go ahead and mix what we have,
3105 * there's no telling what the app is thinking anyway */
3107 /* adjust for our frequency and our sample size */
3108 probably_valid_left
= MulDiv(probably_valid_left
,
3109 1 << DSOUND_FREQSHIFT
,
3110 dsb
->wfx
.nBlockAlign
* dsb
->freqAdjust
) *
3111 primarybuf
->wfx
.nBlockAlign
;
3112 /* check whether to clip mix_len */
3113 if (probably_valid_left
< mixlen
) {
3114 TRACE("clipping to probably_valid_left=%ld\n", probably_valid_left
);
3115 mixlen
= probably_valid_left
;
3119 /* cut mixlen with what's already been mixed */
3120 if (mixlen
< primary_done
) {
3121 /* huh? and still CalcPlayPosition didn't
3122 * detect an underrun? */
3123 FIXME("problem with underrun detection (mixlen=%ld < primary_done=%ld)\n", mixlen
, primary_done
);
3126 len
= mixlen
- primary_done
;
3127 TRACE("remaining mixlen=%ld\n", len
);
3129 if (len
< primarybuf
->dsound
->fraglen
) {
3130 /* smaller than a fragment, wait until it gets larger
3131 * before we take the mixing overhead */
3132 TRACE("mixlen not worth it, deferring mixing\n");
3136 /* ok, we know how much to mix, let's go */
3137 still_behind
= (adv_done
> primary_done
);
3139 slen
= primarybuf
->buflen
- dsb
->primary_mixpos
;
3140 if (slen
> len
) slen
= len
;
3141 slen
= DSOUND_MixInBuffer(dsb
, dsb
->primary_mixpos
, slen
);
3143 if ((dsb
->primary_mixpos
< primarybuf
->buf_mixpos
) &&
3144 (dsb
->primary_mixpos
+ slen
>= primarybuf
->buf_mixpos
))
3145 still_behind
= FALSE
;
3147 dsb
->primary_mixpos
+= slen
; len
-= slen
;
3148 while (dsb
->primary_mixpos
>= primarybuf
->buflen
)
3149 dsb
->primary_mixpos
-= primarybuf
->buflen
;
3151 if ((dsb
->state
== STATE_STOPPED
) || !slen
) break;
3153 TRACE("new primary_mixpos=%ld, primary_advbase=%ld\n", dsb
->primary_mixpos
, primarybuf
->buf_mixpos
);
3154 TRACE("mixed data len=%ld, still_behind=%d\n", mixlen
-len
, still_behind
);
3155 /* return how far we think the primary buffer can
3156 * advance its underrun detector...*/
3157 if (still_behind
) return 0;
3158 if ((mixlen
- len
) < primary_done
) return 0;
3159 slen
= ((dsb
->primary_mixpos
< primarybuf
->buf_mixpos
) ?
3160 primarybuf
->buflen
: 0) + dsb
->primary_mixpos
-
3161 primarybuf
->buf_mixpos
;
3162 if (slen
> mixlen
) {
3163 /* the primary_done and still_behind checks above should have worked */
3164 FIXME("problem with advancement calculation (advlen=%ld > mixlen=%ld)\n", slen
, mixlen
);
3170 static DWORD
DSOUND_MixToPrimary(DWORD playpos
, DWORD writepos
, DWORD mixlen
, BOOL recover
)
3172 INT i
, len
, maxlen
= 0;
3173 IDirectSoundBufferImpl
*dsb
;
3175 TRACE("(%ld,%ld,%ld)\n", playpos
, writepos
, mixlen
);
3176 for (i
= dsound
->nrofbuffers
- 1; i
>= 0; i
--) {
3177 dsb
= dsound
->buffers
[i
];
3179 if (!dsb
|| !(ICOM_VTBL(dsb
)))
3181 if (dsb
->buflen
&& dsb
->state
&& !dsb
->hwbuf
) {
3182 TRACE("Checking %p, mixlen=%ld\n", dsb
, mixlen
);
3183 EnterCriticalSection(&(dsb
->lock
));
3184 if (dsb
->state
== STATE_STOPPING
) {
3185 DSOUND_MixCancel(dsb
, writepos
, TRUE
);
3186 dsb
->state
= STATE_STOPPED
;
3188 if ((dsb
->state
== STATE_STARTING
) || recover
)
3189 dsb
->primary_mixpos
= writepos
;
3190 len
= DSOUND_MixOne(dsb
, playpos
, writepos
, mixlen
);
3191 if (dsb
->state
== STATE_STARTING
)
3192 dsb
->state
= STATE_PLAYING
;
3193 maxlen
= (len
> maxlen
) ? len
: maxlen
;
3195 LeaveCriticalSection(&(dsb
->lock
));
3202 static void DSOUND_MixReset(DWORD writepos
)
3205 IDirectSoundBufferImpl
*dsb
;
3208 TRACE("(%ld)\n", writepos
);
3210 /* the sound of silence */
3211 nfiller
= primarybuf
->wfx
.wBitsPerSample
== 8 ? 128 : 0;
3213 /* reset all buffer mix positions */
3214 for (i
= dsound
->nrofbuffers
- 1; i
>= 0; i
--) {
3215 dsb
= dsound
->buffers
[i
];
3217 if (!dsb
|| !(ICOM_VTBL(dsb
)))
3219 if (dsb
->buflen
&& dsb
->state
&& !dsb
->hwbuf
) {
3220 TRACE("Resetting %p\n", dsb
);
3221 EnterCriticalSection(&(dsb
->lock
));
3222 if (dsb
->state
== STATE_STOPPING
) {
3223 dsb
->state
= STATE_STOPPED
;
3225 else if (dsb
->state
== STATE_STARTING
) {
3228 DSOUND_MixCancel(dsb
, writepos
, FALSE
);
3230 LeaveCriticalSection(&(dsb
->lock
));
3234 /* wipe out premixed data */
3235 if (primarybuf
->buf_mixpos
< writepos
) {
3236 memset(primarybuf
->buffer
+ writepos
, nfiller
, primarybuf
->buflen
- writepos
);
3237 memset(primarybuf
->buffer
, nfiller
, primarybuf
->buf_mixpos
);
3239 memset(primarybuf
->buffer
+ writepos
, nfiller
, primarybuf
->buf_mixpos
- writepos
);
3242 /* reset primary mix position */
3243 primarybuf
->buf_mixpos
= writepos
;
3246 static void DSOUND_CheckReset(IDirectSoundImpl
*dsound
, DWORD writepos
)
3248 if (primarybuf
->need_remix
) {
3249 DSOUND_MixReset(writepos
);
3250 primarybuf
->need_remix
= FALSE
;
3251 /* maximize Half-Life performance */
3252 dsound
->prebuf
= DS_SND_QUEUE_MIN
;
3254 /* if (dsound->prebuf < DS_SND_QUEUE_MAX) dsound->prebuf++; */
3256 TRACE("premix adjust: %d\n", dsound
->prebuf
);
3259 static void DSOUND_WaveQueue(IDirectSoundImpl
*dsound
, DWORD mixq
)
3261 if (mixq
+ dsound
->pwqueue
> DS_HEL_QUEUE
) mixq
= DS_HEL_QUEUE
- dsound
->pwqueue
;
3262 TRACE("queueing %ld buffers, starting at %d\n", mixq
, dsound
->pwwrite
);
3263 for (; mixq
; mixq
--) {
3264 waveOutWrite(dsound
->hwo
, dsound
->pwave
[dsound
->pwwrite
], sizeof(WAVEHDR
));
3266 if (dsound
->pwwrite
>= DS_HEL_FRAGS
) dsound
->pwwrite
= 0;
3271 /* #define SYNC_CALLBACK */
3273 static void DSOUND_PerformMix(void)
3279 EnterCriticalSection(&(dsound
->lock
));
3281 if (!primarybuf
|| !primarybuf
->ref
) {
3282 /* seems the primary buffer is currently being released */
3283 LeaveCriticalSection(&(dsound
->lock
));
3287 /* the sound of silence */
3288 nfiller
= primarybuf
->wfx
.wBitsPerSample
== 8 ? 128 : 0;
3290 /* whether the primary is forced to play even without secondary buffers */
3291 forced
= ((primarybuf
->state
== STATE_PLAYING
) || (primarybuf
->state
== STATE_STARTING
));
3293 TRACE("entering at %ld\n", GetTickCount());
3294 if (dsound
->priolevel
!= DSSCL_WRITEPRIMARY
) {
3295 BOOL paused
= ((primarybuf
->state
== STATE_STOPPED
) || (primarybuf
->state
== STATE_STARTING
));
3296 /* FIXME: document variables */
3297 DWORD playpos
, writepos
, inq
, maxq
, frag
;
3298 if (primarybuf
->hwbuf
) {
3299 hres
= IDsDriverBuffer_GetPosition(primarybuf
->hwbuf
, &playpos
, &writepos
);
3301 LeaveCriticalSection(&(dsound
->lock
));
3304 /* Well, we *could* do Just-In-Time mixing using the writepos,
3305 * but that's a little bit ambitious and unnecessary... */
3306 /* rather add our safety margin to the writepos, if we're playing */
3308 writepos
+= primarybuf
->writelead
;
3309 while (writepos
>= primarybuf
->buflen
)
3310 writepos
-= primarybuf
->buflen
;
3311 } else writepos
= playpos
;
3314 playpos
= dsound
->pwplay
* dsound
->fraglen
;
3317 writepos
+= DS_HEL_MARGIN
* dsound
->fraglen
;
3318 while (writepos
>= primarybuf
->buflen
)
3319 writepos
-= primarybuf
->buflen
;
3322 TRACE("primary playpos=%ld, writepos=%ld, clrpos=%ld, mixpos=%ld\n",
3323 playpos
,writepos
,primarybuf
->playpos
,primarybuf
->buf_mixpos
);
3324 /* wipe out just-played sound data */
3325 if (playpos
< primarybuf
->playpos
) {
3326 memset(primarybuf
->buffer
+ primarybuf
->playpos
, nfiller
, primarybuf
->buflen
- primarybuf
->playpos
);
3327 memset(primarybuf
->buffer
, nfiller
, playpos
);
3329 memset(primarybuf
->buffer
+ primarybuf
->playpos
, nfiller
, playpos
- primarybuf
->playpos
);
3331 primarybuf
->playpos
= playpos
;
3333 EnterCriticalSection(&(primarybuf
->lock
));
3335 /* reset mixing if necessary */
3336 DSOUND_CheckReset(dsound
, writepos
);
3338 /* check how much prebuffering is left */
3339 inq
= primarybuf
->buf_mixpos
;
3341 inq
+= primarybuf
->buflen
;
3344 /* find the maximum we can prebuffer */
3347 if (maxq
< writepos
)
3348 maxq
+= primarybuf
->buflen
;
3350 } else maxq
= primarybuf
->buflen
;
3352 /* clip maxq to dsound->prebuf */
3353 frag
= dsound
->prebuf
* dsound
->fraglen
;
3354 if (maxq
> frag
) maxq
= frag
;
3356 /* check for consistency */
3358 /* the playback position must have passed our last
3359 * mixed position, i.e. it's an underrun, or we have
3360 * nothing more to play */
3361 TRACE("reached end of mixed data (inq=%ld, maxq=%ld)\n", inq
, maxq
);
3363 /* stop the playback now, to allow buffers to refill */
3364 if (primarybuf
->state
== STATE_PLAYING
) {
3365 primarybuf
->state
= STATE_STARTING
;
3367 else if (primarybuf
->state
== STATE_STOPPING
) {
3368 primarybuf
->state
= STATE_STOPPED
;
3371 /* how can we have an underrun if we aren't playing? */
3372 WARN("unexpected primary state (%ld)\n", primarybuf
->state
);
3374 #ifdef SYNC_CALLBACK
3375 /* DSOUND_callback may need this lock */
3376 LeaveCriticalSection(&(primarybuf
->lock
));
3378 DSOUND_PrimaryStop(primarybuf
);
3379 #ifdef SYNC_CALLBACK
3380 EnterCriticalSection(&(primarybuf
->lock
));
3382 if (primarybuf
->hwbuf
) {
3383 /* the Stop is supposed to reset play position to beginning of buffer */
3384 /* unfortunately, OSS is not able to do so, so get current pointer */
3385 hres
= IDsDriverBuffer_GetPosition(primarybuf
->hwbuf
, &playpos
, NULL
);
3387 LeaveCriticalSection(&(dsound
->lock
));
3388 LeaveCriticalSection(&(primarybuf
->lock
));
3392 playpos
= dsound
->pwplay
* dsound
->fraglen
;
3395 primarybuf
->playpos
= playpos
;
3396 primarybuf
->buf_mixpos
= writepos
;
3398 maxq
= primarybuf
->buflen
;
3399 if (maxq
> frag
) maxq
= frag
;
3400 memset(primarybuf
->buffer
, nfiller
, primarybuf
->buflen
);
3405 frag
= DSOUND_MixToPrimary(playpos
, writepos
, maxq
, paused
);
3406 if (forced
) frag
= maxq
- inq
;
3407 primarybuf
->buf_mixpos
+= frag
;
3408 while (primarybuf
->buf_mixpos
>= primarybuf
->buflen
)
3409 primarybuf
->buf_mixpos
-= primarybuf
->buflen
;
3412 /* buffers have been filled, restart playback */
3413 if (primarybuf
->state
== STATE_STARTING
) {
3414 primarybuf
->state
= STATE_PLAYING
;
3416 else if (primarybuf
->state
== STATE_STOPPED
) {
3417 /* the primarybuf is supposed to play if there's something to play
3418 * even if it is reported as stopped, so don't let this confuse you */
3419 primarybuf
->state
= STATE_STOPPING
;
3421 LeaveCriticalSection(&(primarybuf
->lock
));
3423 DSOUND_PrimaryPlay(primarybuf
);
3424 TRACE("starting playback\n");
3428 LeaveCriticalSection(&(primarybuf
->lock
));
3430 /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
3431 if (primarybuf
->state
== STATE_STARTING
) {
3432 DSOUND_PrimaryPlay(primarybuf
);
3433 primarybuf
->state
= STATE_PLAYING
;
3435 else if (primarybuf
->state
== STATE_STOPPING
) {
3436 DSOUND_PrimaryStop(primarybuf
);
3437 primarybuf
->state
= STATE_STOPPED
;
3440 TRACE("completed processing at %ld\n", GetTickCount());
3441 LeaveCriticalSection(&(dsound
->lock
));
3444 static void CALLBACK
DSOUND_timer(UINT timerID
, UINT msg
, DWORD dwUser
, DWORD dw1
, DWORD dw2
)
3446 if (!dsound
|| !primarybuf
) {
3447 ERR("dsound died without killing us?\n");
3448 timeKillEvent(timerID
);
3449 timeEndPeriod(DS_TIME_RES
);
3454 DSOUND_PerformMix();
3457 static void CALLBACK
DSOUND_callback(HWAVEOUT hwo
, UINT msg
, DWORD dwUser
, DWORD dw1
, DWORD dw2
)
3459 IDirectSoundImpl
* This
= (IDirectSoundImpl
*)dwUser
;
3460 TRACE("entering at %ld, msg=%08x\n", GetTickCount(), msg
);
3461 if (msg
== MM_WOM_DONE
) {
3462 DWORD inq
, mixq
, fraglen
, buflen
, pwplay
, playpos
, mixpos
;
3463 if (This
->pwqueue
== (DWORD
)-1) {
3464 TRACE("completed due to reset\n");
3467 /* it could be a bad idea to enter critical section here... if there's lock contention,
3468 * the resulting scheduling delays might obstruct the winmm player thread */
3469 #ifdef SYNC_CALLBACK
3470 EnterCriticalSection(&(primarybuf
->lock
));
3472 /* retrieve current values */
3473 fraglen
= dsound
->fraglen
;
3474 buflen
= primarybuf
->buflen
;
3475 pwplay
= dsound
->pwplay
;
3476 playpos
= pwplay
* fraglen
;
3477 mixpos
= primarybuf
->buf_mixpos
;
3478 /* check remaining mixed data */
3479 inq
= ((mixpos
< playpos
) ? buflen
: 0) + mixpos
- playpos
;
3480 mixq
= inq
/ fraglen
;
3481 if ((inq
- (mixq
* fraglen
)) > 0) mixq
++;
3482 /* complete the playing buffer */
3483 TRACE("done playing primary pos=%ld\n", playpos
);
3485 if (pwplay
>= DS_HEL_FRAGS
) pwplay
= 0;
3486 /* write new values */
3487 dsound
->pwplay
= pwplay
;
3489 /* queue new buffer if we have data for it */
3490 if (inq
>1) DSOUND_WaveQueue(This
, inq
-1);
3491 #ifdef SYNC_CALLBACK
3492 LeaveCriticalSection(&(primarybuf
->lock
));
3495 TRACE("completed\n");
3498 /*******************************************************************************
3499 * DirectSoundCreate (DSOUND.1)
3501 HRESULT WINAPI
DirectSoundCreate(REFGUID lpGUID
,LPDIRECTSOUND
*ppDS
,IUnknown
*pUnkOuter
)
3503 IDirectSoundImpl
** ippDS
=(IDirectSoundImpl
**)ppDS
;
3504 PIDSDRIVER drv
= NULL
;
3507 HRESULT err
= DS_OK
;
3510 TRACE("(%p,%p,%p)\n",lpGUID
,ippDS
,pUnkOuter
);
3512 TRACE("DirectSoundCreate (%p)\n", ippDS
);
3515 return DSERR_INVALIDPARAM
;
3518 IDirectSound_AddRef((LPDIRECTSOUND
)dsound
);
3523 /* Enumerate WINMM audio devices and find the one we want */
3524 wodn
= waveOutGetNumDevs();
3525 if (!wodn
) return DSERR_NODRIVER
;
3527 /* FIXME: How do we find the GUID of an audio device? */
3528 /* Well, just use the first available device for now... */
3530 /* Get output device caps */
3531 waveOutGetDevCapsA(wod
, &wcaps
, sizeof(wcaps
));
3532 /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
3533 waveOutMessage(wod
, DRV_QUERYDSOUNDIFACE
, (DWORD
)&drv
, 0);
3535 /* Allocate memory */
3536 *ippDS
= (IDirectSoundImpl
*)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSoundImpl
));
3538 return DSERR_OUTOFMEMORY
;
3540 ICOM_VTBL(*ippDS
) = &dsvt
;
3543 (*ippDS
)->driver
= drv
;
3544 (*ippDS
)->fraglen
= 0;
3545 (*ippDS
)->priolevel
= DSSCL_NORMAL
;
3546 (*ippDS
)->nrofbuffers
= 0;
3547 (*ippDS
)->buffers
= NULL
;
3548 (*ippDS
)->primary
= NULL
;
3549 (*ippDS
)->listener
= NULL
;
3551 (*ippDS
)->prebuf
= DS_SND_QUEUE_MAX
;
3553 /* Get driver description */
3555 IDsDriver_GetDriverDesc(drv
,&((*ippDS
)->drvdesc
));
3557 /* if no DirectSound interface available, use WINMM API instead */
3558 (*ippDS
)->drvdesc
.dwFlags
= DSDDESC_DOMMSYSTEMOPEN
| DSDDESC_DOMMSYSTEMSETFORMAT
;
3559 (*ippDS
)->drvdesc
.dnDevNode
= wod
; /* FIXME? */
3562 /* Set default wave format (may need it for waveOutOpen) */
3563 (*ippDS
)->wfx
.wFormatTag
= WAVE_FORMAT_PCM
;
3564 (*ippDS
)->wfx
.nChannels
= 2;
3565 (*ippDS
)->wfx
.nSamplesPerSec
= 22050;
3566 (*ippDS
)->wfx
.nAvgBytesPerSec
= 44100;
3567 (*ippDS
)->wfx
.nBlockAlign
= 2;
3568 (*ippDS
)->wfx
.wBitsPerSample
= 8;
3570 /* If the driver requests being opened through MMSYSTEM
3571 * (which is recommended by the DDK), it is supposed to happen
3572 * before the DirectSound interface is opened */
3573 if ((*ippDS
)->drvdesc
.dwFlags
& DSDDESC_DOMMSYSTEMOPEN
) {
3574 /* FIXME: is this right? */
3575 err
= mmErr(waveOutOpen(&((*ippDS
)->hwo
), (*ippDS
)->drvdesc
.dnDevNode
, &((*ippDS
)->wfx
),
3576 (DWORD
)DSOUND_callback
, (DWORD
)(*ippDS
),
3577 CALLBACK_FUNCTION
| WAVE_DIRECTSOUND
));
3580 if (drv
&& (err
== DS_OK
))
3581 err
= IDsDriver_Open(drv
);
3583 /* FIXME: do we want to handle a temporarily busy device? */
3585 HeapFree(GetProcessHeap(),0,*ippDS
);
3590 /* the driver is now open, so it's now allowed to call GetCaps */
3592 IDsDriver_GetCaps(drv
,&((*ippDS
)->drvcaps
));
3596 /* FIXME: look at wcaps */
3597 (*ippDS
)->drvcaps
.dwFlags
=
3598 DSCAPS_PRIMARY16BIT
| DSCAPS_PRIMARYSTEREO
;
3600 (*ippDS
)->drvcaps
.dwFlags
|= DSCAPS_EMULDRIVER
;
3602 /* Allocate memory for HEL buffer headers */
3603 for (c
=0; c
<DS_HEL_FRAGS
; c
++) {
3604 (*ippDS
)->pwave
[c
] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,sizeof(WAVEHDR
));
3605 if (!(*ippDS
)->pwave
[c
]) {
3606 /* Argh, out of memory */
3608 HeapFree(GetProcessHeap(),0,(*ippDS
)->pwave
[c
]);
3609 waveOutClose((*ippDS
)->hwo
);
3610 HeapFree(GetProcessHeap(),0,*ippDS
);
3612 return DSERR_OUTOFMEMORY
;
3618 InitializeCriticalSection(&((*ippDS
)->lock
));
3622 if (primarybuf
== NULL
) {
3626 dsbd
.dwSize
= sizeof(DSBUFFERDESC
);
3627 dsbd
.dwFlags
= DSBCAPS_PRIMARYBUFFER
;
3628 dsbd
.dwBufferBytes
= 0;
3629 dsbd
.lpwfxFormat
= &(dsound
->wfx
);
3630 hr
= IDirectSound_CreateSoundBuffer(*ppDS
, &dsbd
, (LPDIRECTSOUNDBUFFER
*)&primarybuf
, NULL
);
3634 /* dsound->primary is NULL - don't need to Release */
3635 dsound
->primary
= primarybuf
;
3636 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER
)primarybuf
);
3638 timeBeginPeriod(DS_TIME_RES
);
3639 dsound
->timerID
= timeSetEvent(DS_TIME_DEL
, DS_TIME_RES
, DSOUND_timer
,
3640 (DWORD
)dsound
, TIME_PERIODIC
| TIME_CALLBACK_FUNCTION
);
3645 /***************************************************************************
3646 * DirectSoundCaptureCreate [DSOUND.6]
3648 * Create and initialize a DirectSoundCapture interface
3652 * Failure: DSERR_NOAGGREGATION, DSERR_ALLOCATED, DSERR_INVALIDPARAM,
3655 HRESULT WINAPI
DirectSoundCaptureCreate(
3657 LPDIRECTSOUNDCAPTURE
* lplpDSC
,
3658 LPUNKNOWN pUnkOuter
)
3660 TRACE("(%s,%p,%p)\n", debugstr_guid(lpcGUID
), lplpDSC
, pUnkOuter
);
3663 return DSERR_NOAGGREGATION
;
3666 /* Default device? */
3668 return DSOUND_CreateDirectSoundCapture( (LPVOID
*)lplpDSC
);
3671 FIXME( "Unknown GUID %s\n", debugstr_guid(lpcGUID
) );
3674 return DSERR_OUTOFMEMORY
;
3677 /***************************************************************************
3678 * DirectSoundCaptureEnumerateA [DSOUND.7]
3680 * Enumerate all DirectSound drivers installed in the system
3684 * Failure: DSERR_INVALIDPARAM
3686 HRESULT WINAPI
DirectSoundCaptureEnumerateA(
3687 LPDSENUMCALLBACKA lpDSEnumCallback
,
3690 TRACE("(%p,%p)\n", lpDSEnumCallback
, lpContext
);
3692 if ( lpDSEnumCallback
)
3693 lpDSEnumCallback(NULL
,"WINE Primary Sound Capture Driver",
3694 "SoundCap",lpContext
);
3700 /***************************************************************************
3701 * DirectSoundCaptureEnumerateW [DSOUND.8]
3703 * Enumerate all DirectSound drivers installed in the system
3707 * Failure: DSERR_INVALIDPARAM
3709 HRESULT WINAPI
DirectSoundCaptureEnumerateW(
3710 LPDSENUMCALLBACKW lpDSEnumCallback
,
3713 FIXME("(%p,%p):stub\n", lpDSEnumCallback
, lpContext
);
3718 DSOUND_CreateDirectSoundCapture( LPVOID
* ppobj
)
3720 *ppobj
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof( IDirectSoundCaptureImpl
) );
3722 if ( *ppobj
== NULL
) {
3723 return DSERR_OUTOFMEMORY
;
3727 ICOM_THIS(IDirectSoundCaptureImpl
,*ppobj
);
3730 ICOM_VTBL(This
) = &dscvt
;
3732 InitializeCriticalSection( &This
->lock
);
3738 static HRESULT WINAPI
3739 IDirectSoundCaptureImpl_QueryInterface(
3740 LPDIRECTSOUNDCAPTURE iface
,
3744 ICOM_THIS(IDirectSoundCaptureImpl
,iface
);
3746 FIXME( "(%p)->(%s,%p): stub\n", This
, debugstr_guid(riid
), ppobj
);
3752 IDirectSoundCaptureImpl_AddRef( LPDIRECTSOUNDCAPTURE iface
)
3755 ICOM_THIS(IDirectSoundCaptureImpl
,iface
);
3757 EnterCriticalSection( &This
->lock
);
3759 TRACE( "(%p) was 0x%08lx\n", This
, This
->ref
);
3760 uRef
= ++(This
->ref
);
3762 LeaveCriticalSection( &This
->lock
);
3768 IDirectSoundCaptureImpl_Release( LPDIRECTSOUNDCAPTURE iface
)
3771 ICOM_THIS(IDirectSoundCaptureImpl
,iface
);
3773 EnterCriticalSection( &This
->lock
);
3775 TRACE( "(%p) was 0x%08lx\n", This
, This
->ref
);
3776 uRef
= --(This
->ref
);
3778 LeaveCriticalSection( &This
->lock
);
3781 DeleteCriticalSection( &This
->lock
);
3782 HeapFree( GetProcessHeap(), 0, This
);
3788 static HRESULT WINAPI
3789 IDirectSoundCaptureImpl_CreateCaptureBuffer(
3790 LPDIRECTSOUNDCAPTURE iface
,
3791 LPCDSCBUFFERDESC lpcDSCBufferDesc
,
3792 LPDIRECTSOUNDCAPTUREBUFFER
* lplpDSCaptureBuffer
,
3796 ICOM_THIS(IDirectSoundCaptureImpl
,iface
);
3798 TRACE( "(%p)->(%p,%p,%p)\n", This
, lpcDSCBufferDesc
, lplpDSCaptureBuffer
, pUnk
);
3801 return DSERR_INVALIDPARAM
;
3804 hr
= DSOUND_CreateDirectSoundCaptureBuffer( lpcDSCBufferDesc
, (LPVOID
*)lplpDSCaptureBuffer
);
3809 static HRESULT WINAPI
3810 IDirectSoundCaptureImpl_GetCaps(
3811 LPDIRECTSOUNDCAPTURE iface
,
3812 LPDSCCAPS lpDSCCaps
)
3814 ICOM_THIS(IDirectSoundCaptureImpl
,iface
);
3816 FIXME( "(%p)->(%p): stub\n", This
, lpDSCCaps
);
3821 static HRESULT WINAPI
3822 IDirectSoundCaptureImpl_Initialize(
3823 LPDIRECTSOUNDCAPTURE iface
,
3826 ICOM_THIS(IDirectSoundCaptureImpl
,iface
);
3828 FIXME( "(%p)->(%p): stub\n", This
, lpcGUID
);
3834 static ICOM_VTABLE(IDirectSoundCapture
) dscvt
=
3836 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3837 /* IUnknown methods */
3838 IDirectSoundCaptureImpl_QueryInterface
,
3839 IDirectSoundCaptureImpl_AddRef
,
3840 IDirectSoundCaptureImpl_Release
,
3842 /* IDirectSoundCapture methods */
3843 IDirectSoundCaptureImpl_CreateCaptureBuffer
,
3844 IDirectSoundCaptureImpl_GetCaps
,
3845 IDirectSoundCaptureImpl_Initialize
3849 DSOUND_CreateDirectSoundCaptureBuffer( LPCDSCBUFFERDESC lpcDSCBufferDesc
, LPVOID
* ppobj
)
3852 FIXME( "(%p,%p): ignoring lpcDSCBufferDesc\n", lpcDSCBufferDesc
, ppobj
);
3854 *ppobj
= HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof( IDirectSoundCaptureBufferImpl
) );
3856 if ( *ppobj
== NULL
) {
3857 return DSERR_OUTOFMEMORY
;
3861 ICOM_THIS(IDirectSoundCaptureBufferImpl
,*ppobj
);
3864 ICOM_VTBL(This
) = &dscbvt
;
3866 InitializeCriticalSection( &This
->lock
);
3873 static HRESULT WINAPI
3874 IDirectSoundCaptureBufferImpl_QueryInterface(
3875 LPDIRECTSOUNDCAPTUREBUFFER iface
,
3879 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
3881 FIXME( "(%p)->(%s,%p): stub\n", This
, debugstr_guid(riid
), ppobj
);
3887 IDirectSoundCaptureBufferImpl_AddRef( LPDIRECTSOUNDCAPTUREBUFFER iface
)
3890 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
3892 EnterCriticalSection( &This
->lock
);
3894 TRACE( "(%p) was 0x%08lx\n", This
, This
->ref
);
3895 uRef
= ++(This
->ref
);
3897 LeaveCriticalSection( &This
->lock
);
3903 IDirectSoundCaptureBufferImpl_Release( LPDIRECTSOUNDCAPTUREBUFFER iface
)
3906 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
3908 EnterCriticalSection( &This
->lock
);
3910 TRACE( "(%p) was 0x%08lx\n", This
, This
->ref
);
3911 uRef
= --(This
->ref
);
3913 LeaveCriticalSection( &This
->lock
);
3916 DeleteCriticalSection( &This
->lock
);
3917 HeapFree( GetProcessHeap(), 0, This
);
3923 static HRESULT WINAPI
3924 IDirectSoundCaptureBufferImpl_GetCaps(
3925 LPDIRECTSOUNDCAPTUREBUFFER iface
,
3926 LPDSCBCAPS lpDSCBCaps
)
3928 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
3930 FIXME( "(%p)->(%p): stub\n", This
, lpDSCBCaps
);
3935 static HRESULT WINAPI
3936 IDirectSoundCaptureBufferImpl_GetCurrentPosition(
3937 LPDIRECTSOUNDCAPTUREBUFFER iface
,
3938 LPDWORD lpdwCapturePosition
,
3939 LPDWORD lpdwReadPosition
)
3941 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
3943 FIXME( "(%p)->(%p,%p): stub\n", This
, lpdwCapturePosition
, lpdwReadPosition
);
3948 static HRESULT WINAPI
3949 IDirectSoundCaptureBufferImpl_GetFormat(
3950 LPDIRECTSOUNDCAPTUREBUFFER iface
,
3951 LPWAVEFORMATEX lpwfxFormat
,
3952 DWORD dwSizeAllocated
,
3953 LPDWORD lpdwSizeWritten
)
3955 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
3957 FIXME( "(%p)->(%p,0x%08lx,%p): stub\n", This
, lpwfxFormat
, dwSizeAllocated
, lpdwSizeWritten
);
3962 static HRESULT WINAPI
3963 IDirectSoundCaptureBufferImpl_GetStatus(
3964 LPDIRECTSOUNDCAPTUREBUFFER iface
,
3965 LPDWORD lpdwStatus
)
3967 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
3969 FIXME( "(%p)->(%p): stub\n", This
, lpdwStatus
);
3974 static HRESULT WINAPI
3975 IDirectSoundCaptureBufferImpl_Initialize(
3976 LPDIRECTSOUNDCAPTUREBUFFER iface
,
3977 LPDIRECTSOUNDCAPTURE lpDSC
,
3978 LPCDSCBUFFERDESC lpcDSCBDesc
)
3980 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
3982 FIXME( "(%p)->(%p,%p): stub\n", This
, lpDSC
, lpcDSCBDesc
);
3987 static HRESULT WINAPI
3988 IDirectSoundCaptureBufferImpl_Lock(
3989 LPDIRECTSOUNDCAPTUREBUFFER iface
,
3992 LPVOID
* lplpvAudioPtr1
,
3993 LPDWORD lpdwAudioBytes1
,
3994 LPVOID
* lplpvAudioPtr2
,
3995 LPDWORD lpdwAudioBytes2
,
3998 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
4000 FIXME( "(%p)->(%08lu,%08lu,%p,%p,%p,%p,0x%08lx): stub\n", This
, dwReadCusor
, dwReadBytes
, lplpvAudioPtr1
, lpdwAudioBytes1
, lplpvAudioPtr2
, lpdwAudioBytes2
, dwFlags
);
4005 static HRESULT WINAPI
4006 IDirectSoundCaptureBufferImpl_Start(
4007 LPDIRECTSOUNDCAPTUREBUFFER iface
,
4010 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
4012 FIXME( "(%p)->(0x%08lx): stub\n", This
, dwFlags
);
4017 static HRESULT WINAPI
4018 IDirectSoundCaptureBufferImpl_Stop( LPDIRECTSOUNDCAPTUREBUFFER iface
)
4020 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
4022 FIXME( "(%p): stub\n", This
);
4027 static HRESULT WINAPI
4028 IDirectSoundCaptureBufferImpl_Unlock(
4029 LPDIRECTSOUNDCAPTUREBUFFER iface
,
4030 LPVOID lpvAudioPtr1
,
4031 DWORD dwAudioBytes1
,
4032 LPVOID lpvAudioPtr2
,
4033 DWORD dwAudioBytes2
)
4035 ICOM_THIS(IDirectSoundCaptureBufferImpl
,iface
);
4037 FIXME( "(%p)->(%p,%08lu,%p,%08lu): stub\n", This
, lpvAudioPtr1
, dwAudioBytes1
, lpvAudioPtr2
, dwAudioBytes2
);
4043 static ICOM_VTABLE(IDirectSoundCaptureBuffer
) dscbvt
=
4045 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
4046 /* IUnknown methods */
4047 IDirectSoundCaptureBufferImpl_QueryInterface
,
4048 IDirectSoundCaptureBufferImpl_AddRef
,
4049 IDirectSoundCaptureBufferImpl_Release
,
4051 /* IDirectSoundCaptureBuffer methods */
4052 IDirectSoundCaptureBufferImpl_GetCaps
,
4053 IDirectSoundCaptureBufferImpl_GetCurrentPosition
,
4054 IDirectSoundCaptureBufferImpl_GetFormat
,
4055 IDirectSoundCaptureBufferImpl_GetStatus
,
4056 IDirectSoundCaptureBufferImpl_Initialize
,
4057 IDirectSoundCaptureBufferImpl_Lock
,
4058 IDirectSoundCaptureBufferImpl_Start
,
4059 IDirectSoundCaptureBufferImpl_Stop
,
4060 IDirectSoundCaptureBufferImpl_Unlock
4063 /*******************************************************************************
4064 * DirectSound ClassFactory
4068 /* IUnknown fields */
4069 ICOM_VFIELD(IClassFactory
);
4071 } IClassFactoryImpl
;
4073 static HRESULT WINAPI
4074 DSCF_QueryInterface(LPCLASSFACTORY iface
,REFIID riid
,LPVOID
*ppobj
) {
4075 ICOM_THIS(IClassFactoryImpl
,iface
);
4077 FIXME("(%p)->(%s,%p),stub!\n",This
,debugstr_guid(riid
),ppobj
);
4078 return E_NOINTERFACE
;
4082 DSCF_AddRef(LPCLASSFACTORY iface
) {
4083 ICOM_THIS(IClassFactoryImpl
,iface
);
4084 return ++(This
->ref
);
4087 static ULONG WINAPI
DSCF_Release(LPCLASSFACTORY iface
) {
4088 ICOM_THIS(IClassFactoryImpl
,iface
);
4089 /* static class, won't be freed */
4090 return --(This
->ref
);
4093 static HRESULT WINAPI
DSCF_CreateInstance(
4094 LPCLASSFACTORY iface
,LPUNKNOWN pOuter
,REFIID riid
,LPVOID
*ppobj
4096 ICOM_THIS(IClassFactoryImpl
,iface
);
4098 TRACE("(%p)->(%p,%s,%p)\n",This
,pOuter
,debugstr_guid(riid
),ppobj
);
4099 if ( IsEqualGUID( &IID_IDirectSound
, riid
) ) {
4100 /* FIXME: reuse already created dsound if present? */
4101 return DirectSoundCreate(riid
,(LPDIRECTSOUND
*)ppobj
,pOuter
);
4103 return E_NOINTERFACE
;
4106 static HRESULT WINAPI
DSCF_LockServer(LPCLASSFACTORY iface
,BOOL dolock
) {
4107 ICOM_THIS(IClassFactoryImpl
,iface
);
4108 FIXME("(%p)->(%d),stub!\n",This
,dolock
);
4112 static ICOM_VTABLE(IClassFactory
) DSCF_Vtbl
= {
4113 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
4114 DSCF_QueryInterface
,
4117 DSCF_CreateInstance
,
4120 static IClassFactoryImpl DSOUND_CF
= {&DSCF_Vtbl
, 1 };
4122 /*******************************************************************************
4123 * DllGetClassObject [DSOUND.5]
4124 * Retrieves class object from a DLL object
4127 * Docs say returns STDAPI
4130 * rclsid [I] CLSID for the class object
4131 * riid [I] Reference to identifier of interface for class object
4132 * ppv [O] Address of variable to receive interface pointer for riid
4136 * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
4139 DWORD WINAPI
DSOUND_DllGetClassObject(REFCLSID rclsid
,REFIID riid
,LPVOID
*ppv
)
4141 TRACE("(%p,%p,%p)\n", debugstr_guid(rclsid
), debugstr_guid(riid
), ppv
);
4142 if ( IsEqualCLSID( &IID_IClassFactory
, riid
) ) {
4143 *ppv
= (LPVOID
)&DSOUND_CF
;
4144 IClassFactory_AddRef((IClassFactory
*)*ppv
);
4148 FIXME("(%p,%p,%p): no interface found.\n", debugstr_guid(rclsid
), debugstr_guid(riid
), ppv
);
4149 return CLASS_E_CLASSNOTAVAILABLE
;
4153 /*******************************************************************************
4154 * DllCanUnloadNow [DSOUND.4] Determines whether the DLL is in use.
4160 DWORD WINAPI
DSOUND_DllCanUnloadNow(void)
4162 FIXME("(void): stub\n");