wine/multimedia/dsound.c
Robert Riggs b0fc58cd13 DirectSound3DListener and DirectSound3DBuffer stubs, new mixer code,
primary buffer is implemented closer to what the SDK docs specify,
other SDK documented behaviour enforced. Completed the devcaps list.
1998-11-22 15:08:48 +00:00

1918 lines
49 KiB
C

/* 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.
*
* FIXME: This file is full of race conditions and unlocked variable access
* from two threads. But we usually don't need to bother.
*
* Tested with a Soundblaster clone and a Gravis UltraSound Classic.
*
* 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.
*/
#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 "windows.h"
#include "winerror.h"
#include "interfaces.h"
#include "mmsystem.h"
#include "dsound.h"
#include "thread.h"
#include "debug.h"
#include "xmalloc.h"
#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 */
static int audiofd = -1;
static int audioOK = 0;
static LPDIRECTSOUND dsound = NULL;
static LPDIRECTSOUNDBUFFER primarybuf = NULL;
static int DSOUND_setformat(LPWAVEFORMATEX wfex);
static void DSOUND_CloseAudio(void);
#endif
HRESULT WINAPI DirectSoundEnumerate32A(LPDSENUMCALLBACK32A enumcb,LPVOID context) {
#ifdef HAVE_OSS
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 IDirectSound3DBuffer_QueryInterface(
LPDIRECTSOUND3DBUFFER this, REFIID riid, LPVOID *ppobj)
{
char xbuf[50];
WINE_StringFromCLSID(riid,xbuf);
TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
return E_FAIL;
}
static ULONG WINAPI IDirectSound3DBuffer_AddRef(LPDIRECTSOUND3DBUFFER this)
{
this->ref++;
return this->ref;
}
static ULONG WINAPI IDirectSound3DBuffer_Release(LPDIRECTSOUND3DBUFFER this)
{
if(--this->ref)
return this->ref;
HeapFree(GetProcessHeap(),0,this->buffer);
HeapFree(GetProcessHeap(),0,this);
return 0;
}
// IDirectSound3DBuffer methods
static HRESULT WINAPI IDirectSound3DBuffer_GetAllParameters(
LPDIRECTSOUND3DBUFFER this,
LPDS3DBUFFER lpDs3dBuffer)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DBuffer_GetConeAngles(
LPDIRECTSOUND3DBUFFER this,
LPDWORD lpdwInsideConeAngle,
LPDWORD lpdwOutsideConeAngle)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DBuffer_GetConeOrientation(
LPDIRECTSOUND3DBUFFER this,
LPD3DVECTOR lpvConeOrientation)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DBuffer_GetConeOutsideVolume(
LPDIRECTSOUND3DBUFFER this,
LPLONG lplConeOutsideVolume)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DBuffer_GetMaxDistance(
LPDIRECTSOUND3DBUFFER this,
LPD3DVALUE lpfMaxDistance)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DBuffer_GetMinDistance(
LPDIRECTSOUND3DBUFFER this,
LPD3DVALUE lpfMinDistance)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DBuffer_GetMode(
LPDIRECTSOUND3DBUFFER this,
LPDWORD lpdwMode)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DBuffer_GetPosition(
LPDIRECTSOUND3DBUFFER this,
LPD3DVECTOR lpvPosition)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DBuffer_GetVelocity(
LPDIRECTSOUND3DBUFFER this,
LPD3DVECTOR lpvVelocity)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DBuffer_SetAllParameters(
LPDIRECTSOUND3DBUFFER this,
LPCDS3DBUFFER lpcDs3dBuffer,
DWORD dwApply)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DBuffer_SetConeAngles(
LPDIRECTSOUND3DBUFFER this,
DWORD dwInsideConeAngle,
DWORD dwOutsideConeAngle,
DWORD dwApply)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DBuffer_SetConeOrientation(
LPDIRECTSOUND3DBUFFER this,
D3DVALUE x, D3DVALUE y, D3DVALUE z,
DWORD dwApply)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DBuffer_SetConeOutsideVolume(
LPDIRECTSOUND3DBUFFER this,
LONG lConeOutsideVolume,
DWORD dwApply)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DBuffer_SetMaxDistance(
LPDIRECTSOUND3DBUFFER this,
D3DVALUE fMaxDistance,
DWORD dwApply)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DBuffer_SetMinDistance(
LPDIRECTSOUND3DBUFFER this,
D3DVALUE fMinDistance,
DWORD dwApply)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DBuffer_SetMode(
LPDIRECTSOUND3DBUFFER this,
DWORD dwMode,
DWORD dwApply)
{
TRACE(dsound, "mode = %lx\n", dwMode);
this->ds3db.dwMode = dwMode;
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DBuffer_SetPosition(
LPDIRECTSOUND3DBUFFER this,
D3DVALUE x, D3DVALUE y, D3DVALUE z,
DWORD dwApply)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DBuffer_SetVelocity(
LPDIRECTSOUND3DBUFFER this,
D3DVALUE x, D3DVALUE y, D3DVALUE z,
DWORD dwApply)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
IDirectSound3DBuffer_VTable ds3dbvt = {
// IUnknown methods
IDirectSound3DBuffer_QueryInterface,
IDirectSound3DBuffer_AddRef,
IDirectSound3DBuffer_Release,
// IDirectSound3DBuffer methods
IDirectSound3DBuffer_GetAllParameters,
IDirectSound3DBuffer_GetConeAngles,
IDirectSound3DBuffer_GetConeOrientation,
IDirectSound3DBuffer_GetConeOutsideVolume,
IDirectSound3DBuffer_GetMaxDistance,
IDirectSound3DBuffer_GetMinDistance,
IDirectSound3DBuffer_GetMode,
IDirectSound3DBuffer_GetPosition,
IDirectSound3DBuffer_GetVelocity,
IDirectSound3DBuffer_SetAllParameters,
IDirectSound3DBuffer_SetConeAngles,
IDirectSound3DBuffer_SetConeOrientation,
IDirectSound3DBuffer_SetConeOutsideVolume,
IDirectSound3DBuffer_SetMaxDistance,
IDirectSound3DBuffer_SetMinDistance,
IDirectSound3DBuffer_SetMode,
IDirectSound3DBuffer_SetPosition,
IDirectSound3DBuffer_SetVelocity,
};
#ifdef USE_DSOUND3D
static int DSOUND_Create3DBuffer(LPDIRECTSOUNDBUFFER 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 IDirectSound3DListener_QueryInterface(
LPDIRECTSOUND3DLISTENER this, REFIID riid, LPVOID *ppobj)
{
char xbuf[50];
WINE_StringFromCLSID(riid,xbuf);
TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
return E_FAIL;
}
static ULONG WINAPI IDirectSound3DListener_AddRef(LPDIRECTSOUND3DLISTENER this)
{
this->ref++;
return this->ref;
}
static ULONG WINAPI IDirectSound3DListener_Release(LPDIRECTSOUND3DLISTENER this)
{
this->ref--;
return this->ref;
}
// IDirectSound3DListener methods
static HRESULT WINAPI IDirectSound3DListener_GetAllParameter(
LPDIRECTSOUND3DLISTENER this,
LPDS3DLISTENER lpDS3DL)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DListener_GetDistanceFactor(
LPDIRECTSOUND3DLISTENER this,
LPD3DVALUE lpfDistanceFactor)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DListener_GetDopplerFactor(
LPDIRECTSOUND3DLISTENER this,
LPD3DVALUE lpfDopplerFactor)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DListener_GetOrientation(
LPDIRECTSOUND3DLISTENER this,
LPD3DVECTOR lpvOrientFront,
LPD3DVECTOR lpvOrientTop)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DListener_GetPosition(
LPDIRECTSOUND3DLISTENER this,
LPD3DVECTOR lpvPosition)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DListener_GetRolloffFactor(
LPDIRECTSOUND3DLISTENER this,
LPD3DVALUE lpfRolloffFactor)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DListener_GetVelocity(
LPDIRECTSOUND3DLISTENER this,
LPD3DVECTOR lpvVelocity)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DListener_SetAllParameters(
LPDIRECTSOUND3DLISTENER this,
LPCDS3DLISTENER lpcDS3DL,
DWORD dwApply)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DListener_SetDistanceFactor(
LPDIRECTSOUND3DLISTENER this,
D3DVALUE fDistanceFactor,
DWORD dwApply)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DListener_SetDopplerFactor(
LPDIRECTSOUND3DLISTENER this,
D3DVALUE fDopplerFactor,
DWORD dwApply)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DListener_SetOrientation(
LPDIRECTSOUND3DLISTENER this,
D3DVALUE xFront, D3DVALUE yFront, D3DVALUE zFront,
D3DVALUE xTop, D3DVALUE yTop, D3DVALUE zTop,
DWORD dwApply)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DListener_SetPosition(
LPDIRECTSOUND3DLISTENER this,
D3DVALUE x, D3DVALUE y, D3DVALUE z,
DWORD dwApply)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DListener_SetRolloffFactor(
LPDIRECTSOUND3DLISTENER this,
D3DVALUE fRolloffFactor,
DWORD dwApply)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DListener_SetVelocity(
LPDIRECTSOUND3DLISTENER this,
D3DVALUE x, D3DVALUE y, D3DVALUE z,
DWORD dwApply)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
static HRESULT WINAPI IDirectSound3DListener_CommitDeferredSettings(
LPDIRECTSOUND3DLISTENER this)
{
FIXME(dsound,"stub\n");
return DS_OK;
}
IDirectSound3DListener_VTable ds3dlvt = {
// IUnknown methods
IDirectSound3DListener_QueryInterface,
IDirectSound3DListener_AddRef,
IDirectSound3DListener_Release,
// IDirectSound3DListener methods
IDirectSound3DListener_GetAllParameter,
IDirectSound3DListener_GetDistanceFactor,
IDirectSound3DListener_GetDopplerFactor,
IDirectSound3DListener_GetOrientation,
IDirectSound3DListener_GetPosition,
IDirectSound3DListener_GetRolloffFactor,
IDirectSound3DListener_GetVelocity,
IDirectSound3DListener_SetAllParameters,
IDirectSound3DListener_SetDistanceFactor,
IDirectSound3DListener_SetDopplerFactor,
IDirectSound3DListener_SetOrientation,
IDirectSound3DListener_SetPosition,
IDirectSound3DListener_SetRolloffFactor,
IDirectSound3DListener_SetVelocity,
IDirectSound3DListener_CommitDeferredSettings,
};
/*******************************************************************************
* IDirectSoundNotify
*/
static HRESULT WINAPI IDirectSoundNotify_QueryInterface(
LPDIRECTSOUNDNOTIFY this,REFIID riid,LPVOID *ppobj
) {
char xbuf[50];
WINE_StringFromCLSID(riid,xbuf);
TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
return E_FAIL;
}
static ULONG WINAPI IDirectSoundNotify_AddRef(LPDIRECTSOUNDNOTIFY this) {
return ++(this->ref);
}
static ULONG WINAPI IDirectSoundNotify_Release(LPDIRECTSOUNDNOTIFY this) {
this->ref--;
if (!this->ref) {
this->dsb->lpvtbl->fnRelease(this->dsb);
HeapFree(GetProcessHeap(),0,this);
return 0;
}
return this->ref;
}
static int _sort_notifies(const void *a,const void *b) {
LPDSBPOSITIONNOTIFY na = (LPDSBPOSITIONNOTIFY)a;
LPDSBPOSITIONNOTIFY nb = (LPDSBPOSITIONNOTIFY)b;
return na->dwOffset-nb->dwOffset;
}
static HRESULT WINAPI IDirectSoundNotify_SetNotificationPositions(
LPDIRECTSOUNDNOTIFY this,DWORD howmuch,LPCDSBPOSITIONNOTIFY notify
) {
int i;
if (TRACE_ON(dsound)) {
TRACE(dsound,"(%p,0x%08lx,%p)\n",this,howmuch,notify);
for (i=0;i<howmuch;i++)
TRACE(dsound,"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;
qsort(this->dsb->notifies,this->dsb->nrofnotifies,sizeof(DSBPOSITIONNOTIFY),_sort_notifies);
return 0;
}
IDirectSoundNotify_VTable dsnvt = {
IDirectSoundNotify_QueryInterface,
IDirectSoundNotify_AddRef,
IDirectSoundNotify_Release,
IDirectSoundNotify_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 IDirectSoundBuffer_SetFormat(
LPDIRECTSOUNDBUFFER this,LPWAVEFORMATEX wfex
) {
LPDIRECTSOUNDBUFFER *dsb;
int i;
if (primarybuf->wfx.nSamplesPerSec != wfex->nSamplesPerSec) {
dsb = dsound->buffers;
for (i = 0; i < dsound->nrofbuffers; i++, dsb++)
(*dsb)->freqAdjust = ((*dsb)->freq << 14) / wfex->nSamplesPerSec;
}
memcpy(&(primarybuf->wfx),wfex,sizeof(primarybuf->wfx));
TRACE(dsound,"(%p,%p)\n", this,wfex);
TRACE(dsound,"(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 (this->dsbd.dwFlags & DSBCAPS_PRIMARYBUFFER) {
this->wfx.nAvgBytesPerSec =
this->wfx.nSamplesPerSec * this->wfx.nBlockAlign;
DSOUND_CloseAudio();
return DS_OK;
}
return 0;
}
static HRESULT WINAPI IDirectSoundBuffer_SetVolume(
LPDIRECTSOUNDBUFFER this,LONG vol
) {
double temp;
TRACE(dsound,"(%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(dsound, "Volume control of primary unimplemented.\n");
this->volume = vol;
return DS_OK;
}
this->volume = vol;
temp = (double) (this->volume + (this->pan < 0 ? this->pan : 0));
this->lVolAdjust = (ULONG) (pow(2.0, temp / 600.0) * 65536.0);
temp = (double) (this->volume - (this->pan > 0 ? this->pan : 0));
this->rVolAdjust = (ULONG) (pow(2.0, temp / 600.0) * 65536.0);
TRACE(dsound, "left = %lx, right = %lx\n", this->lVolAdjust, this->rVolAdjust);
return DS_OK;
}
static HRESULT WINAPI IDirectSoundBuffer_GetVolume(
LPDIRECTSOUNDBUFFER this,LPLONG vol
) {
TRACE(dsound,"(%p,%p)\n",this,vol);
*vol = this->volume;
return 0;
}
static HRESULT WINAPI IDirectSoundBuffer_SetFrequency(
LPDIRECTSOUNDBUFFER this,DWORD freq
) {
TRACE(dsound,"(%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;
this->freq = freq;
this->freqAdjust = (freq << 14) / primarybuf->wfx.nSamplesPerSec;
this->nAvgBytesPerSec = freq * (this->wfx.wBitsPerSample >> 3) * this->wfx.nChannels;
return DS_OK;
}
static HRESULT WINAPI IDirectSoundBuffer_Play(
LPDIRECTSOUNDBUFFER this,DWORD reserved1,DWORD reserved2,DWORD flags
) {
TRACE(dsound,"(%p,%08lx,%08lx,%08lx)\n",
this,reserved1,reserved2,flags
);
this->playflags = flags;
this->playing = 1;
return 0;
}
static HRESULT WINAPI IDirectSoundBuffer_Stop(LPDIRECTSOUNDBUFFER this) {
TRACE(dsound,"(%p)\n",this);
this->playing = 0;
return 0;
}
static DWORD WINAPI IDirectSoundBuffer_AddRef(LPDIRECTSOUNDBUFFER this) {
// TRACE(dsound,"(%p) ref was %ld\n",this, this->ref);
return ++(this->ref);
}
static DWORD WINAPI IDirectSoundBuffer_Release(LPDIRECTSOUNDBUFFER this) {
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--;
this->dsound->lpvtbl->fnRelease(this->dsound);
}
if (this->ds3db && this->ds3db->lpvtbl)
this->ds3db->lpvtbl->fnRelease(this->ds3db);
HeapFree(GetProcessHeap(),0,this->buffer);
HeapFree(GetProcessHeap(),0,this);
return 0;
}
static HRESULT WINAPI IDirectSoundBuffer_GetCurrentPosition(
LPDIRECTSOUNDBUFFER this,LPDWORD playpos,LPDWORD writepos
) {
TRACE(dsound,"(%p,%p,%p)\n",this,playpos,writepos);
if (playpos) *playpos = this->playpos;
if (writepos) *writepos = this->writepos;
return 0;
}
static HRESULT WINAPI IDirectSoundBuffer_GetStatus(
LPDIRECTSOUNDBUFFER this,LPDWORD status
) {
TRACE(dsound,"(%p,%p)\n",this,status);
*status = 0;
if (this->playing)
*status |= DSBSTATUS_PLAYING;
if (this->playflags & DSBPLAY_LOOPING)
*status |= DSBSTATUS_LOOPING;
return 0;
}
static HRESULT WINAPI IDirectSoundBuffer_GetFormat(
LPDIRECTSOUNDBUFFER this,LPWAVEFORMATEX lpwf,DWORD wfsize,LPDWORD wfwritten
) {
TRACE(dsound,"(%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 0;
}
static HRESULT WINAPI IDirectSoundBuffer_Lock(
LPDIRECTSOUNDBUFFER this,DWORD writecursor,DWORD writebytes,LPVOID lplpaudioptr1,LPDWORD audiobytes1,LPVOID lplpaudioptr2,LPDWORD audiobytes2,DWORD flags
) {
TRACE(dsound,"(%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(dsound,"->%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(dsound,"->%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 0;
}
static HRESULT WINAPI IDirectSoundBuffer_SetCurrentPosition(
LPDIRECTSOUNDBUFFER this,DWORD newpos
) {
TRACE(dsound,"(%p,%ld)\n",this,newpos);
this->playpos = newpos;
return 0;
}
static HRESULT WINAPI IDirectSoundBuffer_SetPan(
LPDIRECTSOUNDBUFFER this,LONG pan
) {
double temp;
TRACE(dsound,"(%p,%ld)\n",this,pan);
// What do we do if some moron uses SetPan with
// a mono primary buffer?
// 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;
if ((pan > DSBPAN_RIGHT) || (pan < DSBPAN_LEFT))
return DSERR_INVALIDPARAM;
this->pan = pan;
temp = (double) (this->volume + (this->pan < 0 ? this->pan : 0));
this->lVolAdjust = (ULONG) (pow(2.0, temp / 600.0) * 65536.0);
temp = (double) (this->volume - (this->pan > 0 ? this->pan : 0));
this->rVolAdjust = (ULONG) (pow(2.0, temp / 600.0) * 65536.0);
return DS_OK;
}
static HRESULT WINAPI IDirectSoundBuffer_GetPan(
LPDIRECTSOUNDBUFFER this,LPLONG pan
) {
TRACE(dsound,"(%p,%p)\n",this,pan);
*pan = this->pan;
return 0;
}
static HRESULT WINAPI IDirectSoundBuffer_Unlock(
LPDIRECTSOUNDBUFFER this,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
) {
FIXME(dsound,"(%p,%p,%ld,%p,%ld):stub\n", this,p1,x1,p2,x2);
this->writepos = this->playpos + (this->wfx.nAvgBytesPerSec >> 4);
this->writepos %= this->buflen;
#if 0
// This is highly experimental and liable to break things
if (this->dsbd.dwFlags & DSBCAPS_CTRL3D)
DSOUND_Create3DBuffer(this);
#endif
return 0;
}
static HRESULT WINAPI IDirectSoundBuffer_GetFrequency(
LPDIRECTSOUNDBUFFER this,LPDWORD freq
) {
TRACE(dsound,"(%p,%p)\n",this,freq);
*freq = this->freq;
return 0;
}
static HRESULT WINAPI IDirectSoundBuffer_Initialize(
LPDIRECTSOUNDBUFFER this,LPDIRECTSOUND dsound,LPDSBUFFERDESC dbsd
) {
FIXME(dsound,"(%p,%p,%p):stub\n",this,dsound,dbsd);
printf("Re-Init!!!\n");
return DSERR_ALREADYINITIALIZED;
}
static HRESULT WINAPI IDirectSoundBuffer_GetCaps(
LPDIRECTSOUNDBUFFER this,LPDSBCAPS caps
) {
TRACE(dsound,"(%p)->(%p)\n",this,caps);
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 IDirectSoundBuffer_QueryInterface(
LPDIRECTSOUNDBUFFER this,REFIID riid,LPVOID *ppobj
) {
char xbuf[50];
WINE_StringFromCLSID(riid,xbuf);
TRACE(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
if (!memcmp(&IID_IDirectSoundNotify,riid,sizeof(*riid))) {
IDirectSoundNotify *dsn;
dsn = (LPDIRECTSOUNDNOTIFY)HeapAlloc(GetProcessHeap(),0,sizeof(*dsn));
dsn->ref = 1;
dsn->dsb = this;
this->lpvtbl->fnAddRef(this);
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 struct tagLPDIRECTSOUNDBUFFER_VTABLE dsbvt = {
IDirectSoundBuffer_QueryInterface,
IDirectSoundBuffer_AddRef,
IDirectSoundBuffer_Release,
IDirectSoundBuffer_GetCaps,
IDirectSoundBuffer_GetCurrentPosition,
IDirectSoundBuffer_GetFormat,
IDirectSoundBuffer_GetVolume,
IDirectSoundBuffer_GetPan,
IDirectSoundBuffer_GetFrequency,
IDirectSoundBuffer_GetStatus,
IDirectSoundBuffer_Initialize,
IDirectSoundBuffer_Lock,
IDirectSoundBuffer_Play,
IDirectSoundBuffer_SetCurrentPosition,
IDirectSoundBuffer_SetFormat,
IDirectSoundBuffer_SetVolume,
IDirectSoundBuffer_SetPan,
IDirectSoundBuffer_SetFrequency,
IDirectSoundBuffer_Stop,
IDirectSoundBuffer_Unlock
};
/*******************************************************************************
* IDirectSound
*/
static HRESULT WINAPI IDirectSound_SetCooperativeLevel(
LPDIRECTSOUND this,HWND32 hwnd,DWORD level
) {
FIXME(dsound,"(%p,%08lx,%ld):stub\n",this,(DWORD)hwnd,level);
return 0;
}
static HRESULT WINAPI IDirectSound_CreateSoundBuffer(
LPDIRECTSOUND this,LPDSBUFFERDESC dsbd,LPLPDIRECTSOUNDBUFFER ppdsb,LPUNKNOWN lpunk
) {
LPWAVEFORMATEX wfex = dsbd->lpwfxFormat;
if (TRACE_ON(dsound)) {
TRACE(dsound,"(%p,%p,%p,%p)\n",this,dsbd,ppdsb,lpunk);
TRACE(dsound,"(size=%ld)\n",dsbd->dwSize);
TRACE(dsound,"(flags=0x%08lx\n",dsbd->dwFlags);
_dump_DSBCAPS(dsbd->dwFlags);
TRACE(dsound,"(bufferbytes=%ld)\n",dsbd->dwBufferBytes);
TRACE(dsound,"(lpwfxFormat=%p)\n",dsbd->lpwfxFormat);
}
if (wfex)
TRACE(dsound,"(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) {
*ppdsb = primarybuf;
return DS_OK;
} // Else create primarybuf
}
*ppdsb = (LPDIRECTSOUNDBUFFER)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBuffer));
if (*ppdsb == NULL)
return DSERR_OUTOFMEMORY;
(*ppdsb)->ref =1;
TRACE(dsound, "Created buffer at %p\n", *ppdsb);
if (dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)
(*ppdsb)->buflen = dsound->wfx.nAvgBytesPerSec;
else
(*ppdsb)->buflen = dsbd->dwBufferBytes;
(*ppdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,(*ppdsb)->buflen);
if ((*ppdsb)->buffer == NULL) {
HeapFree(GetProcessHeap(),0,(*ppdsb));
*ppdsb = NULL;
return DSERR_OUTOFMEMORY;
}
(*ppdsb)->playpos = 0;
(*ppdsb)->writepos = 0;
(*ppdsb)->lpvtbl = &dsbvt;
(*ppdsb)->dsound = this;
(*ppdsb)->playing = 0;
(*ppdsb)->lVolAdjust = (1 << 16);
(*ppdsb)->rVolAdjust = (1 << 16);
(*ppdsb)->freq = dsbd->lpwfxFormat->nSamplesPerSec;
if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
(*ppdsb)->freqAdjust = ((*ppdsb)->freq << 14) /
primarybuf->wfx.nSamplesPerSec;
(*ppdsb)->nAvgBytesPerSec = (*ppdsb)->freq *
(dsbd->lpwfxFormat->wBitsPerSample >> 3) *
dsbd->lpwfxFormat->nChannels;
}
memcpy(&((*ppdsb)->dsbd),dsbd,sizeof(*dsbd));
/* register buffer */
if (!(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER)) {
this->buffers = (LPDIRECTSOUNDBUFFER*)HeapReAlloc(GetProcessHeap(),0,this->buffers,sizeof(LPDIRECTSOUNDBUFFER)*(this->nrofbuffers+1));
this->buffers[this->nrofbuffers] = *ppdsb;
this->nrofbuffers++;
this->lpvtbl->fnAddRef(this);
}
if (dsbd->lpwfxFormat && !(dsbd->dwFlags & DSBCAPS_PRIMARYBUFFER))
memcpy(&((*ppdsb)->wfx), dsbd->lpwfxFormat,
sizeof((*ppdsb)->wfx));
#if 0
if (dsbd->dwFlags & DSBCAPS_CTRL3D) {
IDirectSound3DBuffer *ds3db;
ds3db = (LPDIRECTSOUND3DBUFFER)HeapAlloc(GetProcessHeap(),
0,sizeof(*ds3db));
ds3db->ref = 1;
ds3db->dsb = (*ppdsb);
ds3db->lpvtbl = &ds3dbvt;
(*ppdsb)->ds3db = ds3db;
ds3db->ds3db.dwSize = sizeof(DS3DBUFFER);
ds3db->ds3db.vPosition.x = 0.0;
ds3db->ds3db.vPosition.y = 0.0;
ds3db->ds3db.vPosition.z = 0.0;
ds3db->ds3db.vVelocity.x = 0.0;
ds3db->ds3db.vVelocity.y = 0.0;
ds3db->ds3db.vVelocity.z = 0.0;
ds3db->ds3db.dwInsideConeAngle = DS3D_DEFAULTCONEANGLE;
ds3db->ds3db.dwOutsideConeAngle = DS3D_DEFAULTCONEANGLE;
ds3db->ds3db.vConeOrientation.x = 0.0;
ds3db->ds3db.vConeOrientation.y = 0.0;
ds3db->ds3db.vConeOrientation.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 = ((*ppdsb)->buflen * primarybuf->wfx.nBlockAlign) /
(*ppdsb)->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 IDirectSound_DuplicateSoundBuffer(
LPDIRECTSOUND this,LPDIRECTSOUNDBUFFER pdsb,LPLPDIRECTSOUNDBUFFER ppdsb
) {
TRACE(dsound,"(%p,%p,%p)\n",this,pdsb,ppdsb);
*ppdsb = (LPDIRECTSOUNDBUFFER)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDirectSoundBuffer));
(*ppdsb)->ref =1;
(*ppdsb)->buffer = (LPBYTE)HeapAlloc(GetProcessHeap(),0,pdsb->buflen);
memcpy((*ppdsb)->buffer,pdsb->buffer,pdsb->buflen);
(*ppdsb)->buflen = pdsb->buflen;
(*ppdsb)->playpos = 0;
(*ppdsb)->writepos = 0;
(*ppdsb)->lpvtbl = &dsbvt;
(*ppdsb)->dsound = this;
memcpy(&((*ppdsb)->wfx), &(pdsb->wfx), sizeof((*ppdsb)->wfx));
/* register buffer */
this->buffers = (LPDIRECTSOUNDBUFFER*)HeapReAlloc(GetProcessHeap(),0,this->buffers,sizeof(LPDIRECTSOUNDBUFFER)*(this->nrofbuffers+1));
this->buffers[this->nrofbuffers] = *ppdsb;
this->nrofbuffers++;
this->lpvtbl->fnAddRef(this);
return 0;
}
static HRESULT WINAPI IDirectSound_GetCaps(LPDIRECTSOUND this,LPDSCAPS caps) {
TRACE(dsound,"(%p,%p)\n",this,caps);
TRACE(dsound,"(flags=0x%08lx)\n",caps->dwFlags);
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 IDirectSound_AddRef(LPDIRECTSOUND this) {
return ++(this->ref);
}
static ULONG WINAPI IDirectSound_Release(LPDIRECTSOUND this) {
TRACE(dsound,"(%p), ref was %ld\n",this,this->ref);
if (!--(this->ref)) {
HeapFree(GetProcessHeap(),0,this);
dsound = NULL;
DSOUND_CloseAudio();
return 0;
}
return this->ref;
}
static HRESULT WINAPI IDirectSound_SetSpeakerConfig(
LPDIRECTSOUND this,DWORD config
) {
FIXME(dsound,"(%p,0x%08lx):stub\n",this,config);
return 0;
}
static HRESULT WINAPI IDirectSound_QueryInterface(
LPDIRECTSOUND this,REFIID riid,LPVOID *ppobj
) {
char xbuf[50];
if (!memcmp(&IID_IDirectSound3DListener,riid,sizeof(*riid))) {
if (this->listener) {
*ppobj = this->listener;
return DS_OK;
}
this->listener = (LPDIRECTSOUND3DLISTENER)HeapAlloc(
GetProcessHeap(), 0, sizeof(*(this->listener)));
this->listener->ref = 1;
this->listener->lpvtbl = &ds3dlvt;
this->lpvtbl->fnAddRef(this);
this->listener->ds3dl.dwSize = sizeof(DS3DLISTENER);
this->listener->ds3dl.vPosition.x = 0.0;
this->listener->ds3dl.vPosition.y = 0.0;
this->listener->ds3dl.vPosition.z = 0.0;
this->listener->ds3dl.vVelocity.x = 0.0;
this->listener->ds3dl.vVelocity.y = 0.0;
this->listener->ds3dl.vVelocity.z = 0.0;
this->listener->ds3dl.vOrientFront.x = 0.0;
this->listener->ds3dl.vOrientFront.y = 0.0;
this->listener->ds3dl.vOrientFront.z = 1.0;
this->listener->ds3dl.vOrientTop.x = 0.0;
this->listener->ds3dl.vOrientTop.y = 1.0;
this->listener->ds3dl.vOrientTop.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(dsound,"(%p,%s,%p)\n",this,xbuf,ppobj);
return E_FAIL;
}
static struct tagLPDIRECTSOUND_VTABLE dsvt = {
IDirectSound_QueryInterface,
IDirectSound_AddRef,
IDirectSound_Release,
IDirectSound_CreateSoundBuffer,
IDirectSound_GetCaps,
IDirectSound_DuplicateSoundBuffer,
IDirectSound_SetCooperativeLevel,
(void *)8,
(void *)9,
IDirectSound_SetSpeakerConfig,
(void *)11
};
static int
DSOUND_setformat(LPWAVEFORMATEX wfex) {
int xx,channels,speed,format,nformat;
// Race condition here... called by DSOUND_thread() and SetFormat()
if (!audioOK) {
TRACE(dsound, "(%p) deferred\n", wfex);
return 0;
}
switch (wfex->wFormatTag) {
default:
WARN(dsound,"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(dsound,"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(dsound,"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(dsound,"(freq=%ld,channels=%d,bits=%d)\n",
wfex->nSamplesPerSec,wfex->nChannels,wfex->wBitsPerSample
);
return 0;
}
static void DSOUND_CheckEvent(IDirectSoundBuffer *dsb, int len)
{
int i;
DWORD offset;
LPDSBPOSITIONNOTIFY event;
if (dsb->nrofnotifies == 0)
return;
TRACE(dsound,"(%p)\n", dsb);
for (i = 0; i < dsb->nrofnotifies; i++) {
event = dsb->notifies + i;
offset = event->dwOffset;
if ((dsb->playpos + len) >= dsb->buflen)
if ((offset <= (dsb->playpos + len - dsb->buflen)) ||
(offset > dsb->playpos)) {
SetEvent(event->hEventNotify);
TRACE(dsound,"signalled event %d\n", event->hEventNotify);
}
else
if ((offset > dsb->playpos) || (offset <= (dsb->playpos + len))) {
SetEvent(event->hEventNotify);
TRACE(dsound,"signalled event %d\n", event->hEventNotify);
}
}
}
// This should be the correct way to do this.
// Anyone want to do the optimized assembly?
static inline short cvt8to16(char byte)
{
short s = 0, sbit = 1;
char cbit = 1;
int i;
for (i = 0; i < 8; i++) {
if (byte & cbit)
s |= sbit;
cbit <<= 1;
sbit <<= 2;
}
if (byte < 0)
s |= 0x8000; // sign bit
return s;
}
static inline char cvt16to8(short word)
{
char c = 0, cbit = 1;
short sbits = 3;
int i;
for (i = 0; i < 7; i++) {
if (word & sbits)
c |= cbit;
cbit <<= 1;
sbits <<= 2;
}
if (word < 0)
c |= 0x80; // sign bit
return c;
}
static inline void get_fields(const IDirectSoundBuffer *dsb, char *buf, int *fl, int *fr)
{
short *bufs = (short *) buf;
// TRACE(dsound, "(%p)", buf);
if ((dsb->wfx.wBitsPerSample == 8) && dsb->wfx.nChannels == 2) {
*fl = cvt8to16(*buf);
*fr = cvt8to16(*(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) {
*fr = *fl = cvt8to16(*buf);
return;
}
if ((dsb->wfx.wBitsPerSample == 16) && dsb->wfx.nChannels == 1) {
*fr = *fl = *bufs;
return;
}
FIXME(dsound, "get_fields found an unsupported configuration\n");
return;
}
static inline void set_fields(char *buf, int fl, int fr)
{
short *bufs = (short *) buf;
if ((primarybuf->wfx.wBitsPerSample == 8) && (primarybuf->wfx.nChannels == 2)) {
*buf = cvt16to8(fl);
*(buf + 1) = cvt16to8(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 = cvt16to8((fl + fr) / 2);
return;
}
if ((primarybuf->wfx.wBitsPerSample == 16) && (primarybuf->wfx.nChannels == 1)) {
*bufs = (fl + fr) / 2;
return;
}
FIXME(dsound, "set_fields found an unsupported configuration\n");
return;
}
// Now with PerfectPitch (tm) technology
static void DSOUND_MixerNorm(IDirectSoundBuffer *dsb, char *buf, int len)
{
int i, ipos, fieldL, fieldR;
char *ibp, *obp;
int iAdvance = dsb->wfx.nBlockAlign;
int oAdvance = primarybuf->wfx.nBlockAlign;
ibp = dsb->buffer + dsb->playpos;
obp = buf;
TRACE(dsound, "(%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(dsound, "(%p) Best case\n", dsb);
if ((ibp + len) <= (char *)(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;
}
// Check for same sample rate
if (dsb->freq == primarybuf->wfx.nSamplesPerSec) {
TRACE(dsound, "(%p) Same sample rate %ld = primary %ld\n", dsb,
dsb->freq, primarybuf->wfx.nSamplesPerSec);
for (i = 0; i < len; i += oAdvance) {
get_fields(dsb, ibp, &fieldL, &fieldR);
ibp += iAdvance;
set_fields(obp, fieldL, fieldR);
obp += oAdvance;
if (ibp > (char *)(dsb->buffer + dsb->buflen))
ibp = dsb->buffer; // wrap
}
return;
}
// Mix in different sample rates
//
// New PerfectPitch(tm) Technology (c) 1998 Rob Riggs
// Patent Pending :-]
for (i = 0; i < len; i += oAdvance) {
ipos = (iAdvance * ((i * dsb->freqAdjust) >> 14)) + 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;
}
static void DSOUND_MixerVol(IDirectSoundBuffer *dsb, char *buf, int len)
{
int i;
char *bpc;
short *bps;
TRACE(dsound, "(%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 += (primarybuf->wfx.wBitsPerSample >> 3)) {
register int val;
switch (primarybuf->wfx.wBitsPerSample) {
case 8:
bpc = buf + i;
val = *bpc;
val = (val * (i & 1 ? dsb->rVolAdjust : dsb->lVolAdjust)) >> 16;
*bpc = (char) val;
break;
case 16:
bps = (short *) (buf + i);
val = *bps;
val = (val * (i & 1 ? dsb->rVolAdjust : dsb->lVolAdjust)) >> 16;
*bps = (short) val;
break;
default:
// Very ugly!
FIXME(dsound, "MixerVol had a nasty error\n");
}
}
}
#ifdef USE_DSOUND3D
static void DSOUND_Mixer3D(IDirectSoundBuffer *dsb, char *buf, int len)
{
char *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(dsound, "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 DWORD DSOUND_MixInBuffer(IDirectSoundBuffer *dsb)
{
int i, len, ilen, temp, field;
int advance = primarybuf->wfx.wBitsPerSample >> 3;
char *buf, *ibuf, *obuf;
short *ibufs, *obufs;
// The most we will use
len = primarybuf->wfx.nAvgBytesPerSec >> 4; // 60 ms
len &= ~3; // 4 byte alignment
if (!(dsb->playflags & DSBPLAY_LOOPING)) {
temp = ((primarybuf->wfx.nAvgBytesPerSec * dsb->buflen) /
dsb->wfx.nAvgBytesPerSec) -
((primarybuf->wfx.nAvgBytesPerSec * dsb->playpos) /
dsb->nAvgBytesPerSec);
len = (len > temp) ? temp : len;
}
ilen = (len * dsb->nAvgBytesPerSec) / primarybuf->wfx.nAvgBytesPerSec;
if ((buf = ibuf = (char *) malloc(len)) == NULL)
return 0;
TRACE(dsound, "MixInBuffer (%p) len = %d\n", dsb, len);
DSOUND_MixerNorm(dsb, ibuf, len);
if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||
(dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME))
DSOUND_MixerVol(dsb, ibuf, len);
TRACE(dsound, "Mixing buffer - advance = %d\n", advance);
obuf = primarybuf->buffer + primarybuf->playpos;
for (i = 0; i < len; i += advance) {
obufs = (short *) obuf;
ibufs = (short *) ibuf;
if (primarybuf->wfx.wBitsPerSample == 8) {
field = (char) *ibuf;
field += (char) *obuf;
field = field > 127 ? 127 : field;
field = field < -128 ? -128 : field;
*obuf = (char) field;
} else {
field = *ibufs;
field += *obufs;
field = field > 32767 ? 32767 : field;
field = field < -32768 ? -32768 : field;
*obufs = field;
}
ibuf += advance;
obuf += advance;
if (obuf > (char *)(primarybuf->buffer + primarybuf->buflen))
obuf = primarybuf->buffer;
}
free(buf);
if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY)
DSOUND_CheckEvent(dsb, len);
dsb->playpos += ilen;
dsb->writepos += ilen;
if (dsb->playpos >= dsb->buflen) {
if (!(dsb->playflags & DSBPLAY_LOOPING)) {
dsb->playing = 0;
dsb->writepos = 0;
dsb->playpos = 0;
} 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;
IDirectSoundBuffer *dsb;
for (i = dsound->nrofbuffers - 1; i >= 0; i--) {
dsb = dsound->buffers[i];
if (!dsb || !(dsb->lpvtbl))
continue;
dsb->lpvtbl->fnAddRef(dsb);
if (dsb->buflen && dsb->playing) {
len = DSOUND_MixInBuffer(dsb);
maxlen = len > maxlen ? len : maxlen;
}
dsb->lpvtbl->fnRelease(dsb);
}
if (maxlen > 0) {
primarybuf->writepos += maxlen;
if (primarybuf->writepos > primarybuf->buflen)
primarybuf->writepos -= primarybuf->buflen;
}
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) {
perror("open /dev/audio");
audiofd = -1;
return DSERR_NODRIVER;
}
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)
{
audioOK = 0; // race condition
Sleep(5);
close(audiofd);
primarybuf->playpos = 0;
primarybuf->writepos = primarybuf->wfx.nAvgBytesPerSec >> 4;
memset(primarybuf->buffer, 0, primarybuf->buflen);
audiofd = -1;
TRACE(dsound, "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 DWORD WINAPI DSOUND_thread(LPVOID arg)
{
int maxlen = primarybuf->wfx.nAvgBytesPerSec >> 4;
int len;
TRACE(dsound,"dsound is at pid %d\n",getpid());
while (1) {
if (!dsound) {
WARN(dsound,"DSOUND thread giving up.\n");
ExitThread(0);
}
if (getppid()==1) {
WARN(dsound,"DSOUND father died? Giving up.\n");
ExitThread(0);
}
/* RACE: dsound could be deleted */
dsound->lpvtbl->fnAddRef(dsound);
if (primarybuf == NULL) { // Should never happen
/* no soundbuffer yet... wait. */
Sleep(100);
dsound->lpvtbl->fnRelease(dsound);
continue;
}
len = DSOUND_MixPrimary();
if (primarybuf->playing)
len = maxlen > len ? maxlen : len;
if (len) {
if (audioOK == 0)
DSOUND_OpenAudio();
if (primarybuf->playpos + len > primarybuf->buflen) {
if (DSOUND_WriteAudio(
primarybuf->buffer + primarybuf->playpos,
primarybuf->buflen - primarybuf->playpos)
!= 0) {
perror("DSOUND_WriteAudio");
ExitThread(0);
}
memset(primarybuf->buffer + primarybuf->playpos, 0,
primarybuf->buflen - primarybuf->playpos);
if (DSOUND_WriteAudio(primarybuf->buffer,
len - (primarybuf->buflen - primarybuf->playpos)) != 0) {
perror("DSOUND_WriteAudio");
ExitThread(0);
}
memset(primarybuf->buffer, 0,
len - (primarybuf->buflen - primarybuf->playpos));
} else {
if (DSOUND_WriteAudio(
primarybuf->buffer + primarybuf->playpos,
len) != 0) {
perror("DSOUND_WriteAudio");
ExitThread(0);
}
memset(primarybuf->buffer + primarybuf->playpos, 0, len);
}
primarybuf->playpos += len;
if (primarybuf->playpos >= primarybuf->buflen)
primarybuf->playpos -= primarybuf->buflen;
primarybuf->writepos = primarybuf->playpos + maxlen;
if (primarybuf->writepos >= primarybuf->buflen)
primarybuf->writepos -= primarybuf->buflen;
} else {
/* no soundbuffer. close and wait. */
if (audioOK)
DSOUND_CloseAudio();
Sleep(100);
}
dsound->lpvtbl->fnRelease(dsound);
}
ExitThread(0);
}
#endif /* HAVE_OSS */
HRESULT WINAPI DirectSoundCreate(LPGUID lpGUID,LPDIRECTSOUND *ppDS,IUnknown *pUnkOuter )
{
if (lpGUID)
TRACE(dsound,"(%p,%p,%p)\n",lpGUID,ppDS,pUnkOuter);
else
TRACE(dsound,"DirectSoundCreate\n");
#ifdef HAVE_OSS
if (primarybuf)
return DSERR_ALLOCATED;
*ppDS = (LPDIRECTSOUND)HeapAlloc(GetProcessHeap(),0,sizeof(IDirectSound));
(*ppDS)->ref = 1;
(*ppDS)->lpvtbl = &dsvt;
(*ppDS)->buffers = NULL;
(*ppDS)->nrofbuffers = 0;
(*ppDS)->wfx.wFormatTag = 1;
(*ppDS)->wfx.nChannels = 2;
(*ppDS)->wfx.nSamplesPerSec = 22050;
(*ppDS)->wfx.nAvgBytesPerSec = 44100;
(*ppDS)->wfx.nBlockAlign = 2;
(*ppDS)->wfx.wBitsPerSample = 8;
if (!dsound) {
HANDLE32 hnd;
DWORD xid;
dsound = (*ppDS);
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, &primarybuf, NULL);
if (hr != DS_OK) {
dsound->primary = primarybuf;
return hr;
}
}
hnd = CreateThread(NULL,0,DSOUND_thread,0,0,&xid);
}
return 0;
#else
MessageBox32A(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
}
/*******************************************************************************
* 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 DllGetClassObject( REFCLSID rclsid, REFIID riid, LPVOID *ppv )
{
FIXME(dsound, "(%p,%p,%p): stub\n", rclsid, riid, ppv);
return S_OK;
}
/*******************************************************************************
* DllCanUnloadNow [DSOUND.3] Determines whether the DLL is in use.
*
* RETURNS
* Success: S_OK
* Failure: S_FALSE
*/
DWORD WINAPI DllCanUnloadNow(void)
{
FIXME(dsound, "(void): stub\n");
return S_FALSE;
}