204 lines
5.9 KiB
C++
204 lines
5.9 KiB
C++
#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<uint8_t> write_buffer{0};
|
|
Vector<int32_t> out;
|
|
Vector<uint8_t> 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
|