Commit 828086ef authored by Davin McCall's avatar Davin McCall Committed by Alexandre Julliard

Avoid getting ahead of dsound mixer when using non-hw device. Also

avoid using internal ALSA call to retrieve hardware play position.
parent 079e7cac
...@@ -3011,6 +3011,10 @@ struct IDsDriverBufferImpl ...@@ -3011,6 +3011,10 @@ struct IDsDriverBufferImpl
snd_pcm_uframes_t mmap_buflen_frames; snd_pcm_uframes_t mmap_buflen_frames;
snd_pcm_channel_area_t * mmap_areas; snd_pcm_channel_area_t * mmap_areas;
snd_async_handler_t * mmap_async_handler; snd_async_handler_t * mmap_async_handler;
snd_pcm_uframes_t mmap_ppos; /* play position */
/* Do we have a direct hardware buffer - SND_PCM_TYPE_HW? */
int mmap_mode;
}; };
static void DSDB_CheckXRUN(IDsDriverBufferImpl* pdbi) static void DSDB_CheckXRUN(IDsDriverBufferImpl* pdbi)
...@@ -3037,86 +3041,77 @@ static void DSDB_CheckXRUN(IDsDriverBufferImpl* pdbi) ...@@ -3037,86 +3041,77 @@ static void DSDB_CheckXRUN(IDsDriverBufferImpl* pdbi)
} }
} }
static void DSDB_MMAPCopy(IDsDriverBufferImpl* pdbi) static void DSDB_MMAPCopy(IDsDriverBufferImpl* pdbi, int mul)
{ {
WINE_WAVEDEV * wwo = &(WOutDev[pdbi->drv->wDevID]); WINE_WAVEDEV * wwo = &(WOutDev[pdbi->drv->wDevID]);
unsigned int channels;
snd_pcm_format_t format;
snd_pcm_uframes_t period_size; snd_pcm_uframes_t period_size;
snd_pcm_sframes_t avail; snd_pcm_sframes_t avail;
int err; int err;
int dir=0; int dir=0;
const snd_pcm_channel_area_t *areas;
snd_pcm_uframes_t ofs;
snd_pcm_uframes_t frames;
snd_pcm_uframes_t wanted;
if ( !pdbi->mmap_buffer || !wwo->hw_params || !wwo->pcm) if ( !pdbi->mmap_buffer || !wwo->hw_params || !wwo->pcm)
return; return;
err = snd_pcm_hw_params_get_channels(wwo->hw_params, &channels);
err = snd_pcm_hw_params_get_format(wwo->hw_params, &format);
dir=0;
err = snd_pcm_hw_params_get_period_size(wwo->hw_params, &period_size, &dir); err = snd_pcm_hw_params_get_period_size(wwo->hw_params, &period_size, &dir);
avail = snd_pcm_avail_update(wwo->pcm); avail = snd_pcm_avail_update(wwo->pcm);
DSDB_CheckXRUN(pdbi); DSDB_CheckXRUN(pdbi);
TRACE("avail=%d format=%s channels=%d\n", (int)avail, snd_pcm_format_name(format), channels ); TRACE("avail=%d, mul=%d\n", (int)avail, mul);
while (avail >= period_size) frames = pdbi->mmap_buflen_frames;
{
const snd_pcm_channel_area_t *areas; EnterCriticalSection(&pdbi->mmap_crst);
snd_pcm_uframes_t ofs;
snd_pcm_uframes_t frames; /* we want to commit the given number of periods, or the whole lot */
int err; wanted = mul == 0 ? frames : period_size * 2;
frames = avail / period_size * period_size; /* round down to a multiple of period_size */ snd_pcm_mmap_begin(wwo->pcm, &areas, &ofs, &frames);
if (areas != pdbi->mmap_areas || areas->addr != pdbi->mmap_areas->addr)
EnterCriticalSection(&pdbi->mmap_crst); FIXME("Can't access sound driver's buffer directly.\n");
snd_pcm_mmap_begin(wwo->pcm, &areas, &ofs, &frames); /* mark our current play position */
if (areas != pdbi->mmap_areas || areas->addr != pdbi->mmap_areas->addr) pdbi->mmap_ppos = ofs;
FIXME("Can't access sound driver's buffer directly.\n");
err = snd_pcm_mmap_commit(wwo->pcm, ofs, frames); if (frames > wanted)
frames = wanted;
LeaveCriticalSection(&pdbi->mmap_crst);
err = snd_pcm_mmap_commit(wwo->pcm, ofs, frames);
if ( err != (snd_pcm_sframes_t) frames)
ERR("mmap partially failed.\n"); /* Check to make sure we committed all we want to commit. ALSA
* only gives a contiguous linear region, so we need to check this
avail = snd_pcm_avail_update(wwo->pcm); * in case we've reached the end of the buffer, in which case we
} * can wrap around back to the beginning. */
if (frames < wanted) {
if (avail > 0) frames = wanted -= frames;
{ snd_pcm_mmap_begin(wwo->pcm, &areas, &ofs, &frames);
const snd_pcm_channel_area_t *areas; snd_pcm_mmap_commit(wwo->pcm, ofs, frames);
snd_pcm_uframes_t ofs; }
snd_pcm_uframes_t frames;
int err; LeaveCriticalSection(&pdbi->mmap_crst);
frames = avail;
EnterCriticalSection(&pdbi->mmap_crst);
snd_pcm_mmap_begin(wwo->pcm, &areas, &ofs, &frames);
if (areas != pdbi->mmap_areas || areas->addr != pdbi->mmap_areas->addr)
FIXME("Can't access sound driver's buffer directly.\n");
err = snd_pcm_mmap_commit(wwo->pcm, ofs, frames);
LeaveCriticalSection(&pdbi->mmap_crst);
if ( err != (snd_pcm_sframes_t) frames)
ERR("mmap partially failed.\n");
avail = snd_pcm_avail_update(wwo->pcm);
}
} }
static void DSDB_PCMCallback(snd_async_handler_t *ahandler) static void DSDB_PCMCallback(snd_async_handler_t *ahandler)
{ {
int periods;
/* snd_pcm_t * handle = snd_async_handler_get_pcm(ahandler); */ /* snd_pcm_t * handle = snd_async_handler_get_pcm(ahandler); */
IDsDriverBufferImpl* pdbi = snd_async_handler_get_callback_private(ahandler); IDsDriverBufferImpl* pdbi = snd_async_handler_get_callback_private(ahandler);
TRACE("callback called\n"); TRACE("callback called\n");
DSDB_MMAPCopy(pdbi);
/* Commit another block (the entire buffer if it's a direct hw buffer) */
periods = pdbi->mmap_mode == SND_PCM_TYPE_HW ? 0 : 1;
DSDB_MMAPCopy(pdbi, periods);
} }
/**
* Allocate the memory-mapped buffer for direct sound, and set up the
* callback.
*/
static int DSDB_CreateMMAP(IDsDriverBufferImpl* pdbi) static int DSDB_CreateMMAP(IDsDriverBufferImpl* pdbi)
{ {
WINE_WAVEDEV * wwo = &(WOutDev[pdbi->drv->wDevID]); WINE_WAVEDEV * wwo = &(WOutDev[pdbi->drv->wDevID]);
...@@ -3134,7 +3129,14 @@ static int DSDB_CreateMMAP(IDsDriverBufferImpl* pdbi) ...@@ -3134,7 +3129,14 @@ static int DSDB_CreateMMAP(IDsDriverBufferImpl* pdbi)
err = snd_pcm_hw_params_get_channels(wwo->hw_params, &channels); err = snd_pcm_hw_params_get_channels(wwo->hw_params, &channels);
bits_per_sample = snd_pcm_format_physical_width(format); bits_per_sample = snd_pcm_format_physical_width(format);
bits_per_frame = bits_per_sample * channels; bits_per_frame = bits_per_sample * channels;
pdbi->mmap_mode = snd_pcm_type(wwo->pcm);
if (pdbi->mmap_mode == SND_PCM_TYPE_HW) {
TRACE("mmap'd buffer is a hardware buffer.\n");
}
else {
TRACE("mmap'd buffer is an ALSA emulation of hardware buffer.\n");
}
if (TRACE_ON(wave)) if (TRACE_ON(wave))
ALSA_TraceParameters(wwo->hw_params, NULL, FALSE); ALSA_TraceParameters(wwo->hw_params, NULL, FALSE);
...@@ -3286,6 +3288,7 @@ static HRESULT WINAPI IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER iface, ...@@ -3286,6 +3288,7 @@ static HRESULT WINAPI IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER iface,
WINE_WAVEDEV * wwo = &(WOutDev[This->drv->wDevID]); WINE_WAVEDEV * wwo = &(WOutDev[This->drv->wDevID]);
snd_pcm_uframes_t hw_ptr; snd_pcm_uframes_t hw_ptr;
snd_pcm_uframes_t period_size; snd_pcm_uframes_t period_size;
snd_pcm_state_t state;
int dir; int dir;
int err; int err;
...@@ -3296,17 +3299,19 @@ static HRESULT WINAPI IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER iface, ...@@ -3296,17 +3299,19 @@ static HRESULT WINAPI IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER iface,
if (wwo->pcm == NULL) return DSERR_GENERIC; if (wwo->pcm == NULL) return DSERR_GENERIC;
/** we need to track down buffer underruns */ /** we need to track down buffer underruns */
DSDB_CheckXRUN(This); DSDB_CheckXRUN(This);
EnterCriticalSection(&This->mmap_crst); EnterCriticalSection(&This->mmap_crst);
/* FIXME: snd_pcm_mmap_hw_ptr() should not be accessed by a user app. */ hw_ptr = This->mmap_ppos;
/* It will NOT return what why want anyway. */
hw_ptr = _snd_pcm_mmap_hw_ptr(wwo->pcm); state = snd_pcm_state(wwo->pcm);
if (hw_ptr >= period_size) hw_ptr -= period_size; else hw_ptr = 0; if (state != SND_PCM_STATE_RUNNING)
hw_ptr = 0;
if (lpdwPlay) if (lpdwPlay)
*lpdwPlay = snd_pcm_frames_to_bytes(wwo->pcm, hw_ptr/ period_size * period_size) % This->mmap_buflen_bytes; *lpdwPlay = snd_pcm_frames_to_bytes(wwo->pcm, hw_ptr) % This->mmap_buflen_bytes;
if (lpdwWrite) if (lpdwWrite)
*lpdwWrite = snd_pcm_frames_to_bytes(wwo->pcm, (hw_ptr / period_size + 1) * period_size ) % This->mmap_buflen_bytes; *lpdwWrite = snd_pcm_frames_to_bytes(wwo->pcm, hw_ptr + period_size * 2) % This->mmap_buflen_bytes;
LeaveCriticalSection(&This->mmap_crst); LeaveCriticalSection(&This->mmap_crst);
TRACE("hw_ptr=0x%08x, playpos=%ld, writepos=%ld\n", (unsigned int)hw_ptr, lpdwPlay?*lpdwPlay:-1, lpdwWrite?*lpdwWrite:-1); TRACE("hw_ptr=0x%08x, playpos=%ld, writepos=%ld\n", (unsigned int)hw_ptr, lpdwPlay?*lpdwPlay:-1, lpdwWrite?*lpdwWrite:-1);
...@@ -3331,10 +3336,29 @@ static HRESULT WINAPI IDsDriverBufferImpl_Play(PIDSDRIVERBUFFER iface, DWORD dwR ...@@ -3331,10 +3336,29 @@ static HRESULT WINAPI IDsDriverBufferImpl_Play(PIDSDRIVERBUFFER iface, DWORD dwR
state = snd_pcm_state(wwo->pcm); state = snd_pcm_state(wwo->pcm);
} }
if ( state == SND_PCM_STATE_PREPARED ) if ( state == SND_PCM_STATE_PREPARED )
{ {
DSDB_MMAPCopy(This); /* If we have a direct hardware buffer, we can commit the whole lot
* immediately (periods = 0), otherwise we prime the queue with only
* 2 periods.
*
* Why 2? We want a small number so that we don't get ahead of the
* DirectSound mixer. But we don't want to ever let the buffer get
* completely empty - having 2 periods gives us time to commit another
* period when the first expires.
*
* The potential for buffer underrun is high, but that's the reality
* of using a translated buffer (the whole point of DirectSound is
* to provide direct access to the hardware).
*
* A better implementation would use the buffer Lock() and Unlock()
* methods to determine how far ahead we can commit, and to rewind if
* necessary.
*/
int periods = This->mmap_mode == SND_PCM_TYPE_HW ? 0 : 2;
DSDB_MMAPCopy(This, periods);
err = snd_pcm_start(wwo->pcm); err = snd_pcm_start(wwo->pcm);
} }
return DS_OK; return DS_OK;
} }
......
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