Support playing tracks by track id

custom
jacqueline 2 years ago
parent 4e5dba1583
commit 6ff8b5886e
  1. 6
      src/audio/audio_decoder.cpp
  2. 44
      src/audio/audio_fsm.cpp
  3. 30
      src/audio/fatfs_audio_input.cpp
  4. 2
      src/audio/include/audio_events.hpp
  5. 11
      src/audio/include/audio_fsm.hpp
  6. 3
      src/audio/include/fatfs_audio_input.hpp
  7. 12
      src/database/database.cpp
  8. 2
      src/database/include/database.hpp
  9. 7
      src/system_fsm/include/system_events.hpp
  10. 2
      src/system_fsm/running.cpp
  11. 2
      src/tasks/tasks.cpp

@ -89,9 +89,11 @@ auto AudioDecoder::Process(const std::vector<InputStream>& inputs,
// Check the input stream's format has changed (or, by extension, if this is // Check the input stream's format has changed (or, by extension, if this is
// the first stream). // the first stream).
if (!current_input_format_ || *current_input_format_ != info.format) { if (!current_input_format_ || *current_input_format_ != info.format) {
ESP_LOGI(kTag, "beginning new stream");
has_samples_to_send_ = false; has_samples_to_send_ = false;
ProcessStreamInfo(info); if (!ProcessStreamInfo(info)) {
return;
}
ESP_LOGI(kTag, "beginning new stream");
auto res = current_codec_->BeginStream(input->data()); auto res = current_codec_->BeginStream(input->data());
input->consume(res.first); input->consume(res.first);
if (res.second.has_error()) { if (res.second.has_error()) {

@ -5,6 +5,7 @@
*/ */
#include "audio_fsm.hpp" #include "audio_fsm.hpp"
#include <future>
#include <memory> #include <memory>
#include <variant> #include <variant>
#include "audio_decoder.hpp" #include "audio_decoder.hpp"
@ -14,6 +15,7 @@
#include "i2s_audio_output.hpp" #include "i2s_audio_output.hpp"
#include "i2s_dac.hpp" #include "i2s_dac.hpp"
#include "pipeline.hpp" #include "pipeline.hpp"
#include "track.hpp"
namespace audio { namespace audio {
@ -59,18 +61,38 @@ auto AudioState::Init(drivers::GpioExpander* gpio_expander,
return true; return true;
} }
void AudioState::react(const system_fsm::StorageMounted& ev) {
sDatabase = ev.db;
}
namespace states { namespace states {
void Uninitialised::react(const system_fsm::BootComplete&) { void Uninitialised::react(const system_fsm::BootComplete&) {
transit<Standby>(); transit<Standby>();
} }
void Standby::react(const PlayFile& ev) { void Standby::react(const InputFileOpened& ev) {
if (sFileSource->OpenFile(ev.filename)) {
transit<Playback>(); transit<Playback>();
}
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() { void Playback::entry() {
ESP_LOGI(kTag, "beginning playback"); ESP_LOGI(kTag, "beginning playback");
sI2SOutput->SetInUse(true); sI2SOutput->SetInUse(true);
@ -81,6 +103,16 @@ void Playback::exit() {
sI2SOutput->SetInUse(false); 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) { void Playback::react(const InputFileFinished& ev) {
ESP_LOGI(kTag, "finished file"); ESP_LOGI(kTag, "finished file");
if (sTrackQueue.empty()) { if (sTrackQueue.empty()) {
@ -91,6 +123,14 @@ void Playback::react(const InputFileFinished& ev) {
if (std::holds_alternative<std::string>(next_item)) { if (std::holds_alternative<std::string>(next_item)) {
sFileSource->OpenFile(std::get<std::string>(next_item)); sFileSource->OpenFile(std::get<std::string>(next_item));
} else if (std::holds_alternative<database::TrackId>(next_item)) {
auto db = sDatabase.lock();
if (!db) {
ESP_LOGW(kTag, "database not open; ignoring play request");
return;
}
sFileSource->OpenFile(
db->GetTrackPath(std::get<database::TrackId>(next_item)));
} }
} }

@ -8,7 +8,9 @@
#include <stdint.h> #include <stdint.h>
#include <algorithm> #include <algorithm>
#include <chrono>
#include <cstdint> #include <cstdint>
#include <future>
#include <memory> #include <memory>
#include <string> #include <string>
#include <variant> #include <variant>
@ -38,6 +40,7 @@ namespace audio {
FatfsAudioInput::FatfsAudioInput() FatfsAudioInput::FatfsAudioInput()
: IAudioElement(), : IAudioElement(),
pending_path_(),
current_file_(), current_file_(),
is_file_open_(false), is_file_open_(false),
current_container_(), current_container_(),
@ -45,11 +48,19 @@ FatfsAudioInput::FatfsAudioInput()
FatfsAudioInput::~FatfsAudioInput() {} FatfsAudioInput::~FatfsAudioInput() {}
auto FatfsAudioInput::OpenFile(std::future<std::optional<std::string>>&& path)
-> void {
pending_path_ = std::move(path);
}
auto FatfsAudioInput::OpenFile(const std::string& path) -> bool { auto FatfsAudioInput::OpenFile(const std::string& path) -> bool {
if (is_file_open_) { if (is_file_open_) {
f_close(&current_file_); f_close(&current_file_);
is_file_open_ = false; is_file_open_ = false;
} }
if (pending_path_) {
pending_path_ = {};
}
ESP_LOGI(kTag, "opening file %s", path.c_str()); ESP_LOGI(kTag, "opening file %s", path.c_str());
database::TagParserImpl tag_parser; database::TagParserImpl tag_parser;
@ -89,16 +100,33 @@ auto FatfsAudioInput::OpenFile(const std::string& path) -> bool {
return false; return false;
} }
events::Dispatch<InputFileOpened, AudioState>({});
is_file_open_ = true; is_file_open_ = true;
return true; return true;
} }
auto FatfsAudioInput::NeedsToProcess() const -> bool { auto FatfsAudioInput::NeedsToProcess() const -> bool {
return is_file_open_; return is_file_open_ || pending_path_;
} }
auto FatfsAudioInput::Process(const std::vector<InputStream>& inputs, auto FatfsAudioInput::Process(const std::vector<InputStream>& inputs,
OutputStream* output) -> void { 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_) { if (!is_file_open_) {
return; return;
} }

@ -21,9 +21,9 @@ struct PlayFile : tinyfsm::Event {
struct PlayTrack : tinyfsm::Event { struct PlayTrack : tinyfsm::Event {
database::TrackId id; database::TrackId id;
std::optional<database::TrackData> data; std::optional<database::TrackData> data;
std::optional<database::TrackTags> tags;
}; };
struct InputFileOpened : tinyfsm::Event {};
struct InputFileFinished : tinyfsm::Event {}; struct InputFileFinished : tinyfsm::Event {};
struct AudioPipelineIdle : tinyfsm::Event {}; struct AudioPipelineIdle : tinyfsm::Event {};

@ -38,10 +38,13 @@ class AudioState : public tinyfsm::Fsm<AudioState> {
/* Fallback event handler. Does nothing. */ /* Fallback event handler. Does nothing. */
void react(const tinyfsm::Event& ev) {} void react(const tinyfsm::Event& ev) {}
void react(const system_fsm::StorageMounted&);
virtual void react(const system_fsm::BootComplete&) {} virtual void react(const system_fsm::BootComplete&) {}
virtual void react(const PlayTrack&) {} virtual void react(const PlayTrack&) {}
virtual void react(const PlayFile&) {} virtual void react(const PlayFile&) {}
virtual void react(const InputFileOpened&) {}
virtual void react(const InputFileFinished&) {} virtual void react(const InputFileFinished&) {}
virtual void react(const AudioPipelineIdle&) {} virtual void react(const AudioPipelineIdle&) {}
@ -69,8 +72,10 @@ class Uninitialised : public AudioState {
class Standby : public AudioState { class Standby : public AudioState {
public: public:
void react(const PlayTrack&) override {} void react(const InputFileOpened&) override;
void react(const PlayTrack&) override;
void react(const PlayFile&) override; void react(const PlayFile&) override;
using AudioState::react; using AudioState::react;
}; };
@ -79,6 +84,10 @@ class Playback : public AudioState {
void entry() override; void entry() override;
void exit() 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 InputFileFinished&) override;
void react(const AudioPipelineIdle&) override; void react(const AudioPipelineIdle&) override;

@ -7,6 +7,7 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <future>
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
@ -33,6 +34,7 @@ class FatfsAudioInput : public IAudioElement {
FatfsAudioInput(); FatfsAudioInput();
~FatfsAudioInput(); ~FatfsAudioInput();
auto OpenFile(std::future<std::optional<std::string>>&& path) -> void;
auto OpenFile(const std::string& path) -> bool; auto OpenFile(const std::string& path) -> bool;
auto NeedsToProcess() const -> bool override; auto NeedsToProcess() const -> bool override;
@ -47,6 +49,7 @@ class FatfsAudioInput : public IAudioElement {
auto ContainerToStreamType(database::Encoding) auto ContainerToStreamType(database::Encoding)
-> std::optional<codecs::StreamType>; -> std::optional<codecs::StreamType>;
std::optional<std::future<std::optional<std::string>>> pending_path_;
FIL current_file_; FIL current_file_;
bool is_file_open_; bool is_file_open_;

@ -229,6 +229,18 @@ auto Database::Update() -> std::future<void> {
}); });
} }
auto Database::GetTrackPath(TrackId id)
-> std::future<std::optional<std::string>> {
return worker_task_->Dispatch<std::optional<std::string>>(
[=, this]() -> std::optional<std::string> {
auto track_data = dbGetTrackData(id);
if (track_data) {
return track_data->filepath();
}
return {};
});
}
auto Database::GetTracks(std::size_t page_size) -> std::future<Result<Track>*> { auto Database::GetTracks(std::size_t page_size) -> std::future<Result<Track>*> {
return worker_task_->Dispatch<Result<Track>*>([=, this]() -> Result<Track>* { return worker_task_->Dispatch<Result<Track>*>([=, this]() -> Result<Track>* {
Continuation<Track> c{.iterator = nullptr, Continuation<Track> c{.iterator = nullptr,

@ -82,6 +82,8 @@ class Database {
auto Update() -> std::future<void>; auto Update() -> std::future<void>;
auto GetTrackPath(TrackId id) -> std::future<std::optional<std::string>>;
auto GetTracks(std::size_t page_size) -> std::future<Result<Track>*>; auto GetTracks(std::size_t page_size) -> std::future<Result<Track>*>;
auto GetDump(std::size_t page_size) -> std::future<Result<std::string>*>; auto GetDump(std::size_t page_size) -> std::future<Result<std::string>*>;

@ -6,6 +6,9 @@
#pragma once #pragma once
#include <memory>
#include "database.hpp"
#include "tinyfsm.hpp" #include "tinyfsm.hpp"
namespace system_fsm { namespace system_fsm {
@ -38,7 +41,9 @@ struct StorageUnmountRequested : tinyfsm::Event {};
/* /*
* Sent by SysState when the system storage has been successfully mounted. * Sent by SysState when the system storage has been successfully mounted.
*/ */
struct StorageMounted : tinyfsm::Event {}; struct StorageMounted : tinyfsm::Event {
std::weak_ptr<database::Database> db;
};
struct StorageError : tinyfsm::Event {}; struct StorageError : tinyfsm::Event {};

@ -50,7 +50,7 @@ void Running::entry() {
ESP_LOGI(kTag, "storage loaded okay"); ESP_LOGI(kTag, "storage loaded okay");
events::Dispatch<StorageMounted, SystemState, audio::AudioState, ui::UiState>( events::Dispatch<StorageMounted, SystemState, audio::AudioState, ui::UiState>(
StorageMounted()); StorageMounted{.db = sDatabase});
} }
void Running::exit() { void Running::exit() {

@ -45,7 +45,7 @@ auto AllocateStack() -> cpp::span<StackType_t>;
// amount of stack space. // amount of stack space.
template <> template <>
auto AllocateStack<Type::kAudio>() -> cpp::span<StackType_t> { auto AllocateStack<Type::kAudio>() -> cpp::span<StackType_t> {
std::size_t size = 32 * 1024; std::size_t size = 48 * 1024;
return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_DEFAULT)), return {static_cast<StackType_t*>(heap_caps_malloc(size, MALLOC_CAP_DEFAULT)),
size}; size};
} }

Loading…
Cancel
Save