diff --git a/src/audio/audio_decoder.cpp b/src/audio/audio_decoder.cpp index abb94e85..b4af65fb 100644 --- a/src/audio/audio_decoder.cpp +++ b/src/audio/audio_decoder.cpp @@ -89,9 +89,11 @@ auto AudioDecoder::Process(const std::vector& inputs, // Check the input stream's format has changed (or, by extension, if this is // the first stream). if (!current_input_format_ || *current_input_format_ != info.format) { - ESP_LOGI(kTag, "beginning new stream"); has_samples_to_send_ = false; - ProcessStreamInfo(info); + if (!ProcessStreamInfo(info)) { + return; + } + ESP_LOGI(kTag, "beginning new stream"); auto res = current_codec_->BeginStream(input->data()); input->consume(res.first); if (res.second.has_error()) { diff --git a/src/audio/audio_fsm.cpp b/src/audio/audio_fsm.cpp index 0be28250..a65b9f3b 100644 --- a/src/audio/audio_fsm.cpp +++ b/src/audio/audio_fsm.cpp @@ -5,6 +5,7 @@ */ #include "audio_fsm.hpp" +#include #include #include #include "audio_decoder.hpp" @@ -14,6 +15,7 @@ #include "i2s_audio_output.hpp" #include "i2s_dac.hpp" #include "pipeline.hpp" +#include "track.hpp" namespace audio { @@ -59,18 +61,38 @@ auto AudioState::Init(drivers::GpioExpander* gpio_expander, return true; } +void AudioState::react(const system_fsm::StorageMounted& ev) { + sDatabase = ev.db; +} + namespace states { void Uninitialised::react(const system_fsm::BootComplete&) { transit(); } -void Standby::react(const PlayFile& ev) { - if (sFileSource->OpenFile(ev.filename)) { - transit(); +void Standby::react(const InputFileOpened& ev) { + transit(); +} + +void Standby::react(const PlayTrack& ev) { + auto db = sDatabase.lock(); + if (!db) { + ESP_LOGW(kTag, "database not open; ignoring play request"); + return; + } + + if (ev.data) { + sFileSource->OpenFile(ev.data->filepath()); + } else { + sFileSource->OpenFile(db->GetTrackPath(ev.id)); } } +void Standby::react(const PlayFile& ev) { + sFileSource->OpenFile(ev.filename); +} + void Playback::entry() { ESP_LOGI(kTag, "beginning playback"); sI2SOutput->SetInUse(true); @@ -81,6 +103,16 @@ void Playback::exit() { sI2SOutput->SetInUse(false); } +void Playback::react(const PlayTrack& ev) { + sTrackQueue.push_back(EnqueuedItem(ev.id)); +} + +void Playback::react(const PlayFile& ev) { + sTrackQueue.push_back(EnqueuedItem(ev.filename)); +} + +void Playback::react(const InputFileOpened& ev) {} + void Playback::react(const InputFileFinished& ev) { ESP_LOGI(kTag, "finished file"); if (sTrackQueue.empty()) { @@ -91,6 +123,14 @@ void Playback::react(const InputFileFinished& ev) { if (std::holds_alternative(next_item)) { sFileSource->OpenFile(std::get(next_item)); + } else if (std::holds_alternative(next_item)) { + auto db = sDatabase.lock(); + if (!db) { + ESP_LOGW(kTag, "database not open; ignoring play request"); + return; + } + sFileSource->OpenFile( + db->GetTrackPath(std::get(next_item))); } } diff --git a/src/audio/fatfs_audio_input.cpp b/src/audio/fatfs_audio_input.cpp index c26ff0ad..77b104d3 100644 --- a/src/audio/fatfs_audio_input.cpp +++ b/src/audio/fatfs_audio_input.cpp @@ -8,7 +8,9 @@ #include #include +#include #include +#include #include #include #include @@ -38,6 +40,7 @@ namespace audio { FatfsAudioInput::FatfsAudioInput() : IAudioElement(), + pending_path_(), current_file_(), is_file_open_(false), current_container_(), @@ -45,11 +48,19 @@ FatfsAudioInput::FatfsAudioInput() FatfsAudioInput::~FatfsAudioInput() {} +auto FatfsAudioInput::OpenFile(std::future>&& path) + -> void { + pending_path_ = std::move(path); +} + auto FatfsAudioInput::OpenFile(const std::string& path) -> bool { if (is_file_open_) { f_close(¤t_file_); is_file_open_ = false; } + if (pending_path_) { + pending_path_ = {}; + } ESP_LOGI(kTag, "opening file %s", path.c_str()); database::TagParserImpl tag_parser; @@ -89,16 +100,33 @@ auto FatfsAudioInput::OpenFile(const std::string& path) -> bool { return false; } + events::Dispatch({}); is_file_open_ = true; return true; } auto FatfsAudioInput::NeedsToProcess() const -> bool { - return is_file_open_; + return is_file_open_ || pending_path_; } auto FatfsAudioInput::Process(const std::vector& inputs, OutputStream* output) -> void { + if (pending_path_) { + ESP_LOGI(kTag, "waiting for path"); + if (!pending_path_->valid()) { + pending_path_ = {}; + } else { + if (pending_path_->wait_for(std::chrono::seconds(0)) == + std::future_status::ready) { + ESP_LOGI(kTag, "path ready!"); + auto result = pending_path_->get(); + if (result) { + OpenFile(*result); + } + } + } + } + if (!is_file_open_) { return; } diff --git a/src/audio/include/audio_events.hpp b/src/audio/include/audio_events.hpp index eebf5efe..60a0740c 100644 --- a/src/audio/include/audio_events.hpp +++ b/src/audio/include/audio_events.hpp @@ -21,9 +21,9 @@ struct PlayFile : tinyfsm::Event { struct PlayTrack : tinyfsm::Event { database::TrackId id; std::optional data; - std::optional tags; }; +struct InputFileOpened : tinyfsm::Event {}; struct InputFileFinished : tinyfsm::Event {}; struct AudioPipelineIdle : tinyfsm::Event {}; diff --git a/src/audio/include/audio_fsm.hpp b/src/audio/include/audio_fsm.hpp index 72654ab5..bd902706 100644 --- a/src/audio/include/audio_fsm.hpp +++ b/src/audio/include/audio_fsm.hpp @@ -38,10 +38,13 @@ class AudioState : public tinyfsm::Fsm { /* Fallback event handler. Does nothing. */ void react(const tinyfsm::Event& ev) {} + void react(const system_fsm::StorageMounted&); + virtual void react(const system_fsm::BootComplete&) {} virtual void react(const PlayTrack&) {} virtual void react(const PlayFile&) {} + virtual void react(const InputFileOpened&) {} virtual void react(const InputFileFinished&) {} virtual void react(const AudioPipelineIdle&) {} @@ -69,8 +72,10 @@ class Uninitialised : public AudioState { class Standby : public AudioState { public: - void react(const PlayTrack&) override {} + void react(const InputFileOpened&) override; + void react(const PlayTrack&) override; void react(const PlayFile&) override; + using AudioState::react; }; @@ -79,6 +84,10 @@ class Playback : public AudioState { void entry() override; void exit() override; + void react(const PlayTrack&) override; + void react(const PlayFile&) override; + + void react(const InputFileOpened&) override; void react(const InputFileFinished&) override; void react(const AudioPipelineIdle&) override; diff --git a/src/audio/include/fatfs_audio_input.hpp b/src/audio/include/fatfs_audio_input.hpp index f5a65d0d..ab392f54 100644 --- a/src/audio/include/fatfs_audio_input.hpp +++ b/src/audio/include/fatfs_audio_input.hpp @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include #include @@ -33,6 +34,7 @@ class FatfsAudioInput : public IAudioElement { FatfsAudioInput(); ~FatfsAudioInput(); + auto OpenFile(std::future>&& path) -> void; auto OpenFile(const std::string& path) -> bool; auto NeedsToProcess() const -> bool override; @@ -47,6 +49,7 @@ class FatfsAudioInput : public IAudioElement { auto ContainerToStreamType(database::Encoding) -> std::optional; + std::optional>> pending_path_; FIL current_file_; bool is_file_open_; diff --git a/src/database/database.cpp b/src/database/database.cpp index a96b3eab..ac5e4873 100644 --- a/src/database/database.cpp +++ b/src/database/database.cpp @@ -229,6 +229,18 @@ auto Database::Update() -> std::future { }); } +auto Database::GetTrackPath(TrackId id) + -> std::future> { + return worker_task_->Dispatch>( + [=, this]() -> std::optional { + auto track_data = dbGetTrackData(id); + if (track_data) { + return track_data->filepath(); + } + return {}; + }); +} + auto Database::GetTracks(std::size_t page_size) -> std::future*> { return worker_task_->Dispatch*>([=, this]() -> Result* { Continuation c{.iterator = nullptr, diff --git a/src/database/include/database.hpp b/src/database/include/database.hpp index 1a8388e8..8fecc5f6 100644 --- a/src/database/include/database.hpp +++ b/src/database/include/database.hpp @@ -82,6 +82,8 @@ class Database { auto Update() -> std::future; + auto GetTrackPath(TrackId id) -> std::future>; + auto GetTracks(std::size_t page_size) -> std::future*>; auto GetDump(std::size_t page_size) -> std::future*>; diff --git a/src/system_fsm/include/system_events.hpp b/src/system_fsm/include/system_events.hpp index 38929616..ec202c69 100644 --- a/src/system_fsm/include/system_events.hpp +++ b/src/system_fsm/include/system_events.hpp @@ -6,6 +6,9 @@ #pragma once +#include + +#include "database.hpp" #include "tinyfsm.hpp" namespace system_fsm { @@ -38,7 +41,9 @@ struct StorageUnmountRequested : tinyfsm::Event {}; /* * Sent by SysState when the system storage has been successfully mounted. */ -struct StorageMounted : tinyfsm::Event {}; +struct StorageMounted : tinyfsm::Event { + std::weak_ptr db; +}; struct StorageError : tinyfsm::Event {}; diff --git a/src/system_fsm/running.cpp b/src/system_fsm/running.cpp index f9ff6140..87c25440 100644 --- a/src/system_fsm/running.cpp +++ b/src/system_fsm/running.cpp @@ -50,7 +50,7 @@ void Running::entry() { ESP_LOGI(kTag, "storage loaded okay"); events::Dispatch( - StorageMounted()); + StorageMounted{.db = sDatabase}); } void Running::exit() { diff --git a/src/tasks/tasks.cpp b/src/tasks/tasks.cpp index c28f463c..7365813e 100644 --- a/src/tasks/tasks.cpp +++ b/src/tasks/tasks.cpp @@ -45,7 +45,7 @@ auto AllocateStack() -> cpp::span; // amount of stack space. template <> auto AllocateStack() -> cpp::span { - std::size_t size = 32 * 1024; + std::size_t size = 48 * 1024; return {static_cast(heap_caps_malloc(size, MALLOC_CAP_DEFAULT)), size}; }