--- a
+++ b/sc2src/watcher.cpp
@@ -0,0 +1,119 @@
+/* Copyright (C) 2016 J.F.Dockes
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the
+ *   Free Software Foundation, Inc.,
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+#include "config.h"
+
+#include <unistd.h>
+#include <thread>
+
+#include "watcher.h"
+#include "rcvqueue.h"
+#include "smallut.h"
+
+using namespace std;
+
+/* Parameters */
+static string o_cmd;
+static unsigned int o_idlesecs;
+static unsigned int o_pollsecs;
+static bool o_doclosealsa;
+
+/* Queue we watch */
+static AudioEater *o_eater;
+
+/* Queue state */
+static bool o_active;
+static time_t o_last_active;
+static unsigned int o_lastcntvalue;
+
+static thread o_worker;
+
+static void watcher(void *)
+{
+    string argument;
+    for (;;) {
+        LOGDEB("watcher: active " << o_active << " lastpktcnt " <<
+               o_lastcntvalue << " pktcnt " << o_eater->pktcounter << endl);
+        if (!o_active && o_lastcntvalue != o_eater->pktcounter) {
+            // If not currently active and the packet counter changed,
+            // switch to active, exec command.
+            o_active = true;
+            argument = "on";
+        } else if (o_active && o_lastcntvalue == o_eater->pktcounter &&
+                   time(0) - o_last_active > o_idlesecs) {
+            // If state currently active but no packet count change,
+            // and the delay is expired, switch to inactive and
+            // execute the command
+            o_active = false;
+            argument = "off";
+            LOGDEB("watcher: CLOSING ALSA " << endl);
+            if (o_doclosealsa) {
+                // The reopen is auto-performed by the audio queue to
+                // avoid delay.
+                alsa_close();
+            }
+        }
+        if (!argument.empty()) {
+            string cmd(o_cmd + " " + argument);
+            LOGDEB("watcher: executing " << cmd << endl);
+            int ret = system(cmd.c_str());
+            (void)ret;
+            argument.clear();
+        }
+        // In all cases, if the packet counter changed, record the new
+        // value and the current time
+        if (o_lastcntvalue != o_eater->pktcounter) {
+            o_lastcntvalue = o_eater->pktcounter;
+            o_last_active = time(0);
+        }
+        sleep(o_pollsecs);
+    }
+}
+
+void startWatcher(ConfSimple& config)
+{
+    string cmd, sidlesecs, spollsecs, sclosealsa;
+    unsigned int idlesecs(900), pollsecs(2);
+    bool closealsa(true);
+
+    config.get("scwatchercmd", cmd);
+    config.get("scwatcheridlesecs", sidlesecs);
+    if (!sidlesecs.empty()) {
+        idlesecs = (unsigned int)atoi(sidlesecs.c_str());
+    }
+    config.get("scwatcherpollsecs", spollsecs);
+    if (!spollsecs.empty()) {
+        pollsecs = (unsigned int)atoi(spollsecs.c_str());
+    }
+    config.get("scwatcherclosealsa", sclosealsa);
+    if (!sclosealsa.empty()) {
+        closealsa = stringToBool(sclosealsa);
+    }
+
+    if (cmd.empty() && !closealsa) {
+        LOGDEB("startWatcher: no watcher: empty cmd and closealsa is false\n");
+        return;
+    }
+
+    o_cmd = cmd;
+    o_idlesecs = idlesecs ? idlesecs : 900;
+    o_pollsecs = pollsecs ? pollsecs : 2;
+    o_doclosealsa = closealsa;
+    o_eater = &alsaAudioEater;
+    
+    o_worker = thread(watcher, nullptr);
+    o_worker.detach();
+}