Andrew Eikum : winealsa.drv: Inject handle_underrun= 1 setting for pulse-backed devices.

Alexandre Julliard julliard at winehq.org
Fri Aug 5 10:20:51 CDT 2011


Module: wine
Branch: master
Commit: 9ad60d1d143809c6ae477eb53e75c3dc93736e0e
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=9ad60d1d143809c6ae477eb53e75c3dc93736e0e

Author: Andrew Eikum <aeikum at codeweavers.com>
Date:   Fri Aug  5 09:33:10 2011 -0500

winealsa.drv: Inject handle_underrun=1 setting for pulse-backed devices.

---

 dlls/winealsa.drv/mmdevdrv.c |  110 +++++++++++++++++++++++++++++++++++++++--
 1 files changed, 104 insertions(+), 6 deletions(-)

diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c
index ded6b2a..6c902f5 100644
--- a/dlls/winealsa.drv/mmdevdrv.c
+++ b/dlls/winealsa.drv/mmdevdrv.c
@@ -407,12 +407,96 @@ HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, char ***keys,
     return S_OK;
 }
 
+/* Using the pulse PCM device from alsa-plugins 1.0.24 triggers a bug
+ * which causes audio to cease playing after a few minutes of playback.
+ * Setting handle_underrun=1 on pulse-backed ALSA devices seems to work
+ * around this issue. */
+static snd_config_t *make_handle_underrun_config(const char *name)
+{
+    snd_config_t *lconf, *dev_node, *hu_node, *type_node;
+    char dev_node_name[64];
+    const char *type_str;
+    int err;
+
+    snd_config_update();
+
+    if((err = snd_config_copy(&lconf, snd_config)) < 0){
+        WARN("snd_config_copy failed: %d (%s)\n", err, snd_strerror(err));
+        return NULL;
+    }
+
+    sprintf(dev_node_name, "pcm.%s", name);
+    err = snd_config_search(lconf, dev_node_name, &dev_node);
+    if(err == -ENOENT){
+        snd_config_delete(lconf);
+        return NULL;
+    }
+    if(err < 0){
+        snd_config_delete(lconf);
+        WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
+        return NULL;
+    }
+
+    /* ALSA is extremely fragile. If it runs into a config setting it doesn't
+     * recognize, it tends to fail or assert. So we only want to inject
+     * handle_underrun=1 on devices that we know will recognize it. */
+    err = snd_config_search(dev_node, "type", &type_node);
+    if(err == -ENOENT){
+        snd_config_delete(lconf);
+        return NULL;
+    }
+    if(err < 0){
+        snd_config_delete(lconf);
+        WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
+        return NULL;
+    }
+
+    if((err = snd_config_get_string(type_node, &type_str)) < 0){
+        snd_config_delete(lconf);
+        return NULL;
+    }
+
+    if(strcmp(type_str, "pulse") != 0){
+        snd_config_delete(lconf);
+        return NULL;
+    }
+
+    err = snd_config_search(dev_node, "handle_underrun", &hu_node);
+    if(err >= 0){
+        /* user already has an explicit handle_underrun setting, so don't
+         * use a local config */
+        snd_config_delete(lconf);
+        return NULL;
+    }
+    if(err != -ENOENT){
+        snd_config_delete(lconf);
+        WARN("snd_config_search failed: %d (%s)\n", err, snd_strerror(err));
+        return NULL;
+    }
+
+    if((err = snd_config_imake_integer(&hu_node, "handle_underrun", 1)) < 0){
+        snd_config_delete(lconf);
+        WARN("snd_config_imake_integer failed: %d (%s)\n", err,
+                snd_strerror(err));
+        return NULL;
+    }
+
+    if((err = snd_config_add(dev_node, hu_node)) < 0){
+        snd_config_delete(lconf);
+        WARN("snd_config_add failed: %d (%s)\n", err, snd_strerror(err));
+        return NULL;
+    }
+
+    return lconf;
+}
+
 HRESULT WINAPI AUDDRV_GetAudioEndpoint(const char *key, IMMDevice *dev,
         EDataFlow dataflow, IAudioClient **out)
 {
     ACImpl *This;
     int err;
     snd_pcm_stream_t stream;
+    snd_config_t *lconf;
 
     TRACE("\"%s\" %p %d %p\n", key, dev, dataflow, out);
 
@@ -436,13 +520,27 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(const char *key, IMMDevice *dev,
         return E_UNEXPECTED;
     }
 
+    lconf = make_handle_underrun_config(key);
+
     This->dataflow = dataflow;
-    if((err = snd_pcm_open(&This->pcm_handle, key, stream,
-                    SND_PCM_NONBLOCK)) < 0){
-        HeapFree(GetProcessHeap(), 0, This);
-        WARN("Unable to open PCM \"%s\": %d (%s)\n", key, err,
-                snd_strerror(err));
-        return E_FAIL;
+    if(lconf){
+        if((err = snd_pcm_open_lconf(&This->pcm_handle, key, stream,
+                        SND_PCM_NONBLOCK, lconf)) < 0){
+            snd_config_delete(lconf);
+            HeapFree(GetProcessHeap(), 0, This);
+            WARN("Unable to open PCM \"%s\": %d (%s)\n", key, err,
+                    snd_strerror(err));
+            return E_FAIL;
+        }
+        snd_config_delete(lconf);
+    }else{
+        if((err = snd_pcm_open(&This->pcm_handle, key, stream,
+                        SND_PCM_NONBLOCK)) < 0){
+            HeapFree(GetProcessHeap(), 0, This);
+            WARN("Unable to open PCM \"%s\": %d (%s)\n", key, err,
+                    snd_strerror(err));
+            return E_FAIL;
+        }
     }
 
     This->hw_params = HeapAlloc(GetProcessHeap(), 0,




More information about the wine-cvs mailing list