[audio] use microFLAC library for decoding (#15372)

This commit is contained in:
Kevin Ahrendt
2026-04-02 10:37:14 -05:00
committed by GitHub
parent b8a9d327f0
commit da8d9d9c2d
4 changed files with 42 additions and 54 deletions
+1
View File
@@ -210,6 +210,7 @@ async def to_code(config):
data = _get_data()
if data.flac_support:
cg.add_define("USE_AUDIO_FLAC_SUPPORT")
add_idf_component(name="esphome/micro-flac", ref="0.1.1")
if data.mp3_support:
cg.add_define("USE_AUDIO_MP3_SUPPORT")
if data.opus_support:
+33 -50
View File
@@ -84,13 +84,10 @@ esp_err_t AudioDecoder::start(AudioFileType audio_file_type) {
switch (this->audio_file_type_) {
#ifdef USE_AUDIO_FLAC_SUPPORT
case AudioFileType::FLAC:
this->flac_decoder_ = make_unique<esp_audio_libs::flac::FLACDecoder>();
// CRC check slows down decoding by 15-20% on an ESP32-S3. FLAC sources in ESPHome are either from an http source
// or built into the firmware, so the data integrity is already verified by the time it gets to the decoder,
// making the CRC check unnecessary.
this->flac_decoder_->set_crc_check_enabled(false);
this->flac_decoder_ = make_unique<micro_flac::FLACDecoder>();
this->free_buffer_required_ =
this->output_transfer_buffer_->capacity(); // Adjusted and reallocated after reading the header
this->decoder_buffers_internally_ = true;
break;
#endif
#ifdef USE_AUDIO_MP3_SUPPORT
@@ -268,59 +265,45 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
#ifdef USE_AUDIO_FLAC_SUPPORT
FileDecoderState AudioDecoder::decode_flac_() {
if (!this->audio_stream_info_.has_value()) {
// Header hasn't been read
auto result = this->flac_decoder_->read_header(this->input_buffer_->data(), this->input_buffer_->available());
size_t bytes_consumed, samples_decoded;
if (result > esp_audio_libs::flac::FLAC_DECODER_HEADER_OUT_OF_DATA) {
// Serrious error reading FLAC header, there is no recovery
return FileDecoderState::FAILED;
micro_flac::FLACDecoderResult result = this->flac_decoder_->decode(
this->input_buffer_->data(), this->input_buffer_->available(), this->output_transfer_buffer_->get_buffer_end(),
this->output_transfer_buffer_->free(), bytes_consumed, samples_decoded);
if (result == micro_flac::FLAC_DECODER_SUCCESS) {
if (samples_decoded > 0 && this->audio_stream_info_.has_value()) {
this->output_transfer_buffer_->increase_buffer_length(
this->audio_stream_info_.value().samples_to_bytes(samples_decoded));
}
size_t bytes_consumed = this->flac_decoder_->get_bytes_index();
this->input_buffer_->consume(bytes_consumed);
} else if (result == micro_flac::FLAC_DECODER_HEADER_READY) {
// Header just parsed, stream info now available
const auto &info = this->flac_decoder_->get_stream_info();
this->audio_stream_info_ = audio::AudioStreamInfo(info.bits_per_sample(), info.num_channels(), info.sample_rate());
if (result == esp_audio_libs::flac::FLAC_DECODER_HEADER_OUT_OF_DATA) {
return FileDecoderState::MORE_TO_PROCESS;
}
// Reallocate the output transfer buffer to the smallest necessary size
this->free_buffer_required_ = flac_decoder_->get_output_buffer_size_bytes();
// Reallocate the output transfer buffer to the required size
this->free_buffer_required_ = this->flac_decoder_->get_output_buffer_size_samples() * info.bytes_per_sample();
if (!this->output_transfer_buffer_->reallocate(this->free_buffer_required_)) {
// Couldn't reallocate output buffer
return FileDecoderState::FAILED;
}
this->audio_stream_info_ =
audio::AudioStreamInfo(this->flac_decoder_->get_sample_depth(), this->flac_decoder_->get_num_channels(),
this->flac_decoder_->get_sample_rate());
return FileDecoderState::MORE_TO_PROCESS;
}
uint32_t output_samples = 0;
auto result = this->flac_decoder_->decode_frame(this->input_buffer_->data(), this->input_buffer_->available(),
this->output_transfer_buffer_->get_buffer_end(), &output_samples);
if (result == esp_audio_libs::flac::FLAC_DECODER_ERROR_OUT_OF_DATA) {
// Not an issue, just needs more data that we'll get next time.
return FileDecoderState::POTENTIALLY_FAILED;
}
size_t bytes_consumed = this->flac_decoder_->get_bytes_index();
this->input_buffer_->consume(bytes_consumed);
if (result > esp_audio_libs::flac::FLAC_DECODER_ERROR_OUT_OF_DATA) {
// Corrupted frame, don't retry with current buffer content, wait for new sync
return FileDecoderState::POTENTIALLY_FAILED;
}
// We have successfully decoded some input data and have new output data
this->output_transfer_buffer_->increase_buffer_length(
this->audio_stream_info_.value().samples_to_bytes(output_samples));
if (result == esp_audio_libs::flac::FLAC_DECODER_NO_MORE_FRAMES) {
this->input_buffer_->consume(bytes_consumed);
} else if (result == micro_flac::FLAC_DECODER_END_OF_STREAM) {
this->input_buffer_->consume(bytes_consumed);
return FileDecoderState::END_OF_FILE;
} else if (result == micro_flac::FLAC_DECODER_NEED_MORE_DATA) {
this->input_buffer_->consume(bytes_consumed);
return FileDecoderState::MORE_TO_PROCESS;
} else if (result == micro_flac::FLAC_DECODER_ERROR_OUTPUT_TOO_SMALL) {
// Reallocate to decode the frame on the next call
const auto &info = this->flac_decoder_->get_stream_info();
this->free_buffer_required_ = this->flac_decoder_->get_output_buffer_size_samples() * info.bytes_per_sample();
if (!this->output_transfer_buffer_->reallocate(this->free_buffer_required_)) {
return FileDecoderState::FAILED;
}
} else {
ESP_LOGE(TAG, "FLAC decoder failed: %d", static_cast<int>(result));
return FileDecoderState::POTENTIALLY_FAILED;
}
return FileDecoderState::MORE_TO_PROCESS;
+6 -4
View File
@@ -16,14 +16,16 @@
#include "esp_err.h"
// esp-audio-libs
#ifdef USE_AUDIO_FLAC_SUPPORT
#include <flac_decoder.h>
#endif
#ifdef USE_AUDIO_MP3_SUPPORT
#include <mp3_decoder.h>
#endif
#include <wav_decoder.h>
// micro-flac
#ifdef USE_AUDIO_FLAC_SUPPORT
#include <micro_flac/flac_decoder.h>
#endif
// micro-opus
#ifdef USE_AUDIO_OPUS_SUPPORT
#include <micro_opus/ogg_opus_decoder.h>
@@ -119,7 +121,7 @@ class AudioDecoder {
std::unique_ptr<esp_audio_libs::wav_decoder::WAVDecoder> wav_decoder_;
#ifdef USE_AUDIO_FLAC_SUPPORT
FileDecoderState decode_flac_();
std::unique_ptr<esp_audio_libs::flac::FLACDecoder> flac_decoder_;
std::unique_ptr<micro_flac::FLACDecoder> flac_decoder_;
#endif
#ifdef USE_AUDIO_MP3_SUPPORT
FileDecoderState decode_mp3_();
+2
View File
@@ -3,6 +3,8 @@ dependencies:
version: "7.4.2"
esphome/esp-audio-libs:
version: 2.0.4
esphome/micro-flac:
version: 0.1.1
esphome/micro-opus:
version: 0.3.6
espressif/esp-dsp: