Change start/stop functions for ALSA output to deal with an intermittent
authorbruceperens <bruceperens@01035d8c-6547-0410-b346-abe4f91aad63>
Fri, 25 Apr 2014 20:44:35 +0000 (20:44 +0000)
committerbruceperens <bruceperens@01035d8c-6547-0410-b346-abe4f91aad63>
Fri, 25 Apr 2014 20:44:35 +0000 (20:44 +0000)
"roughness" problem on the output. Always pre-load after underrun.
Use snd_pcm_drop() to stop, and don't call snd_pcm_prepare() until we're about
to write.

git-svn-id: https://svn.code.sf.net/p/freetel/code@1564 01035d8c-6547-0410-b346-abe4f91aad63

freedv-server/source/platform/linux/audio_out_alsa.cpp

index b8e50b142a88b2e179acab8ac95eec53e9dd61e5..3d7e3f9500d0f8625a5f113c5afcff5e7eb47c17 100644 (file)
@@ -133,7 +133,8 @@ namespace FreeDV {
       // a shared clock, and the more expensive equipment that supports it,
       // to avoid this problem.
       //
-      int16_t  buf[AudioFrameSamples];
+      snd_pcm_prepare(handle);
+      int16_t  buf[AudioFrameSamples * 2];
       memset(buf, 0, sizeof(buf));
       snd_pcm_writei(handle, buf, sizeof(buf) / sizeof(*buf));
     }
@@ -180,56 +181,55 @@ namespace FreeDV {
   std::size_t
   AudioOutALSA::ready()
   {
-    snd_pcm_sframes_t  available = 0;
-    snd_pcm_sframes_t  delay = 0;
-    int                        error;
-
-    if ( !started )
-      return AudioFrameSamples;
-
-    error = snd_pcm_avail_delay(handle, &available, &delay);
-    if ( delay > (AudioFrameSamples * 8) ) {
-      const double seconds = (double)delay / (double)SampleRate;
-
-      std::cerr << "ALSA output \"" << parameters
-       << "\": overlong delay, dropped  "
-       << seconds << " seconds of output." << std::endl;
-      snd_pcm_drop(handle);
-      snd_pcm_prepare(handle);
-      snd_pcm_pause(handle, 1);
-      started = false;
-      return AudioFrameSamples;
-    }
-
-    if ( error == -EPIPE ) {
-      std::cerr << "ALSA output \"" << parameters << "\": ready underrun." << std::endl;
-      snd_pcm_recover(handle, error, 1);
-      snd_pcm_pause(handle, 1);
-      started = false;
-
-      return AudioFrameSamples;
+    const unsigned int MaximumDelayFrames = 8;
+
+    for ( unsigned int loop = 0; loop < 10; loop++ ) {
+      snd_pcm_sframes_t        available = 0;
+      snd_pcm_sframes_t        delay = 0;
+  
+      const int error = snd_pcm_avail_delay(handle, &available, &delay);
+  
+      // If we've not started, allow the first write to be large, but
+      // not as large as the MaximumDelayFrames + the preload frame size.
+      if ( !started )
+        return AudioFrameSamples * MaximumDelayFrames - 3;
+  
+      if ( error ) {
+        if ( error == -EPIPE ) {
+          std::cerr << "ALSA output \"" << parameters << "\": ready underrun." << std::endl;
+          snd_pcm_drop(handle);
+          started = false;
+        }
+        else
+          do_throw(error, "Get Frames Available for Write");
+      }
+      else if ( delay > (AudioFrameSamples * MaximumDelayFrames) ) {
+        const double seconds = (double)delay / (double)SampleRate;
+  
+        std::cerr << "ALSA output \"" << parameters
+         << "\": overlong delay, dropped  "
+         << seconds << " seconds of output." << std::endl;
+        snd_pcm_drop(handle);
+        started = false;
+        continue;
+      }
+      else
+       return available;
     }
-
-    if ( error == 0 )
-      return available;
-    else
-      do_throw(error, "Get Frames Available for Write");
+    do_throw(0, "Audio output stuck in ready()");
+    return 0; // NOTREACHED.
   }
 
   void
   AudioOutALSA::start()
   {
     snd_pcm_drop(handle);
-    snd_pcm_prepare(handle);
-    snd_pcm_pause(handle, 1);
   }
 
   void
   AudioOutALSA::stop()
   {
     snd_pcm_drop(handle);
-    snd_pcm_prepare(handle);
-    snd_pcm_pause(handle, 1);
     started = false;
   }