use std::collections::HashMap; use std::path::PathBuf; use std::sync::Arc; use crate::engine::{LinkState, PatternChange, SequencerSnapshot}; use crate::model; use crate::settings::Settings; use crate::state::StagedChange; use super::App; impl App { pub fn save_settings(&self, link: &LinkState) { let settings = Settings { audio: crate::settings::AudioSettings { output_device: self.audio.config.output_device.clone(), input_device: self.audio.config.input_device.clone(), channels: self.audio.config.channels, buffer_size: self.audio.config.buffer_size, max_voices: self.audio.config.max_voices, }, display: crate::settings::DisplaySettings { fps: self.audio.config.refresh_rate.to_fps(), runtime_highlight: self.ui.runtime_highlight, show_scope: self.audio.config.show_scope, show_spectrum: self.audio.config.show_spectrum, show_preview: self.audio.config.show_preview, show_completion: self.ui.show_completion, performance_mode: self.ui.performance_mode, color_scheme: self.ui.color_scheme, layout: self.audio.config.layout, hue_rotation: self.ui.hue_rotation, onboarding_dismissed: self.ui.onboarding_dismissed.clone(), font: self.ui.font.clone(), zoom_factor: self.ui.zoom_factor, }, link: crate::settings::LinkSettings { enabled: link.is_enabled(), tempo: link.tempo(), quantum: link.quantum(), }, midi: crate::settings::MidiSettings { output_devices: { let outputs = crate::midi::list_midi_outputs(); self.midi .selected_outputs .iter() .map(|opt| { opt.and_then(|idx| outputs.get(idx).map(|d| d.name.clone())) .unwrap_or_default() }) .collect() }, input_devices: { let inputs = crate::midi::list_midi_inputs(); self.midi .selected_inputs .iter() .map(|opt| { opt.and_then(|idx| inputs.get(idx).map(|d| d.name.clone())) .unwrap_or_default() }) .collect() }, }, }; settings.save(); } 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 .set_status(format!("Saved: {}", final_path.display())); self.project_state.file_path = Some(final_path); } Err(e) => { self.ui.set_status(format!("Save error: {e}")); } } } pub fn load(&mut self, path: PathBuf, link: &LinkState) { 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.undo.clear(); self.variables.store(Arc::new(HashMap::new())); self.dict.lock().clear(); self.evaluate_prelude(link); 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::PhaseLock, }); } self.ui.set_status(format!("Loaded: {}", path.display())); self.project_state.file_path = Some(path); } Err(e) => { self.ui.set_status(format!("Load error: {e}")); } } } }