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