#pragma once #include "AudioTools/AudioCodecs/AudioCodecsBase.h" #include "AudioTools/CoreAudio/Buffers.h" #include "foxen-flac.h" namespace audio_tools { #define FOXEN_IN_BUFFER_SIZE 1024 * 2 #define FOXEN_OUT_BUFFER_SIZE 1024 * 4 /** * @brief Foxen FLAC Decoder using https://github.com/astoeckel/libfoxenflac * Unlike FLACDecoder which is a streaming decoder, this is a simple * AudioDecoder implementation. * @ingroup codecs * @ingroup decoder * @author Phil Schatzmann * @copyright GPLv3 */ class FLACDecoderFoxen : public AudioDecoder { public: FLACDecoderFoxen() = default; /// Default Constructor FLACDecoderFoxen(int maxBlockSize, int maxChannels, bool convertTo16Bits = true, bool releaseOnEnd = false) { is_convert_to_16 = convertTo16Bits; max_block_size = maxBlockSize; max_channels = maxChannels; is_release_memory_on_end = releaseOnEnd; }; /// Destructor - calls end(); ~FLACDecoderFoxen() { end(); } bool begin() { TRACEI(); is_active = false; size_t foxen_size = fx_flac_size(max_block_size, max_channels); foxen_data.resize(foxen_size); flac = fx_flac_init(foxen_data.data(), max_block_size, max_channels); if (flac != nullptr) { is_active = true; write_buffer.resize(in_buffer_size); out.resize(out_buffer_size); } else { LOGE("not enough memory"); if (is_stop_on_error) stop(); } return is_active; } void end() { TRACEI(); flush(); if (flac != nullptr && is_release_memory_on_end) { foxen_data.resize(0); write_buffer.resize(0); out.resize(0); } is_active = false; } size_t write(const uint8_t *data, size_t len) override { LOGD("write: %d", len); // no processing if not active if (!is_active) return 0; size_t result = write_buffer.writeArray(data, len); LOGD("write_buffer availabe: %d", write_buffer.available()); while (write_buffer.available() > 0) { if (!decode()) break; } // if the buffer is full we could not decode anything if (write_buffer.available() == write_buffer.size()) { LOGE("Decoder did not consume any data"); if (is_stop_on_error) stop(); } LOGD("write: %d -> %d", len, result); return result; } void flush() { decode(); } operator bool() override { return is_active; } /// Defines the input buffer size (default is 2k) void setInBufferSize(int size) { in_buffer_size = size; } /// Defines the number of 32 bit samples for providing the result (default is /// 4k) void setOutBufferSize(int size) { out_buffer_size = size; } /// Defines the maximum FLAC blocksize: drives the buffer allocation void setMaxBlockSize(int size) { max_block_size = size; } /// Defines the maximum number of channels: drives the buffer allocation void setMaxChannels(int ch) { max_channels = ch; } /// Select between 16 and 32 bit output: the default is 16 bits void set32Bit(bool flag) { is_convert_to_16 = !flag; } protected: fx_flac_t *flac = nullptr; SingleBuffer write_buffer{0}; Vector out; Vector foxen_data{0}; bool is_active = false; bool is_convert_to_16 = true; bool is_stop_on_error = true; bool is_release_memory_on_end = false; int bits_eff = 0; int max_block_size = 5 * 1024; int max_channels = 2; int in_buffer_size = FOXEN_IN_BUFFER_SIZE; int out_buffer_size = FOXEN_OUT_BUFFER_SIZE; bool decode() { TRACED(); if (!is_active) return false; uint32_t out_len = out.size(); uint32_t buf_len = write_buffer.available(); uint32_t buf_len_result = buf_len; int rc = fx_flac_process(flac, write_buffer.data(), &buf_len_result, out.data(), &out_len); // assert(out_len <= FOXEN_OUT_BUFFER_SIZE); switch (rc) { case FLAC_END_OF_METADATA: { processMetadata(); } break; case FLAC_ERR: { LOGE("FLAC decoder in error state!"); if (is_stop_on_error) stop(); } break; default: { if (out_len > 0) { LOGD("Providing data: %d samples", out_len); if (is_convert_to_16) { write16BitData(out_len); } else { write32BitData(out_len); } } } break; } LOGD("processed: %d bytes of %d -> %d samples", buf_len_result, buf_len, out_len); // removed processed bytes from buffer write_buffer.clearArray(buf_len_result); return buf_len_result > 0 || out_len > 0; } void write32BitData(int out_len) { TRACED(); // write the result to the output destination writeBlocking(p_print, (uint8_t *)out.data(), out_len * sizeof(int32_t)); } void write16BitData(int out_len) { TRACED(); // in place convert to 16 bits int16_t *out16 = (int16_t *)out.data(); for (int j = 0; j < out_len; j++) { out16[j] = out.data()[j] >> 16; // 65538; } // write the result to the output destination LOGI("writeBlocking: %d", out_len * sizeof(int16_t)); writeBlocking(p_print, (uint8_t *)out.data(), out_len * sizeof(int16_t)); } void processMetadata() { bits_eff = fx_flac_get_streaminfo(flac, FLAC_KEY_SAMPLE_SIZE); int info_blocksize = fx_flac_get_streaminfo(flac, FLAC_KEY_MAX_BLOCK_SIZE); LOGI("bits: %d", bits_eff); LOGI("blocksize: %d", info_blocksize); // assert(bits_eff == 32); info.sample_rate = fx_flac_get_streaminfo(flac, FLAC_KEY_SAMPLE_RATE); info.channels = fx_flac_get_streaminfo(flac, FLAC_KEY_N_CHANNELS); info.bits_per_sample = is_convert_to_16 ? 16 : bits_eff; info.logInfo(); if (info.channels > max_channels) { LOGE("max channels too low: %d -> %d", max_channels, info.channels); if (is_stop_on_error) stop(); } if (info_blocksize > max_block_size) { LOGE("max channels too low: %d -> %d", max_block_size, info_blocksize); if (is_stop_on_error) stop(); } notifyAudioChange(info); } }; } // namespace audio_tools