use super::{decode_region_audio, Editor}; use crate::engine::EngineCommand; use crate::history::EditCommand; use crate::waveform::WaveformPeaks; impl Editor { pub(crate) fn perform_undo(&mut self) { let cmd = match self.history.pop_undo() { Some(c) => c, None => return, }; match cmd { EditCommand::MoveRegion { track_index, region_id, old_start, new_start, old_start_sample, new_start_sample } => { debug_log!("undo MoveRegion: region={} track={}", region_id, track_index); if let Some(track) = self.tracks.get_mut(track_index) { if let Some(region) = track.regions.iter_mut().find(|r| r.id == region_id) { region.start_time = old_start; region.start_sample = old_start_sample; } } self.history.push_redo(EditCommand::MoveRegion { track_index, region_id, old_start: new_start, new_start: old_start, old_start_sample: new_start_sample, new_start_sample: old_start_sample, }); } EditCommand::MoveRegionAcrossTracks { region_id, old_track, new_track, old_start, new_start, old_start_sample, new_start_sample } => { debug_log!("undo cross-track move: region={} track {}->{}", region_id, new_track, old_track); if new_track < self.tracks.len() { if let Some(pos) = self.tracks[new_track].regions.iter().position(|r| r.id == region_id) { let mut region = self.tracks[new_track].regions.remove(pos); region.start_time = old_start; region.start_sample = old_start_sample; if old_track < self.tracks.len() { if let Some(ref engine) = self.engine { engine.send(EngineCommand::UnloadRegionAudio { region_id }); if let Some(ref audio_file) = region.audio_file { let abs_path = self.project_path.join(audio_file); if let Some((audio_l, audio_r)) = decode_region_audio(&abs_path, self.project_config.sample_rate) { engine.send(EngineCommand::LoadRegionAudio { bus_name: self.tracks[old_track].bus_name.clone(), region_id, start_sample: old_start_sample, audio_l, audio_r, fade_in_samples: region.fade_in_samples, fade_out_samples: region.fade_out_samples, }); } } } self.tracks[old_track].regions.push(region); } } } self.history.push_redo(EditCommand::MoveRegionAcrossTracks { region_id, old_track: new_track, new_track: old_track, old_start: new_start, new_start: old_start, old_start_sample: new_start_sample, new_start_sample: old_start_sample, }); } EditCommand::DeleteRegion { track_index, region } => { debug_log!("undo DeleteRegion: region={} track={}", region.id, track_index); let region_clone = region.clone(); if track_index < self.tracks.len() { if let Some(ref audio_file) = region.audio_file { let abs_path = self.project_path.join(audio_file); if let Some((audio_l, audio_r)) = decode_region_audio(&abs_path, self.project_config.sample_rate) { let start = (region.start_sample as usize).min(audio_l.len()); let end = (start + region.length_samples as usize).min(audio_l.len()); let sl = &audio_l[start..end]; let sr = &audio_r[start.min(audio_r.len())..end.min(audio_r.len())]; self.waveform_cache.insert( region.id, WaveformPeaks::from_stereo(sl, sr), ); if let Some(ref engine) = self.engine { engine.send(EngineCommand::LoadRegionAudio { bus_name: self.tracks[track_index].bus_name.clone(), region_id: region.id, start_sample: region.start_sample, audio_l: sl.to_vec(), audio_r: sr.to_vec(), fade_in_samples: region.fade_in_samples, fade_out_samples: region.fade_out_samples, }); } } } self.tracks[track_index].regions.push(region); } self.history.push_redo(EditCommand::DeleteRegion { track_index, region: region_clone, }); } EditCommand::DeleteTrack { index, track } => { debug_log!("undo DeleteTrack: index={}", index); let track_clone = track.clone(); if index <= self.tracks.len() { if let Some(ref engine) = self.engine { engine.send(EngineCommand::CreateBus { name: track.bus_name.clone(), is_midi: track.track_type == crate::track::TrackType::Midi, }); } self.tracks.insert(index, track); } self.history.push_redo(EditCommand::DeleteTrack { index, track: track_clone, }); } EditCommand::CreateTrack { index } => { debug_log!("undo CreateTrack: index={}", index); if index < self.tracks.len() { let removed = self.tracks.remove(index); if let Some(ref engine) = self.engine { engine.send(EngineCommand::RemoveBus { name: removed.bus_name.clone(), }); } self.history.push_redo(EditCommand::CreateTrack { index }); } } EditCommand::DuplicateTrack { source_index, new_index } => { debug_log!("undo DuplicateTrack: source={} new={}", source_index, new_index); if new_index < self.tracks.len() { let removed = self.tracks.remove(new_index); if let Some(ref engine) = self.engine { engine.send(EngineCommand::RemoveBus { name: removed.bus_name.clone(), }); } } self.history.push_redo(EditCommand::DuplicateTrack { source_index, new_index }); } EditCommand::SplitRegion { track_index, original_id, original_region, left_id, right_id, split_sample } => { debug_log!("undo SplitRegion: original={} track={}", original_id, track_index); if let Some(track) = self.tracks.get_mut(track_index) { track.regions.retain(|r| r.id != left_id && r.id != right_id); self.waveform_cache.remove(&left_id); self.waveform_cache.remove(&right_id); if let Some(ref engine) = self.engine { engine.send(EngineCommand::UnloadRegionAudio { region_id: left_id }); engine.send(EngineCommand::UnloadRegionAudio { region_id: right_id }); } let orig_clone = original_region.clone(); if let Some(ref audio_file) = original_region.audio_file { let abs_path = self.project_path.join(audio_file); if let Ok(decoder) = crate::codec::XtcDecoder::open(&abs_path) { if let Ok((audio_l, audio_r)) = decoder.decode_real(&abs_path) { let start = original_region.start_sample as usize; let end = (start + original_region.length_samples as usize).min(audio_l.len()); let sl = &audio_l[start.min(audio_l.len())..end]; let sr = &audio_r[start.min(audio_r.len())..end.min(audio_r.len())]; self.waveform_cache.insert( original_id, WaveformPeaks::from_stereo(sl, sr), ); if let Some(ref engine) = self.engine { engine.send(EngineCommand::LoadRegionAudio { bus_name: track.bus_name.clone(), region_id: original_id, start_sample: original_region.start_sample, audio_l: audio_l[start.min(audio_l.len())..end].to_vec(), audio_r: audio_r[start.min(audio_r.len())..end.min(audio_r.len())].to_vec(), fade_in_samples: original_region.fade_in_samples, fade_out_samples: original_region.fade_out_samples, }); } } } } track.regions.push(original_region); self.history.push_redo(EditCommand::SplitRegion { track_index, original_id, original_region: orig_clone, left_id, right_id, split_sample, }); } } EditCommand::PasteRegions { entries } => { debug_log!("undo PasteRegions: {} regions", entries.len()); let entries_clone = entries.clone(); for (track_index, region) in &entries { if *track_index < self.tracks.len() { self.tracks[*track_index].regions.retain(|r| r.id != region.id); self.waveform_cache.remove(®ion.id); if let Some(ref engine) = self.engine { engine.send(EngineCommand::UnloadRegionAudio { region_id: region.id }); } } } self.history.push_redo(EditCommand::PasteRegions { entries: entries_clone }); } EditCommand::CutRegions { entries } => { debug_log!("undo CutRegions: {} regions", entries.len()); let entries_clone = entries.clone(); for (track_index, region) in &entries { if *track_index < self.tracks.len() { if let Some(ref audio_file) = region.audio_file { let abs_path = self.project_path.join(audio_file); if let Ok(decoder) = crate::codec::XtcDecoder::open(&abs_path) { if let Ok((audio_l, audio_r)) = decoder.decode_real(&abs_path) { let start = (region.start_sample as usize).min(audio_l.len()); let end = (start + region.length_samples as usize).min(audio_l.len()); let sl = &audio_l[start..end]; let sr = &audio_r[start.min(audio_r.len())..end.min(audio_r.len())]; self.waveform_cache.insert( region.id, WaveformPeaks::from_stereo(sl, sr), ); if let Some(ref engine) = self.engine { engine.send(EngineCommand::LoadRegionAudio { bus_name: self.tracks[*track_index].bus_name.clone(), region_id: region.id, start_sample: region.start_sample, audio_l: sl.to_vec(), audio_r: sr.to_vec(), fade_in_samples: region.fade_in_samples, fade_out_samples: region.fade_out_samples, }); } } } } self.tracks[*track_index].regions.push(region.clone()); } } self.history.push_redo(EditCommand::CutRegions { entries: entries_clone }); } EditCommand::AudioQuantize { track_index, original_region, result_regions } => { if track_index < self.tracks.len() { for r in &result_regions { self.tracks[track_index].regions.retain(|rr| rr.id != r.id); self.waveform_cache.remove(&r.id); if let Some(ref engine) = self.engine { engine.send(EngineCommand::UnloadRegionAudio { region_id: r.id }); } } let orig = original_region.clone(); if let Some(ref audio_file) = orig.audio_file { let abs_path = self.project_path.join(audio_file); if let Some((audio_l, audio_r)) = decode_region_audio(&abs_path, self.project_config.sample_rate) { let s = (orig.start_sample as usize).min(audio_l.len()); let e = (s + orig.length_samples as usize).min(audio_l.len()); let sl = &audio_l[s..e]; let sr = &audio_r[s.min(audio_r.len())..e.min(audio_r.len())]; self.waveform_cache.insert(orig.id, WaveformPeaks::from_stereo(sl, sr)); if let Some(ref engine) = self.engine { engine.send(EngineCommand::LoadRegionAudio { bus_name: self.tracks[track_index].bus_name.clone(), region_id: orig.id, start_sample: orig.start_sample, audio_l: sl.to_vec(), audio_r: sr.to_vec(), fade_in_samples: orig.fade_in_samples, fade_out_samples: orig.fade_out_samples, }); } } } self.tracks[track_index].regions.push(orig); } self.history.push_redo(EditCommand::AudioQuantize { track_index, original_region, result_regions, }); } EditCommand::SetTempo { old_tempo, new_tempo, old_tempo_map, new_tempo_map } => { self.tempo = old_tempo; self.project_config.tempo = old_tempo; self.tempo_map = old_tempo_map.clone(); self.sync_tempo_to_engine(); self.history.push_redo(EditCommand::SetTempo { old_tempo: new_tempo, new_tempo: old_tempo, old_tempo_map: new_tempo_map, new_tempo_map: old_tempo_map, }); } EditCommand::SplitStems { track_indices } => { for &idx in track_indices.iter().rev() { if idx < self.tracks.len() { let removed = self.tracks.remove(idx); for r in &removed.regions { self.waveform_cache.remove(&r.id); if let Some(ref engine) = self.engine { engine.send(EngineCommand::UnloadRegionAudio { region_id: r.id }); } } if let Some(ref engine) = self.engine { engine.send(EngineCommand::RemoveBus { name: removed.bus_name.clone() }); } } } self.history.push_redo(EditCommand::SplitStems { track_indices }); } } } }