3 * Copyright 1998 Marcus Meissner
4 * Copyright 1998 Rob Riggs
5 * Copyright 2000-2001 TransGaming Technologies, Inc.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * Most thread locking is complete. There may be a few race
23 * conditions still lurking.
25 * Tested with a Soundblaster clone, a Gravis UltraSound Classic,
26 * and a Turtle Beach Tropez+.
29 * Implement DirectSoundCapture API
30 * Implement SetCooperativeLevel properly (need to address focus issues)
31 * Implement DirectSound3DBuffers (stubs in place)
32 * Use hardware 3D support if available
33 * Add critical section locking inside Release and AddRef methods
34 * Handle static buffers - put those in hardware, non-static not in hardware
35 * Hardware DuplicateSoundBuffer
36 * Proper volume calculation, and setting volume in HEL primary buffer
37 * Optimize WINMM and negotiate fragment size, decrease DS_HEL_MARGIN
41 #include "wine/port.h"
45 #include <sys/types.h>
46 #include <sys/fcntl.h>
50 #include <math.h> /* Insomnia - pow() function */
61 #include "wine/windef16.h"
62 #include "wine/winbase16.h"
63 #include "wine/debug.h"
67 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
69 /* these are eligible for tuning... they must be high on slow machines... */
70 /* some stuff may get more responsive with lower values though... */
71 #define DS_EMULDRIVER 1 /* some games (Quake 2, UT) refuse to accept
72 emulated dsound devices. set to 0 ! */
73 #define DS_HEL_FRAGS 48 /* HEL only: number of waveOut fragments in primary buffer
74 * (changing this won't help you) */
75 #define DS_HEL_MARGIN 5 /* HEL only: number of waveOut fragments ahead to mix in new buffers
76 * (keep this close or equal to DS_HEL_QUEUE for best results) */
77 #define DS_HEL_QUEUE 5 /* HEL only: number of waveOut fragments ahead to queue to driver
78 * (this will affect HEL sound reliability and latency) */
80 #define DS_SND_QUEUE_MAX 28 /* max number of fragments to prebuffer */
81 #define DS_SND_QUEUE_MIN 12 /* min number of fragments to prebuffer */
83 /* Linux does not support better timing than 10ms */
84 #define DS_TIME_RES 10 /* Resolution of multimedia timer */
85 #define DS_TIME_DEL 10 /* Delay of multimedia timer callback, and duration of HEL fragment */
87 /*****************************************************************************
88 * Predeclare the interface implementation structures
90 typedef struct IDirectSoundImpl IDirectSoundImpl;
91 typedef struct IDirectSoundBufferImpl IDirectSoundBufferImpl;
92 typedef struct IDirectSoundNotifyImpl IDirectSoundNotifyImpl;
93 typedef struct IDirectSound3DListenerImpl IDirectSound3DListenerImpl;
94 typedef struct IDirectSound3DBufferImpl IDirectSound3DBufferImpl;
95 typedef struct IDirectSoundCaptureImpl IDirectSoundCaptureImpl;
96 typedef struct IDirectSoundCaptureBufferImpl IDirectSoundCaptureBufferImpl;
97 typedef struct IKsPropertySetImpl IKsPropertySetImpl;
100 /*****************************************************************************
101 * IDirectSound implementation structure
103 struct IDirectSoundImpl
105 /* IUnknown fields */
106 ICOM_VFIELD(IDirectSound);
108 /* IDirectSoundImpl fields */
110 DSDRIVERDESC drvdesc;
111 DSDRIVERCAPS drvcaps;
113 LPWAVEHDR pwave[DS_HEL_FRAGS];
114 UINT timerID, pwplay, pwwrite, pwqueue, prebuf;
118 IDirectSoundBufferImpl** buffers;
119 IDirectSoundBufferImpl* primary;
120 IDirectSound3DListenerImpl* listener;
121 WAVEFORMATEX wfx; /* current main waveformat */
122 CRITICAL_SECTION lock;
125 /*****************************************************************************
126 * IDirectSoundBuffer implementation structure
128 struct IDirectSoundBufferImpl
130 /* FIXME: document */
131 /* IUnknown fields */
132 ICOM_VFIELD(IDirectSoundBuffer);
134 /* IDirectSoundBufferImpl fields */
135 PIDSDRIVERBUFFER hwbuf;
138 IDirectSound3DBufferImpl* ds3db;
139 DWORD playflags,state,leadin;
140 DWORD playpos,startpos,writelead,buflen;
141 DWORD nAvgBytesPerSec;
144 IDirectSoundBufferImpl* parent; /* for duplicates */
145 IDirectSoundImpl* dsound;
147 LPDSBPOSITIONNOTIFY notifies;
149 CRITICAL_SECTION lock;
150 /* used for frequency conversion (PerfectPitch) */
151 ULONG freqAdjust, freqAcc;
152 /* used for intelligent (well, sort of) prebuffering */
153 DWORD probably_valid_to;
154 DWORD primary_mixpos, buf_mixpos;
158 #define STATE_STOPPED 0
159 #define STATE_STARTING 1
160 #define STATE_PLAYING 2
161 #define STATE_STOPPING 3
163 /*****************************************************************************
164 * IDirectSoundNotify implementation structure
166 struct IDirectSoundNotifyImpl
168 /* IUnknown fields */
169 ICOM_VFIELD(IDirectSoundNotify);
171 /* IDirectSoundNotifyImpl fields */
172 IDirectSoundBufferImpl* dsb;
175 /*****************************************************************************
176 * IDirectSound3DListener implementation structure
178 struct IDirectSound3DListenerImpl
180 /* IUnknown fields */
181 ICOM_VFIELD(IDirectSound3DListener);
183 /* IDirectSound3DListenerImpl fields */
184 IDirectSoundBufferImpl* dsb;
186 CRITICAL_SECTION lock;
189 struct IKsPropertySetImpl
191 /* IUnknown fields */
192 ICOM_VFIELD(IKsPropertySet);
194 /* IKsPropertySetImpl fields */
195 IDirectSound3DBufferImpl *ds3db; /* backptr, no ref */
198 /*****************************************************************************
199 * IDirectSound3DBuffer implementation structure
201 struct IDirectSound3DBufferImpl
203 /* IUnknown fields */
204 ICOM_VFIELD(IDirectSound3DBuffer);
206 /* IDirectSound3DBufferImpl fields */
207 IDirectSoundBufferImpl* dsb;
211 CRITICAL_SECTION lock;
212 IKsPropertySetImpl* iks;
216 /*****************************************************************************
217 * IDirectSoundCapture implementation structure
219 struct IDirectSoundCaptureImpl
221 /* IUnknown fields */
222 ICOM_VFIELD(IDirectSoundCapture);
225 /* IDirectSoundCaptureImpl fields */
226 CRITICAL_SECTION lock;
229 /*****************************************************************************
230 * IDirectSoundCapture implementation structure
232 struct IDirectSoundCaptureBufferImpl
234 /* IUnknown fields */
235 ICOM_VFIELD(IDirectSoundCaptureBuffer);
238 /* IDirectSoundCaptureBufferImpl fields */
239 CRITICAL_SECTION lock;
243 /* #define USE_DSOUND3D 1 */
245 #define DSOUND_FREQSHIFT (14)
247 static IDirectSoundImpl* dsound = NULL;
249 static IDirectSoundBufferImpl* primarybuf = NULL;
251 static void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len);
252 static void DSOUND_MixCancelAt(IDirectSoundBufferImpl *dsb, DWORD buf_writepos);
254 static void DSOUND_WaveQueue(IDirectSoundImpl *dsound, DWORD mixq);
255 static void DSOUND_PerformMix(void);
256 static void CALLBACK DSOUND_callback(HWAVEOUT hwo, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2);
258 static HRESULT DSOUND_CreateDirectSoundCapture( LPVOID* ppobj );
259 static HRESULT DSOUND_CreateDirectSoundCaptureBuffer( LPCDSCBUFFERDESC lpcDSCBufferDesc, LPVOID* ppobj );
261 static ICOM_VTABLE(IDirectSoundCapture) dscvt;
262 static ICOM_VTABLE(IDirectSoundCaptureBuffer) dscbvt;
264 static HRESULT mmErr(UINT err)
267 case MMSYSERR_NOERROR:
269 case MMSYSERR_ALLOCATED:
270 return DSERR_ALLOCATED;
271 case MMSYSERR_INVALHANDLE:
272 return DSERR_GENERIC; /* FIXME */
273 case MMSYSERR_NODRIVER:
274 return DSERR_NODRIVER;
276 return DSERR_OUTOFMEMORY;
277 case MMSYSERR_INVALPARAM:
278 return DSERR_INVALIDPARAM;
280 FIXME("Unknown MMSYS error %d\n",err);
281 return DSERR_GENERIC;
285 static int ds_emuldriver = DS_EMULDRIVER;
286 static int ds_hel_margin = DS_HEL_MARGIN;
287 static int ds_hel_queue = DS_HEL_QUEUE;
288 static int ds_snd_queue_max = DS_SND_QUEUE_MAX;
289 static int ds_snd_queue_min = DS_SND_QUEUE_MIN;
292 * Get a config key from either the app-specific or the default config
295 inline static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
296 char *buffer, DWORD size )
298 if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, buffer, &size )) return 0;
299 return RegQueryValueExA( defkey, name, 0, NULL, buffer, &size );
304 * Setup the dsound options.
307 inline static void setup_dsound_options(void)
309 char buffer[MAX_PATH+1];
310 HKEY hkey, appkey = 0;
312 buffer[MAX_PATH]='\0';
314 if (RegCreateKeyExA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\dsound", 0, NULL,
315 REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL ))
317 ERR("Cannot create config registry key\n" );
321 if (GetModuleFileName16( GetCurrentTask(), buffer, MAX_PATH ) ||
322 GetModuleFileNameA( 0, buffer, MAX_PATH ))
326 if (!RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\AppDefaults", &tmpkey ))
328 char appname[MAX_PATH+16];
329 char *p = strrchr( buffer, '\\' );
331 appname[MAX_PATH]='\0';
332 strncpy(appname,p+1,MAX_PATH);
333 strcat(appname,"\\dsound");
334 TRACE("appname = [%s] \n",appname);
335 if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
336 RegCloseKey( tmpkey );
343 if (!get_config_key( hkey, appkey, "EmulDriver", buffer, MAX_PATH ))
344 ds_emuldriver = atoi(buffer);
346 if (!get_config_key( hkey, appkey, "HELmargin", buffer, MAX_PATH ))
347 ds_hel_margin = atoi(buffer);
349 if (!get_config_key( hkey, appkey, "HELqueue", buffer, MAX_PATH ))
350 ds_hel_queue = atoi(buffer);
352 if (!get_config_key( hkey, appkey, "SndQueueMax", buffer, MAX_PATH ))
353 ds_snd_queue_max = atoi(buffer);
355 if (!get_config_key( hkey, appkey, "SndQueueMin", buffer, MAX_PATH ))
356 ds_snd_queue_min = atoi(buffer);
358 if (appkey) RegCloseKey( appkey );
361 if (ds_emuldriver != DS_EMULDRIVER )
362 WARN("ds_emuldriver = %d (default=%d)\n",ds_emuldriver, DS_EMULDRIVER);
363 if (ds_hel_margin != DS_HEL_MARGIN )
364 WARN("ds_hel_margin = %d (default=%d)\n",ds_hel_margin, DS_HEL_MARGIN );
365 if (ds_hel_queue != DS_HEL_QUEUE )
366 WARN("ds_hel_queue = %d (default=%d)\n",ds_hel_queue, DS_HEL_QUEUE );
367 if (ds_snd_queue_max != DS_SND_QUEUE_MAX)
368 WARN("ds_snd_queue_max = %d (default=%d)\n",ds_snd_queue_max ,DS_SND_QUEUE_MAX);
369 if (ds_snd_queue_min != DS_SND_QUEUE_MIN)
370 WARN("ds_snd_queue_min = %d (default=%d)\n",ds_snd_queue_min ,DS_SND_QUEUE_MIN);
377 /***************************************************************************
378 * DirectSoundEnumerateA [DSOUND.2]
380 * Enumerate all DirectSound drivers installed in the system
384 * Failure: DSERR_INVALIDPARAM
386 HRESULT WINAPI DirectSoundEnumerateA(
387 LPDSENUMCALLBACKA lpDSEnumCallback,
390 TRACE("lpDSEnumCallback = %p, lpContext = %p\n",
391 lpDSEnumCallback, lpContext);
394 if (lpDSEnumCallback != NULL)
395 if (lpDSEnumCallback(NULL, "Primary DirectSound Driver",
397 lpDSEnumCallback((LPGUID)&DSDEVID_WinePlayback,
398 "WINE DirectSound", "sound",
405 /***************************************************************************
406 * DirectSoundEnumerateW [DSOUND.3]
408 * Enumerate all DirectSound drivers installed in the system
412 * Failure: DSERR_INVALIDPARAM
414 HRESULT WINAPI DirectSoundEnumerateW(
415 LPDSENUMCALLBACKW lpDSEnumCallback,
418 FIXME("lpDSEnumCallback = %p, lpContext = %p: stub\n",
419 lpDSEnumCallback, lpContext);
425 static void _dump_DSBCAPS(DWORD xmask) {
430 #define FE(x) { x, #x },
431 FE(DSBCAPS_PRIMARYBUFFER)
433 FE(DSBCAPS_LOCHARDWARE)
434 FE(DSBCAPS_LOCSOFTWARE)
436 FE(DSBCAPS_CTRLFREQUENCY)
438 FE(DSBCAPS_CTRLVOLUME)
439 FE(DSBCAPS_CTRLPOSITIONNOTIFY)
440 FE(DSBCAPS_CTRLDEFAULT)
442 FE(DSBCAPS_STICKYFOCUS)
443 FE(DSBCAPS_GLOBALFOCUS)
444 FE(DSBCAPS_GETCURRENTPOSITION2)
445 FE(DSBCAPS_MUTE3DATMAXDISTANCE)
450 for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++)
451 if ((flags[i].mask & xmask) == flags[i].mask)
452 DPRINTF("%s ",flags[i].name);
455 /*******************************************************************************
459 /* IUnknown methods */
461 static HRESULT WINAPI IKsPropertySetImpl_QueryInterface(
462 LPKSPROPERTYSET iface, REFIID riid, LPVOID *ppobj
464 ICOM_THIS(IKsPropertySetImpl,iface);
466 FIXME("(%p,%s,%p), stub!\n",This,debugstr_guid(riid),ppobj);
472 static ULONG WINAPI IKsPropertySetImpl_AddRef(LPKSPROPERTYSET iface) {
473 ICOM_THIS(IKsPropertySetImpl,iface);
481 static ULONG WINAPI IKsPropertySetImpl_Release(LPKSPROPERTYSET iface) {
482 ICOM_THIS(IKsPropertySetImpl,iface);
490 static HRESULT WINAPI IKsPropertySetImpl_Get(LPKSPROPERTYSET iface,
491 REFGUID guidPropSet, ULONG dwPropID,
492 LPVOID pInstanceData, ULONG cbInstanceData,
493 LPVOID pPropData, ULONG cbPropData,
496 ICOM_THIS(IKsPropertySetImpl,iface);
498 FIXME("(%p,%s,%ld,%p,%ld,%p,%ld,%p), stub!\n",This,debugstr_guid(guidPropSet),dwPropID,pInstanceData,cbInstanceData,pPropData,cbPropData,pcbReturned);
499 return E_PROP_ID_UNSUPPORTED;
504 static HRESULT WINAPI IKsPropertySetImpl_Set(LPKSPROPERTYSET iface,
505 REFGUID guidPropSet, ULONG dwPropID,
506 LPVOID pInstanceData, ULONG cbInstanceData,
507 LPVOID pPropData, ULONG cbPropData
509 ICOM_THIS(IKsPropertySetImpl,iface);
511 FIXME("(%p,%s,%ld,%p,%ld,%p,%ld), stub!\n",This,debugstr_guid(guidPropSet),dwPropID,pInstanceData,cbInstanceData,pPropData,cbPropData);
512 return E_PROP_ID_UNSUPPORTED;
517 static HRESULT WINAPI IKsPropertySetImpl_QuerySupport(LPKSPROPERTYSET iface,
518 REFGUID guidPropSet, ULONG dwPropID, PULONG pTypeSupport
520 ICOM_THIS(IKsPropertySetImpl,iface);
522 FIXME("(%p,%s,%ld,%p), stub!\n",This,debugstr_guid(guidPropSet),dwPropID,pTypeSupport);
523 return E_PROP_ID_UNSUPPORTED;
528 static ICOM_VTABLE(IKsPropertySet) iksvt = {
529 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
530 IKsPropertySetImpl_QueryInterface,
531 IKsPropertySetImpl_AddRef,
532 IKsPropertySetImpl_Release,
533 IKsPropertySetImpl_Get,
534 IKsPropertySetImpl_Set,
535 IKsPropertySetImpl_QuerySupport
539 /*******************************************************************************
540 * IDirectSound3DBuffer
543 /* IUnknown methods */
545 static HRESULT WINAPI IDirectSound3DBufferImpl_QueryInterface(
546 LPDIRECTSOUND3DBUFFER iface, REFIID riid, LPVOID *ppobj)
548 ICOM_THIS(IDirectSound3DBufferImpl,iface);
550 if ( IsEqualGUID( &IID_IKsPropertySet, riid ) ) {
551 IDirectSound3DBuffer_AddRef(iface);
556 FIXME("(%p,%s,%p), no such interface.\n",This,debugstr_guid(riid),ppobj);
562 static ULONG WINAPI IDirectSound3DBufferImpl_AddRef(LPDIRECTSOUND3DBUFFER iface)
564 ICOM_THIS(IDirectSound3DBufferImpl,iface);
571 static ULONG WINAPI IDirectSound3DBufferImpl_Release(LPDIRECTSOUND3DBUFFER iface)
573 ICOM_THIS(IDirectSound3DBufferImpl,iface);
575 TRACE("(%p) ref was %ld\n", This, This->ref);
581 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
583 DeleteCriticalSection(&This->lock);
585 HeapFree(GetProcessHeap(),0,This->buffer);
586 HeapFree(GetProcessHeap(),0,This);
592 /* IDirectSound3DBuffer methods */
594 static HRESULT WINAPI IDirectSound3DBufferImpl_GetAllParameters(
595 LPDIRECTSOUND3DBUFFER iface,
596 LPDS3DBUFFER lpDs3dBuffer)
604 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeAngles(
605 LPDIRECTSOUND3DBUFFER iface,
606 LPDWORD lpdwInsideConeAngle,
607 LPDWORD lpdwOutsideConeAngle)
615 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeOrientation(
616 LPDIRECTSOUND3DBUFFER iface,
617 LPD3DVECTOR lpvConeOrientation)
625 static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeOutsideVolume(
626 LPDIRECTSOUND3DBUFFER iface,
627 LPLONG lplConeOutsideVolume)
635 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMaxDistance(
636 LPDIRECTSOUND3DBUFFER iface,
637 LPD3DVALUE lpfMaxDistance)
645 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMinDistance(
646 LPDIRECTSOUND3DBUFFER iface,
647 LPD3DVALUE lpfMinDistance)
655 static HRESULT WINAPI IDirectSound3DBufferImpl_GetMode(
656 LPDIRECTSOUND3DBUFFER iface,
665 static HRESULT WINAPI IDirectSound3DBufferImpl_GetPosition(
666 LPDIRECTSOUND3DBUFFER iface,
667 LPD3DVECTOR lpvPosition)
675 static HRESULT WINAPI IDirectSound3DBufferImpl_GetVelocity(
676 LPDIRECTSOUND3DBUFFER iface,
677 LPD3DVECTOR lpvVelocity)
685 static HRESULT WINAPI IDirectSound3DBufferImpl_SetAllParameters(
686 LPDIRECTSOUND3DBUFFER iface,
687 LPCDS3DBUFFER lpcDs3dBuffer,
696 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeAngles(
697 LPDIRECTSOUND3DBUFFER iface,
698 DWORD dwInsideConeAngle,
699 DWORD dwOutsideConeAngle,
708 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeOrientation(
709 LPDIRECTSOUND3DBUFFER iface,
710 D3DVALUE x, D3DVALUE y, D3DVALUE z,
719 static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeOutsideVolume(
720 LPDIRECTSOUND3DBUFFER iface,
721 LONG lConeOutsideVolume,
730 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMaxDistance(
731 LPDIRECTSOUND3DBUFFER iface,
732 D3DVALUE fMaxDistance,
741 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMinDistance(
742 LPDIRECTSOUND3DBUFFER iface,
743 D3DVALUE fMinDistance,
752 static HRESULT WINAPI IDirectSound3DBufferImpl_SetMode(
753 LPDIRECTSOUND3DBUFFER iface,
757 ICOM_THIS(IDirectSound3DBufferImpl,iface);
758 TRACE("mode = %lx\n", dwMode);
759 This->ds3db.dwMode = dwMode;
765 static HRESULT WINAPI IDirectSound3DBufferImpl_SetPosition(
766 LPDIRECTSOUND3DBUFFER iface,
767 D3DVALUE x, D3DVALUE y, D3DVALUE z,
776 static HRESULT WINAPI IDirectSound3DBufferImpl_SetVelocity(
777 LPDIRECTSOUND3DBUFFER iface,
778 D3DVALUE x, D3DVALUE y, D3DVALUE z,
787 static ICOM_VTABLE(IDirectSound3DBuffer) ds3dbvt =
789 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
790 /* IUnknown methods */
791 IDirectSound3DBufferImpl_QueryInterface,
792 IDirectSound3DBufferImpl_AddRef,
793 IDirectSound3DBufferImpl_Release,
794 /* IDirectSound3DBuffer methods */
795 IDirectSound3DBufferImpl_GetAllParameters,
796 IDirectSound3DBufferImpl_GetConeAngles,
797 IDirectSound3DBufferImpl_GetConeOrientation,
798 IDirectSound3DBufferImpl_GetConeOutsideVolume,
799 IDirectSound3DBufferImpl_GetMaxDistance,
800 IDirectSound3DBufferImpl_GetMinDistance,
801 IDirectSound3DBufferImpl_GetMode,
802 IDirectSound3DBufferImpl_GetPosition,
803 IDirectSound3DBufferImpl_GetVelocity,
804 IDirectSound3DBufferImpl_SetAllParameters,
805 IDirectSound3DBufferImpl_SetConeAngles,
806 IDirectSound3DBufferImpl_SetConeOrientation,
807 IDirectSound3DBufferImpl_SetConeOutsideVolume,
808 IDirectSound3DBufferImpl_SetMaxDistance,
809 IDirectSound3DBufferImpl_SetMinDistance,
810 IDirectSound3DBufferImpl_SetMode,
811 IDirectSound3DBufferImpl_SetPosition,
812 IDirectSound3DBufferImpl_SetVelocity,
817 static int DSOUND_Create3DBuffer(IDirectSoundBufferImpl* dsb)
819 DWORD i, temp, iSize, oSize, offset;
820 LPBYTE bIbuf, bObuf, bTbuf = NULL;
821 LPWORD wIbuf, wObuf, wTbuf = NULL;
823 /* Inside DirectX says it's stupid but allowed */
824 if (dsb->wfx.nChannels == 2) {
825 /* Convert to mono */
826 if (dsb->wfx.wBitsPerSample == 16) {
827 iSize = dsb->buflen / 4;
828 wTbuf = malloc(dsb->buflen / 2);
830 return DSERR_OUTOFMEMORY;
831 for (i = 0; i < iSize; i++)
832 wTbuf[i] = (dsb->buffer[i * 2] + dsb->buffer[(i * 2) + 1]) / 2;
835 iSize = dsb->buflen / 2;
836 bTbuf = malloc(dsb->buflen / 2);
838 return DSERR_OUTOFMEMORY;
839 for (i = 0; i < iSize; i++)
840 bTbuf[i] = (dsb->buffer[i * 2] + dsb->buffer[(i * 2) + 1]) / 2;
844 if (dsb->wfx.wBitsPerSample == 16) {
845 iSize = dsb->buflen / 2;
846 wIbuf = (LPWORD) dsb->buffer;
848 bIbuf = (LPBYTE) dsb->buffer;
853 if (primarybuf->wfx.wBitsPerSample == 16) {
854 wObuf = (LPWORD) dsb->ds3db->buffer;
855 oSize = dsb->ds3db->buflen / 2;
857 bObuf = (LPBYTE) dsb->ds3db->buffer;
858 oSize = dsb->ds3db->buflen;
861 offset = primarybuf->wfx.nSamplesPerSec / 100; /* 10ms */
862 if (primarybuf->wfx.wBitsPerSample == 16 && dsb->wfx.wBitsPerSample == 16)
863 for (i = 0; i < iSize; i++) {
866 temp += wIbuf[i - offset] >> 9;
868 temp += wIbuf[i + iSize - offset] >> 9;
870 wObuf[(i * 2) + 1] = temp;
872 else if (primarybuf->wfx.wBitsPerSample == 8 && dsb->wfx.wBitsPerSample == 8)
873 for (i = 0; i < iSize; i++) {
876 temp += bIbuf[i - offset] >> 5;
878 temp += bIbuf[i + iSize - offset] >> 5;
880 bObuf[(i * 2) + 1] = temp;
891 /*******************************************************************************
892 * IDirectSound3DListener
895 /* IUnknown methods */
896 static HRESULT WINAPI IDirectSound3DListenerImpl_QueryInterface(
897 LPDIRECTSOUND3DLISTENER iface, REFIID riid, LPVOID *ppobj)
899 ICOM_THIS(IDirectSound3DListenerImpl,iface);
901 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
905 static ULONG WINAPI IDirectSound3DListenerImpl_AddRef(LPDIRECTSOUND3DLISTENER iface)
907 ICOM_THIS(IDirectSound3DListenerImpl,iface);
912 static ULONG WINAPI IDirectSound3DListenerImpl_Release(LPDIRECTSOUND3DLISTENER iface)
915 ICOM_THIS(IDirectSound3DListenerImpl,iface);
917 TRACE("(%p) ref was %ld\n", This, This->ref);
919 ulReturn = --This->ref;
921 /* Free all resources */
922 if( ulReturn == 0 ) {
924 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
925 DeleteCriticalSection(&This->lock);
926 HeapFree(GetProcessHeap(),0,This);
932 /* IDirectSound3DListener methods */
933 static HRESULT WINAPI IDirectSound3DListenerImpl_GetAllParameter(
934 LPDIRECTSOUND3DLISTENER iface,
935 LPDS3DLISTENER lpDS3DL)
941 static HRESULT WINAPI IDirectSound3DListenerImpl_GetDistanceFactor(
942 LPDIRECTSOUND3DLISTENER iface,
943 LPD3DVALUE lpfDistanceFactor)
949 static HRESULT WINAPI IDirectSound3DListenerImpl_GetDopplerFactor(
950 LPDIRECTSOUND3DLISTENER iface,
951 LPD3DVALUE lpfDopplerFactor)
957 static HRESULT WINAPI IDirectSound3DListenerImpl_GetOrientation(
958 LPDIRECTSOUND3DLISTENER iface,
959 LPD3DVECTOR lpvOrientFront,
960 LPD3DVECTOR lpvOrientTop)
966 static HRESULT WINAPI IDirectSound3DListenerImpl_GetPosition(
967 LPDIRECTSOUND3DLISTENER iface,
968 LPD3DVECTOR lpvPosition)
974 static HRESULT WINAPI IDirectSound3DListenerImpl_GetRolloffFactor(
975 LPDIRECTSOUND3DLISTENER iface,
976 LPD3DVALUE lpfRolloffFactor)
982 static HRESULT WINAPI IDirectSound3DListenerImpl_GetVelocity(
983 LPDIRECTSOUND3DLISTENER iface,
984 LPD3DVECTOR lpvVelocity)
990 static HRESULT WINAPI IDirectSound3DListenerImpl_SetAllParameters(
991 LPDIRECTSOUND3DLISTENER iface,
992 LPCDS3DLISTENER lpcDS3DL,
999 static HRESULT WINAPI IDirectSound3DListenerImpl_SetDistanceFactor(
1000 LPDIRECTSOUND3DLISTENER iface,
1001 D3DVALUE fDistanceFactor,
1008 static HRESULT WINAPI IDirectSound3DListenerImpl_SetDopplerFactor(
1009 LPDIRECTSOUND3DLISTENER iface,
1010 D3DVALUE fDopplerFactor,
1017 static HRESULT WINAPI IDirectSound3DListenerImpl_SetOrientation(
1018 LPDIRECTSOUND3DLISTENER iface,
1019 D3DVALUE xFront, D3DVALUE yFront, D3DVALUE zFront,
1020 D3DVALUE xTop, D3DVALUE yTop, D3DVALUE zTop,
1027 static HRESULT WINAPI IDirectSound3DListenerImpl_SetPosition(
1028 LPDIRECTSOUND3DLISTENER iface,
1029 D3DVALUE x, D3DVALUE y, D3DVALUE z,
1036 static HRESULT WINAPI IDirectSound3DListenerImpl_SetRolloffFactor(
1037 LPDIRECTSOUND3DLISTENER iface,
1038 D3DVALUE fRolloffFactor,
1045 static HRESULT WINAPI IDirectSound3DListenerImpl_SetVelocity(
1046 LPDIRECTSOUND3DLISTENER iface,
1047 D3DVALUE x, D3DVALUE y, D3DVALUE z,
1054 static HRESULT WINAPI IDirectSound3DListenerImpl_CommitDeferredSettings(
1055 LPDIRECTSOUND3DLISTENER iface)
1062 static ICOM_VTABLE(IDirectSound3DListener) ds3dlvt =
1064 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1065 /* IUnknown methods */
1066 IDirectSound3DListenerImpl_QueryInterface,
1067 IDirectSound3DListenerImpl_AddRef,
1068 IDirectSound3DListenerImpl_Release,
1069 /* IDirectSound3DListener methods */
1070 IDirectSound3DListenerImpl_GetAllParameter,
1071 IDirectSound3DListenerImpl_GetDistanceFactor,
1072 IDirectSound3DListenerImpl_GetDopplerFactor,
1073 IDirectSound3DListenerImpl_GetOrientation,
1074 IDirectSound3DListenerImpl_GetPosition,
1075 IDirectSound3DListenerImpl_GetRolloffFactor,
1076 IDirectSound3DListenerImpl_GetVelocity,
1077 IDirectSound3DListenerImpl_SetAllParameters,
1078 IDirectSound3DListenerImpl_SetDistanceFactor,
1079 IDirectSound3DListenerImpl_SetDopplerFactor,
1080 IDirectSound3DListenerImpl_SetOrientation,
1081 IDirectSound3DListenerImpl_SetPosition,
1082 IDirectSound3DListenerImpl_SetRolloffFactor,
1083 IDirectSound3DListenerImpl_SetVelocity,
1084 IDirectSound3DListenerImpl_CommitDeferredSettings,
1087 /*******************************************************************************
1088 * IDirectSoundNotify
1090 static HRESULT WINAPI IDirectSoundNotifyImpl_QueryInterface(
1091 LPDIRECTSOUNDNOTIFY iface,REFIID riid,LPVOID *ppobj
1093 ICOM_THIS(IDirectSoundNotifyImpl,iface);
1095 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
1099 static ULONG WINAPI IDirectSoundNotifyImpl_AddRef(LPDIRECTSOUNDNOTIFY iface) {
1100 ICOM_THIS(IDirectSoundNotifyImpl,iface);
1101 return ++(This->ref);
1104 static ULONG WINAPI IDirectSoundNotifyImpl_Release(LPDIRECTSOUNDNOTIFY iface) {
1105 ICOM_THIS(IDirectSoundNotifyImpl,iface);
1107 TRACE("(%p) ref was %ld\n", This, This->ref);
1111 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->dsb);
1112 HeapFree(GetProcessHeap(),0,This);
1118 static HRESULT WINAPI IDirectSoundNotifyImpl_SetNotificationPositions(
1119 LPDIRECTSOUNDNOTIFY iface,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify
1121 ICOM_THIS(IDirectSoundNotifyImpl,iface);
1124 if (TRACE_ON(dsound)) {
1125 TRACE("(%p,0x%08lx,%p)\n",This,howmuch,notify);
1126 for (i=0;i<howmuch;i++)
1127 TRACE("notify at %ld to 0x%08lx\n",
1128 notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
1130 This->dsb->notifies = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,This->dsb->notifies,(This->dsb->nrofnotifies+howmuch)*sizeof(DSBPOSITIONNOTIFY));
1131 memcpy( This->dsb->notifies+This->dsb->nrofnotifies,
1133 howmuch*sizeof(DSBPOSITIONNOTIFY)
1135 This->dsb->nrofnotifies+=howmuch;
1140 static ICOM_VTABLE(IDirectSoundNotify) dsnvt =
1142 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
1143 IDirectSoundNotifyImpl_QueryInterface,
1144 IDirectSoundNotifyImpl_AddRef,
1145 IDirectSoundNotifyImpl_Release,
1146 IDirectSoundNotifyImpl_SetNotificationPositions,
1149 /*******************************************************************************
1150 * IDirectSoundBuffer
1153 static void DSOUND_RecalcVolPan(PDSVOLUMEPAN volpan)
1157 /* the AmpFactors are expressed in 16.16 fixed point */
1158 volpan->dwVolAmpFactor = (ULONG) (pow(2.0, volpan->lVolume / 600.0) * 65536);
1159 /* FIXME: dwPan{Left|Right}AmpFactor */
1161 /* FIXME: use calculated vol and pan ampfactors */
1162 temp = (double) (volpan->lVolume - (volpan->lPan > 0 ? volpan->lPan : 0));
1163 volpan->dwTotalLeftAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 65536);
1164 temp = (double) (volpan->lVolume + (volpan->lPan < 0 ? volpan->lPan : 0));
1165 volpan->dwTotalRightAmpFactor = (ULONG) (pow(2.0, temp / 600.0) * 65536);
1167 TRACE("left = %lx, right = %lx\n", volpan->dwTotalLeftAmpFactor, volpan->dwTotalRightAmpFactor);
1170 static void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
1174 sw = dsb->wfx.nChannels * (dsb->wfx.wBitsPerSample / 8);
1175 if ((dsb->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && dsb->hwbuf) {
1177 /* let fragment size approximate the timer delay */
1178 fraglen = (dsb->freq * DS_TIME_DEL / 1000) * sw;
1179 /* reduce fragment size until an integer number of them fits in the buffer */
1180 /* (FIXME: this may or may not be a good idea) */
1181 while (dsb->buflen % fraglen) fraglen -= sw;
1182 dsb->dsound->fraglen = fraglen;
1183 TRACE("fraglen=%ld\n", dsb->dsound->fraglen);
1185 /* calculate the 10ms write lead */
1186 dsb->writelead = (dsb->freq / 100) * sw;
1189 static HRESULT DSOUND_PrimaryOpen(IDirectSoundBufferImpl *dsb)
1191 HRESULT err = DS_OK;
1193 /* are we using waveOut stuff? */
1197 HRESULT merr = DS_OK;
1198 /* Start in pause mode, to allow buffers to get filled */
1199 waveOutPause(dsb->dsound->hwo);
1200 if (dsb->state == STATE_PLAYING) dsb->state = STATE_STARTING;
1201 else if (dsb->state == STATE_STOPPING) dsb->state = STATE_STOPPED;
1202 /* use fragments of 10ms (1/100s) each (which should get us within
1203 * the documented write cursor lead of 10-15ms) */
1204 buflen = ((dsb->wfx.nAvgBytesPerSec / 100) & ~3) * DS_HEL_FRAGS;
1205 TRACE("desired buflen=%ld, old buffer=%p\n", buflen, dsb->buffer);
1206 /* reallocate emulated primary buffer */
1207 newbuf = (LPBYTE)HeapReAlloc(GetProcessHeap(),0,dsb->buffer,buflen);
1208 if (newbuf == NULL) {
1209 ERR("failed to allocate primary buffer\n");
1210 merr = DSERR_OUTOFMEMORY;
1211 /* but the old buffer might still exists and must be re-prepared */
1213 dsb->buffer = newbuf;
1214 dsb->buflen = buflen;
1218 IDirectSoundImpl *ds = dsb->dsound;
1220 ds->fraglen = dsb->buflen / DS_HEL_FRAGS;
1222 /* prepare fragment headers */
1223 for (c=0; c<DS_HEL_FRAGS; c++) {
1224 ds->pwave[c]->lpData = dsb->buffer + c*ds->fraglen;
1225 ds->pwave[c]->dwBufferLength = ds->fraglen;
1226 ds->pwave[c]->dwUser = (DWORD)dsb;
1227 ds->pwave[c]->dwFlags = 0;
1228 ds->pwave[c]->dwLoops = 0;
1229 err = mmErr(waveOutPrepareHeader(ds->hwo,ds->pwave[c],sizeof(WAVEHDR)));
1232 waveOutUnprepareHeader(ds->hwo,ds->pwave[c],sizeof(WAVEHDR));
1240 memset(dsb->buffer, (dsb->wfx.wBitsPerSample == 16) ? 0 : 128, dsb->buflen);
1241 TRACE("fraglen=%ld\n", ds->fraglen);
1242 DSOUND_WaveQueue(dsb->dsound, (DWORD)-1);
1244 if ((err == DS_OK) && (merr != DS_OK))
1251 static void DSOUND_PrimaryClose(IDirectSoundBufferImpl *dsb)
1253 /* are we using waveOut stuff? */
1256 IDirectSoundImpl *ds = dsb->dsound;
1258 ds->pwqueue = (DWORD)-1; /* resetting queues */
1259 waveOutReset(ds->hwo);
1260 for (c=0; c<DS_HEL_FRAGS; c++)
1261 waveOutUnprepareHeader(ds->hwo, ds->pwave[c], sizeof(WAVEHDR));
1266 static HRESULT DSOUND_PrimaryPlay(IDirectSoundBufferImpl *dsb)
1268 HRESULT err = DS_OK;
1270 err = IDsDriverBuffer_Play(dsb->hwbuf, 0, 0, DSBPLAY_LOOPING);
1272 err = mmErr(waveOutRestart(dsb->dsound->hwo));
1276 static HRESULT DSOUND_PrimaryStop(IDirectSoundBufferImpl *dsb)
1278 HRESULT err = DS_OK;
1283 err = IDsDriverBuffer_Stop(dsb->hwbuf);
1284 if (err == DSERR_BUFFERLOST) {
1285 /* Wine-only: the driver wants us to reopen the device */
1286 /* FIXME: check for errors */
1287 IDsDriverBuffer_Release(primarybuf->hwbuf);
1288 waveOutClose(dsb->dsound->hwo);
1289 dsb->dsound->hwo = 0;
1290 err = mmErr(waveOutOpen(&(dsb->dsound->hwo), dsb->dsound->drvdesc.dnDevNode,
1291 &(primarybuf->wfx), (DWORD)DSOUND_callback, (DWORD)dsb->dsound,
1292 CALLBACK_FUNCTION | WAVE_DIRECTSOUND));
1294 err = IDsDriver_CreateSoundBuffer(dsb->dsound->driver,&(dsb->wfx),dsb->dsbd.dwFlags,0,
1295 &(dsb->buflen),&(dsb->buffer),
1296 (LPVOID)&(dsb->hwbuf));
1300 err = mmErr(waveOutPause(dsb->dsound->hwo));
1304 /* This sets this format for the <em>Primary Buffer Only</em> */
1305 /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */
1306 static HRESULT WINAPI IDirectSoundBufferImpl_SetFormat(
1307 LPDIRECTSOUNDBUFFER iface,LPWAVEFORMATEX wfex
1309 ICOM_THIS(IDirectSoundBufferImpl,iface);
1310 IDirectSoundBufferImpl** dsb;
1311 HRESULT err = DS_OK;
1314 /* Let's be pedantic! */
1315 if ((wfex == NULL) ||
1316 (wfex->wFormatTag != WAVE_FORMAT_PCM) ||
1317 (wfex->nChannels < 1) || (wfex->nChannels > 2) ||
1318 (wfex->nSamplesPerSec < 1) ||
1319 (wfex->nBlockAlign < 1) || (wfex->nChannels > 4) ||
1320 ((wfex->wBitsPerSample != 8) && (wfex->wBitsPerSample != 16))) {
1321 TRACE("failed pedantic check!\n");
1322 return DSERR_INVALIDPARAM;
1326 EnterCriticalSection(&(This->dsound->lock));
1328 if (primarybuf->wfx.nSamplesPerSec != wfex->nSamplesPerSec) {
1329 dsb = dsound->buffers;
1330 for (i = 0; i < dsound->nrofbuffers; i++, dsb++) {
1332 EnterCriticalSection(&((*dsb)->lock));
1334 (*dsb)->freqAdjust = ((*dsb)->freq << DSOUND_FREQSHIFT) /
1335 wfex->nSamplesPerSec;
1337 LeaveCriticalSection(&((*dsb)->lock));
1342 memcpy(&(primarybuf->wfx), wfex, sizeof(primarybuf->wfx));
1344 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
1345 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
1346 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
1347 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
1348 wfex->wBitsPerSample, wfex->cbSize);
1350 primarybuf->wfx.nAvgBytesPerSec =
1351 This->wfx.nSamplesPerSec * This->wfx.nBlockAlign;
1353 if (primarybuf->dsound->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMSETFORMAT) {
1354 /* FIXME: check for errors */
1355 DSOUND_PrimaryClose(primarybuf);
1356 waveOutClose(This->dsound->hwo);
1357 This->dsound->hwo = 0;
1358 err = mmErr(waveOutOpen(&(This->dsound->hwo), This->dsound->drvdesc.dnDevNode,
1359 &(primarybuf->wfx), (DWORD)DSOUND_callback, (DWORD)This->dsound,
1360 CALLBACK_FUNCTION | WAVE_DIRECTSOUND));
1362 DSOUND_PrimaryOpen(primarybuf);
1364 if (primarybuf->hwbuf) {
1365 err = IDsDriverBuffer_SetFormat(primarybuf->hwbuf, &(primarybuf->wfx));
1366 if (err == DSERR_BUFFERLOST) {
1367 /* Wine-only: the driver wants us to recreate the HW buffer */
1368 IDsDriverBuffer_Release(primarybuf->hwbuf);
1369 err = IDsDriver_CreateSoundBuffer(primarybuf->dsound->driver,&(primarybuf->wfx),primarybuf->dsbd.dwFlags,0,
1370 &(primarybuf->buflen),&(primarybuf->buffer),
1371 (LPVOID)&(primarybuf->hwbuf));
1372 if (primarybuf->state == STATE_PLAYING) primarybuf->state = STATE_STARTING;
1373 else if (primarybuf->state == STATE_STOPPING) primarybuf->state = STATE_STOPPED;
1375 /* FIXME: should we set err back to DS_OK in all cases ? */
1377 DSOUND_RecalcFormat(primarybuf);
1379 LeaveCriticalSection(&(This->dsound->lock));
1385 static HRESULT WINAPI IDirectSoundBufferImpl_SetVolume(
1386 LPDIRECTSOUNDBUFFER iface,LONG vol
1388 ICOM_THIS(IDirectSoundBufferImpl,iface);
1390 TRACE("(%p,%ld)\n",This,vol);
1392 /* I'm not sure if we need this for primary buffer */
1393 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
1394 return DSERR_CONTROLUNAVAIL;
1396 if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN))
1397 return DSERR_INVALIDPARAM;
1400 EnterCriticalSection(&(This->lock));
1402 This->volpan.lVolume = vol;
1404 DSOUND_RecalcVolPan(&(This->volpan));
1407 IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
1409 else if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
1410 #if 0 /* should we really do this? */
1411 /* the DS volume ranges from 0 (max, 0dB attenuation) to -10000 (min, 100dB attenuation) */
1412 /* the MM volume ranges from 0 to 0xffff in an unspecified logarithmic scale */
1413 WORD cvol = 0xffff + vol*6 + vol/2;
1414 DWORD vol = cvol | ((DWORD)cvol << 16)
1415 waveOutSetVolume(This->dsound->hwo, vol);
1419 LeaveCriticalSection(&(This->lock));
1425 static HRESULT WINAPI IDirectSoundBufferImpl_GetVolume(
1426 LPDIRECTSOUNDBUFFER iface,LPLONG vol
1428 ICOM_THIS(IDirectSoundBufferImpl,iface);
1429 TRACE("(%p,%p)\n",This,vol);
1432 return DSERR_INVALIDPARAM;
1434 *vol = This->volpan.lVolume;
1438 static HRESULT WINAPI IDirectSoundBufferImpl_SetFrequency(
1439 LPDIRECTSOUNDBUFFER iface,DWORD freq
1441 ICOM_THIS(IDirectSoundBufferImpl,iface);
1442 TRACE("(%p,%ld)\n",This,freq);
1444 /* You cannot set the frequency of the primary buffer */
1445 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY) ||
1446 (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
1447 return DSERR_CONTROLUNAVAIL;
1449 if (!freq) freq = This->wfx.nSamplesPerSec;
1451 if ((freq < DSBFREQUENCY_MIN) || (freq > DSBFREQUENCY_MAX))
1452 return DSERR_INVALIDPARAM;
1455 EnterCriticalSection(&(This->lock));
1458 This->freqAdjust = (freq << DSOUND_FREQSHIFT) / primarybuf->wfx.nSamplesPerSec;
1459 This->nAvgBytesPerSec = freq * This->wfx.nBlockAlign;
1460 DSOUND_RecalcFormat(This);
1462 LeaveCriticalSection(&(This->lock));
1468 static HRESULT WINAPI IDirectSoundBufferImpl_Play(
1469 LPDIRECTSOUNDBUFFER iface,DWORD reserved1,DWORD reserved2,DWORD flags
1471 ICOM_THIS(IDirectSoundBufferImpl,iface);
1472 TRACE("(%p,%08lx,%08lx,%08lx)\n",
1473 This,reserved1,reserved2,flags
1477 EnterCriticalSection(&(This->lock));
1479 This->playflags = flags;
1480 if (This->state == STATE_STOPPED) {
1481 This->leadin = TRUE;
1482 This->startpos = This->buf_mixpos;
1483 This->state = STATE_STARTING;
1484 } else if (This->state == STATE_STOPPING)
1485 This->state = STATE_PLAYING;
1486 if (!(This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && This->hwbuf) {
1487 IDsDriverBuffer_Play(This->hwbuf, 0, 0, This->playflags);
1488 This->state = STATE_PLAYING;
1491 LeaveCriticalSection(&(This->lock));
1497 static HRESULT WINAPI IDirectSoundBufferImpl_Stop(LPDIRECTSOUNDBUFFER iface)
1499 ICOM_THIS(IDirectSoundBufferImpl,iface);
1500 TRACE("(%p)\n",This);
1503 EnterCriticalSection(&(This->lock));
1505 if (This->state == STATE_PLAYING)
1506 This->state = STATE_STOPPING;
1507 else if (This->state == STATE_STARTING)
1508 This->state = STATE_STOPPED;
1509 if (!(This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) && This->hwbuf) {
1510 IDsDriverBuffer_Stop(This->hwbuf);
1511 This->state = STATE_STOPPED;
1513 DSOUND_CheckEvent(This, 0);
1515 LeaveCriticalSection(&(This->lock));
1521 static DWORD WINAPI IDirectSoundBufferImpl_AddRef(LPDIRECTSOUNDBUFFER iface) {
1522 ICOM_THIS(IDirectSoundBufferImpl,iface);
1525 TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId());
1527 ref = InterlockedIncrement(&(This->ref));
1529 FIXME("thread-safety alert! AddRef-ing with a zero refcount!\n");
1533 static DWORD WINAPI IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER iface) {
1534 ICOM_THIS(IDirectSoundBufferImpl,iface);
1538 TRACE("(%p) ref was %ld, thread is %lx\n",This, This->ref, GetCurrentThreadId());
1540 ref = InterlockedDecrement(&(This->ref));
1541 if (ref) return ref;
1543 EnterCriticalSection(&(This->dsound->lock));
1544 for (i=0;i<This->dsound->nrofbuffers;i++)
1545 if (This->dsound->buffers[i] == This)
1548 if (i < This->dsound->nrofbuffers) {
1549 /* Put the last buffer of the list in the (now empty) position */
1550 This->dsound->buffers[i] = This->dsound->buffers[This->dsound->nrofbuffers - 1];
1551 This->dsound->nrofbuffers--;
1552 This->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,This->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER)*This->dsound->nrofbuffers);
1553 TRACE("buffer count is now %d\n", This->dsound->nrofbuffers);
1554 IDirectSound_Release((LPDIRECTSOUND)This->dsound);
1556 LeaveCriticalSection(&(This->dsound->lock));
1558 DeleteCriticalSection(&(This->lock));
1559 if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1560 DSOUND_PrimaryClose(This);
1562 IDsDriverBuffer_Release(This->hwbuf);
1565 IDirectSound3DBuffer_Release((LPDIRECTSOUND3DBUFFER)This->ds3db);
1567 /* this is a duplicate buffer */
1568 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->parent);
1570 /* this is a toplevel buffer */
1571 HeapFree(GetProcessHeap(),0,This->buffer);
1573 HeapFree(GetProcessHeap(),0,This);
1575 if (This == primarybuf)
1581 static DWORD DSOUND_CalcPlayPosition(IDirectSoundBufferImpl *This,
1582 DWORD state, DWORD pplay, DWORD pwrite, DWORD pmix, DWORD bmix)
1586 TRACE("primary playpos=%ld, mixpos=%ld\n", pplay, pmix);
1587 TRACE("this mixpos=%ld, time=%ld\n", bmix, GetTickCount());
1589 /* the actual primary play position (pplay) is always behind last mixed (pmix),
1590 * unless the computer is too slow or something */
1591 /* we need to know how far away we are from there */
1592 #if 0 /* we'll never fill the primary entirely */
1593 if (pmix == pplay) {
1594 if ((state == STATE_PLAYING) || (state == STATE_STOPPING)) {
1595 /* wow, the software mixer is really doing well,
1596 * seems the entire primary buffer is filled! */
1597 pmix += primarybuf->buflen;
1599 /* else: the primary buffer is not playing, so probably empty */
1602 if (pmix < pplay) pmix += primarybuf->buflen; /* wraparound */
1604 /* detect buffer underrun */
1605 if (pwrite < pplay) pwrite += primarybuf->buflen; /* wraparound */
1607 if (pmix > (ds_snd_queue_max * primarybuf->dsound->fraglen + pwrite + primarybuf->writelead)) {
1608 WARN("detected an underrun: primary queue was %ld\n",pmix);
1611 /* divide the offset by its sample size */
1612 pmix /= primarybuf->wfx.nBlockAlign;
1613 TRACE("primary back-samples=%ld\n",pmix);
1614 /* adjust for our frequency */
1615 pmix = (pmix * This->freqAdjust) >> DSOUND_FREQSHIFT;
1616 /* multiply by our own sample size */
1617 pmix *= This->wfx.nBlockAlign;
1618 TRACE("this back-offset=%ld\n", pmix);
1619 /* subtract from our last mixed position */
1621 while (bplay < pmix) bplay += This->buflen; /* wraparound */
1623 if (This->leadin && ((bplay < This->startpos) || (bplay > bmix))) {
1624 /* seems we haven't started playing yet */
1625 TRACE("this still in lead-in phase\n");
1626 bplay = This->startpos;
1628 /* return the result */
1632 static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
1633 LPDIRECTSOUNDBUFFER iface,LPDWORD playpos,LPDWORD writepos
1636 ICOM_THIS(IDirectSoundBufferImpl,iface);
1637 TRACE("(%p,%p,%p)\n",This,playpos,writepos);
1639 hres=IDsDriverBuffer_GetPosition(This->hwbuf,playpos,writepos);
1643 else if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
1646 mtime.wType = TIME_BYTES;
1647 waveOutGetPosition(This->dsound->hwo, &mtime, sizeof(mtime));
1648 mtime.u.cb = mtime.u.cb % This->buflen;
1649 *playpos = mtime.u.cb;
1652 /* the writepos should only be used by apps with WRITEPRIMARY priority,
1653 * in which case our software mixer is disabled anyway */
1654 *writepos = (This->dsound->pwplay + ds_hel_margin) * This->dsound->fraglen;
1655 while (*writepos >= This->buflen)
1656 *writepos -= This->buflen;
1659 if (playpos && (This->state != STATE_PLAYING)) {
1660 /* we haven't been merged into the primary buffer (yet) */
1661 *playpos = This->buf_mixpos;
1664 DWORD pplay, pwrite, lplay, splay, pstate;
1665 /* let's get this exact; first, recursively call GetPosition on the primary */
1666 EnterCriticalSection(&(primarybuf->lock));
1667 IDirectSoundBufferImpl_GetCurrentPosition((LPDIRECTSOUNDBUFFER)primarybuf, &pplay, &pwrite);
1668 /* detect HEL mode underrun */
1669 pstate = primarybuf->state;
1670 if (!(primarybuf->hwbuf || primarybuf->dsound->pwqueue)) {
1671 TRACE("detected an underrun\n");
1673 if (pstate == STATE_PLAYING)
1674 pstate = STATE_STARTING;
1675 else if (pstate == STATE_STOPPING)
1676 pstate = STATE_STOPPED;
1678 /* get data for ourselves while we still have the lock */
1679 pstate &= This->state;
1680 lplay = This->primary_mixpos;
1681 splay = This->buf_mixpos;
1682 if ((This->dsbd.dwFlags & DSBCAPS_GETCURRENTPOSITION2) || primarybuf->hwbuf) {
1683 /* calculate play position using this */
1684 *playpos = DSOUND_CalcPlayPosition(This, pstate, pplay, pwrite, lplay, splay);
1686 /* (unless the app isn't using GETCURRENTPOSITION2) */
1687 /* don't know exactly how this should be handled...
1688 * the docs says that play cursor is reported as directly
1689 * behind write cursor, hmm... */
1690 /* let's just do what might work for Half-Life */
1692 wp = (This->dsound->pwplay + ds_hel_margin) * This->dsound->fraglen;
1693 while (wp >= primarybuf->buflen)
1694 wp -= primarybuf->buflen;
1695 *playpos = DSOUND_CalcPlayPosition(This, pstate, wp, pwrite, lplay, splay);
1697 LeaveCriticalSection(&(primarybuf->lock));
1699 if (writepos) *writepos = This->buf_mixpos;
1702 if (This->state != STATE_STOPPED)
1703 /* apply the documented 10ms lead to writepos */
1704 *writepos += This->writelead;
1705 while (*writepos >= This->buflen) *writepos -= This->buflen;
1707 TRACE("playpos = %ld, writepos = %ld (%p, time=%ld)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
1711 static HRESULT WINAPI IDirectSoundBufferImpl_GetStatus(
1712 LPDIRECTSOUNDBUFFER iface,LPDWORD status
1714 ICOM_THIS(IDirectSoundBufferImpl,iface);
1715 TRACE("(%p,%p), thread is %lx\n",This,status,GetCurrentThreadId());
1718 return DSERR_INVALIDPARAM;
1721 if ((This->state == STATE_STARTING) || (This->state == STATE_PLAYING)) {
1722 *status |= DSBSTATUS_PLAYING;
1723 if (This->playflags & DSBPLAY_LOOPING)
1724 *status |= DSBSTATUS_LOOPING;
1727 TRACE("status=%lx\n", *status);
1732 static HRESULT WINAPI IDirectSoundBufferImpl_GetFormat(
1733 LPDIRECTSOUNDBUFFER iface,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
1735 ICOM_THIS(IDirectSoundBufferImpl,iface);
1736 TRACE("(%p,%p,%ld,%p)\n",This,lpwf,wfsize,wfwritten);
1738 if (wfsize>sizeof(This->wfx))
1739 wfsize = sizeof(This->wfx);
1740 if (lpwf) { /* NULL is valid */
1741 memcpy(lpwf,&(This->wfx),wfsize);
1743 *wfwritten = wfsize;
1746 *wfwritten = sizeof(This->wfx);
1748 return DSERR_INVALIDPARAM;
1753 static HRESULT WINAPI IDirectSoundBufferImpl_Lock(
1754 LPDIRECTSOUNDBUFFER iface,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
1756 ICOM_THIS(IDirectSoundBufferImpl,iface);
1759 TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx) at %ld\n",
1771 if (flags & DSBLOCK_FROMWRITECURSOR) {
1773 /* GetCurrentPosition does too much magic to duplicate here */
1774 IDirectSoundBufferImpl_GetCurrentPosition(iface, NULL, &writepos);
1775 writecursor += writepos;
1777 if (flags & DSBLOCK_ENTIREBUFFER)
1778 writebytes = This->buflen;
1779 if (writebytes > This->buflen)
1780 writebytes = This->buflen;
1782 assert(audiobytes1!=audiobytes2);
1783 assert(lplpaudioptr1!=lplpaudioptr2);
1785 if ((writebytes == This->buflen) &&
1786 ((This->state == STATE_STARTING) ||
1787 (This->state == STATE_PLAYING)))
1788 /* some games, like Half-Life, try to be clever (not) and
1789 * keep one secondary buffer, and mix sounds into it itself,
1790 * locking the entire buffer every time... so we can just forget
1791 * about tracking the last-written-to-position... */
1792 This->probably_valid_to = (DWORD)-1;
1794 This->probably_valid_to = writecursor;
1796 if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1797 capf = DSDDESC_DONTNEEDPRIMARYLOCK;
1799 capf = DSDDESC_DONTNEEDSECONDARYLOCK;
1800 if (!(This->dsound->drvdesc.dwFlags & capf) && This->hwbuf) {
1801 IDsDriverBuffer_Lock(This->hwbuf,
1802 lplpaudioptr1, audiobytes1,
1803 lplpaudioptr2, audiobytes2,
1804 writecursor, writebytes,
1809 if (writecursor+writebytes <= This->buflen) {
1810 *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor;
1811 *audiobytes1 = writebytes;
1813 *(LPBYTE*)lplpaudioptr2 = NULL;
1816 TRACE("->%ld.0\n",writebytes);
1818 *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor;
1819 *audiobytes1 = This->buflen-writecursor;
1821 *(LPBYTE*)lplpaudioptr2 = This->buffer;
1823 *audiobytes2 = writebytes-(This->buflen-writecursor);
1824 TRACE("->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0);
1826 /* if the segment between playpos and buf_mixpos is touched,
1827 * we need to cancel some mixing */
1828 if (This->buf_mixpos >= This->playpos) {
1829 if (This->buf_mixpos > writecursor &&
1830 This->playpos <= writecursor+writebytes)
1834 if (This->buf_mixpos > writecursor ||
1835 This->playpos <= writecursor+writebytes)
1839 TRACE("locking prebuffered region, ouch\n");
1840 DSOUND_MixCancelAt(This, writecursor);
1846 static HRESULT WINAPI IDirectSoundBufferImpl_SetCurrentPosition(
1847 LPDIRECTSOUNDBUFFER iface,DWORD newpos
1849 ICOM_THIS(IDirectSoundBufferImpl,iface);
1850 TRACE("(%p,%ld)\n",This,newpos);
1853 EnterCriticalSection(&(This->lock));
1855 This->buf_mixpos = newpos;
1857 IDsDriverBuffer_SetPosition(This->hwbuf, This->buf_mixpos);
1859 LeaveCriticalSection(&(This->lock));
1865 static HRESULT WINAPI IDirectSoundBufferImpl_SetPan(
1866 LPDIRECTSOUNDBUFFER iface,LONG pan
1868 ICOM_THIS(IDirectSoundBufferImpl,iface);
1870 TRACE("(%p,%ld)\n",This,pan);
1872 if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT))
1873 return DSERR_INVALIDPARAM;
1875 /* You cannot set the pan of the primary buffer */
1876 /* and you cannot use both pan and 3D controls */
1877 if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
1878 (This->dsbd.dwFlags & DSBCAPS_CTRL3D) ||
1879 (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER))
1880 return DSERR_CONTROLUNAVAIL;
1883 EnterCriticalSection(&(This->lock));
1885 This->volpan.lPan = pan;
1887 DSOUND_RecalcVolPan(&(This->volpan));
1890 IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
1893 LeaveCriticalSection(&(This->lock));
1899 static HRESULT WINAPI IDirectSoundBufferImpl_GetPan(
1900 LPDIRECTSOUNDBUFFER iface,LPLONG pan
1902 ICOM_THIS(IDirectSoundBufferImpl,iface);
1903 TRACE("(%p,%p)\n",This,pan);
1906 return DSERR_INVALIDPARAM;
1908 *pan = This->volpan.lPan;
1913 static HRESULT WINAPI IDirectSoundBufferImpl_Unlock(
1914 LPDIRECTSOUNDBUFFER iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
1916 ICOM_THIS(IDirectSoundBufferImpl,iface);
1917 DWORD capf, probably_valid_to;
1919 TRACE("(%p,%p,%ld,%p,%ld):stub\n", This,p1,x1,p2,x2);
1922 /* Preprocess 3D buffers... */
1924 /* This is highly experimental and liable to break things */
1925 if (This->dsbd.dwFlags & DSBCAPS_CTRL3D)
1926 DSOUND_Create3DBuffer(This);
1929 if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)
1930 capf = DSDDESC_DONTNEEDPRIMARYLOCK;
1932 capf = DSDDESC_DONTNEEDSECONDARYLOCK;
1933 if (!(This->dsound->drvdesc.dwFlags & capf) && This->hwbuf) {
1934 IDsDriverBuffer_Unlock(This->hwbuf, p1, x1, p2, x2);
1937 if (p2) probably_valid_to = (((LPBYTE)p2)-This->buffer) + x2;
1938 else probably_valid_to = (((LPBYTE)p1)-This->buffer) + x1;
1939 while (probably_valid_to >= This->buflen)
1940 probably_valid_to -= This->buflen;
1941 if ((probably_valid_to == 0) && ((x1+x2) == This->buflen) &&
1942 ((This->state == STATE_STARTING) ||
1943 (This->state == STATE_PLAYING)))
1944 /* see IDirectSoundBufferImpl_Lock */
1945 probably_valid_to = (DWORD)-1;
1946 This->probably_valid_to = probably_valid_to;
1951 static HRESULT WINAPI IDirectSoundBufferImpl_Restore(
1952 LPDIRECTSOUNDBUFFER iface
1954 ICOM_THIS(IDirectSoundBufferImpl,iface);
1955 FIXME("(%p):stub\n",This);
1959 static HRESULT WINAPI IDirectSoundBufferImpl_GetFrequency(
1960 LPDIRECTSOUNDBUFFER iface,LPDWORD freq
1962 ICOM_THIS(IDirectSoundBufferImpl,iface);
1963 TRACE("(%p,%p)\n",This,freq);
1966 return DSERR_INVALIDPARAM;
1969 TRACE("-> %ld\n", *freq);
1974 static HRESULT WINAPI IDirectSoundBufferImpl_Initialize(
1975 LPDIRECTSOUNDBUFFER iface,LPDIRECTSOUND dsound,LPDSBUFFERDESC dbsd
1977 ICOM_THIS(IDirectSoundBufferImpl,iface);
1978 FIXME("(%p,%p,%p):stub\n",This,dsound,dbsd);
1979 DPRINTF("Re-Init!!!\n");
1980 return DSERR_ALREADYINITIALIZED;
1983 static HRESULT WINAPI IDirectSoundBufferImpl_GetCaps(
1984 LPDIRECTSOUNDBUFFER iface,LPDSBCAPS caps
1986 ICOM_THIS(IDirectSoundBufferImpl,iface);
1987 TRACE("(%p)->(%p)\n",This,caps);
1990 return DSERR_INVALIDPARAM;
1992 /* I think we should check this value, not set it. See */
1993 /* Inside DirectX, p215. That should apply here, too. */
1994 caps->dwSize = sizeof(*caps);
1996 caps->dwFlags = This->dsbd.dwFlags;
1997 if (This->hwbuf) caps->dwFlags |= DSBCAPS_LOCHARDWARE;
1998 else caps->dwFlags |= DSBCAPS_LOCSOFTWARE;
2000 caps->dwBufferBytes = This->buflen;
2002 /* This value represents the speed of the "unlock" command.
2003 As unlock is quite fast (it does not do anything), I put
2004 4096 ko/s = 4 Mo / s */
2005 /* FIXME: hwbuf speed */
2006 caps->dwUnlockTransferRate = 4096;
2007 caps->dwPlayCpuOverhead = 0;
2012 static HRESULT WINAPI IDirectSoundBufferImpl_QueryInterface(
2013 LPDIRECTSOUNDBUFFER iface,REFIID riid,LPVOID *ppobj
2015 ICOM_THIS(IDirectSoundBufferImpl,iface);
2017 TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
2019 if ( IsEqualGUID( &IID_IDirectSoundNotify, riid ) ) {
2020 IDirectSoundNotifyImpl *dsn;
2022 dsn = (IDirectSoundNotifyImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsn));
2025 IDirectSoundBuffer_AddRef(iface);
2026 ICOM_VTBL(dsn) = &dsnvt;
2027 *ppobj = (LPVOID)dsn;
2032 if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
2033 IDirectSound3DBufferImpl *ds3db;
2035 *ppobj = This->ds3db;
2037 IDirectSound3DBuffer_AddRef((LPDIRECTSOUND3DBUFFER)This->ds3db);
2041 ds3db = (IDirectSound3DBufferImpl*)HeapAlloc(GetProcessHeap(),
2045 ICOM_VTBL(ds3db) = &ds3dbvt;
2046 InitializeCriticalSection(&ds3db->lock);
2048 IDirectSoundBuffer_AddRef(iface);
2050 ds3db->ds3db.dwSize = sizeof(DS3DBUFFER);
2051 ds3db->ds3db.vPosition.u1.x = 0.0;
2052 ds3db->ds3db.vPosition.u2.y = 0.0;
2053 ds3db->ds3db.vPosition.u3.z = 0.0;
2054 ds3db->ds3db.vVelocity.u1.x = 0.0;
2055 ds3db->ds3db.vVelocity.u2.y = 0.0;
2056 ds3db->ds3db.vVelocity.u3.z = 0.0;
2057 ds3db->ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
2058 ds3db->ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
2059 ds3db->ds3db.vConeOrientation.u1.x = 0.0;
2060 ds3db->ds3db.vConeOrientation.u2.y = 0.0;
2061 ds3db->ds3db.vConeOrientation.u3.z = 0.0;
2062 ds3db->ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME; ds3db->ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE;
2063 ds3db->ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
2064 ds3db->ds3db.dwMode = DS3DMODE_NORMAL;
2065 ds3db->buflen = (This->buflen * primarybuf->wfx.nBlockAlign) /
2066 This->wfx.nBlockAlign;
2067 ds3db->buffer = HeapAlloc(GetProcessHeap(), 0, ds3db->buflen);
2068 if (ds3db->buffer == NULL) {
2070 ds3db->ds3db.dwMode = DS3DMODE_DISABLE;
2073 ds3db->iks = (IKsPropertySetImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*(ds3db->iks)));
2074 ds3db->iks->ref = 1;
2075 ds3db->iks->ds3db = ds3db;
2076 ICOM_VTBL(ds3db->iks) = &iksvt;
2081 if ( IsEqualGUID( &IID_IDirectSound3DBuffer, riid ) ) {
2082 FIXME("%s: I know about this GUID, but don't support it yet\n",
2083 debugstr_guid( riid ));
2090 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
2091 IDirectSound3DListenerImpl* dsl;
2093 if (This->dsound->listener) {
2094 *ppobj = This->dsound->listener;
2095 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)This->dsound->listener);
2099 dsl = (IDirectSound3DListenerImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsl));
2101 ICOM_VTBL(dsl) = &ds3dlvt;
2102 *ppobj = (LPVOID)dsl;
2104 dsl->ds3dl.dwSize = sizeof(DS3DLISTENER);
2105 dsl->ds3dl.vPosition.u1.x = 0.0;
2106 dsl->ds3dl.vPosition.u2.y = 0.0;
2107 dsl->ds3dl.vPosition.u3.z = 0.0;
2108 dsl->ds3dl.vVelocity.u1.x = 0.0;
2109 dsl->ds3dl.vVelocity.u2.y = 0.0;
2110 dsl->ds3dl.vVelocity.u3.z = 0.0;
2111 dsl->ds3dl.vOrientFront.u1.x = 0.0;
2112 dsl->ds3dl.vOrientFront.u2.y = 0.0;
2113 dsl->ds3dl.vOrientFront.u3.z = 1.0;
2114 dsl->ds3dl.vOrientTop.u1.x = 0.0;
2115 dsl->ds3dl.vOrientTop.u2.y = 1.0;
2116 dsl->ds3dl.vOrientTop.u3.z = 0.0;
2117 dsl->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
2118 dsl->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
2120 InitializeCriticalSection(&dsl->lock);
2123 IDirectSoundBuffer_AddRef(iface);
2125 This->dsound->listener = dsl;
2126 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)dsl);
2131 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
2132 FIXME("%s: I know about this GUID, but don't support it yet\n",
2133 debugstr_guid( riid ));
2139 FIXME( "Unknown GUID %s\n", debugstr_guid( riid ) );
2146 static ICOM_VTABLE(IDirectSoundBuffer) dsbvt =
2148 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2149 IDirectSoundBufferImpl_QueryInterface,
2150 IDirectSoundBufferImpl_AddRef,
2151 IDirectSoundBufferImpl_Release,
2152 IDirectSoundBufferImpl_GetCaps,
2153 IDirectSoundBufferImpl_GetCurrentPosition,
2154 IDirectSoundBufferImpl_GetFormat,
2155 IDirectSoundBufferImpl_GetVolume,
2156 IDirectSoundBufferImpl_GetPan,
2157 IDirectSoundBufferImpl_GetFrequency,
2158 IDirectSoundBufferImpl_GetStatus,
2159 IDirectSoundBufferImpl_Initialize,
2160 IDirectSoundBufferImpl_Lock,
2161 IDirectSoundBufferImpl_Play,
2162 IDirectSoundBufferImpl_SetCurrentPosition,
2163 IDirectSoundBufferImpl_SetFormat,
2164 IDirectSoundBufferImpl_SetVolume,
2165 IDirectSoundBufferImpl_SetPan,
2166 IDirectSoundBufferImpl_SetFrequency,
2167 IDirectSoundBufferImpl_Stop,
2168 IDirectSoundBufferImpl_Unlock,
2169 IDirectSoundBufferImpl_Restore
2172 /*******************************************************************************
2176 static HRESULT WINAPI IDirectSoundImpl_SetCooperativeLevel(
2177 LPDIRECTSOUND iface,HWND hwnd,DWORD level
2179 ICOM_THIS(IDirectSoundImpl,iface);
2181 FIXME("(%p,%08lx,%ld):stub\n",This,(DWORD)hwnd,level);
2183 This->priolevel = level;
2188 static HRESULT WINAPI IDirectSoundImpl_CreateSoundBuffer(
2189 LPDIRECTSOUND iface,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER ppdsb,LPUNKNOWN lpunk
2191 ICOM_THIS(IDirectSoundImpl,iface);
2192 IDirectSoundBufferImpl** ippdsb=(IDirectSoundBufferImpl**)ppdsb;
2193 LPWAVEFORMATEX wfex;
2194 HRESULT err = DS_OK;
2196 TRACE("(%p,%p,%p,%p)\n",This,dsbd,ippdsb,lpunk);
2198 if ((This == NULL) || (dsbd == NULL) || (ippdsb == NULL))
2199 return DSERR_INVALIDPARAM;
2201 if (TRACE_ON(dsound)) {
2202 TRACE("(structsize=%ld)\n",dsbd->dwSize);
2203 TRACE("(flags=0x%08lx:\n",dsbd->dwFlags);
2204 _dump_DSBCAPS(dsbd->dwFlags);
2206 TRACE("(bufferbytes=%ld)\n",dsbd->dwBufferBytes);
2207 TRACE("(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
2210 wfex = dsbd->lpwfxFormat;
2213 TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld,"
2214 "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n",
2215 wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec,
2216 wfex->nAvgBytesPerSec, wfex->nBlockAlign,
2217 wfex->wBitsPerSample, wfex->cbSize);
2219 if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
2221 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)primarybuf);
2222 *ippdsb = primarybuf;
2223 primarybuf->dsbd.dwFlags = dsbd->dwFlags;
2225 } /* Else create primary buffer */
2227 if (dsbd->dwBufferBytes < DSBSIZE_MIN || dsbd->dwBufferBytes > DSBSIZE_MAX) {
2228 ERR("invalid sound buffer size %ld\n", dsbd->dwBufferBytes);
2229 return DSERR_INVALIDPARAM; /* FIXME: which error? */
2233 *ippdsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBufferImpl));
2234 if (*ippdsb == NULL)
2235 return DSERR_OUTOFMEMORY;
2236 ICOM_VTBL(*ippdsb) = &dsbvt;
2238 (*ippdsb)->dsound = This;
2239 (*ippdsb)->parent = NULL;
2240 (*ippdsb)->buffer = NULL;
2242 memcpy(&((*ippdsb)->dsbd),dsbd,sizeof(*dsbd));
2243 if (dsbd->lpwfxFormat)
2244 memcpy(&((*ippdsb)->wfx), dsbd->lpwfxFormat, sizeof((*ippdsb)->wfx));
2246 TRACE("Created buffer at %p\n", *ippdsb);
2248 if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) {
2249 (*ippdsb)->buflen = dsound->wfx.nAvgBytesPerSec;
2250 (*ippdsb)->freq = dsound->wfx.nSamplesPerSec;
2252 /* FIXME: verify that hardware capabilities (DSCAPS_PRIMARY flags) match */
2255 err = IDsDriver_CreateSoundBuffer(This->driver,wfex,dsbd->dwFlags,0,
2256 &((*ippdsb)->buflen),&((*ippdsb)->buffer),
2257 (LPVOID*)&((*ippdsb)->hwbuf));
2260 err = DSOUND_PrimaryOpen(*ippdsb);
2265 (*ippdsb)->buflen = dsbd->dwBufferBytes;
2266 (*ippdsb)->freq = dsbd->lpwfxFormat->nSamplesPerSec;
2268 /* Check necessary hardware mixing capabilities */
2269 if (wfex->nChannels==2) capf |= DSCAPS_SECONDARYSTEREO;
2270 else capf |= DSCAPS_SECONDARYMONO;
2271 if (wfex->wBitsPerSample==16) capf |= DSCAPS_SECONDARY16BIT;
2272 else capf |= DSCAPS_SECONDARY8BIT;
2273 use_hw = (This->drvcaps.dwFlags & capf) == capf;
2275 /* FIXME: check hardware sample rate mixing capabilities */
2276 /* FIXME: check app hints for software/hardware buffer (STATIC, LOCHARDWARE, etc) */
2277 /* FIXME: check whether any hardware buffers are left */
2278 /* FIXME: handle DSDHEAP_CREATEHEAP for hardware buffers */
2280 /* Allocate system memory if applicable */
2281 if ((This->drvdesc.dwFlags & DSDDESC_USESYSTEMMEMORY) || !use_hw) {
2282 (*ippdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,(*ippdsb)->buflen);
2283 if ((*ippdsb)->buffer == NULL)
2284 err = DSERR_OUTOFMEMORY;
2287 /* Allocate the hardware buffer */
2288 if (use_hw && (err == DS_OK)) {
2289 err = IDsDriver_CreateSoundBuffer(This->driver,wfex,dsbd->dwFlags,0,
2290 &((*ippdsb)->buflen),&((*ippdsb)->buffer),
2291 (LPVOID*)&((*ippdsb)->hwbuf));
2296 if ((*ippdsb)->buffer)
2297 HeapFree(GetProcessHeap(),0,(*ippdsb)->buffer);
2298 HeapFree(GetProcessHeap(),0,(*ippdsb));
2302 /* calculate fragment size and write lead */
2303 DSOUND_RecalcFormat(*ippdsb);
2305 /* It's not necessary to initialize values to zero since */
2306 /* we allocated this structure with HEAP_ZERO_MEMORY... */
2307 (*ippdsb)->playpos = 0;
2308 (*ippdsb)->buf_mixpos = 0;
2309 (*ippdsb)->state = STATE_STOPPED;
2310 DSOUND_RecalcVolPan(&((*ippdsb)->volpan));
2312 if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
2313 (*ippdsb)->freqAdjust = ((*ippdsb)->freq << DSOUND_FREQSHIFT) /
2314 primarybuf->wfx.nSamplesPerSec;
2315 (*ippdsb)->nAvgBytesPerSec = (*ippdsb)->freq *
2316 dsbd->lpwfxFormat->nBlockAlign;
2319 EnterCriticalSection(&(This->lock));
2320 /* register buffer */
2321 if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
2322 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl*)*(This->nrofbuffers+1));
2324 This->buffers = newbuffers;
2325 This->buffers[This->nrofbuffers] = *ippdsb;
2326 This->nrofbuffers++;
2327 TRACE("buffer count is now %d\n", This->nrofbuffers);
2329 ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
2330 err = DSERR_OUTOFMEMORY;
2333 LeaveCriticalSection(&(This->lock));
2335 IDirectSound_AddRef(iface);
2337 InitializeCriticalSection(&((*ippdsb)->lock));
2341 IDirectSoundBuffer_Release(*ppdsb);
2347 if (dsbd->dwFlags & DSBCAPS_CTRL3D) {
2348 IDirectSound3DBufferImpl *ds3db;
2350 ds3db = (IDirectSound3DBufferImpl*)HeapAlloc(GetProcessHeap(),
2352 ICOM_VTBL(ds3db) = &ds3dbvt;
2354 (*ippdsb)->ds3db = ds3db;
2356 ds3db->dsb = (*ippdsb);
2357 IDirectSoundBufferImpl_AddRef((LPDIRECTSOUNDBUFFER)(*ippdsb));
2359 InitializeCriticalSection(&ds3db->lock);
2361 ds3db->ds3db.dwSize = sizeof(DS3DBUFFER);
2362 ds3db->ds3db.vPosition.u1.x = 0.0;
2363 ds3db->ds3db.vPosition.u2.y = 0.0;
2364 ds3db->ds3db.vPosition.u3.z = 0.0;
2365 ds3db->ds3db.vVelocity.u1.x = 0.0;
2366 ds3db->ds3db.vVelocity.u2.y = 0.0;
2367 ds3db->ds3db.vVelocity.u3.z = 0.0;
2368 ds3db->ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
2369 ds3db->ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
2370 ds3db->ds3db.vConeOrientation.u1.x = 0.0;
2371 ds3db->ds3db.vConeOrientation.u2.y = 0.0;
2372 ds3db->ds3db.vConeOrientation.u3.z = 0.0;
2373 ds3db->ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME;
2374 ds3db->ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE;
2375 ds3db->ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE;
2376 ds3db->ds3db.dwMode = DS3DMODE_NORMAL;
2377 ds3db->buflen = ((*ippdsb)->buflen * primarybuf->wfx.nBlockAlign) /
2378 (*ippdsb)->wfx.nBlockAlign;
2379 ds3db->buffer = HeapAlloc(GetProcessHeap(), 0, ds3db->buflen);
2380 if (ds3db->buffer == NULL) {
2382 ds3db->ds3db.dwMode = DS3DMODE_DISABLE;
2384 ds3db->iks = (IKsPropertySetImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*(ds3db->iks)));
2385 ds3db->iks->ref = 1;
2386 ds3db->iks->ds3db = ds3db;
2387 ICOM_VTBL(ds3db->iks) = &iksvt;
2394 static HRESULT WINAPI IDirectSoundImpl_DuplicateSoundBuffer(
2395 LPDIRECTSOUND iface,LPDIRECTSOUNDBUFFER pdsb,LPLPDIRECTSOUNDBUFFER ppdsb
2397 ICOM_THIS(IDirectSoundImpl,iface);
2398 IDirectSoundBufferImpl* ipdsb=(IDirectSoundBufferImpl*)pdsb;
2399 IDirectSoundBufferImpl** ippdsb=(IDirectSoundBufferImpl**)ppdsb;
2400 TRACE("(%p,%p,%p)\n",This,ipdsb,ippdsb);
2403 FIXME("need to duplicate hardware buffer\n");
2406 *ippdsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBufferImpl));
2408 IDirectSoundBuffer_AddRef(pdsb);
2409 memcpy(*ippdsb, ipdsb, sizeof(IDirectSoundBufferImpl));
2411 (*ippdsb)->state = STATE_STOPPED;
2412 (*ippdsb)->playpos = 0;
2413 (*ippdsb)->buf_mixpos = 0;
2414 (*ippdsb)->dsound = This;
2415 (*ippdsb)->parent = ipdsb;
2416 memcpy(&((*ippdsb)->wfx), &(ipdsb->wfx), sizeof((*ippdsb)->wfx));
2417 InitializeCriticalSection(&(*ippdsb)->lock);
2418 /* register buffer */
2419 EnterCriticalSection(&(This->lock));
2421 IDirectSoundBufferImpl **newbuffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl**)*(This->nrofbuffers+1));
2423 This->buffers = newbuffers;
2424 This->buffers[This->nrofbuffers] = *ippdsb;
2425 This->nrofbuffers++;
2426 TRACE("buffer count is now %d\n", This->nrofbuffers);
2428 ERR("out of memory for buffer list! Current buffer count is %d\n", This->nrofbuffers);
2429 /* FIXME: release buffer */
2432 LeaveCriticalSection(&(This->lock));
2433 IDirectSound_AddRef(iface);
2438 static HRESULT WINAPI IDirectSoundImpl_GetCaps(LPDIRECTSOUND iface,LPDSCAPS caps) {
2439 ICOM_THIS(IDirectSoundImpl,iface);
2440 TRACE("(%p,%p)\n",This,caps);
2441 TRACE("(flags=0x%08lx)\n",caps->dwFlags);
2444 return DSERR_INVALIDPARAM;
2446 /* We should check this value, not set it. See Inside DirectX, p215. */
2447 caps->dwSize = sizeof(*caps);
2449 caps->dwFlags = This->drvcaps.dwFlags;
2451 /* FIXME: copy caps from This->drvcaps */
2452 caps->dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
2453 caps->dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
2455 caps->dwPrimaryBuffers = 1;
2457 caps->dwMaxHwMixingAllBuffers = 0;
2458 caps->dwMaxHwMixingStaticBuffers = 0;
2459 caps->dwMaxHwMixingStreamingBuffers = 0;
2461 caps->dwFreeHwMixingAllBuffers = 0;
2462 caps->dwFreeHwMixingStaticBuffers = 0;
2463 caps->dwFreeHwMixingStreamingBuffers = 0;
2465 caps->dwMaxHw3DAllBuffers = 0;
2466 caps->dwMaxHw3DStaticBuffers = 0;
2467 caps->dwMaxHw3DStreamingBuffers = 0;
2469 caps->dwFreeHw3DAllBuffers = 0;
2470 caps->dwFreeHw3DStaticBuffers = 0;
2471 caps->dwFreeHw3DStreamingBuffers = 0;
2473 caps->dwTotalHwMemBytes = 0;
2475 caps->dwFreeHwMemBytes = 0;
2477 caps->dwMaxContigFreeHwMemBytes = 0;
2479 caps->dwUnlockTransferRateHwBuffers = 4096; /* But we have none... */
2481 caps->dwPlayCpuOverheadSwBuffers = 1; /* 1% */
2486 static ULONG WINAPI IDirectSoundImpl_AddRef(LPDIRECTSOUND iface) {
2487 ICOM_THIS(IDirectSoundImpl,iface);
2488 return ++(This->ref);
2491 static ULONG WINAPI IDirectSoundImpl_Release(LPDIRECTSOUND iface) {
2492 ICOM_THIS(IDirectSoundImpl,iface);
2493 TRACE("(%p), ref was %ld\n",This,This->ref);
2494 if (!--(This->ref)) {
2497 timeKillEvent(This->timerID);
2498 timeEndPeriod(DS_TIME_RES);
2501 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)primarybuf);
2503 if (This->buffers) {
2504 for( i=0;i<This->nrofbuffers;i++)
2505 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->buffers[i]);
2509 IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->primary);
2511 DeleteCriticalSection(&This->lock);
2513 IDsDriver_Close(This->driver);
2516 for (c=0; c<DS_HEL_FRAGS; c++)
2517 HeapFree(GetProcessHeap(),0,This->pwave[c]);
2519 if (This->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN) {
2520 waveOutClose(This->hwo);
2523 IDsDriver_Release(This->driver);
2525 HeapFree(GetProcessHeap(),0,This);
2532 static HRESULT WINAPI IDirectSoundImpl_SetSpeakerConfig(
2533 LPDIRECTSOUND iface,DWORD config
2535 ICOM_THIS(IDirectSoundImpl,iface);
2536 FIXME("(%p,0x%08lx):stub\n",This,config);
2540 static HRESULT WINAPI IDirectSoundImpl_QueryInterface(
2541 LPDIRECTSOUND iface,REFIID riid,LPVOID *ppobj
2543 ICOM_THIS(IDirectSoundImpl,iface);
2545 if ( IsEqualGUID( &IID_IDirectSound3DListener, riid ) ) {
2547 if (This->listener) {
2548 *ppobj = This->listener;
2549 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)This->listener);
2553 This->listener = (IDirectSound3DListenerImpl*)HeapAlloc(
2554 GetProcessHeap(), 0, sizeof(*(This->listener)));
2555 This->listener->ref = 1;
2556 ICOM_VTBL(This->listener) = &ds3dlvt;
2557 *ppobj = (LPVOID)This->listener;
2558 IDirectSound3DListener_AddRef((LPDIRECTSOUND3DLISTENER)*ppobj);
2560 This->listener->dsb = NULL;
2562 This->listener->ds3dl.dwSize = sizeof(DS3DLISTENER);
2563 This->listener->ds3dl.vPosition.u1.x = 0.0;
2564 This->listener->ds3dl.vPosition.u2.y = 0.0;
2565 This->listener->ds3dl.vPosition.u3.z = 0.0;
2566 This->listener->ds3dl.vVelocity.u1.x = 0.0;
2567 This->listener->ds3dl.vVelocity.u2.y = 0.0;
2568 This->listener->ds3dl.vVelocity.u3.z = 0.0;
2569 This->listener->ds3dl.vOrientFront.u1.x = 0.0;
2570 This->listener->ds3dl.vOrientFront.u2.y = 0.0;
2571 This->listener->ds3dl.vOrientFront.u3.z = 1.0;
2572 This->listener->ds3dl.vOrientTop.u1.x = 0.0;
2573 This->listener->ds3dl.vOrientTop.u2.y = 1.0;
2574 This->listener->ds3dl.vOrientTop.u3.z = 0.0;
2575 This->listener->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR;
2576 This->listener->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR;
2577 This->listener->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR;
2579 InitializeCriticalSection(&This->listener->lock);
2584 FIXME("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
2588 static HRESULT WINAPI IDirectSoundImpl_Compact(
2589 LPDIRECTSOUND iface)
2591 ICOM_THIS(IDirectSoundImpl,iface);
2592 TRACE("(%p)\n", This);
2596 static HRESULT WINAPI IDirectSoundImpl_GetSpeakerConfig(
2597 LPDIRECTSOUND iface,
2598 LPDWORD lpdwSpeakerConfig)
2600 ICOM_THIS(IDirectSoundImpl,iface);
2601 TRACE("(%p, %p)\n", This, lpdwSpeakerConfig);
2602 *lpdwSpeakerConfig = DSSPEAKER_STEREO | (DSSPEAKER_GEOMETRY_NARROW << 16);
2606 static HRESULT WINAPI IDirectSoundImpl_Initialize(
2607 LPDIRECTSOUND iface,
2610 ICOM_THIS(IDirectSoundImpl,iface);
2611 TRACE("(%p, %p)\n", This, lpcGuid);
2615 static ICOM_VTABLE(IDirectSound) dsvt =
2617 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
2618 IDirectSoundImpl_QueryInterface,
2619 IDirectSoundImpl_AddRef,
2620 IDirectSoundImpl_Release,
2621 IDirectSoundImpl_CreateSoundBuffer,
2622 IDirectSoundImpl_GetCaps,
2623 IDirectSoundImpl_DuplicateSoundBuffer,
2624 IDirectSoundImpl_SetCooperativeLevel,
2625 IDirectSoundImpl_Compact,
2626 IDirectSoundImpl_GetSpeakerConfig,
2627 IDirectSoundImpl_SetSpeakerConfig,
2628 IDirectSoundImpl_Initialize
2632 static void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len)
2636 LPDSBPOSITIONNOTIFY event;
2638 if (dsb->nrofnotifies == 0)
2641 TRACE("(%p) buflen = %ld, playpos = %ld, len = %d\n",
2642 dsb, dsb->buflen, dsb->playpos, len);
2643 for (i = 0; i < dsb->nrofnotifies ; i++) {
2644 event = dsb->notifies + i;
2645 offset = event->dwOffset;
2646 TRACE("checking %d, position %ld, event = %d\n",
2647 i, offset, event->hEventNotify);
2648 /* DSBPN_OFFSETSTOP has to be the last element. So this is */
2649 /* OK. [Inside DirectX, p274] */
2651 /* This also means we can't sort the entries by offset, */
2652 /* because DSBPN_OFFSETSTOP == -1 */
2653 if (offset == DSBPN_OFFSETSTOP) {
2654 if (dsb->state == STATE_STOPPED) {
2655 SetEvent(event->hEventNotify);
2656 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2661 if ((dsb->playpos + len) >= dsb->buflen) {
2662 if ((offset < ((dsb->playpos + len) % dsb->buflen)) ||
2663 (offset >= dsb->playpos)) {
2664 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2665 SetEvent(event->hEventNotify);
2668 if ((offset >= dsb->playpos) && (offset < (dsb->playpos + len))) {
2669 TRACE("signalled event %d (%d)\n", event->hEventNotify, i);
2670 SetEvent(event->hEventNotify);
2676 /* WAV format info can be found at: */
2678 /* http://www.cwi.nl/ftp/audio/AudioFormats.part2 */
2679 /* ftp://ftp.cwi.nl/pub/audio/RIFF-format */
2681 /* Import points to remember: */
2683 /* 8-bit WAV is unsigned */
2684 /* 16-bit WAV is signed */
2686 static inline INT16 cvtU8toS16(BYTE byte)
2688 INT16 s = (byte - 128) << 8;
2693 static inline BYTE cvtS16toU8(INT16 word)
2695 BYTE b = (word + 32768) >> 8;
2701 static inline void cp_fields(const IDirectSoundBufferImpl *dsb, BYTE *ibuf, BYTE *obuf )
2704 if (dsb->wfx.nChannels == 2) {
2705 if (dsb->wfx.wBitsPerSample == 8) {
2706 /* avoid needless 8->16->8 conversion */
2707 if ( (primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 2) ) {
2709 *(obuf+1)=*(ibuf+1);
2712 fl = cvtU8toS16(*ibuf);
2713 fr = cvtU8toS16(*(ibuf + 1));
2714 } else if (dsb->wfx.wBitsPerSample == 16) {
2715 fl = *((INT16 *)ibuf);
2716 fr = *(((INT16 *)ibuf) + 1);
2718 } else if (dsb->wfx.nChannels == 1) {
2719 if (dsb->wfx.wBitsPerSample == 8) {
2720 /* avoid needless 8->16->8 conversion */
2721 if ( (primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 1) ) {
2725 fl = cvtU8toS16(*ibuf);
2727 } else if (dsb->wfx.wBitsPerSample == 16) {
2728 fl = *((INT16 *)ibuf);
2732 if (primarybuf->wfx.nChannels == 2) {
2733 if (primarybuf->wfx.wBitsPerSample == 8) {
2734 *obuf = cvtS16toU8(fl);
2735 *(obuf + 1) = cvtS16toU8(fr);
2738 if (primarybuf->wfx.wBitsPerSample == 16) {
2739 *((INT16 *)obuf) = fl;
2740 *(((INT16 *)obuf) + 1) = fr;
2744 if (primarybuf->wfx.nChannels == 1) {
2745 fl = (fl + fr) >> 1;
2746 if (primarybuf->wfx.wBitsPerSample == 8) {
2747 *obuf = cvtS16toU8(fl);
2750 if (primarybuf->wfx.wBitsPerSample == 16) {
2751 *((INT16 *)obuf) = fl;
2757 /* Now with PerfectPitch (tm) technology */
2758 static INT DSOUND_MixerNorm(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2760 INT i, size, ipos, ilen;
2762 INT iAdvance = dsb->wfx.nBlockAlign;
2763 INT oAdvance = primarybuf->wfx.nBlockAlign;
2765 ibp = dsb->buffer + dsb->buf_mixpos;
2768 TRACE("(%p, %p, %p), buf_mixpos=%ld\n", dsb, ibp, obp, dsb->buf_mixpos);
2769 /* Check for the best case */
2770 if ((dsb->freq == primarybuf->wfx.nSamplesPerSec) &&
2771 (dsb->wfx.wBitsPerSample == primarybuf->wfx.wBitsPerSample) &&
2772 (dsb->wfx.nChannels == primarybuf->wfx.nChannels)) {
2773 DWORD bytesleft = dsb->buflen - dsb->buf_mixpos;
2774 TRACE("(%p) Best case\n", dsb);
2775 if (len <= bytesleft )
2776 memcpy(obp, ibp, len);
2778 memcpy(obp, ibp, bytesleft );
2779 memcpy(obp + bytesleft, dsb->buffer, len - bytesleft);
2784 /* Check for same sample rate */
2785 if (dsb->freq == primarybuf->wfx.nSamplesPerSec) {
2786 TRACE("(%p) Same sample rate %ld = primary %ld\n", dsb,
2787 dsb->freq, primarybuf->wfx.nSamplesPerSec);
2789 for (i = 0; i < len; i += oAdvance) {
2790 cp_fields(dsb, ibp, obp );
2794 if (ibp >= (BYTE *)(dsb->buffer + dsb->buflen))
2795 ibp = dsb->buffer; /* wrap */
2800 /* Mix in different sample rates */
2802 /* New PerfectPitch(tm) Technology (c) 1998 Rob Riggs */
2803 /* Patent Pending :-] */
2805 /* Patent enhancements (c) 2000 Ove KÃ¥ven,
2806 * TransGaming Technologies Inc. */
2808 /* FIXME("(%p) Adjusting frequency: %ld -> %ld (need optimization)\n",
2809 dsb, dsb->freq, primarybuf->wfx.nSamplesPerSec);
2811 size = len / oAdvance;
2813 ipos = dsb->buf_mixpos;
2814 for (i = 0; i < size; i++) {
2815 cp_fields(dsb, (dsb->buffer + ipos), obp);
2817 dsb->freqAcc += dsb->freqAdjust;
2818 if (dsb->freqAcc >= (1<<DSOUND_FREQSHIFT)) {
2819 ULONG adv = (dsb->freqAcc>>DSOUND_FREQSHIFT) * iAdvance;
2820 dsb->freqAcc &= (1<<DSOUND_FREQSHIFT)-1;
2821 ipos += adv; ilen += adv;
2822 while (ipos >= dsb->buflen)
2823 ipos -= dsb->buflen;
2829 static void DSOUND_MixerVol(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2831 INT i, inc = primarybuf->wfx.wBitsPerSample >> 3;
2833 INT16 *bps = (INT16 *) buf;
2835 TRACE("(%p) left = %lx, right = %lx\n", dsb,
2836 dsb->volpan.dwTotalLeftAmpFactor, dsb->volpan.dwTotalRightAmpFactor);
2837 if ((!(dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->volpan.lPan == 0)) &&
2838 (!(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->volpan.lVolume == 0)) &&
2839 !(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))
2840 return; /* Nothing to do */
2842 /* If we end up with some bozo coder using panning or 3D sound */
2843 /* with a mono primary buffer, it could sound very weird using */
2844 /* this method. Oh well, tough patooties. */
2846 for (i = 0; i < len; i += inc) {
2852 /* 8-bit WAV is unsigned, but we need to operate */
2853 /* on signed data for this to work properly */
2855 val = ((val * (i & inc ? dsb->volpan.dwTotalRightAmpFactor : dsb->volpan.dwTotalLeftAmpFactor)) >> 16);
2860 /* 16-bit WAV is signed -- much better */
2862 val = ((val * ((i & inc) ? dsb->volpan.dwTotalRightAmpFactor : dsb->volpan.dwTotalLeftAmpFactor)) >> 16);
2868 FIXME("MixerVol had a nasty error\n");
2874 static void DSOUND_Mixer3D(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len)
2877 DWORD buflen, buf_mixpos;
2879 buflen = dsb->ds3db->buflen;
2880 buf_mixpos = (dsb->buf_mixpos * primarybuf->wfx.nBlockAlign) / dsb->wfx.nBlockAlign;
2881 ibp = dsb->ds3db->buffer + buf_mixpos;
2884 if (buf_mixpos > buflen) {
2885 FIXME("Major breakage\n");
2889 if (len <= (buf_mixpos + buflen))
2890 memcpy(obp, ibp, len);
2892 memcpy(obp, ibp, buflen - buf_mixpos);
2893 memcpy(obp + (buflen - buf_mixpos),
2895 len - (buflen - buf_mixpos));
2901 static void *tmp_buffer;
2902 static size_t tmp_buffer_len = 0;
2904 static void *DSOUND_tmpbuffer(size_t len)
2906 if (len>tmp_buffer_len) {
2907 void *new_buffer = realloc(tmp_buffer, len);
2909 tmp_buffer = new_buffer;
2910 tmp_buffer_len = len;
2917 static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD fraglen)
2919 INT i, len, ilen, temp, field;
2920 INT advance = primarybuf->wfx.wBitsPerSample >> 3;
2921 BYTE *buf, *ibuf, *obuf;
2922 INT16 *ibufs, *obufs;
2925 if (!(dsb->playflags & DSBPLAY_LOOPING)) {
2926 temp = MulDiv(primarybuf->wfx.nAvgBytesPerSec, dsb->buflen,
2927 dsb->nAvgBytesPerSec) -
2928 MulDiv(primarybuf->wfx.nAvgBytesPerSec, dsb->buf_mixpos,
2929 dsb->nAvgBytesPerSec);
2930 len = (len > temp) ? temp : len;
2932 len &= ~3; /* 4 byte alignment */
2935 /* This should only happen if we aren't looping and temp < 4 */
2937 /* We skip the remainder, so check for possible events */
2938 DSOUND_CheckEvent(dsb, dsb->buflen - dsb->buf_mixpos);
2940 dsb->state = STATE_STOPPED;
2942 dsb->buf_mixpos = 0;
2943 dsb->leadin = FALSE;
2944 /* Check for DSBPN_OFFSETSTOP */
2945 DSOUND_CheckEvent(dsb, 0);
2949 /* Been seeing segfaults in malloc() for some reason... */
2950 TRACE("allocating buffer (size = %d)\n", len);
2951 if ((buf = ibuf = (BYTE *) DSOUND_tmpbuffer(len)) == NULL)
2954 TRACE("MixInBuffer (%p) len = %d, dest = %ld\n", dsb, len, writepos);
2956 ilen = DSOUND_MixerNorm(dsb, ibuf, len);
2957 if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
2958 (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
2959 DSOUND_MixerVol(dsb, ibuf, len);
2961 obuf = primarybuf->buffer + writepos;
2962 for (i = 0; i < len; i += advance) {
2963 obufs = (INT16 *) obuf;
2964 ibufs = (INT16 *) ibuf;
2965 if (primarybuf->wfx.wBitsPerSample == 8) {
2966 /* 8-bit WAV is unsigned */
2967 field = (*ibuf - 128);
2968 field += (*obuf - 128);
2969 field = field > 127 ? 127 : field;
2970 field = field < -128 ? -128 : field;
2971 *obuf = field + 128;
2973 /* 16-bit WAV is signed */
2976 field = field > 32767 ? 32767 : field;
2977 field = field < -32768 ? -32768 : field;
2982 if (obuf >= (BYTE *)(primarybuf->buffer + primarybuf->buflen))
2983 obuf = primarybuf->buffer;
2987 if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY)
2988 DSOUND_CheckEvent(dsb, ilen);
2990 if (dsb->leadin && (dsb->startpos > dsb->buf_mixpos) && (dsb->startpos <= dsb->buf_mixpos + ilen)) {
2991 /* HACK... leadin should be reset when the PLAY position reaches the startpos,
2992 * not the MIX position... but if the sound buffer is bigger than our prebuffering
2993 * (which must be the case for the streaming buffers that need this hack anyway)
2994 * plus DS_HEL_MARGIN or equivalent, then this ought to work anyway. */
2995 dsb->leadin = FALSE;
2998 dsb->buf_mixpos += ilen;
3000 if (dsb->buf_mixpos >= dsb->buflen) {
3001 if (!(dsb->playflags & DSBPLAY_LOOPING)) {
3002 dsb->state = STATE_STOPPED;
3004 dsb->buf_mixpos = 0;
3005 dsb->leadin = FALSE;
3006 DSOUND_CheckEvent(dsb, 0); /* For DSBPN_OFFSETSTOP */
3009 while (dsb->buf_mixpos >= dsb->buflen)
3010 dsb->buf_mixpos -= dsb->buflen;
3011 if (dsb->leadin && (dsb->startpos <= dsb->buf_mixpos))
3012 dsb->leadin = FALSE; /* HACK: see above */
3019 static void DSOUND_PhaseCancel(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD len)
3022 INT advance = primarybuf->wfx.wBitsPerSample >> 3;
3023 BYTE *buf, *ibuf, *obuf;
3024 INT16 *ibufs, *obufs;
3026 len &= ~3; /* 4 byte alignment */
3028 TRACE("allocating buffer (size = %ld)\n", len);
3029 if ((buf = ibuf = (BYTE *) DSOUND_tmpbuffer(len)) == NULL)
3032 TRACE("PhaseCancel (%p) len = %ld, dest = %ld\n", dsb, len, writepos);
3034 ilen = DSOUND_MixerNorm(dsb, ibuf, len);
3035 if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
3036 (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
3037 DSOUND_MixerVol(dsb, ibuf, len);
3039 /* subtract instead of add, to phase out premixed data */
3040 obuf = primarybuf->buffer + writepos;
3041 for (i = 0; i < len; i += advance) {
3042 obufs = (INT16 *) obuf;
3043 ibufs = (INT16 *) ibuf;
3044 if (primarybuf->wfx.wBitsPerSample == 8) {
3045 /* 8-bit WAV is unsigned */
3046 field = (*ibuf - 128);
3047 field -= (*obuf - 128);
3048 field = field > 127 ? 127 : field;
3049 field = field < -128 ? -128 : field;
3050 *obuf = field + 128;
3052 /* 16-bit WAV is signed */
3055 field = field > 32767 ? 32767 : field;
3056 field = field < -32768 ? -32768 : field;
3061 if (obuf >= (BYTE *)(primarybuf->buffer + primarybuf->buflen))
3062 obuf = primarybuf->buffer;
3067 static void DSOUND_MixCancel(IDirectSoundBufferImpl *dsb, DWORD writepos, BOOL cancel)
3069 DWORD size, flen, len, npos, nlen;
3070 INT iAdvance = dsb->wfx.nBlockAlign;
3071 INT oAdvance = primarybuf->wfx.nBlockAlign;
3072 /* determine amount of premixed data to cancel */
3073 DWORD primary_done =
3074 ((dsb->primary_mixpos < writepos) ? primarybuf->buflen : 0) +
3075 dsb->primary_mixpos - writepos;
3077 TRACE("(%p, %ld), buf_mixpos=%ld\n", dsb, writepos, dsb->buf_mixpos);
3079 /* backtrack the mix position */
3080 size = primary_done / oAdvance;
3081 flen = size * dsb->freqAdjust;
3082 len = (flen >> DSOUND_FREQSHIFT) * iAdvance;
3083 flen &= (1<<DSOUND_FREQSHIFT)-1;
3084 while (dsb->freqAcc < flen) {
3086 dsb->freqAcc += 1<<DSOUND_FREQSHIFT;
3089 npos = ((dsb->buf_mixpos < len) ? dsb->buflen : 0) +
3090 dsb->buf_mixpos - len;
3091 if (dsb->leadin && (dsb->startpos > npos) && (dsb->startpos <= npos + len)) {
3092 /* stop backtracking at startpos */
3093 npos = dsb->startpos;
3094 len = ((dsb->buf_mixpos < npos) ? dsb->buflen : 0) +
3095 dsb->buf_mixpos - npos;
3096 flen = dsb->freqAcc;
3097 nlen = len / dsb->wfx.nBlockAlign;
3098 nlen = ((nlen << DSOUND_FREQSHIFT) + flen) / dsb->freqAdjust;
3099 nlen *= primarybuf->wfx.nBlockAlign;
3101 ((dsb->primary_mixpos < nlen) ? primarybuf->buflen : 0) +
3102 dsb->primary_mixpos - nlen;
3105 dsb->freqAcc -= flen;
3106 dsb->buf_mixpos = npos;
3107 dsb->primary_mixpos = writepos;
3109 TRACE("new buf_mixpos=%ld, primary_mixpos=%ld (len=%ld)\n",
3110 dsb->buf_mixpos, dsb->primary_mixpos, len);
3112 if (cancel) DSOUND_PhaseCancel(dsb, writepos, len);
3115 static void DSOUND_MixCancelAt(IDirectSoundBufferImpl *dsb, DWORD buf_writepos)
3118 DWORD i, size, flen, len, npos, nlen;
3119 INT iAdvance = dsb->wfx.nBlockAlign;
3120 INT oAdvance = primarybuf->wfx.nBlockAlign;
3121 /* determine amount of premixed data to cancel */
3123 ((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) +
3124 dsb->buf_mixpos - buf_writepos;
3127 WARN("(%p, %ld), buf_mixpos=%ld\n", dsb, buf_writepos, dsb->buf_mixpos);
3128 /* since this is not implemented yet, just cancel *ALL* prebuffering for now
3129 * (which is faster anyway when there's one a single secondary buffer) */
3130 primarybuf->need_remix = TRUE;
3133 static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD playpos, DWORD writepos, DWORD mixlen)
3136 /* determine this buffer's write position */
3137 DWORD buf_writepos = DSOUND_CalcPlayPosition(dsb, dsb->state & primarybuf->state, writepos,
3138 writepos, dsb->primary_mixpos, dsb->buf_mixpos);
3139 /* determine how much already-mixed data exists */
3141 ((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) +
3142 dsb->buf_mixpos - buf_writepos;
3143 DWORD primary_done =
3144 ((dsb->primary_mixpos < writepos) ? primarybuf->buflen : 0) +
3145 dsb->primary_mixpos - writepos;
3147 ((primarybuf->buf_mixpos < writepos) ? primarybuf->buflen : 0) +
3148 primarybuf->buf_mixpos - writepos;
3151 TRACE("buf_writepos=%ld, primary_writepos=%ld\n", buf_writepos, writepos);
3152 TRACE("buf_done=%ld, primary_done=%ld\n", buf_done, primary_done);
3153 TRACE("buf_mixpos=%ld, primary_mixpos=%ld, mixlen=%ld\n", dsb->buf_mixpos, dsb->primary_mixpos,
3155 TRACE("looping=%ld, startpos=%ld, leadin=%ld\n", dsb->playflags, dsb->startpos, dsb->leadin);
3157 /* save write position for non-GETCURRENTPOSITION2... */
3158 dsb->playpos = buf_writepos;
3160 /* check whether CalcPlayPosition detected a mixing underrun */
3161 if ((buf_done == 0) && (dsb->primary_mixpos != writepos)) {
3162 /* it did, but did we have more to play? */
3163 if ((dsb->playflags & DSBPLAY_LOOPING) ||
3164 (dsb->buf_mixpos < dsb->buflen)) {
3165 /* yes, have to recover */
3166 ERR("underrun on sound buffer %p\n", dsb);
3167 TRACE("recovering from underrun: primary_mixpos=%ld\n", writepos);
3169 dsb->primary_mixpos = writepos;
3172 /* determine how far ahead we should mix */
3173 if (((dsb->playflags & DSBPLAY_LOOPING) ||
3174 (dsb->leadin && (dsb->probably_valid_to != 0))) &&
3175 !(dsb->dsbd.dwFlags & DSBCAPS_STATIC)) {
3176 /* if this is a streaming buffer, it typically means that
3177 * we should defer mixing past probably_valid_to as long
3178 * as we can, to avoid unnecessary remixing */
3179 /* the heavy-looking calculations shouldn't be that bad,
3180 * as any game isn't likely to be have more than 1 or 2
3181 * streaming buffers in use at any time anyway... */
3182 DWORD probably_valid_left =
3183 (dsb->probably_valid_to == (DWORD)-1) ? dsb->buflen :
3184 ((dsb->probably_valid_to < buf_writepos) ? dsb->buflen : 0) +
3185 dsb->probably_valid_to - buf_writepos;
3186 /* check for leadin condition */
3187 if ((probably_valid_left == 0) &&
3188 (dsb->probably_valid_to == dsb->startpos) &&
3190 probably_valid_left = dsb->buflen;
3191 TRACE("streaming buffer probably_valid_to=%ld, probably_valid_left=%ld\n",
3192 dsb->probably_valid_to, probably_valid_left);
3193 /* check whether the app's time is already up */
3194 if (probably_valid_left < dsb->writelead) {
3195 WARN("probably_valid_to now within writelead, possible streaming underrun\n");
3196 /* once we pass the point of no return,
3197 * no reason to hold back anymore */
3198 dsb->probably_valid_to = (DWORD)-1;
3199 /* we just have to go ahead and mix what we have,
3200 * there's no telling what the app is thinking anyway */
3202 /* adjust for our frequency and our sample size */
3203 probably_valid_left = MulDiv(probably_valid_left,
3204 1 << DSOUND_FREQSHIFT,
3205 dsb->wfx.nBlockAlign * dsb->freqAdjust) *
3206 primarybuf->wfx.nBlockAlign;
3207 /* check whether to clip mix_len */
3208 if (probably_valid_left < mixlen) {
3209 TRACE("clipping to probably_valid_left=%ld\n", probably_valid_left);
3210 mixlen = probably_valid_left;
3214 /* cut mixlen with what's already been mixed */
3215 if (mixlen < primary_done) {
3216 /* huh? and still CalcPlayPosition didn't
3217 * detect an underrun? */
3218 FIXME("problem with underrun detection (mixlen=%ld < primary_done=%ld)\n", mixlen, primary_done);
3221 len = mixlen - primary_done;
3222 TRACE("remaining mixlen=%ld\n", len);
3224 if (len < primarybuf->dsound->fraglen) {
3225 /* smaller than a fragment, wait until it gets larger
3226 * before we take the mixing overhead */
3227 TRACE("mixlen not worth it, deferring mixing\n");
3231 /* ok, we know how much to mix, let's go */
3232 still_behind = (adv_done > primary_done);
3234 slen = primarybuf->buflen - dsb->primary_mixpos;
3235 if (slen > len) slen = len;
3236 slen = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, slen);
3238 if ((dsb->primary_mixpos < primarybuf->buf_mixpos) &&
3239 (dsb->primary_mixpos + slen >= primarybuf->buf_mixpos))
3240 still_behind = FALSE;
3242 dsb->primary_mixpos += slen; len -= slen;
3243 while (dsb->primary_mixpos >= primarybuf->buflen)
3244 dsb->primary_mixpos -= primarybuf->buflen;
3246 if ((dsb->state == STATE_STOPPED) || !slen) break;
3248 TRACE("new primary_mixpos=%ld, primary_advbase=%ld\n", dsb->primary_mixpos, primarybuf->buf_mixpos);
3249 TRACE("mixed data len=%ld, still_behind=%d\n", mixlen-len, still_behind);
3250 /* return how far we think the primary buffer can
3251 * advance its underrun detector...*/
3252 if (still_behind) return 0;
3253 if ((mixlen - len) < primary_done) return 0;
3254 slen = ((dsb->primary_mixpos < primarybuf->buf_mixpos) ?
3255 primarybuf->buflen : 0) + dsb->primary_mixpos -
3256 primarybuf->buf_mixpos;
3257 if (slen > mixlen) {
3258 /* the primary_done and still_behind checks above should have worked */
3259 FIXME("problem with advancement calculation (advlen=%ld > mixlen=%ld)\n", slen, mixlen);
3265 static DWORD DSOUND_MixToPrimary(DWORD playpos, DWORD writepos, DWORD mixlen, BOOL recover)
3267 INT i, len, maxlen = 0;
3268 IDirectSoundBufferImpl *dsb;
3270 TRACE("(%ld,%ld,%ld)\n", playpos, writepos, mixlen);
3271 for (i = dsound->nrofbuffers - 1; i >= 0; i--) {
3272 dsb = dsound->buffers[i];
3274 if (!dsb || !(ICOM_VTBL(dsb)))
3276 if (dsb->buflen && dsb->state && !dsb->hwbuf) {
3277 TRACE("Checking %p, mixlen=%ld\n", dsb, mixlen);
3278 EnterCriticalSection(&(dsb->lock));
3279 if (dsb->state == STATE_STOPPING) {
3280 DSOUND_MixCancel(dsb, writepos, TRUE);
3281 dsb->state = STATE_STOPPED;
3283 if ((dsb->state == STATE_STARTING) || recover)
3284 dsb->primary_mixpos = writepos;
3285 len = DSOUND_MixOne(dsb, playpos, writepos, mixlen);
3286 if (dsb->state == STATE_STARTING)
3287 dsb->state = STATE_PLAYING;
3288 maxlen = (len > maxlen) ? len : maxlen;
3290 LeaveCriticalSection(&(dsb->lock));
3297 static void DSOUND_MixReset(DWORD writepos)
3300 IDirectSoundBufferImpl *dsb;
3303 TRACE("(%ld)\n", writepos);
3305 /* the sound of silence */
3306 nfiller = primarybuf->wfx.wBitsPerSample == 8 ? 128 : 0;
3308 /* reset all buffer mix positions */
3309 for (i = dsound->nrofbuffers - 1; i >= 0; i--) {
3310 dsb = dsound->buffers[i];
3312 if (!dsb || !(ICOM_VTBL(dsb)))
3314 if (dsb->buflen && dsb->state && !dsb->hwbuf) {
3315 TRACE("Resetting %p\n", dsb);
3316 EnterCriticalSection(&(dsb->lock));
3317 if (dsb->state == STATE_STOPPING) {
3318 dsb->state = STATE_STOPPED;
3320 else if (dsb->state == STATE_STARTING) {
3323 DSOUND_MixCancel(dsb, writepos, FALSE);
3325 LeaveCriticalSection(&(dsb->lock));
3329 /* wipe out premixed data */
3330 if (primarybuf->buf_mixpos < writepos) {
3331 memset(primarybuf->buffer + writepos, nfiller, primarybuf->buflen - writepos);
3332 memset(primarybuf->buffer, nfiller, primarybuf->buf_mixpos);
3334 memset(primarybuf->buffer + writepos, nfiller, primarybuf->buf_mixpos - writepos);
3337 /* reset primary mix position */
3338 primarybuf->buf_mixpos = writepos;
3341 static void DSOUND_CheckReset(IDirectSoundImpl *dsound, DWORD writepos)
3343 if (primarybuf->need_remix) {
3344 DSOUND_MixReset(writepos);
3345 primarybuf->need_remix = FALSE;
3346 /* maximize Half-Life performance */
3347 dsound->prebuf = ds_snd_queue_min;
3349 /* if (dsound->prebuf < ds_snd_queue_max) dsound->prebuf++; */
3351 TRACE("premix adjust: %d\n", dsound->prebuf);
3354 static void DSOUND_WaveQueue(IDirectSoundImpl *dsound, DWORD mixq)
3356 if (mixq + dsound->pwqueue > ds_hel_queue) mixq = ds_hel_queue - dsound->pwqueue;
3357 TRACE("queueing %ld buffers, starting at %d\n", mixq, dsound->pwwrite);
3358 for (; mixq; mixq--) {
3359 waveOutWrite(dsound->hwo, dsound->pwave[dsound->pwwrite], sizeof(WAVEHDR));
3361 if (dsound->pwwrite >= DS_HEL_FRAGS) dsound->pwwrite = 0;
3366 /* #define SYNC_CALLBACK */
3368 static void DSOUND_PerformMix(void)
3374 EnterCriticalSection(&(dsound->lock));
3376 if (!primarybuf || !primarybuf->ref) {
3377 /* seems the primary buffer is currently being released */
3378 LeaveCriticalSection(&(dsound->lock));
3382 /* the sound of silence */
3383 nfiller = primarybuf->wfx.wBitsPerSample == 8 ? 128 : 0;
3385 /* whether the primary is forced to play even without secondary buffers */
3386 forced = ((primarybuf->state == STATE_PLAYING) || (primarybuf->state == STATE_STARTING));
3388 TRACE("entering at %ld\n", GetTickCount());
3389 if (dsound->priolevel != DSSCL_WRITEPRIMARY) {
3390 BOOL paused = ((primarybuf->state == STATE_STOPPED) || (primarybuf->state == STATE_STARTING));
3391 /* FIXME: document variables */
3392 DWORD playpos, writepos, inq, maxq, frag;
3393 if (primarybuf->hwbuf) {
3394 hres = IDsDriverBuffer_GetPosition(primarybuf->hwbuf, &playpos, &writepos);
3396 LeaveCriticalSection(&(dsound->lock));
3399 /* Well, we *could* do Just-In-Time mixing using the writepos,
3400 * but that's a little bit ambitious and unnecessary... */
3401 /* rather add our safety margin to the writepos, if we're playing */
3403 writepos += primarybuf->writelead;
3404 while (writepos >= primarybuf->buflen)
3405 writepos -= primarybuf->buflen;
3406 } else writepos = playpos;
3409 playpos = dsound->pwplay * dsound->fraglen;
3412 writepos += ds_hel_margin * dsound->fraglen;
3413 while (writepos >= primarybuf->buflen)
3414 writepos -= primarybuf->buflen;
3417 TRACE("primary playpos=%ld, writepos=%ld, clrpos=%ld, mixpos=%ld\n",
3418 playpos,writepos,primarybuf->playpos,primarybuf->buf_mixpos);
3419 /* wipe out just-played sound data */
3420 if (playpos < primarybuf->playpos) {
3421 memset(primarybuf->buffer + primarybuf->playpos, nfiller, primarybuf->buflen - primarybuf->playpos);
3422 memset(primarybuf->buffer, nfiller, playpos);
3424 memset(primarybuf->buffer + primarybuf->playpos, nfiller, playpos - primarybuf->playpos);
3426 primarybuf->playpos = playpos;
3428 EnterCriticalSection(&(primarybuf->lock));
3430 /* reset mixing if necessary */
3431 DSOUND_CheckReset(dsound, writepos);
3433 /* check how much prebuffering is left */
3434 inq = primarybuf->buf_mixpos;
3436 inq += primarybuf->buflen;
3439 /* find the maximum we can prebuffer */
3442 if (maxq < writepos)
3443 maxq += primarybuf->buflen;
3445 } else maxq = primarybuf->buflen;
3447 /* clip maxq to dsound->prebuf */
3448 frag = dsound->prebuf * dsound->fraglen;
3449 if (maxq > frag) maxq = frag;
3451 /* check for consistency */
3453 /* the playback position must have passed our last
3454 * mixed position, i.e. it's an underrun, or we have
3455 * nothing more to play */
3456 TRACE("reached end of mixed data (inq=%ld, maxq=%ld)\n", inq, maxq);
3458 /* stop the playback now, to allow buffers to refill */
3459 if (primarybuf->state == STATE_PLAYING) {
3460 primarybuf->state = STATE_STARTING;
3462 else if (primarybuf->state == STATE_STOPPING) {
3463 primarybuf->state = STATE_STOPPED;
3466 /* how can we have an underrun if we aren't playing? */
3467 WARN("unexpected primary state (%ld)\n", primarybuf->state);
3469 #ifdef SYNC_CALLBACK
3470 /* DSOUND_callback may need this lock */
3471 LeaveCriticalSection(&(primarybuf->lock));
3473 DSOUND_PrimaryStop(primarybuf);
3474 #ifdef SYNC_CALLBACK
3475 EnterCriticalSection(&(primarybuf->lock));
3477 if (primarybuf->hwbuf) {
3478 /* the Stop is supposed to reset play position to beginning of buffer */
3479 /* unfortunately, OSS is not able to do so, so get current pointer */
3480 hres = IDsDriverBuffer_GetPosition(primarybuf->hwbuf, &playpos, NULL);
3482 LeaveCriticalSection(&(dsound->lock));
3483 LeaveCriticalSection(&(primarybuf->lock));
3487 playpos = dsound->pwplay * dsound->fraglen;
3490 primarybuf->playpos = playpos;
3491 primarybuf->buf_mixpos = writepos;
3493 maxq = primarybuf->buflen;
3494 if (maxq > frag) maxq = frag;
3495 memset(primarybuf->buffer, nfiller, primarybuf->buflen);
3500 frag = DSOUND_MixToPrimary(playpos, writepos, maxq, paused);
3501 if (forced) frag = maxq - inq;
3502 primarybuf->buf_mixpos += frag;
3503 while (primarybuf->buf_mixpos >= primarybuf->buflen)
3504 primarybuf->buf_mixpos -= primarybuf->buflen;
3507 /* buffers have been filled, restart playback */
3508 if (primarybuf->state == STATE_STARTING) {
3509 primarybuf->state = STATE_PLAYING;
3511 else if (primarybuf->state == STATE_STOPPED) {
3512 /* the primarybuf is supposed to play if there's something to play
3513 * even if it is reported as stopped, so don't let this confuse you */
3514 primarybuf->state = STATE_STOPPING;
3516 LeaveCriticalSection(&(primarybuf->lock));
3518 DSOUND_PrimaryPlay(primarybuf);
3519 TRACE("starting playback\n");
3523 LeaveCriticalSection(&(primarybuf->lock));
3525 /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */
3526 if (primarybuf->state == STATE_STARTING) {
3527 DSOUND_PrimaryPlay(primarybuf);
3528 primarybuf->state = STATE_PLAYING;
3530 else if (primarybuf->state == STATE_STOPPING) {
3531 DSOUND_PrimaryStop(primarybuf);
3532 primarybuf->state = STATE_STOPPED;
3535 TRACE("completed processing at %ld\n", GetTickCount());
3536 LeaveCriticalSection(&(dsound->lock));
3539 static void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
3541 if (!dsound || !primarybuf) {
3542 ERR("dsound died without killing us?\n");
3543 timeKillEvent(timerID);
3544 timeEndPeriod(DS_TIME_RES);
3549 DSOUND_PerformMix();
3552 static void CALLBACK DSOUND_callback(HWAVEOUT hwo, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
3554 IDirectSoundImpl* This = (IDirectSoundImpl*)dwUser;
3555 TRACE("entering at %ld, msg=%08x\n", GetTickCount(), msg);
3556 if (msg == MM_WOM_DONE) {
3557 DWORD inq, mixq, fraglen, buflen, pwplay, playpos, mixpos;
3558 if (This->pwqueue == (DWORD)-1) {
3559 TRACE("completed due to reset\n");
3562 /* it could be a bad idea to enter critical section here... if there's lock contention,
3563 * the resulting scheduling delays might obstruct the winmm player thread */
3564 #ifdef SYNC_CALLBACK
3565 EnterCriticalSection(&(primarybuf->lock));
3567 /* retrieve current values */
3568 fraglen = dsound->fraglen;
3569 buflen = primarybuf->buflen;
3570 pwplay = dsound->pwplay;
3571 playpos = pwplay * fraglen;
3572 mixpos = primarybuf->buf_mixpos;
3573 /* check remaining mixed data */
3574 inq = ((mixpos < playpos) ? buflen : 0) + mixpos - playpos;
3575 mixq = inq / fraglen;
3576 if ((inq - (mixq * fraglen)) > 0) mixq++;
3577 /* complete the playing buffer */
3578 TRACE("done playing primary pos=%ld\n", playpos);
3580 if (pwplay >= DS_HEL_FRAGS) pwplay = 0;
3581 /* write new values */
3582 dsound->pwplay = pwplay;
3584 /* queue new buffer if we have data for it */
3585 if (inq>1) DSOUND_WaveQueue(This, inq-1);
3586 #ifdef SYNC_CALLBACK
3587 LeaveCriticalSection(&(primarybuf->lock));
3590 TRACE("completed\n");
3593 /*******************************************************************************
3594 * DirectSoundCreate (DSOUND.1)
3596 HRESULT WINAPI DirectSoundCreate(REFGUID lpGUID,LPDIRECTSOUND *ppDS,IUnknown *pUnkOuter )
3598 IDirectSoundImpl** ippDS=(IDirectSoundImpl**)ppDS;
3599 PIDSDRIVER drv = NULL;
3602 HRESULT err = DS_OK;
3605 TRACE("(%p,%p,%p)\n",lpGUID,ippDS,pUnkOuter);
3607 TRACE("DirectSoundCreate (%p)\n", ippDS);
3610 return DSERR_INVALIDPARAM;
3613 IDirectSound_AddRef((LPDIRECTSOUND)dsound);
3618 /* get dsound configuration */
3619 setup_dsound_options();
3621 /* Enumerate WINMM audio devices and find the one we want */
3622 wodn = waveOutGetNumDevs();
3623 if (!wodn) return DSERR_NODRIVER;
3625 /* FIXME: How do we find the GUID of an audio device? */
3626 wod = 0; /* start at the first audio device */
3628 /* Get output device caps */
3629 waveOutGetDevCapsA(wod, &wcaps, sizeof(wcaps));
3630 /* DRV_QUERYDSOUNDIFACE is a "Wine extension" to get the DSound interface */
3631 waveOutMessage(wod, DRV_QUERYDSOUNDIFACE, (DWORD)&drv, 0);
3633 /* Allocate memory */
3634 *ippDS = (IDirectSoundImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSoundImpl));
3636 return DSERR_OUTOFMEMORY;
3638 ICOM_VTBL(*ippDS) = &dsvt;
3641 (*ippDS)->driver = drv;
3642 (*ippDS)->fraglen = 0;
3643 (*ippDS)->priolevel = DSSCL_NORMAL;
3644 (*ippDS)->nrofbuffers = 0;
3645 (*ippDS)->buffers = NULL;
3646 (*ippDS)->primary = NULL;
3647 (*ippDS)->listener = NULL;
3649 (*ippDS)->prebuf = ds_snd_queue_max;
3651 /* Get driver description */
3653 IDsDriver_GetDriverDesc(drv,&((*ippDS)->drvdesc));
3655 /* if no DirectSound interface available, use WINMM API instead */
3656 (*ippDS)->drvdesc.dwFlags = DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT;
3657 (*ippDS)->drvdesc.dnDevNode = wod; /* FIXME? */
3660 /* Set default wave format (may need it for waveOutOpen) */
3661 (*ippDS)->wfx.wFormatTag = WAVE_FORMAT_PCM;
3662 (*ippDS)->wfx.nChannels = 2;
3663 (*ippDS)->wfx.nSamplesPerSec = 22050;
3664 (*ippDS)->wfx.nAvgBytesPerSec = 44100;
3665 (*ippDS)->wfx.nBlockAlign = 2;
3666 (*ippDS)->wfx.wBitsPerSample = 8;
3668 /* If the driver requests being opened through MMSYSTEM
3669 * (which is recommended by the DDK), it is supposed to happen
3670 * before the DirectSound interface is opened */
3671 if ((*ippDS)->drvdesc.dwFlags & DSDDESC_DOMMSYSTEMOPEN)
3673 /* FIXME: is this right? */
3675 (*ippDS)->drvdesc.dnDevNode = 0;
3676 err = DSERR_ALLOCATED;
3678 /* if this device is busy try the next one */
3679 while((err == DSERR_ALLOCATED) &&
3680 ((*ippDS)->drvdesc.dnDevNode < wodn))
3682 err = mmErr(waveOutOpen(&((*ippDS)->hwo),
3683 (*ippDS)->drvdesc.dnDevNode, &((*ippDS)->wfx),
3684 (DWORD)DSOUND_callback, (DWORD)(*ippDS),
3685 CALLBACK_FUNCTION | WAVE_DIRECTSOUND));
3686 (*ippDS)->drvdesc.dnDevNode++; /* next wave device */
3689 (*ippDS)->drvdesc.dnDevNode--; /* take away last increment */
3693 if (drv && (err == DS_OK))
3694 err = IDsDriver_Open(drv);
3696 /* FIXME: do we want to handle a temporarily busy device? */
3698 HeapFree(GetProcessHeap(),0,*ippDS);
3703 /* the driver is now open, so it's now allowed to call GetCaps */
3705 IDsDriver_GetCaps(drv,&((*ippDS)->drvcaps));
3710 /* FIXME: look at wcaps */
3711 (*ippDS)->drvcaps.dwFlags =
3712 DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO;
3714 (*ippDS)->drvcaps.dwFlags |= DSCAPS_EMULDRIVER;
3716 /* Allocate memory for HEL buffer headers */
3717 for (c=0; c<DS_HEL_FRAGS; c++) {
3718 (*ippDS)->pwave[c] = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEHDR));
3719 if (!(*ippDS)->pwave[c]) {
3720 /* Argh, out of memory */
3722 HeapFree(GetProcessHeap(),0,(*ippDS)->pwave[c]);
3723 waveOutClose((*ippDS)->hwo);
3724 HeapFree(GetProcessHeap(),0,*ippDS);
3726 return DSERR_OUTOFMEMORY;
3732 InitializeCriticalSection(&((*ippDS)->lock));
3736 if (primarybuf == NULL) {
3740 dsbd.dwSize = sizeof(DSBUFFERDESC);
3741 dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
3742 dsbd.dwBufferBytes = 0;
3743 dsbd.lpwfxFormat = &(dsound->wfx);
3744 hr = IDirectSound_CreateSoundBuffer(*ppDS, &dsbd, (LPDIRECTSOUNDBUFFER*)&primarybuf, NULL);
3748 /* dsound->primary is NULL - don't need to Release */
3749 dsound->primary = primarybuf;
3750 IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)primarybuf);
3752 timeBeginPeriod(DS_TIME_RES);
3753 dsound->timerID = timeSetEvent(DS_TIME_DEL, DS_TIME_RES, DSOUND_timer,
3754 (DWORD)dsound, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
3759 /***************************************************************************
3760 * DirectSoundCaptureCreate [DSOUND.6]
3762 * Create and initialize a DirectSoundCapture interface
3766 * Failure: DSERR_NOAGGREGATION, DSERR_ALLOCATED, DSERR_INVALIDPARAM,
3769 HRESULT WINAPI DirectSoundCaptureCreate(
3771 LPDIRECTSOUNDCAPTURE* lplpDSC,
3772 LPUNKNOWN pUnkOuter )
3774 TRACE("(%s,%p,%p)\n", debugstr_guid(lpcGUID), lplpDSC, pUnkOuter);
3777 return DSERR_NOAGGREGATION;
3780 /* Default device? */
3782 return DSOUND_CreateDirectSoundCapture( (LPVOID*)lplpDSC );
3785 FIXME( "Unknown GUID %s\n", debugstr_guid(lpcGUID) );
3788 return DSERR_OUTOFMEMORY;
3791 /***************************************************************************
3792 * DirectSoundCaptureEnumerateA [DSOUND.7]
3794 * Enumerate all DirectSound drivers installed in the system
3798 * Failure: DSERR_INVALIDPARAM
3800 HRESULT WINAPI DirectSoundCaptureEnumerateA(
3801 LPDSENUMCALLBACKA lpDSEnumCallback,
3804 TRACE("(%p,%p)\n", lpDSEnumCallback, lpContext );
3806 if ( lpDSEnumCallback )
3807 lpDSEnumCallback(NULL,"WINE Primary Sound Capture Driver",
3808 "SoundCap",lpContext);
3814 /***************************************************************************
3815 * DirectSoundCaptureEnumerateW [DSOUND.8]
3817 * Enumerate all DirectSound drivers installed in the system
3821 * Failure: DSERR_INVALIDPARAM
3823 HRESULT WINAPI DirectSoundCaptureEnumerateW(
3824 LPDSENUMCALLBACKW lpDSEnumCallback,
3827 FIXME("(%p,%p):stub\n", lpDSEnumCallback, lpContext );
3832 DSOUND_CreateDirectSoundCapture( LPVOID* ppobj )
3834 *ppobj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( IDirectSoundCaptureImpl ) );
3836 if ( *ppobj == NULL ) {
3837 return DSERR_OUTOFMEMORY;
3841 ICOM_THIS(IDirectSoundCaptureImpl,*ppobj);
3844 ICOM_VTBL(This) = &dscvt;
3846 InitializeCriticalSection( &This->lock );
3852 static HRESULT WINAPI
3853 IDirectSoundCaptureImpl_QueryInterface(
3854 LPDIRECTSOUNDCAPTURE iface,
3858 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3860 FIXME( "(%p)->(%s,%p): stub\n", This, debugstr_guid(riid), ppobj );
3866 IDirectSoundCaptureImpl_AddRef( LPDIRECTSOUNDCAPTURE iface )
3869 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3871 EnterCriticalSection( &This->lock );
3873 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3874 uRef = ++(This->ref);
3876 LeaveCriticalSection( &This->lock );
3882 IDirectSoundCaptureImpl_Release( LPDIRECTSOUNDCAPTURE iface )
3885 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3887 EnterCriticalSection( &This->lock );
3889 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
3890 uRef = --(This->ref);
3892 LeaveCriticalSection( &This->lock );
3895 DeleteCriticalSection( &This->lock );
3896 HeapFree( GetProcessHeap(), 0, This );
3902 static HRESULT WINAPI
3903 IDirectSoundCaptureImpl_CreateCaptureBuffer(
3904 LPDIRECTSOUNDCAPTURE iface,
3905 LPCDSCBUFFERDESC lpcDSCBufferDesc,
3906 LPDIRECTSOUNDCAPTUREBUFFER* lplpDSCaptureBuffer,
3910 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3912 TRACE( "(%p)->(%p,%p,%p)\n", This, lpcDSCBufferDesc, lplpDSCaptureBuffer, pUnk );
3915 return DSERR_INVALIDPARAM;
3918 hr = DSOUND_CreateDirectSoundCaptureBuffer( lpcDSCBufferDesc, (LPVOID*)lplpDSCaptureBuffer );
3923 static HRESULT WINAPI
3924 IDirectSoundCaptureImpl_GetCaps(
3925 LPDIRECTSOUNDCAPTURE iface,
3926 LPDSCCAPS lpDSCCaps )
3928 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3930 FIXME( "(%p)->(%p): stub\n", This, lpDSCCaps );
3935 static HRESULT WINAPI
3936 IDirectSoundCaptureImpl_Initialize(
3937 LPDIRECTSOUNDCAPTURE iface,
3940 ICOM_THIS(IDirectSoundCaptureImpl,iface);
3942 FIXME( "(%p)->(%p): stub\n", This, lpcGUID );
3948 static ICOM_VTABLE(IDirectSoundCapture) dscvt =
3950 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
3951 /* IUnknown methods */
3952 IDirectSoundCaptureImpl_QueryInterface,
3953 IDirectSoundCaptureImpl_AddRef,
3954 IDirectSoundCaptureImpl_Release,
3956 /* IDirectSoundCapture methods */
3957 IDirectSoundCaptureImpl_CreateCaptureBuffer,
3958 IDirectSoundCaptureImpl_GetCaps,
3959 IDirectSoundCaptureImpl_Initialize
3963 DSOUND_CreateDirectSoundCaptureBuffer( LPCDSCBUFFERDESC lpcDSCBufferDesc, LPVOID* ppobj )
3966 FIXME( "(%p,%p): ignoring lpcDSCBufferDesc\n", lpcDSCBufferDesc, ppobj );
3968 *ppobj = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( IDirectSoundCaptureBufferImpl ) );
3970 if ( *ppobj == NULL ) {
3971 return DSERR_OUTOFMEMORY;
3975 ICOM_THIS(IDirectSoundCaptureBufferImpl,*ppobj);
3978 ICOM_VTBL(This) = &dscbvt;
3980 InitializeCriticalSection( &This->lock );
3987 static HRESULT WINAPI
3988 IDirectSoundCaptureBufferImpl_QueryInterface(
3989 LPDIRECTSOUNDCAPTUREBUFFER iface,
3993 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
3995 FIXME( "(%p)->(%s,%p): stub\n", This, debugstr_guid(riid), ppobj );
4001 IDirectSoundCaptureBufferImpl_AddRef( LPDIRECTSOUNDCAPTUREBUFFER iface )
4004 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4006 EnterCriticalSection( &This->lock );
4008 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
4009 uRef = ++(This->ref);
4011 LeaveCriticalSection( &This->lock );
4017 IDirectSoundCaptureBufferImpl_Release( LPDIRECTSOUNDCAPTUREBUFFER iface )
4020 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4022 EnterCriticalSection( &This->lock );
4024 TRACE( "(%p) was 0x%08lx\n", This, This->ref );
4025 uRef = --(This->ref);
4027 LeaveCriticalSection( &This->lock );
4030 DeleteCriticalSection( &This->lock );
4031 HeapFree( GetProcessHeap(), 0, This );
4037 static HRESULT WINAPI
4038 IDirectSoundCaptureBufferImpl_GetCaps(
4039 LPDIRECTSOUNDCAPTUREBUFFER iface,
4040 LPDSCBCAPS lpDSCBCaps )
4042 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4044 FIXME( "(%p)->(%p): stub\n", This, lpDSCBCaps );
4049 static HRESULT WINAPI
4050 IDirectSoundCaptureBufferImpl_GetCurrentPosition(
4051 LPDIRECTSOUNDCAPTUREBUFFER iface,
4052 LPDWORD lpdwCapturePosition,
4053 LPDWORD lpdwReadPosition )
4055 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4057 FIXME( "(%p)->(%p,%p): stub\n", This, lpdwCapturePosition, lpdwReadPosition );
4062 static HRESULT WINAPI
4063 IDirectSoundCaptureBufferImpl_GetFormat(
4064 LPDIRECTSOUNDCAPTUREBUFFER iface,
4065 LPWAVEFORMATEX lpwfxFormat,
4066 DWORD dwSizeAllocated,
4067 LPDWORD lpdwSizeWritten )
4069 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4071 FIXME( "(%p)->(%p,0x%08lx,%p): stub\n", This, lpwfxFormat, dwSizeAllocated, lpdwSizeWritten );
4076 static HRESULT WINAPI
4077 IDirectSoundCaptureBufferImpl_GetStatus(
4078 LPDIRECTSOUNDCAPTUREBUFFER iface,
4079 LPDWORD lpdwStatus )
4081 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4083 FIXME( "(%p)->(%p): stub\n", This, lpdwStatus );
4088 static HRESULT WINAPI
4089 IDirectSoundCaptureBufferImpl_Initialize(
4090 LPDIRECTSOUNDCAPTUREBUFFER iface,
4091 LPDIRECTSOUNDCAPTURE lpDSC,
4092 LPCDSCBUFFERDESC lpcDSCBDesc )
4094 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4096 FIXME( "(%p)->(%p,%p): stub\n", This, lpDSC, lpcDSCBDesc );
4101 static HRESULT WINAPI
4102 IDirectSoundCaptureBufferImpl_Lock(
4103 LPDIRECTSOUNDCAPTUREBUFFER iface,
4106 LPVOID* lplpvAudioPtr1,
4107 LPDWORD lpdwAudioBytes1,
4108 LPVOID* lplpvAudioPtr2,
4109 LPDWORD lpdwAudioBytes2,
4112 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4114 FIXME( "(%p)->(%08lu,%08lu,%p,%p,%p,%p,0x%08lx): stub\n", This, dwReadCusor, dwReadBytes, lplpvAudioPtr1, lpdwAudioBytes1, lplpvAudioPtr2, lpdwAudioBytes2, dwFlags );
4119 static HRESULT WINAPI
4120 IDirectSoundCaptureBufferImpl_Start(
4121 LPDIRECTSOUNDCAPTUREBUFFER iface,
4124 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4126 FIXME( "(%p)->(0x%08lx): stub\n", This, dwFlags );
4131 static HRESULT WINAPI
4132 IDirectSoundCaptureBufferImpl_Stop( LPDIRECTSOUNDCAPTUREBUFFER iface )
4134 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4136 FIXME( "(%p): stub\n", This );
4141 static HRESULT WINAPI
4142 IDirectSoundCaptureBufferImpl_Unlock(
4143 LPDIRECTSOUNDCAPTUREBUFFER iface,
4144 LPVOID lpvAudioPtr1,
4145 DWORD dwAudioBytes1,
4146 LPVOID lpvAudioPtr2,
4147 DWORD dwAudioBytes2 )
4149 ICOM_THIS(IDirectSoundCaptureBufferImpl,iface);
4151 FIXME( "(%p)->(%p,%08lu,%p,%08lu): stub\n", This, lpvAudioPtr1, dwAudioBytes1, lpvAudioPtr2, dwAudioBytes2 );
4157 static ICOM_VTABLE(IDirectSoundCaptureBuffer) dscbvt =
4159 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
4160 /* IUnknown methods */
4161 IDirectSoundCaptureBufferImpl_QueryInterface,
4162 IDirectSoundCaptureBufferImpl_AddRef,
4163 IDirectSoundCaptureBufferImpl_Release,
4165 /* IDirectSoundCaptureBuffer methods */
4166 IDirectSoundCaptureBufferImpl_GetCaps,
4167 IDirectSoundCaptureBufferImpl_GetCurrentPosition,
4168 IDirectSoundCaptureBufferImpl_GetFormat,
4169 IDirectSoundCaptureBufferImpl_GetStatus,
4170 IDirectSoundCaptureBufferImpl_Initialize,
4171 IDirectSoundCaptureBufferImpl_Lock,
4172 IDirectSoundCaptureBufferImpl_Start,
4173 IDirectSoundCaptureBufferImpl_Stop,
4174 IDirectSoundCaptureBufferImpl_Unlock
4177 /*******************************************************************************
4178 * DirectSound ClassFactory
4182 /* IUnknown fields */
4183 ICOM_VFIELD(IClassFactory);
4185 } IClassFactoryImpl;
4187 static HRESULT WINAPI
4188 DSCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) {
4189 ICOM_THIS(IClassFactoryImpl,iface);
4191 FIXME("(%p)->(%s,%p),stub!\n",This,debugstr_guid(riid),ppobj);
4192 return E_NOINTERFACE;
4196 DSCF_AddRef(LPCLASSFACTORY iface) {
4197 ICOM_THIS(IClassFactoryImpl,iface);
4198 return ++(This->ref);
4201 static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface) {
4202 ICOM_THIS(IClassFactoryImpl,iface);
4203 /* static class, won't be freed */
4204 return --(This->ref);
4207 static HRESULT WINAPI DSCF_CreateInstance(
4208 LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj
4210 ICOM_THIS(IClassFactoryImpl,iface);
4212 TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,debugstr_guid(riid),ppobj);
4213 if ( IsEqualGUID( &IID_IDirectSound, riid ) ) {
4214 /* FIXME: reuse already created dsound if present? */
4215 return DirectSoundCreate(riid,(LPDIRECTSOUND*)ppobj,pOuter);
4217 return E_NOINTERFACE;
4220 static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) {
4221 ICOM_THIS(IClassFactoryImpl,iface);
4222 FIXME("(%p)->(%d),stub!\n",This,dolock);
4226 static ICOM_VTABLE(IClassFactory) DSCF_Vtbl = {
4227 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
4228 DSCF_QueryInterface,
4231 DSCF_CreateInstance,
4234 static IClassFactoryImpl DSOUND_CF = {&DSCF_Vtbl, 1 };
4236 /*******************************************************************************
4237 * DllGetClassObject [DSOUND.5]
4238 * Retrieves class object from a DLL object
4241 * Docs say returns STDAPI
4244 * rclsid [I] CLSID for the class object
4245 * riid [I] Reference to identifier of interface for class object
4246 * ppv [O] Address of variable to receive interface pointer for riid
4250 * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG,
4253 DWORD WINAPI DSOUND_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv)
4255 TRACE("(%p,%p,%p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
4256 if ( IsEqualCLSID( &IID_IClassFactory, riid ) ) {
4257 *ppv = (LPVOID)&DSOUND_CF;
4258 IClassFactory_AddRef((IClassFactory*)*ppv);
4262 FIXME("(%p,%p,%p): no interface found.\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
4263 return CLASS_E_CLASSNOTAVAILABLE;
4267 /*******************************************************************************
4268 * DllCanUnloadNow [DSOUND.4] Determines whether the DLL is in use.
4274 DWORD WINAPI DSOUND_DllCanUnloadNow(void)
4276 FIXME("(void): stub\n");