snapshot
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
#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
|
||||
Reference in New Issue
Block a user