mirror of
https://github.com/esphome/esphome.git
synced 2026-05-31 17:06:40 +08:00
[audio] use microFLAC library for decoding (#15372)
This commit is contained in:
@@ -210,6 +210,7 @@ async def to_code(config):
|
|||||||
data = _get_data()
|
data = _get_data()
|
||||||
if data.flac_support:
|
if data.flac_support:
|
||||||
cg.add_define("USE_AUDIO_FLAC_SUPPORT")
|
cg.add_define("USE_AUDIO_FLAC_SUPPORT")
|
||||||
|
add_idf_component(name="esphome/micro-flac", ref="0.1.1")
|
||||||
if data.mp3_support:
|
if data.mp3_support:
|
||||||
cg.add_define("USE_AUDIO_MP3_SUPPORT")
|
cg.add_define("USE_AUDIO_MP3_SUPPORT")
|
||||||
if data.opus_support:
|
if data.opus_support:
|
||||||
|
|||||||
@@ -84,13 +84,10 @@ esp_err_t AudioDecoder::start(AudioFileType audio_file_type) {
|
|||||||
switch (this->audio_file_type_) {
|
switch (this->audio_file_type_) {
|
||||||
#ifdef USE_AUDIO_FLAC_SUPPORT
|
#ifdef USE_AUDIO_FLAC_SUPPORT
|
||||||
case AudioFileType::FLAC:
|
case AudioFileType::FLAC:
|
||||||
this->flac_decoder_ = make_unique<esp_audio_libs::flac::FLACDecoder>();
|
this->flac_decoder_ = make_unique<micro_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->free_buffer_required_ =
|
this->free_buffer_required_ =
|
||||||
this->output_transfer_buffer_->capacity(); // Adjusted and reallocated after reading the header
|
this->output_transfer_buffer_->capacity(); // Adjusted and reallocated after reading the header
|
||||||
|
this->decoder_buffers_internally_ = true;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_AUDIO_MP3_SUPPORT
|
#ifdef USE_AUDIO_MP3_SUPPORT
|
||||||
@@ -268,59 +265,45 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
|
|||||||
|
|
||||||
#ifdef USE_AUDIO_FLAC_SUPPORT
|
#ifdef USE_AUDIO_FLAC_SUPPORT
|
||||||
FileDecoderState AudioDecoder::decode_flac_() {
|
FileDecoderState AudioDecoder::decode_flac_() {
|
||||||
if (!this->audio_stream_info_.has_value()) {
|
size_t bytes_consumed, samples_decoded;
|
||||||
// Header hasn't been read
|
|
||||||
auto result = this->flac_decoder_->read_header(this->input_buffer_->data(), this->input_buffer_->available());
|
|
||||||
|
|
||||||
if (result > esp_audio_libs::flac::FLAC_DECODER_HEADER_OUT_OF_DATA) {
|
micro_flac::FLACDecoderResult result = this->flac_decoder_->decode(
|
||||||
// Serrious error reading FLAC header, there is no recovery
|
this->input_buffer_->data(), this->input_buffer_->available(), this->output_transfer_buffer_->get_buffer_end(),
|
||||||
return FileDecoderState::FAILED;
|
this->output_transfer_buffer_->free(), bytes_consumed, samples_decoded);
|
||||||
}
|
|
||||||
|
|
||||||
size_t bytes_consumed = this->flac_decoder_->get_bytes_index();
|
if (result == micro_flac::FLAC_DECODER_SUCCESS) {
|
||||||
this->input_buffer_->consume(bytes_consumed);
|
if (samples_decoded > 0 && this->audio_stream_info_.has_value()) {
|
||||||
|
|
||||||
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();
|
|
||||||
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->output_transfer_buffer_->increase_buffer_length(
|
||||||
this->audio_stream_info_.value().samples_to_bytes(output_samples));
|
this->audio_stream_info_.value().samples_to_bytes(samples_decoded));
|
||||||
|
}
|
||||||
|
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_NO_MORE_FRAMES) {
|
// 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_)) {
|
||||||
|
return FileDecoderState::FAILED;
|
||||||
|
}
|
||||||
|
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;
|
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;
|
return FileDecoderState::MORE_TO_PROCESS;
|
||||||
|
|||||||
@@ -16,14 +16,16 @@
|
|||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
|
|
||||||
// esp-audio-libs
|
// esp-audio-libs
|
||||||
#ifdef USE_AUDIO_FLAC_SUPPORT
|
|
||||||
#include <flac_decoder.h>
|
|
||||||
#endif
|
|
||||||
#ifdef USE_AUDIO_MP3_SUPPORT
|
#ifdef USE_AUDIO_MP3_SUPPORT
|
||||||
#include <mp3_decoder.h>
|
#include <mp3_decoder.h>
|
||||||
#endif
|
#endif
|
||||||
#include <wav_decoder.h>
|
#include <wav_decoder.h>
|
||||||
|
|
||||||
|
// micro-flac
|
||||||
|
#ifdef USE_AUDIO_FLAC_SUPPORT
|
||||||
|
#include <micro_flac/flac_decoder.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
// micro-opus
|
// micro-opus
|
||||||
#ifdef USE_AUDIO_OPUS_SUPPORT
|
#ifdef USE_AUDIO_OPUS_SUPPORT
|
||||||
#include <micro_opus/ogg_opus_decoder.h>
|
#include <micro_opus/ogg_opus_decoder.h>
|
||||||
@@ -119,7 +121,7 @@ class AudioDecoder {
|
|||||||
std::unique_ptr<esp_audio_libs::wav_decoder::WAVDecoder> wav_decoder_;
|
std::unique_ptr<esp_audio_libs::wav_decoder::WAVDecoder> wav_decoder_;
|
||||||
#ifdef USE_AUDIO_FLAC_SUPPORT
|
#ifdef USE_AUDIO_FLAC_SUPPORT
|
||||||
FileDecoderState decode_flac_();
|
FileDecoderState decode_flac_();
|
||||||
std::unique_ptr<esp_audio_libs::flac::FLACDecoder> flac_decoder_;
|
std::unique_ptr<micro_flac::FLACDecoder> flac_decoder_;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_AUDIO_MP3_SUPPORT
|
#ifdef USE_AUDIO_MP3_SUPPORT
|
||||||
FileDecoderState decode_mp3_();
|
FileDecoderState decode_mp3_();
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ dependencies:
|
|||||||
version: "7.4.2"
|
version: "7.4.2"
|
||||||
esphome/esp-audio-libs:
|
esphome/esp-audio-libs:
|
||||||
version: 2.0.4
|
version: 2.0.4
|
||||||
|
esphome/micro-flac:
|
||||||
|
version: 0.1.1
|
||||||
esphome/micro-opus:
|
esphome/micro-opus:
|
||||||
version: 0.3.6
|
version: 0.3.6
|
||||||
espressif/esp-dsp:
|
espressif/esp-dsp:
|
||||||
|
|||||||
Reference in New Issue
Block a user