Commit a7d6ed8e authored by Maarten Lankhorst's avatar Maarten Lankhorst Committed by Alexandre Julliard

dsound: Simplify mixing by removing remixing support, and fix its waveout breakage.

Based on a patch by Peter Dons Tychsen.
parent 95912460
......@@ -236,8 +236,7 @@ static HRESULT WINAPI IDirectSoundBufferImpl_SetVolume(
hres = IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
if (hres != DS_OK)
WARN("IDsDriverBuffer_SetVolumePan failed\n");
} else
DSOUND_ForceRemix(This);
}
}
LeaveCriticalSection(&(This->lock));
......@@ -297,8 +296,6 @@ static HRESULT WINAPI IDirectSoundBufferImpl_SetFrequency(
This->freqAdjust = (freq << DSOUND_FREQSHIFT) / This->device->pwfx->nSamplesPerSec;
This->nAvgBytesPerSec = freq * This->pwfx->nBlockAlign;
DSOUND_RecalcFormat(This);
if (!This->hwbuf)
DSOUND_ForceRemix(This);
}
LeaveCriticalSection(&(This->lock));
......@@ -424,29 +421,51 @@ DWORD DSOUND_CalcPlayPosition(IDirectSoundBufferImpl *This, DWORD pplay, DWORD p
/* we need to know how far away we are from there */
if (pmix < pplay) pmix += device->buflen; /* wraparound */
pmix -= pplay;
/* detect buffer underrun */
/* detect buffer underrun (sanity) */
if (pwrite < pplay) pwrite += device->buflen; /* wraparound */
pwrite -= pplay;
if (pmix > (ds_snd_queue_max * device->fraglen + pwrite + device->writelead)) {
WARN("detected an underrun: primary queue was %d\n",pmix);
ERR("detected an underrun: primary queue was %d\n",pmix);
pmix = 0;
}
TRACE("primary back-samples=%d\n",pmix);
/* divide the offset by its sample size */
pmix /= device->pwfx->nBlockAlign;
TRACE("primary back-samples=%d\n",pmix);
/* adjust for our frequency */
pmix = (pmix * This->freqAdjust) >> DSOUND_FREQSHIFT;
/* multiply by our own sample size */
pmix *= This->pwfx->nBlockAlign;
TRACE("this back-offset=%d\n", pmix);
/* sanity */
if(pmix > This->buflen){
ERR("Bad length in CalcPlayPosition!\n");
return 0;
}
/* subtract from our last mixed position */
while (bplay < pmix) bplay += This->buflen; /* wraparound */
if (bplay < pmix) bplay += This->buflen; /* wraparound */
bplay -= pmix;
/* check for lead-in */
if (This->leadin && ((bplay < This->startpos) || (bplay > This->buf_mixpos))) {
/* seems we haven't started playing yet */
TRACE("this still in lead-in phase\n");
bplay = This->startpos;
}
/* sanity */
if(bplay > This->buflen){
ERR("Bad play position in CalcPlayPosition!\n");
return 0;
}
/* return the result */
return bplay;
}
......@@ -469,13 +488,13 @@ static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
*playpos = This->buf_mixpos;
} else if (playpos) {
DWORD pplay, pwrite;
/* let's get this exact; first, recursively call GetPosition on the primary */
/* get primary lock, before messing with primary/device data */
EnterCriticalSection(&(This->device->mixlock));
/* let's get this exact; first, recursively call GetPosition on the primary */
if (DSOUND_PrimaryGetPosition(This->device, &pplay, &pwrite) != DS_OK)
WARN("DSOUND_PrimaryGetPosition failed\n");
/* detect HEL mode underrun */
if (!(This->device->hwbuf || This->device->pwqueue))
TRACE("detected an underrun\n");
if ((This->dsbd.dwFlags & DSBCAPS_GETCURRENTPOSITION2) || This->device->hwbuf) {
/* calculate play position using this */
*playpos = DSOUND_CalcPlayPosition(This, pplay, pwrite);
......@@ -489,7 +508,9 @@ static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
wp = (This->device->pwplay + ds_hel_margin) * This->device->fraglen;
wp %= This->device->buflen;
*playpos = DSOUND_CalcPlayPosition(This, wp, pwrite);
TRACE("Using non-GETCURRENTPOSITION2\n");
}
LeaveCriticalSection(&(This->device->mixlock));
}
if (writepos)
......@@ -504,7 +525,10 @@ static HRESULT WINAPI IDirectSoundBufferImpl_GetCurrentPosition(
}
if (playpos)
This->last_playpos = *playpos;
TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:0, writepos?*writepos:0, This, GetTickCount());
TRACE("playpos = %d, writepos = %d, buflen=%d (%p, time=%d)\n",
playpos?*playpos:0, writepos?*writepos:0, This->buflen, This, GetTickCount());
return DS_OK;
}
......@@ -610,19 +634,9 @@ static HRESULT WINAPI IDirectSoundBufferImpl_Lock(
return DSERR_INVALIDPARAM;
}
/* **** */
EnterCriticalSection(&(This->lock));
if ((writebytes == This->buflen) &&
((This->state == STATE_STARTING) ||
(This->state == STATE_PLAYING)))
/* some games, like Half-Life, try to be clever (not) and
* keep one secondary buffer, and mix sounds into it itself,
* locking the entire buffer every time... so we can just forget
* about tracking the last-written-to-position... */
This->probably_valid_to = (DWORD)-1;
else
This->probably_valid_to = writecursor;
if (!(This->device->drvdesc.dwFlags & DSDDESC_DONTNEEDSECONDARYLOCK) && This->hwbuf) {
hres = IDsDriverBuffer_Lock(This->hwbuf,
lplpaudioptr1, audiobytes1,
......@@ -635,7 +649,6 @@ static HRESULT WINAPI IDirectSoundBufferImpl_Lock(
return hres;
}
} else {
BOOL remix = FALSE;
if (writecursor+writebytes <= This->buflen) {
*(LPBYTE*)lplpaudioptr1 = This->buffer->memory+writecursor;
*audiobytes1 = writebytes;
......@@ -643,6 +656,8 @@ static HRESULT WINAPI IDirectSoundBufferImpl_Lock(
*(LPBYTE*)lplpaudioptr2 = NULL;
if (audiobytes2)
*audiobytes2 = 0;
TRACE("Locked %p(%i bytes) and %p(%i bytes) writecursor=%d\n",
*(LPBYTE*)lplpaudioptr1, *audiobytes1, lplpaudioptr2 ? *(LPBYTE*)lplpaudioptr2 : NULL, audiobytes2 ? *audiobytes2: 0, writecursor);
TRACE("->%d.0\n",writebytes);
} else {
*(LPBYTE*)lplpaudioptr1 = This->buffer->memory+writecursor;
......@@ -651,30 +666,12 @@ static HRESULT WINAPI IDirectSoundBufferImpl_Lock(
*(LPBYTE*)lplpaudioptr2 = This->buffer->memory;
if (audiobytes2)
*audiobytes2 = writebytes-(This->buflen-writecursor);
TRACE("->%d.%d\n",*audiobytes1,audiobytes2?*audiobytes2:0);
}
if (This->state == STATE_PLAYING) {
/* if the segment between playpos and buf_mixpos is touched,
* we need to cancel some mixing */
/* we'll assume that the app always calls GetCurrentPosition before
* locking a playing buffer, so that last_playpos is up-to-date */
if (This->buf_mixpos >= This->last_playpos) {
if (This->buf_mixpos > writecursor &&
This->last_playpos < writecursor+writebytes)
remix = TRUE;
} else {
if (This->buf_mixpos > writecursor ||
This->last_playpos < writecursor+writebytes)
remix = TRUE;
}
if (remix) {
TRACE("locking prebuffered region, ouch\n");
DSOUND_MixCancelAt(This, writecursor);
}
TRACE("Locked %p(%i bytes) and %p(%i bytes) writecursor=%d\n", *(LPBYTE*)lplpaudioptr1, *audiobytes1, lplpaudioptr2 ? *(LPBYTE*)lplpaudioptr2 : NULL, audiobytes2 ? *audiobytes2: 0, writecursor);
}
}
LeaveCriticalSection(&(This->lock));
/* **** */
return DS_OK;
}
......@@ -689,9 +686,15 @@ static HRESULT WINAPI IDirectSoundBufferImpl_SetCurrentPosition(
/* **** */
EnterCriticalSection(&(This->lock));
/* start mixing from this new location instead */
newpos %= This->buflen;
newpos -= newpos%This->pwfx->nBlockAlign;
This->buf_mixpos = newpos;
/* at this point, do not attempt to reset buffers, mess with primary mix position,
or anything like that to reduce latancy. The data already prebuffered cannot be changed */
/* position HW buffer if applicable */
if (This->hwbuf) {
hres = IDsDriverBuffer_SetPosition(This->hwbuf, This->buf_mixpos);
if (hres != DS_OK)
......@@ -735,8 +738,7 @@ static HRESULT WINAPI IDirectSoundBufferImpl_SetPan(
hres = IDsDriverBuffer_SetVolumePan(This->hwbuf, &(This->volpan));
if (hres != DS_OK)
WARN("IDsDriverBuffer_SetVolumePan failed\n");
} else
DSOUND_ForceRemix(This);
}
}
LeaveCriticalSection(&(This->lock));
......@@ -770,7 +772,6 @@ static HRESULT WINAPI IDirectSoundBufferImpl_Unlock(
LPDIRECTSOUNDBUFFER8 iface,LPVOID p1,DWORD x1,LPVOID p2,DWORD x2
) {
IDirectSoundBufferImpl *This = (IDirectSoundBufferImpl *)iface;
DWORD probably_valid_to;
HRESULT hres = DS_OK;
TRACE("(%p,%p,%d,%p,%d)\n", This,p1,x1,p2,x2);
......@@ -784,22 +785,9 @@ static HRESULT WINAPI IDirectSoundBufferImpl_Unlock(
WARN("IDsDriverBuffer_Unlock failed\n");
}
if (hres == DS_OK) {
if (p2) probably_valid_to = (((LPBYTE)p2)-This->buffer->memory) + x2;
else probably_valid_to = (((LPBYTE)p1)-This->buffer->memory) + x1;
probably_valid_to %= This->buflen;
if ((probably_valid_to == 0) && ((x1+x2) == This->buflen) &&
((This->state == STATE_STARTING) ||
(This->state == STATE_PLAYING)))
/* see IDirectSoundBufferImpl_Lock */
probably_valid_to = (DWORD)-1;
This->probably_valid_to = probably_valid_to;
}
LeaveCriticalSection(&(This->lock));
/* **** */
TRACE("probably_valid_to=%d\n", This->probably_valid_to);
return hres;
}
......@@ -1044,6 +1032,8 @@ HRESULT IDirectSoundBufferImpl_Create(
dsb->lpVtbl = &dsbvt;
dsb->iks = NULL;
dsb->remix_pos = 0;
/* size depends on version */
CopyMemory(&dsb->dsbd, dsbd, dsbd->dwSize);
......
......@@ -66,8 +66,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dsound);
#define DS_HEL_QUEUE 5 /* HEL only: number of waveOut fragments ahead to queue to driver
* (this will affect HEL sound reliability and latency) */
#define DS_SND_QUEUE_MAX 28 /* max number of fragments to prebuffer */
#define DS_SND_QUEUE_MIN 12 /* min number of fragments to prebuffer */
#define DS_SND_QUEUE_MAX 10 /* max number of fragments to prebuffer */
DirectSoundDevice* DSOUND_renderer[MAXWAVEDRIVERS];
GUID DSOUND_renderer_guids[MAXWAVEDRIVERS];
......@@ -104,7 +103,6 @@ int ds_emuldriver = DS_EMULDRIVER;
int ds_hel_margin = DS_HEL_MARGIN;
int ds_hel_queue = DS_HEL_QUEUE;
int ds_snd_queue_max = DS_SND_QUEUE_MAX;
int ds_snd_queue_min = DS_SND_QUEUE_MIN;
int ds_hw_accel = DS_HW_ACCEL_FULL;
int ds_default_playback = 0;
int ds_default_capture = 0;
......@@ -170,9 +168,6 @@ void setup_dsound_options(void)
if (!get_config_key( hkey, appkey, "SndQueueMax", buffer, MAX_PATH ))
ds_snd_queue_max = atoi(buffer);
if (!get_config_key( hkey, appkey, "SndQueueMin", buffer, MAX_PATH ))
ds_snd_queue_min = atoi(buffer);
if (!get_config_key( hkey, appkey, "HardwareAcceleration", buffer, MAX_PATH )) {
if (strcmp(buffer, "Full") == 0)
ds_hw_accel = DS_HW_ACCEL_FULL;
......@@ -207,8 +202,6 @@ void setup_dsound_options(void)
WARN("ds_hel_queue = %d (default=%d)\n",ds_hel_queue, DS_HEL_QUEUE );
if (ds_snd_queue_max != DS_SND_QUEUE_MAX)
WARN("ds_snd_queue_max = %d (default=%d)\n",ds_snd_queue_max ,DS_SND_QUEUE_MAX);
if (ds_snd_queue_min != DS_SND_QUEUE_MIN)
WARN("ds_snd_queue_min = %d (default=%d)\n",ds_snd_queue_min ,DS_SND_QUEUE_MIN);
if (ds_hw_accel != DS_HW_ACCEL_FULL)
WARN("ds_hw_accel = %s (default=Full)\n",
ds_hw_accel==DS_HW_ACCEL_FULL ? "Full" :
......
......@@ -20,10 +20,11 @@
*/
/* Linux does not support better timing than 10ms */
#define DS_TIME_RES 10 /* Resolution of multimedia timer */
#define DS_TIME_RES 2 /* Resolution of multimedia timer */
#define DS_TIME_DEL 10 /* Delay of multimedia timer callback, and duration of HEL fragment */
#define DS_HEL_FRAGS 48 /* HEL only: number of waveOut fragments in primary buffer
#define DS_HEL_BUFLEN 0x8000 /* HEL: The buffer length of the emulated buffer */
#define DS_HEL_FRAGS 0x40 /* HEL only: number of waveOut fragments in primary buffer
* (changing this won't help you) */
/* direct sound hardware acceleration levels */
......@@ -36,7 +37,6 @@ extern int ds_emuldriver;
extern int ds_hel_margin;
extern int ds_hel_queue;
extern int ds_snd_queue_max;
extern int ds_snd_queue_min;
extern int ds_hw_accel;
extern int ds_default_playback;
extern int ds_default_capture;
......@@ -91,7 +91,6 @@ struct DirectSoundDevice
PIDSDRIVERBUFFER hwbuf;
LPBYTE buffer;
DWORD writelead, buflen, state, playpos, mixpos;
BOOL need_remix;
int nrofbuffers;
IDirectSoundBufferImpl** buffers;
RTL_RWLOCK buffer_list_lock;
......@@ -168,14 +167,12 @@ struct IDirectSoundBufferImpl
DWORD playpos,startpos,writelead,buflen;
DWORD nAvgBytesPerSec;
DWORD freq;
DSVOLUMEPAN volpan, cvolpan;
DSVOLUMEPAN volpan;
DSBUFFERDESC dsbd;
/* used for frequency conversion (PerfectPitch) */
ULONG freqAdjust, freqAcc;
/* used for intelligent (well, sort of) prebuffering */
DWORD probably_valid_to, last_playpos;
DWORD primary_mixpos, buf_mixpos;
BOOL need_remix;
DWORD primary_mixpos, buf_mixpos, last_playpos, remix_pos;
/* IDirectSoundNotifyImpl fields */
IDirectSoundNotifyImpl* notify;
......@@ -439,9 +436,6 @@ DWORD DSOUND_CalcPlayPosition(IDirectSoundBufferImpl *This, DWORD pplay, DWORD p
/* mixer.c */
void DSOUND_CheckEvent(IDirectSoundBufferImpl *dsb, int len);
void DSOUND_ForceRemix(IDirectSoundBufferImpl *dsb);
void DSOUND_MixCancelAt(IDirectSoundBufferImpl *dsb, DWORD buf_writepos);
void DSOUND_WaveQueue(DirectSoundDevice *device, DWORD mixq);
void DSOUND_RecalcVolPan(PDSVOLUMEPAN volpan);
void DSOUND_AmpFactorToVolPan(PDSVOLUMEPAN volpan);
void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb);
......
......@@ -82,12 +82,14 @@ static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
waveOutPause(device->hwo);
if (device->state == STATE_PLAYING) device->state = STATE_STARTING;
else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED;
/* use fragments of 10ms (1/100s) each (which should get us within
* the documented write cursor lead of 10-15ms) */
buflen = ((device->pwfx->nSamplesPerSec / 100) * device->pwfx->nBlockAlign) * DS_HEL_FRAGS;
/* on original windows, the buffer it set to a fixed size, no matter what the settings are.
on windows this size is always fixed (tested on win-xp) */
buflen = DS_HEL_BUFLEN;
TRACE("desired buflen=%d, old buffer=%p\n", buflen, device->buffer);
/* reallocate emulated primary buffer */
/* reallocate emulated primary buffer */
if (device->buffer)
newbuf = HeapReAlloc(GetProcessHeap(),0,device->buffer,buflen);
else
......@@ -106,6 +108,11 @@ static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
device->fraglen = device->buflen / DS_HEL_FRAGS;
/* sanity */
if(device->buflen % DS_HEL_FRAGS){
ERR("Bad DS_HEL_FRAGS resolution\n");
}
/* prepare fragment headers */
for (c=0; c<DS_HEL_FRAGS; c++) {
device->pwave[c]->lpData = (char*)device->buffer + c*device->fraglen;
......@@ -128,7 +135,6 @@ static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device)
device->mixpos = 0;
FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0);
TRACE("fraglen=%d\n", device->fraglen);
DSOUND_WaveQueue(device, (DWORD)-1);
}
if ((err == DS_OK) && (merr != DS_OK))
err = merr;
......@@ -161,10 +167,17 @@ static void DSOUND_PrimaryClose(DirectSoundDevice *device)
if (!device->hwbuf) {
unsigned c;
/* get out of CS when calling the wave system */
LeaveCriticalSection(&(device->mixlock));
/* **** */
device->pwqueue = (DWORD)-1; /* resetting queues */
waveOutReset(device->hwo);
for (c=0; c<DS_HEL_FRAGS; c++)
waveOutUnprepareHeader(device->hwo, device->pwave[c], sizeof(WAVEHDR));
/* **** */
EnterCriticalSection(&(device->mixlock));
/* clear the queue */
device->pwqueue = 0;
} else {
if (IDsDriverBuffer_Release(device->hwbuf) == 0)
......@@ -224,7 +237,8 @@ HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device)
{
TRACE("(%p)\n", device);
EnterCriticalSection(&(device->mixlock));
/* **** */
EnterCriticalSection(&(device->mixlock));
DSOUND_PrimaryClose(device);
if (device->driver) {
......@@ -240,7 +254,10 @@ HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device)
}
HeapFree(GetProcessHeap(),0,device->pwfx);
device->pwfx=NULL;
LeaveCriticalSection(&(device->mixlock));
LeaveCriticalSection(&(device->mixlock));
/* **** */
return DS_OK;
}
......@@ -267,7 +284,6 @@ HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device)
HRESULT err = DS_OK;
TRACE("(%p)\n", device);
EnterCriticalSection(&(device->mixlock));
if (device->hwbuf) {
err = IDsDriverBuffer_Stop(device->hwbuf);
if (err == DSERR_BUFFERLOST) {
......@@ -296,11 +312,20 @@ HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device)
WARN("IDsDriverBuffer_Stop failed\n");
}
} else {
/* dont call the wave system with the lock set */
LeaveCriticalSection(&(device->mixlock));
/* **** */
err = mmErr(waveOutPause(device->hwo));
/* **** */
EnterCriticalSection(&(device->mixlock));
if (err != DS_OK)
WARN("waveOutPause failed\n");
}
LeaveCriticalSection(&(device->mixlock));
return err;
}
......@@ -315,19 +340,20 @@ HRESULT DSOUND_PrimaryGetPosition(DirectSoundDevice *device, LPDWORD playpos, LP
return err;
}
} else {
/* check if playpos was requested */
if (playpos) {
MMTIME mtime;
mtime.wType = TIME_BYTES;
waveOutGetPosition(device->hwo, &mtime, sizeof(mtime));
mtime.u.cb = mtime.u.cb % device->buflen;
*playpos = mtime.u.cb;
/* use the cached play position */
*playpos = device->pwplay * device->fraglen;
}
/* check if writepos was requested */
if (writepos) {
/* the writepos should only be used by apps with WRITEPRIMARY priority,
* in which case our software mixer is disabled anyway */
*writepos = (device->pwplay + ds_hel_margin) * device->fraglen;
while (*writepos >= device->buflen)
*writepos -= device->buflen;
TRACE("pwplay=%i, pwqueue=%i\n", device->pwplay, device->pwqueue);
/* the writepos is the first non-queued position */
*writepos = (device->pwplay + device->pwqueue) * device->fraglen;
*writepos %= device->buflen;
}
}
TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:0, writepos?*writepos:0, device, GetTickCount());
......@@ -607,6 +633,9 @@ static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(
DirectSoundDevice *device = ((PrimaryBufferImpl *)iface)->device;
TRACE("(%p,%p,%p)\n", iface, playpos, writepos);
/* **** */
EnterCriticalSection(&(device->mixlock));
hres = DSOUND_PrimaryGetPosition(device, playpos, writepos);
if (hres != DS_OK) {
WARN("DSOUND_PrimaryGetPosition failed\n");
......@@ -618,6 +647,10 @@ static HRESULT WINAPI PrimaryBufferImpl_GetCurrentPosition(
*writepos += device->writelead;
while (*writepos >= device->buflen) *writepos -= device->buflen;
}
LeaveCriticalSection(&(device->mixlock));
/* **** */
TRACE("playpos = %d, writepos = %d (%p, time=%d)\n", playpos?*playpos:0, writepos?*writepos:0, device, GetTickCount());
return DS_OK;
}
......
......@@ -192,7 +192,6 @@ void DSOUND_Calc3DBuffer(IDirectSoundBufferImpl *dsb)
TRACE("3D processing disabled\n");
/* this one is here only to eliminate annoying warning message */
DSOUND_RecalcVolPan (&dsb->volpan);
DSOUND_ForceRemix (dsb);
break;
case DS3DMODE_NORMAL:
TRACE("Normal 3D processing mode\n");
......@@ -319,7 +318,6 @@ static void DSOUND_Mix3DBuffer(IDirectSoundBufferImpl *dsb)
TRACE("(%p)\n",dsb);
DSOUND_Calc3DBuffer(dsb);
DSOUND_ForceRemix(dsb);
}
static void DSOUND_ChangeListener(IDirectSound3DListenerImpl *ds3dl)
......@@ -332,6 +330,8 @@ static void DSOUND_ChangeListener(IDirectSound3DListenerImpl *ds3dl)
crash without the following line) */
if (ds3dl->device->buffers[i]->ds3db == NULL)
continue;
/* check if this buffer is waiting for recalculation */
if (ds3dl->device->buffers[i]->ds3db_need_recalc)
{
DSOUND_Mix3DBuffer(ds3dl->device->buffers[i]);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment