From 6fedd70771ed5359772791c6a68ea9c3e6fa716d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C3=ABl=20Carr=C3=A9?=
 <rafael.carre@savoirfairelinux.com>
Date: Tue, 30 Aug 2011 14:46:05 -0400
Subject: [PATCH] * #6629: Always restart audio driver when changing parameters
 (ALSA only)

---
 daemon/src/audio/alsa/alsalayer.cpp        | 150 ++++++---------------
 daemon/src/audio/alsa/alsalayer.h          |  12 +-
 daemon/src/audio/audiolayer.h              |  12 +-
 daemon/src/audio/pulseaudio/pulselayer.cpp |  93 ++++---------
 daemon/src/audio/pulseaudio/pulselayer.h   |  11 --
 daemon/src/managerimpl.cpp                 |  27 +++-
 6 files changed, 91 insertions(+), 214 deletions(-)

diff --git a/daemon/src/audio/alsa/alsalayer.cpp b/daemon/src/audio/alsa/alsalayer.cpp
index 1ab1574e4c..7076cc7822 100644
--- a/daemon/src/audio/alsa/alsalayer.cpp
+++ b/daemon/src/audio/alsa/alsalayer.cpp
@@ -104,12 +104,20 @@ AlsaLayer::~AlsaLayer (void)
     closePlaybackStream();
 }
 
-void AlsaLayer::setPlugin(const std::string &plugin)
+bool AlsaLayer::openDevice(snd_pcm_t **pcm, const std::string &dev, snd_pcm_stream_t stream)
 {
-    audioPlugin_ = plugin;
-	delete audioThread_;
-	audioThread_ = NULL;
-	// FIXME : restart audio thread
+	int 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));
+        return false;
+    }
+
+	if (!alsa_set_params(*pcm)) {
+		snd_pcm_close(*pcm);
+		return false;
+	}
+
+	return true;
 }
 
 void
@@ -125,13 +133,13 @@ AlsaLayer::startStream (void)
     std::string pcmc;
 
     if (audioPlugin_ == PCM_DMIX_DSNOOP) {
-        pcmp = buildDeviceTopo (PCM_DMIX, indexOut_, 0);
-        pcmr = buildDeviceTopo (PCM_DMIX, indexRing_, 0);
-        pcmc = buildDeviceTopo (PCM_DSNOOP, indexIn_, 0);
+        pcmp = buildDeviceTopo (PCM_DMIX, indexOut_);
+        pcmr = buildDeviceTopo (PCM_DMIX, indexRing_);
+        pcmc = buildDeviceTopo (PCM_DSNOOP, indexIn_);
     } else {
-        pcmp = buildDeviceTopo (audioPlugin_, indexOut_, 0);
-        pcmr = buildDeviceTopo (audioPlugin_, indexRing_, 0);
-        pcmc = buildDeviceTopo (audioPlugin_, indexIn_, 0);
+        pcmp = buildDeviceTopo (audioPlugin_, indexOut_);
+        pcmr = buildDeviceTopo (audioPlugin_, indexRing_);
+        pcmc = buildDeviceTopo (audioPlugin_, indexIn_);
     }
 
     _debug ("pcmp: %s, index %d", pcmp.c_str(), indexOut_);
@@ -139,54 +147,22 @@ AlsaLayer::startStream (void)
     _debug ("pcmc: %s, index %d", pcmc.c_str(), indexIn_);
 
     if (not is_capture_open_) {
-        _debug ("Audio: Open capture device");
-
-        if (snd_pcm_open (&captureHandle_,  pcmc.c_str(),  SND_PCM_STREAM_CAPTURE, 0) < 0) {
-            _warn ("Audio: Error: Opening capture device %s",  pcmc.c_str());
-
+    	is_capture_open_ = openDevice(&captureHandle_, pcmc, SND_PCM_STREAM_CAPTURE);
+    	if (not is_capture_open_) {
             Manager::instance().getDbusManager()->getConfigurationManager()->errorAlert(ALSA_CAPTURE_DEVICE);
-            is_capture_open_ = false;
-        }
-
-        if (!alsa_set_params (captureHandle_, 0)) {
-            _warn ("Audio: Error: Capture failed");
-            snd_pcm_close (captureHandle_);
-            is_capture_open_ = false;
-        }
-
-        is_capture_open_ = true;
+    	}
     }
 
     if (not is_playback_open_) {
-
-        _debug ("Audio: Open playback device (and ringtone)");
-
-        int err;
-        if ((err = snd_pcm_open (&playbackHandle_, pcmp.c_str(), SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
-            _warn ("Audio: Error while opening playback device %s",  pcmp.c_str());
+    	is_playback_open_ = openDevice(&playbackHandle_, pcmp, SND_PCM_STREAM_PLAYBACK);
+    	if (not is_playback_open_)
             Manager::instance().getDbusManager()->getConfigurationManager()->errorAlert(ALSA_PLAYBACK_DEVICE);
-            is_playback_open_ = false;
-        }
 
-        if (!alsa_set_params (playbackHandle_, 1)) {
-            _warn ("Audio: Error: Playback failed");
-            snd_pcm_close (playbackHandle_);
-            is_playback_open_ = false;
-        }
-
-        if (getIndexOut() != getIndexRing()) {
-
-            if ((err = snd_pcm_open (&ringtoneHandle_, pcmr.c_str(), SND_PCM_STREAM_PLAYBACK, 0)) < 0)
-                _warn ("Audio: Error: Opening ringtone device %s", pcmr.c_str());
-
-            if (!alsa_set_params (ringtoneHandle_, 1)) {
-                _warn ("Audio: Error: Ringtone failed");
-                snd_pcm_close (ringtoneHandle_);
-            }
-        }
-
-        is_playback_open_ = true;
+    	if (getIndexOut() != getIndexRing())
+    		if (!openDevice(&ringtoneHandle_, pcmr, SND_PCM_STREAM_PLAYBACK))
+    			Manager::instance().getDbusManager()->getConfigurationManager()->errorAlert(ALSA_PLAYBACK_DEVICE);
     }
+
     prepareCaptureStream ();
     preparePlaybackStream ();
 
@@ -197,13 +173,8 @@ AlsaLayer::startStream (void)
     flushUrgent();
 
     if (audioThread_ == NULL) {
-        try {
-            _debug ("Audio: Start Audio Thread");
-            audioThread_ = new AlsaThread (this);
-            audioThread_->start();
-        } catch (...) {
-            _debug ("Fail to start audio thread");
-        }
+		audioThread_ = new AlsaThread (this);
+		audioThread_->start();
     }
 
     isStarted_ = true;
@@ -250,32 +221,6 @@ void AlsaLayer::stopCaptureStream (void)
     }
 }
 
-void AlsaLayer::setIndexRing(int index)
-{
-	indexRing_ = index;
-	delete audioThread_;
-	audioThread_ = NULL;
-}
-
-
-void AlsaLayer::setIndexOut(int index)
-{
-	indexOut_ = index;
-	if (is_playback_open_)
-		closePlaybackStream ();
-	delete audioThread_;
-	audioThread_ = NULL;
-}
-
-void AlsaLayer::setIndexIn(int index)
-{
-	indexIn_ = index;
-	if (is_capture_open_)
-		closeCaptureStream ();
-	delete audioThread_;
-	audioThread_ = NULL;
-}
-
 void AlsaLayer::closeCaptureStream (void)
 {
     if (is_capture_prepared_ and is_capture_running_)
@@ -389,7 +334,7 @@ void AlsaLayer::preparePlaybackStream (void)
     }
 }
 
