From efacda2976d35d268d1cda900528eee40b44548a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Forment?= Date: Mon, 2 Feb 2026 01:01:01 +0100 Subject: [PATCH] Feat: more predictable projet load behavior --- crates/project/src/file.rs | 4 ++++ crates/project/src/project.rs | 3 +++ src/app.rs | 24 ++++++++++++++++++++++-- src/input.rs | 1 + src/state/playback.rs | 5 +++++ 5 files changed, 35 insertions(+), 2 deletions(-) diff --git a/crates/project/src/file.rs b/crates/project/src/file.rs index 1ad2ac5..18d079d 100644 --- a/crates/project/src/file.rs +++ b/crates/project/src/file.rs @@ -25,6 +25,8 @@ struct ProjectFile { sample_paths: Vec, #[serde(default = "default_tempo")] tempo: f64, + #[serde(default)] + playing_patterns: Vec<(usize, usize)>, } fn default_tempo() -> f64 { @@ -38,6 +40,7 @@ impl From<&Project> for ProjectFile { banks: project.banks.clone(), sample_paths: project.sample_paths.clone(), tempo: project.tempo, + playing_patterns: project.playing_patterns.clone(), } } } @@ -48,6 +51,7 @@ impl From for Project { banks: file.banks, sample_paths: file.sample_paths, tempo: file.tempo, + playing_patterns: file.playing_patterns, }; project.normalize(); project diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 59cc4d7..53d9cbb 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -450,6 +450,8 @@ pub struct Project { pub sample_paths: Vec, #[serde(default = "default_tempo")] pub tempo: f64, + #[serde(default)] + pub playing_patterns: Vec<(usize, usize)>, } fn default_tempo() -> f64 { @@ -462,6 +464,7 @@ impl Default for Project { banks: (0..MAX_BANKS).map(|_| Bank::default()).collect(), sample_paths: Vec::new(), tempo: default_tempo(), + playing_patterns: Vec::new(), } } } diff --git a/src/app.rs b/src/app.rs index 37be45a..71b245a 100644 --- a/src/app.rs +++ b/src/app.rs @@ -527,10 +527,15 @@ impl App { self.load_step_to_editor(); } - pub fn save(&mut self, path: PathBuf, link: &LinkState) { + pub fn save(&mut self, path: PathBuf, link: &LinkState, snapshot: &SequencerSnapshot) { self.save_editor_to_step(); self.project_state.project.sample_paths = self.audio.config.sample_paths.clone(); self.project_state.project.tempo = link.tempo(); + self.project_state.project.playing_patterns = snapshot + .active_patterns + .iter() + .map(|p| (p.bank, p.pattern)) + .collect(); match model::save(&self.project_state.project, &path) { Ok(final_path) => { self.ui @@ -547,12 +552,27 @@ impl App { match model::load(&path) { Ok(project) => { let tempo = project.tempo; + let playing = project.playing_patterns.clone(); + self.project_state.project = project; self.editor_ctx.step = 0; self.load_step_to_editor(); self.compile_all_steps(link); self.mark_all_patterns_dirty(); link.set_tempo(tempo); + + self.playback.clear_queues(); + self.variables.lock().unwrap().clear(); + self.dict.lock().unwrap().clear(); + + for (bank, pattern) in playing { + self.playback.queued_changes.push(StagedChange { + change: PatternChange::Start { bank, pattern }, + quantization: crate::model::LaunchQuantization::Immediate, + sync_mode: crate::model::SyncMode::Reset, + }); + } + self.ui.set_status(format!("Loaded: {}", path.display())); self.project_state.file_path = Some(path); } @@ -1103,7 +1123,7 @@ impl App { } self.project_state.mark_dirty(bank, pattern); } - AppCommand::Save(path) => self.save(path, link), + AppCommand::Save(path) => self.save(path, link, snapshot), AppCommand::Load(path) => self.load(path, link), // UI diff --git a/src/input.rs b/src/input.rs index 2fb0ff1..3d3fac6 100644 --- a/src/input.rs +++ b/src/input.rs @@ -258,6 +258,7 @@ fn handle_modal_input(ctx: &mut InputContext, key: KeyEvent) -> InputResult { match mode { FileBrowserMode::Save => ctx.dispatch(AppCommand::Save(path)), FileBrowserMode::Load => { + let _ = ctx.seq_cmd_tx.send(SeqCommand::StopAll); ctx.dispatch(AppCommand::Load(path)); load_project_samples(ctx); } diff --git a/src/state/playback.rs b/src/state/playback.rs index 6afef2b..e84f0af 100644 --- a/src/state/playback.rs +++ b/src/state/playback.rs @@ -28,4 +28,9 @@ impl PlaybackState { pub fn toggle(&mut self) { self.playing = !self.playing; } + + pub fn clear_queues(&mut self) { + self.staged_changes.clear(); + self.queued_changes.clear(); + } }