use crate::*; pub trait ArrangerModelApi: JackModelApi + ClockModelApi { fn name (&self) -> &Arc>; fn tracks (&self) -> &Vec; fn tracks_mut (&mut self) -> &mut Vec; fn scenes (&self) -> &Vec; fn scenes_mut (&mut self) -> &mut Vec; fn track_default_name (&self) -> String { format!("Track {}", self.tracks().len() + 1) } fn track_add ( &mut self, name: Option<&str>, color: Option ) -> Usually<&mut ArrangerTrack> { let name = name.map_or_else(||self.track_default_name(), |x|x.to_string()); let track = ArrangerTrack { width: name.len() + 2, color: color.unwrap_or_else(||ItemColor::random()), player: MIDIPlayer::new(&self.jack(), &self.clock(), name.as_str())?, name: Arc::new(name.into()), }; self.tracks_mut().push(track); let index = self.tracks().len() - 1; Ok(&mut self.tracks_mut()[index]) } fn track_del (&mut self, index: usize) { self.tracks_mut().remove(index); for scene in self.scenes_mut().iter_mut() { scene.clips.remove(index); } } fn scene_default_name (&self) -> String { format!("Scene {}", self.scenes().len() + 1) } fn scene_add ( &mut self, name: Option<&str>, color: Option ) -> Usually<&mut ArrangerScene> { let name = name.map_or_else(||self.scene_default_name(), |x|x.to_string()); let scene = ArrangerScene { name: Arc::new(name.into()), clips: vec![None;self.tracks().len()], color: color.unwrap_or_else(||ItemColor::random()), }; self.scenes_mut().push(scene); let index = self.scenes().len() - 1; Ok(&mut self.scenes_mut()[index]) } fn scene_del (&mut self, index: usize) { self.scenes_mut().remove(index); } } impl JackModelApi for ArrangerModel { fn jack (&self) -> &Arc> { &self.transport.jack() } } impl ClockModelApi for ArrangerModel { fn clock (&self) -> &Arc { &self.transport.clock() } } impl TransportModelApi for ArrangerModel { fn transport (&self) -> &jack::Transport { &self.transport.transport() } fn metronome (&self) -> bool { self.transport.metronome() } } impl PhrasePoolModelApi for ArrangerModel { fn phrases (&self) -> &Vec>> { &self.phrases } fn phrases_mut (&mut self) -> &mut Vec>> { &mut self.phrases } } impl ArrangerModelApi for ArrangerModel { fn name (&self) -> &Arc> { &self.name } fn tracks (&self) -> &Vec { &self.tracks } fn tracks_mut (&mut self) -> &mut Vec { &mut self.tracks } fn scenes (&self) -> &Vec { &self.scenes } fn scenes_mut (&mut self) -> &mut Vec { &mut self.scenes } } #[derive(Debug)] pub struct ArrangerModel { /// State of the JACK transport. transport: TransportModel, /// Collection of phrases. phrases: Vec>>, /// Collection of tracks. tracks: Vec, /// Collection of scenes. scenes: Vec, /// Name of arranger name: Arc>, } #[derive(Debug)] pub struct ArrangerTrack { /// Name of track pub name: Arc>, /// Preferred width of track column pub width: usize, /// Identifying color of track pub color: ItemColor, /// The MIDI player for the track pub player: MIDIPlayer } #[derive(Default, Debug, Clone)] pub struct ArrangerScene { /// Name of scene pub name: Arc>, /// Clips in scene, one per track pub clips: Vec>>>, /// Identifying color of scene pub color: ItemColor, } impl ArrangerTrack { pub fn longest_name (tracks: &[Self]) -> usize { tracks.iter().map(|s|s.name.read().unwrap().len()).fold(0, usize::max) } pub const MIN_WIDTH: usize = 3; pub fn width_inc (&mut self) { self.width += 1; } pub fn width_dec (&mut self) { if self.width > Self::MIN_WIDTH { self.width -= 1; } } } impl ArrangerScene { pub fn ppqs (scenes: &[Self], factor: usize) -> Vec<(usize, usize)> { let mut total = 0; if factor == 0 { scenes.iter().map(|scene|{ let pulses = scene.pulses().max(PPQ); total = total + pulses; (pulses, total - pulses) }).collect() } else { (0..=scenes.len()).map(|i|{ (factor*PPQ, factor*PPQ*i) }).collect() } } pub fn longest_name (scenes: &[Self]) -> usize { scenes.iter().map(|s|s.name.read().unwrap().len()).fold(0, usize::max) } /// Returns the pulse length of the longest phrase in the scene pub fn pulses (&self) -> 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 on the given collection of tracks. pub fn is_playing (&self, tracks: &[ArrangerTrack]) -> bool { self.clips.iter().any(|clip|clip.is_some()) && self.clips.iter().enumerate() .all(|(track_index, clip)|match clip { Some(clip) => tracks .get(track_index) .map(|track|if let Some((_, Some(phrase))) = &track.player.phrase { *phrase.read().unwrap() == *clip.read().unwrap() } else { false }) .unwrap_or(false), None => true }) } pub fn clip (&self, index: usize) -> Option<&Arc>> { match self.clips.get(index) { Some(Some(clip)) => Some(clip), _ => None } } //TODO //pub fn from_edn <'a, 'e> (args: &[Edn<'e>]) -> Usually { //let mut name = None; //let mut clips = vec![]; //edn!(edn in args { //Edn::Map(map) => { //let key = map.get(&Edn::Key(":name")); //if let Some(Edn::Str(n)) = key { //name = Some(*n); //} else { //panic!("unexpected key in scene '{name:?}': {key:?}") //} //}, //Edn::Symbol("_") => { //clips.push(None); //}, //Edn::Int(i) => { //clips.push(Some(*i as usize)); //}, //_ => panic!("unexpected in scene '{name:?}': {edn:?}") //}); //Ok(ArrangerScene { //name: Arc::new(name.unwrap_or("").to_string().into()), //color: ItemColor::random(), //clips, //}) //} } #[derive(Clone, Debug)] pub enum ArrangerCommand { Clear, Export, Import, StopAll, Scene(ArrangerSceneCommand), Track(ArrangerTrackCommand), Clip(ArrangerClipCommand), } #[derive(Clone, Debug)] pub enum ArrangerSceneCommand { Add, Delete(usize), RandomColor, Play(usize), Swap(usize, usize), SetSize(usize), SetZoom(usize), } #[derive(Clone, Debug)] pub enum ArrangerTrackCommand { Add, Delete(usize), RandomColor, Stop, Swap(usize, usize), SetSize(usize), SetZoom(usize), } #[derive(Clone, Debug)] pub enum ArrangerClipCommand { Play, Get(usize, usize), Set(usize, usize, Option>>), Edit(Option>>), SetLoop(bool), RandomColor, } impl Command for ArrangerCommand { fn execute (self, state: &mut ArrangerModel) -> Perhaps { match self { Self::Scene(command) => { return Ok(command.execute(state)?.map(Self::Scene)) }, Self::Track(command) => { return Ok(command.execute(state)?.map(Self::Track)) }, Self::Clip(command) => { return Ok(command.execute(state)?.map(Self::Clip)) }, _ => todo!() } Ok(None) } } impl Command for ArrangerSceneCommand { fn execute (self, state: &mut ArrangerModel) -> Perhaps { match self { Self::Delete(index) => { state.scene_del(index); }, _ => todo!() } Ok(None) } } impl Command for ArrangerTrackCommand { fn execute (self, state: &mut ArrangerModel) -> Perhaps { match self { Self::Delete(index) => { state.track_del(index); }, _ => todo!() } Ok(None) } } impl Command for ArrangerClipCommand { fn execute (self, state: &mut ArrangerModel) -> Perhaps { match self { _ => todo!() } Ok(None) } } //impl Command for ArrangerSceneCommand { //} //Edit(phrase) => { state.state.phrase = phrase.clone() }, //ToggleViewMode => { state.state.mode.to_next(); }, //Delete => { state.state.delete(); }, //Activate => { state.state.activate(); }, //ZoomIn => { state.state.zoom_in(); }, //ZoomOut => { state.state.zoom_out(); }, //MoveBack => { state.state.move_back(); }, //MoveForward => { state.state.move_forward(); }, //RandomColor => { state.state.randomize_color(); }, //Put => { state.state.phrase_put(); }, //Get => { state.state.phrase_get(); }, //AddScene => { state.state.scene_add(None, None)?; }, //AddTrack => { state.state.track_add(None, None)?; }, //ToggleLoop => { state.state.toggle_loop() }, //pub fn zoom_in (&mut self) { //if let ArrangerEditorMode::Vertical(factor) = self.mode { //self.mode = ArrangerEditorMode::Vertical(factor + 1) //} //} //pub fn zoom_out (&mut self) { //if let ArrangerEditorMode::Vertical(factor) = self.mode { //self.mode = ArrangerEditorMode::Vertical(factor.saturating_sub(1)) //} //} //pub fn move_back (&mut self) { //match self.selected { //ArrangerEditorFocus::Scene(s) => { //if s > 0 { //self.scenes.swap(s, s - 1); //self.selected = ArrangerEditorFocus::Scene(s - 1); //} //}, //ArrangerEditorFocus::Track(t) => { //if t > 0 { //self.tracks.swap(t, t - 1); //self.selected = ArrangerEditorFocus::Track(t - 1); //// FIXME: also swap clip order in scenes //} //}, //_ => todo!("arrangement: move forward") //} //} //pub fn move_forward (&mut self) { //match self.selected { //ArrangerEditorFocus::Scene(s) => { //if s < self.scenes.len().saturating_sub(1) { //self.scenes.swap(s, s + 1); //self.selected = ArrangerEditorFocus::Scene(s + 1); //} //}, //ArrangerEditorFocus::Track(t) => { //if t < self.tracks.len().saturating_sub(1) { //self.tracks.swap(t, t + 1); //self.selected = ArrangerEditorFocus::Track(t + 1); //// FIXME: also swap clip order in scenes //} //}, //_ => todo!("arrangement: move forward") //} //}