From 42affaef24419aab0b783f21709b3f8bd4675ca8 Mon Sep 17 00:00:00 2001 From: Maksim Lin Date: Wed, 12 Feb 2025 18:19:16 +1100 Subject: [PATCH] autosave proj every 2min if sequencer not running also improve code docs for mainloop, events and clock tick event --- docs/DEV.md | 15 ++++++++++- .../gui/picoTrackerEventManager.cpp | 7 ++--- .../gui/picoTrackerGUIWindowImp.cpp | 1 + sources/Application/AppWindow.cpp | 27 +++++++++++++++++++ sources/Application/AppWindow.h | 4 +++ .../Persistency/PersistencyService.cpp | 10 +++++-- 6 files changed, 58 insertions(+), 6 deletions(-) diff --git a/docs/DEV.md b/docs/DEV.md index f1d2b254..329e9593 100644 --- a/docs/DEV.md +++ b/docs/DEV.md @@ -95,4 +95,17 @@ A helpful tool for debugging MIDI output is [ShowMIDI](https://github.com/gbevin ### USB MIDI output -TODO \ No newline at end of file +TODO + +## Notes on code structure + +### Key classes + +Some classes are worth documenting specifically here as they contain key pieces of functionality. + +`sources/Adapters/picoTracker/gui/picoTrackerEventManager.cpp` + +`picoTrackerEventManager` is important because it's where `MainLoop()` is and hence it's where "events" are dispatched. +It is also here that the 1ms tick timer callback is set up and the callback function for it is found: `timerHandler()`. The `timerHandler()` function is then the source of the `PICO_CLOCK` events, which are then dispatched to the `picoTrackerEventQueue` at the rate of 1Hz. + +`AppWindow::AnimationUpdate()` is the handy spot where we handle calling autosave just because of the convienence of the `AppWindow` class having easy access to the player, the current project name and the persistance service. \ No newline at end of file diff --git a/sources/Adapters/picoTracker/gui/picoTrackerEventManager.cpp b/sources/Adapters/picoTracker/gui/picoTrackerEventManager.cpp index 709e2673..ed33e77d 100644 --- a/sources/Adapters/picoTracker/gui/picoTrackerEventManager.cpp +++ b/sources/Adapters/picoTracker/gui/picoTrackerEventManager.cpp @@ -31,9 +31,12 @@ picoTrackerEventQueue *queue; char inBuffer[INPUT_BUFFER_SIZE]; #endif +// timer callback at a rate of 1kHz (from a 1ms hardware interrupt timer) bool timerHandler(repeating_timer_t *rt) { queue = picoTrackerEventQueue::GetInstance(); gTime_++; + + // send a clock (PICO_CLOCK) event once every second if (gTime_ % 1000 == 0) { queue->push(picoTrackerEvent(PICO_CLOCK)); } @@ -58,9 +61,7 @@ bool picoTrackerEventManager::Init() { keyboardCS_ = new KeyboardControllerSource("keyboard"); - // TODO: fix this, there is a timer service that should be used. Also all of - // this keyRepeat logic is already implemented in the eventdispatcher - // Application/Commands/EventDispatcher.cpp + // setup a repeating timer for 1ms ticks add_repeating_timer_ms(1, timerHandler, NULL, &timer_); return true; } diff --git a/sources/Adapters/picoTracker/gui/picoTrackerGUIWindowImp.cpp b/sources/Adapters/picoTracker/gui/picoTrackerGUIWindowImp.cpp index be9e27b0..53403fc5 100644 --- a/sources/Adapters/picoTracker/gui/picoTrackerGUIWindowImp.cpp +++ b/sources/Adapters/picoTracker/gui/picoTrackerGUIWindowImp.cpp @@ -2,6 +2,7 @@ #include "Adapters/picoTracker/utils/utils.h" #include "Application/Model/Config.h" #include "Application/Utils/char.h" +#include "Player/Player.h" #include "System/Console/Trace.h" #include "System/System/System.h" #include "UIFramework/BasicDatas/GUIEvent.h" diff --git a/sources/Application/AppWindow.cpp b/sources/Application/AppWindow.cpp index 7ac0f1ed..c38395cb 100644 --- a/sources/Application/AppWindow.cpp +++ b/sources/Application/AppWindow.cpp @@ -16,6 +16,8 @@ #include #include +const u_int16_t AUTOSAVE_INTERVAL_IN_SECONDS = 2 * 60; + AppWindow *instance = 0; unsigned char AppWindow::_charScreen[SCREEN_CHARS]; @@ -495,6 +497,18 @@ void AppWindow::AnimationUpdate() { loadProject_ = false; } _currentView->AnimationUpdate(); + + // *attempt* to auto save every AUTOSAVE_INTERVAL_IN_MILLIS + // will return false if auto save was unsuccessful because eg. the sequencer + // is running + // we do this here because for sheer convenience because this + // callback is called every second and we have easy access in this class to + // the player, projectname and persistence service + if (++lastAutoSave > AUTOSAVE_INTERVAL_IN_SECONDS) { + if (autoSave()) { + lastAutoSave = 0; + } + } } void AppWindow::LayoutChildren(){}; @@ -616,3 +630,16 @@ void AppWindow::Print(char *line) { }; void AppWindow::SetColor(ColorDefinition cd) { colorIndex_ = cd; }; + +bool AppWindow::autoSave() { + Player *player = Player::GetInstance(); + // only auto save when sequencer is not running + if (!player->IsRunning()) { + Trace::Log("APPWINDOW", "AutoSaving Project Data"); + // get persistence service and call autosave + PersistencyService *ps = PersistencyService::GetInstance(); + ps->AutoSaveProjectData(projectName_); + return true; + } + return false; +} \ No newline at end of file diff --git a/sources/Application/AppWindow.h b/sources/Application/AppWindow.h index 2726ff0a..0d33db05 100644 --- a/sources/Application/AppWindow.h +++ b/sources/Application/AppWindow.h @@ -75,6 +75,8 @@ class AppWindow : public GUIWindow, I_Observer, Status { void onQuitApp(); private: + bool autoSave(); + View *_currentView; ViewData *_viewData; SongView *_songView; @@ -120,6 +122,8 @@ class AppWindow : public GUIWindow, I_Observer, Status { SysMutex drawMutex_; bool loadProject_ = false; + + uint32_t lastAutoSave = 0; }; #endif diff --git a/sources/Application/Persistency/PersistencyService.cpp b/sources/Application/Persistency/PersistencyService.cpp index c1be7933..30a83119 100644 --- a/sources/Application/Persistency/PersistencyService.cpp +++ b/sources/Application/Persistency/PersistencyService.cpp @@ -133,11 +133,15 @@ PersistencyResult PersistencyService::SaveProjectData(const char *projectName, fp->Close(); delete (fp); - // if *not* doing auto save, then we need to delete the existing autosave + // if we are doing an explicit save (ie nto a autosave), then we need to + // delete the existing autosave file so that this explicit save will be loaded + // in case subsequent autosave has changes the user doesn't want to keep if (!autosave) { etl::vector segments = {PROJECTS_DIR, projectName, AUTO_SAVE_FILENAME}; CreatePath(pathBufferA, segments); picoFS->DeleteFile(pathBufferA.c_str()); + Trace::Log("PERSISTENCYSERVICE", "Deleted Autosave File: %s\n", + pathBufferA.c_str()); } return PERSIST_SAVED; @@ -228,7 +232,9 @@ void PersistencyService::CreatePath( path.clear(); // iterate over segments and concatenate using iterator for (auto it = segments.begin(); it != segments.end(); ++it) { - path.append("/"); path.append(*it); + if (it != segments.end() - 1) { + path.append("/"); + } } }