use crate::*; /// Root level object for standalone `tek_arranger` pub struct Arranger { /// Which view is focused pub focus_cursor: (usize, usize), /// Controls the JACK transport. pub transport: Option>>>, /// Contains all the sequencers. pub arrangement: Arrangement, /// Pool of all phrases in the arrangement pub phrases: Arc>>, /// Phrase editor view pub editor: PhraseEditor, /// This allows the sequencer view to be moved or hidden. pub show_sequencer: Option, /// Slot for modal dialog displayed on top of app. pub modal: Option>>, } /// Sections in the arranger app that may be focused #[derive(Copy, Clone, PartialEq, Eq)] pub enum ArrangerFocus { Transport, Arrangement, PhrasePool, PhraseEditor } /// Represents the tracks and scenes of the composition. pub struct Arrangement { /// Name of arranger pub name: Arc>, /// Collection of phrases. pub phrases: Arc>>, /// Collection of tracks. pub tracks: Vec>, /// Collection of scenes. pub scenes: Vec, /// Currently selected element. pub selected: ArrangementFocus, /// Display mode of arranger pub mode: ArrangementViewMode, /// Whether the arranger is currently focused pub focused: bool } /// Represents a track in the arrangement pub struct ArrangementTrack { /// Name of track pub name: Arc>, /// Inputs pub inputs: Vec<()>, /// MIDI player/recorder pub player: PhrasePlayer, /// Outputs pub outputs: Vec<()>, } #[derive(Default)] pub struct Scene { pub name: Arc>, pub clips: Vec>>>, } #[derive(PartialEq, Clone, Copy)] /// Represents the current user selection in the arranger pub enum ArrangementFocus { /** The whole mix is selected */ Mix, /// A track is selected. Track(usize), /// A scene is selected. Scene(usize), /// A clip (track × scene) is selected. Clip(usize, usize), } /// Display mode of arranger #[derive(PartialEq)] pub enum ArrangementViewMode { Horizontal, Vertical(usize), } /// A collection of phrases to play on each track. pub struct VerticalArranger<'a, E: Engine>( pub &'a Arrangement, pub usize ); pub struct VerticalArrangerGrid<'a>( pub u16, pub &'a [(usize, usize)], pub &'a [(usize, usize)] ); pub struct VerticalArrangerCursor<'a>( pub bool, pub ArrangementFocus, pub u16, pub &'a [(usize, usize)], pub &'a [(usize, usize)], ); pub struct HorizontalArranger<'a, E: Engine>( pub &'a Arrangement ); pub struct ArrangerRenameModal { _engine: std::marker::PhantomData, pub done: bool, pub target: ArrangementFocus, pub value: String, pub result: Arc>, pub cursor: usize } /// Focus layout of arranger app impl FocusGrid for Arranger { fn cursor (&self) -> (usize, usize) { self.focus_cursor } fn cursor_mut (&mut self) -> &mut (usize, usize) { &mut self.focus_cursor } fn layout (&self) -> &[&[ArrangerFocus]] { &[ &[ArrangerFocus::Transport], &[ArrangerFocus::Arrangement, ArrangerFocus::Arrangement], &[ArrangerFocus::PhrasePool, ArrangerFocus::PhraseEditor], ] } fn update_focus (&mut self) { let focused = *self.focused(); if let Some(transport) = self.transport.as_ref() { transport.write().unwrap().focused = focused == ArrangerFocus::Transport } self.arrangement.focused = focused == ArrangerFocus::Arrangement; self.phrases.write().unwrap().focused = focused == ArrangerFocus::PhrasePool; self.editor.focused = focused == ArrangerFocus::PhraseEditor; } } /// General methods for arrangement impl Arrangement { pub fn new (name: &str, phrases: &Arc>>) -> Self { Self { name: Arc::new(RwLock::new(name.into())), mode: ArrangementViewMode::Vertical(2), selected: ArrangementFocus::Clip(0, 0), phrases: phrases.clone(), scenes: vec![], tracks: vec![], focused: false } } pub fn activate (&mut self) { match self.selected { ArrangementFocus::Scene(s) => { for (t, track) in self.tracks.iter_mut().enumerate() { track.player.phrase = self.scenes[s].clips[t].clone(); track.player.reset = true; } }, ArrangementFocus::Clip(t, s) => { self.tracks[t].player.phrase = self.scenes[s].clips[t].clone(); self.tracks[t].player.reset = true; }, _ => {} } } pub fn is_first_row (&self) -> bool { let selected = self.selected; selected.is_mix() || selected.is_track() } pub fn is_last_row (&self) -> bool { let selected = self.selected; (self.scenes.len() == 0 && (selected.is_mix() || selected.is_track())) || match selected { ArrangementFocus::Scene(s) => s == self.scenes.len() - 1, ArrangementFocus::Clip(_, s) => s == self.scenes.len() - 1, _ => false } } } /// Methods for tracks in arrangement impl Arrangement { pub fn track (&self) -> Option<&ArrangementTrack> { self.selected.track().map(|t|self.tracks.get(t)).flatten() } pub fn track_mut (&mut self) -> Option<&mut ArrangementTrack> { self.selected.track().map(|t|self.tracks.get_mut(t)).flatten() } pub fn track_next (&mut self) { self.selected.track_next(self.tracks.len() - 1) } pub fn track_prev (&mut self) { self.selected.track_prev() } pub fn track_add (&mut self, name: Option<&str>) -> Usually<&mut ArrangementTrack> { self.tracks.push(name.map_or_else( || ArrangementTrack::new(&self.track_default_name()), |name| ArrangementTrack::new(name), )); let index = self.tracks.len() - 1; Ok(&mut self.tracks[index]) } pub fn track_del (&mut self) { unimplemented!("Arranger::track_del"); } pub fn track_default_name (&self) -> String { format!("Track {}", self.tracks.len() + 1) } pub fn track_widths (&self) -> Vec<(usize, usize)> { let to_len = |track: &ArrangementTrack|track.name.read().unwrap().len(); let mut lens: Vec = self.tracks.iter().map(to_len).collect(); for scene in self.scenes.iter() { for track_index in 0..self.tracks.len() { if let Some(phrase) = scene.clip(track_index) { let len = phrase.read().unwrap().name.read().unwrap().len(); lens[track_index] = lens[track_index].max(len); } } } let mut total = 0; let to_x_and_w = |len: &usize|{ total = total + *len; (*len, total - *len) }; let mut lens: Vec<(usize, usize)> = lens.iter().map(to_x_and_w).collect(); lens.push((0, total)); lens } } /// Methods for scenes in arrangement impl Arrangement { pub fn scene (&self) -> Option<&Scene> { self.selected.scene().map(|s|self.scenes.get(s)).flatten() } pub fn scene_mut (&mut self) -> Option<&mut Scene> { self.selected.scene().map(|s|self.scenes.get_mut(s)).flatten() } pub fn scene_next (&mut self) { self.selected.scene_next(self.scenes.len() - 1) } pub fn scene_prev (&mut self) { self.selected.scene_prev() } pub fn scene_add (&mut self, name: Option<&str>) -> Usually<&mut Scene> { let clips = vec![None;self.tracks.len()]; self.scenes.push(match name { Some(name) => Scene::new(name, clips), None => Scene::new(&self.scene_default_name(), clips), }); let index = self.scenes.len() - 1; Ok(&mut self.scenes[index]) } pub fn scene_del (&mut self) { unimplemented!("Arranger::scene_del"); } pub fn scene_default_name (&self) -> String { format!("Scene {}", self.scenes.len() + 1) } } /// Methods for phrases in arrangement impl Arrangement { pub fn sequencer (&self) -> Option<&ArrangementTrack> { self.selected.track() .map(|track|self.tracks.get(track)) .flatten() } pub fn sequencer_mut (&mut self) -> Option<&mut ArrangementTrack> { self.selected.track() .map(|track|self.tracks.get_mut(track)) .flatten() } pub fn show_phrase (&mut self) { let (scene, track) = (self.selected.scene(), self.selected.track()); if let (Some(scene_index), Some(track_index)) = (scene, track) { let scene = self.scenes.get(scene_index); let track = self.tracks.get_mut(track_index); if let (Some(scene), Some(track)) = (scene, track) { track.player.phrase = scene.clips[track_index].clone() } } } pub fn phrase (&self) -> Option>> { self.scene()?.clips.get(self.selected.track()?)?.clone() } pub fn phrase_del (&mut self) { let track_index = self.selected.track(); let scene_index = self.selected.scene(); track_index .and_then(|index|self.tracks.get_mut(index).map(|track|(index, track))) .map(|(track_index, _)|{ scene_index .and_then(|index|self.scenes.get_mut(index)) .map(|scene|scene.clips[track_index] = None); }); } pub fn phrase_next (&mut self) { todo!(); //let track_index = self.selected.track(); //let scene_index = self.selected.scene(); //track_index //.and_then(|index|self.tracks.get_mut(index).map(|track|(index, track))) //.and_then(|(track_index, track)|{ //let phrases = track.phrases.len(); //scene_index //.and_then(|index|self.scenes.get_mut(index)) //.and_then(|scene|{ //if let Some(phrase_index) = scene.clips[track_index] { //if phrase_index >= phrases - 1 { //scene.clips[track_index] = None; //} else { //scene.clips[track_index] = Some(phrase_index + 1); //} //} else if phrases > 0 { //scene.clips[track_index] = Some(0); //} //Some(()) //}) //}); } pub fn phrase_prev (&mut self) { todo!(); //let track_index = self.selected.track(); //let scene_index = self.selected.scene(); //track_index //.and_then(|index|self.tracks.get_mut(index).map(|track|(index, track))) //.and_then(|(track_index, track)|{ //let phrases = track.phrases.len(); //scene_index //.and_then(|index|self.scenes.get_mut(index)) //.and_then(|scene|{ //if let Some(phrase_index) = scene.clips[track_index] { //scene.clips[track_index] = if phrase_index == 0 { //None //} else { //Some(phrase_index - 1) //}; //} else if phrases > 0 { //scene.clips[track_index] = Some(phrases - 1); //} //Some(()) //}) //}); } } impl ArrangementTrack { pub fn new (name: &str) -> Self { Self { name: Arc::new(RwLock::new(name.into())), inputs: vec![], player: PhrasePlayer::new(name), outputs: vec![], } } pub fn longest_name (tracks: &[Self]) -> usize { tracks.iter() .map(|s|s.name.read().unwrap().len()) .fold(0, usize::max) } } /// Focus identification methods impl ArrangementFocus { pub fn description ( &self, tracks: &Vec>, scenes: &Vec, ) -> String { format!("Selected: {}", match self { Self::Mix => format!("Everything"), Self::Track(t) => if let Some(track) = tracks.get(*t) { format!("T{t}: {}", &track.name.read().unwrap()) } else { format!("T??") }, Self::Scene(s) => if let Some(scene) = scenes.get(*s) { format!("S{s}: {}", &scene.name.read().unwrap()) } else { format!("S??") }, Self::Clip(t, s) => if let (Some(track), Some(scene)) = ( tracks.get(*t), scenes.get(*s), ) { if let Some(clip) = scene.clip(*t) { format!("T{t} S{s} C{}", &clip.read().unwrap().name.read().unwrap()) } else { format!("T{t} S{s}: Empty") } } else { format!("T{t} S{s}: Empty") } }) } pub fn is_mix (&self) -> bool { match self { Self::Mix => true, _ => false } } pub fn is_track (&self) -> bool { match self { Self::Track(_) => true, _ => false } } pub fn is_scene (&self) -> bool { match self { Self::Scene(_) => true, _ => false } } pub fn is_clip (&self) -> bool { match self { Self::Clip(_, _) => true, _ => false } } pub fn track (&self) -> Option { match self { Self::Clip(t, _) => Some(*t), Self::Track(t) => Some(*t), _ => None } } pub fn track_next (&mut self, last_track: usize) { *self = match self { Self::Mix => Self::Track(0), Self::Track(t) => Self::Track(last_track.min(*t + 1)), Self::Scene(s) => Self::Clip(0, *s), Self::Clip(t, s) => Self::Clip(last_track.min(*t + 1), *s), } } pub fn track_prev (&mut self) { *self = match self { Self::Mix => Self::Mix, Self::Scene(s) => Self::Scene(*s), Self::Track(t) => if *t == 0 { Self::Mix } else { Self::Track(*t - 1) }, Self::Clip(t, s) => if *t == 0 { Self::Scene(*s) } else { Self::Clip(t.saturating_sub(1), *s) } } } pub fn scene (&self) -> Option { match self { Self::Clip(_, s) => Some(*s), Self::Scene(s) => Some(*s), _ => None } } pub fn scene_next (&mut self, last_scene: usize) { *self = match self { Self::Mix => Self::Scene(0), Self::Track(t) => Self::Clip(*t, 0), Self::Scene(s) => Self::Scene(last_scene.min(*s + 1)), Self::Clip(t, s) => Self::Clip(*t, last_scene.min(*s + 1)), } } pub fn scene_prev (&mut self) { *self = match self { Self::Mix => Self::Mix, Self::Track(t) => Self::Track(*t), Self::Scene(s) => if *s == 0 { Self::Mix } else { Self::Scene(*s - 1) }, Self::Clip(t, s) => if *s == 0 { Self::Track(*t) } else { Self::Clip(*t, s.saturating_sub(1)) } } } } /// Arranger display mode can be cycled impl ArrangementViewMode { /// Cycle arranger display mode pub fn to_next (&mut self) { *self = match self { Self::Horizontal => Self::Vertical(1), Self::Vertical(1) => Self::Vertical(2), Self::Vertical(2) => Self::Vertical(2), Self::Vertical(0) => Self::Horizontal, Self::Vertical(_) => Self::Vertical(0), } } } impl ArrangerRenameModal { pub fn new (target: ArrangementFocus, value: &Arc>) -> Self { Self { _engine: Default::default(), done: false, value: value.read().unwrap().clone(), cursor: value.read().unwrap().len(), result: value.clone(), target, } } } impl Exit for ArrangerRenameModal { fn exited (&self) -> bool { self.done } fn exit (&mut self) { self.done = true } } impl Scene { pub fn new ( name: impl AsRef, clips: impl AsRef<[Option>>]> ) -> Self { Self { name: Arc::new(RwLock::new(name.as_ref().into())), clips: clips.as_ref().iter().map(|x|x.clone()).collect(), } } /// Returns the pulse length of the longest phrase in the scene pub fn pulses (&self, tracks: &[ArrangementTrack]) -> usize { self.clips.iter().fold(0, |a, p|a.max(p.as_ref().map(|q|q.read().unwrap().length).unwrap_or(0))) } /// Returns true if all phrases in the scene are currently playing pub fn is_playing (&self, tracks: &[ArrangementTrack]) -> bool { self.clips.iter().enumerate().all(|(track_index, clip)|match clip { Some(clip) => tracks .get(track_index) .map(|track|if let Some(phrase) = &track.player.phrase { *phrase.read().unwrap() == *clip.read().unwrap() } else { false }) .unwrap_or(false), None => true }) } pub fn ppqs (tracks: &[ArrangementTrack], scenes: &[Self]) -> Vec<(usize, usize)> { let mut total = 0; let mut scenes: Vec<(usize, usize)> = scenes.iter().map(|scene|{ let pulses = scene.pulses(tracks).max(PPQ); total = total + pulses; (pulses, total - pulses) }).collect(); scenes.push((0, total)); scenes } pub fn longest_name (scenes: &[Self]) -> usize { scenes.iter() .map(|s|s.name.read().unwrap().len()) .fold(0, usize::max) } pub fn clip (&self, index: usize) -> Option<&Arc>> { if let Some(Some(clip)) = self.clips.get(index) { Some(clip) } else { None } } }