From b644345190197c3662d782c9c1b9f3ac7541643e Mon Sep 17 00:00:00 2001 From: Phil Schatzmann Date: Wed, 31 Aug 2022 19:43:14 +0200 Subject: [PATCH] opus ogg decoder --- src/AudioCodecs/CodecOpusOgg.h | 38 +++++++++++----------- src/AudioCodecs/ContainerOgg.h | 53 +++++++++++++++++++++++-------- src/AudioEffects/SoundGenerator.h | 10 +++--- src/AudioTools/AudioStreams.h | 6 +++- tests/codec/opusogg/opusogg.cpp | 17 +++------- 5 files changed, 73 insertions(+), 51 deletions(-) diff --git a/src/AudioCodecs/CodecOpusOgg.h b/src/AudioCodecs/CodecOpusOgg.h index c1dc8e28d4..16e71e7502 100644 --- a/src/AudioCodecs/CodecOpusOgg.h +++ b/src/AudioCodecs/CodecOpusOgg.h @@ -33,18 +33,18 @@ struct __attribute__((packed)) OpusOggCommentHeader { */ class OpusOggDecoder : public OggContainerDecoder { public: - OpusOggDecoder() = default; - - /// Defines the output Stream - void setOutputStream(Print &out_stream) override { - LOGD(LOG_METHOD); - dec.setOutputStream(out_stream); - p_print = &opus; - } + OpusOggDecoder() { + p_codec = &dec; // OpusAudioDecoder + }; /// Provides access to the Opus configuration OpusSettings &config() { return dec.config(); } + void begin(OpusSettings settings) { + OggContainerDecoder::begin(); + dec.begin(settings); + } + void begin() override { LOGD(LOG_METHOD); OggContainerDecoder::begin(); @@ -60,7 +60,7 @@ class OpusOggDecoder : public OggContainerDecoder { protected: OpusOggHeader header; OpusAudioDecoder dec; - EncodedAudioStream opus{(Print *)nullptr, &dec}; + //EncodedAudioStream opus{(Print *)nullptr, &dec}; virtual void beginOfSegment(ogg_packet *op) { LOGD("bos"); @@ -85,13 +85,17 @@ class OpusOggDecoder : public OggContainerDecoder { */ class OpusOggEncoder : public OggContainerEncoder { public: - OpusOggEncoder() = default; + OpusOggEncoder() { + p_codec = &enc; // OpusAudioEncoder + }; - /// Defines the output Stream - void setOutputStream(Print &out_stream) override { - LOGD(LOG_METHOD); - enc.setOutputStream(out_stream); - p_print = &opus; + /// Provides access to the configuration + OpusEncoderSettings &config() { return enc.config(); } + OpusEncoderSettings &defaultConfig() { return enc.config(); } + + void begin(OpusEncoderSettings settings) { + cfg = settings; + begin(); } void begin() override { @@ -109,14 +113,10 @@ class OpusOggEncoder : public OggContainerEncoder { /// Provides "audio/opus" const char *mime() override { return "audio/opus"; } - /// Provides access to the Opus configuration - OpusEncoderSettings &config() { return enc.config(); } - protected: OpusOggHeader header; OpusOggCommentHeader comment; OpusAudioEncoder enc; - EncodedAudioStream opus{(Print *)nullptr, &enc}; ogg_packet oh1; bool writeHeader() override { diff --git a/src/AudioCodecs/ContainerOgg.h b/src/AudioCodecs/ContainerOgg.h index f1d932f580..fcd52fe97a 100644 --- a/src/AudioCodecs/ContainerOgg.h +++ b/src/AudioCodecs/ContainerOgg.h @@ -219,8 +219,9 @@ class OggContainerEncoder : public AudioEncoder { p_print = &out_stream; } else { EncodedAudioStream* eas = new EncodedAudioStream(); - eas->begin(&out_stream, p_codec); - p_print = eas; + eas->begin(&codec_buffer, p_codec); + p_encoded_audio_stream = eas; + p_print = &out_stream; } } @@ -239,6 +240,7 @@ class OggContainerEncoder : public AudioEncoder { virtual void begin() override { LOGD(LOG_METHOD); is_open = true; + codec_buffer.begin(); if (p_oggz == nullptr) { p_oggz = oggz_new(OGGZ_WRITE | OGGZ_NONSTRICT | OGGZ_AUTO); serialno = oggz_serialno_new(p_oggz); @@ -269,22 +271,39 @@ class OggContainerEncoder : public AudioEncoder { p_oggz = nullptr; } - /// Writes Ogg Packet + /// Writes raw data to be encoded and packaged virtual size_t write(const void *in_ptr, size_t in_size) override { if (!is_open || p_print == nullptr) return 0; LOGD("write: %d", (int) in_size); - op.packet = (uint8_t *)in_ptr; - op.bytes = in_size; - op.granulepos = granulepos += - in_size / sizeof(int16_t) / cfg.channels; // sample - op.b_o_s = false; - op.e_o_s = false; - op.packetno = packetno++; - if (!writePacket(op, OGGZ_FLUSH_AFTER)) { - return 0; - } + if (p_codec!=nullptr){ + // encode the data + size_t eff = p_encoded_audio_stream->write((uint8_t*)in_ptr, in_size); + if (eff!=in_size){ + LOGE("Write overflow"); + } + // get the result from the buffer + void *encoded_data = buffer.address(); + int enoded_size = buffer.available(); + op.packet = (uint8_t *)encoded_data; + op.bytes = enoded_size; + } else { + op.packet = (uint8_t *)in_ptr; + op.bytes = in_size; + } + if (op.bytes>0){ + buffer.reset(); + op.granulepos = granulepos += + in_size / sizeof(int16_t) / cfg.channels; // sample + op.b_o_s = false; + op.e_o_s = false; + op.packetno = packetno++; + is_audio = true; + if (!writePacket(op, OGGZ_FLUSH_AFTER)) { + return 0; + } + } // trigger pysical write while ((oggz_write(p_oggz, in_size)) > 0) ; @@ -297,8 +316,11 @@ class OggContainerEncoder : public AudioEncoder { bool isOpen() { return is_open; } protected: - AudioEncoder* p_codec = nullptr; Print *p_print = nullptr; + Print *p_encoded_audio_stream = nullptr; + SingleBuffer buffer{1024}; + CallbackBufferedStream codec_buffer{buffer}; + AudioEncoder* p_codec = nullptr; volatile bool is_open; OGGZ *p_oggz = nullptr; ogg_packet op; @@ -307,6 +329,7 @@ class OggContainerEncoder : public AudioEncoder { size_t packetno = 0; long serialno = -1; AudioBaseInfo cfg; + bool is_audio = false; virtual bool writePacket(ogg_packet &op, int flag = 0) { LOGD("writePacket: %d", (int) op.bytes); @@ -326,6 +349,7 @@ class OggContainerEncoder : public AudioEncoder { oh.packetno = packetno++; oh.b_o_s = true; oh.e_o_s = false; + is_audio = false; return writePacket(oh); } @@ -337,6 +361,7 @@ class OggContainerEncoder : public AudioEncoder { op.packetno = packetno++; op.b_o_s = false; op.e_o_s = true; + is_audio = false; return writePacket(op, OGGZ_FLUSH_AFTER); } diff --git a/src/AudioEffects/SoundGenerator.h b/src/AudioEffects/SoundGenerator.h index 68157880b0..8d86c88245 100644 --- a/src/AudioEffects/SoundGenerator.h +++ b/src/AudioEffects/SoundGenerator.h @@ -293,7 +293,7 @@ class NoiseGenerator : public SoundGenerator { /** - * @brief Provides 0 as sound data. This can be used e.g. to test the output functionality which should optimally just output + * @brief Provides a fixed value (e.g. 0) as sound data. This can be used e.g. to test the output functionality which should optimally just output * silence and no artifacts. * @author Phil Schatzmann * @copyright GPLv3 @@ -303,17 +303,17 @@ template class SilenceGenerator : public SoundGenerator { public: // the scale defines the max value which is generated - SilenceGenerator(double scale=1.0) { - this->scale = scale; + SilenceGenerator(T value=0) { + this->value = value; } /// Provides a single sample T readSample() { - return 0; // return 0 + return value; // return 0 } protected: - double scale; + T value; }; diff --git a/src/AudioTools/AudioStreams.h b/src/AudioTools/AudioStreams.h index d1176811ec..ebb88cae85 100644 --- a/src/AudioTools/AudioStreams.h +++ b/src/AudioTools/AudioStreams.h @@ -772,6 +772,10 @@ class CallbackBufferedStream : public AudioStreamX { remove_oldest_data = autoRemoveOldestDataIfFull; } + CallbackBufferedStream(BaseBuffer &buffer){ + callback_buffer_ptr = &buffer; + } + virtual ~CallbackBufferedStream() { delete callback_buffer_ptr; } /// Activates the output @@ -828,7 +832,7 @@ class CallbackBufferedStream : public AudioStreamX { } protected: - NBuffer *callback_buffer_ptr; + BaseBuffer *callback_buffer_ptr; bool active; bool remove_oldest_data; diff --git a/tests/codec/opusogg/opusogg.cpp b/tests/codec/opusogg/opusogg.cpp index 22676eb12d..96dd577cb9 100644 --- a/tests/codec/opusogg/opusogg.cpp +++ b/tests/codec/opusogg/opusogg.cpp @@ -12,7 +12,7 @@ #include "AudioCodecs/CodecOpusOgg.h" int sample_rate = 24000; -int channels = 2; // The stream will have 2 channels +int channels = 1; // The stream will have 2 channels int application = OPUS_APPLICATION_AUDIO; // Opus application SineWaveGenerator sineWave( 32000); // subclass of SoundGenerator with max amplitude of 32000 @@ -21,22 +21,15 @@ CsvStream out(Serial, channels); // Output of sound on desktop OpusOggEncoder enc; OpusOggDecoder dec; EncodedAudioStream decoder(out, dec); // encode and write -HexDumpStream hex(Serial); -EncodedAudioStream encoder(&hex, &enc); // encode and write +EncodedAudioStream encoder(&decoder, &enc); // encode and write //EncodedAudioStream encoder(&decoder, &enc); // encode and write StreamCopy copier(encoder, sound); void setup() { Serial.begin(115200); - AudioLogger::instance().begin(Serial, AudioLogger::Debug); - - // start I2S - // Serial.println("starting I2S..."); - // auto cfgi = out.defaultConfig(TX_MODE); - // cfgi.sample_rate = sample_rate; - // cfgi.channels = channels; - // cfgi.bits_per_sample = 16; - // out.begin(cfgi); + AudioLogger::instance().begin(Serial, AudioLogger::Warning); + + // Setup sine wave auto cfgs = sineWave.defaultConfig();