use crate::*; impl TryFrom<&Arc>> for ArrangerApp { type Error = Box; fn try_from (jack: &Arc>) -> Usually { Ok(Self::new(ArrangerView { name: Arc::new(RwLock::new(String::new())), phrases: vec![], phrase: 0, scenes: vec![], tracks: vec![], metronome: false, playing: None.into(), started: None.into(), transport: jack.read().unwrap().transport(), current: Instant::default(), jack: jack.clone(), selected: ArrangerSelection::Clip(0, 0), mode: ArrangerMode::Vertical(2), color: Color::Rgb(28, 35, 25).into(), size: Measure::new(), entered: false, quant: Default::default(), sync: Default::default(), splits: [20, 20], note_buf: vec![], midi_buf: vec![], }.into(), None, None)) } } pub type ArrangerApp = AppView< E, ArrangerView, ArrangerAppCommand, ArrangerStatusBar >; /// Root view for standalone `tek_arranger` pub struct ArrangerView { pub(crate) jack: Arc>, pub(crate) playing: RwLock>, pub(crate) started: RwLock>, pub(crate) current: Instant, pub(crate) quant: Quantize, pub(crate) sync: LaunchSync, pub(crate) transport: jack::Transport, pub(crate) metronome: bool, pub(crate) phrases: Vec>>, pub(crate) phrase: usize, pub(crate) tracks: Vec, pub(crate) scenes: Vec, pub(crate) name: Arc>, pub(crate) splits: [u16;2], pub(crate) selected: ArrangerSelection, pub(crate) mode: ArrangerMode, pub(crate) color: ItemColor, pub(crate) entered: bool, pub(crate) size: Measure, pub(crate) note_buf: Vec, pub(crate) midi_buf: Vec>>, } impl HasJack for ArrangerView { fn jack (&self) -> &Arc> { &self.transport.jack() } } impl Audio for ArrangerApp { fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control { TracksAudio( &mut self.app.tracks, &mut self.app.note_buf, &mut self.app.midi_buf, Default::default(), ).process(client, scope) } } impl ClockApi for ArrangerView { fn timebase (&self) -> &Arc { &self.current.timebase } fn quant (&self) -> &Quantize { &self.quant } fn sync (&self) -> &LaunchSync { &self.sync } } impl PlayheadApi for ArrangerView { fn current (&self) -> &Instant { &self.current } fn transport (&self) -> &jack::Transport { &self.transport } fn playing (&self) -> &RwLock> { &self.playing } fn started (&self) -> &RwLock> { &self.started } } impl HasPhrases for ArrangerView { fn phrases (&self) -> &Vec>> { &self.phrases } fn phrases_mut (&mut self) -> &mut Vec>> { &mut self.phrases } } /// General methods for arranger impl ArrangerView { pub fn selected_scene (&self) -> Option<&ArrangerScene> { self.selected.scene().map(|s|self.scenes().get(s)).flatten() } pub fn selected_scene_mut (&mut self) -> Option<&mut ArrangerScene> { self.selected.scene().map(|s|self.scenes_mut().get_mut(s)).flatten() } pub fn selected_phrase (&self) -> Option>> { self.selected_scene()?.clips.get(self.selected.track()?)?.clone() } /// Focus the editor with the current phrase pub fn show_phrase (&mut self) { self.editor.show(self.selected_phrase().as_ref()); } pub fn activate (&mut self) { let scenes = self.scenes(); let tracks = self.tracks_mut(); match self.selected { ArrangerSelection::Scene(s) => { for (t, track) in tracks.iter_mut().enumerate() { let player = &mut track.player; let clip = scenes[s].clips[t].as_ref(); if player.phrase.is_some() || clip.is_some() { player.enqueue_next(clip); } } // TODO make transport available here, so that // activating a scene when stopped starts playback //if self.is_stopped() { //self.transport.toggle_play() //} }, ArrangerSelection::Clip(t, s) => { tracks[t].player.enqueue_next(scenes[s].clips[t]); }, _ => {} } } 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 { ArrangerSelection::Scene(s) => s == self.scenes().len() - 1, ArrangerSelection::Clip(_, s) => s == self.scenes().len() - 1, _ => false } } pub fn toggle_loop (&mut self) { if let Some(phrase) = self.selected_phrase() { phrase.write().unwrap().toggle_loop() } } pub fn randomize_color (&mut self) { match self.selected { ArrangerSelection::Mix => { self.color = ItemColor::random_dark() }, ArrangerSelection::Track(t) => { self.tracks_mut()[t].color = ItemColor::random() }, ArrangerSelection::Scene(s) => { self.scenes_mut()[s].color = ItemColor::random() }, ArrangerSelection::Clip(t, s) => { if let Some(phrase) = &self.scenes_mut()[s].clips[t] { phrase.write().unwrap().color = ItemColorTriplet::random(); } } } } } impl Audio for ArrangerView { #[inline] fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control { if self.process(client, scope) == Control::Quit { return Control::Quit } // FIXME: one of these per playing track if let ArrangerSelection::Clip(t, s) = self.selected { let phrase = self.scenes().get(s).map(|scene|scene.clips.get(t)); if let Some(Some(Some(phrase))) = phrase { if let Some(track) = self.tracks().get(t) { if let Some((ref started_at, Some(ref playing))) = track.player.phrase { let phrase = phrase.read().unwrap(); if *playing.read().unwrap() == *phrase { let pulse = self.current().pulse.get(); let start = started_at.pulse.get(); let now = (pulse - start) % phrase.length as f64; self.editor.now.set(now); return Control::Continue } } } } } self.editor.now.set(0.); return Control::Continue } } //pub fn track_next (&mut self, last_track: usize) { //use ArrangerSelection::*; //*self = match self { //Mix => Track(0), //Track(t) => Track(last_track.min(*t + 1)), //Scene(s) => Clip(0, *s), //Clip(t, s) => Clip(last_track.min(*t + 1), *s), //} //} //pub fn track_prev (&mut self) { //use ArrangerSelection::*; //*self = match self { //Mix => Mix, //Scene(s) => Scene(*s), //Track(t) => if *t == 0 { Mix } else { Track(*t - 1) }, //Clip(t, s) => if *t == 0 { Scene(*s) } else { Clip(t.saturating_sub(1), *s) } //} //} //pub fn scene_next (&mut self, last_scene: usize) { //use ArrangerSelection::*; //*self = match self { //Mix => Scene(0), //Track(t) => Clip(*t, 0), //Scene(s) => Scene(last_scene.min(*s + 1)), //Clip(t, s) => Clip(*t, last_scene.min(*s + 1)), //} //} //pub fn scene_prev (&mut self) { //use ArrangerSelection::*; //*self = match self { //Mix => Mix, //Track(t) => Track(*t), //Scene(s) => if *s == 0 { Mix } else { Scene(*s - 1) }, //Clip(t, s) => if *s == 0 { Track(*t) } else { Clip(*t, s.saturating_sub(1)) } //} //} //pub fn arranger_menu_bar () -> MenuBar { //use ArrangerCommand as Cmd; //use ArrangerCommand as Edit; //use ArrangerSelection as Focus; //use ArrangerTrackCommand as Track; //use ArrangerClipCommand as Clip; //use ArrangerSceneCommand as Scene; //use TransportCommand as Transport; //MenuBar::new() //.add({ //use ArrangerCommand::*; //Menu::new("File") //.cmd("n", "New project", ArrangerViewCommand::Arranger(New)) //.cmd("l", "Load project", ArrangerViewCommand::Arranger(Load)) //.cmd("s", "Save project", ArrangerViewCommand::Arranger(Save)) //}) //.add({ //Menu::new("Transport") //.cmd("p", "Play", TransportCommand::Transport(Play(None))) //.cmd("P", "Play from start", TransportCommand::Transport(Play(Some(0)))) //.cmd("s", "Pause", TransportCommand::Transport(Stop(None))) //.cmd("S", "Stop and rewind", TransportCommand::Transport(Stop(Some(0)))) //}) //.add({ //use ArrangerCommand::*; //Menu::new("Track") //.cmd("a", "Append new", ArrangerViewCommand::Arranger(AddTrack)) //.cmd("i", "Insert new", ArrangerViewCommand::Arranger(AddTrack)) //.cmd("n", "Rename", ArrangerViewCommand::Arranger(AddTrack)) //.cmd("d", "Delete", ArrangerViewCommand::Arranger(AddTrack)) //.cmd(">", "Move up", ArrangerViewCommand::Arranger(AddTrack)) //.cmd("<", "Move down", ArrangerViewCommand::Arranger(AddTrack)) //}) //.add({ //use ArrangerCommand::*; //Menu::new("Scene") //.cmd("a", "Append new", ArrangerViewCommand::Arranger(AddScene)) //.cmd("i", "Insert new", ArrangerViewCommand::Arranger(AddTrack)) //.cmd("n", "Rename", ArrangerViewCommand::Arranger(AddTrack)) //.cmd("d", "Delete", ArrangerViewCommand::Arranger(AddTrack)) //.cmd(">", "Move up", ArrangerViewCommand::Arranger(AddTrack)) //.cmd("<", "Move down", ArrangerViewCommand::Arranger(AddTrack)) //}) //.add({ //use PhraseRenameCommand as Rename; //use PhraseLengthCommand as Length; //Menu::new("Phrase") //.cmd("a", "Append new", PhrasePoolCommand::Phrases(Append)) //.cmd("i", "Insert new", PhrasePoolCommand::Phrases(Insert)) //.cmd("n", "Rename", PhrasePoolCommand::Phrases(Rename(Rename::Begin))) //.cmd("t", "Set length", PhrasePoolCommand::Phrases(Length(Length::Begin))) //.cmd("d", "Delete", PhrasePoolCommand::Phrases(Delete)) //.cmd("l", "Load from MIDI...", PhrasePoolCommand::Phrases(Import)) //.cmd("s", "Save to MIDI...", PhrasePoolCommand::Phrases(Export)) //.cmd(">", "Move up", PhrasePoolCommand::Phrases(MoveUp)) //.cmd("<", "Move down", PhrasePoolCommand::Phrases(MoveDown)) //}) //} //pub fn phrase_next (&mut self) { //if let ArrangerSelection::Clip(track, scene) = self.selected { //if let Some(ref mut phrase) = self.model.scenes[scene].clips[track] { //let phrases = self.model.phrases.read().unwrap(); //let index = phrases.index_of(&*phrase.read().unwrap()); //if let Some(index) = index { //if index < phrases.len().saturating_sub(1) { //*phrase = phrases[index + 1].clone(); //} //} //} //} //} //pub fn phrase_prev (&mut self) { //if let ArrangerSelection::Clip(track, scene) = self.selected { //if let Some(ref mut phrase) = self.model.scenes[scene].clips[track] { //let phrases = self.model.phrases.read().unwrap(); //let index = phrases.index_of(&*phrase.read().unwrap()); //if let Some(index) = index { //if index > 0 { //*phrase = phrases[index - 1].clone(); //} //} //} //} //} //pub fn phrase_get (&mut self) { //if let ArrangerSelection::Clip(track, scene) = self.selected { //if let Some(phrase) = &self.model.scenes[scene].clips[track] { //let mut phrases = self.model.phrases.write().unwrap(); //if let Some(index) = &*phrases.index_of(&*phrase.read().unwrap()) { //self.model.phrase = index; //} //} //} //} ///// Focus the editor with the current phrase //pub fn edit_phrase (&mut self) { //if self.arrangement.selected.is_clip() && self.arrangement.phrase().is_none() { //self.phrases.append_new(None, Some(self.next_color().into())); //self.arrangement.phrase_put(); //} //self.show_phrase(); //self.focus(ArrangerFocus::PhraseEditor); //self.editor.entered = true; //} //pub fn next_color (&self) -> ItemColor { //if let ArrangerSelection::Clip(track, scene) = self.arrangement.selected { //let track_color = self.arrangement.model.tracks[track].color; //let scene_color = self.arrangement.model.scenes[scene].color; //track_color.mix(scene_color, 0.5).mix(ItemColor::random(), 0.25) //} else { //panic!("could not compute next color") //} //} //pub fn phrase_del (&mut self) { //let track_index = self.selected.track(); //let scene_index = self.selected.scene(); //track_index //.and_then(|index|self.model.tracks.get_mut(index).map(|track|(index, track))) //.map(|(track_index, _)|scene_index //.and_then(|index|self.model.scenes.get_mut(index)) //.map(|scene|scene.clips[track_index] = None)); //} //pub fn phrase_put (&mut self) { //if let ArrangerSelection::Clip(track, scene) = self.selected { //self.model.scenes[scene].clips[track] = self.selected_phrase().clone(); //} //} //pub fn selected_scene (&self) -> Option<&ArrangerScene> { //self.selected.scene().map(|s|self.model.scenes.get(s)).flatten() //} //pub fn selected_scene_mut (&mut self) -> Option<&mut ArrangerScene> { //self.selected.scene().map(|s|self.model.scenes.get_mut(s)).flatten() //} //pub fn selected_phrase (&self) -> Option>> { //self.selected_scene()?.clips.get(self.selected.track()?)?.clone() //}