pub mod looper; pub mod mixer; pub mod phrase; pub mod plugin; pub mod sampler; pub mod scene; pub mod track; pub use self::phrase::Phrase; pub use self::scene::Scene; pub use self::track::Track; pub use self::sampler::{Sampler, Sample, read_sample_data}; pub use self::mixer::Mixer; pub use self::plugin::{Plugin, PluginKind, lv2::LV2Plugin}; use crate::core::*; #[derive(Default)] pub struct App { /// Paths to user directories pub xdg: Option>, /// Main JACK client. pub jack: Option, /// Main MIDI controller. pub midi_in: Option>, /// Main audio outputs. pub audio_outs: Option>>, /// JACK transport handle. pub transport: Option, /// Current transport state pub playing: Option, /// Current position according to transport pub playhead: usize, /// Position of T0 for this playback within global timeline pub play_started: Option<(usize, usize)>, /// Current sample rate and tempo. pub timebase: Arc, /// Display mode of grid section pub grid_mode: bool, /// Display mode of chain section pub chain_mode: bool, /// Display mode of sequencer seciton pub seq_mode: bool, /// Optional modal dialog pub modal: Option>, /// Currently focused section pub section: AppSection, /// Whether the section is focused pub entered: bool, /// Current frame pub metronome: bool, /// Display position of cursor within note range pub note_cursor: usize, /// Range of notes to display pub note_start: usize, /// Display position of cursor within time range pub time_cursor: usize, /// PPQ per display unit pub time_zoom: usize, /// Range of time steps to display pub time_start: usize, /// Focused scene+1, 0 is track list pub scene_cursor: usize, /// Collection of scenes pub scenes: Vec, /// Focused track+1, 0 is scene list pub track_cursor: usize, /// Collection of tracks pub tracks: Vec, pub chunk_size: usize, pub quant: usize, } process!(App |self, _client, scope| { let ( reset, current_frames, current_usecs, next_usecs, period_usecs ) = self.process_update_time(&scope); for track in self.tracks.iter_mut() { track.process( self.midi_in.as_ref().unwrap().iter(scope), &self.timebase, self.playing, self.play_started, self.quant, reset, &scope, (current_frames as usize, self.chunk_size), (current_usecs as usize, next_usecs.saturating_sub(current_usecs) as usize), period_usecs as f64 ); } Control::Continue }); impl App { pub fn process_update_time (&mut self, scope: &ProcessScope) -> (bool, usize, usize, usize, f64) { self.chunk_size = scope.n_frames() as usize; let CycleTimes { current_frames, current_usecs, next_usecs, period_usecs } = scope.cycle_times().unwrap(); let transport = self.transport.as_ref().unwrap().query().unwrap(); self.playhead = transport.pos.frame() as usize; let mut reset = false; if self.playing != Some(transport.state) { match transport.state { TransportState::Rolling => { self.play_started = Some(( current_frames as usize, current_usecs as usize, )); }, TransportState::Stopped => { self.play_started = None; reset = true; }, _ => {} } } self.playing = Some(transport.state); ( reset, current_frames as usize, current_usecs as usize, next_usecs as usize, period_usecs as f64 ) } pub fn client (&self) -> &Client { self.jack.as_ref().unwrap().as_client() } pub fn connect_ports ( &self, a: &Port, b: &Port ) -> Usually<()> { Ok(self.client().connect_ports(a, b)?) } pub fn toggle_play (&mut self) -> Usually<()> { self.playing = match self.playing.expect("after jack init") { TransportState::Stopped => { self.transport.as_ref().unwrap().start()?; Some(TransportState::Starting) }, _ => { self.transport.as_ref().unwrap().stop()?; self.transport.as_ref().unwrap().locate(0)?; Some(TransportState::Stopped) }, }; Ok(()) } pub fn new_track_name (&self) -> String { format!("Track {}", self.tracks.len() + 1) } pub fn add_track ( &mut self, name: Option<&str>, ) -> Usually<&mut Track> { let name = name.ok_or_else(||self.new_track_name())?; self.tracks.push(Track::new(&name, self.client(), None, None)?); self.track_cursor = self.tracks.len(); Ok(&mut self.tracks[self.track_cursor - 1]) } pub fn add_track_with_cb ( &mut self, name: Option<&str>, init: impl FnOnce(&Client, &mut Track)->Usually<()>, ) -> Usually<&mut Track> { let name = name.ok_or_else(||self.new_track_name())?; let mut track = Track::new(&name, self.client(), None, None)?; init(self.client(), &mut track)?; self.tracks.push(track); self.track_cursor = self.tracks.len(); Ok(&mut self.tracks[self.track_cursor - 1]) } pub fn track (&self) -> Option<(usize, &Track)> { match self.track_cursor { 0 => None, _ => { let id = self.track_cursor as usize - 1; self.tracks.get(id).map(|t|(id, t)) } } } pub fn track_mut (&mut self) -> Option<(usize, &mut Track)> { match self.track_cursor { 0 => None, _ => { let id = self.track_cursor as usize - 1; self.tracks.get_mut(id).map(|t|(id, t)) } } } pub fn add_scene (&mut self, name: Option<&str>) -> Usually<&mut Scene> { let name = name.ok_or_else(||format!("Scene {}", self.scenes.len() + 1))?; self.scenes.push(Scene::new(&name, vec![])); self.scene_cursor = self.scenes.len(); Ok(&mut self.scenes[self.scene_cursor - 1]) } pub fn scene (&self) -> Option<(usize, &Scene)> { match self.scene_cursor { 0 => None, _ => { let id = self.scene_cursor as usize - 1; self.scenes.get(id).map(|t|(id, t)) } } } pub fn scene_mut (&mut self) -> Option<(usize, &mut Scene)> { match self.scene_cursor { 0 => None, _ => { let id = self.scene_cursor as usize - 1; self.scenes.get_mut(id).map(|t|(id, t)) } } } pub fn phrase (&self) -> Option<&Phrase> { let (track_id, track) = self.track()?; let (_, scene) = self.scene()?; track.phrases.get((*scene.clips.get(track_id)?)?) } pub fn phrase_mut (&mut self) -> Option<&mut Phrase> { let (track_id, _) = self.track()?; let (_, scene) = self.scene()?; let clip = (*scene.clips.get(track_id)?)?; self.track_mut()?.1.phrases.get_mut(clip) } pub fn phrase_id (&self) -> Option { let (track_id, _) = self.track()?; let (_, scene) = self.scene()?; *scene.clips.get(track_id)? } pub fn selection <'a> (&'a self) -> Selection<'a> { let track_id = if self.track_cursor == 0 { None } else { Some(self.track_cursor - 1) }; let track = (&track_id).map(|id|self.tracks.get(id)).flatten(); let scene_id = if self.scene_cursor == 0 { None } else { Some(self.scene_cursor - 1) }; let scene = (&scene_id).map(|id|self.scenes.get(id)).flatten(); let phrase_id = if let (Some(scene), Some(id)) = (scene, track_id) { if let Some(Some(id)) = scene.clips.get(id) { Some(*id) } else { None } } else { None }; let phrase = if let (Some(id), Some(track)) = (phrase_id, track) { track.phrases.get(id) } else { None }; Selection { track_id, track, scene_id, scene, phrase_id, phrase } } //pub fn selection_mut <'a> (&'a mut self) -> SelectionMut<'a> { //let track_id = if self.track_cursor == 0 { None } else { Some(self.track_cursor - 1) }; //let track = (&track_id).map(|id|self.tracks.get_mut(id)).flatten(); //let scene_id = if self.scene_cursor == 0 { None } else { Some(self.scene_cursor - 1) }; //let scene = (&scene_id).map(|id|self.scenes.get_mut(id)).flatten(); //let phrase_id = if let (Some(scene), Some(id)) = (scene, track_id) { //if let Some(Some(id)) = scene.clips.get_mut(id) { //Some(*id) //} else { //None //} //} else { //None //}; //let phrase = if let (Some(id), Some(track)) = (phrase_id, track) { //track.phrases.get_mut(id) //} else { //None //}; //SelectionMut { //track_id, //track, //scene_id, //scene, //phrase_id, //phrase //} //} } struct Selection<'a> { pub track_id: Option, pub track: Option<&'a Track>, pub scene_id: Option, pub scene: Option<&'a Scene>, pub phrase_id: Option, pub phrase: Option<&'a Phrase>, } struct SelectionMut<'a> { pub track_id: Option, pub track: Option<&'a mut Track>, pub scene_id: Option, pub scene: Option<&'a mut Scene>, pub phrase_id: Option, pub phrase: Option<&'a mut Phrase>, } #[derive(PartialEq, Clone, Copy)] pub enum AppSection { Grid, Sequencer, Chain, } impl Default for AppSection { fn default () -> Self { Self::Grid } } impl AppSection { pub fn prev (&mut self) { *self = match self { Self::Grid => Self::Chain, Self::Sequencer => Self::Grid, Self::Chain => Self::Sequencer, } } pub fn next (&mut self) { *self = match self { Self::Grid => Self::Sequencer, Self::Sequencer => Self::Chain, Self::Chain => Self::Grid, } } }