/* DirectSound * * Copyright 1998 Marcus Meissner * Copyright 1998 Rob Riggs */ /* * Note: This file requires multithread ability. It is not possible to * implement the stuff in a single thread anyway. And most DirectX apps * require threading themselves. * * Most thread locking is complete. There may be a few race * conditions still lurking. * * Tested with a Soundblaster clone, a Gravis UltraSound Classic, * and a Turtle Beach Tropez+. * * TODO: * Implement DirectSoundCapture API * Implement SetCooperativeLevel properly (need to address focus issues) * Use wavetable synth for static buffers if available * Implement DirectSound3DBuffers (stubs in place) * Use hardware 3D support if available (OSS support may be needed first) * Add support for APIs other than OSS: ALSA (http://alsa.jcu.cz/) * and esound (http://www.gnome.org), for instance * * FIXME: Status needs updating. * * Status: * - Wing Commander 4/W95: * The intromovie plays without problems. Nearly lipsynchron. * - DiscWorld 2 * The sound works, but noticeable chunks are left out (from the sound and * the animation). Don't know why yet. * - Diablo: * Sound works, but slows down the movieplayer. * - XvT: * Doesn't sound yet. * - Monkey Island 3: * The background sound of the startscreen works ;) * - WingCommander Prophecy Demo: * Sound works for the intromovie. * - Total Annihilation (1998/12/04): * Sound plays perfectly in the game, but the Smacker movies * (http://www.smacker.com/) play silently. * - A-10 Cuba! Demo (1998/12/04): * Sound works properly (for some people). * - dsstream.exe, from DirectX 5.2 SDK (1998/12/04): * Works properly, but requires "-dll -winmm". * - dsshow.exe, from DirectX 5.2 SDK (1998/12/04): * Initializes the DLL properly with CoCreateInstance(), but the * FileOpen dialog box is broken - could not test properly */ #include "config.h" #include <assert.h> #include <errno.h> #include <sys/types.h> #include <sys/time.h> #include <sys/fcntl.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <math.h> /* Insomnia - pow() function */ #include "dsound.h" #include "winuser.h" #include "winerror.h" #include "wine/obj_base.h" #include "thread.h" #include "debugtools.h" DEFAULT_DEBUG_CHANNEL(dsound) /***************************************************************************** * Predeclare the interface implementation structures */ typedef struct IDirectSoundImpl IDirectSoundImpl; typedef struct IDirectSoundBufferImpl IDirectSoundBufferImpl; typedef struct IDirectSoundNotifyImpl IDirectSoundNotifyImpl; typedef struct IDirectSound3DListenerImpl IDirectSound3DListenerImpl; typedef struct IDirectSound3DBufferImpl IDirectSound3DBufferImpl; /***************************************************************************** * IDirectSound implementation structure */ struct IDirectSoundImpl { /* IUnknown fields */ ICOM_VTABLE(IDirectSound)* lpvtbl; DWORD ref; /* IDirectSoundImpl fields */ DWORD priolevel; int nrofbuffers; IDirectSoundBufferImpl** buffers; IDirectSoundBufferImpl* primary; IDirectSound3DListenerImpl* listener; WAVEFORMATEX wfx; /* current main waveformat */ }; /***************************************************************************** * IDirectSoundBuffer implementation structure */ struct IDirectSoundBufferImpl { /* IUnknown fields */ ICOM_VTABLE(IDirectSoundBuffer)* lpvtbl; DWORD ref; /* IDirectSoundBufferImpl fields */ WAVEFORMATEX wfx; LPBYTE buffer; IDirectSound3DBufferImpl* ds3db; DWORD playflags,playing; DWORD playpos,writepos,buflen; DWORD nAvgBytesPerSec; DWORD freq; ULONG freqAdjust; LONG volume,pan; LONG lVolAdjust,rVolAdjust; IDirectSoundBufferImpl* parent; /* for duplicates */ IDirectSoundImpl* dsound; DSBUFFERDESC dsbd; LPDSBPOSITIONNOTIFY notifies; int nrofnotifies; CRITICAL_SECTION lock; }; /***************************************************************************** * IDirectSoundNotify implementation structure */ struct IDirectSoundNotifyImpl { /* IUnknown fields */ ICOM_VTABLE(IDirectSoundNotify)* lpvtbl; DWORD ref; /* IDirectSoundNotifyImpl fields */ IDirectSoundBufferImpl* dsb; }; /***************************************************************************** * IDirectSound3DListener implementation structure */ struct IDirectSound3DListenerImpl { /* IUnknown fields */ ICOM_VTABLE(IDirectSound3DListener)* lpvtbl; DWORD ref; /* IDirectSound3DListenerImpl fields */ IDirectSoundBufferImpl* dsb; DS3DLISTENER ds3dl; CRITICAL_SECTION lock; }; /***************************************************************************** * IDirectSound3DBuffer implementation structure */ struct IDirectSound3DBufferImpl { /* IUnknown fields */ ICOM_VTABLE(IDirectSound3DBuffer)* lpvtbl; DWORD ref; /* IDirectSound3DBufferImpl fields */ IDirectSoundBufferImpl* dsb; DS3DBUFFER ds3db; LPBYTE buffer; DWORD buflen; CRITICAL_SECTION lock; }; #ifdef HAVE_OSS # include <sys/ioctl.h> # ifdef HAVE_MACHINE_SOUNDCARD_H # include <machine/soundcard.h> # endif # ifdef HAVE_SYS_SOUNDCARD_H # include <sys/soundcard.h> # endif /* #define USE_DSOUND3D 1 */ #define DSOUND_FRAGLEN (primarybuf->wfx.nAvgBytesPerSec >> 4) #define DSOUND_FREQSHIFT (14) static int audiofd = -1; static int audioOK = 0; static IDirectSoundImpl* dsound = NULL; static IDirectSoundBufferImpl* primarybuf = NULL; static int DSOUND_setformat(LPWAVEFORMATEX wfex); static void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len); static void DSOUND_CloseAudio(void); #endif HRESULT WINAPI DirectSoundEnumerateA( LPDSENUMCALLBACKA enumcb, LPVOID context) { TRACE("enumcb = %p, context = %p\n", enumcb, context); #ifdef HAVE_OSS if (enumcb != NULL) enumcb(NULL,"WINE DirectSound using Open Sound System", "sound",context); #endif return 0; } #ifdef HAVE_OSS static void _dump_DSBCAPS(DWORD xmask) { struct { DWORD mask; char *name; } flags[] = { #define FE(x) { x, #x }, FE(DSBCAPS_PRIMARYBUFFER) FE(DSBCAPS_STATIC) FE(DSBCAPS_LOCHARDWARE) FE(DSBCAPS_LOCSOFTWARE) FE(DSBCAPS_CTRLFREQUENCY) FE(DSBCAPS_CTRLPAN) FE(DSBCAPS_CTRLVOLUME) FE(DSBCAPS_CTRLDEFAULT) FE(DSBCAPS_CTRLALL) FE(DSBCAPS_STICKYFOCUS) FE(DSBCAPS_GETCURRENTPOSITION2) }; int i; for (i=0;i<sizeof(flags)/sizeof(flags[0]);i++) if (flags[i].mask & xmask) fprintf(stderr,"%s ",flags[i].name); } /******************************************************************************* * IDirectSound3DBuffer */ /* IUnknown methods */ static HRESULT WINAPI IDirectSound3DBufferImpl_QueryInterface( LPDIRECTSOUND3DBUFFER iface, REFIID riid, LPVOID *ppobj) { ICOM_THIS(IDirectSound3DBufferImpl,iface); char xbuf[50]; WINE_StringFromCLSID(riid,xbuf); TRACE("(%p,%s,%p)\n",This,xbuf,ppobj); return E_FAIL; } static ULONG WINAPI IDirectSound3DBufferImpl_AddRef(LPDIRECTSOUND3DBUFFER iface) { ICOM_THIS(IDirectSound3DBufferImpl,iface); This->ref++; return This->ref; } static ULONG WINAPI IDirectSound3DBufferImpl_Release(LPDIRECTSOUND3DBUFFER iface) { ICOM_THIS(IDirectSound3DBufferImpl,iface); if(--This->ref) return This->ref; HeapFree(GetProcessHeap(),0,This->buffer); HeapFree(GetProcessHeap(),0,This); return 0; } /* IDirectSound3DBuffer methods */ static HRESULT WINAPI IDirectSound3DBufferImpl_GetAllParameters( LPDIRECTSOUND3DBUFFER iface, LPDS3DBUFFER lpDs3dBuffer) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeAngles( LPDIRECTSOUND3DBUFFER iface, LPDWORD lpdwInsideConeAngle, LPDWORD lpdwOutsideConeAngle) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeOrientation( LPDIRECTSOUND3DBUFFER iface, LPD3DVECTOR lpvConeOrientation) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DBufferImpl_GetConeOutsideVolume( LPDIRECTSOUND3DBUFFER iface, LPLONG lplConeOutsideVolume) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DBufferImpl_GetMaxDistance( LPDIRECTSOUND3DBUFFER iface, LPD3DVALUE lpfMaxDistance) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DBufferImpl_GetMinDistance( LPDIRECTSOUND3DBUFFER iface, LPD3DVALUE lpfMinDistance) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DBufferImpl_GetMode( LPDIRECTSOUND3DBUFFER iface, LPDWORD lpdwMode) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DBufferImpl_GetPosition( LPDIRECTSOUND3DBUFFER iface, LPD3DVECTOR lpvPosition) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DBufferImpl_GetVelocity( LPDIRECTSOUND3DBUFFER iface, LPD3DVECTOR lpvVelocity) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DBufferImpl_SetAllParameters( LPDIRECTSOUND3DBUFFER iface, LPCDS3DBUFFER lpcDs3dBuffer, DWORD dwApply) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeAngles( LPDIRECTSOUND3DBUFFER iface, DWORD dwInsideConeAngle, DWORD dwOutsideConeAngle, DWORD dwApply) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeOrientation( LPDIRECTSOUND3DBUFFER iface, D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD dwApply) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DBufferImpl_SetConeOutsideVolume( LPDIRECTSOUND3DBUFFER iface, LONG lConeOutsideVolume, DWORD dwApply) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DBufferImpl_SetMaxDistance( LPDIRECTSOUND3DBUFFER iface, D3DVALUE fMaxDistance, DWORD dwApply) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DBufferImpl_SetMinDistance( LPDIRECTSOUND3DBUFFER iface, D3DVALUE fMinDistance, DWORD dwApply) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DBufferImpl_SetMode( LPDIRECTSOUND3DBUFFER iface, DWORD dwMode, DWORD dwApply) { ICOM_THIS(IDirectSound3DBufferImpl,iface); TRACE("mode = %lx\n", dwMode); This->ds3db.dwMode = dwMode; return DS_OK; } static HRESULT WINAPI IDirectSound3DBufferImpl_SetPosition( LPDIRECTSOUND3DBUFFER iface, D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD dwApply) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DBufferImpl_SetVelocity( LPDIRECTSOUND3DBUFFER iface, D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD dwApply) { FIXME("stub\n"); return DS_OK; } ICOM_VTABLE(IDirectSound3DBuffer) ds3dbvt = { ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE /* IUnknown methods */ IDirectSound3DBufferImpl_QueryInterface, IDirectSound3DBufferImpl_AddRef, IDirectSound3DBufferImpl_Release, /* IDirectSound3DBuffer methods */ IDirectSound3DBufferImpl_GetAllParameters, IDirectSound3DBufferImpl_GetConeAngles, IDirectSound3DBufferImpl_GetConeOrientation, IDirectSound3DBufferImpl_GetConeOutsideVolume, IDirectSound3DBufferImpl_GetMaxDistance, IDirectSound3DBufferImpl_GetMinDistance, IDirectSound3DBufferImpl_GetMode, IDirectSound3DBufferImpl_GetPosition, IDirectSound3DBufferImpl_GetVelocity, IDirectSound3DBufferImpl_SetAllParameters, IDirectSound3DBufferImpl_SetConeAngles, IDirectSound3DBufferImpl_SetConeOrientation, IDirectSound3DBufferImpl_SetConeOutsideVolume, IDirectSound3DBufferImpl_SetMaxDistance, IDirectSound3DBufferImpl_SetMinDistance, IDirectSound3DBufferImpl_SetMode, IDirectSound3DBufferImpl_SetPosition, IDirectSound3DBufferImpl_SetVelocity, }; #ifdef USE_DSOUND3D static int DSOUND_Create3DBuffer(IDirectSoundBufferImpl* dsb) { DWORD i, temp, iSize, oSize, offset; LPBYTE bIbuf, bObuf, bTbuf = NULL; LPWORD wIbuf, wObuf, wTbuf = NULL; /* Inside DirectX says it's stupid but allowed */ if (dsb->wfx.nChannels == 2) { /* Convert to mono */ if (dsb->wfx.wBitsPerSample == 16) { iSize = dsb->buflen / 4; wTbuf = malloc(dsb->buflen / 2); if (wTbuf == NULL) return DSERR_OUTOFMEMORY; for (i = 0; i < iSize; i++) wTbuf[i] = (dsb->buffer[i] + dsb->buffer[(i * 2) + 1]) / 2; wIbuf = wTbuf; } else { iSize = dsb->buflen / 2; bTbuf = malloc(dsb->buflen / 2); if (bTbuf == NULL) return DSERR_OUTOFMEMORY; for (i = 0; i < iSize; i++) bTbuf[i] = (dsb->buffer[i] + dsb->buffer[(i * 2) + 1]) / 2; bIbuf = bTbuf; } } else { if (dsb->wfx.wBitsPerSample == 16) { iSize = dsb->buflen / 2; wIbuf = (LPWORD) dsb->buffer; } else { bIbuf = (LPBYTE) dsb->buffer; iSize = dsb->buflen; } } if (primarybuf->wfx.wBitsPerSample == 16) { wObuf = (LPWORD) dsb->ds3db->buffer; oSize = dsb->ds3db->buflen / 2; } else { bObuf = (LPBYTE) dsb->ds3db->buffer; oSize = dsb->ds3db->buflen; } offset = primarybuf->wfx.nSamplesPerSec / 100; /* 10ms */ if (primarybuf->wfx.wBitsPerSample == 16 && dsb->wfx.wBitsPerSample == 16) for (i = 0; i < iSize; i++) { temp = wIbuf[i]; if (i >= offset) temp += wIbuf[i - offset] >> 9; else temp += wIbuf[i + iSize - offset] >> 9; wObuf[i * 2] = temp; wObuf[(i * 2) + 1] = temp; } else if (primarybuf->wfx.wBitsPerSample == 8 && dsb->wfx.wBitsPerSample == 8) for (i = 0; i < iSize; i++) { temp = bIbuf[i]; if (i >= offset) temp += bIbuf[i - offset] >> 5; else temp += bIbuf[i + iSize - offset] >> 5; bObuf[i * 2] = temp; bObuf[(i * 2) + 1] = temp; } if (wTbuf) free(wTbuf); if (bTbuf) free(bTbuf); return DS_OK; } #endif /******************************************************************************* * IDirectSound3DListener */ /* IUnknown methods */ static HRESULT WINAPI IDirectSound3DListenerImpl_QueryInterface( LPDIRECTSOUND3DLISTENER iface, REFIID riid, LPVOID *ppobj) { ICOM_THIS(IDirectSound3DListenerImpl,iface); char xbuf[50]; WINE_StringFromCLSID(riid,xbuf); TRACE("(%p,%s,%p)\n",This,xbuf,ppobj); return E_FAIL; } static ULONG WINAPI IDirectSound3DListenerImpl_AddRef(LPDIRECTSOUND3DLISTENER iface) { ICOM_THIS(IDirectSound3DListenerImpl,iface); This->ref++; return This->ref; } static ULONG WINAPI IDirectSound3DListenerImpl_Release(LPDIRECTSOUND3DLISTENER iface) { ICOM_THIS(IDirectSound3DListenerImpl,iface); This->ref--; return This->ref; } /* IDirectSound3DListener methods */ static HRESULT WINAPI IDirectSound3DListenerImpl_GetAllParameter( LPDIRECTSOUND3DLISTENER iface, LPDS3DLISTENER lpDS3DL) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DListenerImpl_GetDistanceFactor( LPDIRECTSOUND3DLISTENER iface, LPD3DVALUE lpfDistanceFactor) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DListenerImpl_GetDopplerFactor( LPDIRECTSOUND3DLISTENER iface, LPD3DVALUE lpfDopplerFactor) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DListenerImpl_GetOrientation( LPDIRECTSOUND3DLISTENER iface, LPD3DVECTOR lpvOrientFront, LPD3DVECTOR lpvOrientTop) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DListenerImpl_GetPosition( LPDIRECTSOUND3DLISTENER iface, LPD3DVECTOR lpvPosition) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DListenerImpl_GetRolloffFactor( LPDIRECTSOUND3DLISTENER iface, LPD3DVALUE lpfRolloffFactor) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DListenerImpl_GetVelocity( LPDIRECTSOUND3DLISTENER iface, LPD3DVECTOR lpvVelocity) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DListenerImpl_SetAllParameters( LPDIRECTSOUND3DLISTENER iface, LPCDS3DLISTENER lpcDS3DL, DWORD dwApply) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DListenerImpl_SetDistanceFactor( LPDIRECTSOUND3DLISTENER iface, D3DVALUE fDistanceFactor, DWORD dwApply) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DListenerImpl_SetDopplerFactor( LPDIRECTSOUND3DLISTENER iface, D3DVALUE fDopplerFactor, DWORD dwApply) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DListenerImpl_SetOrientation( LPDIRECTSOUND3DLISTENER iface, D3DVALUE xFront, D3DVALUE yFront, D3DVALUE zFront, D3DVALUE xTop, D3DVALUE yTop, D3DVALUE zTop, DWORD dwApply) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DListenerImpl_SetPosition( LPDIRECTSOUND3DLISTENER iface, D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD dwApply) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DListenerImpl_SetRolloffFactor( LPDIRECTSOUND3DLISTENER iface, D3DVALUE fRolloffFactor, DWORD dwApply) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DListenerImpl_SetVelocity( LPDIRECTSOUND3DLISTENER iface, D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD dwApply) { FIXME("stub\n"); return DS_OK; } static HRESULT WINAPI IDirectSound3DListenerImpl_CommitDeferredSettings( LPDIRECTSOUND3DLISTENER iface) { FIXME("stub\n"); return DS_OK; } ICOM_VTABLE(IDirectSound3DListener) ds3dlvt = { ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE /* IUnknown methods */ IDirectSound3DListenerImpl_QueryInterface, IDirectSound3DListenerImpl_AddRef, IDirectSound3DListenerImpl_Release, /* IDirectSound3DListener methods */ IDirectSound3DListenerImpl_GetAllParameter, IDirectSound3DListenerImpl_GetDistanceFactor, IDirectSound3DListenerImpl_GetDopplerFactor, IDirectSound3DListenerImpl_GetOrientation, IDirectSound3DListenerImpl_GetPosition, IDirectSound3DListenerImpl_GetRolloffFactor, IDirectSound3DListenerImpl_GetVelocity, IDirectSound3DListenerImpl_SetAllParameters, IDirectSound3DListenerImpl_SetDistanceFactor, IDirectSound3DListenerImpl_SetDopplerFactor, IDirectSound3DListenerImpl_SetOrientation, IDirectSound3DListenerImpl_SetPosition, IDirectSound3DListenerImpl_SetRolloffFactor, IDirectSound3DListenerImpl_SetVelocity, IDirectSound3DListenerImpl_CommitDeferredSettings, }; /******************************************************************************* * IDirectSoundNotify */ static HRESULT WINAPI IDirectSoundNotifyImpl_QueryInterface( LPDIRECTSOUNDNOTIFY iface,REFIID riid,LPVOID *ppobj ) { ICOM_THIS(IDirectSoundNotifyImpl,iface); char xbuf[50]; WINE_StringFromCLSID(riid,xbuf); TRACE("(%p,%s,%p)\n",This,xbuf,ppobj); return E_FAIL; } static ULONG WINAPI IDirectSoundNotifyImpl_AddRef(LPDIRECTSOUNDNOTIFY iface) { ICOM_THIS(IDirectSoundNotifyImpl,iface); return ++(This->ref); } static ULONG WINAPI IDirectSoundNotifyImpl_Release(LPDIRECTSOUNDNOTIFY iface) { ICOM_THIS(IDirectSoundNotifyImpl,iface); This->ref--; if (!This->ref) { IDirectSoundNotify_Release((LPDIRECTSOUNDBUFFER)This->dsb); HeapFree(GetProcessHeap(),0,This); return 0; } return This->ref; } static HRESULT WINAPI IDirectSoundNotifyImpl_SetNotificationPositions( LPDIRECTSOUNDNOTIFY iface,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify ) { ICOM_THIS(IDirectSoundNotifyImpl,iface); int i; if (TRACE_ON(dsound)) { TRACE("(%p,0x%08lx,%p)\n",This,howmuch,notify); for (i=0;i<howmuch;i++) TRACE("notify at %ld to 0x%08lx\n", notify[i].dwOffset,(DWORD)notify[i].hEventNotify); } This->dsb->notifies = HeapReAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,This->dsb->notifies,(This->dsb->nrofnotifies+howmuch)*sizeof(DSBPOSITIONNOTIFY)); memcpy( This->dsb->notifies+This->dsb->nrofnotifies, notify, howmuch*sizeof(DSBPOSITIONNOTIFY) ); This->dsb->nrofnotifies+=howmuch; return 0; } ICOM_VTABLE(IDirectSoundNotify) dsnvt = { ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE IDirectSoundNotifyImpl_QueryInterface, IDirectSoundNotifyImpl_AddRef, IDirectSoundNotifyImpl_Release, IDirectSoundNotifyImpl_SetNotificationPositions, }; /******************************************************************************* * IDirectSoundBuffer */ /* This sets this format for the <em>Primary Buffer Only</em> */ /* See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 120 */ static HRESULT WINAPI IDirectSoundBufferImpl_SetFormat( LPDIRECTSOUNDBUFFER iface,LPWAVEFORMATEX wfex ) { ICOM_THIS(IDirectSoundBufferImpl,iface); IDirectSoundBufferImpl** dsb; int i; /* Let's be pedantic! */ if ((wfex == NULL) || (wfex->wFormatTag != WAVE_FORMAT_PCM) || (wfex->nChannels < 1) || (wfex->nChannels > 2) || (wfex->nSamplesPerSec < 1) || (wfex->nBlockAlign < 1) || (wfex->nChannels > 4) || ((wfex->wBitsPerSample != 8) && (wfex->wBitsPerSample != 16))) { TRACE("failed pedantic check!\n"); return DSERR_INVALIDPARAM; } /* **** */ EnterCriticalSection(&(primarybuf->lock)); if (primarybuf->wfx.nSamplesPerSec != wfex->nSamplesPerSec) { dsb = dsound->buffers; for (i = 0; i < dsound->nrofbuffers; i++, dsb++) { /* **** */ EnterCriticalSection(&((*dsb)->lock)); (*dsb)->freqAdjust = ((*dsb)->freq << DSOUND_FREQSHIFT) / wfex->nSamplesPerSec; LeaveCriticalSection(&((*dsb)->lock)); /* **** */ } } memcpy(&(primarybuf->wfx), wfex, sizeof(primarybuf->wfx)); TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld" "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n", wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec, wfex->nAvgBytesPerSec, wfex->nBlockAlign, wfex->wBitsPerSample, wfex->cbSize); primarybuf->wfx.nAvgBytesPerSec = This->wfx.nSamplesPerSec * This->wfx.nBlockAlign; DSOUND_CloseAudio(); LeaveCriticalSection(&(primarybuf->lock)); /* **** */ return DS_OK; } static HRESULT WINAPI IDirectSoundBufferImpl_SetVolume( LPDIRECTSOUNDBUFFER iface,LONG vol ) { ICOM_THIS(IDirectSoundBufferImpl,iface); double temp; TRACE("(%p,%ld)\n",This,vol); /* I'm not sure if we need this for primary buffer */ if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) return DSERR_CONTROLUNAVAIL; if ((vol > DSBVOLUME_MAX) || (vol < DSBVOLUME_MIN)) return DSERR_INVALIDPARAM; /* This needs to adjust the soundcard volume when */ /* called for the primary buffer */ if (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) { FIXME("Volume control of primary unimplemented.\n"); This->volume = vol; return DS_OK; } /* **** */ EnterCriticalSection(&(This->lock)); This->volume = vol; temp = (double) (This->volume - (This->pan > 0 ? This->pan : 0)); This->lVolAdjust = (ULONG) (pow(2.0, temp / 600.0) * 32768.0); temp = (double) (This->volume + (This->pan < 0 ? This->pan : 0)); This->rVolAdjust = (ULONG) (pow(2.0, temp / 600.0) * 32768.0); LeaveCriticalSection(&(This->lock)); /* **** */ TRACE("left = %lx, right = %lx\n", This->lVolAdjust, This->rVolAdjust); return DS_OK; } static HRESULT WINAPI IDirectSoundBufferImpl_GetVolume( LPDIRECTSOUNDBUFFER iface,LPLONG vol ) { ICOM_THIS(IDirectSoundBufferImpl,iface); TRACE("(%p,%p)\n",This,vol); if (vol == NULL) return DSERR_INVALIDPARAM; *vol = This->volume; return DS_OK; } static HRESULT WINAPI IDirectSoundBufferImpl_SetFrequency( LPDIRECTSOUNDBUFFER iface,DWORD freq ) { ICOM_THIS(IDirectSoundBufferImpl,iface); TRACE("(%p,%ld)\n",This,freq); /* You cannot set the frequency of the primary buffer */ if (!(This->dsbd.dwFlags & DSBCAPS_CTRLFREQUENCY) || (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)) return DSERR_CONTROLUNAVAIL; if ((freq < DSBFREQUENCY_MIN) || (freq > DSBFREQUENCY_MAX)) return DSERR_INVALIDPARAM; /* **** */ EnterCriticalSection(&(This->lock)); This->freq = freq; This->freqAdjust = (freq << DSOUND_FREQSHIFT) / primarybuf->wfx.nSamplesPerSec; This->nAvgBytesPerSec = freq * This->wfx.nBlockAlign; LeaveCriticalSection(&(This->lock)); /* **** */ return DS_OK; } static HRESULT WINAPI IDirectSoundBufferImpl_Play( LPDIRECTSOUNDBUFFER iface,DWORD reserved1,DWORD reserved2,DWORD flags ) { ICOM_THIS(IDirectSoundBufferImpl,iface); TRACE("(%p,%08lx,%08lx,%08lx)\n", This,reserved1,reserved2,flags ); This->playflags = flags; This->playing = 1; return DS_OK; } static HRESULT WINAPI IDirectSoundBufferImpl_Stop(LPDIRECTSOUNDBUFFER iface) { ICOM_THIS(IDirectSoundBufferImpl,iface); TRACE("(%p)\n",This); /* **** */ EnterCriticalSection(&(This->lock)); This->playing = 0; DSOUND_CheckEvent(This, 0); LeaveCriticalSection(&(This->lock)); /* **** */ return DS_OK; } static DWORD WINAPI IDirectSoundBufferImpl_AddRef(LPDIRECTSOUNDBUFFER iface) { ICOM_THIS(IDirectSoundBufferImpl,iface); /* TRACE(dsound,"(%p) ref was %ld\n",This, This->ref); */ return ++(This->ref); } static DWORD WINAPI IDirectSoundBufferImpl_Release(LPDIRECTSOUNDBUFFER iface) { ICOM_THIS(IDirectSoundBufferImpl,iface); int i; /* TRACE(dsound,"(%p) ref was %ld\n",This, This->ref); */ if (--This->ref) return This->ref; for (i=0;i<This->dsound->nrofbuffers;i++) if (This->dsound->buffers[i] == This) break; if (i < This->dsound->nrofbuffers) { /* Put the last buffer of the list in the (now empty) position */ This->dsound->buffers[i] = This->dsound->buffers[This->dsound->nrofbuffers - 1]; This->dsound->buffers = HeapReAlloc(GetProcessHeap(),0,This->dsound->buffers,sizeof(LPDIRECTSOUNDBUFFER)*This->dsound->nrofbuffers); This->dsound->nrofbuffers--; IDirectSound_Release((LPDIRECTSOUND)This->dsound); } DeleteCriticalSection(&(This->lock)); if (This->ds3db && This->ds3db->lpvtbl) IDirectSound3DBuffer_Release((LPDIRECTSOUND3DBUFFER)This->ds3db); if (This->parent) /* this is a duplicate buffer */ IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)This->parent); else /* this is a toplevel buffer */ HeapFree(GetProcessHeap(),0,This->buffer); HeapFree(GetProcessHeap(),0,This); if (This == primarybuf) primarybuf = NULL; return DS_OK; } static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition( LPDIRECTSOUNDBUFFER iface,LPDWORD playpos,LPDWORD writepos ) { ICOM_THIS(IDirectSoundBufferImpl,iface); TRACE("(%p,%p,%p)\n",This,playpos,writepos); if (playpos) *playpos = This->playpos; if (writepos) *writepos = This->writepos; TRACE("playpos = %ld, writepos = %ld\n", *playpos, *writepos); return DS_OK; } static HRESULT WINAPI IDirectSoundBufferImpl_GetStatus( LPDIRECTSOUNDBUFFER iface,LPDWORD status ) { ICOM_THIS(IDirectSoundBufferImpl,iface); TRACE("(%p,%p)\n",This,status); if (status == NULL) return DSERR_INVALIDPARAM; *status = 0; if (This->playing) *status |= DSBSTATUS_PLAYING; if (This->playflags & DSBPLAY_LOOPING) *status |= DSBSTATUS_LOOPING; return DS_OK; } static HRESULT WINAPI IDirectSoundBufferImpl_GetFormat( LPDIRECTSOUNDBUFFER iface,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten ) { ICOM_THIS(IDirectSoundBufferImpl,iface); TRACE("(%p,%p,%ld,%p)\n",This,lpwf,wfsize,wfwritten); if (wfsize>sizeof(This->wfx)) wfsize = sizeof(This->wfx); if (lpwf) { /* NULL is valid */ memcpy(lpwf,&(This->wfx),wfsize); if (wfwritten) *wfwritten = wfsize; } else if (wfwritten) *wfwritten = sizeof(This->wfx); else return DSERR_INVALIDPARAM; return DS_OK; } static HRESULT WINAPI IDirectSoundBufferImpl_Lock( LPDIRECTSOUNDBUFFER iface,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags ) { ICOM_THIS(IDirectSoundBufferImpl,iface); TRACE("(%p,%ld,%ld,%p,%p,%p,%p,0x%08lx)\n", This, writecursor, writebytes, lplpaudioptr1, audiobytes1, lplpaudioptr2, audiobytes2, flags ); if (flags & DSBLOCK_FROMWRITECURSOR) writecursor += This->writepos; if (flags & DSBLOCK_ENTIREBUFFER) writebytes = This->buflen; if (writebytes > This->buflen) writebytes = This->buflen; assert(audiobytes1!=audiobytes2); assert(lplpaudioptr1!=lplpaudioptr2); if (writecursor+writebytes <= This->buflen) { *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor; *audiobytes1 = writebytes; if (lplpaudioptr2) *(LPBYTE*)lplpaudioptr2 = NULL; if (audiobytes2) *audiobytes2 = 0; TRACE("->%ld.0\n",writebytes); } else { *(LPBYTE*)lplpaudioptr1 = This->buffer+writecursor; *audiobytes1 = This->buflen-writecursor; if (lplpaudioptr2) *(LPBYTE*)lplpaudioptr2 = This->buffer; if (audiobytes2) *audiobytes2 = writebytes-(This->buflen-writecursor); TRACE("->%ld.%ld\n",*audiobytes1,audiobytes2?*audiobytes2:0); } /* No. See file:///cdrom/sdk52/docs/worddoc/dsound.doc page 21 */ /* This->writepos=(writecursor+writebytes)%This->buflen; */ return DS_OK; } static HRESULT WINAPI IDirectSoundBufferImpl_SetCurrentPosition( LPDIRECTSOUNDBUFFER iface,DWORD newpos ) { ICOM_THIS(IDirectSoundBufferImpl,iface); TRACE("(%p,%ld)\n",This,newpos); /* **** */ EnterCriticalSection(&(This->lock)); This->playpos = newpos; LeaveCriticalSection(&(This->lock)); /* **** */ return 0; } static HRESULT WINAPI IDirectSoundBufferImpl_SetPan( LPDIRECTSOUNDBUFFER iface,LONG pan ) { ICOM_THIS(IDirectSoundBufferImpl,iface); double temp; TRACE("(%p,%ld)\n",This,pan); if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT)) return DSERR_INVALIDPARAM; /* You cannot set the pan of the primary buffer */ /* and you cannot use both pan and 3D controls */ if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (This->dsbd.dwFlags & DSBCAPS_CTRL3D) || (This->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER)) return DSERR_CONTROLUNAVAIL; /* **** */ EnterCriticalSection(&(This->lock)); This->pan = pan; temp = (double) (This->volume - (This->pan > 0 ? This->pan : 0)); This->lVolAdjust = (ULONG) (pow(2.0, temp / 600.0) * 32768.0); temp = (double) (This->volume + (This->pan < 0 ? This->pan : 0)); This->rVolAdjust = (ULONG) (pow(2.0, temp / 600.0) * 32768.0); LeaveCriticalSection(&(This->lock)); /* **** */ return DS_OK; } static HRESULT WINAPI IDirectSoundBufferImpl_GetPan( LPDIRECTSOUNDBUFFER iface,LPLONG pan ) { ICOM_THIS(IDirectSoundBufferImpl,iface); TRACE("(%p,%p)\n",This,pan); if (pan == NULL) return DSERR_INVALIDPARAM; *pan = This->pan; return DS_OK; } static HRESULT WINAPI IDirectSoundBufferImpl_Unlock( LPDIRECTSOUNDBUFFER iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2 ) { ICOM_THIS(IDirectSoundBufferImpl,iface); TRACE("(%p,%p,%ld,%p,%ld):stub\n", This,p1,x1,p2,x2); /* There is really nothing to do here. Should someone */ /* choose to implement static buffers in hardware (by */ /* using a wave table synth, for example) this is where */ /* you'd want to do the loading. For software buffers, */ /* which is what we currently use, we need do nothing. */ #if 0 /* It's also the place to pre-process 3D buffers... */ /* This is highly experimental and liable to break things */ if (This->dsbd.dwFlags & DSBCAPS_CTRL3D) DSOUND_Create3DBuffer(This); #endif return DS_OK; } static HRESULT WINAPI IDirectSoundBufferImpl_GetFrequency( LPDIRECTSOUNDBUFFER iface,LPDWORD freq ) { ICOM_THIS(IDirectSoundBufferImpl,iface); TRACE("(%p,%p)\n",This,freq); if (freq == NULL) return DSERR_INVALIDPARAM; *freq = This->freq; return DS_OK; } static HRESULT WINAPI IDirectSoundBufferImpl_Initialize( LPDIRECTSOUNDBUFFER iface,LPDIRECTSOUND dsound,LPDSBUFFERDESC dbsd ) { ICOM_THIS(IDirectSoundBufferImpl,iface); FIXME("(%p,%p,%p):stub\n",This,dsound,dbsd); printf("Re-Init!!!\n"); return DSERR_ALREADYINITIALIZED; } static HRESULT WINAPI IDirectSoundBufferImpl_GetCaps( LPDIRECTSOUNDBUFFER iface,LPDSBCAPS caps ) { ICOM_THIS(IDirectSoundBufferImpl,iface); TRACE("(%p)->(%p)\n",This,caps); if (caps == NULL) return DSERR_INVALIDPARAM; /* I think we should check this value, not set it. See */ /* Inside DirectX, p215. That should apply here, too. */ caps->dwSize = sizeof(*caps); caps->dwFlags = This->dsbd.dwFlags | DSBCAPS_LOCSOFTWARE; caps->dwBufferBytes = This->dsbd.dwBufferBytes; /* This value represents the speed of the "unlock" command. As unlock is quite fast (it does not do anything), I put 4096 ko/s = 4 Mo / s */ caps->dwUnlockTransferRate = 4096; caps->dwPlayCpuOverhead = 0; return DS_OK; } static HRESULT WINAPI IDirectSoundBufferImpl_QueryInterface( LPDIRECTSOUNDBUFFER iface,REFIID riid,LPVOID *ppobj ) { ICOM_THIS(IDirectSoundBufferImpl,iface); char xbuf[50]; WINE_StringFromCLSID(riid,xbuf); TRACE("(%p,%s,%p)\n",This,xbuf,ppobj); if (!memcmp(&IID_IDirectSoundNotify,riid,sizeof(*riid))) { IDirectSoundNotifyImpl *dsn; dsn = (IDirectSoundNotifyImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(*dsn)); dsn->ref = 1; dsn->dsb = This; IDirectSoundBuffer_AddRef(iface); dsn->lpvtbl = &dsnvt; *ppobj = (LPVOID)dsn; return 0; } if (!memcmp(&IID_IDirectSound3DBuffer,riid,sizeof(*riid))) { *ppobj = This->ds3db; if (*ppobj) return DS_OK; } return E_FAIL; } static ICOM_VTABLE(IDirectSoundBuffer) dsbvt = { ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE IDirectSoundBufferImpl_QueryInterface, IDirectSoundBufferImpl_AddRef, IDirectSoundBufferImpl_Release, IDirectSoundBufferImpl_GetCaps, IDirectSoundBufferImpl_GetCurrentPosition, IDirectSoundBufferImpl_GetFormat, IDirectSoundBufferImpl_GetVolume, IDirectSoundBufferImpl_GetPan, IDirectSoundBufferImpl_GetFrequency, IDirectSoundBufferImpl_GetStatus, IDirectSoundBufferImpl_Initialize, IDirectSoundBufferImpl_Lock, IDirectSoundBufferImpl_Play, IDirectSoundBufferImpl_SetCurrentPosition, IDirectSoundBufferImpl_SetFormat, IDirectSoundBufferImpl_SetVolume, IDirectSoundBufferImpl_SetPan, IDirectSoundBufferImpl_SetFrequency, IDirectSoundBufferImpl_Stop, IDirectSoundBufferImpl_Unlock }; /******************************************************************************* * IDirectSound */ static HRESULT WINAPI IDirectSoundImpl_SetCooperativeLevel( LPDIRECTSOUND iface,HWND hwnd,DWORD level ) { ICOM_THIS(IDirectSoundImpl,iface); FIXME("(%p,%08lx,%ld):stub\n",This,(DWORD)hwnd,level); return 0; } static HRESULT WINAPI IDirectSoundImpl_CreateSoundBuffer( LPDIRECTSOUND iface,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER ppdsb,LPUNKNOWN lpunk ) { ICOM_THIS(IDirectSoundImpl,iface); IDirectSoundBufferImpl** ippdsb=(IDirectSoundBufferImpl**)ppdsb; LPWAVEFORMATEX wfex; TRACE("(%p,%p,%p,%p)\n",This,dsbd,ippdsb,lpunk); if ((This == NULL) || (dsbd == NULL) || (ippdsb == NULL)) return DSERR_INVALIDPARAM; if (TRACE_ON(dsound)) { TRACE("(size=%ld)\n",dsbd->dwSize); TRACE("(flags=0x%08lx\n",dsbd->dwFlags); _dump_DSBCAPS(dsbd->dwFlags); TRACE("(bufferbytes=%ld)\n",dsbd->dwBufferBytes); TRACE("(lpwfxFormat=%p)\n",dsbd->lpwfxFormat); } wfex = dsbd->lpwfxFormat; if (wfex) TRACE("(formattag=0x%04x,chans=%d,samplerate=%ld" "bytespersec=%ld,blockalign=%d,bitspersamp=%d,cbSize=%d)\n", wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec, wfex->nAvgBytesPerSec, wfex->nBlockAlign, wfex->wBitsPerSample, wfex->cbSize); if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) { if (primarybuf) { IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)primarybuf); *ippdsb = primarybuf; primarybuf->dsbd.dwFlags = dsbd->dwFlags; return DS_OK; } /* Else create primarybuf */ } *ippdsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBufferImpl)); if (*ippdsb == NULL) return DSERR_OUTOFMEMORY; (*ippdsb)->ref = 1; TRACE("Created buffer at %p\n", *ippdsb); if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER) { (*ippdsb)->buflen = dsound->wfx.nAvgBytesPerSec; (*ippdsb)->freq = dsound->wfx.nSamplesPerSec; } else { (*ippdsb)->buflen = dsbd->dwBufferBytes; (*ippdsb)->freq = dsbd->lpwfxFormat->nSamplesPerSec; } (*ippdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,(*ippdsb)->buflen); if ((*ippdsb)->buffer == NULL) { HeapFree(GetProcessHeap(),0,(*ippdsb)); *ippdsb = NULL; return DSERR_OUTOFMEMORY; } /* It's not necessary to initialize values to zero since */ /* we allocated this structure with HEAP_ZERO_MEMORY... */ (*ippdsb)->playpos = 0; (*ippdsb)->writepos = 0; (*ippdsb)->parent = NULL; (*ippdsb)->lpvtbl = &dsbvt; (*ippdsb)->dsound = This; (*ippdsb)->playing = 0; (*ippdsb)->lVolAdjust = (1 << 15); (*ippdsb)->rVolAdjust = (1 << 15); if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) { (*ippdsb)->freqAdjust = ((*ippdsb)->freq << DSOUND_FREQSHIFT) / primarybuf->wfx.nSamplesPerSec; (*ippdsb)->nAvgBytesPerSec = (*ippdsb)->freq * dsbd->lpwfxFormat->nBlockAlign; } memcpy(&((*ippdsb)->dsbd),dsbd,sizeof(*dsbd)); /* register buffer */ if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) { This->buffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl*)*(This->nrofbuffers+1)); This->buffers[This->nrofbuffers] = *ippdsb; This->nrofbuffers++; } IDirectSound_AddRef(iface); if (dsbd->lpwfxFormat) memcpy(&((*ippdsb)->wfx), dsbd->lpwfxFormat, sizeof((*ippdsb)->wfx)); InitializeCriticalSection(&((*ippdsb)->lock)); #if USE_DSOUND3D if (dsbd->dwFlags & DSBCAPS_CTRL3D) { IDirectSound3DBufferImpl *ds3db; ds3db = (IDirectSound3DBufferImpl*)HeapAlloc(GetProcessHeap(), 0,sizeof(*ds3db)); ds3db->ref = 1; ds3db->dsb = (*ippdsb); ds3db->lpvtbl = &ds3dbvt; (*ippdsb)->ds3db = ds3db; ds3db->ds3db.dwSize = sizeof(DS3DBUFFER); ds3db->ds3db.vPosition.x.x = 0.0; ds3db->ds3db.vPosition.y.y = 0.0; ds3db->ds3db.vPosition.z.z = 0.0; ds3db->ds3db.vVelocity.x.x = 0.0; ds3db->ds3db.vVelocity.y.y = 0.0; ds3db->ds3db.vVelocity.z.z = 0.0; ds3db->ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE; ds3db->ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE; ds3db->ds3db.vConeOrientation.x.x = 0.0; ds3db->ds3db.vConeOrientation.y.y = 0.0; ds3db->ds3db.vConeOrientation.z.z = 0.0; ds3db->ds3db.lConeOutsideVolume = DS3D_DEFAULTCONEOUTSIDEVOLUME; ds3db->ds3db.flMinDistance = DS3D_DEFAULTMINDISTANCE; ds3db->ds3db.flMaxDistance = DS3D_DEFAULTMAXDISTANCE; ds3db->ds3db.dwMode = DS3DMODE_NORMAL; ds3db->buflen = ((*ippdsb)->buflen * primarybuf->wfx.nBlockAlign) / (*ippdsb)->wfx.nBlockAlign; ds3db->buffer = HeapAlloc(GetProcessHeap(), 0, ds3db->buflen); if (ds3db->buffer == NULL) { ds3db->buflen = 0; ds3db->ds3db.dwMode = DS3DMODE_DISABLE; } } #endif return DS_OK; } static HRESULT WINAPI IDirectSoundImpl_DuplicateSoundBuffer( LPDIRECTSOUND iface,LPDIRECTSOUNDBUFFER pdsb,LPLPDIRECTSOUNDBUFFER ppdsb ) { ICOM_THIS(IDirectSoundImpl,iface); IDirectSoundBufferImpl* ipdsb=(IDirectSoundBufferImpl*)pdsb; IDirectSoundBufferImpl** ippdsb=(IDirectSoundBufferImpl**)ppdsb; TRACE("(%p,%p,%p)\n",This,ipdsb,ippdsb); *ippdsb = (IDirectSoundBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBufferImpl)); IDirectSoundBuffer_AddRef(pdsb); memcpy(*ippdsb, ipdsb, sizeof(IDirectSoundBufferImpl)); (*ippdsb)->ref = 1; (*ippdsb)->playpos = 0; (*ippdsb)->writepos = 0; (*ippdsb)->dsound = This; (*ippdsb)->parent = ipdsb; memcpy(&((*ippdsb)->wfx), &(ipdsb->wfx), sizeof((*ippdsb)->wfx)); /* register buffer */ This->buffers = (IDirectSoundBufferImpl**)HeapReAlloc(GetProcessHeap(),0,This->buffers,sizeof(IDirectSoundBufferImpl**)*(This->nrofbuffers+1)); This->buffers[This->nrofbuffers] = *ippdsb; This->nrofbuffers++; IDirectSound_AddRef(iface); return 0; } static HRESULT WINAPI IDirectSoundImpl_GetCaps(LPDIRECTSOUND iface,LPDSCAPS caps) { ICOM_THIS(IDirectSoundImpl,iface); TRACE("(%p,%p)\n",This,caps); TRACE("(flags=0x%08lx)\n",caps->dwFlags); if (caps == NULL) return DSERR_INVALIDPARAM; /* We should check this value, not set it. See Inside DirectX, p215. */ caps->dwSize = sizeof(*caps); caps->dwFlags = DSCAPS_PRIMARYSTEREO | DSCAPS_PRIMARY16BIT | DSCAPS_SECONDARYSTEREO | DSCAPS_SECONDARY16BIT | DSCAPS_CONTINUOUSRATE; /* FIXME: query OSS */ caps->dwMinSecondarySampleRate = DSBFREQUENCY_MIN; caps->dwMaxSecondarySampleRate = DSBFREQUENCY_MAX; caps->dwPrimaryBuffers = 1; caps->dwMaxHwMixingAllBuffers = 0; caps->dwMaxHwMixingStaticBuffers = 0; caps->dwMaxHwMixingStreamingBuffers = 0; caps->dwFreeHwMixingAllBuffers = 0; caps->dwFreeHwMixingStaticBuffers = 0; caps->dwFreeHwMixingStreamingBuffers = 0; caps->dwMaxHw3DAllBuffers = 0; caps->dwMaxHw3DStaticBuffers = 0; caps->dwMaxHw3DStreamingBuffers = 0; caps->dwFreeHw3DAllBuffers = 0; caps->dwFreeHw3DStaticBuffers = 0; caps->dwFreeHw3DStreamingBuffers = 0; caps->dwTotalHwMemBytes = 0; caps->dwFreeHwMemBytes = 0; caps->dwMaxContigFreeHwMemBytes = 0; caps->dwUnlockTransferRateHwBuffers = 4096; /* But we have none... */ caps->dwPlayCpuOverheadSwBuffers = 1; /* 1% */ return 0; } static ULONG WINAPI IDirectSoundImpl_AddRef(LPDIRECTSOUND iface) { ICOM_THIS(IDirectSoundImpl,iface); return ++(This->ref); } static ULONG WINAPI IDirectSoundImpl_Release(LPDIRECTSOUND iface) { ICOM_THIS(IDirectSoundImpl,iface); TRACE("(%p), ref was %ld\n",This,This->ref); if (!--(This->ref)) { DSOUND_CloseAudio(); while(IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)primarybuf)); /* Deallocate */ FIXME("need to release all buffers!\n"); HeapFree(GetProcessHeap(),0,This); dsound = NULL; return 0; } return This->ref; } static HRESULT WINAPI IDirectSoundImpl_SetSpeakerConfig( LPDIRECTSOUND iface,DWORD config ) { ICOM_THIS(IDirectSoundImpl,iface); FIXME("(%p,0x%08lx):stub\n",This,config); return 0; } static HRESULT WINAPI IDirectSoundImpl_QueryInterface( LPDIRECTSOUND iface,REFIID riid,LPVOID *ppobj ) { ICOM_THIS(IDirectSoundImpl,iface); char xbuf[50]; if (!memcmp(&IID_IDirectSound3DListener,riid,sizeof(*riid))) { if (This->listener) { *ppobj = This->listener; return DS_OK; } This->listener = (IDirectSound3DListenerImpl*)HeapAlloc( GetProcessHeap(), 0, sizeof(*(This->listener))); This->listener->ref = 1; This->listener->lpvtbl = &ds3dlvt; IDirectSound_AddRef(iface); This->listener->ds3dl.dwSize = sizeof(DS3DLISTENER); This->listener->ds3dl.vPosition.x.x = 0.0; This->listener->ds3dl.vPosition.y.y = 0.0; This->listener->ds3dl.vPosition.z.z = 0.0; This->listener->ds3dl.vVelocity.x.x = 0.0; This->listener->ds3dl.vVelocity.y.y = 0.0; This->listener->ds3dl.vVelocity.z.z = 0.0; This->listener->ds3dl.vOrientFront.x.x = 0.0; This->listener->ds3dl.vOrientFront.y.y = 0.0; This->listener->ds3dl.vOrientFront.z.z = 1.0; This->listener->ds3dl.vOrientTop.x.x = 0.0; This->listener->ds3dl.vOrientTop.y.y = 1.0; This->listener->ds3dl.vOrientTop.z.z = 0.0; This->listener->ds3dl.flDistanceFactor = DS3D_DEFAULTDISTANCEFACTOR; This->listener->ds3dl.flRolloffFactor = DS3D_DEFAULTROLLOFFFACTOR; This->listener->ds3dl.flDopplerFactor = DS3D_DEFAULTDOPPLERFACTOR; *ppobj = (LPVOID)This->listener; return DS_OK; } WINE_StringFromCLSID(riid,xbuf); TRACE("(%p,%s,%p)\n",This,xbuf,ppobj); return E_FAIL; } static HRESULT WINAPI IDirectSoundImpl_Compact( LPDIRECTSOUND iface) { ICOM_THIS(IDirectSoundImpl,iface); TRACE("(%p)\n", This); return DS_OK; } static HRESULT WINAPI IDirectSoundImpl_GetSpeakerConfig( LPDIRECTSOUND iface, LPDWORD lpdwSpeakerConfig) { ICOM_THIS(IDirectSoundImpl,iface); TRACE("(%p, %p)\n", This, lpdwSpeakerConfig); *lpdwSpeakerConfig = DSSPEAKER_STEREO | (DSSPEAKER_GEOMETRY_NARROW << 16); return DS_OK; } static HRESULT WINAPI IDirectSoundImpl_Initialize( LPDIRECTSOUND iface, LPGUID lpGuid) { ICOM_THIS(IDirectSoundImpl,iface); TRACE("(%p, %p)\n", This, lpGuid); return DS_OK; } static ICOM_VTABLE(IDirectSound) dsvt = { ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE IDirectSoundImpl_QueryInterface, IDirectSoundImpl_AddRef, IDirectSoundImpl_Release, IDirectSoundImpl_CreateSoundBuffer, IDirectSoundImpl_GetCaps, IDirectSoundImpl_DuplicateSoundBuffer, IDirectSoundImpl_SetCooperativeLevel, IDirectSoundImpl_Compact, IDirectSoundImpl_GetSpeakerConfig, IDirectSoundImpl_SetSpeakerConfig, IDirectSoundImpl_Initialize }; /* See http://www.opensound.com/pguide/audio.html for more details */ static int DSOUND_setformat(LPWAVEFORMATEX wfex) { int xx,channels,speed,format,nformat; if (!audioOK) { TRACE("(%p) deferred\n", wfex); return 0; } switch (wfex->wFormatTag) { default: WARN("unknown WAVE_FORMAT tag %d\n",wfex->wFormatTag); return DSERR_BADFORMAT; case WAVE_FORMAT_PCM: break; } if (wfex->wBitsPerSample==8) format = AFMT_U8; else format = AFMT_S16_LE; if (-1==ioctl(audiofd,SNDCTL_DSP_GETFMTS,&xx)) { perror("ioctl SNDCTL_DSP_GETFMTS"); return -1; } if ((xx&format)!=format) {/* format unsupported */ FIXME("SNDCTL_DSP_GETFMTS: format not supported\n"); return -1; } nformat = format; if (-1==ioctl(audiofd,SNDCTL_DSP_SETFMT,&nformat)) { perror("ioctl SNDCTL_DSP_SETFMT"); return -1; } if (nformat!=format) {/* didn't work */ FIXME("SNDCTL_DSP_GETFMTS: format not set\n"); return -1; } channels = wfex->nChannels-1; if (-1==ioctl(audiofd,SNDCTL_DSP_STEREO,&channels)) { perror("ioctl SNDCTL_DSP_STEREO"); return -1; } speed = wfex->nSamplesPerSec; if (-1==ioctl(audiofd,SNDCTL_DSP_SPEED,&speed)) { perror("ioctl SNDCTL_DSP_SPEED"); return -1; } TRACE("(freq=%ld,channels=%d,bits=%d)\n", wfex->nSamplesPerSec,wfex->nChannels,wfex->wBitsPerSample ); return 0; } static void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len) { int i; DWORD offset; LPDSBPOSITIONNOTIFY event; if (dsb->nrofnotifies == 0) return; TRACE("(%p) buflen = %ld, playpos = %ld, len = %d\n", dsb, dsb->buflen, dsb->playpos, len); for (i = 0; i < dsb->nrofnotifies ; i++) { event = dsb->notifies + i; offset = event->dwOffset; TRACE("checking %d, position %ld, event = %d\n", i, offset, event->hEventNotify); /* DSBPN_OFFSETSTOP has to be the last element. So this is */ /* OK. [Inside DirectX, p274] */ /* */ /* This also means we can't sort the entries by offset, */ /* because DSBPN_OFFSETSTOP == -1 */ if (offset == DSBPN_OFFSETSTOP) { if (dsb->playing == 0) { SetEvent(event->hEventNotify); TRACE("signalled event %d (%d)\n", event->hEventNotify, i); return; } else return; } if ((dsb->playpos + len) >= dsb->buflen) { if ((offset < ((dsb->playpos + len) % dsb->buflen)) || (offset >= dsb->playpos)) { TRACE("signalled event %d (%d)\n", event->hEventNotify, i); SetEvent(event->hEventNotify); } } else { if ((offset >= dsb->playpos) && (offset < (dsb->playpos + len))) { TRACE("signalled event %d (%d)\n", event->hEventNotify, i); SetEvent(event->hEventNotify); } } } } /* WAV format info can be found at: */ /* */ /* http://www.cwi.nl/ftp/audio/AudioFormats.part2 */ /* ftp://ftp.cwi.nl/pub/audio/RIFF-format */ /* */ /* Import points to remember: */ /* */ /* 8-bit WAV is unsigned */ /* 16-bit WAV is signed */ static inline INT16 cvtU8toS16(BYTE byte) { INT16 s = (byte - 128) << 8; return s; } static inline BYTE cvtS16toU8(INT16 word) { BYTE b = (word + 32768) >> 8; return b; } /* We should be able to optimize these two inline functions */ /* so that we aren't doing 8->16->8 conversions when it is */ /* not necessary. But this is still a WIP. Optimize later. */ static inline void get_fields(const IDirectSoundBufferImpl *dsb, BYTE *buf, INT *fl, INT *fr) { INT16 *bufs = (INT16 *) buf; /* TRACE(dsound, "(%p)", buf); */ if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 2) { *fl = cvtU8toS16(*buf); *fr = cvtU8toS16(*(buf + 1)); return; } if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 2) { *fl = *bufs; *fr = *(bufs + 1); return; } if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 1) { *fl = cvtU8toS16(*buf); *fr = *fl; return; } if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 1) { *fl = *bufs; *fr = *bufs; return; } FIXME("get_fields found an unsupported configuration\n"); return; } static inline void set_fields(BYTE *buf, INT fl, INT fr) { INT16 *bufs = (INT16 *) buf; if ((primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 2)) { *buf = cvtS16toU8(fl); *(buf + 1) = cvtS16toU8(fr); return; } if ((primarybuf->wfx.wBitsPerSample == 16) && (primarybuf->wfx.nChannels == 2)) { *bufs = fl; *(bufs + 1) = fr; return; } if ((primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 1)) { *buf = cvtS16toU8((fl + fr) >> 1); return; } if ((primarybuf->wfx.wBitsPerSample == 16) && (primarybuf->wfx.nChannels == 1)) { *bufs = (fl + fr) >> 1; return; } FIXME("set_fields found an unsupported configuration\n"); return; } /* Now with PerfectPitch (tm) technology */ static INT DSOUND_MixerNorm(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len) { INT i, size, ipos, ilen, fieldL, fieldR; BYTE *ibp, *obp; INT iAdvance = dsb->wfx.nBlockAlign; INT oAdvance = primarybuf->wfx.nBlockAlign; ibp = dsb->buffer + dsb->playpos; obp = buf; TRACE("(%p, %p, %p), playpos=%8.8lx\n", dsb, ibp, obp, dsb->playpos); /* Check for the best case */ if ((dsb->freq == primarybuf->wfx.nSamplesPerSec) && (dsb->wfx.wBitsPerSample == primarybuf->wfx.wBitsPerSample) && (dsb->wfx.nChannels == primarybuf->wfx.nChannels)) { TRACE("(%p) Best case\n", dsb); if ((ibp + len) < (BYTE *)(dsb->buffer + dsb->buflen)) memcpy(obp, ibp, len); else { /* wrap */ memcpy(obp, ibp, dsb->buflen - dsb->playpos); memcpy(obp + (dsb->buflen - dsb->playpos), dsb->buffer, len - (dsb->buflen - dsb->playpos)); } return len; } /* Check for same sample rate */ if (dsb->freq == primarybuf->wfx.nSamplesPerSec) { TRACE("(%p) Same sample rate %ld = primary %ld\n", dsb, dsb->freq, primarybuf->wfx.nSamplesPerSec); ilen = 0; for (i = 0; i < len; i += oAdvance) { get_fields(dsb, ibp, &fieldL, &fieldR); ibp += iAdvance; ilen += iAdvance; set_fields(obp, fieldL, fieldR); obp += oAdvance; if (ibp >= (BYTE *)(dsb->buffer + dsb->buflen)) ibp = dsb->buffer; /* wrap */ } return (ilen); } /* Mix in different sample rates */ /* */ /* New PerfectPitch(tm) Technology (c) 1998 Rob Riggs */ /* Patent Pending :-] */ TRACE("(%p) Adjusting frequency: %ld -> %ld\n", dsb, dsb->freq, primarybuf->wfx.nSamplesPerSec); size = len / oAdvance; ilen = ((size * dsb->freqAdjust) >> DSOUND_FREQSHIFT) * iAdvance; for (i = 0; i < size; i++) { ipos = (((i * dsb->freqAdjust) >> DSOUND_FREQSHIFT) * iAdvance) + dsb->playpos; if (ipos >= dsb->buflen) ipos %= dsb->buflen; /* wrap */ get_fields(dsb, (dsb->buffer + ipos), &fieldL, &fieldR); set_fields(obp, fieldL, fieldR); obp += oAdvance; } return ilen; } static void DSOUND_MixerVol(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len) { INT i, inc = primarybuf->wfx.wBitsPerSample >> 3; BYTE *bpc = buf; INT16 *bps = (INT16 *) buf; TRACE("(%p) left = %lx, right = %lx\n", dsb, dsb->lVolAdjust, dsb->rVolAdjust); if ((!(dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->pan == 0)) && (!(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) || (dsb->volume == 0)) && !(dsb->dsbd.dwFlags & DSBCAPS_CTRL3D)) return; /* Nothing to do */ /* If we end up with some bozo coder using panning or 3D sound */ /* with a mono primary buffer, it could sound very weird using */ /* this method. Oh well, tough patooties. */ for (i = 0; i < len; i += inc) { INT val; switch (inc) { case 1: /* 8-bit WAV is unsigned, but we need to operate */ /* on signed data for this to work properly */ val = *bpc - 128; val = ((val * (i & inc ? dsb->rVolAdjust : dsb->lVolAdjust)) >> 15); *bpc = val + 128; bpc++; break; case 2: /* 16-bit WAV is signed -- much better */ val = *bps; val = ((val * ((i & inc) ? dsb->rVolAdjust : dsb->lVolAdjust)) >> 15); *bps = val; bps++; break; default: /* Very ugly! */ FIXME("MixerVol had a nasty error\n"); } } } #ifdef USE_DSOUND3D static void DSOUND_Mixer3D(IDirectSoundBufferImpl *dsb, BYTE *buf, INT len) { BYTE *ibp, *obp; DWORD buflen, playpos; buflen = dsb->ds3db->buflen; playpos = (dsb->playpos * primarybuf->wfx.nBlockAlign) / dsb->wfx.nBlockAlign; ibp = dsb->ds3db->buffer + playpos; obp = buf; if (playpos > buflen) { FIXME("Major breakage"); return; } if (len <= (playpos + buflen)) memcpy(obp, ibp, len); else { /* wrap */ memcpy(obp, ibp, buflen - playpos); memcpy(obp + (buflen - playpos), dsb->buffer, len - (buflen - playpos)); } return; } #endif static void *tmp_buffer; static size_t tmp_buffer_len = 0; static void *DSOUND_tmpbuffer(size_t len) { if (len>tmp_buffer_len) { void *new_buffer = realloc(tmp_buffer, len); if (new_buffer) { tmp_buffer = new_buffer; tmp_buffer_len = len; } return new_buffer; } return tmp_buffer; } static DWORD DSOUND_MixInBuffer(IDirectSoundBufferImpl *dsb) { INT i, len, ilen, temp, field; INT advance = primarybuf->wfx.wBitsPerSample >> 3; BYTE *buf, *ibuf, *obuf; INT16 *ibufs, *obufs; len = DSOUND_FRAGLEN; /* The most we will use */ if (!(dsb->playflags & DSBPLAY_LOOPING)) { temp = MulDiv(primarybuf->wfx.nAvgBytesPerSec, dsb->buflen, dsb->nAvgBytesPerSec) - MulDiv(primarybuf->wfx.nAvgBytesPerSec, dsb->playpos, dsb->nAvgBytesPerSec); len = (len > temp) ? temp : len; } len &= ~3; /* 4 byte alignment */ if (len == 0) { /* This should only happen if we aren't looping and temp < 4 */ /* We skip the remainder, so check for possible events */ DSOUND_CheckEvent(dsb, dsb->buflen - dsb->playpos); /* Stop */ dsb->playing = 0; dsb->writepos = 0; dsb->playpos = 0; /* Check for DSBPN_OFFSETSTOP */ DSOUND_CheckEvent(dsb, 0); return 0; } /* Been seeing segfaults in malloc() for some reason... */ TRACE("allocating buffer (size = %d)\n", len); if ((buf = ibuf = (BYTE *) DSOUND_tmpbuffer(len)) == NULL) return 0; TRACE("MixInBuffer (%p) len = %d\n", dsb, len); ilen = DSOUND_MixerNorm(dsb, ibuf, len); if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) || (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) DSOUND_MixerVol(dsb, ibuf, len); obuf = primarybuf->buffer + primarybuf->playpos; for (i = 0; i < len; i += advance) { obufs = (INT16 *) obuf; ibufs = (INT16 *) ibuf; if (primarybuf->wfx.wBitsPerSample == 8) { /* 8-bit WAV is unsigned */ field = (*ibuf - 128); field += (*obuf - 128); field = field > 127 ? 127 : field; field = field < -128 ? -128 : field; *obuf = field + 128; } else { /* 16-bit WAV is signed */ field = *ibufs; field += *obufs; field = field > 32767 ? 32767 : field; field = field < -32768 ? -32768 : field; *obufs = field; } ibuf += advance; obuf += advance; if (obuf >= (BYTE *)(primarybuf->buffer + primarybuf->buflen)) obuf = primarybuf->buffer; } /* free(buf); */ if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY) DSOUND_CheckEvent(dsb, ilen); dsb->playpos += ilen; dsb->writepos = dsb->playpos + ilen; if (dsb->playpos >= dsb->buflen) { if (!(dsb->playflags & DSBPLAY_LOOPING)) { dsb->playing = 0; dsb->writepos = 0; dsb->playpos = 0; DSOUND_CheckEvent(dsb, 0); /* For DSBPN_OFFSETSTOP */ } else dsb->playpos %= dsb->buflen; /* wrap */ } if (dsb->writepos >= dsb->buflen) dsb->writepos %= dsb->buflen; return len; } static DWORD WINAPI DSOUND_MixPrimary(void) { INT i, len, maxlen = 0; IDirectSoundBufferImpl *dsb; for (i = dsound->nrofbuffers - 1; i >= 0; i--) { dsb = dsound->buffers[i]; if (!dsb || !(dsb->lpvtbl)) continue; IDirectSoundBuffer_AddRef((LPDIRECTSOUNDBUFFER)dsb); if (dsb->buflen && dsb->playing) { EnterCriticalSection(&(dsb->lock)); len = DSOUND_MixInBuffer(dsb); maxlen = len > maxlen ? len : maxlen; LeaveCriticalSection(&(dsb->lock)); } IDirectSoundBuffer_Release((LPDIRECTSOUNDBUFFER)dsb); } return maxlen; } static int DSOUND_OpenAudio(void) { int audioFragment; if (primarybuf == NULL) return DSERR_OUTOFMEMORY; while (audiofd != -1) sleep(5); audiofd = open("/dev/audio",O_WRONLY); if (audiofd==-1) { /* Don't worry if sound is busy at the moment */ if (errno != EBUSY) perror("open /dev/audio"); return audiofd; /* -1 */ } /* We should probably do something here if SETFRAGMENT fails... */ audioFragment=0x0002000c; if (-1==ioctl(audiofd,SNDCTL_DSP_SETFRAGMENT,&audioFragment)) perror("ioctl SETFRAGMENT"); audioOK = 1; DSOUND_setformat(&(primarybuf->wfx)); return 0; } static void DSOUND_CloseAudio(void) { int neutral; neutral = primarybuf->wfx.wBitsPerSample == 8 ? 128 : 0; audioOK = 0; /* race condition */ Sleep(5); /* It's possible we've been called with audio closed */ /* from SetFormat()... this is just to force a call */ /* to OpenAudio() to reset the hardware properly */ if (audiofd != -1) close(audiofd); primarybuf->playpos = 0; primarybuf->writepos = DSOUND_FRAGLEN; memset(primarybuf->buffer, neutral, primarybuf->buflen); audiofd = -1; TRACE("Audio stopped\n"); } static int DSOUND_WriteAudio(char *buf, int len) { int result, left = 0; while (left < len) { result = write(audiofd, buf + left, len - left); if (result == -1) { if (errno == EINTR) continue; else return result; } left += result; } return 0; } static void DSOUND_OutputPrimary(int len) { int neutral, flen1, flen2; char *frag1, *frag2; /* This is a bad place for this. We need to clear the */ /* buffer with a neutral value, for unsigned 8-bit WAVE */ /* that's 128, for signed 16-bit it's 0 */ neutral = primarybuf->wfx.wBitsPerSample == 8 ? 128 : 0; /* **** */ EnterCriticalSection(&(primarybuf->lock)); /* Write out the */ if ((audioOK == 1) || (DSOUND_OpenAudio() == 0)) { if (primarybuf->playpos + len >= primarybuf->buflen) { frag1 = primarybuf->buffer + primarybuf->playpos; flen1 = primarybuf->buflen - primarybuf->playpos; frag2 = primarybuf->buffer; flen2 = len - (primarybuf->buflen - primarybuf->playpos); if (DSOUND_WriteAudio(frag1, flen1) != 0) { perror("DSOUND_WriteAudio"); LeaveCriticalSection(&(primarybuf->lock)); ExitThread(0); } memset(frag1, neutral, flen1); if (DSOUND_WriteAudio(frag2, flen2) != 0) { perror("DSOUND_WriteAudio"); LeaveCriticalSection(&(primarybuf->lock)); ExitThread(0); } memset(frag2, neutral, flen2); } else { frag1 = primarybuf->buffer + primarybuf->playpos; flen1 = len; if (DSOUND_WriteAudio(frag1, flen1) != 0) { perror("DSOUND_WriteAudio"); LeaveCriticalSection(&(primarybuf->lock)); ExitThread(0); } memset(frag1, neutral, flen1); } } else { /* Can't play audio at the moment -- we need to sleep */ /* to make up for the time we'd be blocked in write() */ /* to /dev/audio */ Sleep(60); } primarybuf->playpos += len; if (primarybuf->playpos >= primarybuf->buflen) primarybuf->playpos %= primarybuf->buflen; primarybuf->writepos = primarybuf->playpos + DSOUND_FRAGLEN; if (primarybuf->writepos >= primarybuf->buflen) primarybuf->writepos %= primarybuf->buflen; LeaveCriticalSection(&(primarybuf->lock)); /* **** */ } static DWORD WINAPI DSOUND_thread(LPVOID arg) { int len; TRACE("dsound is at pid %d\n",getpid()); while (1) { if (!dsound) { WARN("DSOUND thread giving up.\n"); ExitThread(0); } if (getppid()==1) { WARN("DSOUND father died? Giving up.\n"); ExitThread(0); } /* RACE: dsound could be deleted */ IDirectSound_AddRef((LPDIRECTSOUND)dsound); if (primarybuf == NULL) { /* Should never happen */ WARN("Lost the primary buffer!\n"); IDirectSound_Release((LPDIRECTSOUND)dsound); ExitThread(0); } /* **** */ EnterCriticalSection(&(primarybuf->lock)); len = DSOUND_MixPrimary(); LeaveCriticalSection(&(primarybuf->lock)); /* **** */ if (primarybuf->playing) len = DSOUND_FRAGLEN > len ? DSOUND_FRAGLEN : len; if (len) { /* This does all the work */ DSOUND_OutputPrimary(len); } else { /* no buffers playing -- close and wait */ if (audioOK) DSOUND_CloseAudio(); Sleep(100); } IDirectSound_Release((LPDIRECTSOUND)dsound); } ExitThread(0); } #endif /* HAVE_OSS */ HRESULT WINAPI DirectSoundCreate(REFGUID lpGUID,LPDIRECTSOUND *ppDS,IUnknown *pUnkOuter ) { IDirectSoundImpl** ippDS=(IDirectSoundImpl**)ppDS; if (lpGUID) TRACE("(%p,%p,%p)\n",lpGUID,ippDS,pUnkOuter); else TRACE("DirectSoundCreate (%p)\n", ippDS); #ifdef HAVE_OSS if (ippDS == NULL) return DSERR_INVALIDPARAM; if (primarybuf) { IDirectSound_AddRef((LPDIRECTSOUND)dsound); *ippDS = dsound; return DS_OK; } /* Check that we actually have audio capabilities */ /* If we do, whether it's busy or not, we continue */ /* otherwise we return with DSERR_NODRIVER */ audiofd = open("/dev/audio",O_WRONLY); if (audiofd == -1) { if (errno == ENODEV) { MESSAGE("No sound hardware found.\n"); return DSERR_NODRIVER; } else if (errno == EBUSY) { MESSAGE("Sound device busy, will keep trying.\n"); } else { MESSAGE("Unexpected error (%d) while checking for sound support.\n",errno); return DSERR_GENERIC; } } else { close(audiofd); audiofd = -1; } *ippDS = (IDirectSoundImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSoundImpl)); if (*ippDS == NULL) return DSERR_OUTOFMEMORY; (*ippDS)->ref = 1; (*ippDS)->lpvtbl = &dsvt; (*ippDS)->buffers = NULL; (*ippDS)->nrofbuffers = 0; (*ippDS)->wfx.wFormatTag = 1; (*ippDS)->wfx.nChannels = 2; (*ippDS)->wfx.nSamplesPerSec = 22050; (*ippDS)->wfx.nAvgBytesPerSec = 44100; (*ippDS)->wfx.nBlockAlign = 2; (*ippDS)->wfx.wBitsPerSample = 8; if (!dsound) { HANDLE hnd; DWORD xid; dsound = (*ippDS); if (primarybuf == NULL) { DSBUFFERDESC dsbd; HRESULT hr; dsbd.dwSize = sizeof(DSBUFFERDESC); dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER; dsbd.dwBufferBytes = 0; dsbd.lpwfxFormat = &(dsound->wfx); hr = IDirectSound_CreateSoundBuffer(*ppDS, &dsbd, (LPDIRECTSOUNDBUFFER*)&primarybuf, NULL); if (hr != DS_OK) return hr; dsound->primary = primarybuf; } memset(primarybuf->buffer, 128, primarybuf->buflen); hnd = CreateThread(NULL,0,DSOUND_thread,0,0,&xid); } return DS_OK; #else MessageBoxA(0,"DirectSound needs the Open Sound System Driver, which has not been found by ./configure.","WINE DirectSound",MB_OK|MB_ICONSTOP); return DSERR_NODRIVER; #endif } /******************************************************************************* * DirectSound ClassFactory */ typedef struct { /* IUnknown fields */ ICOM_VTABLE(IClassFactory)* lpvtbl; DWORD ref; } IClassFactoryImpl; static HRESULT WINAPI DSCF_QueryInterface(LPCLASSFACTORY iface,REFIID riid,LPVOID *ppobj) { ICOM_THIS(IClassFactoryImpl,iface); char buf[80]; if (HIWORD(riid)) WINE_StringFromCLSID(riid,buf); else sprintf(buf,"<guid-0x%04x>",LOWORD(riid)); FIXME("(%p)->(%s,%p),stub!\n",This,buf,ppobj); return E_NOINTERFACE; } static ULONG WINAPI DSCF_AddRef(LPCLASSFACTORY iface) { ICOM_THIS(IClassFactoryImpl,iface); return ++(This->ref); } static ULONG WINAPI DSCF_Release(LPCLASSFACTORY iface) { ICOM_THIS(IClassFactoryImpl,iface); /* static class, won't be freed */ return --(This->ref); } static HRESULT WINAPI DSCF_CreateInstance( LPCLASSFACTORY iface,LPUNKNOWN pOuter,REFIID riid,LPVOID *ppobj ) { ICOM_THIS(IClassFactoryImpl,iface); char buf[80]; WINE_StringFromCLSID(riid,buf); TRACE("(%p)->(%p,%s,%p)\n",This,pOuter,buf,ppobj); if (!memcmp(riid,&IID_IDirectSound,sizeof(IID_IDirectSound))) { /* FIXME: reuse already created dsound if present? */ return DirectSoundCreate(riid,(LPDIRECTSOUND*)ppobj,pOuter); } return E_NOINTERFACE; } static HRESULT WINAPI DSCF_LockServer(LPCLASSFACTORY iface,BOOL dolock) { ICOM_THIS(IClassFactoryImpl,iface); FIXME("(%p)->(%d),stub!\n",This,dolock); return S_OK; } static ICOM_VTABLE(IClassFactory) DSCF_Vtbl = { DSCF_QueryInterface, DSCF_AddRef, DSCF_Release, DSCF_CreateInstance, DSCF_LockServer }; static IClassFactoryImpl DSOUND_CF = {&DSCF_Vtbl, 1 }; /******************************************************************************* * DllGetClassObject [DSOUND.4] * Retrieves class object from a DLL object * * NOTES * Docs say returns STDAPI * * PARAMS * rclsid [I] CLSID for the class object * riid [I] Reference to identifier of interface for class object * ppv [O] Address of variable to receive interface pointer for riid * * RETURNS * Success: S_OK * Failure: CLASS_E_CLASSNOTAVAILABLE, E_OUTOFMEMORY, E_INVALIDARG, * E_UNEXPECTED */ DWORD WINAPI DSOUND_DllGetClassObject(REFCLSID rclsid,REFIID riid,LPVOID *ppv) { char buf[80],xbuf[80]; if (HIWORD(rclsid)) WINE_StringFromCLSID(rclsid,xbuf); else sprintf(xbuf,"<guid-0x%04x>",LOWORD(rclsid)); if (HIWORD(riid)) WINE_StringFromCLSID(riid,buf); else sprintf(buf,"<guid-0x%04x>",LOWORD(riid)); WINE_StringFromCLSID(riid,xbuf); TRACE("(%p,%p,%p)\n", xbuf, buf, ppv); if (!memcmp(riid,&IID_IClassFactory,sizeof(IID_IClassFactory))) { *ppv = (LPVOID)&DSOUND_CF; IClassFactory_AddRef((IClassFactory*)*ppv); return S_OK; } FIXME("(%p,%p,%p): no interface found.\n", xbuf, buf, ppv); return E_NOINTERFACE; } /******************************************************************************* * DllCanUnloadNow [DSOUND.3] Determines whether the DLL is in use. * * RETURNS * Success: S_OK * Failure: S_FALSE */ DWORD WINAPI DSOUND_DllCanUnloadNow(void) { FIXME("(void): stub\n"); return S_FALSE; }