Make ALSA more tolerant of differing hardware.
authorbruceperens <bruceperens@01035d8c-6547-0410-b346-abe4f91aad63>
Fri, 25 Apr 2014 00:35:48 +0000 (00:35 +0000)
committerbruceperens <bruceperens@01035d8c-6547-0410-b346-abe4f91aad63>
Fri, 25 Apr 2014 00:35:48 +0000 (00:35 +0000)
git-svn-id: https://svn.code.sf.net/p/freetel/code@1555 01035d8c-6547-0410-b346-abe4f91aad63

freedv-server/source/drivers.h
freedv-server/source/platform/linux/alsa.cpp
freedv-server/source/platform/linux/audio_in_alsa.cpp
freedv-server/source/platform/linux/audio_out_alsa.cpp

index e49e64978cb478211b6868cfc4c2fa78af4ae14a..954587cc65d60a15ce051fd32d5bd3fc966e4dbd 100644 (file)
@@ -63,20 +63,16 @@ const unsigned int  SamplesPerMillisecond = ((double)SampleRate / 1000.0);
 /// size we expect to use.
 const unsigned int     AudioFrameDuration = 10;
 
-/// The number of audio samples in an audio frame.
-///
-const unsigned int     AudioFrameSamples = SamplesPerMillisecond
-                        * AudioFrameDuration;
-
-/// The maximum frame duration in milliseconds. This will be used to set
-/// buffer sizes. It must be larger than the frame duration used by your
-/// modem/framer/codec combination, or they will stall.
-const unsigned int     MaximumFrameDuration = 100;
+/// The number of audio samples in an audio frame. Audio frames must be a
+/// power of two (this is a common hardware requirement) and must be shorter
+/// than any codec/modem frame in the program.
+const unsigned int     AudioFrameSamples = 512;
 
 /// The number of audio samples in the maximum-duration frame.
+/// This must be a power of two (this is a common hardware requirement) and
+/// must be at least twice the value of AudioFrameSamples.
 ///
-const unsigned int     MaximumFrameSamples = SamplesPerMillisecond
-                        * MaximumFrameDuration;
+const unsigned int     MaximumFrameSamples = 32768;
 
 /// Allocate memory and copy a string into it, so that it is permanently
 /// stored.
