From 52ea78a466663881f66877d55b9c8e84a45b1be1 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Fri, 5 Jul 2024 18:42:13 +0300 Subject: [PATCH] count time from start of playback --- src/main.rs | 310 +++++---------------------------------------- src/model.rs | 267 ++++++++++++++++++++++++++++++++++++++ src/model/track.rs | 17 ++- 3 files changed, 309 insertions(+), 285 deletions(-) diff --git a/src/main.rs b/src/main.rs index 67ade2de..9af4db96 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,23 +17,36 @@ pub mod jack; use crate::{core::*, model::*}; pub fn main () -> Usually<()> { - App::default().run(Some(|app: Arc>|{ + let mut app = App::default(); + + // Load config + let xdg = Arc::new(microxdg::XdgApp::new("tek")?); + app.xdg = Some(xdg.clone()); + if crate::config::AppPaths::new(&xdg)?.should_create() { + app.modal = Some(Box::new(crate::config::SetupModal(Some(xdg.clone())))); + } + let midi_from = ["nanoKEY Studio.*capture.*"]; + let audio_into = ["Komplete.+:playback_FL", "Komplete.+:playback_FR"]; + + // Init + let ppq = app.timebase.ppq() as usize; + app.track_cursor = 1; + app.scene_cursor = 1; + app.note_start = 12; + app.time_zoom = 12; + app.quant = 24; + + // Start main loop + app.run(Some(|app: Arc>|{ let mut state = app.lock().unwrap(); - let xdg = Arc::new(microxdg::XdgApp::new("tek")?); - state.xdg = Some(xdg.clone()); - - if crate::config::AppPaths::new(&xdg)?.should_create() { - state.modal = Some(Box::new(crate::config::SetupModal(Some(xdg.clone())))); - } - + // Start JACK and setup device graph let jack = jack_run("tek", &app)?; let client = jack.as_client(); state.transport = Some(client.transport()); - state.playing = Some(TransportState::Stopped); state.midi_in = Some(client.register_port("midi-in", MidiIn)?); - let _ = ["nanoKEY Studio.*capture.*"] + let _ = midi_from .iter() .map(|name|client .ports(Some(name), None, PortFlags::empty()) @@ -47,15 +60,7 @@ pub fn main () -> Usually<()> { .collect::>()) .collect::>()?; - let timebase = &state.timebase; - let ppq = timebase.ppq() as usize; - state.track_cursor = 1; - state.scene_cursor = 1; - state.note_start = 12; - state.time_zoom = 12; - state.quant = 24; - - let outputs: Vec<_> = ["Komplete.+:playback_FL", "Komplete.+:playback_FR"] + let audio_outs: Vec<_> = audio_into .iter() .map(|name|client .ports(Some(name), None, PortFlags::empty()) @@ -85,10 +90,10 @@ pub fn main () -> Usually<()> { )?, |track, device|{ device.connect_audio_in(0, &track.devices[0].audio_outs()?[0])?; device.connect_audio_in(0, &track.devices[0].audio_outs()?[1])?; - if let Some(Some(left)) = outputs.get(0) { + if let Some(Some(left)) = audio_outs.get(0) { device.connect_audio_out(0, left)?; } - if let Some(Some(right)) = outputs.get(0) { + if let Some(Some(right)) = audio_outs.get(0) { device.connect_audio_out(1, right)?; } Ok(()) @@ -103,6 +108,7 @@ pub fn main () -> Usually<()> { 12 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, })); track.add_phrase("D-Beat", ppq * 4, Some(phrase! { + 00 * ppq/4 => MidiMessage::NoteOn { key: 44.into(), vel: 100.into() }, 00 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, 04 * ppq/4 => MidiMessage::NoteOn { key: 40.into(), vel: 100.into() }, 08 * ppq/4 => MidiMessage::NoteOn { key: 36.into(), vel: 100.into() }, @@ -124,10 +130,10 @@ pub fn main () -> Usually<()> { "file:///home/user/.lv2/Odin2.lv2" )?, |track, device|{ device.connect_midi_in(0, &track.midi_out.clone_unowned())?; - if let Some(Some(left)) = outputs.get(0) { + if let Some(Some(left)) = audio_outs.get(0) { device.connect_audio_out(0, left)?; } - if let Some(Some(right)) = outputs.get(0) { + if let Some(Some(right)) = audio_outs.get(0) { device.connect_audio_out(1, right)?; } Ok(()) @@ -153,10 +159,10 @@ pub fn main () -> Usually<()> { "file:///home/user/.lv2/Helm.lv2" )?, |track, device|{ device.connect_midi_in(0, &track.midi_out.clone_unowned())?; - if let Some(Some(left)) = outputs.get(0) { + if let Some(Some(left)) = audio_outs.get(0) { device.connect_audio_out(0, left)?; } - if let Some(Some(right)) = outputs.get(0) { + if let Some(Some(right)) = audio_outs.get(0) { device.connect_audio_out(1, right)?; } Ok(()) @@ -177,257 +183,5 @@ pub fn main () -> Usually<()> { Ok(()) })) -} - -#[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, - /// Transport status - pub playing: Option, - /// Current transport position - pub playhead: 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: usize, - /// 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 transport = self.transport.as_ref().unwrap().query().unwrap(); - let mut panic = false; - if Some(transport.state) != self.playing { - panic = true; - } - self.playing = Some(transport.state); - self.playhead = transport.pos.frame() as usize; - self.chunk_size = scope.n_frames() as usize; - let CycleTimes { - current_frames, - current_usecs, - next_usecs, - period_usecs - } = scope.cycle_times().unwrap(); - for track in self.tracks.iter_mut() { - track.process( - self.midi_in.as_ref().unwrap().iter(scope), - &self.timebase, - self.playing, - self.quant, - panic, - &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 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 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 add_track ( - &mut self, - name: Option<&str>, - ) -> Usually<&mut Track> { - let name = name.ok_or_else(||format!("Track {}", self.tracks.len() + 1))?; - 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 Fn(&Client, &mut Track)->Usually<()>, - ) -> Usually<&mut Track> { - let name = name.ok_or_else(||format!("Track {}", self.tracks.len() + 1))?; - 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 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>, + } diff --git a/src/model.rs b/src/model.rs index 0cb54422..f5cdce9d 100644 --- a/src/model.rs +++ b/src/model.rs @@ -12,3 +12,270 @@ pub use self::track::Track; pub use self::sampler::{Sampler, Sample}; 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, + /// 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: usize, + /// 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| { + 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 send_all_off = false; + if self.playing != Some(transport.state) { + match transport.state { + TransportState::Rolling => { + self.play_started = Some(current_usecs as usize); + }, + TransportState::Stopped => { + self.play_started = None; + send_all_off = true; + }, + _ => {} + } + } + self.playing = Some(transport.state); + 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, + send_all_off, + &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 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 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 add_track ( + &mut self, + name: Option<&str>, + ) -> Usually<&mut Track> { + let name = name.ok_or_else(||format!("Track {}", self.tracks.len() + 1))?; + 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 Fn(&Client, &mut Track)->Usually<()>, + ) -> Usually<&mut Track> { + let name = name.ok_or_else(||format!("Track {}", self.tracks.len() + 1))?; + 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 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>, +} diff --git a/src/model/track.rs b/src/model/track.rs index 10a9ec6b..9d974548 100644 --- a/src/model/track.rs +++ b/src/model/track.rs @@ -115,6 +115,7 @@ impl Track { input: MidiIter, timebase: &Arc, playing: Option, + started: Option, quant: usize, panic: bool, scope: &ProcessScope, @@ -136,13 +137,15 @@ impl Track { // Play from phrase into output buffer let phrase = &mut self.sequence.map(|id|self.phrases.get_mut(id)).flatten(); if playing == Some(TransportState::Rolling) { - if let Some(phrase) = phrase { - phrase.process_out( - &mut self.midi_out_buf, - &mut self.notes_on, - timebase, - (usec0, usecs, period) - ); + if let Some(started) = started { + if let Some(phrase) = phrase { + phrase.process_out( + &mut self.midi_out_buf, + &mut self.notes_on, + timebase, + (usec0 - started, usecs, period) + ); + } } } // Monitor and record input