audio-oxide/au-o2-gui/src/editor/undo.rs

316 lines
17 KiB
Rust

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(&region.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 });
}
}
}
}