summaryrefslogtreecommitdiff
path: root/src/audio
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-06-22 09:40:46 +1000
committerjacqueline <me@jacqueline.id.au>2023-06-22 09:40:46 +1000
commitcde8002df4a86a2a6efd4aa0b5c174b2f39f7bf7 (patch)
tree58f7281b56539eee5a5b12e981b59349511c3c2c /src/audio
parentb58b072d2d42cc1a9dab3e6b27f2f3ae70fe7610 (diff)
downloadtangara-fw-cde8002df4a86a2a6efd4aa0b5c174b2f39f7bf7.tar.gz
Fix (i think?) mysterious overly large reads in libmad
Diffstat (limited to 'src/audio')
-rw-r--r--src/audio/audio_task.cpp10
-rw-r--r--src/audio/fatfs_audio_input.cpp20
2 files changed, 23 insertions, 7 deletions
diff --git a/src/audio/audio_task.cpp b/src/audio/audio_task.cpp
index d4c1d27a..24bc7be7 100644
--- a/src/audio/audio_task.cpp
+++ b/src/audio/audio_task.cpp
@@ -173,11 +173,11 @@ void AudioTaskMain(std::unique_ptr<Pipeline> pipeline, IAudioSink* sink) {
float samples_sunk = bytes_sunk;
samples_sunk /= pcm.channels;
- int8_t bps = pcm.bits_per_sample;
- if (bps == 24) {
- bps = 32;
- }
- samples_sunk /= (bps / 8);
+ // Samples must be aligned to 16 bits. The number of actual bytes per
+ // sample is therefore the bps divided by 16, rounded up (align to word),
+ // times two (convert to bytes).
+ uint8_t bytes_per_sample = ((pcm.bits_per_sample + 16 - 1) / 16) * 2;
+ samples_sunk /= bytes_per_sample;
current_sample_in_second += samples_sunk;
while (current_sample_in_second >= pcm.sample_rate) {
diff --git a/src/audio/fatfs_audio_input.cpp b/src/audio/fatfs_audio_input.cpp
index 86b455f0..ca5b02a1 100644
--- a/src/audio/fatfs_audio_input.cpp
+++ b/src/audio/fatfs_audio_input.cpp
@@ -9,6 +9,7 @@
#include <algorithm>
#include <chrono>
+#include <cstddef>
#include <cstdint>
#include <future>
#include <memory>
@@ -71,8 +72,7 @@ auto FatfsAudioInput::OpenFile(const std::string& path) -> bool {
database::TrackTags tags;
if (!tag_parser.ReadAndParseTags(path, &tags)) {
ESP_LOGE(kTag, "failed to read tags");
- tags.encoding = database::Encoding::kFlac;
- // return false;
+ return false;
}
auto stream_type = ContainerToStreamType(tags.encoding);
@@ -115,6 +115,8 @@ auto FatfsAudioInput::NeedsToProcess() const -> bool {
auto FatfsAudioInput::Process(const std::vector<InputStream>& inputs,
OutputStream* output) -> void {
+ // If the next path is being given to us asynchronously, then we need to check
+ // in regularly to see if it's available yet.
if (pending_path_) {
if (!pending_path_->valid()) {
pending_path_ = {};
@@ -133,11 +135,15 @@ auto FatfsAudioInput::Process(const std::vector<InputStream>& inputs,
return;
}
+ // If the output buffer isn't ready for a new stream, then we need to wait.
if (!has_prepared_output_ && !output->prepare(*current_format_)) {
return;
}
has_prepared_output_ = true;
+ // Performing many small reads is inefficient; it's better to do fewer, larger
+ // reads. Try to achieve this by only reading in new bytes if the output
+ // buffer has been mostly drained.
std::size_t max_size = output->data().size_bytes();
if (max_size < output->data().size_bytes() / 2) {
return;
@@ -148,6 +154,7 @@ auto FatfsAudioInput::Process(const std::vector<InputStream>& inputs,
f_read(&current_file_, output->data().data(), max_size, &size);
if (result != FR_OK) {
ESP_LOGE(kTag, "file I/O error %d", result);
+ output->mark_producer_finished();
// TODO(jacqueline): Handle errors.
return;
}
@@ -155,6 +162,15 @@ auto FatfsAudioInput::Process(const std::vector<InputStream>& inputs,
output->add(size);
if (size < max_size || f_eof(&current_file_)) {
+ // HACK: In order to decode the last frame of a file, libmad requires 8
+ // 0-bytes ( == MAD_GUARD_BYTES) to be appended to the end of the stream.
+ // It would be better to do this within mad.cpp, but so far it's the only
+ // decoder that has such a requirement.
+ if (current_container_ == database::Encoding::kMp3) {
+ std::fill_n(output->data().begin(), 8, std::byte(0));
+ output->add(8);
+ }
+
f_close(&current_file_);
is_file_open_ = false;
has_prepared_output_ = false;