Switch to side-by-side view

--- a/sc2src/alsadirect.cpp
+++ b/sc2src/alsadirect.cpp
@@ -33,6 +33,7 @@
 
 #include <iostream>
 #include <queue>
+#include <mutex>
 #include <alsa/asoundlib.h>
 
 #include <samplerate.h>
@@ -62,9 +63,14 @@
    started */
 static bool qinit = false;
 
+/* Alsa. Locked while we work with it */
+static std::mutex alsa_mutex;
 static snd_pcm_t *pcm;
-
-// From MPD recovery code
+static string alsadevice("default");
+static snd_pcm_sframes_t alsa_delay;
+
+// From MPD recovery code.
+// Note: no locking: we're called from alsawriter holding the lock.
 static int alsa_recover(snd_pcm_t *pcm, int err)
 {
     if (err == -EPIPE) {
@@ -120,6 +126,8 @@
 static snd_pcm_uframes_t periodframes;
 static snd_pcm_uframes_t bufferframes;
 
+static bool alsa_init(const string& dev, AudioMessage *tsk);
+
 static void *alsawriter(void *p)
 {
     while (true) {
@@ -138,12 +146,23 @@
             return (void*)1;
         }
 
+        std::unique_lock<std::mutex> lock(alsa_mutex);
+        if (pcm == nullptr) {
+            if (!alsa_init(alsadevice, tsk)) {
+                alsaqueue.workerExit();
+                return (void*)1;
+            }
+        }
+
         snd_pcm_uframes_t frames = tsk->frames();
         char *buf = tsk->m_buf;
         // This loop is copied from the alsa sample, but it should not
         // be necessary, in synchronous mode, alsa is supposed to
         // perform complete writes except for errors or interrupts
         while (frames > 0) {
+            if (snd_pcm_delay(pcm, &alsa_delay) < 0) {
+                alsa_delay = 0;
+            }
 //            LOGDEB("alsawriter: avail frames " << snd_pcm_avail(pcm) <<
 //                   " writing " << frames << endl);
             snd_pcm_sframes_t ret =  snd_pcm_writei(pcm, tsk->m_buf, frames);
@@ -296,23 +315,18 @@
 
 error:
     LOGERR("alsa_init: " << cmd << " error:" << snd_strerror(err) << endl);
+    alsa_close();
     return false;
 }
 
-static void alsa_close()
+void alsa_close()
 {
     LOGDEB("alsawriter: alsa close\n");
-    snd_pcm_close(pcm);
-}
-
-// Current in-driver delay in samples
-static int alsadelay()
-{
-    snd_pcm_sframes_t delay;
-    if (snd_pcm_delay(pcm, &delay) >= 0) {
-        return delay;
-    } else {
-        return 0;
+    std::unique_lock<std::mutex> lock(alsa_mutex);
+    alsa_delay = 0;
+    if (pcm != nullptr) {
+        snd_pcm_close(pcm);
+        pcm = nullptr;
     }
 }
 
@@ -394,7 +408,7 @@
     
     if (qinit) {
         // Qsize in frames. This is the variable to control
-        qs = alsaqueue.qsize() * bufframes + alsadelay();
+        qs = alsaqueue.qsize() * bufframes + alsa_delay;
         // Error term
         double qstargframes = qstarg * bufframes;
         double et =  ((qstargframes - qs) / qstargframes);
@@ -680,7 +694,6 @@
     LOGDEB("audioEater: alsadirect. Will use converter type " << 
            cvt_type << endl);
 
-    string alsadevice("default");
     ctxt->config->get("scalsadevice", alsadevice);    
 
     WorkQueue<AudioMessage*> *queue = ctxt->queue;
@@ -709,6 +722,8 @@
             LOGDEB("audioEater: alsadirect: queue take failed\n");
             goto done;
         }
+        alsaAudioEater.pktcounter++;
+        
         if (tsk->m_bytes == 0 || tsk->m_chans == 0 || tsk->m_bits == 0) {
             LOGDEB("Zero buf\n");
             continue;
@@ -716,9 +731,6 @@
 
         // 1st time: init. We don't want to do this before we have data.
         if (src_state == 0) {
-            if (!alsa_init(alsadevice, tsk)) {
-                goto done;
-            }
             if (cvt_type != -1) {
                 src_state = src_new(cvt_type, tsk->m_chans, &src_error);
             } else {