When draining the digital transmission before key-up, zero-fill the last frame
authorbruceperens <bruceperens@01035d8c-6547-0410-b346-abe4f91aad63>
Fri, 25 Apr 2014 19:42:42 +0000 (19:42 +0000)
committerbruceperens <bruceperens@01035d8c-6547-0410-b346-abe4f91aad63>
Fri, 25 Apr 2014 19:42:42 +0000 (19:42 +0000)
so that a full codec and modem frame are encoded, even though there are not
enough audio samples to fill them.

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

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

index f898b82a99ecb8e5aa718700fd508ff5a4a898fc..cdb3a81fd4b3b47d89821a178002c219e8c5e26f 100644 (file)
@@ -51,10 +51,10 @@ namespace FreeDV {
                         std::size_t data_length,
                         std::size_t *sample_length);
 
-    /// Return the minimum duration of a frame in milliseconds.
-    /// \return The minimum duration of a frame in milliseconds.
-    virtual int
-                       min_frame_duration() const;
+    /// \return The number of samples required to encode a frame in the
+    /// currently-selected mode.
+    virtual std::size_t
+    samples_per_frame() const;
   };
 
   CodecNoOp::CodecNoOp(const char * _parameters)
@@ -96,14 +96,14 @@ namespace FreeDV {
       return 0;
     }
     memcpy(o, i, length * 2);
-    *sample_length = length / 2;
-    return length;
+    *sample_length = length;
+    return length * 2;
   }
 
-  int
-  CodecNoOp::min_frame_duration() const
+  std::size_t
+  CodecNoOp::samples_per_frame() const
   {
-    return FrameDuration;
+    return FrameSamples;
   }
 
   Codec *
index 020279b9e57122147eb7c9604d117a72455e17c4..4dd5222db9dd51da94714dd45aec26060f822fa0 100644 (file)
@@ -425,10 +425,10 @@ public:
      std::size_t data_length,
      std::size_t * sample_length) = 0;
 
-    /// Return the minimum duration of a frame in milliseconds.
-    /// \return The minimum duration of a frame in milliseconds.
-    virtual int
-    min_frame_duration() const = 0;
+    /// \return The number of samples required to encode a frame in the
+    /// currently-selected mode.
+    virtual std::size_t
+    samples_per_frame() const = 0;
 };
 
 /// Virtual base class for protocol framers.
@@ -556,10 +556,10 @@ public:
      std::size_t * data_length,
      std::size_t sample_length) = 0;
 
-    /// Return the minimum duration of a frame in milliseconds.
-    /// \return The minimum duration of a frame in milliseconds.
-    virtual int
-    min_frame_duration() const = 0;
+    /// \return The number of bytes required to encode a frame in the current
+    /// mode.
+    virtual std::size_t
+    bytes_per_frame() const = 0;
 };
 
 /// Push-to-talk input driver.
index f1580a6eccf0cf329f4c14aace97fa94ab4aa40c..79af2139d99cf4912d9f24cf2c84dc4a39c604b5 100644 (file)
@@ -54,11 +54,6 @@ namespace FreeDV {
                         std::int16_t * o, \
                         std::size_t *data_length,
                         std::size_t sample_length);
-
-    /// Return the minimum duration of a frame in milliseconds.
-    /// \return The minimum duration of a frame in milliseconds.
-    virtual int
-                       min_frame_duration() const;
   };
 
   ModemNoOp::ModemNoOp(const char * _parameters)
@@ -104,12 +99,6 @@ namespace FreeDV {
     return length;
   }
 
