Use the old-style parameter setting functions, so that I can set the period
authorbruceperens <bruceperens@01035d8c-6547-0410-b346-abe4f91aad63>
Thu, 13 Mar 2014 05:31:47 +0000 (05:31 +0000)
committerbruceperens <bruceperens@01035d8c-6547-0410-b346-abe4f91aad63>
Thu, 13 Mar 2014 05:31:47 +0000 (05:31 +0000)
size and buffer size.

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

freedv-server/source/drivers.h
freedv-server/source/interfaces.cpp
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
freedv-server/source/run.cpp

index e6aa2e5d238f0866c4bcc611aa7441782b04af98..b8925a373488167241e9e611f1a99f5957c62b7e 100644 (file)
@@ -696,6 +696,13 @@ namespace FreeDV {
     return a < b ? a : b;
   }
 
+  /// Non-template version of max().
+  inline std::size_t
+  max(std::size_t a, std::size_t b)
+  {
+    return a > b ? a : b;
+  }
+
   struct DriverList {
     const char *       key;
     union {
index 7f41d81838744d36871e289d3c19badf1cbc94c8..7ce77ec3de877980c74f32c11443a3aadf0a2841 100644 (file)
@@ -16,6 +16,9 @@ namespace FreeDV {
     if ( !codec )
       codec = Driver::CodecNoOp(empty);
 
+    if ( !framer )
+      framer = Driver::FramerNoOp(empty);
+
     if ( !keying_output )
       keying_output = Driver::KeyingSink(empty);
 
@@ -55,6 +58,7 @@ namespace FreeDV {
       stream << program_name << " \\" << std::endl;
     }
     stream << "--codec=\"" << *codec << "\" \\" << std::endl;
+    stream << "--framer=\"" << *framer << "\" \\" << std::endl;
     stream << "--gui=\"" << *user_interface << "\" \\" << std::endl;
     stream << "--keying=\"" << *keying_output << "\" \\" << std::endl;
     stream << "--loudspeaker=\"" << *loudspeaker << "\" \\" << std::endl;
index f2aa5215344c14cce40387d1acf6325e8196e112..8946138d202fb6cd90a1dbbdae4ffa876022d51a 100644 (file)
@@ -1,6 +1,7 @@
+#include "alsa.h"
 #include <iostream>
-#include <alsa/asoundlib.h>
 #include <sstream>
+#include <stdexcept>
 
 namespace FreeDV {
   static std::ostream &
@@ -28,7 +29,6 @@ namespace FreeDV {
   {
     int                        card_index = -1;
     snd_pcm_t *                pcm_handle;
-    snd_ctl_card_info_t *      info = 0;
   
     const int error = snd_pcm_open(
      &pcm_handle,
@@ -70,4 +70,99 @@ namespace FreeDV {
   
     return stream;
   }
+
+  static void
+  do_throw(
+   const int error,
+   const char * name,
+   const char * message = 0)
+  {
+    std::ostringstream str;
+
+    str << "ALSA device \"" << name << "\" set-up error: ";
+     if ( message )
+       str << message << ": ";
+     str << snd_strerror(error) << '.';
+    throw std::runtime_error(str.str().c_str());
+  }
+
+  snd_pcm_t *
+  ALSASetup(
+   const char *                name,
+   snd_pcm_stream_t    stream,
+   int                 mode,
+   snd_pcm_format_t    format,
+   snd_pcm_access_t    access,
+   unsigned int        channels,
+   unsigned int        rate,
+   snd_pcm_uframes_t   period_size,
+   snd_pcm_uframes_t   buffer_size)
+  {
+    int                        error;
+    snd_pcm_t *                handle = 0;
+    snd_pcm_hw_params_t *      hw_params = 0;
+    error = snd_pcm_open(
+     &handle,
+     name,
+     stream,
+     mode);
+    if ( error < 0 )
+      return 0;
+    if ( (error = snd_pcm_hw_params_malloc(&hw_params)) < 0 ) {
+      snd_pcm_close(handle);
+      do_throw(error, name, "ALSA hardare parameter allocation");
+    }
+    if ( (error = snd_pcm_hw_params_any(handle, hw_params )) < 0 ) {
+      snd_pcm_close(handle);
+      do_throw(error, name, "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, "Set format");
+    }
+
+    if ( (error = snd_pcm_hw_params_set_access(handle, hw_params, access )) < 0 ) {
+      snd_pcm_close(handle);
+      do_throw(error, name, "Set access");
+    }
+
+    if ( (error = snd_pcm_hw_params_set_channels(handle, hw_params, channels )) < 0 ) {
+      snd_pcm_close(handle);
+      do_throw(error, name, "Set channels");
+    }
+
+    if ( (error = snd_pcm_hw_params_set_rate(handle, hw_params, rate, 0 )) < 0 ) {
+      snd_pcm_close(handle);
+      do_throw(error, name, "Set rate");
+    }
+
+    if ( (error = snd_pcm_hw_params_set_rate_resample(handle, hw_params, 0)) < 0 ) {
+      snd_pcm_close(handle);
+      do_throw(error, name, "Disable resampling");
+    }
+
+    if ( (error = snd_pcm_hw_params_set_period_size(handle, hw_params, period_size, 0)) < 0 ) {
+      snd_pcm_close(handle);
+      do_throw(error, name, "Set I/O period size");
+    }
+
+    if ( (error = snd_pcm_hw_params_set_buffer_size(handle, hw_params, buffer_size)) < 0 ) {
+      snd_pcm_close(handle);
+      do_throw(error, name, "Set I/O buffer size");
+    }
+    if ( (error = snd_pcm_hw_params(handle, hw_params)) < 0 ) {
+      snd_pcm_close(handle);
+      do_throw(error, name, "ALSA hardware parameter select");
+    }
+    snd_pcm_hw_params_free(hw_params);
+    return handle;
+  }
 }
index 0c8b938992dc0639cb8b8ce0f18d687c360d3d72..d40aad9d4d18dcaa5144fa393f99b6b288556464 100644 (file)
@@ -1,10 +1,9 @@
 /// The ALSA audio input driver. 
 
+#include "drivers.h"
+#include "alsa.h"
 #include <stdlib.h>
 #include <errno.h>
-#include "drivers.h"
-#include <alsa/asoundlib.h>
-#include <sys/ioctl.h>
 #include <sstream>
 #include <stdexcept>
 
@@ -14,7 +13,7 @@ namespace FreeDV {
   /// Audio input "ALSA", Uses the Linux ALSA Audio API.
   class AudioInALSA : public AudioInput {
   private:
-    static const int   overlong_delay = 300;
+    static const int   overlong_delay = 10000;
 
     char * const       parameters;
     snd_pcm_t *                handle;
@@ -27,7 +26,7 @@ namespace FreeDV {
       str << "Error on ALSA audio input " << parameters << ": ";
        if ( message )
          str << message << ": ";
-       str << snd_strerror(error);
+       str << snd_strerror(error) << '.';
       throw std::runtime_error(str.str().c_str());
     }
   public:
@@ -52,33 +51,18 @@ namespace FreeDV {
     const snd_pcm_format_t     format = SND_PCM_FORMAT_S16;
     const snd_pcm_access_t     access = SND_PCM_ACCESS_RW_INTERLEAVED;
     const unsigned int         channels = 1;
-    const unsigned int         rate = 48000;
-    const int                  soft_resample = 0;
-    const unsigned int         latency = 0;
     int                                error;
 
-    handle = 0;
-    error = snd_pcm_open(
-     &handle,
+    handle = ALSASetup(
      p,
      SND_PCM_STREAM_CAPTURE,
-     0);
-
-    if ( error != 0 )
-      do_throw(error, "Open");
-
-    if ( (error = snd_pcm_set_params(
-     handle,
+     0,
      format,
      access,
      channels,
-     rate,
-     soft_resample,
-     latency)) < 0 )
-      do_throw(error, "Set Parameters");
-
-    if ( (error = snd_pcm_prepare(handle)) < 0 )
-      do_throw(error, "Prepare");
+     SampleRate,
+     SampleRate / 100,
+     SampleRate / 10);
 
     snd_pcm_start(handle);
   }
@@ -136,8 +120,6 @@ namespace FreeDV {
     // audio samples and a corresponding delay. This can also happen if the
     // incoming audio interface runs at a faster rate than the outgoing one.
     if ( delay >= overlong_delay && available > 0 ) {
-      int dropped = 0;
-
       snd_pcm_drop(handle);
       snd_pcm_prepare(handle);
       snd_pcm_start(handle);
@@ -153,7 +135,7 @@ namespace FreeDV {
     if ( error == -EPIPE ) {
       snd_pcm_recover(handle, error, 1);
       available = snd_pcm_avail_delay(handle, &available, &delay);
-      std::cerr << "ALSA input " << parameters << ": read underrun." << std::endl;
+      std::cerr << "ALSA input " << parameters << ": ready underrun." << std::endl;
     }
 
     if ( error >= 0 )
index 427141fe76719d03a560e5fbd3ef2445d3877e18..85d9d88a8b2989f690d56661289c31dc3745d181 100644 (file)
@@ -1,9 +1,9 @@
 /// The ALSA audio output driver. 
 
+#include "alsa.h"
 #include <stdlib.h>
 #include <errno.h>
 #include "drivers.h"
-#include <alsa/asoundlib.h>
 #include <sys/ioctl.h>
 #include <sstream>
 #include <stdexcept>
@@ -25,7 +25,7 @@ namespace FreeDV {
       str << "Error on ALSA audio output " << parameters << ": ";
        if ( message )
          str << message << ": ";
-       str << snd_strerror(error);
+       str << snd_strerror(error) << '.';
       throw std::runtime_error(str.str().c_str());
     }
   public:
@@ -50,33 +50,17 @@ namespace FreeDV {
     const snd_pcm_format_t     format = SND_PCM_FORMAT_S16;
     const snd_pcm_access_t     access = SND_PCM_ACCESS_RW_INTERLEAVED;
     const unsigned int         channels = 1;
-    const unsigned int         rate = 48000;
-    const int                  soft_resample = 0;
-    const unsigned int         latency = 10000;
-    int                                error;
-
-    handle = 0;
-    error = snd_pcm_open(
-     &handle,
+
+    handle = ALSASetup(
      p,
      SND_PCM_STREAM_PLAYBACK,
-     0);
-
-    if ( error != 0 )
-      do_throw(error, "Open");
-
-    if ( (error = snd_pcm_set_params(
-     handle,
+     0,
      format,
      access,
      channels,
-     rate,
-     soft_resample,
-     latency)) < 0 )
-      do_throw(error, "Set Parameters");
-
-    if ( (error = snd_pcm_prepare(handle)) < 0 )
-      do_throw(error, "Prepare");
+     SampleRate,
+     SampleRate / 100,
+     SampleRate / 10);
   }
 
   AudioOutALSA::~AudioOutALSA()
@@ -90,18 +74,17 @@ namespace FreeDV {
   std::size_t
   AudioOutALSA::write16(const std::int16_t * array, std::size_t length)
   {
-    int result = snd_pcm_writei(handle, array, length);
-    if ( result == -EPIPE ) {
-      snd_pcm_recover(handle, result, 1);
-      result = snd_pcm_writei(handle, array, length);
+    int error = snd_pcm_writei(handle, array, length);
+    if ( error == -EPIPE ) {
       std::cerr << "ALSA output " << parameters << ": write underrun." << std::endl;
-      if ( result == -EPIPE )
-        return 0;
+      snd_pcm_recover(handle, error, 1);
+      error = snd_pcm_writei(handle, array, length);
     }
-    if ( result >= 0 )
-      return result;
+
+    if ( error >= 0 )
+      return error;
     else {
-      do_throw(result, "Write");
+      do_throw(error, "Write");
       return 0; // do_throw doesn't return.
     }
   }
@@ -126,17 +109,24 @@ namespace FreeDV {
     int                        error;
 
     error = snd_pcm_avail_delay(handle, &available, &delay);
+    if ( error == -EIO )
+      return 10;
+
     if ( error == -EPIPE ) {
+      std::cerr << "ALSA output " << parameters << ": ready underrun." << std::endl;
       snd_pcm_recover(handle, error, 1);
-      available = snd_pcm_avail_delay(handle, &available, &delay);
-      std::cerr << "ALSA output " << parameters << ": write underrun." << std::endl;
+      snd_pcm_pause(handle, 1);
+      return 10;
+      if ( error < 0 )
+        return 0;
     }
+
     if ( error == 0 )
       return available;
     else if ( error == -EPIPE )
       return 0;
     else {
-      do_throw(available, "Get Frames Available for Write");
+      do_throw(error, "Get Frames Available for Write");
       return 0; // do_throw doesn't return.
     }
   }
index 4bb7c46d869e1d3028a7f8a64dce3093c8e73090..c023e34a3e8f51c34700559f12d217f380ac5efc 100644 (file)
@@ -5,6 +5,7 @@
 #include <iostream>
 #include <fstream>
 #include <string.h>
+#include <sys/time.h>
 
 /// FIX:
 ///
@@ -33,11 +34,14 @@ namespace FreeDV {
     FIFO               out_fifo;
     bool               ptt_digital;
     bool               ptt_ssb;
+    std::size_t                min_frame_duration;
+    struct timespec    last_frame_time;
+    struct timespec    next_frame_time;
  
     void               key_down();
     void               key_up();
     void               receive();
-    void               reset_fifos();
+    void               reset();
     void               transmit_digital();
     void               transmit_ssb();
   public:
@@ -52,6 +56,7 @@ namespace FreeDV {
     codec_fifo(TempSize * 2), in_fifo(TempSize * 2),
     out_fifo(TempSize * 2), ptt_digital(false), ptt_ssb(false)
   {
+    reset();
   }
 
   Run::~Run()
@@ -59,8 +64,9 @@ namespace FreeDV {
   }
 
   void
-  Run::reset_fifos()
+  Run::reset()
   {
+    clock_gettime(CLOCK_MONOTONIC, &last_frame_time);
     in_fifo.reset();
     codec_fifo.reset();
     out_fifo.reset();
@@ -94,6 +100,9 @@ namespace FreeDV {
   void
   Run::receive()
   {
+    struct timespec    start_time;
+    clock_gettime(CLOCK_MONOTONIC, &start_time);
+
     // Drain any data that the loudspeaker can take.
     const std::size_t  out_samples = min(
                         i->loudspeaker->ready(),
@@ -161,16 +170,42 @@ namespace FreeDV {
       if ( bytes_to_decode > 0 )
         codec_fifo.get_done(bytes_to_decode);
 
-      if ( result > 0 )
+      // TODO: We can do clock tuning here to maximize power saving, if it
+      // results in any noticable gain.
+      if ( result > 0 ) {
         out_fifo.put_done(result * 2);
+
+        last_frame_time = start_time;
+        const long duration = ((min_frame_duration - 1) * 1000000);
+
+        next_frame_time.tv_sec = last_frame_time.tv_sec;
+        next_frame_time.tv_nsec = last_frame_time.tv_nsec + duration;
+        next_frame_time.tv_sec += next_frame_time.tv_nsec / 1000000000;
+        next_frame_time.tv_nsec %= 1000000000;
+      }
+      else {
+       // Add a microsecond. We really could poll the I/O interfaces instead.
+        next_frame_time.tv_nsec += 1000000;
+        next_frame_time.tv_sec += next_frame_time.tv_nsec / 1000000000;
+        next_frame_time.tv_nsec %= 1000000000;
+      }
     }
   }
   
   void
   Run::run()
   {
+    // The no-op codec, modem, and framer may have very small durations.
+    // So we set a minimum here.
+    min_frame_duration = 1;
+    min_frame_duration = max(min_frame_duration, i->modem->min_frame_duration());
+    min_frame_duration = max(min_frame_duration, i->codec->min_frame_duration());
+    min_frame_duration = max(min_frame_duration, i->framer->min_frame_duration());
+
+    assert(min_frame_duration < 1000000);
+
     while ( true ) {
-      usleep(1000);
+      // clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next_frame_time, 0);
       receive();
     }
   }