From c65106e3e2b861a004cb367dad0b7e5968ce09bd Mon Sep 17 00:00:00 2001
From: Tristan Matthews <tristan.matthews@savoirfairelinux.com>
Date: Tue, 6 Sep 2011 14:25:47 -0400
Subject: [PATCH] * #6852: alsa: retry if device is busy

This is due to an issue with dmix where it will not release the device
after being closed.
---
 daemon/src/audio/alsa/alsalayer.cpp | 16 ++++++++++++----
 daemon/src/audio/alsa/alsalayer.h   |  5 +++++
 daemon/src/managerimpl.cpp          |  2 ++
 3 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/daemon/src/audio/alsa/alsalayer.cpp b/daemon/src/audio/alsa/alsalayer.cpp
index 6a0fd0c86c..56b1ae2a4c 100644
--- a/daemon/src/audio/alsa/alsalayer.cpp
+++ b/daemon/src/audio/alsa/alsalayer.cpp
@@ -101,11 +101,20 @@ AlsaLayer::~AlsaLayer (void)
     closePlaybackStream();
 }
 
+// Retry approach taken from pa_linux_alsa.c, part of PortAudio
 bool AlsaLayer::openDevice(snd_pcm_t **pcm, const std::string &dev, snd_pcm_stream_t stream)
 {
-	int err = snd_pcm_open(pcm, dev.c_str(),stream, 0);
+    static const int MAX_RETRIES = 100;
+    int err = snd_pcm_open(pcm, dev.c_str(), stream, 0);
+    // Retry if busy, since dmix plugin may not have released the device yet
+    for (int tries = 0; tries < MAX_RETRIES and err == -EBUSY; ++tries) {
+        usleep(10000);
+        err = snd_pcm_open(pcm, dev.c_str(), stream, 0);
+    }
+
     if (err < 0) {
-        _error("Alsa: couldn't open device %s : %s",  dev.c_str(), snd_strerror(err));
+        _error("Alsa: couldn't open device %s : %s",  dev.c_str(),
+                snd_strerror(err));
         return false;
     }
 
@@ -141,9 +150,8 @@ AlsaLayer::startStream (void)
 
     if (not is_capture_open_) {
     	is_capture_open_ = openDevice(&captureHandle_, pcmc, SND_PCM_STREAM_CAPTURE);
-    	if (not is_capture_open_) {
+        if (not is_capture_open_)
             Manager::instance().getDbusManager()->getConfigurationManager()->errorAlert(ALSA_CAPTURE_DEVICE);
-    	}
     }
 
     if (not is_playback_open_) {
diff --git a/daemon/src/audio/alsa/alsalayer.h b/daemon/src/audio/alsa/alsalayer.h
index 9c14a1e6c2..a406667a00 100644
--- a/daemon/src/audio/alsa/alsalayer.h
+++ b/daemon/src/audio/alsa/alsalayer.h
@@ -146,6 +146,11 @@ class AlsaLayer : public AudioLayer
     private:
 
 
+        /**
+         * Calls snd_pcm_open and retries if device is busy, since dmix plugin
+         * will often hold on to a device temporarily after it has been opened
+         * and closed.
+         */
         bool openDevice(snd_pcm_t **pcm, const std::string &dev, snd_pcm_stream_t stream);
 
         /**
diff --git a/daemon/src/managerimpl.cpp b/daemon/src/managerimpl.cpp
index 6e7c93b381..11713224ed 100644
--- a/daemon/src/managerimpl.cpp
+++ b/daemon/src/managerimpl.cpp
@@ -1994,6 +1994,7 @@ void ManagerImpl::setAudioPlugin (const std::string& audioPlugin)
     // Recreate audio driver with new settings
     delete _audiodriver;
     _audiodriver = new AlsaLayer;
+    assert(preferences.getAudioApi() == ALSA_API_STR);
     if (wasStarted)
         _audiodriver->startStream();
 
@@ -2033,6 +2034,7 @@ void ManagerImpl::setAudioDevice (const int index, int streamType)
     // Recreate audio driver with new settings
     delete _audiodriver;
     _audiodriver = new AlsaLayer;
+    assert(preferences.getAudioApi() == ALSA_API_STR);
 
     if (wasStarted)
         _audiodriver->startStream();
-- 
GitLab