Andrew Eikum : winealsa.drv: Limit the data written to ALSA's buffer.
Alexandre Julliard
julliard at winehq.org
Tue Dec 20 13:43:24 CST 2011
Module: wine
Branch: master
Commit: 54b24f39260a3780bdf97d810a69647d7357cb62
URL: http://source.winehq.org/git/wine.git/?a=commit;h=54b24f39260a3780bdf97d810a69647d7357cb62
Author: Andrew Eikum <aeikum at codeweavers.com>
Date: Mon Dec 19 15:24:13 2011 -0600
winealsa.drv: Limit the data written to ALSA's buffer.
Based on an algorithm by Alexey Loukianov.
---
dlls/winealsa.drv/mmdevdrv.c | 64 +++++++++++++++++++----------------------
1 files changed, 30 insertions(+), 34 deletions(-)
diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c
index 994628e..dcf7d32 100644
--- a/dlls/winealsa.drv/mmdevdrv.c
+++ b/dlls/winealsa.drv/mmdevdrv.c
@@ -94,7 +94,7 @@ struct ACImpl {
LONG ref;
snd_pcm_t *pcm_handle;
- snd_pcm_uframes_t alsa_bufsize_frames;
+ snd_pcm_uframes_t alsa_bufsize_frames, alsa_period_frames;
snd_pcm_hw_params_t *hw_params; /* does not hold state between calls */
snd_pcm_format_t alsa_format;
@@ -110,7 +110,7 @@ struct ACImpl {
BOOL initted, started;
REFERENCE_TIME mmdev_period_rt;
UINT64 written_frames, last_pos_frames;
- UINT32 bufsize_frames, held_frames, tmp_buffer_frames;
+ UINT32 bufsize_frames, held_frames, tmp_buffer_frames, mmdev_period_frames;
UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */
HANDLE timer;
@@ -808,9 +808,8 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
ACImpl *This = impl_from_IAudioClient(iface);
snd_pcm_sw_params_t *sw_params = NULL;
snd_pcm_format_t format;
- snd_pcm_uframes_t alsa_period_frames;
const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
- unsigned int rate, mmdev_period_frames, alsa_period_us;
+ unsigned int rate, alsa_period_us;
int err, i;
HRESULT hr = S_OK;
@@ -955,19 +954,13 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
goto exit;
}
- if((err = snd_pcm_hw_params_current(This->pcm_handle, This->hw_params)) < 0){
- WARN("Unable to get current hw params: %d (%s)\n", err, snd_strerror(err));
- hr = E_FAIL;
- goto exit;
- }
-
if((err = snd_pcm_hw_params_get_period_size(This->hw_params,
- &alsa_period_frames, NULL)) < 0){
+ &This->alsa_period_frames, NULL)) < 0){
WARN("Unable to get period size: %d (%s)\n", err, snd_strerror(err));
hr = E_FAIL;
goto exit;
}
- TRACE("alsa_period_frames: %lu\n", alsa_period_frames);
+ TRACE("alsa_period_frames: %lu\n", This->alsa_period_frames);
if((err = snd_pcm_hw_params_get_buffer_size(This->hw_params,
&This->alsa_bufsize_frames)) < 0){
@@ -1020,10 +1013,10 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
/* Check if the ALSA buffer is so small that it will run out before
* the next MMDevAPI period tick occurs. Allow a little wiggle room
* with 120% of the period time. */
- mmdev_period_frames = fmt->nSamplesPerSec * (This->mmdev_period_rt / 10000000.);
- if(This->alsa_bufsize_frames < 1.2 * mmdev_period_frames)
+ This->mmdev_period_frames = (fmt->nSamplesPerSec * This->mmdev_period_rt) / 10000000.;
+ if(This->alsa_bufsize_frames < 1.2 * This->mmdev_period_frames)
FIXME("ALSA buffer time is smaller than our period time. Expect underruns. (%lu < %u)\n",
- This->alsa_bufsize_frames, mmdev_period_frames);
+ This->alsa_bufsize_frames, This->mmdev_period_frames);
This->bufsize_frames = ceil((duration / 10000000.) * fmt->nSamplesPerSec);
This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
@@ -1151,23 +1144,11 @@ static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
return AUDCLNT_E_NOT_INITIALIZED;
}
- if(This->dataflow == eRender){
- snd_pcm_sframes_t avail_frames;
+ *out = This->held_frames;
- avail_frames = snd_pcm_avail_update(This->pcm_handle);
- if(This->alsa_bufsize_frames < avail_frames ||
- snd_pcm_state(This->pcm_handle) == SND_PCM_STATE_XRUN)
- *out = This->held_frames;
- else
- *out = This->alsa_bufsize_frames - avail_frames + This->held_frames;
- TRACE("PCM state: %u, avail: %ld, pad: %u\n",
- snd_pcm_state(This->pcm_handle), avail_frames, *out);
- }else if(This->dataflow == eCapture){
- *out = This->held_frames;
- }else{
- LeaveCriticalSection(&This->lock);
- return E_UNEXPECTED;
- }
+ /* call required to get accurate snd_pcm_state() */
+ snd_pcm_avail_update(This->pcm_handle);
+ TRACE("pad: %u, state: %u\n", *out, snd_pcm_state(This->pcm_handle));
LeaveCriticalSection(&This->lock);
@@ -1554,7 +1535,7 @@ static snd_pcm_sframes_t alsa_write_best_effort(snd_pcm_t *handle, BYTE *buf,
static void alsa_write_data(ACImpl *This)
{
snd_pcm_sframes_t written;
- snd_pcm_uframes_t to_write, avail;
+ snd_pcm_uframes_t to_write, avail, write_limit, max_period, in_alsa;
int err;
BYTE *buf =
This->local_buffer + (This->lcl_offs_frames * This->fmt->nBlockAlign);
@@ -1566,6 +1547,8 @@ static void alsa_write_data(ACImpl *This)
avail > This->alsa_bufsize_frames){
TRACE("XRun state, recovering\n");
+ avail = This->alsa_bufsize_frames;
+
if((err = snd_pcm_recover(This->pcm_handle, -EPIPE, 1)) < 0)
WARN("snd_pcm_recover failed: %d (%s)\n", err, snd_strerror(err));
@@ -1584,6 +1567,19 @@ static void alsa_write_data(ACImpl *This)
else
to_write = This->held_frames;
+ max_period = max(This->mmdev_period_frames, This->alsa_period_frames);
+
+ /* try to keep 3 ALSA periods or 3 MMDevAPI periods in the ALSA buffer and
+ * no more */
+ write_limit = 0;
+ in_alsa = This->alsa_bufsize_frames - avail;
+ while(in_alsa + write_limit < max_period * 3)
+ write_limit += max_period;
+ if(write_limit == 0)
+ return;
+
+ to_write = min(to_write, write_limit);
+
written = alsa_write_best_effort(This->pcm_handle, buf, to_write, This);
if(written < 0){
WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
@@ -1599,10 +1595,10 @@ static void alsa_write_data(ACImpl *This)
return;
}
- if(This->held_frames){
+ if(This->held_frames && (written < write_limit)){
/* wrapped and have some data back at the start to write */
written = alsa_write_best_effort(This->pcm_handle, This->local_buffer,
- This->held_frames, This);
+ min(This->held_frames, write_limit - written), This);
if(written < 0){
WARN("Couldn't write: %ld (%s)\n", written, snd_strerror(written));
return;
More information about the wine-cvs
mailing list