#pragma once #include "ADPCM.h" // https://github.com/pschatzmann/adpcm #include "AudioTools/AudioCodecs/AudioCodecsBase.h" namespace audio_tools { /** * @brief Decoder for ADPCM. Depends on https://github.com/pschatzmann/adpcm * @ingroup codecs * @ingroup decoder * @author Phil Schatzmann * @copyright GPLv3 */ class ADPCMDecoder : public AudioDecoderExt { public: ADPCMDecoder() = default; ADPCMDecoder(AVCodecID id, int blockSize = ADAPCM_DEFAULT_BLOCK_SIZE) { setBlockSize(blockSize); setId(id); } /// Destructor ~ADPCMDecoder() { if (p_decoder) delete p_decoder; } // (re) defines the codec id: set the block size first void setId(AVCodecID id) { codec_id = id; if (p_decoder != nullptr) { setImplementation(); } } // defines the block size (= size of encoded frame) void setBlockSize(int blockSize) override { block_size = blockSize; if (p_decoder == nullptr) return; p_decoder->setBlockSize(blockSize); } /// Provides the block size (= size of encoded frame) (only available after /// calling begin) int blockSize() { if (p_decoder == nullptr) return block_size; return p_decoder->blockSize(); } /// Provides the frame size (size of decoded frame) (only available after /// calling begin) int frameSize() { if (p_decoder == nullptr) return 0; return p_decoder->frameSize() * 2; } bool begin() override { TRACEI(); if (p_decoder == nullptr) { setImplementation(); } if (is_started) return true; current_byte = 0; LOGI("sample_rate: %d, channels: %d", info.sample_rate, info.channels); p_decoder->begin(info.sample_rate, info.channels); LOGI("frameSize: %d", (int)frameSize()); LOGI("blockSize: %d", (int)blockSize()); block_size = p_decoder->blockSize(); assert(block_size > 0); assert(p_decoder->frameSize() > 0); adpcm_block.resize(block_size); notifyAudioChange(info); is_started = true; return true; } void end() override { TRACEI(); if (p_decoder != nullptr) p_decoder->end(); adpcm_block.resize(0); is_started = false; } virtual void setOutput(Print &out_stream) override { p_print = &out_stream; } virtual size_t write(const uint8_t *data, size_t len) override { TRACED(); uint8_t *input_buffer8 = (uint8_t *)data; LOGD("write: %d", (int)len); for (int j = 0; j < len; j++) { decode(input_buffer8[j]); } return len; } void flush() { if (p_decoder != nullptr) p_decoder->flush(); } operator bool() override { return is_started; } protected: adpcm_ffmpeg::ADPCMDecoder *p_decoder = nullptr; Vector adpcm_block; Print *p_print = nullptr; int current_byte = 0; int block_size = ADAPCM_DEFAULT_BLOCK_SIZE; AVCodecID codec_id = AV_CODEC_ID_ADPCM_MS; bool is_started = false; virtual bool decode(uint8_t byte) { if (p_decoder == nullptr) return false; adpcm_block[current_byte++] = byte; if (current_byte >= block_size) { TRACED(); adpcm_ffmpeg::AVFrame &frame = p_decoder->decode(&adpcm_block[0], block_size); // print the result int16_t *data = (int16_t *)frame.data[0]; size_t byte_count = frame.nb_samples * sizeof(int16_t) * info.channels; size_t written = p_print->write((uint8_t *)data, byte_count); if (written != byte_count) { LOGE("decode %d -> %d -> %d", block_size, (int)byte_count, (int)written); } else { LOGD("decode %d -> %d -> %d", block_size, (int)byte_count, (int)written); } // restart from array begin current_byte = 0; } return true; } /// change the decoder implementation void setImplementation() { // delete the old decoder if (p_decoder != nullptr) { p_decoder->end(); delete p_decoder; p_decoder = nullptr; } if (codec_id == AV_CODEC_ID_ADPCM_IMA_AMV) { info.sample_rate = 22050; info.channels = 1; info.bits_per_sample = 16; } p_decoder = adpcm_ffmpeg::ADPCMDecoderFactory::create(codec_id); if (p_decoder != nullptr) { p_decoder->setCodecID(codec_id); p_decoder->setBlockSize(block_size); } else { LOGE("Decoder not implemented"); } } }; /** * @brief Encoder for ADPCM - Depends on https://github.com/pschatzmann/adpcm * @ingroup codecs * @ingroup p_encoder-> * @author Phil Schatzmann * @copyright GPLv3 */ class ADPCMEncoder : public AudioEncoderExt { public: ADPCMEncoder() = default; ADPCMEncoder(AVCodecID id, int blockSize = ADAPCM_DEFAULT_BLOCK_SIZE) { setId(id); setBlockSize(blockSize); } /// Destructor ~ADPCMEncoder() { if (p_encoder != nullptr) delete p_encoder; } /// (re) defines the codec id void setId(AVCodecID id) { codec_id = id; if (p_encoder != nullptr) { setImplementation(); } } /// (re) defines the block size void setBlockSize(int blockSize) { block_size = blockSize; if (p_encoder == nullptr) return; p_encoder->setBlockSize(blockSize); } /// Provides the block size (size of encoded frame) (only available after /// calling begin) int blockSize() override { if (p_encoder == nullptr) return 0; return p_encoder->blockSize(); } /// Provides the frame size (size of decoded frame) (only available after /// calling begin) int frameSize() { if (p_encoder == nullptr) return 0; return p_encoder->frameSize() * 2; } bool begin() override { TRACEI(); if (p_encoder == nullptr) { setImplementation(); }; if (is_started) return true; LOGI("sample_rate: %d, channels: %d", info.sample_rate, info.channels); p_encoder->begin(info.sample_rate, info.channels); LOGI("frameSize: %d", (int)frameSize()); LOGI("blockSize: %d", (int)blockSize()); assert(info.sample_rate != 0); assert(p_encoder->frameSize() != 0); total_samples = p_encoder->frameSize() * info.channels; pcm_block.resize(total_samples); current_sample = 0; is_started = true; return true; } void end() override { TRACEI(); pcm_block.resize(0); if (p_encoder == nullptr) return; p_encoder->end(); is_started = false; } const char *mime() override { return "audio/adpcm"; } void setOutput(Print &out_stream) override { p_print = &out_stream; } operator bool() override { return is_started; } size_t write(const uint8_t *data, size_t len) override { LOGD("write: %d", (int)len); int16_t *data16 = (int16_t *)data; for (int j = 0; j < len / 2; j++) { encode(data16[j]); } return len; } /// provides the frame duration in us (for rtsp) virtual uint32_t frameDurationUs() override { if (p_encoder == nullptr || info.sample_rate == 0) { return 20000; // Default 20ms if not initialized } // Get the number of samples per frame from the encoder int samplesPerFrame = p_encoder->frameSize(); if (samplesPerFrame <= 0) { return 20000; // Default 20ms if invalid frame size } // Calculate frame duration: (samples_per_frame / sample_rate) * 1000000 us uint32_t durationUs = (samplesPerFrame * 1000000) / info.sample_rate; return durationUs; } protected: AVCodecID codec_id = AV_CODEC_ID_ADPCM_MS; adpcm_ffmpeg::ADPCMEncoder *p_encoder = nullptr; Vector pcm_block; Print *p_print = nullptr; bool is_started = false; int current_sample = 0; int total_samples = 0; int current_id = -1; int block_size = ADAPCM_DEFAULT_BLOCK_SIZE; virtual bool encode(int16_t sample) { if (p_encoder == nullptr) return false; pcm_block[current_sample++] = sample; if (current_sample >= total_samples) { TRACED(); adpcm_ffmpeg::AVPacket &packet = p_encoder->encode(&pcm_block[0], total_samples); if (packet.size > 0) { size_t written = p_print->write(packet.data, packet.size); if (written != packet.size) { LOGE("encode %d->%d->%d", 2 * total_samples, (int)packet.size, (int)written); } else { LOGD("encode %d->%d->%d", 2 * total_samples, (int)packet.size, (int)written); } } // restart from array begin current_sample = 0; } return true; } /// change the encoder implementation bool setImplementation() { bool rc = true; // delete the old encoder if (p_encoder != nullptr) { p_encoder->end(); delete p_encoder; p_encoder = nullptr; } if (codec_id == AV_CODEC_ID_ADPCM_IMA_AMV) { info.sample_rate = 22050; info.channels = 1; info.bits_per_sample = 16; } p_encoder = adpcm_ffmpeg::ADPCMEncoderFactory::create(codec_id); if (p_encoder != nullptr) { p_encoder->setCodecID(codec_id); p_encoder->setBlockSize(block_size); } else { LOGE("Encoder not implemented"); rc = false; } return rc; } }; } // namespace audio_tools