From e0df94f2e3f5d377ceaaa59ca4b43ddd3f7fa592 Mon Sep 17 00:00:00 2001 From: bruceperens Date: Thu, 6 Mar 2014 05:00:12 +0000 Subject: [PATCH] Add more fifos. Make the codec and modem indicate the amount consumed, as well 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 | 18 +++-- freedv-server/source/drivers.h | 49 +++++++----- freedv-server/source/fifo.cpp | 17 +++-- freedv-server/source/modem_noop.cpp | 21 +++-- freedv-server/source/run.cpp | 114 +++++++++++++++++++++++++--- freedv-server/source/test/fifo.cpp | 25 ++++-- 6 files changed, 190 insertions(+), 54 deletions(-) diff --git a/freedv-server/source/codec_noop.cpp b/freedv-server/source/codec_noop.cpp index b4fa9dc7..da8f9b31 100644 --- a/freedv-server/source/codec_noop.cpp +++ b/freedv-server/source/codec_noop.cpp @@ -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 diff --git a/freedv-server/source/drivers.h b/freedv-server/source/drivers.h index ca672972..597d4f39 100644 --- a/freedv-server/source/drivers.h +++ b/freedv-server/source/drivers.h @@ -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. diff --git a/freedv-server/source/fifo.cpp b/freedv-server/source/fifo.cpp index b4df8ea0..4edc893d 100644 --- a/freedv-server/source/fifo.cpp +++ b/freedv-server/source/fifo.cpp @@ -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; } } diff --git a/freedv-server/source/modem_noop.cpp b/freedv-server/source/modem_noop.cpp index 5d1a62c1..7a898755 100644 --- a/freedv-server/source/modem_noop.cpp +++ b/freedv-server/source/modem_noop.cpp @@ -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 diff --git a/freedv-server/source/run.cpp b/freedv-server/source/run.cpp index b3838eae..b8974241 100644 --- a/freedv-server/source/run.cpp +++ b/freedv-server/source/run.cpp @@ -3,6 +3,7 @@ #include "drivers.h" #include #include +#include /// FIX: /// @@ -17,24 +18,25 @@ /// 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 diff --git a/freedv-server/source/test/fifo.cpp b/freedv-server/source/test/fifo.cpp index da7a6ae1..42794ad6 100644 --- a/freedv-server/source/test/fifo.cpp +++ b/freedv-server/source/test/fifo.cpp @@ -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()); +} -- 2.25.1