Add modem_codec2_reverse for testing.
authorbruceperens <bruceperens@01035d8c-6547-0410-b346-abe4f91aad63>
Fri, 30 May 2014 03:00:41 +0000 (03:00 +0000)
committerbruceperens <bruceperens@01035d8c-6547-0410-b346-abe4f91aad63>
Fri, 30 May 2014 03:00:41 +0000 (03:00 +0000)
git-svn-id: https://svn.code.sf.net/p/freetel/code@1613 01035d8c-6547-0410-b346-abe4f91aad63

freedv-server/source/modem_codec2_reverse.cpp [new file with mode: 0644]

diff --git a/freedv-server/source/modem_codec2_reverse.cpp b/freedv-server/source/modem_codec2_reverse.cpp
new file mode 100644 (file)
index 0000000..b56dad8
--- /dev/null
@@ -0,0 +1,194 @@
+/// \file modem_codec2_reverse.cpp
+/// This "modem" is actually codec2 running in reverse, for testing the codec.
+///
+/// If you run codec2 as the codec and this as the modem, you should be able
+/// to pipe audio all of the way through the system. Thus, you'd be using the
+/// loudspeaker output as the transmitter and the microphone input as the
+/// receiver (along with their usual configuration as the microphone and
+/// loudspeaker). 
+///
+/// \copyright Copyright (C) 2013-2014 Algoram. See the LICENSE file.
+///
+
+#include "drivers.h"
+#include <stdexcept>
+#include <stdlib.h>
+extern "C" {
+  #include <codec2.h>
+}
+
+namespace FreeDV {
+  /// Codec2.
+  class Codec2_Reverse : public Modem {
+  private:
+    // Override copy constructor and operator = ().
+                       Codec2_Reverse(const Codec2_Reverse &);
+    Codec2_Reverse &   operator = (const Codec2_Reverse &);
+
+    struct CODEC2 *    c;
+    std::size_t                samples_per;
+    std::size_t                bytes_per;
+
+  public:
+
+    /// Instantiate the Reverse Codec2 modem.
+                       Codec2_Reverse(const char *);
+                       ~Codec2_Reverse();
+
+    /// Decode from data bytes to audio samples.
+    /// \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 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
+                       demodulate16(
+                        const std::int16_t *   i,
+                        std::uint8_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
+    /// of signed 16-bit integers.
+    /// \param o The encoded data, in an array of unsigned 8-bit integers.
+    /// \param data_length The number of 8-bit data to be encoded.
+    /// \param sample_length On call: The number of 16-bit audio samples to
+    /// be encoded. On return: the number of samples that were consumed.
+    /// \return The number of 8-bit data elements in the encoded array.
+    virtual std::size_t
+                       modulate16(
+                        const std::uint8_t *   i,
+                        std::int16_t *         o, \
+                        std::size_t *          data_length,
+                        std::size_t            sample_length);
+
+    /// \return The number of bytes required to encode a frame in the
+    /// currently-selected mode.
+    virtual std::size_t        bytes_per_frame() const;
+  };
+
+  Codec2_Reverse::Codec2_Reverse(const char * _parameters)
+  : Modem("codec2-reverse", _parameters), c(0), samples_per(0), bytes_per(0)
+  {
+    int        mode = CODEC2_MODE_1600;
+
+    switch ( atoi(parameters) ) {
+    case 0:
+    default:
+      throw std::runtime_error(
+       "codec2: must specify rate," \
+       " valid values are 1200,1300,1400,1600,2400.");
+       break; // NOTREACHED
+    case 1200:
+      mode = CODEC2_MODE_1200;
+      break;
+    case 1300:
+      mode = CODEC2_MODE_1300;
+      break;
+    case 1400:
+      mode = CODEC2_MODE_1400;
+      break;
+    case 1600:
+      mode = CODEC2_MODE_1600;
+      break;
+    case 2400:
+      mode = CODEC2_MODE_2400;
+      break;
+    }
+    c = codec2_create(mode);
+
+    if ( c == 0 )
+      throw std::runtime_error("codec2: failed to create.");
+
+    samples_per = codec2_samples_per_frame(c);
+    bytes_per = (codec2_bits_per_frame(c) + 7) / 8;
+  }
+
+  Codec2_Reverse::~Codec2_Reverse()
+  {
+    codec2_destroy(c);
+  }
+
+  std::size_t
+  Codec2_Reverse::demodulate16(
+   const std::int16_t *        i,
+   std::uint8_t *      o,
+   std::size_t         data_length,
+   std::size_t *       sample_length)
+  {
+    std::size_t        bytes_read = 0;
+    std::size_t        samples_read = 0;
+
+    while ( data_length >= bytes_per && *sample_length >= samples_per ) {
+      // FIX: Const cast away. Remove if we can get const correctness in
+      // libcodec2.
+      codec2_encode(c, o, (std::int16_t *)i);
+
+      o += bytes_per;
+      bytes_read += bytes_per;
+      data_length -= bytes_per;
+
+      i += samples_per;
+      samples_read += samples_per;
+      *sample_length -= samples_per;
+    }
+    *sample_length = samples_read;
+    return bytes_read;
+  }
+
+  std::size_t
+  Codec2_Reverse::modulate16(
+   const std::uint8_t *        i,
+   std::int16_t *      o,
+   std::size_t *       data_length,
+   std::size_t         sample_length)
+  {
+    std::size_t        bytes_read = 0;
+    std::size_t        samples_read = 0;
+
+    while ( *data_length >= bytes_per && sample_length >= samples_per ) {
+      codec2_decode(c, o, i);
+
+      i += bytes_per;
+      bytes_read += bytes_per;
+      *data_length -= bytes_per;
+
+      o += samples_per;
+      samples_read += samples_per;
+      sample_length -= samples_per;
+    }
+    *data_length = bytes_read;
+    return samples_read;
+  }
+
+  std::size_t
+  Codec2_Reverse::bytes_per_frame() const
+  {
+    return bytes_per;
+  }
+
+  Modem *
+  Driver::Codec2_Reverse(const char * parameter)
+  {
+    return new ::FreeDV::Codec2_Reverse(parameter);
+  }
+
+  static std::ostream &
+  Codec2_Reverse_Enumerator(std::ostream & stream)
+  {
+    stream << "\"codec2:1600\" (1200,1300,1400,1600,2400,3200)" << std::endl;
+    return stream;
+  }
+
+  static bool
+  initializer()
+  {
+    driver_manager()->register_modem("codec2-reverse", Driver::Codec2_Reverse, Codec2_Reverse_Enumerator);
+    return true;
+  }
+  static const bool UNUSED initialized = initializer();
+}