11static const char *
const TAG =
"audio.decoder";
13static const uint32_t DECODING_TIMEOUT_MS = 50;
14static const uint32_t READ_WRITE_TIMEOUT_MS = 20;
16static const uint32_t MAX_POTENTIALLY_FAILED_COUNT = 10;
19 : input_buffer_size_(input_buffer_size) {
24#ifdef USE_AUDIO_MP3_SUPPORT
26 esp_audio_libs::helix_decoder::MP3FreeDecoder(this->
mp3_decoder_);
34 return ESP_ERR_NO_MEM;
36 source->set_source(input_ring_buffer);
42 auto source = make_unique<ConstAudioSourceBuffer>();
53 return ESP_ERR_NO_MEM;
62 return ESP_ERR_NO_MEM;
71 return ESP_ERR_NO_MEM;
76 return ESP_ERR_NO_MEM;
85#ifdef USE_AUDIO_FLAC_SUPPORT
93#ifdef USE_AUDIO_MP3_SUPPORT
95 this->
mp3_decoder_ = esp_audio_libs::helix_decoder::MP3InitDecoder();
104#ifdef USE_AUDIO_OPUS_SUPPORT
106 this->
opus_decoder_ = make_unique<micro_opus::OggOpusDecoder>();
113 this->
wav_decoder_ = make_unique<esp_audio_libs::wav_decoder::WAVDecoder>();
125 return ESP_ERR_NOT_SUPPORTED;
137 if (stop_gracefully) {
152 if (stop_gracefully) {
163 bool first_loop_iteration =
true;
165 size_t bytes_processed = 0;
166 size_t bytes_available_before_processing = 0;
172 size_t bytes_written =
182 delay(READ_WRITE_TIMEOUT_MS);
187 (
millis() - decoding_start > DECODING_TIMEOUT_MS)) {
195 size_t bytes_read = this->
input_buffer_->fill(pdMS_TO_TICKS(READ_WRITE_TIMEOUT_MS),
198 if (!first_loop_iteration && (this->
input_buffer_->available() < bytes_processed)) {
206 bytes_available_before_processing = this->
input_buffer_->available();
211 if ((this->
input_buffer_->free() == 0) && first_loop_iteration) {
225#ifdef USE_AUDIO_FLAC_SUPPORT
230#ifdef USE_AUDIO_MP3_SUPPORT
235#ifdef USE_AUDIO_OPUS_SUPPORT
250 first_loop_iteration =
false;
251 bytes_processed = bytes_available_before_processing - this->
input_buffer_->available();
266#ifdef USE_AUDIO_FLAC_SUPPORT
268 size_t bytes_consumed, samples_decoded;
270 micro_flac::FLACDecoderResult result = this->
flac_decoder_->decode(
271 this->
input_buffer_->data(), this->input_buffer_->available(), this->output_transfer_buffer_->get_buffer_end(),
272 this->output_transfer_buffer_->free(), bytes_consumed, samples_decoded);
274 if (result == micro_flac::FLAC_DECODER_SUCCESS) {
280 }
else if (result == micro_flac::FLAC_DECODER_HEADER_READY) {
291 }
else if (result == micro_flac::FLAC_DECODER_END_OF_STREAM) {
294 }
else if (result == micro_flac::FLAC_DECODER_NEED_MORE_DATA) {
297 }
else if (result == micro_flac::FLAC_DECODER_ERROR_OUTPUT_TOO_SMALL) {
305 ESP_LOGE(TAG,
"FLAC decoder failed: %d",
static_cast<int>(result));
313#ifdef USE_AUDIO_MP3_SUPPORT
317 int32_t offset = esp_audio_libs::helix_decoder::MP3FindSyncWord(this->
input_buffer_->data(), buffer_length);
330 int err = esp_audio_libs::helix_decoder::MP3Decode(this->
mp3_decoder_, &buffer_start, &buffer_length,
333 size_t consumed = this->
input_buffer_->available() - buffer_length;
338 case esp_audio_libs::helix_decoder::ERR_MP3_OUT_OF_MEMORY:
340 case esp_audio_libs::helix_decoder::ERR_MP3_NULL_POINTER:
349 esp_audio_libs::helix_decoder::MP3FrameInfo mp3_frame_info;
350 esp_audio_libs::helix_decoder::MP3GetLastFrameInfo(this->
mp3_decoder_, &mp3_frame_info);
351 if (mp3_frame_info.outputSamps > 0) {
352 int bytes_per_sample = (mp3_frame_info.bitsPerSample / 8);
366#ifdef USE_AUDIO_OPUS_SUPPORT
368 bool processed_header = this->
opus_decoder_->is_initialized();
370 size_t bytes_consumed, samples_decoded;
372 micro_opus::OggOpusResult result = this->
opus_decoder_->decode(
373 this->
input_buffer_->data(), this->input_buffer_->available(), this->output_transfer_buffer_->get_buffer_end(),
374 this->output_transfer_buffer_->free(), bytes_consumed, samples_decoded);
376 if (result == micro_opus::OGG_OPUS_OK) {
377 if (!processed_header && this->
opus_decoder_->is_initialized()) {
381 this->opus_decoder_->get_sample_rate());
389 }
else if (result == micro_opus::OGG_OPUS_OUTPUT_BUFFER_TOO_SMALL) {
397 ESP_LOGE(TAG,
"Opus decoder failed: %" PRId8, result);
408 esp_audio_libs::wav_decoder::WAVDecoderResult result =
411 if (result == esp_audio_libs::wav_decoder::WAV_DECODER_SUCCESS_IN_DATA) {
415 this->
wav_decoder_->bits_per_sample(), this->wav_decoder_->num_channels(), this->wav_decoder_->sample_rate());
420 }
else if (result == esp_audio_libs::wav_decoder::WAV_DECODER_WARNING_INCOMPLETE_DATA) {
436 if (bytes_to_copy > 0) {
bool decoder_buffers_internally_
optional< AudioStreamInfo > audio_stream_info_
uint32_t accumulated_frames_written_
esp_err_t start(AudioFileType audio_file_type)
Sets up decoding the file.
esp_audio_libs::helix_decoder::HMP3Decoder mp3_decoder_
FileDecoderState decode_opus_()
std::unique_ptr< AudioReadableBuffer > input_buffer_
std::unique_ptr< micro_flac::FLACDecoder > flac_decoder_
uint32_t potentially_failed_count_
size_t free_buffer_required_
~AudioDecoder()
Deallocates the MP3 decoder (the flac, opus, and wav decoders are deallocated automatically)
std::unique_ptr< AudioSinkTransferBuffer > output_transfer_buffer_
size_t input_buffer_size_
AudioFileType audio_file_type_
FileDecoderState decode_flac_()
std::unique_ptr< micro_opus::OggOpusDecoder > opus_decoder_
esp_err_t add_source(std::weak_ptr< RingBuffer > &input_ring_buffer)
Adds a source ring buffer for raw file data.
std::unique_ptr< esp_audio_libs::wav_decoder::WAVDecoder > wav_decoder_
esp_err_t add_sink(std::weak_ptr< RingBuffer > &output_ring_buffer)
Adds a sink ring buffer for decoded audio.
FileDecoderState decode_wav_()
AudioDecoderState decode(bool stop_gracefully)
Decodes audio from the ring buffer source and writes to the sink.
FileDecoderState decode_mp3_()
AudioDecoder(size_t input_buffer_size, size_t output_buffer_size)
Allocates the output transfer buffer and stores the input buffer size for later use by add_source()
Abstract interface for writing decoded audio data to a sink.
static std::unique_ptr< AudioSinkTransferBuffer > create(size_t buffer_size)
Creates a new sink transfer buffer.
static std::unique_ptr< AudioSourceTransferBuffer > create(size_t buffer_size)
Creates a new source transfer buffer.
Providing packet encoding functions for exchanging data with a remote host.
void HOT delay(uint32_t ms)
uint32_t IRAM_ATTR HOT millis()