Support playing tracks by track id

custom
jacqueline 2 years ago
parent 4e5dba1583
commit 6ff8b5886e
  1. 6
      src/audio/audio_decoder.cpp
  2. 46
      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
// 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()) {

@ -5,6 +5,7 @@
*/
#include "audio_fsm.hpp"
#include <future>
#include <memory>
#include <variant>
#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<Standby>();
}
void Standby::react(const PlayFile& ev) {
if (sFileSource->OpenFile(ev.filename)) {
transit<Playback>();
void Standby::react(const InputFileOpened& ev) {
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() {
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<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 <algorithm>
#include <chrono>
#include <cstdint>
#include <future>
#include <memory>
#include <string>
#include <variant>
@ -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<std::optional<std::string>>&& path)
-> void {
pending_path_ = std::move(path);
}
auto FatfsAudioInput::OpenFile(const std::string& path) -> bool {
if (is_file_open_) {
f_close(&current_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<InputFileOpened, AudioState>({});
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<InputStream>& 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;
}

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

@ -38,10 +38,13 @@ class AudioState : public tinyfsm::Fsm<AudioState> {
/* 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;

@ -7,6 +7,7 @@
#pragma once
#include <cstdint>
#include <future>
#include <memory>
#include <string>
#include <vector>
@ -33,6 +34,7 @@ class FatfsAudioInput : public IAudioElement {
FatfsAudioInput();
~FatfsAudioInput();
auto OpenFile(std::future<std::optional<std::string>>&& 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<codecs::StreamType>;
std::optional<std::future<std::optional<std::string>>> pending_path_;
FIL current_file_;
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>*> {
return worker_task_->Dispatch<Result<Track>*>([=, this]() -> Result<Track>* {
Continuation<Track> c{.iterator = nullptr,

@ -82,6 +82,8 @@ class Database {
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 GetDump(std::size_t page_size) -> std::future<Result<std::string>*>;

@ -6,6 +6,9 @@
#pragma once
#include <memory>
#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<database::Database> db;
};
struct StorageError : tinyfsm::Event {};

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

@ -45,7 +45,7 @@ auto AllocateStack() -> cpp::span<StackType_t>;
// amount of stack space.
template <>
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)),
size};
}

Loading…
Cancel
Save