3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998 Rob Riggs
5 * Copyright 2000-2001 TransGaming Technologies, Inc.
8 * Most thread locking is complete. There may be a few race
9 * conditions still lurking.
11 * Tested with a Soundblaster clone, a Gravis UltraSound Classic,
12 * and a Turtle Beach Tropez+.
15 * Implement DirectSoundCapture API
16 * Implement SetCooperativeLevel properly (need to address focus issues)
17 * Implement DirectSound3DBuffers (stubs in place)
18 * Use hardware 3D support if available
19 * Add critical section locking inside Release and AddRef methods
20 * Handle static buffers - put those in hardware, non-static not in hardware
21 * Hardware DuplicateSoundBuffer
22 * Proper volume calculation, and setting volume in HEL primary buffer
23 * Optimize WINMM and negotiate fragment size, decrease DS_HEL_MARGIN
29 #include <sys/types.h>
30 #include <sys/fcntl.h>
34 #include <math.h> /* Insomnia - pow() function */
43 #include "wine/windef16.h"
44 #include "debugtools.h"
48 DEFAULT_DEBUG_CHANNEL(dsound);
50 /* these are eligible for tuning... they must be high on slow machines... */
51 /* especially since the WINMM overhead is pretty high, and could be improved quite a bit;
52 * the high DS_HEL_MARGIN reflects the currently high wineoss/HEL latency
53 * some settings here should probably get ported to wine.conf */
54 #define DS_EMULDRIVER 1 /* some games (Quake 2, UT) refuse to accept
55 emulated dsound devices. set to 0 ! */
56 #define DS_HEL_FRAGS 48 /* HEL only: number of waveOut fragments in primary buffer */
57 #define DS_HEL_MARGIN 4 /* HEL only: number of waveOut fragments ahead to mix in new buffers */
59 #define DS_SND_QUEUE 28 /* max number of fragments to prebuffer */
61 /* Linux does not support better timing than 10ms */
62 #define DS_TIME_RES 10 /* Resolution of multimedia timer */
63 #define DS_TIME_DEL 10 /* Delay of multimedia timer callback, and duration of HEL fragment */
65 /*****************************************************************************
66 * Predeclare the interface implementation structures
68 typedef struct IDirectSoundImpl IDirectSoundImpl;
69 typedef struct IDirectSoundBufferImpl IDirectSoundBufferImpl;
70 typedef struct IDirectSoundNotifyImpl IDirectSoundNotifyImpl;
71 typedef struct IDirectSound3DListenerImpl IDirectSound3DListenerImpl;
72 typedef struct IDirectSound3DBufferImpl IDirectSound3DBufferImpl;
73 typedef struct IDirectSoundCaptureImpl IDirectSoundCaptureImpl;
74 typedef struct IDirectSoundCaptureBufferImpl IDirectSoundCaptureBufferImpl;
75 typedef struct IKsPropertySetImpl IKsPropertySetImpl;
77 /*****************************************************************************
78 * IDirectSound implementation structure
80 struct IDirectSoundImpl
83 ICOM_VFIELD(IDirectSound);
85 /* IDirectSoundImpl fields */
90 LPWAVEHDR pwave[DS_HEL_FRAGS];
91 UINT timerID, pwplay, pwwrite, pwqueue;
95 IDirectSoundBufferImpl** buffers;
96 IDirectSoundBufferImpl* primary;
97 IDirectSound3DListenerImpl* listener;
98 WAVEFORMATEX wfx; /* current main waveformat */
99 CRITICAL_SECTION lock;
102 /*****************************************************************************
103 * IDirectSoundBuffer implementation structure
105 struct IDirectSoundBufferImpl
107 /* FIXME: document */
108 /* IUnknown fields */
109 ICOM_VFIELD(IDirectSoundBuffer);
111 /* IDirectSoundBufferImpl fields */
112 PIDSDRIVERBUFFER hwbuf;
115 IDirectSound3DBufferImpl* ds3db;
116 DWORD playflags,state,leadin;
117 DWORD playpos,startpos,writelead,buflen;
118 DWORD nAvgBytesPerSec;
121 IDirectSoundBufferImpl* parent; /* for duplicates */
122 IDirectSoundImpl* dsound;
124 LPDSBPOSITIONNOTIFY notifies;
126 CRITICAL_SECTION lock;
127 /* used for frequency conversion (PerfectPitch) */
128 ULONG freqAdjust, freqAcc;
129 /* used for intelligent (well, sort of) prebuffering */
130 DWORD probably_valid_to;
131 DWORD primary_mixpos, buf_mixpos;
134 #define STATE_STOPPED 0
135 #define STATE_STARTING 1
136 #define STATE_PLAYING 2
137 #define STATE_STOPPING 3
139 /*****************************************************************************
140 * IDirectSoundNotify implementation structure
142 struct IDirectSoundNotifyImpl
144 /* IUnknown fields */
145 ICOM_VFIELD(IDirectSoundNotify);
147 /* IDirectSoundNotifyImpl fields */
148 IDirectSoundBufferImpl* dsb;
151 /*****************************************************************************
152 * IDirectSound3DListener implementation structure
154 struct IDirectSound3DListenerImpl
156 /* IUnknown fields */
157 ICOM_VFIELD(IDirectSound3DListener);
159 /* IDirectSound3DListenerImpl fields */
160 IDirectSoundBufferImpl* dsb;
162 CRITICAL_SECTION lock;
165 struct IKsPropertySetImpl
167 /* IUnknown fields */
168 ICOM_VFIELD(IKsPropertySet);
170 /* IKsPropertySetImpl fields */
171 IDirectSound3DBufferImpl *ds3db; /* backptr, no ref */
174 /*****************************************************************************
175 * IDirectSound3DBuffer implementation structure
177 struct IDirectSound3DBufferImpl
179 /* IUnknown fields */
180 ICOM_VFIELD(IDirectSound3DBuffer);
182 /* IDirectSound3DBufferImpl fields */
183 IDirectSoundBufferImpl* dsb;
187 CRITICAL_SECTION lock;
188 IKsPropertySetImpl* iks;
192 /*****************************************************************************
193 * IDirectSoundCapture implementation structure
195 struct IDirectSoundCaptureImpl
197 /* IUnknown fields */
198 ICOM_VFIELD(IDirectSoundCapture);
201 /* IDirectSoundCaptureImpl fields */
202 CRITICAL_SECTION lock;
205 /*****************************************************************************
206 * IDirectSoundCapture implementation structure
208 struct IDirectSoundCaptureBufferImpl
210 /* IUnknown fields */
211 ICOM_VFIELD(IDirectSoundCaptureBuffer);
214 /* IDirectSoundCaptureBufferImpl fields */
215 CRITICAL_SECTION lock;
219 /* #define USE_DSOUND3D 1 */
221 #define DSOUND_FREQSHIFT (14)
223 static IDirectSoundImpl* dsound = NULL;
225 static IDirectSoundBufferImpl* primarybuf = NULL;
227 static void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len);
229 static HRESULT DSOUND_CreateDirectSoundCapture( LPVOID* ppobj );
230 static HRESULT DSOUND_CreateDirectSoundCaptureBuffer( LPCDSCBUFFERDESC lpcDSCBufferDesc, LPVOID* ppobj );
232 static ICOM_VTABLE(IDirectSoundCapture) dscvt;
233 static ICOM_VTABLE(IDirectSoundCaptureBuffer) dscbvt;
235 static HRESULT mmErr(UINT err)
238 case MMSYSERR_NOERROR:
240 case MMSYSERR_ALLOCATED:
241 return DSERR_ALLOCATED;
242 case MMSYSERR_INVALHANDLE:
243 return DSERR_GENERIC; /* FIXME */
244 case MMSYSERR_NODRIVER:
245 return DSERR_NODRIVER;
247 return DSERR_OUTOFMEMORY;
248 case MMSYSERR_INVALPARAM:
249 return DSERR_INVALIDPARAM;
251 FIXME("Unknown MMSYS error %d\n",err);
252 return DSERR_GENERIC;
256 /***************************************************************************
257 * DirectSoundEnumerateA [DSOUND.2]
259 * Enumerate all DirectSound drivers installed in the system
263 * Failure: DSERR_INVALIDPARAM
265 HRESULT WINAPI DirectSoundEnumerateA(
266 LPDSENUMCALLBACKA lpDSEnumCallback,
269 TRACE("lpDSEnumCallback = %p, lpContext = %p\n",
270 lpDSEnumCallback, lpContext);
273 if (lpDSEnumCallback != NULL)
274 lpDSEnumCallback(NULL,"WINE DirectSound using Open Sound System",
281 /***************************************************************************
282 * DirectSoundEnumerateW [DSOUND.3]
284 * Enumerate all DirectSound drivers installed in the system
288 * Failure: DSERR_INVALIDPARAM
290 HRESULT WINAPI DirectSoundEnumerateW(
291 LPDSENUMCALLBACKW lpDSEnumCallback,
294 FIXME("lpDSEnumCallback = %p, lpContext = %p: stub\n",
295 lpDSEnumCallback, lpContext);
301 static void _dump_DSBCAPS(DWORD xmask) {
306 #define FE(x) { x, #x },
307 FE(DSBCAPS_PRIMARYBUFFER)
309 FE(DSBCAPS_LOCHARDWARE)
310 FE(DSBCAPS_LOCSOFTWARE)
312 FE(DSBCAPS_CTRLFREQUENCY)
314 FE(DSBCAPS_CTRLVOLUME)
315 FE(DSBCAPS_CTRLPOSITIONNOTIFY)
316 FE(DSBCAPS_CTRLDEFAULT)
318 FE(DSBCAPS_STICKYFOCUS)
319 FE(DSBCAPS_GLOBALFOCUS)
320 FE(DSBCAPS_GETCURRENTPOSITION2)
321 FE(DSBCAPS_MUTE3DATMAXDISTANCE)
326 for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
327 if ((flags[i].mask & xmask) == flags[i].mask)
328 DPRINTF("%s ",flags[i].name);
331 /*******************************************************************************
335 /* IUnknown methods */
337 static HRESULT WINAPI IKsPropertySetImpl_QueryInterface(
338 LPKSPROPERTYSET iface, REFIID riid, LPVOID *ppobj
340 ICOM_THIS(IKsPropertySetImpl,iface);
342 FIXME("(%p,%s,%p), stub!\n",This,debugstr_guid(riid),ppobj);
348 static ULONG WINAPI IKsPropertySetImpl_AddRef(LPKSPROPERTYSET iface) {
349 ICOM_THIS(IKsPropertySetImpl,iface);
357 static ULONG WINAPI IKsPropertySetImpl_Release(LPKSPROPERTYSET iface) {
358 ICOM_THIS(IKsPropertySetImpl,iface);
366 static HRESULT WINAPI IKsPropertySetImpl_Get(LPKSPROPERTYSET iface,
367 REFGUID guidPropSet, ULONG dwPropID,
368 LPVOID pInstanceData, ULONG cbInstanceData,
369 LPVOID pPropData, ULONG cbPropData,
372 ICOM_THIS(IKsPropertySetImpl,iface);
374 FIXME("(%p,%s,%ld,%p,%ld,%p,%ld,%p), stub!\n",This,debugstr_guid(guidPropSet),dwPropID,pInstanceData,cbInstanceData,pPropData,cbPropData,pcbReturned);
375 return E_PROP_ID_UNSUPPORTED;
380 static HRESULT WINAPI IKsPropertySetImpl_Set(LPKSPROPERTYSET iface,
381 REFGUID guidPropSet, ULONG dwPropID,
382 LPVOID pInstanceData, ULONG cbInstanceData,
383 LPVOID pPropData, ULONG cbPropData
385 ICOM_THIS(IKsPropertySetImpl,iface);
387 FIXME("(%p,%s,%ld,%p,%ld,%p,%ld), stub!\n",This,debugstr_guid(guidPropSet),dwPropID,pInstanceData,cbInstanceData,pPropData,cbPropData);
388 return E_PROP_ID_UNSUPPORTED;
393 static HRESULT WINAPI IKsPropertySetImpl_QuerySupport(LPKSPROPERTYSET iface,
394 REFGUID guidPropSet, ULONG dwPropID, PULONG pTypeSupport
396 ICOM_THIS(IKsPropertySetImpl,iface);
398 FIXME("(%p,%s,%ld,%p), stub!\n",This,debugstr_guid(guidPropSet),dwPropID,pTypeSupport);
399 return E_PROP_ID_UNSUPPORTED;
404 static ICOM_VTABLE(IKsPropertySet) iksvt = {
405 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
406 IKsPropertySetImpl_QueryInterface,
407 IKsPropertySetImpl_AddRef,
408 IKsPropertySetImpl_Release,
409 IKsPropertySetImpl_Get,
410 IKsPropertySetImpl_Set,
411 IKsPropertySetImpl_QuerySupport
415 /*******************************************************************************
416 * IDirectSound3DBuffer
419 /* IUnknown methods */
421 static HRESULT WINAPI IDirectSound3DBufferImpl_QueryInterface(
422 LPDIRECTSOUND3DBUFFER iface, REFIID riid, LPVOID *ppobj)
424 ICOM_THIS(IDirectSound3DBufferImpl,iface);
426 if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
427 IDirectSound3DBuffer_AddRef(iface);
432 FIXME("(%p,%s,%p), no such interface.\n",This,debugstr_guid(riid),ppobj);
438 static ULONG WINAPI IDirectSound3DBufferImpl_AddRef(LPDIRECTSOUND3DBUFFER iface)
440 ICOM_THIS(IDirectSound3DBufferImpl,iface);
447 static ULONG WINAPI IDirectSound3DBufferImpl_Release(LPDIRECTSOUND3DBUFFER iface)
449 ICOM_THIS(IDirectSound3DBufferImpl,iface);
451 TRACE("(%p) ref was %ld\n", This, This->ref);
457 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
459 DeleteCriticalSection(&This->lock);
461 HeapFree(GetProcessHeap(),0,This->buffer);
462 HeapFree(GetProcessHeap(),0,This);
468 /* IDirectSound3DBuffer methods */
470 static HRESULT WINAPI IDirectSound3DBufferImpl_GetAllParameters(
471 LPDIRECTSOUND3DBUFFER iface,
472 LPDS3DBUFFER lpDs3dBuffer)
480 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeAngles(
481 LPDIRECTSOUND3DBUFFER iface,
482 LPDWORD lpdwInsideConeAngle,
483 LPDWORD lpdwOutsideConeAngle)
491 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeOrientation(
492 LPDIRECTSOUND3DBUFFER iface,
493 LPD3DVECTOR lpvConeOrientation)
501 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeOutsideVolume(
502 LPDIRECTSOUND3DBUFFER iface,
503 LPLONG lplConeOutsideVolume)
511 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMaxDistance(
512 LPDIRECTSOUND3DBUFFER iface,
513 LPD3DVALUE lpfMaxDistance)
521 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMinDistance(
522 LPDIRECTSOUND3DBUFFER iface,
523 LPD3DVALUE lpfMinDistance)
531 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMode(
532 LPDIRECTSOUND3DBUFFER iface,
541 static HRESULT WINAPI IDirectSound3DBufferImpl_GetPosition(
542 LPDIRECTSOUND3DBUFFER iface,
543 LPD3DVECTOR lpvPosition)
551 static HRESULT WINAPI IDirectSound3DBufferImpl_GetVelocity(
552 LPDIRECTSOUND3DBUFFER iface,
553 LPD3DVECTOR lpvVelocity)
561 static HRESULT WINAPI IDirectSound3DBufferImpl_SetAllParameters(
562 LPDIRECTSOUND3DBUFFER iface,
563 LPCDS3DBUFFER lpcDs3dBuffer,
572 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeAngles(
573 LPDIRECTSOUND3DBUFFER iface,
574 DWORD dwInsideConeAngle,
575 DWORD dwOutsideConeAngle,
584 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeOrientation(
585 LPDIRECTSOUND3DBUFFER iface,
586 D3DVALUE x, D3DVALUE y, D3DVALUE z,
595 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeOutsideVolume(
596 LPDIRECTSOUND3DBUFFER iface,
597 LONG lConeOutsideVolume,
606 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMaxDistance(
607 LPDIRECTSOUND3DBUFFER iface,
608 D3DVALUE fMaxDistance,
617 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMinDistance(
618 LPDIRECTSOUND3DBUFFER iface,
619 D3DVALUE fMinDistance,
628 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMode(
629 LPDIRECTSOUND3DBUFFER iface,
633 ICOM_THIS(IDirectSound3DBufferImpl,iface);
634 TRACE("mode = %lx\n", dwMode);
635 This->ds3db.dwMode = dwMode;
641 static HRESULT WINAPI IDirectSound3DBufferImpl_SetPosition(
642 LPDIRECTSOUND3DBUFFER iface,
643 D3DVALUE x, D3DVALUE y, D3DVALUE z,
652 static HRESULT WINAPI IDirectSound3DBufferImpl_SetVelocity(
653 LPDIRECTSOUND3DBUFFER iface,
654 D3DVALUE x, D3DVALUE y, D3DVALUE z,
663 static ICOM_VTABLE(IDirectSound3DBuffer) ds3dbvt =
665 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
666 /* IUnknown methods */
667 IDirectSound3DBufferImpl_QueryInterface,
668 IDirectSound3DBufferImpl_AddRef,
669 IDirectSound3DBufferImpl_Release,
670 /* IDirectSound3DBuffer methods */
671 IDirectSound3DBufferImpl_GetAllParameters,
672 IDirectSound3DBufferImpl_GetConeAngles,
673 IDirectSound3DBufferImpl_GetConeOrientation,
674 IDirectSound3DBufferImpl_GetConeOutsideVolume,
675 IDirectSound3DBufferImpl_GetMaxDistance,
676 IDirectSound3DBufferImpl_GetMinDistance,
677 IDirectSound3DBufferImpl_GetMode,
678 IDirectSound3DBufferImpl_GetPosition,
679 IDirectSound3DBufferImpl_GetVelocity,
680 IDirectSound3DBufferImpl_SetAllParameters,
681 IDirectSound3DBufferImpl_SetConeAngles,
682 IDirectSound3DBufferImpl_SetConeOrientation,
683 IDirectSound3DBufferImpl_SetConeOutsideVolume,
684 IDirectSound3DBufferImpl_SetMaxDistance,
685 IDirectSound3DBufferImpl_SetMinDistance,
686 IDirectSound3DBufferImpl_SetMode,
687 IDirectSound3DBufferImpl_SetPosition,
688 IDirectSound3DBufferImpl_SetVelocity,
693 static int DSOUND_Create3DBuffer(IDirectSoundBufferImpl* dsb)
695 DWORD i, temp, iSize, oSize, offset;
696 LPBYTE bIbuf, bObuf, bTbuf = NULL;
697 LPWORD wIbuf, wObuf, wTbuf = NULL;
699 /* Inside DirectX says it's stupid but allowed */
700 if (dsb->wfx.nChannels == 2) {
701 /* Convert to mono */
702 if (dsb->wfx.wBitsPerSample == 16) {
703 iSize = dsb->buflen / 4;
704 wTbuf = malloc(dsb->buflen / 2);
706 return DSERR_OUTOFMEMORY;
707 for (i = 0; i < iSize; i++)
708 wTbuf[i] = (dsb->buffer[i * 2] + dsb->buffer[(i * 2) + 1]) / 2;
711 iSize = dsb->buflen / 2;
712 bTbuf = malloc(dsb->buflen / 2);
714 return DSERR_OUTOFMEMORY;
715 for (i = 0; i < iSize; i++)
716 bTbuf[i] = (dsb->buffer[i * 2] + dsb->buffer[(i * 2) + 1]) / 2;
720 if (dsb->wfx.wBitsPerSample == 16) {
721 iSize = dsb->buflen / 2;
722 wIbuf = (LPWORD) dsb->buffer;
724 bIbuf = (LPBYTE) dsb->buffer;
729 if (primarybuf->wfx.wBitsPerSample == 16) {
730 wObuf = (LPWORD) dsb->ds3db->buffer;
731 oSize = dsb->ds3db->buflen / 2;
733 bObuf = (LPBYTE) dsb->ds3db->buffer;
734 oSize = dsb->ds3db->buflen;
737 offset = primarybuf->wfx.nSamplesPerSec / 100; /* 10ms */
738 if (primarybuf->wfx.wBitsPerSample == 16 && dsb->wfx.wBitsPerSample == 16)
739 for (i = 0; i < iSize; i++) {
742 temp += wIbuf[i - offset] >> 9;
744 temp += wIbuf[i + iSize - offset] >> 9;
746 wObuf[(i * 2) + 1] = temp;
748 else if (primarybuf->wfx.wBitsPerSample == 8 && dsb->wfx.wBitsPerSample == 8)
749 for (i = 0; i < iSize; i++) {
752 temp += bIbuf[i - offset] >> 5;
754 temp += bIbuf[i + iSize - offset] >> 5;
756 bObuf[(i * 2) + 1] = temp;
767 /*******************************************************************************
768 * IDirectSound3DListener
771 /* IUnknown methods */
772 static HRESULT WINAPI IDirectSound3DListenerImpl_QueryInterface(
773 LPDIRECTSOUND3DLISTENER iface, REFIID riid, LPVOID *ppobj)
775 ICOM_THIS(IDirectSound3DListenerImpl,iface);
777 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
781 static ULONG WINAPI IDirectSound3DListenerImpl_AddRef(LPDIRECTSOUND3DLISTENER iface)
783 ICOM_THIS(IDirectSound3DListenerImpl,iface);
788 static ULONG WINAPI IDirectSound3DListenerImpl_Release(LPDIRECTSOUND3DLISTENER iface)
791 ICOM_THIS(IDirectSound3DListenerImpl,iface);
793 TRACE("(%p) ref was %ld\n", This, This->ref);
795 ulReturn = --This->ref;
797 /* Free all resources */
798 if( ulReturn == 0 ) {
800 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
801 DeleteCriticalSection(&This->lock);
802 HeapFree(GetProcessHeap(),0,This);
808 /* IDirectSound3DListener methods */
809 static HRESULT WINAPI IDirectSound3DListenerImpl_GetAllParameter(
810 LPDIRECTSOUND3DLISTENER iface,
811 LPDS3DLISTENER lpDS3DL)
817 static HRESULT WINAPI IDirectSound3DListenerImpl_GetDistanceFactor(
818 LPDIRECTSOUND3DLISTENER iface,
819 LPD3DVALUE lpfDistanceFactor)
825 static HRESULT WINAPI IDirectSound3DListenerImpl_GetDopplerFactor(
826 LPDIRECTSOUND3DLISTENER iface,
827 LPD3DVALUE lpfDopplerFactor)
833 static HRESULT WINAPI IDirectSound3DListenerImpl_GetOrientation(
834 LPDIRECTSOUND3DLISTENER iface,
835 LPD3DVECTOR lpvOrientFront,
836 LPD3DVECTOR lpvOrientTop)
842 static HRESULT WINAPI IDirectSound3DListenerImpl_GetPosition(
843 LPDIRECTSOUND3DLISTENER iface,
844 LPD3DVECTOR lpvPosition)
850 static HRESULT WINAPI IDirectSound3DListenerImpl_GetRolloffFactor(
851 LPDIRECTSOUND3DLISTENER iface,
852 LPD3DVALUE lpfRolloffFactor)
858 static HRESULT WINAPI IDirectSound3DListenerImpl_GetVelocity(
859 LPDIRECTSOUND3DLISTENER iface,
860 LPD3DVECTOR lpvVelocity)
866 static HRESULT WINAPI IDirectSound3DListenerImpl_SetAllParameters(
867 LPDIRECTSOUND3DLISTENER iface,
868 LPCDS3DLISTENER lpcDS3DL,
875 static HRESULT WINAPI IDirectSound3DListenerImpl_SetDistanceFactor(
876 LPDIRECTSOUND3DLISTENER iface,
877 D3DVALUE fDistanceFactor,
884 static HRESULT WINAPI IDirectSound3DListenerImpl_SetDopplerFactor(
885 LPDIRECTSOUND3DLISTENER iface,
886 D3DVALUE fDopplerFactor,
893 static HRESULT WINAPI IDirectSound3DListenerImpl_SetOrientation(
894 LPDIRECTSOUND3DLISTENER iface,
895 D3DVALUE xFront, D3DVALUE yFront, D3DVALUE zFront,
896 D3DVALUE xTop, D3DVALUE yTop, D3DVALUE zTop,
903 static HRESULT WINAPI IDirectSound3DListenerImpl_SetPosition(
904 LPDIRECTSOUND3DLISTENER iface,
905 D3DVALUE x, D3DVALUE y, D3DVALUE z,
912 static HRESULT WINAPI IDirectSound3DListenerImpl_SetRolloffFactor(
913 LPDIRECTSOUND3DLISTENER iface,
914 D3DVALUE fRolloffFactor,
921 static HRESULT WINAPI IDirectSound3DListenerImpl_SetVelocity(
922 LPDIRECTSOUND3DLISTENER iface,
923 D3DVALUE x, D3DVALUE y, D3DVALUE z,
930 static HRESULT WINAPI IDirectSound3DListenerImpl_CommitDeferredSettings(
931 LPDIRECTSOUND3DLISTENER iface)
938 static ICOM_VTABLE(IDirectSound3DListener) ds3dlvt =
940 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
941 /* IUnknown methods */
942 IDirectSound3DListenerImpl_QueryInterface,
943 IDirectSound3DListenerImpl_AddRef,
944 IDirectSound3DListenerImpl_Release,
945 /* IDirectSound3DListener methods */
946 IDirectSound3DListenerImpl_GetAllParameter,
947 IDirectSound3DListenerImpl_GetDistanceFactor,
948 IDirectSound3DListenerImpl_GetDopplerFactor,
949 IDirectSound3DListenerImpl_GetOrientation,
950 IDirectSound3DListenerImpl_GetPosition,
951 IDirectSound3DListenerImpl_GetRolloffFactor,
952 IDirectSound3DListenerImpl_GetVelocity,
953 IDirectSound3DListenerImpl_SetAllParameters,
954 IDirectSound3DListenerImpl_SetDistanceFactor,
955 IDirectSound3DListenerImpl_SetDopplerFactor,
956 IDirectSound3DListenerImpl_SetOrientation,
957 IDirectSound3DListenerImpl_SetPosition,
958 IDirectSound3DListenerImpl_SetRolloffFactor,
959 IDirectSound3DListenerImpl_SetVelocity,
960 IDirectSound3DListenerImpl_CommitDeferredSettings,
963 /*******************************************************************************
966 static HRESULT WINAPI IDirectSoundNotifyImpl_QueryInterface(
967 LPDIRECTSOUNDNOTIFY iface,REFIID riid,LPVOID *ppobj
969 ICOM_THIS(IDirectSoundNotifyImpl,iface);
971 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
975 static ULONG WINAPI IDirectSoundNotifyImpl_AddRef(LPDIRECTSOUNDNOTIFY iface) {
976 ICOM_THIS(IDirectSoundNotifyImpl,iface);
977 return ++(This->ref);
980 static ULONG WINAPI IDirectSoundNotifyImpl_Release(LPDIRECTSOUNDNOTIFY iface) {
981 ICOM_THIS(IDirectSoundNotifyImpl,iface);
983 TRACE("(%p) ref was %ld\n", This, This->ref);
987 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
988 HeapFree(GetProcessHeap(),0,This);
994 static HRESULT WINAPI IDirectSoundNotifyImpl_SetNotificationPositions(
995 LPDIRECTSOUNDNOTIFY iface,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify
997 ICOM_THIS(IDirectSoundNotifyImpl,iface);
1000 if (TRACE_ON(dsound)) {
1001 TRACE("(%p,0x%08lx,%p)\n",This,howmuch,notify);
1002 for (i=0;i<howmuch;i++)
1003 TRACE("notify at %ld to 0x%08lx\n",
1004 notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
1006 This->dsb->notifies = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,This->dsb->notifies,(This->dsb->nrofnotifies+howmuch)*sizeof(DSBPOSITIONNOTIFY));
1007 memcpy( This->dsb->notifies+This->dsb->nrofnotifies,
1009 howmuch*sizeof(DSBPOSITIONNOTIFY)
1011 This->dsb->nrofnotifies+=howmuch;
1016 static ICOM_VTABLE(IDirectSoundNotify) dsnvt =
1018 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1019 IDirectSoundNotifyImpl_QueryInterface,
1020 IDirectSoundNotifyImpl_AddRef,
1021 IDirectSoundNotifyImpl_Release,
1022 IDirectSoundNotifyImpl_SetNotificationPositions,
1025 /*******************************************************************************
1026 * IDirectSoundBuffer
1029 static void DSOUND_RecalcVolPan(PDSVOLUMEPAN volpan)
1033 /* the AmpFactors are expressed in 16.16 fixed point */
1034 volpan->dwVolAmpFactor = (ULONG) (pow(2.0, volpan->lVolume / 600.0) * 65536);
1035 /* FIXME: dwPan{Left|Right}AmpFactor */
1037 /* FIXME: use calculated vol and pan ampfactors */
1038 temp = (double) (volpan->lVolume - (volpan->lPan > 0 ? volpan->lPan : 0));
1039 volpan->dwTotalLeftAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 65536);
1040 temp = (double) (volpan->lVolume + (volpan->lPan < 0 ? volpan->lPan : 0));
1041 volpan->dwTotalRightAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 65536);
1043 TRACE("left = %lx, right = %lx\n", volpan->dwTotalLeftAmpFactor, volpan->dwTotalRightAmpFactor);
1046 static void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
1050 sw = dsb->wfx.nChannels * (dsb->wfx.wBitsPerSample / 8);
1051 if ((dsb->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && dsb->hwbuf) {
1053 /* let fragment size approximate the timer delay */
1054 fraglen = (dsb->freq * DS_TIME_DEL / 1000) * sw;
1055 /* reduce fragment size until an integer number of them fits in the buffer */
1056 /* (FIXME: this may or may not be a good idea) */
1057 while (dsb->buflen % fraglen) fraglen -= sw;
1058 dsb->dsound->fraglen = fraglen;
1059 TRACE("fraglen=%ld\n", dsb->dsound->fraglen);
1061 /* calculate the 10ms write lead */
1062 dsb->writelead = (dsb->freq / 100) * sw;
1065 static HRESULT DSOUND_PrimaryOpen(IDirectSoundBufferImpl *dsb)
1067 HRESULT err = DS_OK;
1069 /* are we using waveOut stuff? */
1073 HRESULT merr = DS_OK;
1074 /* Start in pause mode, to allow buffers to get filled */
1075 waveOutPause(dsb->dsound->hwo);
1076 if (dsb->state == STATE_PLAYING) dsb->state = STATE_STARTING;
1077 else if (dsb->state == STATE_STOPPING) dsb->state = STATE_STOPPED;
1078 /* use fragments of 10ms (1/100s) each (which should get us within
1079 * the documented write cursor lead of 10-15ms) */
1080 buflen = ((dsb->wfx.nAvgBytesPerSec / 100) & ~3) * DS_HEL_FRAGS;
1081 TRACE("desired buflen=%ld, old buffer=%p\n", buflen, dsb->buffer);
1082 /* reallocate emulated primary buffer */
1083 newbuf = (LPBYTE)HeapReAlloc(GetProcessHeap(),0,dsb->buffer,buflen);
1084 if (newbuf == NULL) {
1085 ERR("failed to allocate primary buffer\n");
1086 merr = DSERR_OUTOFMEMORY;
1087 /* but the old buffer might still exists and must be re-prepared */
1089 dsb->buffer = newbuf;
1090 dsb->buflen = buflen;
1094 IDirectSoundImpl *ds = dsb->dsound;
1096 ds->fraglen = dsb->buflen / DS_HEL_FRAGS;
1098 /* prepare fragment headers */
1099 for (c=0; c<DS_HEL_FRAGS; c++) {
1100 ds->pwave[c]->lpData = dsb->buffer + c*ds->fraglen;
1101 ds->pwave[c]->dwBufferLength = ds->fraglen;
1102 ds->pwave[c]->dwUser = (DWORD)dsb;
1103 ds->pwave[c]->dwFlags = 0;
1104 ds->pwave[c]->dwLoops = 0;
1105 err = mmErr(waveOutPrepareHeader(ds->hwo,ds->pwave[c],sizeof(WAVEHDR)));
1108 waveOutUnprepareHeader(ds->hwo,ds->pwave[c],sizeof(WAVEHDR));
1116 memset(dsb->buffer, (dsb->wfx.wBitsPerSample == 16) ? 0 : 128, dsb->buflen);
1117 TRACE("fraglen=%ld\n", ds->fraglen);
1119 if ((err == DS_OK) && (merr != DS_OK))
1126 static void DSOUND_PrimaryClose(IDirectSoundBufferImpl *dsb)
1128 /* are we using waveOut stuff? */
1131 IDirectSoundImpl *ds = dsb->dsound;
1133 waveOutReset(ds->hwo);
1134 for (c=0; c<DS_HEL_FRAGS; c++)
1135 waveOutUnprepareHeader(ds->hwo, ds->pwave[c], sizeof(WAVEHDR));
1139 static HRESULT DSOUND_PrimaryPlay(IDirectSoundBufferImpl *dsb)
1141 HRESULT err = DS_OK;
1143 err = IDsDriverBuffer_Play(dsb->hwbuf, 0, 0, DSBPLAY_LOOPING);
1147 static HRESULT DSOUND_PrimaryStop(IDirectSoundBufferImpl *dsb)
1149 HRESULT err = DS_OK;
1151 err = IDsDriverBuffer_Stop(dsb->hwbuf);
1152 if (err == DSERR_BUFFERLOST) {
1153 /* Wine-only: the driver wants us to reopen the device */
1154 /* FIXME: check for errors */
1155 IDsDriverBuffer_Release(primarybuf->hwbuf);
1156 waveOutClose(dsb->dsound->hwo);
1157 dsb->dsound->hwo = 0;
1158 waveOutOpen(&(dsb->dsound->hwo), dsb->dsound->drvdesc.dnDevNode,
1159 &(primarybuf->wfx), 0, 0, CALLBACK_NULL | WAVE_DIRECTSOUND);
1160 err = IDsDriver_CreateSoundBuffer(dsb->dsound->driver,&(dsb->wfx),dsb->dsbd.dwFlags,0,
1161 &(dsb->buflen),&(dsb->buffer),
1162 (LPVOID)&(dsb->hwbuf));
1168 /* This sets this format for the <em>Primary Buffer Only</em> */
1169 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
1170 static HRESULT WINAPI IDirectSoundBufferImpl_SetFormat(
1171 LPDIRECTSOUNDBUFFER iface,LPWAVEFORMATEX wfex
1173 ICOM_THIS(IDirectSoundBufferImpl,iface);
1174 IDirectSoundBufferImpl** dsb;
1175 HRESULT err = DS_OK;
1178 /* Let's be pedantic! */
1179 if ((wfex == NULL) ||
1180 (wfex->wFormatTag != WAVE_FORMAT_PCM) ||
1181 (wfex->nChannels < 1) || (wfex->nChannels > 2) ||
1182 (wfex->nSamplesPerSec < 1) ||
1183 (wfex->nBlockAlign < 1) || (wfex->nChannels > 4) ||
1184 ((wfex->wBitsPerSample != 8) && (wfex->wBitsPerSample != 16))) {
1185 TRACE("failed pedantic check!\n");
1186 return DSERR_INVALIDPARAM;
1190 EnterCriticalSection(&(This->dsound->lock));
1192 if (primarybuf->wfx.nSamplesPerSec != wfex->nSamplesPerSec) {
1193 dsb = dsound->buffers;
1194 for (i = 0; i < dsound->nrofbuffers; i++, dsb++) {
1196 EnterCriticalSection(&((*dsb)->lock));
1198 (*dsb)->freqAdjust = ((*dsb)->freq << DSOUND_FREQSHIFT) /
1199 wfex->nSamplesPerSec;
1201 LeaveCriticalSection(&((*dsb)->lock));
1206 memcpy(&(primarybuf->wfx), wfex, sizeof(primarybuf->wfx));
1208 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
1209 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1210 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
1211 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
1212 wfex->wBitsPerSample, wfex->cbSize);
1214 primarybuf->wfx.nAvgBytesPerSec =
1215 This->wfx.nSamplesPerSec * This->wfx.nBlockAlign;
1216 if (primarybuf->dsound->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) {
1217 /* FIXME: check for errors */
1218 DSOUND_PrimaryClose(primarybuf);
1219 waveOutClose(This->dsound->hwo);
1220 This->dsound->hwo = 0;
1221 waveOutOpen(&(This->dsound->hwo), This->dsound->drvdesc.dnDevNode,
1222 &(primarybuf->wfx), 0, 0, CALLBACK_NULL | WAVE_DIRECTSOUND);
1223 DSOUND_PrimaryOpen(primarybuf);
1225 if (primarybuf->hwbuf) {
1226 err = IDsDriverBuffer_SetFormat(primarybuf->hwbuf, &(primarybuf->wfx));
1227 if (err == DSERR_BUFFERLOST) {
1228 /* Wine-only: the driver wants us to recreate the HW buffer */
1229 IDsDriverBuffer_Release(primarybuf->hwbuf);
1230 err = IDsDriver_CreateSoundBuffer(primarybuf->dsound->driver,&(primarybuf->wfx),primarybuf->dsbd.dwFlags,0,
1231 &(primarybuf->buflen),&(primarybuf->buffer),
1232 (LPVOID)&(primarybuf->hwbuf));
1233 if (primarybuf->state == STATE_PLAYING) primarybuf->state = STATE_STARTING;
1234 else if (primarybuf->state == STATE_STOPPING) primarybuf->state = STATE_STOPPED;
1237 DSOUND_RecalcFormat(primarybuf);
1239 LeaveCriticalSection(&(This->dsound->lock));
1245 static HRESULT WINAPI IDirectSoundBufferImpl_SetVolume(
1246 LPDIRECTSOUNDBUFFER iface,LONG vol
1248 ICOM_THIS(IDirectSoundBufferImpl,iface);
1250 TRACE("(%p,%ld)\n",This,vol);
1252 /* I'm not sure if we need this for primary buffer */
1253 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
1254 return DSERR_CONTROLUNAVAIL;
1256 if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN))
1257 return DSERR_INVALIDPARAM;
1260 EnterCriticalSection(&(This->lock));
1262 This->volpan.lVolume = vol;
1264 DSOUND_RecalcVolPan(&(This->volpan));
1267 IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
1269 else if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
1270 #if 0 /* should we really do this? */
1271 /* the DS volume ranges from 0 (max, 0dB attenuation) to -10000 (min, 100dB attenuation) */
1272 /* the MM volume ranges from 0 to 0xffff in an unspecified logarithmic scale */
1273 WORD cvol = 0xffff + vol*6 + vol/2;
1274 DWORD vol = cvol | ((DWORD)cvol << 16)
1275 waveOutSetVolume(This->dsound->hwo, vol);
1279 LeaveCriticalSection(&(This->lock));
1285 static HRESULT WINAPI IDirectSoundBufferImpl_GetVolume(
1286 LPDIRECTSOUNDBUFFER iface,LPLONG vol
1288 ICOM_THIS(IDirectSoundBufferImpl,iface);
1289 TRACE("(%p,%p)\n",This,vol);
1292 return DSERR_INVALIDPARAM;
1294 *vol = This->volpan.lVolume;
1298 static HRESULT WINAPI IDirectSoundBufferImpl_SetFrequency(
1299 LPDIRECTSOUNDBUFFER iface,DWORD freq
1301 ICOM_THIS(IDirectSoundBufferImpl,iface);
1302 TRACE("(%p,%ld)\n",This,freq);
1304 /* You cannot set the frequency of the primary buffer */
1305 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY) ||
1306 (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
1307 return DSERR_CONTROLUNAVAIL;
1309 if (!freq) freq = This->wfx.nSamplesPerSec;
1311 if ((freq < DSBFREQUENCY_MIN) || (freq > DSBFREQUENCY_MAX))
1312 return DSERR_INVALIDPARAM;
1315 EnterCriticalSection(&(This->lock));
1318 This->freqAdjust = (freq << DSOUND_FREQSHIFT) / primarybuf->wfx.nSamplesPerSec;
1319 This->nAvgBytesPerSec = freq * This->wfx.nBlockAlign;
1320 DSOUND_RecalcFormat(This);
1322 LeaveCriticalSection(&(This->lock));
1328 static HRESULT WINAPI IDirectSoundBufferImpl_Play(
1329 LPDIRECTSOUNDBUFFER iface,DWORD reserved1,DWORD reserved2,DWORD flags
1331 ICOM_THIS(IDirectSoundBufferImpl,iface);
1332 TRACE("(%p,%08lx,%08lx,%08lx)\n",
1333 This,reserved1,reserved2,flags
1337 EnterCriticalSection(&(This->lock));
1339 This->playflags = flags;
1340 if (This->state == STATE_STOPPED) {
1341 This->leadin = TRUE;
1342 This->startpos = This->buf_mixpos;
1343 This->state = STATE_STARTING;
1344 } else if (This->state == STATE_STOPPING)
1345 This->state = STATE_PLAYING;
1346 if (!(This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && This->hwbuf) {
1347 IDsDriverBuffer_Play(This->hwbuf, 0, 0, This->playflags);
1348 This->state = STATE_PLAYING;
1351 LeaveCriticalSection(&(This->lock));
1357 static HRESULT WINAPI IDirectSoundBufferImpl_Stop(LPDIRECTSOUNDBUFFER iface)
1359 ICOM_THIS(IDirectSoundBufferImpl,iface);
1360 TRACE("(%p)\n",This);
1363 EnterCriticalSection(&(This->lock));
1365 if (This->state == STATE_PLAYING)
1366 This->state = STATE_STOPPING;
1367 else if (This->state == STATE_STARTING)
1368 This->state = STATE_STOPPED;
1369 if (!(This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && This->hwbuf) {
1370 IDsDriverBuffer_Stop(This->hwbuf);
1371 This->state = STATE_STOPPED;
1373 DSOUND_CheckEvent(This, 0);
1375 LeaveCriticalSection(&(This->lock));
1381 static DWORD WINAPI IDirectSoundBufferImpl_AddRef(LPDIRECTSOUNDBUFFER iface) {
1382 ICOM_THIS(IDirectSoundBufferImpl,iface);
1385 TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId());
1387 ref = InterlockedIncrement(&(This->ref));
1389 FIXME("thread-safety alert! AddRef-ing with a zero refcount!\n");
1393 static DWORD WINAPI IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER iface) {
1394 ICOM_THIS(IDirectSoundBufferImpl,iface);
1398 TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId());
1400 ref = InterlockedDecrement(&(This->ref));
1401 if (ref) return ref;
1403 EnterCriticalSection(&(This->dsound->lock));
1404 for (i=0;i<This->dsound->nrofbuffers;i++)
1405 if (This->dsound->buffers[i] == This)
1408 if (i < This->dsound->nrofbuffers) {
1409 /* Put the last buffer of the list in the (now empty) position */
1410 This->dsound->buffers[i] = This->dsound->buffers[This->dsound->nrofbuffers - 1];
1411 This->dsound->nrofbuffers--;
1412 This->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,This->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER)*This->dsound->nrofbuffers);
1413 TRACE("buffer count is now %d\n", This->dsound->nrofbuffers);
1414 IDirectSound_Release((LPDIRECTSOUND)This->dsound);
1416 LeaveCriticalSection(&(This->dsound->lock));
1418 DeleteCriticalSection(&(This->lock));
1419 if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1420 DSOUND_PrimaryClose(This);
1422 IDsDriverBuffer_Release(This->hwbuf);
1425 IDirectSound3DBuffer_Release((LPDIRECTSOUND3DBUFFER)This->ds3db);
1427 /* this is a duplicate buffer */
1428 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->parent);
1430 /* this is a toplevel buffer */
1431 HeapFree(GetProcessHeap(),0,This->buffer);
1433 HeapFree(GetProcessHeap(),0,This);
1435 if (This == primarybuf)
1441 static DWORD DSOUND_CalcPlayPosition(IDirectSoundBufferImpl *This,
1442 DWORD state, DWORD pplay, DWORD pwrite, DWORD pmix, DWORD bmix)
1446 TRACE("primary playpos=%ld, mixpos=%ld\n", pplay, pmix);
1447 TRACE("this mixpos=%ld\n", bmix);
1449 /* the actual primary play position (pplay) is always behind last mixed (pmix),
1450 * unless the computer is too slow or something */
1451 /* we need to know how far away we are from there */
1452 if (pmix == pplay) {
1453 if ((state == STATE_PLAYING) || (state == STATE_STOPPING)) {
1454 /* wow, the software mixer is really doing well,
1455 * seems the entire primary buffer is filled! */
1456 pmix += primarybuf->buflen;
1458 /* else: the primary buffer is not playing, so probably empty */
1460 if (pmix < pplay) pmix += primarybuf->buflen; /* wraparound */
1462 /* detect buffer underrun */
1463 if (pwrite < pplay) pwrite += primarybuf->buflen; /* wraparound */
1465 if (pmix > (DS_SND_QUEUE * primarybuf->dsound->fraglen + pwrite + primarybuf->writelead)) {
1466 WARN("detected an underrun: primary queue was %ld\n",pmix);
1469 /* divide the offset by its sample size */
1470 pmix /= primarybuf->wfx.nBlockAlign;
1471 TRACE("primary back-samples=%ld\n",pmix);
1472 /* adjust for our frequency */
1473 pmix = (pmix * This->freqAdjust) >> DSOUND_FREQSHIFT;
1474 /* multiply by our own sample size */
1475 pmix *= This->wfx.nBlockAlign;
1476 TRACE("this back-offset=%ld\n", pmix);
1477 /* subtract from our last mixed position */
1479 while (bplay < pmix) bplay += This->buflen; /* wraparound */
1481 if (This->leadin && ((bplay < This->startpos) || (bplay > bmix))) {
1482 /* seems we haven't started playing yet */
1483 TRACE("this still in lead-in phase\n");
1484 bplay = This->startpos;
1486 /* return the result */
1490 static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
1491 LPDIRECTSOUNDBUFFER iface,LPDWORD playpos,LPDWORD writepos
1494 ICOM_THIS(IDirectSoundBufferImpl,iface);
1495 TRACE("(%p,%p,%p)\n",This,playpos,writepos);
1497 hres=IDsDriverBuffer_GetPosition(This->hwbuf,playpos,writepos);
1502 else if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
1505 mtime.wType = TIME_BYTES;
1506 waveOutGetPosition(This->dsound->hwo, &mtime, sizeof(mtime));
1507 mtime.u.cb = mtime.u.cb % This->buflen;
1508 *playpos = mtime.u.cb;
1511 /* the writepos should only be used by apps with WRITEPRIMARY priority,
1512 * in which case our software mixer is disabled anyway */
1513 *writepos = This->playpos + DS_HEL_MARGIN * This->dsound->fraglen;
1514 while (*writepos >= This->buflen)
1515 *writepos -= This->buflen;
1518 if (playpos && (This->state != STATE_PLAYING)) {
1519 /* we haven't been merged into the primary buffer (yet) */
1520 *playpos = This->buf_mixpos;
1523 DWORD pplay, pwrite, lplay, splay, pstate;
1524 /* let's get this exact; first, recursively call GetPosition on the primary */
1525 EnterCriticalSection(&(primarybuf->lock));
1526 if ((This->dsbd.dwFlags & DSBCAPS_GETCURRENTPOSITION2) || primarybuf->hwbuf || !DS_EMULDRIVER) {
1527 IDirectSoundBufferImpl_GetCurrentPosition((LPDIRECTSOUNDBUFFER)primarybuf, &pplay, &pwrite);
1528 /* detect HEL mode underrun */
1529 pstate = primarybuf->state;
1530 if (!(primarybuf->hwbuf || primarybuf->dsound->pwqueue)) {
1531 TRACE("detected an underrun\n");
1533 if (pstate == STATE_PLAYING)
1534 pstate = STATE_STARTING;
1535 else if (pstate == STATE_STOPPING)
1536 pstate = STATE_STOPPED;
1538 /* get data for ourselves while we still have the lock */
1539 pstate &= This->state;
1540 lplay = This->primary_mixpos;
1541 splay = This->buf_mixpos;
1542 /* calculate play position using this */
1543 *playpos = DSOUND_CalcPlayPosition(This, pstate, pplay, pwrite, lplay, splay);
1545 /* (unless the app isn't using GETCURRENTPOSITION2) */
1546 /* don't know exactly how this should be handled...
1547 * the docs says that play cursor is reported as directly
1548 * behind write cursor, hmm... */
1549 *playpos = This->playpos;
1551 LeaveCriticalSection(&(primarybuf->lock));
1553 if (writepos) *writepos = This->buf_mixpos;
1556 if (This->state != STATE_STOPPED)
1557 /* apply the documented 10ms lead to writepos */
1558 *writepos += This->writelead;
1559 while (*writepos >= This->buflen) *writepos -= This->buflen;
1561 TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
1565 static HRESULT WINAPI IDirectSoundBufferImpl_GetStatus(
1566 LPDIRECTSOUNDBUFFER iface,LPDWORD status
1568 ICOM_THIS(IDirectSoundBufferImpl,iface);
1569 TRACE("(%p,%p), thread is %lx\n",This,status,GetCurrentThreadId());
1572 return DSERR_INVALIDPARAM;
1575 if ((This->state == STATE_STARTING) || (This->state == STATE_PLAYING))
1576 *status |= DSBSTATUS_PLAYING;
1577 if (This->playflags & DSBPLAY_LOOPING)
1578 *status |= DSBSTATUS_LOOPING;
1580 TRACE("status=%lx\n", *status);
1585 static HRESULT WINAPI IDirectSoundBufferImpl_GetFormat(
1586 LPDIRECTSOUNDBUFFER iface,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
1588 ICOM_THIS(IDirectSoundBufferImpl,iface);
1589 TRACE("(%p,%p,%ld,%p)\n",This,lpwf,wfsize,wfwritten);
1591 if (wfsize>sizeof(This->wfx))
1592 wfsize = sizeof(This->wfx);
1593 if (lpwf) { /* NULL is valid */
1594 memcpy(lpwf,&(This->wfx),wfsize);
1596 *wfwritten = wfsize;
1599 *wfwritten = sizeof(This->wfx);
1601 return DSERR_INVALIDPARAM;
1606 static HRESULT WINAPI IDirectSoundBufferImpl_Lock(
1607 LPDIRECTSOUNDBUFFER iface,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
1609 ICOM_THIS(IDirectSoundBufferImpl,iface);
1612 TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx)\n",
1623 if (flags & DSBLOCK_FROMWRITECURSOR) {
1625 /* GetCurrentPosition does too much magic to duplicate here */
1626 IDirectSoundBufferImpl_GetCurrentPosition(iface, NULL, &writepos);
1627 writecursor += writepos;
1629 if (flags & DSBLOCK_ENTIREBUFFER)
1630 writebytes = This->buflen;
1631 if (writebytes > This->buflen)
1632 writebytes = This->buflen;
1634 assert(audiobytes1!=audiobytes2);
1635 assert(lplpaudioptr1!=lplpaudioptr2);
1637 if ((writebytes == This->buflen) &&
1638 ((This->state == STATE_STARTING) ||
1639 (This->state == STATE_PLAYING)))
1640 /* some games, like Half-Life, try to be clever (not) and
1641 * keep one secondary buffer, and mix sounds into it itself,
1642 * locking the entire buffer every time... so we can just forget
1643 * about tracking the last-written-to-position... */
1644 This->probably_valid_to = (DWORD)-1;
1646 This->probably_valid_to = writecursor;
1648 if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1649 capf = DSDDESC_DONTNEEDPRIMARYLOCK;
1651 capf = DSDDESC_DONTNEEDSECONDARYLOCK;
1652 if (!(This->dsound->drvdesc.dwFlags & capf) && This->hwbuf) {
1653 IDsDriverBuffer_Lock(This->hwbuf,
1654 lplpaudioptr1, audiobytes1,
1655 lplpaudioptr2, audiobytes2,
1656 writecursor, writebytes,
1659 if (writecursor+writebytes <= This->buflen) {
1660 *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor;
1661 *audiobytes1 = writebytes;
1663 *(LPBYTE*)lplpaudioptr2 = NULL;
1666 TRACE("->%ld.0\n",writebytes);
1668 *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor;
1669 *audiobytes1 = This->buflen-writecursor;
1671 *(LPBYTE*)lplpaudioptr2 = This->buffer;
1673 *audiobytes2 = writebytes-(This->buflen-writecursor);
1674 TRACE("->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
1679 static HRESULT WINAPI IDirectSoundBufferImpl_SetCurrentPosition(
1680 LPDIRECTSOUNDBUFFER iface,DWORD newpos
1682 ICOM_THIS(IDirectSoundBufferImpl,iface);
1683 TRACE("(%p,%ld)\n",This,newpos);
1686 EnterCriticalSection(&(This->lock));
1688 This->buf_mixpos = newpos;
1690 IDsDriverBuffer_SetPosition(This->hwbuf, This->buf_mixpos);
1692 LeaveCriticalSection(&(This->lock));
1698 static HRESULT WINAPI IDirectSoundBufferImpl_SetPan(
1699 LPDIRECTSOUNDBUFFER iface,LONG pan
1701 ICOM_THIS(IDirectSoundBufferImpl,iface);
1703 TRACE("(%p,%ld)\n",This,pan);
1705 if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT))
1706 return DSERR_INVALIDPARAM;
1708 /* You cannot set the pan of the primary buffer */
1709 /* and you cannot use both pan and 3D controls */
1710 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
1711 (This->dsbd.dwFlags & DSBCAPS_CTRL3D) ||
1712 (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
1713 return DSERR_CONTROLUNAVAIL;
1716 EnterCriticalSection(&(This->lock));
1718 This->volpan.lPan = pan;
1720 DSOUND_RecalcVolPan(&(This->volpan));
1723 IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
1726 LeaveCriticalSection(&(This->lock));
1732 static HRESULT WINAPI IDirectSoundBufferImpl_GetPan(
1733 LPDIRECTSOUNDBUFFER iface,LPLONG pan
1735 ICOM_THIS(IDirectSoundBufferImpl,iface);
1736 TRACE("(%p,%p)\n",This,pan);
1739 return DSERR_INVALIDPARAM;
1741 *pan = This->volpan.lPan;
1746 static HRESULT WINAPI IDirectSoundBufferImpl_Unlock(
1747 LPDIRECTSOUNDBUFFER iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
1749 ICOM_THIS(IDirectSoundBufferImpl,iface);
1750 DWORD capf, probably_valid_to;
1752 TRACE("(%p,%p,%ld,%p,%ld):stub\n", This,p1,x1,p2,x2);
1755 /* Preprocess 3D buffers... */
1757 /* This is highly experimental and liable to break things */
1758 if (This->dsbd.dwFlags & DSBCAPS_CTRL3D)
1759 DSOUND_Create3DBuffer(This);
1762 if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1763 capf = DSDDESC_DONTNEEDPRIMARYLOCK;
1765 capf = DSDDESC_DONTNEEDSECONDARYLOCK;
1766 if (!(This->dsound->drvdesc.dwFlags & capf) && This->hwbuf) {
1767 IDsDriverBuffer_Unlock(This->hwbuf, p1, x1, p2, x2);
1770 if (p2) probably_valid_to = (((LPBYTE)p2)-This->buffer) + x2;
1771 else probably_valid_to = (((LPBYTE)p1)-This->buffer) + x1;
1772 while (probably_valid_to >= This->buflen)
1773 probably_valid_to -= This->buflen;
1774 if ((probably_valid_to == 0) && ((x1+x2) == This->buflen) &&
1775 ((This->state == STATE_STARTING) ||
1776 (This->state == STATE_PLAYING)))
1777 /* see IDirectSoundBufferImpl_Lock */
1778 probably_valid_to = (DWORD)-1;
1779 This->probably_valid_to = probably_valid_to;
1784 static HRESULT WINAPI IDirectSoundBufferImpl_Restore(
1785 LPDIRECTSOUNDBUFFER iface
1787 ICOM_THIS(IDirectSoundBufferImpl,iface);
1788 FIXME("(%p):stub\n",This);
1792 static HRESULT WINAPI IDirectSoundBufferImpl_GetFrequency(
1793 LPDIRECTSOUNDBUFFER iface,LPDWORD freq
1795 ICOM_THIS(IDirectSoundBufferImpl,iface);
1796 TRACE("(%p,%p)\n",This,freq);
1799 return DSERR_INVALIDPARAM;
1802 TRACE("-> %ld\n", *freq);
1807 static HRESULT WINAPI IDirectSoundBufferImpl_Initialize(
1808 LPDIRECTSOUNDBUFFER iface,LPDIRECTSOUND dsound,LPDSBUFFERDESC dbsd
1810 ICOM_THIS(IDirectSoundBufferImpl,iface);
1811 FIXME("(%p,%p,%p):stub\n",This,dsound,dbsd);
1812 DPRINTF("Re-Init!!!\n");
1813 return DSERR_ALREADYINITIALIZED;
1816 static HRESULT WINAPI IDirectSoundBufferImpl_GetCaps(
1817 LPDIRECTSOUNDBUFFER iface,LPDSBCAPS caps
1819 ICOM_THIS(IDirectSoundBufferImpl,iface);
1820 TRACE("(%p)->(%p)\n",This,caps);
1823 return DSERR_INVALIDPARAM;
1825 /* I think we should check this value, not set it. See */
1826 /* Inside DirectX, p215. That should apply here, too. */
1827 caps->dwSize = sizeof(*caps);
1829 caps->dwFlags = This->dsbd.dwFlags;
1830 if (This->hwbuf) caps->dwFlags |= DSBCAPS_LOCHARDWARE;
1831 else caps->dwFlags |= DSBCAPS_LOCSOFTWARE;
1833 caps->dwBufferBytes = This->dsbd.dwBufferBytes;
1835 /* This value represents the speed of the "unlock" command.
1836 As unlock is quite fast (it does not do anything), I put
1837 4096 ko/s = 4 Mo / s */
1838 /* FIXME: hwbuf speed */
1839 caps->dwUnlockTransferRate = 4096;
1840 caps->dwPlayCpuOverhead = 0;
1845 static HRESULT WINAPI IDirectSoundBufferImpl_QueryInterface(
1846 LPDIRECTSOUNDBUFFER iface,REFIID riid,LPVOID *ppobj
1848 ICOM_THIS(IDirectSoundBufferImpl,iface);
1850 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
1852 if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
1853 IDirectSoundNotifyImpl *dsn;
1855 dsn = (IDirectSoundNotifyImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsn));
1858 IDirectSoundBuffer_AddRef(iface);
1859 ICOM_VTBL(dsn) = &dsnvt;
1860 *ppobj = (LPVOID)dsn;
1865 if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1866 IDirectSound3DBufferImpl *ds3db;
1868 *ppobj = This->ds3db;
1870 IDirectSound3DBuffer_AddRef((LPDIRECTSOUND3DBUFFER)This->ds3db);
1874 ds3db = (IDirectSound3DBufferImpl*)HeapAlloc(GetProcessHeap(),
1878 ICOM_VTBL(ds3db) = &ds3dbvt;
1879 InitializeCriticalSection(&ds3db->lock);
1881 IDirectSoundBuffer_AddRef(iface);
1883 ds3db->ds3db.dwSize = sizeof(DS3DBUFFER);
1884 ds3db->ds3db.vPosition.u1.x = 0.0;
1885 ds3db->ds3db.vPosition.u2.y = 0.0;
1886 ds3db->ds3db.vPosition.u3.z = 0.0;
1887 ds3db->ds3db.vVelocity.u1.x = 0.0;
1888 ds3db->ds3db.vVelocity.u2.y = 0.0;
1889 ds3db->ds3db.vVelocity.u3.z = 0.0;
1890 ds3db->ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
1891 ds3db->ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
1892 ds3db->ds3db.vConeOrientation.u1.x = 0.0;
1893 ds3db->ds3db.vConeOrientation.u2.y = 0.0;
1894 ds3db->ds3db.vConeOrientation.u3.z = 0.0;
1895 ds3db->ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME; ds3db->ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE;
1896 ds3db->ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
1897 ds3db->ds3db.dwMode = DS3DMODE_NORMAL;
1898 ds3db->buflen = (This->buflen * primarybuf->wfx.nBlockAlign) /
1899 This->wfx.nBlockAlign;
1900 ds3db->buffer = HeapAlloc(GetProcessHeap(), 0, ds3db->buflen);
1901 if (ds3db->buffer == NULL) {
1903 ds3db->ds3db.dwMode = DS3DMODE_DISABLE;
1906 ds3db->iks = (IKsPropertySetImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*(ds3db->iks)));
1907 ds3db->iks->ref = 1;
1908 ds3db->iks->ds3db = ds3db;
1909 ICOM_VTBL(ds3db->iks) = &iksvt;
1914 if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
1915 FIXME("%s: I know about this GUID, but don't support it yet\n",
1916 debugstr_guid( riid ));
1922 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
1923 IDirectSound3DListenerImpl* dsl;
1925 if (This->dsound->listener) {
1926 *ppobj = This->dsound->listener;
1927 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)This->dsound->listener);
1931 dsl = (IDirectSound3DListenerImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsl));
1933 ICOM_VTBL(dsl) = &ds3dlvt;
1934 *ppobj = (LPVOID)dsl;
1936 dsl->ds3dl.dwSize = sizeof(DS3DLISTENER);
1937 dsl->ds3dl.vPosition.u1.x = 0.0;
1938 dsl->ds3dl.vPosition.u2.y = 0.0;
1939 dsl->ds3dl.vPosition.u3.z = 0.0;
1940 dsl->ds3dl.vVelocity.u1.x = 0.0;
1941 dsl->ds3dl.vVelocity.u2.y = 0.0;
1942 dsl->ds3dl.vVelocity.u3.z = 0.0;
1943 dsl->ds3dl.vOrientFront.u1.x = 0.0;
1944 dsl->ds3dl.vOrientFront.u2.y = 0.0;
1945 dsl->ds3dl.vOrientFront.u3.z = 1.0;
1946 dsl->ds3dl.vOrientTop.u1.x = 0.0;
1947 dsl->ds3dl.vOrientTop.u2.y = 1.0;
1948 dsl->ds3dl.vOrientTop.u3.z = 0.0;
1949 dsl->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
1950 dsl->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
1952 InitializeCriticalSection(&dsl->lock);
1955 IDirectSoundBuffer_AddRef(iface);
1957 This->dsound->listener = dsl;
1958 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)dsl);
1963 FIXME( "Unknown GUID %s\n", debugstr_guid( riid ) );
1970 static ICOM_VTABLE(IDirectSoundBuffer) dsbvt =
1972 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1973 IDirectSoundBufferImpl_QueryInterface,
1974 IDirectSoundBufferImpl_AddRef,
1975 IDirectSoundBufferImpl_Release,
1976 IDirectSoundBufferImpl_GetCaps,
1977 IDirectSoundBufferImpl_GetCurrentPosition,
1978 IDirectSoundBufferImpl_GetFormat,
1979 IDirectSoundBufferImpl_GetVolume,
1980 IDirectSoundBufferImpl_GetPan,
1981 IDirectSoundBufferImpl_GetFrequency,
1982 IDirectSoundBufferImpl_GetStatus,
1983 IDirectSoundBufferImpl_Initialize,
1984 IDirectSoundBufferImpl_Lock,
1985 IDirectSoundBufferImpl_Play,
1986 IDirectSoundBufferImpl_SetCurrentPosition,
1987 IDirectSoundBufferImpl_SetFormat,
1988 IDirectSoundBufferImpl_SetVolume,
1989 IDirectSoundBufferImpl_SetPan,
1990 IDirectSoundBufferImpl_SetFrequency,
1991 IDirectSoundBufferImpl_Stop,
1992 IDirectSoundBufferImpl_Unlock,
1993 IDirectSoundBufferImpl_Restore
1996 /*******************************************************************************
2000 static HRESULT WINAPI IDirectSoundImpl_SetCooperativeLevel(
2001 LPDIRECTSOUND iface,HWND hwnd,DWORD level
2003 ICOM_THIS(IDirectSoundImpl,iface);
2005 FIXME("(%p,%08lx,%ld):stub\n",This,(DWORD)hwnd,level);
2007 This->priolevel = level;
2012 static HRESULT WINAPI IDirectSoundImpl_CreateSoundBuffer(
2013 LPDIRECTSOUND iface,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER ppdsb,LPUNKNOWN lpunk
2015 ICOM_THIS(IDirectSoundImpl,iface);
2016 IDirectSoundBufferImpl** ippdsb=(IDirectSoundBufferImpl**)ppdsb;
2017 LPWAVEFORMATEX wfex;
2018 HRESULT err = DS_OK;
2020 TRACE("(%p,%p,%p,%p)\n",This,dsbd,ippdsb,lpunk);
2022 if ((This == NULL) || (dsbd == NULL) || (ippdsb == NULL))
2023 return DSERR_INVALIDPARAM;
2025 if (TRACE_ON(dsound)) {
2026 TRACE("(structsize=%ld)\n",dsbd->dwSize);
2027 TRACE("(flags=0x%08lx:\n",dsbd->dwFlags);
2028 _dump_DSBCAPS(dsbd->dwFlags);
2030 TRACE("(bufferbytes=%ld)\n",dsbd->dwBufferBytes);
2031 TRACE("(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
2034 wfex = dsbd->lpwfxFormat;
2037 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
2038 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
2039 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
2040 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
2041 wfex->wBitsPerSample, wfex->cbSize);
2043 if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
2045 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)primarybuf);
2046 *ippdsb = primarybuf;
2047 primarybuf->dsbd.dwFlags = dsbd->dwFlags;
2049 } /* Else create primary buffer */
2051 if (dsbd->dwBufferBytes < DSBSIZE_MIN || dsbd->dwBufferBytes > DSBSIZE_MAX) {
2052 ERR("invalid sound buffer size %ld\n", dsbd->dwBufferBytes);
2053 return DSERR_INVALIDPARAM; /* FIXME: which error? */
2057 *ippdsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBufferImpl));
2058 if (*ippdsb == NULL)
2059 return DSERR_OUTOFMEMORY;
2060 ICOM_VTBL(*ippdsb) = &dsbvt;
2062 (*ippdsb)->dsound = This;
2063 (*ippdsb)->parent = NULL;
2064 (*ippdsb)->buffer = NULL;
2066 memcpy(&((*ippdsb)->dsbd),dsbd,sizeof(*dsbd));
2067 if (dsbd->lpwfxFormat)
2068 memcpy(&((*ippdsb)->wfx), dsbd->lpwfxFormat, sizeof((*ippdsb)->wfx));
2070 TRACE("Created buffer at %p\n", *ippdsb);
2072 if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
2073 (*ippdsb)->buflen = dsound->wfx.nAvgBytesPerSec;
2074 (*ippdsb)->freq = dsound->wfx.nSamplesPerSec;
2076 /* FIXME: verify that hardware capabilities (DSCAPS_PRIMARY flags) match */
2079 err = IDsDriver_CreateSoundBuffer(This->driver,wfex,dsbd->dwFlags,0,
2080 &((*ippdsb)->buflen),&((*ippdsb)->buffer),
2081 (LPVOID*)&((*ippdsb)->hwbuf));
2084 err = DSOUND_PrimaryOpen(*ippdsb);
2089 (*ippdsb)->buflen = dsbd->dwBufferBytes;
2090 (*ippdsb)->freq = dsbd->lpwfxFormat->nSamplesPerSec;
2092 /* Check necessary hardware mixing capabilities */
2093 if (wfex->nChannels==2) capf |= DSCAPS_SECONDARYSTEREO;
2094 else capf |= DSCAPS_SECONDARYMONO;
2095 if (wfex->wBitsPerSample==16) capf |= DSCAPS_SECONDARY16BIT;
2096 else capf |= DSCAPS_SECONDARY8BIT;
2097 use_hw = (This->drvcaps.dwFlags & capf) == capf;
2099 /* FIXME: check hardware sample rate mixing capabilities */
2100 /* FIXME: check app hints for software/hardware buffer (STATIC, LOCHARDWARE, etc) */
2101 /* FIXME: check whether any hardware buffers are left */
2102 /* FIXME: handle DSDHEAP_CREATEHEAP for hardware buffers */
2104 /* Allocate system memory if applicable */
2105 if ((This->drvdesc.dwFlags & DSDDESC_USESYSTEMMEMORY) || !use_hw) {
2106 (*ippdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,(*ippdsb)->buflen);
2107 if ((*ippdsb)->buffer == NULL)
2108 err = DSERR_OUTOFMEMORY;
2111 /* Allocate the hardware buffer */
2112 if (use_hw && (err == DS_OK)) {
2113 err = IDsDriver_CreateSoundBuffer(This->driver,wfex,dsbd->dwFlags,0,
2114 &((*ippdsb)->buflen),&((*ippdsb)->buffer),
2115 (LPVOID*)&((*ippdsb)->hwbuf));
2120 if ((*ippdsb)->buffer)
2121 HeapFree(GetProcessHeap(),0,(*ippdsb)->buffer);
2122 HeapFree(GetProcessHeap(),0,(*ippdsb));
2126 /* calculate fragment size and write lead */
2127 DSOUND_RecalcFormat(*ippdsb);
2129 /* It's not necessary to initialize values to zero since */
2130 /* we allocated this structure with HEAP_ZERO_MEMORY... */
2131 (*ippdsb)->playpos = 0;
2132 (*ippdsb)->buf_mixpos = 0;
2133 (*ippdsb)->state = STATE_STOPPED;
2134 DSOUND_RecalcVolPan(&((*ippdsb)->volpan));
2136 if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
2137 (*ippdsb)->freqAdjust = ((*ippdsb)->freq << DSOUND_FREQSHIFT) /
2138 primarybuf->wfx.nSamplesPerSec;
2139 (*ippdsb)->nAvgBytesPerSec = (*ippdsb)->freq *
2140 dsbd->lpwfxFormat->nBlockAlign;
2143 EnterCriticalSection(&(This->lock));
2144 /* register buffer */
2145 if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
2146 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl*)*(This->nrofbuffers+1));
2148 This->buffers = newbuffers;
2149 This->buffers[This->nrofbuffers] = *ippdsb;
2150 This->nrofbuffers++;
2151 TRACE("buffer count is now %d\n", This->nrofbuffers);
2153 ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
2154 err = DSERR_OUTOFMEMORY;
2157 LeaveCriticalSection(&(This->lock));
2159 IDirectSound_AddRef(iface);
2161 InitializeCriticalSection(&((*ippdsb)->lock));
2165 IDirectSoundBuffer_Release(*ppdsb);
2171 if (dsbd->dwFlags & DSBCAPS_CTRL3D) {
2172 IDirectSound3DBufferImpl *ds3db;
2174 ds3db = (IDirectSound3DBufferImpl*)HeapAlloc(GetProcessHeap(),
2176 ICOM_VTBL(ds3db) = &ds3dbvt;
2178 (*ippdsb)->ds3db = ds3db;
2180 ds3db->dsb = (*ippdsb);
2181 IDirectSoundBufferImpl_AddRef((LPDIRECTSOUNDBUFFER)(*ippdsb));
2183 InitializeCriticalSection(&ds3db->lock);
2185 ds3db->ds3db.dwSize = sizeof(DS3DBUFFER);
2186 ds3db->ds3db.vPosition.u1.x = 0.0;
2187 ds3db->ds3db.vPosition.u2.y = 0.0;
2188 ds3db->ds3db.vPosition.u3.z = 0.0;
2189 ds3db->ds3db.vVelocity.u1.x = 0.0;
2190 ds3db->ds3db.vVelocity.u2.y = 0.0;
2191 ds3db->ds3db.vVelocity.u3.z = 0.0;
2192 ds3db->ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
2193 ds3db->ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
2194 ds3db->ds3db.vConeOrientation.u1.x = 0.0;
2195 ds3db->ds3db.vConeOrientation.u2.y = 0.0;
2196 ds3db->ds3db.vConeOrientation.u3.z = 0.0;
2197 ds3db->ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME;
2198 ds3db->ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE;
2199 ds3db->ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
2200 ds3db->ds3db.dwMode = DS3DMODE_NORMAL;
2201 ds3db->buflen = ((*ippdsb)->buflen * primarybuf->wfx.nBlockAlign) /
2202 (*ippdsb)->wfx.nBlockAlign;
2203 ds3db->buffer = HeapAlloc(GetProcessHeap(), 0, ds3db->buflen);
2204 if (ds3db->buffer == NULL) {
2206 ds3db->ds3db.dwMode = DS3DMODE_DISABLE;
2208 ds3db->iks = (IKsPropertySetImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*(ds3db->iks)));
2209 ds3db->iks->ref = 1;
2210 ds3db->iks->ds3db = ds3db;
2211 ICOM_VTBL(ds3db->iks) = &iksvt;
2218 static HRESULT WINAPI IDirectSoundImpl_DuplicateSoundBuffer(
2219 LPDIRECTSOUND iface,LPDIRECTSOUNDBUFFER pdsb,LPLPDIRECTSOUNDBUFFER ppdsb
2221 ICOM_THIS(IDirectSoundImpl,iface);
2222 IDirectSoundBufferImpl* ipdsb=(IDirectSoundBufferImpl*)pdsb;
2223 IDirectSoundBufferImpl** ippdsb=(IDirectSoundBufferImpl**)ppdsb;
2224 TRACE("(%p,%p,%p)\n",This,ipdsb,ippdsb);
2227 FIXME("need to duplicate hardware buffer\n");
2230 *ippdsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBufferImpl));
2232 IDirectSoundBuffer_AddRef(pdsb);
2233 memcpy(*ippdsb, ipdsb, sizeof(IDirectSoundBufferImpl));
2235 (*ippdsb)->state = STATE_STOPPED;
2236 (*ippdsb)->playpos = 0;
2237 (*ippdsb)->buf_mixpos = 0;
2238 (*ippdsb)->dsound = This;
2239 (*ippdsb)->parent = ipdsb;
2240 memcpy(&((*ippdsb)->wfx), &(ipdsb->wfx), sizeof((*ippdsb)->wfx));
2241 InitializeCriticalSection(&(*ippdsb)->lock);
2242 /* register buffer */
2243 EnterCriticalSection(&(This->lock));
2245 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl**)*(This->nrofbuffers+1));
2247 This->buffers = newbuffers;
2248 This->buffers[This->nrofbuffers] = *ippdsb;
2249 This->nrofbuffers++;
2250 TRACE("buffer count is now %d\n", This->nrofbuffers);
2252 ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
2253 /* FIXME: release buffer */
2256 LeaveCriticalSection(&(This->lock));
2257 IDirectSound_AddRef(iface);
2262 static HRESULT WINAPI IDirectSoundImpl_GetCaps(LPDIRECTSOUND iface,LPDSCAPS caps) {
2263 ICOM_THIS(IDirectSoundImpl,iface);
2264 TRACE("(%p,%p)\n",This,caps);
2265 TRACE("(flags=0x%08lx)\n",caps->dwFlags);
2268 return DSERR_INVALIDPARAM;
2270 /* We should check this value, not set it. See Inside DirectX, p215. */
2271 caps->dwSize = sizeof(*caps);
2273 caps->dwFlags = This->drvcaps.dwFlags;
2275 /* FIXME: copy caps from This->drvcaps */
2276 caps->dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
2277 caps->dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
2279 caps->dwPrimaryBuffers = 1;
2281 caps->dwMaxHwMixingAllBuffers = 0;
2282 caps->dwMaxHwMixingStaticBuffers = 0;
2283 caps->dwMaxHwMixingStreamingBuffers = 0;
2285 caps->dwFreeHwMixingAllBuffers = 0;
2286 caps->dwFreeHwMixingStaticBuffers = 0;
2287 caps->dwFreeHwMixingStreamingBuffers = 0;
2289 caps->dwMaxHw3DAllBuffers = 0;
2290 caps->dwMaxHw3DStaticBuffers = 0;
2291 caps->dwMaxHw3DStreamingBuffers = 0;
2293 caps->dwFreeHw3DAllBuffers = 0;
2294 caps->dwFreeHw3DStaticBuffers = 0;
2295 caps->dwFreeHw3DStreamingBuffers = 0;
2297 caps->dwTotalHwMemBytes = 0;
2299 caps->dwFreeHwMemBytes = 0;
2301 caps->dwMaxContigFreeHwMemBytes = 0;
2303 caps->dwUnlockTransferRateHwBuffers = 4096; /* But we have none... */
2305 caps->dwPlayCpuOverheadSwBuffers = 1; /* 1% */
2310 static ULONG WINAPI IDirectSoundImpl_AddRef(LPDIRECTSOUND iface) {
2311 ICOM_THIS(IDirectSoundImpl,iface);
2312 return ++(This->ref);
2315 static ULONG WINAPI IDirectSoundImpl_Release(LPDIRECTSOUND iface) {
2316 ICOM_THIS(IDirectSoundImpl,iface);
2317 TRACE("(%p), ref was %ld\n",This,This->ref);
2318 if (!--(This->ref)) {
2321 timeKillEvent(This->timerID);
2322 timeEndPeriod(DS_TIME_RES);
2325 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)primarybuf);
2327 if (This->buffers) {
2328 for( i=0;i<This->nrofbuffers;i++)
2329 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->buffers[i]);
2333 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->primary);
2335 DeleteCriticalSection(&This->lock);
2337 IDsDriver_Close(This->driver);
2340 for (c=0; c<DS_HEL_FRAGS; c++)
2341 HeapFree(GetProcessHeap(),0,This->pwave[c]);
2343 if (This->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) {
2344 waveOutClose(This->hwo);
2347 IDsDriver_Release(This->driver);
2349 HeapFree(GetProcessHeap(),0,This);
2356 static HRESULT WINAPI IDirectSoundImpl_SetSpeakerConfig(
2357 LPDIRECTSOUND iface,DWORD config
2359 ICOM_THIS(IDirectSoundImpl,iface);
2360 FIXME("(%p,0x%08lx):stub\n",This,config);
2364 static HRESULT WINAPI IDirectSoundImpl_QueryInterface(
2365 LPDIRECTSOUND iface,REFIID riid,LPVOID *ppobj
2367 ICOM_THIS(IDirectSoundImpl,iface);
2369 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
2371 if (This->listener) {
2372 *ppobj = This->listener;
2373 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)This->listener);
2377 This->listener = (IDirectSound3DListenerImpl*)HeapAlloc(
2378 GetProcessHeap(), 0, sizeof(*(This->listener)));
2379 This->listener->ref = 1;
2380 ICOM_VTBL(This->listener) = &ds3dlvt;
2381 *ppobj = (LPVOID)This->listener;
2382 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj);
2384 This->listener->dsb = NULL;
2386 This->listener->ds3dl.dwSize = sizeof(DS3DLISTENER);
2387 This->listener->ds3dl.vPosition.u1.x = 0.0;
2388 This->listener->ds3dl.vPosition.u2.y = 0.0;
2389 This->listener->ds3dl.vPosition.u3.z = 0.0;
2390 This->listener->ds3dl.vVelocity.u1.x = 0.0;
2391 This->listener->ds3dl.vVelocity.u2.y = 0.0;
2392 This->listener->ds3dl.vVelocity.u3.z = 0.0;
2393 This->listener->ds3dl.vOrientFront.u1.x = 0.0;
2394 This->listener->ds3dl.vOrientFront.u2.y = 0.0;
2395 This->listener->ds3dl.vOrientFront.u3.z = 1.0;
2396 This->listener->ds3dl.vOrientTop.u1.x = 0.0;
2397 This->listener->ds3dl.vOrientTop.u2.y = 1.0;
2398 This->listener->ds3dl.vOrientTop.u3.z = 0.0;
2399 This->listener->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
2400 This->listener->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
2401 This->listener->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
2403 InitializeCriticalSection(&This->listener->lock);
2408 FIXME("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
2412 static HRESULT WINAPI IDirectSoundImpl_Compact(
2413 LPDIRECTSOUND iface)
2415 ICOM_THIS(IDirectSoundImpl,iface);
2416 TRACE("(%p)\n", This);
2420 static HRESULT WINAPI IDirectSoundImpl_GetSpeakerConfig(
2421 LPDIRECTSOUND iface,
2422 LPDWORD lpdwSpeakerConfig)
2424 ICOM_THIS(IDirectSoundImpl,iface);
2425 TRACE("(%p, %p)\n", This, lpdwSpeakerConfig);
2426 *lpdwSpeakerConfig = DSSPEAKER_STEREO | (DSSPEAKER_GEOMETRY_NARROW << 16);
2430 static HRESULT WINAPI IDirectSoundImpl_Initialize(
2431 LPDIRECTSOUND iface,
2434 ICOM_THIS(IDirectSoundImpl,iface);
2435 TRACE("(%p, %p)\n", This, lpcGuid);
2439 static ICOM_VTABLE(IDirectSound) dsvt =
2441 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2442 IDirectSoundImpl_QueryInterface,
2443 IDirectSoundImpl_AddRef,
2444 IDirectSoundImpl_Release,
2445 IDirectSoundImpl_CreateSoundBuffer,
2446 IDirectSoundImpl_GetCaps,
2447 IDirectSoundImpl_DuplicateSoundBuffer,
2448 IDirectSoundImpl_SetCooperativeLevel,
2449 IDirectSoundImpl_Compact,
2450 IDirectSoundImpl_GetSpeakerConfig,
2451 IDirectSoundImpl_SetSpeakerConfig,
2452 IDirectSoundImpl_Initialize
2456 static void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len)
2460 LPDSBPOSITIONNOTIFY event;
2462 if (dsb->nrofnotifies == 0)
2465 TRACE("(%p) buflen = %ld, playpos = %ld, len = %d\n",
2466 dsb, dsb->buflen, dsb->playpos, len);
2467 for (i = 0; i < dsb->nrofnotifies ; i++) {
2468 event = dsb->notifies + i;
2469 offset = event->dwOffset;
2470 TRACE("checking %d, position %ld, event = %d\n",
2471 i, offset, event->hEventNotify);
2472 /* DSBPN_OFFSETSTOP has to be the last element. So this is */
2473 /* OK. [Inside DirectX, p274] */
2475 /* This also means we can't sort the entries by offset, */
2476 /* because DSBPN_OFFSETSTOP == -1 */
2477 if (offset == DSBPN_OFFSETSTOP) {
2478 if (dsb->state == STATE_STOPPED) {
2479 SetEvent(event->hEventNotify);
2480 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2485 if ((dsb->playpos + len) >= dsb->buflen) {
2486 if ((offset < ((dsb->playpos + len) % dsb->buflen)) ||
2487 (offset >= dsb->playpos)) {
2488 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2489 SetEvent(event->hEventNotify);
2492 if ((offset >= dsb->playpos) && (offset < (dsb->playpos + len))) {
2493 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2494 SetEvent(event->hEventNotify);
2500 /* WAV format info can be found at: */
2502 /* http://www.cwi.nl/ftp/audio/AudioFormats.part2 */
2503 /* ftp://ftp.cwi.nl/pub/audio/RIFF-format */
2505 /* Import points to remember: */
2507 /* 8-bit WAV is unsigned */
2508 /* 16-bit WAV is signed */
2510 static inline INT16 cvtU8toS16(BYTE byte)
2512 INT16 s = (byte - 128) << 8;
2517 static inline BYTE cvtS16toU8(INT16 word)
2519 BYTE b = (word + 32768) >> 8;
2525 /* We should be able to optimize these two inline functions */
2526 /* so that we aren't doing 8->16->8 conversions when it is */
2527 /* not necessary. But this is still a WIP. Optimize later. */
2528 static inline void get_fields(const IDirectSoundBufferImpl *dsb, BYTE *buf, INT *fl, INT *fr)
2530 INT16 *bufs = (INT16 *) buf;
2532 /* TRACE("(%p)\n", buf); */
2533 if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 2) {
2534 *fl = cvtU8toS16(*buf);
2535 *fr = cvtU8toS16(*(buf + 1));
2539 if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 2) {
2545 if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 1) {
2546 *fl = cvtU8toS16(*buf);
2551 if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 1) {
2557 FIXME("get_fields found an unsupported configuration\n");
2561 static inline void set_fields(BYTE *buf, INT fl, INT fr)
2563 INT16 *bufs = (INT16 *) buf;
2565 if ((primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 2)) {
2566 *buf = cvtS16toU8(fl);
2567 *(buf + 1) = cvtS16toU8(fr);
2571 if ((primarybuf->wfx.wBitsPerSample == 16) && (primarybuf->wfx.nChannels == 2)) {
2577 if ((primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 1)) {
2578 *buf = cvtS16toU8((fl + fr) >> 1);
2582 if ((primarybuf->wfx.wBitsPerSample == 16) && (primarybuf->wfx.nChannels == 1)) {
2583 *bufs = (fl + fr) >> 1;
2586 FIXME("set_fields found an unsupported configuration\n");
2590 /* Now with PerfectPitch (tm) technology */
2591 static INT DSOUND_MixerNorm(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2593 INT i, size, ipos, ilen, fieldL, fieldR;
2595 INT iAdvance = dsb->wfx.nBlockAlign;
2596 INT oAdvance = primarybuf->wfx.nBlockAlign;
2598 ibp = dsb->buffer + dsb->buf_mixpos;
2601 TRACE("(%p, %p, %p), buf_mixpos=%ld\n", dsb, ibp, obp, dsb->buf_mixpos);
2602 /* Check for the best case */
2603 if ((dsb->freq == primarybuf->wfx.nSamplesPerSec) &&
2604 (dsb->wfx.wBitsPerSample == primarybuf->wfx.wBitsPerSample) &&
2605 (dsb->wfx.nChannels == primarybuf->wfx.nChannels)) {
2606 DWORD bytesleft = dsb->buflen - dsb->buf_mixpos;
2607 TRACE("(%p) Best case\n", dsb);
2608 if (len <= bytesleft )
2609 memcpy(obp, ibp, len);
2611 memcpy(obp, ibp, bytesleft );
2612 memcpy(obp + bytesleft, dsb->buffer, len - bytesleft);
2617 /* Check for same sample rate */
2618 if (dsb->freq == primarybuf->wfx.nSamplesPerSec) {
2619 TRACE("(%p) Same sample rate %ld = primary %ld\n", dsb,
2620 dsb->freq, primarybuf->wfx.nSamplesPerSec);
2622 for (i = 0; i < len; i += oAdvance) {
2623 get_fields(dsb, ibp, &fieldL, &fieldR);
2626 set_fields(obp, fieldL, fieldR);
2628 if (ibp >= (BYTE *)(dsb->buffer + dsb->buflen))
2629 ibp = dsb->buffer; /* wrap */
2634 /* Mix in different sample rates */
2636 /* New PerfectPitch(tm) Technology (c) 1998 Rob Riggs */
2637 /* Patent Pending :-] */
2639 /* Patent enhancements (c) 2000 Ove KÃ¥ven,
2640 * TransGaming Technologies Inc. */
2642 TRACE("(%p) Adjusting frequency: %ld -> %ld\n",
2643 dsb, dsb->freq, primarybuf->wfx.nSamplesPerSec);
2645 size = len / oAdvance;
2647 ipos = dsb->buf_mixpos;
2648 for (i = 0; i < size; i++) {
2649 get_fields(dsb, (dsb->buffer + ipos), &fieldL, &fieldR);
2650 set_fields(obp, fieldL, fieldR);
2653 dsb->freqAcc += dsb->freqAdjust;
2654 if (dsb->freqAcc >= (1<<DSOUND_FREQSHIFT)) {
2655 ULONG adv = (dsb->freqAcc>>DSOUND_FREQSHIFT) * iAdvance;
2656 dsb->freqAcc &= (1<<DSOUND_FREQSHIFT)-1;
2657 ipos += adv; ilen += adv;
2658 while (ipos >= dsb->buflen)
2659 ipos -= dsb->buflen;
2665 static void DSOUND_MixerVol(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2667 INT i, inc = primarybuf->wfx.wBitsPerSample >> 3;
2669 INT16 *bps = (INT16 *) buf;
2671 TRACE("(%p) left = %lx, right = %lx\n", dsb,
2672 dsb->volpan.dwTotalLeftAmpFactor, dsb->volpan.dwTotalRightAmpFactor);
2673 if ((!(dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->volpan.lPan == 0)) &&
2674 (!(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->volpan.lVolume == 0)) &&
2675 !(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
2676 return; /* Nothing to do */
2678 /* If we end up with some bozo coder using panning or 3D sound */
2679 /* with a mono primary buffer, it could sound very weird using */
2680 /* this method. Oh well, tough patooties. */
2682 for (i = 0; i < len; i += inc) {
2688 /* 8-bit WAV is unsigned, but we need to operate */
2689 /* on signed data for this to work properly */
2691 val = ((val * (i & inc ? dsb->volpan.dwTotalRightAmpFactor : dsb->volpan.dwTotalLeftAmpFactor)) >> 16);
2696 /* 16-bit WAV is signed -- much better */
2698 val = ((val * ((i & inc) ? dsb->volpan.dwTotalRightAmpFactor : dsb->volpan.dwTotalLeftAmpFactor)) >> 16);
2704 FIXME("MixerVol had a nasty error\n");
2710 static void DSOUND_Mixer3D(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2713 DWORD buflen, buf_mixpos;
2715 buflen = dsb->ds3db->buflen;
2716 buf_mixpos = (dsb->buf_mixpos * primarybuf->wfx.nBlockAlign) / dsb->wfx.nBlockAlign;
2717 ibp = dsb->ds3db->buffer + buf_mixpos;
2720 if (buf_mixpos > buflen) {
2721 FIXME("Major breakage\n");
2725 if (len <= (buf_mixpos + buflen))
2726 memcpy(obp, ibp, len);
2728 memcpy(obp, ibp, buflen - buf_mixpos);
2729 memcpy(obp + (buflen - buf_mixpos),
2731 len - (buflen - buf_mixpos));
2737 static void *tmp_buffer;
2738 static size_t tmp_buffer_len = 0;
2740 static void *DSOUND_tmpbuffer(size_t len)
2742 if (len>tmp_buffer_len) {
2743 void *new_buffer = realloc(tmp_buffer, len);
2745 tmp_buffer = new_buffer;
2746 tmp_buffer_len = len;
2753 static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD fraglen)
2755 INT i, len, ilen, temp, field;
2756 INT advance = primarybuf->wfx.wBitsPerSample >> 3;
2757 BYTE *buf, *ibuf, *obuf;
2758 INT16 *ibufs, *obufs;
2761 if (!(dsb->playflags & DSBPLAY_LOOPING)) {
2762 temp = MulDiv(primarybuf->wfx.nAvgBytesPerSec, dsb->buflen,
2763 dsb->nAvgBytesPerSec) -
2764 MulDiv(primarybuf->wfx.nAvgBytesPerSec, dsb->buf_mixpos,
2765 dsb->nAvgBytesPerSec);
2766 len = (len > temp) ? temp : len;
2768 len &= ~3; /* 4 byte alignment */
2771 /* This should only happen if we aren't looping and temp < 4 */
2773 /* We skip the remainder, so check for possible events */
2774 DSOUND_CheckEvent(dsb, dsb->buflen - dsb->buf_mixpos);
2776 dsb->state = STATE_STOPPED;
2778 dsb->buf_mixpos = 0;
2779 dsb->leadin = FALSE;
2780 /* Check for DSBPN_OFFSETSTOP */
2781 DSOUND_CheckEvent(dsb, 0);
2785 /* Been seeing segfaults in malloc() for some reason... */
2786 TRACE("allocating buffer (size = %d)\n", len);
2787 if ((buf = ibuf = (BYTE *) DSOUND_tmpbuffer(len)) == NULL)
2790 TRACE("MixInBuffer (%p) len = %d, dest = %ld\n", dsb, len, writepos);
2792 ilen = DSOUND_MixerNorm(dsb, ibuf, len);
2793 if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
2794 (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
2795 DSOUND_MixerVol(dsb, ibuf, len);
2797 obuf = primarybuf->buffer + writepos;
2798 for (i = 0; i < len; i += advance) {
2799 obufs = (INT16 *) obuf;
2800 ibufs = (INT16 *) ibuf;
2801 if (primarybuf->wfx.wBitsPerSample == 8) {
2802 /* 8-bit WAV is unsigned */
2803 field = (*ibuf - 128);
2804 field += (*obuf - 128);
2805 field = field > 127 ? 127 : field;
2806 field = field < -128 ? -128 : field;
2807 *obuf = field + 128;
2809 /* 16-bit WAV is signed */
2812 field = field > 32767 ? 32767 : field;
2813 field = field < -32768 ? -32768 : field;
2818 if (obuf >= (BYTE *)(primarybuf->buffer + primarybuf->buflen))
2819 obuf = primarybuf->buffer;
2823 if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY)
2824 DSOUND_CheckEvent(dsb, ilen);
2826 if (dsb->leadin && (dsb->startpos > dsb->buf_mixpos) && (dsb->startpos <= dsb->buf_mixpos + ilen)) {
2827 /* HACK... leadin should be reset when the PLAY position reaches the startpos,
2828 * not the MIX position... but if the sound buffer is bigger than our prebuffering
2829 * (which must be the case for the streaming buffers that need this hack anyway)
2830 * plus DS_HEL_MARGIN or equivalent, then this ought to work anyway. */
2831 dsb->leadin = FALSE;
2834 dsb->buf_mixpos += ilen;
2836 if (dsb->buf_mixpos >= dsb->buflen) {
2837 if (!(dsb->playflags & DSBPLAY_LOOPING)) {
2838 dsb->state = STATE_STOPPED;
2840 dsb->buf_mixpos = 0;
2841 dsb->leadin = FALSE;
2842 DSOUND_CheckEvent(dsb, 0); /* For DSBPN_OFFSETSTOP */
2845 while (dsb->buf_mixpos >= dsb->buflen)
2846 dsb->buf_mixpos -= dsb->buflen;
2847 if (dsb->leadin && (dsb->startpos <= dsb->buf_mixpos))
2848 dsb->leadin = FALSE; /* HACK: see above */
2855 static void DSOUND_MixCancel(IDirectSoundBufferImpl *dsb, DWORD writepos)
2857 FIXME("prebuffer cancel not implemented yet\n");
2860 static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD playpos, DWORD writepos, DWORD mixlen)
2863 /* determine this buffer's write position */
2864 DWORD buf_writepos = DSOUND_CalcPlayPosition(dsb, dsb->state & primarybuf->state, writepos,
2865 writepos, dsb->primary_mixpos, dsb->buf_mixpos);
2866 /* determine how much already-mixed data exists */
2868 ((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) +
2869 dsb->buf_mixpos - buf_writepos;
2870 DWORD primary_done =
2871 ((dsb->primary_mixpos < writepos) ? primarybuf->buflen : 0) +
2872 dsb->primary_mixpos - writepos;
2874 ((primarybuf->buf_mixpos < writepos) ? primarybuf->buflen : 0) +
2875 primarybuf->buf_mixpos - writepos;
2878 TRACE("buf_writepos=%ld, primary_writepos=%ld\n", buf_writepos, writepos);
2879 TRACE("buf_done=%ld, primary_done=%ld\n", buf_done, primary_done);
2880 TRACE("buf_mixpos=%ld, primary_mixpos=%ld, mixlen=%ld\n", dsb->buf_mixpos, dsb->primary_mixpos,
2882 TRACE("looping=%ld, startpos=%ld, leadin=%ld\n", dsb->playflags, dsb->startpos, dsb->leadin);
2884 /* save write position for non-GETCURRENTPOSITION2... */
2885 dsb->playpos = buf_writepos;
2887 /* check whether CalcPlayPosition detected a mixing underrun */
2888 if ((buf_done == 0) && (dsb->primary_mixpos != writepos)) {
2889 /* it did, but did we have more to play? */
2890 if ((dsb->playflags & DSBPLAY_LOOPING) ||
2891 (dsb->buf_mixpos < dsb->buflen)) {
2892 /* yes, have to recover */
2893 ERR("underrun on sound buffer %p\n", dsb);
2894 TRACE("recovering from underrun: primary_mixpos=%ld\n", writepos);
2896 dsb->primary_mixpos = writepos;
2899 /* determine how far ahead we should mix */
2900 if (((dsb->playflags & DSBPLAY_LOOPING) ||
2901 (dsb->leadin && (dsb->probably_valid_to != 0))) &&
2902 !(dsb->dsbd.dwFlags & DSBCAPS_STATIC)) {
2903 /* if this is a streaming buffer, it typically means that
2904 * we should defer mixing past probably_valid_to as long
2905 * as we can, to avoid unnecessary remixing */
2906 /* the heavy-looking calculations shouldn't be that bad,
2907 * as any game isn't likely to be have more than 1 or 2
2908 * streaming buffers in use at any time anyway... */
2909 DWORD probably_valid_left =
2910 (dsb->probably_valid_to == (DWORD)-1) ? dsb->buflen :
2911 ((dsb->probably_valid_to < buf_writepos) ? dsb->buflen : 0) +
2912 dsb->probably_valid_to - buf_writepos;
2913 /* check for leadin condition */
2914 if ((probably_valid_left == 0) &&
2915 (dsb->probably_valid_to == dsb->startpos) &&
2917 probably_valid_left = dsb->buflen;
2918 TRACE("streaming buffer probably_valid_to=%ld, probably_valid_left=%ld\n",
2919 dsb->probably_valid_to, probably_valid_left);
2920 /* check whether the app's time is already up */
2921 if (probably_valid_left < dsb->writelead) {
2922 WARN("probably_valid_to now within writelead, possible streaming underrun\n");
2923 /* once we pass the point of no return,
2924 * no reason to hold back anymore */
2925 dsb->probably_valid_to = (DWORD)-1;
2926 /* we just have to go ahead and mix what we have,
2927 * there's no telling what the app is thinking anyway */
2929 /* divide valid length by our sample size */
2930 probably_valid_left /= dsb->wfx.nBlockAlign;
2931 /* adjust for our frequency */
2932 probably_valid_left = (probably_valid_left << DSOUND_FREQSHIFT) / dsb->freqAdjust;
2933 /* multiply by primary sample size */
2934 probably_valid_left *= primarybuf->wfx.nBlockAlign;
2935 /* check whether to clip mix_len */
2936 if (probably_valid_left < mixlen) {
2937 TRACE("clipping to probably_valid_left=%ld\n", probably_valid_left);
2938 mixlen = probably_valid_left;
2942 /* cut mixlen with what's already been mixed */
2943 if (mixlen < primary_done) {
2944 /* huh? and still CalcPlayPosition didn't
2945 * detect an underrun? */
2946 FIXME("problem with underrun detection (mixlen=%ld < primary_done=%ld)\n", mixlen, primary_done);
2949 len = mixlen - primary_done;
2950 TRACE("remaining mixlen=%ld\n", len);
2952 if (len < primarybuf->dsound->fraglen) {
2953 /* smaller than a fragment, wait until it gets larger
2954 * before we take the mixing overhead */
2955 TRACE("mixlen not worth it, deferring mixing\n");
2959 /* ok, we know how much to mix, let's go */
2960 still_behind = (adv_done > primary_done);
2962 slen = primarybuf->buflen - dsb->primary_mixpos;
2963 if (slen > len) slen = len;
2964 slen = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, slen);
2966 if ((dsb->primary_mixpos < primarybuf->buf_mixpos) &&
2967 (dsb->primary_mixpos + slen >= primarybuf->buf_mixpos))
2968 still_behind = FALSE;
2970 dsb->primary_mixpos += slen; len -= slen;
2971 while (dsb->primary_mixpos >= primarybuf->buflen)
2972 dsb->primary_mixpos -= primarybuf->buflen;
2974 if ((dsb->state == STATE_STOPPED) || !slen) break;
2976 TRACE("new primary_mixpos=%ld, primary_advbase=%ld\n", dsb->primary_mixpos, primarybuf->buf_mixpos);
2977 TRACE("mixed data len=%ld, still_behind=%d\n", mixlen-len, still_behind);
2978 /* return how far we think the primary buffer can
2979 * advance its underrun detector...*/
2980 if (still_behind) return 0;
2981 if ((mixlen - len) < primary_done) return 0;
2982 slen = ((dsb->primary_mixpos < primarybuf->buf_mixpos) ?
2983 primarybuf->buflen : 0) + dsb->primary_mixpos -
2984 primarybuf->buf_mixpos;
2985 if (slen > mixlen) {
2986 /* the primary_done and still_behind checks above should have worked */
2987 FIXME("problem with advancement calculation (advlen=%ld > mixlen=%ld)\n", slen, mixlen);
2993 static DWORD DSOUND_MixToPrimary(DWORD playpos, DWORD writepos, DWORD mixlen, BOOL recover)
2995 INT i, len, maxlen = 0;
2996 IDirectSoundBufferImpl *dsb;
2998 TRACE("(%ld,%ld,%ld)\n", playpos, writepos, mixlen);
2999 for (i = dsound->nrofbuffers - 1; i >= 0; i--) {
3000 dsb = dsound->buffers[i];
3002 if (!dsb || !(ICOM_VTBL(dsb)))
3004 if (dsb->buflen && dsb->state && !dsb->hwbuf) {
3005 TRACE("Checking %p, mixlen=%ld\n", dsb, mixlen);
3006 EnterCriticalSection(&(dsb->lock));
3007 if (dsb->state == STATE_STOPPING) {
3008 DSOUND_MixCancel(dsb, writepos);
3009 dsb->state = STATE_STOPPED;
3011 if ((dsb->state == STATE_STARTING) || recover)
3012 dsb->primary_mixpos = writepos;
3013 len = DSOUND_MixOne(dsb, playpos, writepos, mixlen);
3014 if (dsb->state == STATE_STARTING)
3015 dsb->state = STATE_PLAYING;
3016 maxlen = (len > maxlen) ? len : maxlen;
3018 LeaveCriticalSection(&(dsb->lock));
3025 static void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
3031 if (!dsound || !primarybuf) {
3032 ERR("dsound died without killing us?\n");
3033 timeKillEvent(timerID);
3034 timeEndPeriod(DS_TIME_RES);
3038 EnterCriticalSection(&(dsound->lock));
3040 if (!primarybuf || !primarybuf->ref) {
3041 /* seems the primary buffer is currently being released */
3042 LeaveCriticalSection(&(dsound->lock));
3046 /* the sound of silence */
3047 nfiller = primarybuf->wfx.wBitsPerSample == 8 ? 128 : 0;
3049 /* whether the primary is forced to play even without secondary buffers */
3050 forced = ((primarybuf->state == STATE_PLAYING) || (primarybuf->state == STATE_STARTING));
3052 if (primarybuf->hwbuf) {
3053 if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
3054 BOOL paused = ((primarybuf->state == STATE_STOPPED) || (primarybuf->state == STATE_STARTING));
3055 /* FIXME: document variables */
3056 DWORD playpos, writepos, inq, maxq, frag;
3057 hres = IDsDriverBuffer_GetPosition(primarybuf->hwbuf, &playpos, &writepos);
3059 LeaveCriticalSection(&(dsound->lock));
3062 /* Well, we *could* do Just-In-Time mixing using the writepos,
3063 * but that's a little bit ambitious and unnecessary... */
3064 /* rather add our safety margin to the writepos, if we're playing */
3066 writepos += primarybuf->writelead;
3067 while (writepos >= primarybuf->buflen)
3068 writepos -= primarybuf->buflen;
3069 } else writepos = playpos;
3070 TRACE("primary playpos=%ld, writepos=%ld, clrpos=%ld, mixpos=%ld\n",
3071 playpos,writepos,primarybuf->playpos,primarybuf->buf_mixpos);
3072 /* wipe out just-played sound data */
3073 if (playpos < primarybuf->playpos) {
3074 memset(primarybuf->buffer + primarybuf->playpos, nfiller, primarybuf->buflen - primarybuf->playpos);
3075 memset(primarybuf->buffer, nfiller, playpos);
3077 memset(primarybuf->buffer + primarybuf->playpos, nfiller, playpos - primarybuf->playpos);
3079 primarybuf->playpos = playpos;
3081 /* check how much prebuffering is left */
3082 inq = primarybuf->buf_mixpos;
3084 inq += primarybuf->buflen;
3087 /* find the maximum we can prebuffer */
3090 if (maxq < writepos)
3091 maxq += primarybuf->buflen;
3093 } else maxq = primarybuf->buflen;
3095 /* clip maxq to DS_SND_QUEUE */
3096 frag = DS_SND_QUEUE * dsound->fraglen;
3097 if (maxq > frag) maxq = frag;
3099 EnterCriticalSection(&(primarybuf->lock));
3101 /* check for consistency */
3103 /* the playback position must have passed our last
3104 * mixed position, i.e. it's an underrun, or we have
3105 * nothing more to play */
3106 TRACE("reached end of mixed data (inq=%ld, maxq=%ld)\n", inq, maxq);
3108 /* stop the playback now, to allow buffers to refill */
3109 DSOUND_PrimaryStop(primarybuf);
3110 if (primarybuf->state == STATE_PLAYING) {
3111 primarybuf->state = STATE_STARTING;
3113 else if (primarybuf->state == STATE_STOPPING) {
3114 primarybuf->state = STATE_STOPPED;
3117 /* how can we have an underrun if we aren't playing? */
3118 WARN("unexpected primary state (%ld)\n", primarybuf->state);
3120 /* the Stop is supposed to reset play position to beginning of buffer */
3121 /* unfortunately, OSS is not able to do so, so get current pointer */
3122 hres = IDsDriverBuffer_GetPosition(primarybuf->hwbuf, &playpos, NULL);
3124 LeaveCriticalSection(&(dsound->lock));
3125 LeaveCriticalSection(&(primarybuf->lock));
3129 primarybuf->playpos = playpos;
3130 primarybuf->buf_mixpos = writepos;
3132 maxq = primarybuf->buflen;
3133 if (maxq > frag) maxq = frag;
3134 memset(primarybuf->buffer, nfiller, primarybuf->buflen);
3139 frag = DSOUND_MixToPrimary(playpos, writepos, maxq, paused);
3140 if (forced) frag = maxq - inq;
3141 primarybuf->buf_mixpos += frag;
3142 while (primarybuf->buf_mixpos >= primarybuf->buflen)
3143 primarybuf->buf_mixpos -= primarybuf->buflen;
3146 /* buffers have been filled, restart playback */
3147 if (primarybuf->state == STATE_STARTING) {
3148 DSOUND_PrimaryPlay(primarybuf);
3149 primarybuf->state = STATE_PLAYING;
3150 TRACE("starting playback\n");
3152 else if (primarybuf->state == STATE_STOPPED) {
3153 /* the primarybuf is supposed to play if there's something to play
3154 * even if it is reported as stopped, so don't let this confuse you */
3155 DSOUND_PrimaryPlay(primarybuf);
3156 primarybuf->state = STATE_STOPPING;
3157 TRACE("starting playback\n");
3160 LeaveCriticalSection(&(primarybuf->lock));
3162 /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
3163 if (primarybuf->state == STATE_STARTING) {
3164 DSOUND_PrimaryPlay(primarybuf);
3165 primarybuf->state = STATE_PLAYING;
3167 else if (primarybuf->state == STATE_STOPPING) {
3168 DSOUND_PrimaryStop(primarybuf);
3169 primarybuf->state = STATE_STOPPED;
3173 /* using waveOut stuff */
3174 /* if no buffers are playing, we should be in pause mode now */
3175 DWORD writepos, mixq;
3176 /* clean out completed fragments */
3177 while (dsound->pwqueue && (dsound->pwave[dsound->pwplay]->dwFlags & WHDR_DONE)) {
3178 if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
3179 DWORD pos = dsound->pwplay * dsound->fraglen;
3180 TRACE("done playing primary pos=%ld\n",pos);
3181 memset(primarybuf->buffer + pos, nfiller, dsound->fraglen);
3184 if (dsound->pwplay >= DS_HEL_FRAGS) dsound->pwplay = 0;
3187 primarybuf->playpos = dsound->pwplay * dsound->fraglen;
3188 TRACE("primary playpos=%ld, mixpos=%ld\n",primarybuf->playpos,primarybuf->buf_mixpos);
3189 EnterCriticalSection(&(primarybuf->lock));
3191 if (!dsound->pwqueue) {
3192 /* this is either an underrun or we have nothing more to play...
3193 * since playback has already stopped now, we can enter pause mode,
3194 * in order to allow buffers to refill */
3195 if (primarybuf->state == STATE_PLAYING) {
3196 TRACE("no more fragments ready to play\n");
3197 waveOutPause(dsound->hwo);
3198 primarybuf->state = STATE_STARTING;
3200 else if (primarybuf->state == STATE_STOPPING) {
3201 TRACE("no more fragments ready to play\n");
3202 waveOutPause(dsound->hwo);
3203 primarybuf->state = STATE_STOPPED;
3207 /* find next write position, plus some extra margin, if necessary */
3208 mixq = DS_HEL_MARGIN;
3209 if (mixq > dsound->pwqueue) mixq = dsound->pwqueue;
3210 writepos = primarybuf->playpos + mixq * dsound->fraglen;
3211 while (writepos >= primarybuf->buflen) writepos -= primarybuf->buflen;
3214 if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
3215 DWORD frag, maxq = DS_SND_QUEUE * dsound->fraglen;
3216 frag = DSOUND_MixToPrimary(primarybuf->playpos, writepos, maxq, FALSE);
3217 mixq = frag / dsound->fraglen;
3218 if (frag - (mixq * dsound->fraglen))
3221 if (forced) mixq = DS_SND_QUEUE;
3223 /* Make sure that we don't rewrite any fragments that have already been
3224 written and are waiting to be played */
3225 mixq = (dsound->pwqueue > mixq) ? 0 : (mixq - dsound->pwqueue);
3228 for (; mixq; mixq--) {
3229 waveOutWrite(dsound->hwo, dsound->pwave[dsound->pwwrite], sizeof(WAVEHDR));
3231 if (dsound->pwwrite >= DS_HEL_FRAGS) dsound->pwwrite = 0;
3234 primarybuf->buf_mixpos = dsound->pwwrite * dsound->fraglen;
3236 if (dsound->pwqueue) {
3237 /* buffers have been filled, restart playback */
3238 if (primarybuf->state == STATE_STARTING) {
3239 waveOutRestart(dsound->hwo);
3240 primarybuf->state = STATE_PLAYING;
3241 TRACE("starting playback\n");
3243 else if (primarybuf->state == STATE_STOPPED) {
3244 /* the primarybuf is supposed to play if there's something to play
3245 * even if it is reported as stopped, so don't let this confuse you */
3246 waveOutRestart(dsound->hwo);
3247 primarybuf->state = STATE_STOPPING;
3248 TRACE("starting playback\n");
3251 LeaveCriticalSection(&(primarybuf->lock));
3253 TRACE("completed processing\n");
3254 LeaveCriticalSection(&(dsound->lock));
3257 /*******************************************************************************
3258 * DirectSoundCreate (DSOUND.1)
3260 HRESULT WINAPI DirectSoundCreate(REFGUID lpGUID,LPDIRECTSOUND *ppDS,IUnknown *pUnkOuter )
3262 IDirectSoundImpl** ippDS=(IDirectSoundImpl**)ppDS;
3263 PIDSDRIVER drv = NULL;
3266 HRESULT err = DS_OK;
3269 TRACE("(%p,%p,%p)\n",lpGUID,ippDS,pUnkOuter);
3271 TRACE("DirectSoundCreate (%p)\n", ippDS);
3274 return DSERR_INVALIDPARAM;
3277 IDirectSound_AddRef((LPDIRECTSOUND)dsound);
3282 /* Enumerate WINMM audio devices and find the one we want */
3283 wodn = waveOutGetNumDevs();
3284 if (!wodn) return DSERR_NODRIVER;
3286 /* FIXME: How do we find the GUID of an audio device? */
3287 /* Well, just use the first available device for now... */
3289 /* Get output device caps */
3290 waveOutGetDevCapsA(wod, &wcaps, sizeof(wcaps));
3291 /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
3292 waveOutMessage(wod, DRV_QUERYDSOUNDIFACE, (DWORD)&drv, 0);
3294 /* Allocate memory */
3295 *ippDS = (IDirectSoundImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSoundImpl));
3297 return DSERR_OUTOFMEMORY;
3299 ICOM_VTBL(*ippDS) = &dsvt;
3302 (*ippDS)->driver = drv;
3303 (*ippDS)->fraglen = 0;
3304 (*ippDS)->priolevel = DSSCL_NORMAL;
3305 (*ippDS)->nrofbuffers = 0;
3306 (*ippDS)->buffers = NULL;
3307 (*ippDS)->primary = NULL;
3308 (*ippDS)->listener = NULL;
3310 /* Get driver description */
3312 IDsDriver_GetDriverDesc(drv,&((*ippDS)->drvdesc));
3314 /* if no DirectSound interface available, use WINMM API instead */
3315 (*ippDS)->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
3316 (*ippDS)->drvdesc.dnDevNode = wod; /* FIXME? */
3319 /* Set default wave format (may need it for waveOutOpen) */
3320 (*ippDS)->wfx.wFormatTag = WAVE_FORMAT_PCM;
3321 (*ippDS)->wfx.nChannels = 2;
3322 (*ippDS)->wfx.nSamplesPerSec = 22050;
3323 (*ippDS)->wfx.nAvgBytesPerSec = 44100;
3324 (*ippDS)->wfx.nBlockAlign = 2;
3325 (*ippDS)->wfx.wBitsPerSample = 8;
3327 /* If the driver requests being opened through MMSYSTEM
3328 * (which is recommended by the DDK), it is supposed to happen
3329 * before the DirectSound interface is opened */
3330 if ((*ippDS)->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) {
3331 /* FIXME: is this right? */
3332 err = mmErr(waveOutOpen(&((*ippDS)->hwo), (*ippDS)->drvdesc.dnDevNode, &((*ippDS)->wfx),
3333 0, 0, CALLBACK_NULL | WAVE_DIRECTSOUND));
3336 if (drv && (err == DS_OK))
3337 err = IDsDriver_Open(drv);
3339 /* FIXME: do we want to handle a temporarily busy device? */
3341 HeapFree(GetProcessHeap(),0,*ippDS);
3346 /* the driver is now open, so it's now allowed to call GetCaps */
3348 IDsDriver_GetCaps(drv,&((*ippDS)->drvcaps));
3352 /* FIXME: look at wcaps */
3353 (*ippDS)->drvcaps.dwFlags =
3354 DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO;
3356 (*ippDS)->drvcaps.dwFlags |= DSCAPS_EMULDRIVER;
3358 /* Allocate memory for HEL buffer headers */
3359 for (c=0; c<DS_HEL_FRAGS; c++) {
3360 (*ippDS)->pwave[c] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEHDR));
3361 if (!(*ippDS)->pwave[c]) {
3362 /* Argh, out of memory */
3364 HeapFree(GetProcessHeap(),0,(*ippDS)->pwave[c]);
3365 waveOutClose((*ippDS)->hwo);
3366 HeapFree(GetProcessHeap(),0,*ippDS);
3368 return DSERR_OUTOFMEMORY;
3374 InitializeCriticalSection(&((*ippDS)->lock));
3378 if (primarybuf == NULL) {
3382 dsbd.dwSize = sizeof(DSBUFFERDESC);
3383 dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
3384 dsbd.dwBufferBytes = 0;
3385 dsbd.lpwfxFormat = &(dsound->wfx);
3386 hr = IDirectSound_CreateSoundBuffer(*ppDS, &dsbd, (LPDIRECTSOUNDBUFFER*)&primarybuf, NULL);
3390 /* dsound->primary is NULL - don't need to Release */
3391 dsound->primary = primarybuf;
3392 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)primarybuf);
3394 timeBeginPeriod(DS_TIME_RES);
3395 dsound->timerID = timeSetEvent(DS_TIME_DEL, DS_TIME_RES, DSOUND_timer,
3396 (DWORD)dsound, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
3401 /***************************************************************************
3402 * DirectSoundCaptureCreate [DSOUND.6]
3404 * Create and initialize a DirectSoundCapture interface
3408 * Failure: DSERR_NOAGGREGATION, DSERR_ALLOCATED, DSERR_INVALIDPARAM,
3411 HRESULT WINAPI DirectSoundCaptureCreate(
3413 LPDIRECTSOUNDCAPTURE* lplpDSC,
3414 LPUNKNOWN pUnkOuter )
3416 TRACE("(%s,%p,%p)\n", debugstr_guid(lpcGUID), lplpDSC, pUnkOuter);
3419 return DSERR_NOAGGREGATION;
3422 /* Default device? */
3424 return DSOUND_CreateDirectSoundCapture( (LPVOID*)lplpDSC );
3427 FIXME( "Unknown GUID %s\n", debugstr_guid(lpcGUID) );
3430 return DSERR_OUTOFMEMORY;
3433 /***************************************************************************
3434 * DirectSoundCaptureEnumerateA [DSOUND.7]
3436 * Enumerate all DirectSound drivers installed in the system
3440 * Failure: DSERR_INVALIDPARAM
3442 HRESULT WINAPI DirectSoundCaptureEnumerateA(
3443 LPDSENUMCALLBACKA lpDSEnumCallback,
3446 TRACE("(%p,%p)\n", lpDSEnumCallback, lpContext );
3448 if ( lpDSEnumCallback )
3449 lpDSEnumCallback(NULL,"WINE Primary Sound Capture Driver",
3450 "SoundCap",lpContext);
3456 /***************************************************************************
3457 * DirectSoundCaptureEnumerateW [DSOUND.8]
3459 * Enumerate all DirectSound drivers installed in the system
3463 * Failure: DSERR_INVALIDPARAM
3465 HRESULT WINAPI DirectSoundCaptureEnumerateW(
3466 LPDSENUMCALLBACKW lpDSEnumCallback,
3469 FIXME("(%p,%p):stub\n", lpDSEnumCallback, lpContext );
3474 DSOUND_CreateDirectSoundCapture( LPVOID* ppobj )
3476 *ppobj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( IDirectSoundCaptureImpl ) );
3478 if ( *ppobj == NULL ) {
3479 return DSERR_OUTOFMEMORY;
3483 ICOM_THIS(IDirectSoundCaptureImpl,*ppobj);
3486 ICOM_VTBL(This) = &dscvt;
3488 InitializeCriticalSection( &This->lock );
3494 static HRESULT WINAPI
3495 IDirectSoundCaptureImpl_QueryInterface(
3496 LPDIRECTSOUNDCAPTURE iface,
3500 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3502 FIXME( "(%p)->(%s,%p): stub\n", This, debugstr_guid(riid), ppobj );
3508 IDirectSoundCaptureImpl_AddRef( LPDIRECTSOUNDCAPTURE iface )
3511 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3513 EnterCriticalSection( &This->lock );
3515 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3516 uRef = ++(This->ref);
3518 LeaveCriticalSection( &This->lock );
3524 IDirectSoundCaptureImpl_Release( LPDIRECTSOUNDCAPTURE iface )
3527 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3529 EnterCriticalSection( &This->lock );
3531 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3532 uRef = --(This->ref);
3534 LeaveCriticalSection( &This->lock );
3537 DeleteCriticalSection( &This->lock );
3538 HeapFree( GetProcessHeap(), 0, This );
3544 static HRESULT WINAPI
3545 IDirectSoundCaptureImpl_CreateCaptureBuffer(
3546 LPDIRECTSOUNDCAPTURE iface,
3547 LPCDSCBUFFERDESC lpcDSCBufferDesc,
3548 LPDIRECTSOUNDCAPTUREBUFFER* lplpDSCaptureBuffer,
3552 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3554 TRACE( "(%p)->(%p,%p,%p)\n", This, lpcDSCBufferDesc, lplpDSCaptureBuffer, pUnk );
3557 return DSERR_INVALIDPARAM;
3560 hr = DSOUND_CreateDirectSoundCaptureBuffer( lpcDSCBufferDesc, (LPVOID*)lplpDSCaptureBuffer );
3565 static HRESULT WINAPI
3566 IDirectSoundCaptureImpl_GetCaps(
3567 LPDIRECTSOUNDCAPTURE iface,
3568 LPDSCCAPS lpDSCCaps )
3570 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3572 FIXME( "(%p)->(%p): stub\n", This, lpDSCCaps );
3577 static HRESULT WINAPI
3578 IDirectSoundCaptureImpl_Initialize(
3579 LPDIRECTSOUNDCAPTURE iface,
3582 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3584 FIXME( "(%p)->(%p): stub\n", This, lpcGUID );
3590 static ICOM_VTABLE(IDirectSoundCapture) dscvt =
3592 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3593 /* IUnknown methods */
3594 IDirectSoundCaptureImpl_QueryInterface,
3595 IDirectSoundCaptureImpl_AddRef,
3596 IDirectSoundCaptureImpl_Release,
3598 /* IDirectSoundCapture methods */
3599 IDirectSoundCaptureImpl_CreateCaptureBuffer,
3600 IDirectSoundCaptureImpl_GetCaps,
3601 IDirectSoundCaptureImpl_Initialize
3605 DSOUND_CreateDirectSoundCaptureBuffer( LPCDSCBUFFERDESC lpcDSCBufferDesc, LPVOID* ppobj )
3608 FIXME( "(%p,%p): ignoring lpcDSCBufferDesc\n", lpcDSCBufferDesc, ppobj );
3610 *ppobj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( IDirectSoundCaptureBufferImpl ) );
3612 if ( *ppobj == NULL ) {
3613 return DSERR_OUTOFMEMORY;
3617 ICOM_THIS(IDirectSoundCaptureBufferImpl,*ppobj);
3620 ICOM_VTBL(This) = &dscbvt;
3622 InitializeCriticalSection( &This->lock );
3629 static HRESULT WINAPI
3630 IDirectSoundCaptureBufferImpl_QueryInterface(
3631 LPDIRECTSOUNDCAPTUREBUFFER iface,
3635 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3637 FIXME( "(%p)->(%s,%p): stub\n", This, debugstr_guid(riid), ppobj );
3643 IDirectSoundCaptureBufferImpl_AddRef( LPDIRECTSOUNDCAPTUREBUFFER iface )
3646 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3648 EnterCriticalSection( &This->lock );
3650 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3651 uRef = ++(This->ref);
3653 LeaveCriticalSection( &This->lock );
3659 IDirectSoundCaptureBufferImpl_Release( LPDIRECTSOUNDCAPTUREBUFFER iface )
3662 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3664 EnterCriticalSection( &This->lock );
3666 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3667 uRef = --(This->ref);
3669 LeaveCriticalSection( &This->lock );
3672 DeleteCriticalSection( &This->lock );
3673 HeapFree( GetProcessHeap(), 0, This );
3679 static HRESULT WINAPI
3680 IDirectSoundCaptureBufferImpl_GetCaps(
3681 LPDIRECTSOUNDCAPTUREBUFFER iface,
3682 LPDSCBCAPS lpDSCBCaps )
3684 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3686 FIXME( "(%p)->(%p): stub\n", This, lpDSCBCaps );
3691 static HRESULT WINAPI
3692 IDirectSoundCaptureBufferImpl_GetCurrentPosition(
3693 LPDIRECTSOUNDCAPTUREBUFFER iface,
3694 LPDWORD lpdwCapturePosition,
3695 LPDWORD lpdwReadPosition )
3697 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3699 FIXME( "(%p)->(%p,%p): stub\n", This, lpdwCapturePosition, lpdwReadPosition );
3704 static HRESULT WINAPI
3705 IDirectSoundCaptureBufferImpl_GetFormat(
3706 LPDIRECTSOUNDCAPTUREBUFFER iface,
3707 LPWAVEFORMATEX lpwfxFormat,
3708 DWORD dwSizeAllocated,
3709 LPDWORD lpdwSizeWritten )
3711 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3713 FIXME( "(%p)->(%p,0x%08lx,%p): stub\n", This, lpwfxFormat, dwSizeAllocated, lpdwSizeWritten );
3718 static HRESULT WINAPI
3719 IDirectSoundCaptureBufferImpl_GetStatus(
3720 LPDIRECTSOUNDCAPTUREBUFFER iface,
3721 LPDWORD lpdwStatus )
3723 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3725 FIXME( "(%p)->(%p): stub\n", This, lpdwStatus );
3730 static HRESULT WINAPI
3731 IDirectSoundCaptureBufferImpl_Initialize(
3732 LPDIRECTSOUNDCAPTUREBUFFER iface,
3733 LPDIRECTSOUNDCAPTURE lpDSC,
3734 LPCDSCBUFFERDESC lpcDSCBDesc )
3736 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3738 FIXME( "(%p)->(%p,%p): stub\n", This, lpDSC, lpcDSCBDesc );
3743 static HRESULT WINAPI
3744 IDirectSoundCaptureBufferImpl_Lock(
3745 LPDIRECTSOUNDCAPTUREBUFFER iface,
3748 LPVOID* lplpvAudioPtr1,
3749 LPDWORD lpdwAudioBytes1,
3750 LPVOID* lplpvAudioPtr2,
3751 LPDWORD lpdwAudioBytes2,
3754 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3756 FIXME( "(%p)->(%08lu,%08lu,%p,%p,%p,%p,0x%08lx): stub\n", This, dwReadCusor, dwReadBytes, lplpvAudioPtr1, lpdwAudioBytes1, lplpvAudioPtr2, lpdwAudioBytes2, dwFlags );
3761 static HRESULT WINAPI
3762 IDirectSoundCaptureBufferImpl_Start(
3763 LPDIRECTSOUNDCAPTUREBUFFER iface,
3766 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3768 FIXME( "(%p)->(0x%08lx): stub\n", This, dwFlags );
3773 static HRESULT WINAPI
3774 IDirectSoundCaptureBufferImpl_Stop( LPDIRECTSOUNDCAPTUREBUFFER iface )
3776 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3778 FIXME( "(%p): stub\n", This );
3783 static HRESULT WINAPI
3784 IDirectSoundCaptureBufferImpl_Unlock(
3785 LPDIRECTSOUNDCAPTUREBUFFER iface,
3786 LPVOID lpvAudioPtr1,
3787 DWORD dwAudioBytes1,
3788 LPVOID lpvAudioPtr2,
3789 DWORD dwAudioBytes2 )
3791 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3793 FIXME( "(%p)->(%p,%08lu,%p,%08lu): stub\n", This, lpvAudioPtr1, dwAudioBytes1, lpvAudioPtr2, dwAudioBytes2 );
3799 static ICOM_VTABLE(IDirectSoundCaptureBuffer) dscbvt =
3801 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3802 /* IUnknown methods */
3803 IDirectSoundCaptureBufferImpl_QueryInterface,
3804 IDirectSoundCaptureBufferImpl_AddRef,
3805 IDirectSoundCaptureBufferImpl_Release,
3807 /* IDirectSoundCaptureBuffer methods */
3808 IDirectSoundCaptureBufferImpl_GetCaps,
3809 IDirectSoundCaptureBufferImpl_GetCurrentPosition,
3810 IDirectSoundCaptureBufferImpl_GetFormat,
3811 IDirectSoundCaptureBufferImpl_GetStatus,
3812 IDirectSoundCaptureBufferImpl_Initialize,
3813 IDirectSoundCaptureBufferImpl_Lock,
3814 IDirectSoundCaptureBufferImpl_Start,
3815 IDirectSoundCaptureBufferImpl_Stop,
3816 IDirectSoundCaptureBufferImpl_Unlock
3819 /*******************************************************************************
3820 * DirectSound ClassFactory
3824 /* IUnknown fields */
3825 ICOM_VFIELD(IClassFactory);
3827 } IClassFactoryImpl;
3829 static HRESULT WINAPI
3830 DSCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
3831 ICOM_THIS(IClassFactoryImpl,iface);
3833 FIXME("(%p)->(%s,%p),stub!\n",This,debugstr_guid(riid),ppobj);
3834 return E_NOINTERFACE;
3838 DSCF_AddRef(LPCLASSFACTORY iface) {
3839 ICOM_THIS(IClassFactoryImpl,iface);
3840 return ++(This->ref);
3843 static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface) {
3844 ICOM_THIS(IClassFactoryImpl,iface);
3845 /* static class, won't be freed */
3846 return --(This->ref);
3849 static HRESULT WINAPI DSCF_CreateInstance(
3850 LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
3852 ICOM_THIS(IClassFactoryImpl,iface);
3854 TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
3855 if ( IsEqualGUID( &IID_IDirectSound, riid ) ) {
3856 /* FIXME: reuse already created dsound if present? */
3857 return DirectSoundCreate(riid,(LPDIRECTSOUND*)ppobj,pOuter);
3859 return E_NOINTERFACE;
3862 static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
3863 ICOM_THIS(IClassFactoryImpl,iface);
3864 FIXME("(%p)->(%d),stub!\n",This,dolock);
3868 static ICOM_VTABLE(IClassFactory) DSCF_Vtbl = {
3869 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3870 DSCF_QueryInterface,
3873 DSCF_CreateInstance,
3876 static IClassFactoryImpl DSOUND_CF = {&DSCF_Vtbl, 1 };
3878 /*******************************************************************************
3879 * DllGetClassObject [DSOUND.5]
3880 * Retrieves class object from a DLL object
3883 * Docs say returns STDAPI
3886 * rclsid [I] CLSID for the class object
3887 * riid [I] Reference to identifier of interface for class object
3888 * ppv [O] Address of variable to receive interface pointer for riid
3892 * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
3895 DWORD WINAPI DSOUND_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
3897 TRACE("(%p,%p,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
3898 if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
3899 *ppv = (LPVOID)&DSOUND_CF;
3900 IClassFactory_AddRef((IClassFactory*)*ppv);
3904 FIXME("(%p,%p,%p): no interface found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
3905 return CLASS_E_CLASSNOTAVAILABLE;
3909 /*******************************************************************************
3910 * DllCanUnloadNow [DSOUND.4] Determines whether the DLL is in use.
3916 DWORD WINAPI DSOUND_DllCanUnloadNow(void)
3918 FIXME("(void): stub\n");