-bool AlsaLayer::alsa_set_params (snd_pcm_t *pcm_handle, int type)
+bool AlsaLayer::alsa_set_params (snd_pcm_t *pcm_handle)
 {
     snd_pcm_hw_params_t *hwparams = NULL;
     snd_pcm_sw_params_t *swparams = NULL;
@@ -428,13 +373,13 @@ bool AlsaLayer::alsa_set_params (snd_pcm_t *pcm_handle, int type)
     unsigned int exact_ivalue = audioSampleRate_;
 
     if ((err = snd_pcm_hw_params_set_rate_near (pcm_handle, hwparams, &exact_ivalue, &dir) < 0)) {
-        _debug ("Audio: Error: Cannot set sample rate (%s)", snd_strerror (err));
+        _error("Alsa: Cannot set sample rate (%s)", snd_strerror (err));
         return false;
     } else
-        _debug ("Audio: Set audio rate to %d", audioSampleRate_);
+        _debug ("Alsa: Set audio rate to %d", audioSampleRate_);
 
     if (dir != 0) {
-        _debug ("Audio: Error: (%i) The chosen rate %d Hz is not supported by your hardware.Using %d Hz instead. ", type , audioSampleRate_, exact_ivalue);
+        _error("Alsa: The chosen rate %d Hz is not supported by your hardware.Using %d Hz instead. ", audioSampleRate_, exact_ivalue);
         //audioSampleRate_ = exact_ivalue;
         // FIXME
     }
@@ -456,7 +401,7 @@ bool AlsaLayer::alsa_set_params (snd_pcm_t *pcm_handle, int type)
     }
 
     if (dir != 0)
-        _debug ("Audio: Warning: (%i) The chosen period size %lu bytes is not supported by your hardware.Using %lu instead. ", type, periodsize, exact_lvalue);
+        _warn("Alsa: The chosen period size %lu bytes is not supported by your hardware.Using %lu instead. ", periodsize, exact_lvalue);
 
     periodSize_ = exact_lvalue;
     /* Set the number of fragments */
@@ -619,36 +564,22 @@ AlsaLayer::handle_xrun_playback (snd_pcm_t *handle)
 }
 
 std::string
-AlsaLayer::buildDeviceTopo (const std::string &plugin, int card, int subdevice)
+AlsaLayer::buildDeviceTopo (const std::string &plugin, int card)
 {
-    std::stringstream ss, ss1;
+    std::stringstream ss;
     std::string pcm(plugin);
 
     if (pcm == PCM_DEFAULT)
         return pcm;
 
-    ss << card;
+    ss << ":" << card;
 
-    pcm.append (":");
-
-    pcm.append (ss.str());
-
-    if (subdevice != 0) {
-        pcm.append (",");
-        ss1 << subdevice;
-        pcm.append (ss1.str());
-    }
-
-    _debug ("Audio: Device topo: %s", pcm.c_str());
-
-    return pcm;
+    return pcm + ss.str();
 }
 
 std::vector<std::string>
 AlsaLayer::getSoundCardsInfo (int stream)
 {
-    _debug ("Audio: Get sound cards info: ");
-
     snd_ctl_t* handle;
     snd_ctl_card_info_t *info;
     snd_pcm_info_t* pcminfo;
@@ -656,7 +587,6 @@ AlsaLayer::getSoundCardsInfo (int stream)
     snd_pcm_info_alloca (&pcminfo);
 
     int numCard = -1 ;
-    std::string description;
 
     std::vector<std::string> cards_id;
     if (snd_card_next (&numCard) < 0 || numCard < 0)
@@ -679,7 +609,7 @@ AlsaLayer::getSoundCardsInfo (int stream)
                                 numCard,
                                 snd_ctl_card_info_get_id (info),
                                 snd_ctl_card_info_get_name (info));
-                    description = snd_ctl_card_info_get_name (info);
+                    std::string description = snd_ctl_card_info_get_name (info);
                     description.append (" - ");
                     description.append (snd_pcm_info_get_name (pcminfo));
                     cards_id.push_back (description);
diff --git a/daemon/src/audio/alsa/alsalayer.h b/daemon/src/audio/alsa/alsalayer.h
index c29ccda940..a8a8629c8e 100644
--- a/daemon/src/audio/alsa/alsalayer.h
+++ b/daemon/src/audio/alsa/alsalayer.h
@@ -83,10 +83,9 @@ class AlsaLayer : public AudioLayer
          * Concatenate two strings. Used to build a valid pcm device name.
          * @param plugin the alsa PCM name
          * @param card the sound card number
