diff --git a/firmware/src/core_a7/aux_core_main.cc b/firmware/src/core_a7/aux_core_main.cc index 20a0c3e04..f59616780 100644 --- a/firmware/src/core_a7/aux_core_main.cc +++ b/firmware/src/core_a7/aux_core_main.cc @@ -11,9 +11,6 @@ #include "internal_plugin_manager.hh" #include "patch_play/patch_player.hh" -// Just to fix clangd: -#include "helpers.hpp" - using FrameBufferT = std::array; static inline FrameBufferT framebuf1 alignas(64); @@ -107,8 +104,9 @@ extern "C" void aux_core_main() { // while (HWSemaphore::is_locked()) // ; - HAL_Delay(300); //allow time to load initial patch: TODO use semaphor + //HAL_Delay(300); //allow time to load initial patch: TODO use semaphor + ui.load_initial_patch(); ui.autoload_plugins(); while (true) { diff --git a/firmware/src/core_a7/main.cc b/firmware/src/core_a7/main.cc index abec605c5..69cf69fb1 100644 --- a/firmware/src/core_a7/main.cc +++ b/firmware/src/core_a7/main.cc @@ -107,7 +107,6 @@ void main() { // ~290ms until while loop StaticBuffers::sync_params.clear(); - patch_playloader.load_initial_patch(); audio.start(); diff --git a/firmware/src/gui/pages/base.hh b/firmware/src/gui/pages/base.hh index ab6aead32..4026cc644 100644 --- a/firmware/src/gui/pages/base.hh +++ b/firmware/src/gui/pages/base.hh @@ -34,6 +34,7 @@ struct GuiState { std::optional force_refresh_vol{}; bool do_write_settings{}; + uint32_t write_settings_after_ms{}; Toggler back_button{}; }; diff --git a/firmware/src/gui/pages/page_manager.hh b/firmware/src/gui/pages/page_manager.hh index 6f10f7ddb..e905cb0a2 100644 --- a/firmware/src/gui/pages/page_manager.hh +++ b/firmware/src/gui/pages/page_manager.hh @@ -196,6 +196,20 @@ public: pr_info("Wrote settings\n"); gui_state.do_write_settings = false; + gui_state.write_settings_after_ms = 0; + } + + if (gui_state.write_settings_after_ms > 0) { + if (get_time() >= gui_state.write_settings_after_ms) { + + if (!Settings::write_settings(info.patch_storage, info.settings)) { + pr_err("Failed to write settings file (timer)\n"); + } else { + pr_info("Wrote settings (timer)\n"); + } + + gui_state.write_settings_after_ms = 0; + } } } diff --git a/firmware/src/gui/pages/patch_view.hh b/firmware/src/gui/pages/patch_view.hh index d315834e3..a00285156 100644 --- a/firmware/src/gui/pages/patch_view.hh +++ b/firmware/src/gui/pages/patch_view.hh @@ -491,10 +491,28 @@ private: page->redraw_modulename(); } + void save_last_opened_patch_in_settings() { + if (settings.last_patch_vol != patches.get_view_patch_vol() || + settings.last_patch_opened != patches.get_view_patch_filename()) + { + settings.last_patch_vol = patches.get_view_patch_vol(); + settings.last_patch_opened = patches.get_view_patch_filename(); + pr_dbg("Will set last_patch opened to %s on %d\n", + settings.last_patch_opened.c_str(), + settings.last_patch_vol); + + if (gui_state.write_settings_after_ms == 0) { + gui_state.write_settings_after_ms = get_time() + 60 * 1000; //1 minute delay + pr_dbg("Setting timer...\n"); + } + } + } + static void playbut_cb(lv_event_t *event) { auto page = static_cast(event->user_data); if (!page->is_patch_playing) { page->patch_playloader.request_load_view_patch(); + page->save_last_opened_patch_in_settings(); } else { if (page->patch_playloader.is_audio_muted()) { page->patch_playloader.start_audio(); diff --git a/firmware/src/gui/pages/system_tab.hh b/firmware/src/gui/pages/system_tab.hh index dbb0f3d9e..6cb935b77 100644 --- a/firmware/src/gui/pages/system_tab.hh +++ b/firmware/src/gui/pages/system_tab.hh @@ -145,6 +145,11 @@ private: [page](bool ok) { if (ok) { page->storage.request_reset_factory_patches(); + while (true) { + auto msg = page->storage.get_message(); + if (msg.message_type == FileStorageProxy::FactoryResetPatchesDone) + break; + } } }, "Do you really want to PERMANENTLY DELETE all patches stored internally? This will replace them with " diff --git a/firmware/src/gui/ui.hh b/firmware/src/gui/ui.hh index 02a377252..c53edd02e 100644 --- a/firmware/src/gui/ui.hh +++ b/firmware/src/gui/ui.hh @@ -98,6 +98,10 @@ public: return params.displays; } + void load_initial_patch() { + patch_playloader.load_initial_patch(settings.last_patch_opened, settings.last_patch_vol); + } + bool new_patch_data = false; private: diff --git a/firmware/src/patch_file/patch_fileio.hh b/firmware/src/patch_file/patch_fileio.hh index f8878e78f..f8363d267 100644 --- a/firmware/src/patch_file/patch_fileio.hh +++ b/firmware/src/patch_file/patch_fileio.hh @@ -133,7 +133,7 @@ public: patch = patch.subspan(0, patch.size() - 1); if (!fileio.update_or_create_file(filename, patch)) { - pr_err("Error: aborted creating default patches to flash\n"); + pr_err("Error: failed to write %d. Aborted creating default patches to flash\n", filename.c_str()); return false; } } diff --git a/firmware/src/patch_play/patch_playloader.hh b/firmware/src/patch_play/patch_playloader.hh index 40d0d25a1..726eb98e1 100644 --- a/firmware/src/patch_play/patch_playloader.hh +++ b/firmware/src/patch_play/patch_playloader.hh @@ -21,10 +21,15 @@ struct PatchPlayLoader { , patches_{patches} { } - void load_initial_patch() { + void load_initial_patch(std::string_view patchname, Volume patch_vol) { uint32_t tries = 10000; - PatchLocation initial_patch_loc{"/SlothDrone.yml", Volume::NorFlash}; + if (patchname.length() == 0) { + patchname = "/SlothDrone.yml"; + patch_vol = Volume::NorFlash; + } + + PatchLocation initial_patch_loc{patchname, patch_vol}; while (--tries) { if (storage_.request_load_patch(initial_patch_loc)) break; diff --git a/firmware/src/user_settings/settings.hh b/firmware/src/user_settings/settings.hh index 2dfef4d09..81954aa51 100644 --- a/firmware/src/user_settings/settings.hh +++ b/firmware/src/user_settings/settings.hh @@ -1,5 +1,6 @@ #pragma once #include "conf/audio_settings.hh" +#include "fs/volumes.hh" #include "gui/pages/view_settings.hh" #include "plugin_autoload_settings.hh" @@ -11,6 +12,8 @@ struct UserSettings { ModuleDisplaySettings module_view{}; AudioSettings audio{}; PluginAutoloadSettings plugin_autoload{}; + std::string last_patch_opened{}; + Volume last_patch_vol{Volume::NorFlash}; }; } // namespace MetaModule diff --git a/firmware/src/user_settings/settings_parse.cc b/firmware/src/user_settings/settings_parse.cc index bfaf6a6cb..85dd3fb3e 100644 --- a/firmware/src/user_settings/settings_parse.cc +++ b/firmware/src/user_settings/settings_parse.cc @@ -103,6 +103,17 @@ bool parse(std::span yaml, UserSettings *settings) { read_or_default(node, "module_view", settings, &UserSettings::module_view); read_or_default(node, "audio", settings, &UserSettings::audio); read_or_default(node, "plugin_autoload", settings, &UserSettings::plugin_autoload); + read_or_default(node, "last_patch_opened", settings, &UserSettings::last_patch_opened); + + // TODO: cleaner way to parse and enum and reject out of range? + if (node.is_map() && node.has_child("last_patch_vol")) { + unsigned t = 0; + node["last_patch_vol"] >> t; + if (t < static_cast(Volume::MaxVolumes)) + settings->last_patch_vol = static_cast(t); + } else { + settings->last_patch_vol = UserSettings{}.last_patch_vol; + } return true; } diff --git a/firmware/src/user_settings/settings_serialize.cc b/firmware/src/user_settings/settings_serialize.cc index 144304283..3e4d4ff40 100644 --- a/firmware/src/user_settings/settings_serialize.cc +++ b/firmware/src/user_settings/settings_serialize.cc @@ -63,6 +63,8 @@ uint32_t serialize(UserSettings const &settings, std::span buffer) { data["module_view"] << settings.module_view; data["audio"] << settings.audio; data["plugin_autoload"] << settings.plugin_autoload; + data["last_patch_opened"] << settings.last_patch_opened; + data["last_patch_vol"] << static_cast(settings.last_patch_vol); auto res = ryml::emit_yaml(tree, c4::substr(buffer.data(), buffer.size())); return res.size(); diff --git a/firmware/tests/settings_parse_tests.cc b/firmware/tests/settings_parse_tests.cc index 62ef3222f..a46368ba0 100644 --- a/firmware/tests/settings_parse_tests.cc +++ b/firmware/tests/settings_parse_tests.cc @@ -44,6 +44,9 @@ TEST_CASE("Parse settings file") { - Plugin One - Plugin Two + last_patch_opened: '/somedir/SomePatch.yml' + last_patch_vol: 1 + )"; // clang format-on @@ -78,6 +81,9 @@ TEST_CASE("Parse settings file") { CHECK(settings.plugin_autoload.slug.at(0) == "Plugin One"); CHECK(settings.plugin_autoload.slug.at(1) == "Plugin Two"); + + CHECK(settings.last_patch_opened == "/somedir/SomePatch.yml"); + CHECK(settings.last_patch_vol == MetaModule::Volume::SDCard); } TEST_CASE("Get default settings if file is missing fields") { @@ -166,6 +172,9 @@ TEST_CASE("Get default settings if file is missing fields") { CHECK(settings.audio.block_size == 64); CHECK(settings.plugin_autoload.slug.size() == 0); + + CHECK(settings.last_patch_opened == ""); + CHECK(settings.last_patch_vol == MetaModule::Volume::NorFlash); } TEST_CASE("Serialize settings") { @@ -200,6 +209,9 @@ TEST_CASE("Serialize settings") { settings.plugin_autoload.slug.emplace_back("Plugin One"); settings.plugin_autoload.slug.emplace_back("Plugin Two"); + settings.last_patch_vol = MetaModule::Volume::SDCard; + settings.last_patch_opened = "SomePatch.yml"; + // clang format-off std::string expected = R"(Settings: patch_view: @@ -234,6 +246,8 @@ TEST_CASE("Serialize settings") { plugin_autoload: - Plugin One - Plugin Two + last_patch_opened: SomePatch.yml + last_patch_vol: 1 )"; // clang format-on