diff --git a/firmware/Euclidean/Euclidean.ino b/firmware/Euclidean/Euclidean.ino index 6a497e2..f0fdc54 100644 --- a/firmware/Euclidean/Euclidean.ino +++ b/firmware/Euclidean/Euclidean.ino @@ -244,8 +244,16 @@ void HandleEncoderPressed() { if (app.selected_param == PARAM_MAIN_LOAD_DATA) { if (app.selected_sub_param < StateManager::MAX_SAVE_SLOTS) { app.selected_save_slot = app.selected_sub_param; + // Load pattern data into app state. stateManager.loadData(app, app.selected_save_slot); - InitGravity(app); + // Load global performance settings if they have changed. + if (gravity.clock.Tempo() != app.tempo) { + gravity.clock.SetTempo(app.tempo); + } + // Load global settings only if clock is not active. + if (gravity.clock.IsPaused()) { + InitGravity(app); + } } } if (app.selected_param == PARAM_MAIN_RESET_STATE) { diff --git a/firmware/Euclidean/save_state.cpp b/firmware/Euclidean/save_state.cpp index 8045206..283739c 100644 --- a/firmware/Euclidean/save_state.cpp +++ b/firmware/Euclidean/save_state.cpp @@ -35,20 +35,23 @@ const int StateManager::EEPROM_DATA_START_ADDR = sizeof(StateManager::Metadata); StateManager::StateManager() : _isDirty(false), _lastChangeTime(0) {} bool StateManager::initialize(AppState &app) { + noInterrupts(); + bool success = false; if (_isDataValid()) { // Load global settings. _loadMetadata(app); // Load app data from the transient slot. _loadState(app, TRANSIENT_SLOT); - return true; + success = true; } // EEPROM does not contain save data for this firmware & version. else { // Erase EEPROM and initialize state. Save default pattern to all save // slots. factoryReset(app); - return false; } + interrupts(); + return success; } bool StateManager::loadData(AppState &app, byte slot_index) { @@ -56,38 +59,49 @@ bool StateManager::loadData(AppState &app, byte slot_index) { if (slot_index >= MAX_SAVE_SLOTS + 1) return false; + noInterrupts(); + // Load the state data from the specified EEPROM slot and update the app state // save slot. _loadState(app, slot_index); app.selected_save_slot = slot_index; - // Persist this change in the global metadata. - _saveMetadata(app); + // Persist this change in the global metadata on next update. + _isDirty = true; + interrupts(); return true; } // Save app state to user specified save slot. void StateManager::saveData(const AppState &app) { + noInterrupts(); // Check if slot_index is within max range + 1 for transient. - if (app.selected_save_slot >= MAX_SAVE_SLOTS + 1) + if (app.selected_save_slot >= MAX_SAVE_SLOTS + 1) { + interrupts(); return; + } _saveState(app, app.selected_save_slot); _saveMetadata(app); _isDirty = false; + interrupts(); } // Save transient state if it has changed and enough time has passed since last // save. void StateManager::update(const AppState &app) { if (_isDirty && (millis() - _lastChangeTime > SAVE_DELAY_MS)) { + noInterrupts(); _saveState(app, TRANSIENT_SLOT); _saveMetadata(app); _isDirty = false; + interrupts(); } } void StateManager::reset(AppState &app) { + noInterrupts(); + AppState default_app; app.tempo = default_app.tempo; app.selected_param = default_app.selected_param; @@ -105,6 +119,7 @@ void StateManager::reset(AppState &app) { _loadMetadata(app); _isDirty = false; + interrupts(); } void StateManager::markDirty() { @@ -142,7 +157,6 @@ void StateManager::_saveState(const AppState &app, byte slot_index) { if (app.selected_save_slot >= MAX_SAVE_SLOTS + 1) return; - noInterrupts(); static EepromData save_data; save_data.tempo = app.tempo; @@ -169,7 +183,6 @@ void StateManager::_saveState(const AppState &app, byte slot_index) { int address = EEPROM_DATA_START_ADDR + (slot_index * sizeof(EepromData)); EEPROM.put(address, save_data); - interrupts(); } void StateManager::_loadState(AppState &app, byte slot_index) { @@ -177,7 +190,6 @@ void StateManager::_loadState(AppState &app, byte slot_index) { if (slot_index >= MAX_SAVE_SLOTS + 1) return; - noInterrupts(); static EepromData load_data; int address = EEPROM_DATA_START_ADDR + (slot_index * sizeof(EepromData)); EEPROM.get(address, load_data); @@ -201,11 +213,9 @@ void StateManager::_loadState(AppState &app, byte slot_index) { ch.setCv1Dest(static_cast(saved_ch_state.cv1_dest)); ch.setCv2Dest(static_cast(saved_ch_state.cv2_dest)); } - interrupts(); } void StateManager::_saveMetadata(const AppState &app) { - noInterrupts(); Metadata current_meta; strcpy(current_meta.sketch_name, SKETCH_NAME); strcpy(current_meta.version, SEMANTIC_VERSION); @@ -216,15 +226,12 @@ void StateManager::_saveMetadata(const AppState &app) { current_meta.rotate_display = app.rotate_display; EEPROM.put(METADATA_START_ADDR, current_meta); - interrupts(); } void StateManager::_loadMetadata(AppState &app) { - noInterrupts(); Metadata metadata; EEPROM.get(METADATA_START_ADDR, metadata); app.selected_save_slot = metadata.selected_save_slot; app.encoder_reversed = metadata.encoder_reversed; app.rotate_display = metadata.rotate_display; - interrupts(); } \ No newline at end of file diff --git a/firmware/Gravity/Gravity.ino b/firmware/Gravity/Gravity.ino index 31b5805..b4f9b84 100644 --- a/firmware/Gravity/Gravity.ino +++ b/firmware/Gravity/Gravity.ino @@ -244,8 +244,16 @@ void HandleEncoderPressed() { if (app.selected_param == PARAM_MAIN_LOAD_DATA) { if (app.selected_sub_param < StateManager::MAX_SAVE_SLOTS) { app.selected_save_slot = app.selected_sub_param; + // Load pattern data into app state. stateManager.loadData(app, app.selected_save_slot); - InitGravity(app); + // Load global performance settings if they have changed. + if (gravity.clock.Tempo() != app.tempo) { + gravity.clock.SetTempo(app.tempo); + } + // Load global settings only if clock is not active. + if (gravity.clock.IsPaused()) { + InitGravity(app); + } } } if (app.selected_param == PARAM_MAIN_RESET_STATE) { diff --git a/firmware/Gravity/save_state.cpp b/firmware/Gravity/save_state.cpp index d51b89a..6e6c141 100644 --- a/firmware/Gravity/save_state.cpp +++ b/firmware/Gravity/save_state.cpp @@ -35,20 +35,23 @@ const int StateManager::EEPROM_DATA_START_ADDR = sizeof(StateManager::Metadata); StateManager::StateManager() : _isDirty(false), _lastChangeTime(0) {} bool StateManager::initialize(AppState &app) { + noInterrupts(); + bool success = false; if (_isDataValid()) { // Load global settings. _loadMetadata(app); // Load app data from the transient slot. _loadState(app, TRANSIENT_SLOT); - return true; + success = true; } // EEPROM does not contain save data for this firmware & version. else { // Erase EEPROM and initialize state. Save default pattern to all save // slots. factoryReset(app); - return false; } + interrupts(); + return success; } bool StateManager::loadData(AppState &app, byte slot_index) { @@ -56,38 +59,49 @@ bool StateManager::loadData(AppState &app, byte slot_index) { if (slot_index >= MAX_SAVE_SLOTS + 1) return false; + noInterrupts(); + // Load the state data from the specified EEPROM slot and update the app state // save slot. _loadState(app, slot_index); app.selected_save_slot = slot_index; - // Persist this change in the global metadata. - _saveMetadata(app); + // Persist this change in the global metadata on next update. + _isDirty = true; + interrupts(); return true; } // Save app state to user specified save slot. void StateManager::saveData(const AppState &app) { + noInterrupts(); // Check if slot_index is within max range + 1 for transient. - if (app.selected_save_slot >= MAX_SAVE_SLOTS + 1) + if (app.selected_save_slot >= MAX_SAVE_SLOTS + 1) { + interrupts(); return; + } _saveState(app, app.selected_save_slot); _saveMetadata(app); _isDirty = false; + interrupts(); } // Save transient state if it has changed and enough time has passed since last // save. void StateManager::update(const AppState &app) { if (_isDirty && (millis() - _lastChangeTime > SAVE_DELAY_MS)) { + noInterrupts(); _saveState(app, TRANSIENT_SLOT); _saveMetadata(app); _isDirty = false; + interrupts(); } } void StateManager::reset(AppState &app) { + noInterrupts(); + AppState default_app; app.tempo = default_app.tempo; app.selected_param = default_app.selected_param; @@ -105,6 +119,7 @@ void StateManager::reset(AppState &app) { _loadMetadata(app); _isDirty = false; + interrupts(); } void StateManager::markDirty() { @@ -142,7 +157,6 @@ void StateManager::_saveState(const AppState &app, byte slot_index) { if (app.selected_save_slot >= MAX_SAVE_SLOTS + 1) return; - noInterrupts(); static EepromData save_data; save_data.tempo = app.tempo; @@ -170,7 +184,6 @@ void StateManager::_saveState(const AppState &app, byte slot_index) { int address = EEPROM_DATA_START_ADDR + (slot_index * sizeof(EepromData)); EEPROM.put(address, save_data); - interrupts(); } void StateManager::_loadState(AppState &app, byte slot_index) { @@ -178,7 +191,6 @@ void StateManager::_loadState(AppState &app, byte slot_index) { if (slot_index >= MAX_SAVE_SLOTS + 1) return; - noInterrupts(); static EepromData load_data; int address = EEPROM_DATA_START_ADDR + (slot_index * sizeof(EepromData)); EEPROM.get(address, load_data); @@ -203,11 +215,9 @@ void StateManager::_loadState(AppState &app, byte slot_index) { ch.setCv1Dest(static_cast(saved_ch_state.cv1_dest)); ch.setCv2Dest(static_cast(saved_ch_state.cv2_dest)); } - interrupts(); } void StateManager::_saveMetadata(const AppState &app) { - noInterrupts(); Metadata current_meta; strcpy(current_meta.sketch_name, SKETCH_NAME); strcpy(current_meta.version, SEMANTIC_VERSION); @@ -218,15 +228,12 @@ void StateManager::_saveMetadata(const AppState &app) { current_meta.rotate_display = app.rotate_display; EEPROM.put(METADATA_START_ADDR, current_meta); - interrupts(); } void StateManager::_loadMetadata(AppState &app) { - noInterrupts(); Metadata metadata; EEPROM.get(METADATA_START_ADDR, metadata); app.selected_save_slot = metadata.selected_save_slot; app.encoder_reversed = metadata.encoder_reversed; app.rotate_display = metadata.rotate_display; - interrupts(); } \ No newline at end of file