-         * @param subdevice the subdevice number
          * @return std::string the concatenated string
          */
-        std::string buildDeviceTopo (const std::string &plugin, int card, int subdevice);
+        std::string buildDeviceTopo (const std::string &plugin, int card);
 
         /**
          * Scan the sound card available on the system
@@ -135,7 +134,6 @@ class AlsaLayer : public AudioLayer
         int getIndexIn() const {
             return indexIn_;
         }
-        void setIndexIn(int);
 
         /**
          * Get the index of the audio card for playback
@@ -145,7 +143,6 @@ class AlsaLayer : public AudioLayer
         int getIndexOut() const {
             return indexOut_;
         }
-        void setIndexOut(int);
 
         /**
 		 * Get the index of the audio card for ringtone (could be differnet from playback)
@@ -155,13 +152,12 @@ class AlsaLayer : public AudioLayer
         int getIndexRing() const {
             return indexRing_;
         }
-        void setIndexRing(int);
-
-        void setPlugin(const std::string &plugin);
 
     private:
 
 
+        bool openDevice(snd_pcm_t **pcm, const std::string &dev, snd_pcm_stream_t stream);
+
         /**
          * Number of audio cards on which capture stream has been opened
          */
@@ -201,7 +197,7 @@ class AlsaLayer : public AudioLayer
         void startPlaybackStream (void);
         void preparePlaybackStream (void);
 
-        bool alsa_set_params (snd_pcm_t *pcm_handle, int type);
+        bool alsa_set_params (snd_pcm_t *pcm_handle);
 
         /**
          * Copy a data buffer in the internal ring buffer
diff --git a/daemon/src/audio/audiolayer.h b/daemon/src/audio/audiolayer.h
index 56b47e2a8c..f6e348b0d4 100644
--- a/daemon/src/audio/audiolayer.h
+++ b/daemon/src/audio/audiolayer.h
@@ -142,19 +142,9 @@ class AudioLayer
         void notifyincomingCall (void);
 
     protected:
-
-        /**
-         * Drop the pending frames and close the capture device
-         */
-        virtual void closeCaptureStream (void) = 0;
-
-        /**
-         * Drop the pending frames and close the playback device
-         */
-        virtual void closePlaybackStream (void) = 0;
  
         /**
-	 * Wether or not the audio layer stream is started
+         * Wether or not the audio layer stream is started
          */
         bool isStarted_;
 
diff --git a/daemon/src/audio/pulseaudio/pulselayer.cpp b/daemon/src/audio/pulseaudio/pulselayer.cpp
index 90f09b3e19..42d29b3909 100644
--- a/daemon/src/audio/pulseaudio/pulselayer.cpp
+++ b/daemon/src/audio/pulseaudio/pulselayer.cpp
@@ -310,21 +310,14 @@ void PulseLayer::context_state_callback (pa_context* c, void* user_data)
 
 void PulseLayer::updateSinkList (void)
 {
-    _debug ("Audio: Update sink list");
-
     getSinkList()->clear();
-
     pa_context_get_sink_info_list (context_, sink_input_info_callback,  this);
 }
 
 void PulseLayer::updateSourceList (void)
 {
-    _debug ("Audio: Update source list");
-
     getSourceList()->clear();
-
     pa_context_get_source_info_list (context_, source_input_info_callback, this);
-
 }
 
 bool PulseLayer::inSinkList (const std::string &deviceName) const