index 2cd1a75f850d33819d16585c490401ace9e42ba0..7b69fa666732b3e5f8ec706059b21091f7160da8 100644 (file)
@@ -228,6 +228,7 @@ namespace FreeDV {
     int                                error;
     snd_pcm_t *                        handle = 0;
     snd_pcm_hw_params_t *      hw_params = 0;
+    snd_pcm_sw_params_t *      sw_params = 0;
  
     error = open_by_longname(&handle, name, stream, mode);
     if ( error < 0 ) {
@@ -241,53 +242,73 @@ namespace FreeDV {
          return 0;
     }
  
-    if ( (error = snd_pcm_hw_params_malloc(&hw_params)) < 0 ) {
-      snd_pcm_close(handle);
-      do_throw(error, name, stream, "ALSA hardare parameter allocation");
-    }
-    if ( (error = snd_pcm_hw_params_any(handle, hw_params )) < 0 ) {
-      snd_pcm_close(handle);
-      do_throw(error, name, stream, "Get configuration space for device");
-    }
-
-    if ( (error = snd_pcm_hw_params_set_format(handle, hw_params, format )) < 0 ) {
-      snd_pcm_close(handle);
-      do_throw(error, name, stream, "Set format");
-    }
-
-    if ( (error = snd_pcm_hw_params_set_access(handle, hw_params, access )) < 0 ) {
-      snd_pcm_close(handle);
-      do_throw(error, name, stream, "Set access");
-    }
+    try {
+      if ( (error = snd_pcm_hw_params_malloc(&hw_params)) < 0 )
+        do_throw(error, name, stream, "ALSA hardare parameter allocation");
+   
+      if ( (error = snd_pcm_hw_params_any(handle, hw_params )) < 0 )
+        do_throw(error, name, stream, "Get configuration space for device");
+  
+      if ( (error = snd_pcm_hw_params_set_format(handle, hw_params, format )) < 0 )
+        do_throw(error, name, stream, "Set format");
+  
+      if ( (error = snd_pcm_hw_params_set_access(handle, hw_params, access )) < 0 )
+        do_throw(error, name, stream, "Set access");
+  
+      if ( (error = snd_pcm_hw_params_set_channels(handle, hw_params, channels )) < 0 )
+        do_throw(error, name, stream, "Set channels");
+  
+      if ( (error = snd_pcm_hw_params_set_rate(handle, hw_params, rate, 0 )) < 0 )
+        do_throw(error, name, stream, "Set rate");
+  
+      if ( (error = snd_pcm_hw_params_set_period_size_near(handle, hw_params, &period_size, 0)) < 0 )
+        do_throw(error, name, stream, "Set I/O period size");
+  
+      if ( (error = snd_pcm_hw_params_set_buffer_size_near(handle, hw_params, &buffer_size)) < 0 )
+        do_throw(error, name, stream, "Set I/O buffer size");
+   
+      if ( (error = snd_pcm_hw_params(handle, hw_params)) < 0 )
+        do_throw(error, name, stream, "ALSA hardware parameter select");
+   
+      snd_pcm_hw_params_free(hw_params);
+   
+      if ((error = snd_pcm_sw_params_malloc(&sw_params)) < 0)
+        do_throw(error, name, stream, "ALSA software parameter allocate");
+  
+      if ((error = snd_pcm_sw_params_current(handle, sw_params)) < 0)
+        do_throw(error, name, stream, "ALSA get software parameters");
+  
+      if ((error = snd_pcm_sw_params_set_avail_min(handle, sw_params,
+       period_size)) < 0)
+        do_throw(error, name, stream, "ALSA software set minimum available.");
+  
+      if (( error = snd_pcm_sw_params_set_period_event(handle, sw_params, 1))
+       < 0 )
+        do_throw(error, name, stream, "ALSA software set period event.");
 
-    if ( (error = snd_pcm_hw_params_set_channels(handle, hw_params, channels )) < 0 ) {
-      snd_pcm_close(handle);
-      do_throw(error, name, stream, "Set channels");
-    }
+      if ((error = snd_pcm_sw_params_set_start_threshold(handle, sw_params, period_size))
+       < 0)
+        do_throw(error, name, stream,
+         "ALSA set software start threshold");
 
-    if ( (error = snd_pcm_hw_params_set_rate(handle, hw_params, rate, 0 )) < 0 ) {
-      snd_pcm_close(handle);
-      do_throw(error, name, stream, "Set rate");
-    }
+      if (( error = snd_pcm_sw_params_set_stop_threshold(handle, sw_params,
+       buffer_size + 1)) < 0 )
+        do_throw(error, name, stream,
+         "ALSA set software stop threshold");
 
-    if ( (error = snd_pcm_hw_params_set_period_size_near(handle, hw_params, &period_size, 0)) < 0 ) {
-      snd_pcm_close(handle);
-      do_throw(error, name, stream, "Set I/O period size");
-    }
+      if ((error = snd_pcm_sw_params(handle, sw_params)) < 0)
+        do_throw(error, name, stream, "ALSA set software parameters");
+      
+      snd_pcm_sw_params_free(sw_params);
 
-    if ( (error = snd_pcm_hw_params_set_buffer_size_near(handle, hw_params, &buffer_size)) < 0 ) {
-      snd_pcm_close(handle);
-      do_throw(error, name, stream, "Set I/O buffer size");
+      if ((error = snd_pcm_prepare(handle)) < 0)
+        do_throw(error, name, stream, "ALSA prepare audio interface for use");
     }
-    if ( (error = snd_pcm_hw_params(handle, hw_params)) < 0 ) {
+    catch (...)
+    {
       snd_pcm_close(handle);
-      do_throw(error, name, stream, "ALSA hardware parameter select");
+      throw; // Re-throws the current exception without modification.
     }
-    snd_pcm_hw_params_free(hw_params);
     return handle;
   }
 }
index 2a805302ceb9a3a3781ca373634297f9ea9312fc..6d95544a12b80406271778aa63a3b13b6ebf5784 100644 (file)
@@ -82,7 +82,7 @@ namespace FreeDV {
      SND_PCM_ACCESS_RW_INTERLEAVED,
      1,
      SampleRate,
-     AudioFrameSamples,
+     AudioFrameSamples, 
      AudioFrameSamples * 2);
 
     if ( handle == 0 )
@@ -188,6 +188,7 @@ namespace FreeDV {
 
     if ( error == -EPIPE ) {
       snd_pcm_recover(handle, error, 1);
+      snd_pcm_start(handle);
       std::cerr << "ALSA input \"" << parameters << "\": ready underrun." << std::endl;
       return 0;
     }
index bbeb2c495e5f3f869619926fd22b6c332e1fddd2..6f370c0fc6bf7b0ca3de8aed53b7878418ae2075 100644 (file)
@@ -122,7 +122,7 @@ namespace FreeDV {
       // a shared clock, and the more expensive equipment that supports it,
       // to avoid this problem.
       //
-      int16_t  buf[AudioFrameSamples / 100];
+      int16_t  buf[AudioFrameSamples];
       memset(buf, 0, sizeof(buf));
       snd_pcm_writei(handle, buf, sizeof(buf) / sizeof(*buf));
     }
@@ -177,7 +177,7 @@ namespace FreeDV {
       return AudioFrameSamples;
 
     error = snd_pcm_avail_delay(handle, &available, &delay);
-    if ( delay > (AudioFrameSamples * 4) ) {
+    if ( delay > (AudioFrameSamples * 8) ) {
       const double seconds = (double)delay / (double)SampleRate;
 
       std::cerr << "ALSA output \"" << parameters