Add more fifos. Make the codec and modem indicate the amount consumed, as well
authorbruceperens <bruceperens@01035d8c-6547-0410-b346-abe4f91aad63>
Thu, 6 Mar 2014 05:00:12 +0000 (05:00 +0000)
committerbruceperens <bruceperens@01035d8c-6547-0410-b346-abe4f91aad63>
Thu, 6 Mar 2014 05:00:12 +0000 (05:00 +0000)
as the amount output.

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

freedv-server/source/codec_noop.cpp
freedv-server/source/drivers.h
freedv-server/source/fifo.cpp
freedv-server/source/modem_noop.cpp
freedv-server/source/run.cpp
freedv-server/source/test/fifo.cpp

index b4fa9dc728ad5a1c1f367042a3bf1b3b9df10fbd..da8f9b3147de09b68f92c7972134385b030601b7 100644 (file)
@@ -22,12 +22,16 @@ namespace FreeDV {
     /// \param i The encoded data, in an array of unsigned 8-bit integers.
     /// \param o The array of audio samples after decoding, in an array
     /// of signed 16-bit integers.
-    /// \param length The number of bytes of data to be decoded.
-    /// \return The number of std::int16_t elements in the decoded array.
+    /// \param data_length When called: The number of bytes of data that are
+    /// available to be decoded. On return: the number of bytes of data
+    /// that were consumed.
+    /// \param sample_length The number of audio samples that may be decoded.
+    /// \return The number of audio samples that were actually decoded.
     virtual std::size_t
-                       decode16(const std::uint8_t * i, std::int16_t * o, \
-                        std::size_t length);
-
+                       decode16(const std::uint8_t * i,
+                        std::int16_t * o,
+                        std::size_t * data_length,
+                        std::size_t sample_length);
 
     /// Encode from audio samples to data bytes.
     /// \param i The array of audio samples to be encoded, in an array
@@ -70,9 +74,9 @@ namespace FreeDV {
   }
 
   std::size_t
-  CodecNoOp::decode16(const std::uint8_t * i, std::int16_t * o, std::size_t length)
+  CodecNoOp::decode16(const std::uint8_t * i, std::int16_t * o, std::size_t * data_length, std::size_t sample_length)
   {
-    return length;
+    return sample_length;
   }
 
   std::size_t
index ca672972085e291238a4ae202fe22b389f6074ea..597d4f390bbb2aab4ee3d3cd6b08235a64b2de05 100644 (file)
@@ -26,13 +26,13 @@ namespace FreeDV {
   /// size of the embedded version of this program. 
   class FIFO {
   private:
-    char * const       buffer;
-    const char * const buffer_end;
-    char *             in;
-    const char *       out;
+    uint8_t * const            buffer;
+    const uint8_t * const      buffer_end;
+    uint8_t *                  in;
+    const uint8_t *            out;
 
     void               out_overrun(std::size_t length) const;
-    char *             reorder(std::size_t length);
+    uint8_t *          reorder(std::size_t length);
 
   public:
     /// Create the FIFO object.
@@ -56,8 +56,8 @@ namespace FreeDV {
     /// to the length passed to incoming_buffer().
     /// \param io_length The size of buffer in chars requested.
     /// \return The address of the buffer for incoming data.
-    inline char *      incoming_buffer(std::size_t io_length) {
-                         const char * io_end = in + io_length;
+    inline uint8_t *   incoming_buffer(std::size_t io_length) {
+                         const uint8_t * io_end = in + io_length;
 
                          if ( io_end > buffer_end )
                             return reorder(io_length);
@@ -82,7 +82,7 @@ namespace FreeDV {
     /// \param length The amount of data requested. This must be smaller
     /// than or equal to the amount returned by outgoing_available().
     /// \return The address of the data to be read.
-    inline const char *        outgoing_buffer(std::size_t length) {
+    inline const uint8_t *     outgoing_buffer(std::size_t length) {
                          if ( length > in - out )
                            out_overrun(length);
                          return out;
@@ -95,6 +95,9 @@ namespace FreeDV {
     inline void                outgoing_done(std::size_t length) {
                          out += length;
                        }
+
+    /// Discard any buffered data.
+    void               reset();
   };
 
   /// Set the real-time parameters in the scheduler before running our main
@@ -280,11 +283,16 @@ namespace FreeDV {
     /// \param i The encoded data, in an array of unsigned 8-bit integers.
     /// \param o The array of audio samples after decoding, in an array
     /// of signed 16-bit integers.
-    /// \param length The number of bytes of data to be decoded.
-    /// \return The number of std::int16_t elements in the decoded array.
+    /// \param data_length When called: The number of bytes of data that are
+    /// available to be decoded. On return: the number of bytes of data
+    /// that were consumed.
+    /// \param sample_length The number of audio samples that may be decoded.
+    /// \return The number of audio samples that were actually decoded.
     virtual std::size_t
-                       decode16(const std::uint8_t * i, std::int16_t * o, \
-                        std::size_t length) = 0;
+                       decode16(const std::uint8_t * i,
+                        std::int16_t * o,
+                        std::size_t * data_length,
+                        std::size_t sample_length) = 0;
 
 
     /// Encode from audio samples to data bytes.
@@ -294,7 +302,7 @@ namespace FreeDV {
     /// \param length The number of audio samples to be encoded.
     /// \return The number of std::uint8_t elements in the encoded array.
     virtual std::size_t
-                       encode16(const std::int16_t * i, std::uint8_t * o, \
+                       encode16(const std::int16_t * i, std::uint8_t * o,
                         std::size_t length) = 0;
 
     /// Return the duration of a frame in milliseconds.
@@ -441,11 +449,16 @@ namespace FreeDV {
     /// \param i The array of audio samples to be demodulated, in an array
     /// of signed 16-bit integers.
     /// \param o The demodulated data, in an array of unsigned 8-bit integers.
-    /// \param length The number of audio samples to be demodulated.
-    /// \return The number of std::uint8_t elements in the demodulated array.
+    /// \param sample_length On call: The number of audio samples to be
+    /// demodulated. On return: The number of audio samples consumed.
+    /// \param data_length The number of bytes of data that may be demodulated.
+    /// \return The number of bytes of data that were actually decoded.
     virtual std::size_t
-                       demodulate16(const std::int16_t * i, std::uint8_t * o, \
-                        std::size_t length) = 0;
+                       demodulate16(
+                        const std::int16_t * i,
+                        std::uint8_t * o,
+                        std::size_t * sample_length,
+                        std::size_t data_length) = 0;
 
     /// Modulate from data to audio samples.
     /// \param i The data, in an array of unsigned 8-bit integers.
@@ -454,7 +467,7 @@ namespace FreeDV {
     /// \param length The number of bytes of data to be modulated.
     /// \return The number of std::int16_t elements in the modulated array.
     virtual std::size_t
-                       modulate16(const std::uint8_t * i, std::int16_t * o, \
+                       modulate16(const std::uint8_t * i, std::int16_t * o,
                         std::size_t length) = 0;
 
     /// Return the duration of a frame in milliseconds.
index b4df8ea05f9d6c56d6675e563d6acf6534193a07..4edc893ddc66816dd8c098cd6851cae2928ef496 100644 (file)
@@ -4,9 +4,9 @@
 
 namespace FreeDV {
   FIFO::FIFO(std::size_t size)
-  : buffer(new char[size]), buffer_end(buffer + size), in(buffer), out(buffer)
+  : buffer(new uint8_t[size]), buffer_end(buffer + size)
   {
-    
+    reset();
   }
 
   FIFO::~FIFO()
@@ -14,7 +14,13 @@ namespace FreeDV {
     delete buffer;
   }
 
-  char *
+  void
+  FIFO::out_overrun(std::size_t size) const
+  {
+    throw std::runtime_error("FIFO outgoing data overrun.");
+  }
+
+  uint8_t *
   FIFO::reorder(std::size_t size)
   {
     const std::size_t bytes = in - out;
@@ -37,8 +43,9 @@ namespace FreeDV {
   }
 
   void
-  FIFO::out_overrun(std::size_t size) const
+  FIFO::reset()
   {
-    throw std::runtime_error("FIFO outgoing data overrun.");
+    in = buffer;
+    out = buffer;
   }
 }
index 5d1a62c107643b49e895df7484616bd6cc20e042..7a898755634c4578e6748c37c9b3bd3ee9cb6914 100644 (file)
@@ -24,11 +24,16 @@ namespace FreeDV {
     /// \param i The array of audio samples to be demodulated, in an array
     /// of signed 16-bit integers.
     /// \param o The demodulated data, in an array of unsigned 8-bit integers.
-    /// \param length The number of audio samples to be demodulated.
-    /// \return The number of std::uint8_t elements in the demodulated array.
+    /// \param sample_length On call: The number of audio samples to be
+    /// demodulated. On return: The number of audio samples consumed.
+    /// \param data_length The number of bytes of data that may be demodulated.
+    /// \return The number of bytes of data that were actually decoded.
     virtual std::size_t
-                       demodulate16(const std::int16_t * i, std::uint8_t * o, \
-                        std::size_t length);
+                       demodulate16(
+                        const std::int16_t * i,
+                        std::uint8_t * o,
+                        std::size_t * sample_length,
+                        std::size_t data_length);
 
     /// Modulate from data to audio samples.
     /// \param i The data, in an array of unsigned 8-bit integers.
@@ -71,9 +76,13 @@ namespace FreeDV {
   }
 
   std::size_t
-  ModemNoOp::demodulate16(const std::int16_t * i, std::uint8_t * o, std::size_t length)
+  ModemNoOp::demodulate16(
+   const std::int16_t * i,
+   std::uint8_t * o,
+   std::size_t * sample_length,
+   std::size_t data_length)
   {
-    return length;
+    return data_length;
   }
 
   std::size_t
index b3838eae1dfa3a180079b59e0d18461bb156e4f8..b8974241358792176d14330df8955071edc0d4da 100644 (file)
@@ -3,6 +3,7 @@
 #include "drivers.h"
 #include <unistd.h>
 #include <iostream>
+#include <string.h>
 
 /// FIX:
 ///
 /// device queue first, allow it all to drain out the transmitter, and then
 /// un-key the transmitter.
 ///
-/// Codec to Modem connection:
-/// We need a circular buffer between the codec and the modem. It's perfectly
-/// possible for the modem to demodulate nothing, given an array of audio
-/// samples. We can't invoke the codec until we have an entire frame for it
-/// to work upon.
 
 namespace FreeDV {
+
   class Run {
   private:
-    Interfaces *       i;
+    const std::size_t  TempSize = 2048;
+    Interfaces * const i;
     bool               begin_transmit;
     bool               begin_receive;
+    FIFO               input_fifo;
+    FIFO               codec_fifo;
+    FIFO               output_fifo;
     bool               ptt_digital;
     bool               ptt_ssb;
  
     void               key_down();
     void               key_up();
     void               receive();
+    void               reset_fifos();
     void               transmit_digital();
     void               transmit_ssb();
   public:
@@ -46,7 +48,8 @@ namespace FreeDV {
   
   Run::Run(Interfaces * interfaces)
   : begin_receive(true), begin_transmit(false), i(interfaces),
-    ptt_digital(false), ptt_ssb(false)
+    ptt_digital(false), ptt_ssb(false),
+    input_fifo(TempSize * 2), codec_fifo(TempSize * 2), output_fifo(TempSize * 2)
   {
   }
 
@@ -54,6 +57,14 @@ namespace FreeDV {
   {
   }
 
+  void
+  Run::reset_fifos()
+  {
+    input_fifo.reset();
+    codec_fifo.reset();
+    output_fifo.reset();
+  }
+
   int
   Run::run()
   {
@@ -81,9 +92,11 @@ namespace FreeDV {
         }
       }
       if ( begin_transmit ) {
+        reset_fifos();
         key_down();
       }
       else if ( begin_receive ) {
+        reset_fifos();
         key_up();
       }
       else if ( ptt_digital ) {
@@ -100,8 +113,7 @@ namespace FreeDV {
   }
 
   void
-  Run::key_down()
-  {
+  Run::key_down() {
     if ( i->keying_output->ready() ) {
       i->keying_output->key(true);
       begin_transmit = false;
@@ -123,11 +135,91 @@ namespace FreeDV {
     }
   }
   
+  // FIX: Parallelize the modem and codec into their own threads. Make the
+  // FIFO do locking.
   void
   Run::receive()
   {
-    const std::size_t  samples_to_decode = i->receiver->ready()
-                        % i->modem->samples_per_frame();
+    // Drain any data that the loudspeaker can take.
+    const std::size_t  output_samples = std::min(
+                        i->loudspeaker->ready(),
+                        (output_fifo.outgoing_available() / 2));
+
+    if ( output_samples ) {
+      const std::size_t result = i->loudspeaker->write16(
+                                 (std::int16_t *)output_fifo.outgoing_buffer
+                                  (output_samples * 2),
+                                 output_samples);
+
+      if ( result > 0 )
+        output_fifo.outgoing_done(result * 2);
+      else
+       std::cerr << "Loudspeaker I/O error: " << strerror(errno) << std::endl;
+    }
+    
+    // Fill any data that the receiver can provide.
+    const std::size_t  input_samples = std::min(
+                        i->receiver->ready(),
+                        (input_fifo.incoming_available() / 2));
+
+    if ( input_samples ) {
+      const std::size_t result = i->receiver->read16(
+        (std::int16_t *)input_fifo.incoming_buffer(input_samples * 2),
+        input_samples);
+
+      if ( result > 0 )
+        output_fifo.outgoing_done(result * 2);
+      else
+       std::cerr << "Loudspeaker I/O error: " << strerror(errno) << std::endl;
+    }
+    
+    const std::size_t  frames_to_demodulate = (codec_fifo.incoming_available()
+                        / i->modem->bytes_per_frame());
+
+    if ( frames_to_demodulate && input_fifo.outgoing_available() > i->modem->bytes_per_frame()) {
+      std::size_t      samples_to_demodulate = input_fifo.outgoing_available()
+                        / 2;
+      const std::size_t        bytes_to_demodulate = frames_to_demodulate
+                        * i->modem->bytes_per_frame();
+
+      std::size_t result = i->modem->demodulate16(
+                           (const std::int16_t *)input_fifo.outgoing_buffer(
+                            samples_to_demodulate * 2),
+                           codec_fifo.incoming_buffer(bytes_to_demodulate),
+                           &samples_to_demodulate,
+                           bytes_to_demodulate);
+
+      if ( samples_to_demodulate > 0 )
+        input_fifo.outgoing_done(samples_to_demodulate * 2);
+
+      if ( result > 0 )
+        output_fifo.incoming_done(result * i->modem->bytes_per_frame());
+    }
+
+    const std::size_t  frames_to_decode = 
+                        (output_fifo.incoming_available() / 2)
+                         / i->codec->samples_per_frame();
+
+    if ( frames_to_decode > 0
+     && codec_fifo.outgoing_available() > i->codec->bytes_per_frame() ) {
+      std::size_t bytes_to_decode = codec_fifo.outgoing_available();
+
+      const std::size_t samples_to_decode = frames_to_decode \
+                                            * i->codec->samples_per_frame();
+
+      const std::size_t result = i->codec->decode16(
+                                 codec_fifo.outgoing_buffer(bytes_to_decode),
+                                 (std::int16_t *)output_fifo.incoming_buffer(
+                                   samples_to_decode * 2),
+                                 &bytes_to_decode,
+                                 samples_to_decode);
+
+      if ( bytes_to_decode > 0 )
+        codec_fifo.outgoing_done(bytes_to_decode);
+
+      if ( result > 0 )
+        output_fifo.incoming_done(result * 2);
+    }
   }
   
   void
index da7a6ae19a5c8b119afa4d56ef3f0c00f97441da..42794ad603a2c27649fdbdfb6305a5218dc20dd2 100644 (file)
@@ -32,7 +32,7 @@ TEST_F(FIFOTest, CanFillAndDrain) {
   ASSERT_EQ(0, f->incoming_available());
   EXPECT_THROW(f->incoming_buffer(1), std::runtime_error);
   ASSERT_EQ(100, f->outgoing_available());
-  ASSERT_NE((char *)0, f->outgoing_buffer(100));
+  ASSERT_NE((uint8_t *)0, f->outgoing_buffer(100));
   f->outgoing_done(100);
   ASSERT_EQ(0, f->outgoing_available());
   ASSERT_EQ(100, f->incoming_available());
@@ -45,26 +45,37 @@ TEST_F(FIFOTest, CanFillAndDrain) {
 }
 
 TEST_F(FIFOTest, Reorder) {
-  char * b;
-  const char * r;
+  uint8_t * b;
+  const uint8_t * r;
 
   ASSERT_EQ(100, f->incoming_available());
-  ASSERT_NE((char *)0, b = f->incoming_buffer(99));
+  ASSERT_NE((uint8_t *)0, b = f->incoming_buffer(99));
   memset(b, 0, 98);
   b[98] = 'b';
   f->incoming_done(99);
   ASSERT_EQ(1, f->incoming_available());
   ASSERT_EQ(99, f->outgoing_available());
-  ASSERT_NE((char *)0, f->outgoing_buffer(98));
+  ASSERT_NE((uint8_t *)0, f->outgoing_buffer(98));
   f->outgoing_done(98);
   ASSERT_EQ(1, f->outgoing_available());
   ASSERT_EQ(99, f->incoming_available());
   // This should cause reorder().
-  ASSERT_NE((char *)0, b = f->incoming_buffer(2));
+  ASSERT_NE((uint8_t *)0, b = f->incoming_buffer(2));
   f->incoming_done(0);
   ASSERT_EQ(99, f->incoming_available());
   ASSERT_EQ(1, f->outgoing_available());
-  ASSERT_NE((char *)0, r = f->outgoing_buffer(1));
+  ASSERT_NE((uint8_t *)0, r = f->outgoing_buffer(1));
   ASSERT_EQ('b', *r);
   f->outgoing_done(1);
 }
+
+TEST_F(FIFOTest, CanReset) {
+  ASSERT_EQ(100, f->incoming_available());
+  ASSERT_EQ(0, f->outgoing_available());
+  f->incoming_done(100);
+  ASSERT_EQ(100, f->outgoing_available());
+  ASSERT_EQ(0, f->incoming_available());
+  f->reset();
+  ASSERT_EQ(0, f->outgoing_available());
+  ASSERT_EQ(100, f->incoming_available());
+}