/// \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
}
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
/// 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.
/// 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);
/// \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;
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
/// \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.
/// \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.
/// \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.
/// \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.
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()
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;
}
void
- FIFO::out_overrun(std::size_t size) const
+ FIFO::reset()
{
- throw std::runtime_error("FIFO outgoing data overrun.");
+ in = buffer;
+ out = buffer;
}
}
/// \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.
}
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
#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:
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)
{
}
{
}
+ void
+ Run::reset_fifos()
+ {
+ input_fifo.reset();
+ codec_fifo.reset();
+ output_fifo.reset();
+ }
+
int
Run::run()
{
}
}
if ( begin_transmit ) {
+ reset_fifos();
key_down();
}
else if ( begin_receive ) {
+ reset_fifos();
key_up();
}
else if ( ptt_digital ) {
}
void
- Run::key_down()
- {
+ Run::key_down() {
if ( i->keying_output->ready() ) {
i->keying_output->key(true);
begin_transmit = false;
}
}
+ // 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
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());
}
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());
+}