Feat: more predictable projet load behavior

This commit is contained in:
2026-02-02 01:01:01 +01:00
parent ccce0df79d
commit efacda2976
5 changed files with 35 additions and 2 deletions

View File

@@ -25,6 +25,8 @@ struct ProjectFile {
sample_paths: Vec<PathBuf>, sample_paths: Vec<PathBuf>,
#[serde(default = "default_tempo")] #[serde(default = "default_tempo")]
tempo: f64, tempo: f64,
#[serde(default)]
playing_patterns: Vec<(usize, usize)>,
} }
fn default_tempo() -> f64 { fn default_tempo() -> f64 {
@@ -38,6 +40,7 @@ impl From<&Project> for ProjectFile {
banks: project.banks.clone(), banks: project.banks.clone(),
sample_paths: project.sample_paths.clone(), sample_paths: project.sample_paths.clone(),
tempo: project.tempo, tempo: project.tempo,
playing_patterns: project.playing_patterns.clone(),
} }
} }
} }
@@ -48,6 +51,7 @@ impl From<ProjectFile> for Project {
banks: file.banks, banks: file.banks,
sample_paths: file.sample_paths, sample_paths: file.sample_paths,
tempo: file.tempo, tempo: file.tempo,
playing_patterns: file.playing_patterns,
}; };
project.normalize(); project.normalize();
project project

View File

@@ -450,6 +450,8 @@ pub struct Project {
pub sample_paths: Vec<PathBuf>, pub sample_paths: Vec<PathBuf>,
#[serde(default = "default_tempo")] #[serde(default = "default_tempo")]
pub tempo: f64, pub tempo: f64,
#[serde(default)]
pub playing_patterns: Vec<(usize, usize)>,
} }
fn default_tempo() -> f64 { fn default_tempo() -> f64 {
@@ -462,6 +464,7 @@ impl Default for Project {
banks: (0..MAX_BANKS).map(|_| Bank::default()).collect(), banks: (0..MAX_BANKS).map(|_| Bank::default()).collect(),
sample_paths: Vec::new(), sample_paths: Vec::new(),
tempo: default_tempo(), tempo: default_tempo(),
playing_patterns: Vec::new(),
} }
} }
} }

View File

@@ -527,10 +527,15 @@ impl App {
self.load_step_to_editor(); 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.save_editor_to_step();
self.project_state.project.sample_paths = self.audio.config.sample_paths.clone(); self.project_state.project.sample_paths = self.audio.config.sample_paths.clone();
self.project_state.project.tempo = link.tempo(); 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) { match model::save(&self.project_state.project, &path) {
Ok(final_path) => { Ok(final_path) => {
self.ui self.ui
@@ -547,12 +552,27 @@ impl App {
match model::load(&path) { match model::load(&path) {
Ok(project) => { Ok(project) => {
let tempo = project.tempo; let tempo = project.tempo;
let playing = project.playing_patterns.clone();
self.project_state.project = project; self.project_state.project = project;
self.editor_ctx.step = 0; self.editor_ctx.step = 0;
self.load_step_to_editor(); self.load_step_to_editor();
self.compile_all_steps(link); self.compile_all_steps(link);
self.mark_all_patterns_dirty(); self.mark_all_patterns_dirty();
link.set_tempo(tempo); 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.ui.set_status(format!("Loaded: {}", path.display()));
self.project_state.file_path = Some(path); self.project_state.file_path = Some(path);
} }
@@ -1103,7 +1123,7 @@ impl App {
} }
self.project_state.mark_dirty(bank, pattern); 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), AppCommand::Load(path) => self.load(path, link),
// UI // UI

View File

@@ -258,6 +258,7 @@ fn handle_modal_input(ctx: &mut InputContext, key: KeyEvent) -> InputResult {
match mode { match mode {
FileBrowserMode::Save => ctx.dispatch(AppCommand::Save(path)), FileBrowserMode::Save => ctx.dispatch(AppCommand::Save(path)),
FileBrowserMode::Load => { FileBrowserMode::Load => {
let _ = ctx.seq_cmd_tx.send(SeqCommand::StopAll);
ctx.dispatch(AppCommand::Load(path)); ctx.dispatch(AppCommand::Load(path));
load_project_samples(ctx); load_project_samples(ctx);
} }

View File

@@ -28,4 +28,9 @@ impl PlaybackState {
pub fn toggle(&mut self) { pub fn toggle(&mut self) {
self.playing = !self.playing; self.playing = !self.playing;
} }
pub fn clear_queues(&mut self) {
self.staged_changes.clear();
self.queued_changes.clear();
}
} }