use crate::*; #[derive(Debug)] pub struct Arrangement { /// JACK client handle (needs to not be dropped for standalone mode to work). pub jack: Arc>, /// Global timebase pub clock: Arc, /// Name of arranger pub name: Arc>, /// Collection of phrases. pub phrases: Arc>>, /// Collection of tracks. pub tracks: Vec, /// Collection of scenes. pub scenes: Vec, } #[derive(Debug)] pub struct ArrangementTrack { /// 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 ArrangementScene { /// Name of scene pub name: Arc>, /// Clips in scene, one per track pub clips: Vec>>>, /// Identifying color of scene pub color: ItemColor, } impl Audio for Arrangement { #[inline] fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control { for track in self.tracks.iter_mut() { if track.process(client, scope) == Control::Quit { return Control::Quit } } Control::Continue } } impl Audio for ArrangementTrack { #[inline] fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control { self.player.process(client, scope) } } impl Arrangement { pub fn is_stopped (&self) -> bool { *self.clock.playing.read().unwrap() == Some(TransportState::Stopped) } } impl ArrangementScene { /// 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: &[MIDIPlayer]) -> 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.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(ArrangementScene { //name: Arc::new(name.unwrap_or("").to_string().into()), //color: ItemColor::random(), //clips, //}) //} 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) } } impl ArrangementTrack { 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; } } }