diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp index 4fad3114..7e0fb207 100644 --- a/src/audio/audio_task.cpp +++ b/src/audio/audio_task.cpp @@ -53,20 +53,23 @@ static const char* kTag = "audio_dec"; static constexpr std::size_t kSampleBufferSize = 16 * 1024; -Timer::Timer(StreamInfo::Pcm format) - : format_(format), - current_seconds_(0), - current_sample_in_second_(0), - total_duration_seconds_(0) {} - -auto Timer::SetLengthSeconds(uint32_t len) -> void { - total_duration_seconds_ = len; - has_duration_ = true; -} - -auto Timer::SetLengthBytes(uint32_t len) -> void { - total_duration_seconds_ = bytes_to_samples(len) / format_.sample_rate; - has_duration_ = true; +Timer::Timer(const StreamInfo::Pcm& format, const Duration& duration) + : format_(format), current_seconds_(0), current_sample_in_second_(0) { + switch (duration.src) { + case Duration::Source::kLibTags: + ESP_LOGI(kTag, "using duration from libtags"); + total_duration_seconds_ = duration.duration; + break; + case Duration::Source::kCodec: + ESP_LOGI(kTag, "using duration from decoder"); + total_duration_seconds_ = duration.duration; + break; + case Duration::Source::kFileSize: + ESP_LOGW(kTag, "calculating duration from filesize"); + total_duration_seconds_ = + bytes_to_samples(duration.duration) / format_.sample_rate; + break; + } } auto Timer::AddBytes(std::size_t bytes) -> void { @@ -137,14 +140,6 @@ void AudioTask::Main() { if (ForwardPcmStream(*pcm, stream.data())) { stream.consume(stream.data().size_bytes()); } - if (!timer_->has_duration()) { - if (stream.info().total_length_seconds()) { - timer_->SetLengthSeconds(*stream.info().total_length_seconds()); - } else { - timer_->SetLengthBytes( - stream.info().total_length_bytes().value_or(0)); - } - } return; } @@ -235,16 +230,20 @@ auto AudioTask::BeginDecoding(InputStream& stream) -> bool { .sample_rate = format.sample_rate_hz, }; - if (!ConfigureSink(new_format)) { - return false; - } - + Duration duration; if (format.duration_seconds) { - timer_->SetLengthSeconds(*format.duration_seconds); + duration.src = Duration::Source::kCodec; + duration.duration = *format.duration_seconds; } else if (stream.info().total_length_seconds()) { - timer_->SetLengthSeconds(*stream.info().total_length_seconds()); + duration.src = Duration::Source::kLibTags; + duration.duration = *stream.info().total_length_seconds(); } else { - timer_->SetLengthBytes(stream.info().total_length_bytes().value_or(0)); + duration.src = Duration::Source::kFileSize; + duration.duration = *stream.info().total_length_bytes(); + } + + if (!ConfigureSink(new_format, duration)) { + return false; } return true; @@ -307,7 +306,11 @@ auto AudioTask::ForwardPcmStream(StreamInfo::Pcm& format, cpp::span samples) -> bool { // First we need to reconfigure the sink for this sample format. if (format != current_output_format_) { - if (!ConfigureSink(format)) { + Duration d{ + .src = Duration::Source::kFileSize, + .duration = samples.size_bytes(), + }; + if (!ConfigureSink(format, d)) { return false; } } @@ -319,7 +322,8 @@ auto AudioTask::ForwardPcmStream(StreamInfo::Pcm& format, return true; } -auto AudioTask::ConfigureSink(const StreamInfo::Pcm& format) -> bool { +auto AudioTask::ConfigureSink(const StreamInfo::Pcm& format, + const Duration& duration) -> bool { if (format != current_output_format_) { // The new format is different to the old one. Wait for the sink to drain // before continuing. @@ -337,7 +341,7 @@ auto AudioTask::ConfigureSink(const StreamInfo::Pcm& format) -> bool { } current_output_format_ = format; - timer_.reset(new Timer(format)); + timer_.reset(new Timer(format, duration)); return true; } diff --git a/src/audio/include/audio_task.hpp b/src/audio/include/audio_task.hpp index 72732021..f6e9789b 100644 --- a/src/audio/include/audio_task.hpp +++ b/src/audio/include/audio_task.hpp @@ -18,15 +18,21 @@ namespace audio { +struct Duration { + enum class Source { + kLibTags, + kCodec, + kFileSize, + }; + Source src; + uint32_t duration; +}; + class Timer { public: - explicit Timer(StreamInfo::Pcm); - - auto SetLengthSeconds(uint32_t) -> void; - auto SetLengthBytes(uint32_t) -> void; + Timer(const StreamInfo::Pcm&, const Duration&); auto AddBytes(std::size_t) -> void; - auto has_duration() const -> bool { return has_duration_; } private: auto bytes_to_samples(uint32_t) -> uint32_t; @@ -36,7 +42,6 @@ class Timer { uint32_t current_seconds_; uint32_t current_sample_in_second_; - bool has_duration_; uint32_t total_duration_seconds_; }; @@ -57,7 +62,7 @@ class AudioTask { auto ForwardPcmStream(StreamInfo::Pcm&, cpp::span) -> bool; - auto ConfigureSink(const StreamInfo::Pcm&) -> bool; + auto ConfigureSink(const StreamInfo::Pcm&, const Duration&) -> bool; IAudioSource* source_; IAudioSink* sink_; diff --git a/src/codecs/foxenflac.cpp b/src/codecs/foxenflac.cpp index ce18975c..3a727ce2 100644 --- a/src/codecs/foxenflac.cpp +++ b/src/codecs/foxenflac.cpp @@ -6,6 +6,7 @@ #include "foxenflac.hpp" #include +#include #include @@ -44,13 +45,20 @@ auto FoxenFlacDecoder::BeginStream(const cpp::span input) return {bytes_used, cpp::fail(Error::kMalformedData)}; } - return {bytes_used, - OutputFormat{ - .num_channels = static_cast(channels), - .bits_per_sample = 32, // libfoxenflac output is fixed-size. - .sample_rate_hz = static_cast(fs), - .duration_seconds = {}, - }}; + OutputFormat format{ + .num_channels = static_cast(channels), + .bits_per_sample = 32, // libfoxenflac output is fixed-size. + .sample_rate_hz = static_cast(fs), + .duration_seconds = {}, + .bits_per_second = {}, + }; + + uint64_t num_samples = fx_flac_get_streaminfo(flac_, FLAC_KEY_N_SAMPLES); + if (num_samples > 0) { + format.duration_seconds = num_samples / fs; + } + + return {bytes_used, format}; } auto FoxenFlacDecoder::ContinueStream(cpp::span input,