@@ -341,22 +334,15 @@ bool PulseLayer::inSourceList (const std::string &deviceName) const
 
 void PulseLayer::createStreams (pa_context* c)
 {
-    _info ("Audio: Create streams");
-
-    playback_ = new AudioStream (c, mainloop_, PLAYBACK_STREAM_NAME, PLAYBACK_STREAM, audioSampleRate_);
-
     std::string playbackDevice(audioPref.getDevicePlayback());
     std::string recordDevice(audioPref.getDeviceRecord());
     std::string ringtoneDevice(audioPref.getDeviceRingtone());
 
-    _debug ("Audio: Device for playback: %s", playbackDevice.c_str());
-    _debug ("Audio: Device for record: %s", recordDevice.c_str());
-    _debug ("Audio: Device for ringtone: %s", ringtoneDevice.c_str());
+    _debug ("PulseAudio: Devices: playback %s , record %s , ringtone %s",
+    		playbackDevice.c_str(), recordDevice.c_str(), ringtoneDevice.c_str());
 
-    if (inSinkList (playbackDevice))
-        playback_->connectStream (&playbackDevice);
-    else
-        playback_->connectStream (NULL);
+    playback_ = new AudioStream (c, mainloop_, PLAYBACK_STREAM_NAME, PLAYBACK_STREAM, audioSampleRate_);
+	playback_->connectStream(inSinkList(playbackDevice) ? &playbackDevice : NULL);
 
     pa_stream_set_write_callback (playback_->pulseStream(), playback_callback, this);
     pa_stream_set_overflow_callback (playback_->pulseStream(), playback_overflow_callback, this);
@@ -365,22 +351,14 @@ void PulseLayer::createStreams (pa_context* c)
     pa_stream_set_latency_update_callback (playback_->pulseStream(), latency_update_callback, this);
 
     record_ = new AudioStream (c, mainloop_, CAPTURE_STREAM_NAME, CAPTURE_STREAM, audioSampleRate_);
-
-    if (inSourceList (recordDevice))
-        record_->connectStream (&recordDevice);
-    else
-        record_->connectStream (NULL);
+	record_->connectStream (inSourceList(recordDevice) ? &recordDevice : NULL);
 
     pa_stream_set_read_callback (record_->pulseStream() , capture_callback, this);
     pa_stream_set_moved_callback (record_->pulseStream(), stream_moved_callback, this);
     pa_stream_set_latency_update_callback (record_->pulseStream(), latency_update_callback, this);
 
     ringtone_ = new AudioStream (c, mainloop_, RINGTONE_STREAM_NAME, RINGTONE_STREAM, audioSampleRate_);
-
-    if (inSourceList (ringtoneDevice))
-        ringtone_->connectStream (&ringtoneDevice);
-    else
-        ringtone_->connectStream (NULL);
+	ringtone_->connectStream(inSourceList(ringtoneDevice) ? &ringtoneDevice : NULL);
 
     pa_stream_set_write_callback (ringtone_->pulseStream(), ringtone_callback, this);
     pa_stream_set_moved_callback (ringtone_->pulseStream(), stream_moved_callback, this);
@@ -393,43 +371,13 @@ void PulseLayer::createStreams (pa_context* c)
 
 
 void PulseLayer::disconnectAudioStream (void)
-{
-    _info ("Audio: Disconnect audio stream");
-
-    closePlaybackStream();
-    closeCaptureStream();
-}
-
-
-void PulseLayer::closeCaptureStream (void)
-{
-    if (record_) {
-
-        if (record_->pulseStream()) {
-            const char *name = pa_stream_get_device_name (record_->pulseStream());
-
-            if (name && strlen (name)) {
-                _debug ("Audio: record device to be stored in config: %s", name);
-                audioPref.setDeviceRecord (name);
-            }
-        }
-
-        delete record_;
-        record_ = NULL;
-    }
-}
-
-
-void PulseLayer::closePlaybackStream (void)
 {
     if (playback_) {
         if (playback_->pulseStream()) {
             const char *name = pa_stream_get_device_name (playback_->pulseStream());
 
-            if (name && strlen (name)) {
-                _debug ("Audio: playback device to be stored in config: %s", name);
+            if (name && *name)
                 audioPref.setDevicePlayback (name);
-            }
         }
 
         delete playback_;
@@ -437,21 +385,28 @@ void PulseLayer::closePlaybackStream (void)
     }
 
     if (ringtone_) {
-        if (ringtone_->pulseStream()) {
-            const char *name = pa_stream_get_device_name (ringtone_->pulseStream());
+		if (ringtone_->pulseStream()) {
+			const char *name = pa_stream_get_device_name (ringtone_->pulseStream());
+			if (name && *name)
+				audioPref.setDeviceRingtone (name);
+		}
 
-            if (name && strlen (name)) {
-                _debug ("Audio: ringtone device to be stored in config: %s", name);
-                audioPref.setDeviceRingtone (name);
-            }
-        }
+		delete ringtone_;
+		ringtone_ = NULL;
+    }
 
-        delete ringtone_;
-        ringtone_ = NULL;
+    if (record_) {
+		if (record_->pulseStream()) {
+			const char *name = pa_stream_get_device_name (record_->pulseStream());
+			if (name && *name)
+				audioPref.setDeviceRecord (name);
+		}
+
+		delete record_;
+		record_ = NULL;
     }
 }
 
-
 void PulseLayer::startStream (void)
 {
     // Create Streams
diff --git a/daemon/src/audio/pulseaudio/pulselayer.h b/daemon/src/audio/pulseaudio/pulselayer.h
index f06ce86539..8c4baf6b45 100644
--- a/daemon/src/audio/pulseaudio/pulselayer.h
+++ b/daemon/src/audio/pulseaudio/pulselayer.h
@@ -166,12 +166,6 @@ class PulseLayer : public AudioLayer
         // Assignment Operator
         PulseLayer& operator= (const PulseLayer& rh);
 
-
-        /**
-         * Drop the pending frames and close the capture device
-         */
-        void closeCaptureStream (void);
-
         /**
          * Write data from the ring buffer to the harware and read data from the hardware
          */
@@ -185,11 +179,6 @@ class PulseLayer : public AudioLayer
          */
         void createStreams (pa_context* c);
 
-        /**
-         * Drop the pending frames and close the playback device
-         */
-        void closePlaybackStream (void);
-
         /**
          * Close the connection with the local pulseaudio server
          */
diff --git a/daemon/src/managerimpl.cpp b/daemon/src/managerimpl.cpp
index 9ad7b54b9c..2aae49f0bd 100644
--- a/daemon/src/managerimpl.cpp
+++ b/daemon/src/managerimpl.cpp
@@ -2053,8 +2053,19 @@ void ManagerImpl::setAudioPlugin (const std::string& audioPlugin)
     audioPreference.setPlugin (audioPlugin);
 
     AlsaLayer *alsa = dynamic_cast<AlsaLayer*>(_audiodriver);
-    if (alsa)
-    	alsa->setPlugin(audioPlugin);
+    if (!alsa) {
+        _error("Can't find alsa device");
+        audioLayerMutexUnlock();
+        return ;
+    }
+
+    bool wasStarted = _audiodriver->isStarted();
+
+    // Recreate audio driver with new settings
+    delete _audiodriver;
+    _audiodriver = new AlsaLayer;
+    if (wasStarted)
+        _audiodriver->startStream();
 
     audioLayerMutexUnlock();
 }
@@ -2073,23 +2084,29 @@ void ManagerImpl::setAudioDevice (const int index, int streamType)
         return ;
     }
 
+    bool wasStarted = _audiodriver->isStarted();
+
     switch (streamType) {
         case SFL_PCM_PLAYBACK:
-        	alsaLayer->setIndexOut(index);
             audioPreference.setCardout (index);
             break;
         case SFL_PCM_CAPTURE:
-        	alsaLayer->setIndexIn(index);
             audioPreference.setCardin (index);
             break;
         case SFL_PCM_RINGTONE:
-        	alsaLayer->setIndexRing(index);
             audioPreference.setCardring (index);
             break;
         default:
             break;
     }
 
+    // Recreate audio driver with new settings
+    delete _audiodriver;
+    _audiodriver = new AlsaLayer;
+
+    if (wasStarted)
+        _audiodriver->startStream();
+
     audioLayerMutexUnlock();
 }
 
-- 
GitLab