Restore the previous track position when booting

custom
jacqueline 1 year ago
parent 53c4ea7805
commit 1455288190
  1. 43
      src/audio/audio_fsm.cpp
  2. 1
      src/audio/include/audio_events.hpp
  3. 2
      src/audio/track_queue.cpp
  4. 8
      src/database/database.cpp

@ -13,6 +13,8 @@
#include "audio_sink.hpp" #include "audio_sink.hpp"
#include "bluetooth_types.hpp" #include "bluetooth_types.hpp"
#include "cppbor.h"
#include "cppbor_parse.h"
#include "esp_heap_caps.h" #include "esp_heap_caps.h"
#include "esp_log.h" #include "esp_log.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
@ -58,6 +60,8 @@ StreamBufferHandle_t AudioState::sDrainBuffer;
std::optional<database::TrackId> AudioState::sCurrentTrack; std::optional<database::TrackId> AudioState::sCurrentTrack;
bool AudioState::sIsPlaybackAllowed; bool AudioState::sIsPlaybackAllowed;
static std::optional<std::pair<std::string, uint32_t>> sLastTrackUpdate;
void AudioState::react(const system_fsm::BluetoothEvent& ev) { void AudioState::react(const system_fsm::BluetoothEvent& ev) {
if (ev.event != drivers::bluetooth::Event::kConnectionStateChanged) { if (ev.event != drivers::bluetooth::Event::kConnectionStateChanged) {
return; return;
@ -310,11 +314,15 @@ void Standby::react(const QueueUpdate& ev) {
if (!current_track || (sCurrentTrack && (*sCurrentTrack == *current_track))) { if (!current_track || (sCurrentTrack && (*sCurrentTrack == *current_track))) {
return; return;
} }
if (ev.reason == QueueUpdate::Reason::kDeserialised && sLastTrackUpdate) {
return;
}
clearDrainBuffer(); clearDrainBuffer();
playTrack(*current_track); playTrack(*current_track);
} }
static const char kQueueKey[] = "audio:queue"; static const char kQueueKey[] = "audio:queue";
static const char kCurrentFileKey[] = "audio:current";
void Standby::react(const system_fsm::KeyLockChanged& ev) { void Standby::react(const system_fsm::KeyLockChanged& ev) {
if (!ev.locking) { if (!ev.locking) {
@ -332,6 +340,14 @@ void Standby::react(const system_fsm::KeyLockChanged& ev) {
return; return;
} }
db->put(kQueueKey, queue.serialise()); db->put(kQueueKey, queue.serialise());
if (sLastTrackUpdate) {
cppbor::Array current_track{
cppbor::Tstr{sLastTrackUpdate->first},
cppbor::Uint{sLastTrackUpdate->second},
};
db->put(kCurrentFileKey, current_track.toString());
}
}); });
} }
@ -341,13 +357,32 @@ void Standby::react(const system_fsm::StorageMounted& ev) {
if (!db) { if (!db) {
return; return;
} }
auto res = db->get(kQueueKey);
if (res) { // Restore the currently playing file before restoring the queue. This way,
// we can fall back to restarting the queue's current track if there's any
// issue restoring the current file.
auto current = db->get(kCurrentFileKey);
if (current) {
// Again, ensure we don't boot-loop by trying to play a track that causes
// a crash over and over again.
db->put(kCurrentFileKey, "");
auto [parsed, unused, err] = cppbor::parse(
reinterpret_cast<uint8_t*>(current->data()), current->size());
if (parsed->type() == cppbor::ARRAY) {
std::string filename = parsed->asArray()->get(0)->asTstr()->value();
uint32_t pos = parsed->asArray()->get(1)->asUint()->value();
sLastTrackUpdate = std::make_pair(filename, pos);
sFileSource->SetPath(filename, pos);
}
}
auto queue = db->get(kQueueKey);
if (queue) {
// Don't restore the same queue again. This ideally should do nothing, // Don't restore the same queue again. This ideally should do nothing,
// but guards against bad edge cases where restoring the queue ends up // but guards against bad edge cases where restoring the queue ends up
// causing a crash. // causing a crash.
db->put(kQueueKey, ""); db->put(kQueueKey, "");
sServices->track_queue().deserialise(*res); sServices->track_queue().deserialise(*queue);
} }
}); });
} }
@ -399,6 +434,7 @@ void Playback::react(const QueueUpdate& ev) {
void Playback::react(const PlaybackUpdate& ev) { void Playback::react(const PlaybackUpdate& ev) {
ESP_LOGI(kTag, "elapsed: %lu, total: %lu", ev.seconds_elapsed, ESP_LOGI(kTag, "elapsed: %lu, total: %lu", ev.seconds_elapsed,
ev.track->duration); ev.track->duration);
sLastTrackUpdate = std::make_pair(ev.track->filepath, ev.seconds_elapsed);
} }
void Playback::react(const internal::InputFileOpened& ev) {} void Playback::react(const internal::InputFileOpened& ev) {}
@ -407,6 +443,7 @@ void Playback::react(const internal::InputFileClosed& ev) {}
void Playback::react(const internal::InputFileFinished& ev) { void Playback::react(const internal::InputFileFinished& ev) {
ESP_LOGI(kTag, "finished playing file"); ESP_LOGI(kTag, "finished playing file");
sLastTrackUpdate.reset();
sServices->track_queue().finish(); sServices->track_queue().finish();
if (!sServices->track_queue().current()) { if (!sServices->track_queue().current()) {
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {

@ -44,6 +44,7 @@ struct QueueUpdate : tinyfsm::Event {
kExplicitUpdate, kExplicitUpdate,
kRepeatingLastTrack, kRepeatingLastTrack,
kTrackFinished, kTrackFinished,
kDeserialised,
}; };
Reason reason; Reason reason;
}; };

@ -486,7 +486,7 @@ auto TrackQueue::deserialise(const std::string& s) -> void {
QueueParseClient client{*this}; QueueParseClient client{*this};
const uint8_t* data = reinterpret_cast<const uint8_t*>(s.data()); const uint8_t* data = reinterpret_cast<const uint8_t*>(s.data());
cppbor::parse(data, data + s.size(), &client); cppbor::parse(data, data + s.size(), &client);
notifyChanged(true, Reason::kExplicitUpdate); notifyChanged(true, Reason::kDeserialised);
} }
} // namespace audio } // namespace audio

@ -229,13 +229,17 @@ auto Database::sizeOnDiskBytes() -> size_t {
} }
auto Database::put(const std::string& key, const std::string& val) -> void { auto Database::put(const std::string& key, const std::string& val) -> void {
db_->Put(leveldb::WriteOptions{}, kKeyCustom + key, val); if (val.empty()) {
db_->Delete(leveldb::WriteOptions{}, kKeyCustom + key);
} else {
db_->Put(leveldb::WriteOptions{}, kKeyCustom + key, val);
}
} }
auto Database::get(const std::string& key) -> std::optional<std::string> { auto Database::get(const std::string& key) -> std::optional<std::string> {
std::string val; std::string val;
auto res = db_->Get(leveldb::ReadOptions{}, kKeyCustom + key, &val); auto res = db_->Get(leveldb::ReadOptions{}, kKeyCustom + key, &val);
if (!res.ok()) { if (!res.ok() || val.empty()) {
return {}; return {};
} }
return val; return val;

Loading…
Cancel
Save