-  int
-  ModemNoOp::min_frame_duration() const
-  {
-    return 1;
-  }
-
   Modem *
   Driver::ModemNoOp(const char * parameter)
   {
index b675f9e2add11274dd4baf8de5be53d93e60341b..3888ce7075e43aa151e098b1bfb334413679be4c 100644 (file)
@@ -128,19 +128,26 @@ namespace FreeDV {
   bool
   Run::drain_digital(bool final)
   {
-    if ( final ) {
-      if ( in_fifo.get_available() == 0
-       &&  codec_fifo.get_available() == 0
-       &&  out_fifo.get_available() == 0 ) {
-        i->transmitter->drain();
-        std::cerr << "Drain digital returning TRUE." << std::endl;
-        return true;
-      }
-    }
 
     std::size_t                samples_to_encode = in_fifo.get_available() / 2;
     const std::size_t  bytes_to_encode = codec_fifo.put_space();
 
+    // If draining the last frame, make sure we have enough samples to encode
+    // the last codec frame. Fill with zero if necessary.
+    // FIX: Replace this with a state before un-key which fades out the
+    // microphone, ending at a codec frame boundary, thus avoiding the click
+    // we make here.
+    if ( final ) {
+      const std::size_t samples_per_frame = i->codec->samples_per_frame();
+      if ( samples_to_encode < samples_per_frame && samples_to_encode > 0 ) {
+        const std::size_t fill = samples_per_frame - samples_to_encode;
+        const std::size_t bytes = fill * 2;
+        memset(in_fifo.put(bytes), 0, bytes);
+        in_fifo.put_done(bytes);
+        samples_to_encode += fill;
+      }
+    }
+
     if ( samples_to_encode > 0 && bytes_to_encode > 0 ) {
       const std::size_t bytes_encoded = i->codec->encode16(
                                  (std::int16_t *)in_fifo.get(
@@ -154,16 +161,27 @@ namespace FreeDV {
         if ( bytes_encoded > 0 )
           codec_fifo.put_done(bytes_encoded);
       }
-      else if ( final && samples_to_encode == 0 ) {
-        // The remainder of the samples in the codec queue are insufficient to
-        // fill a codec frame.
-        return true;
-      }
     }
 
     std::size_t                bytes_to_modulate = codec_fifo.get_available();
     const std::size_t  samples_to_modulate = out_fifo.put_space() / 2;
 
+    // If the codec is drained and we are draining the last modem frame,
+    // make sure we have enough bytes to encode the last modem frame.
+    // Fill with zero if necessary.
+    if ( final && in_fifo.get_available() == 0 ) {
+      const std::size_t bytes_per_frame = i->modem->bytes_per_frame();
+      if ( bytes_to_modulate < bytes_per_frame && bytes_to_modulate > 0 ) {
+        const std::size_t fill = bytes_per_frame - bytes_to_modulate;
+        memset(codec_fifo.put(fill), 0, fill);
+        codec_fifo.put_done(fill);
+        bytes_to_modulate += fill;
+        // Leave this debugging message in place until I have a codec with
+        // a frame size less than a modem frame, and can test this block.
+        std::cerr << "Fill modem." << std::endl;
+      }
+    }
+
     if ( bytes_to_modulate > 0 && samples_to_modulate > 0 ) {
       const std::size_t samples_modulated = i->modem->modulate16(
        codec_fifo.get(bytes_to_modulate),
@@ -171,18 +189,12 @@ namespace FreeDV {
        &bytes_to_modulate,
        samples_to_modulate);
 
-
       if ( bytes_to_modulate > 0 ) {
         codec_fifo.get_done(bytes_to_modulate);
 
         if ( samples_modulated > 0 )
           out_fifo.put_done(samples_modulated * 2);
       }
-      else if ( final && bytes_to_modulate == 0 ) {
-        // The remainder of the samples in the modem queue are insufficient to
-        // fill a modem frame.
-        return true;
-      }
     }
     // Drain any data that the transmitter can take.
     const std::size_t  out_samples = min(
@@ -199,6 +211,16 @@ namespace FreeDV {
       else if ( result < 0 )
        std::cerr << "Transmitter I/O error: " << strerror(errno) << std::endl;
     }
+
+    if ( final ) {
+      if ( in_fifo.get_available() == 0
+       &&  codec_fifo.get_available() == 0
+       &&  out_fifo.get_available() == 0 ) {
+        i->transmitter->drain();
+        return true;
+      }
+    }
+
     return false;
   }