diff --git a/src/control.rs b/src/control.rs index 459ec008..5a2032d2 100644 --- a/src/control.rs +++ b/src/control.rs @@ -166,12 +166,12 @@ fn focus_prev (app: &mut App) -> Usually { Ok(true) } -fn increment (app: &mut App) -> Usually { +fn increment (_: &mut App) -> Usually { Ok(false) } -fn decrement (app: &mut App) -> Usually { +fn decrement (_: &mut App) -> Usually { Ok(false) } @@ -186,7 +186,7 @@ fn delete_track (app: &mut App) -> Usually { if app.tracks.len() > 0 { let track = app.tracks.remove(app.track_cursor); app.track_cursor = app.track_cursor.saturating_sub(1); - app.jack.as_ref().unwrap().as_client().unregister_port(track.midi_out)?; + app.client().unregister_port(track.midi_out)?; return Ok(true) } Ok(false) diff --git a/src/core.rs b/src/core.rs index db9e3dcc..c96985b4 100644 --- a/src/core.rs +++ b/src/core.rs @@ -65,24 +65,22 @@ pub use crate::{submod, pubmod, render, handle, process, phrase, keymap, ports}; // Reexport JACK proto-lib: pub use crate::jack::*; -impl Run for T {} - -pub trait Run: Render + Handle + Send + Sync + Sized + 'static { - fn run (self, init: Option>)->Usually<()>>) -> Usually<()> { - let app = Arc::new(RwLock::new(self)); - let exited = Arc::new(AtomicBool::new(false)); - let _input_thread = input_thread(&exited, &app); - terminal_setup()?; - panic_hook_setup(); - let main_thread = main_thread(&exited, &app)?; - if let Some(init) = init { - init(app)?; - } - main_thread.join().unwrap(); - terminal_teardown()?; - Ok(()) - } +pub fn run (state: Arc>) -> Usually>> + where T: Render + Handle + Send + Sync + Sized + 'static +{ + let exited = Arc::new(AtomicBool::new(false)); + let _input_thread = input_thread(&exited, &state); + terminal_setup()?; + panic_hook_setup(); + let main_thread = main_thread(&exited, &state)?; + main_thread.join().unwrap(); + terminal_teardown()?; + Ok(state) } +pub trait Run: Render + Handle + Send + Sync + Sized + 'static { + fn run (self) -> Usually>> { run(Arc::new(RwLock::new(self))) } +} +impl Run for T {} /// Set up panic hook pub fn panic_hook_setup () { diff --git a/src/core/render.rs b/src/core/render.rs index 00d6bcad..1b70f63c 100644 --- a/src/core/render.rs +++ b/src/core/render.rs @@ -63,7 +63,7 @@ pub trait Render: Send { } impl Render for () { - fn render (&self, b: &mut Buffer, a: Rect) -> Usually { + fn render (&self, _: &mut Buffer, a: Rect) -> Usually { Ok(Rect { x: a.x, y: a.y, width: 0, height: 0 }) } } diff --git a/src/edn.rs b/src/edn.rs index df094b25..6178e6da 100644 --- a/src/edn.rs +++ b/src/edn.rs @@ -14,7 +14,7 @@ macro_rules! edn { } impl App { - pub fn load_edn (&mut self, mut src: &str) -> Usually<()> { + pub fn load_edn (&mut self, mut src: &str) -> Usually<&mut Self> { loop { match clojure_reader::edn::read(src) { Ok((edn, rest)) => { @@ -33,7 +33,7 @@ impl App { }, } } - Ok(()) + Ok(self) } fn load_edn_one <'e> (&mut self, edn: Edn<'e>) -> Usually<()> { match edn { @@ -96,7 +96,7 @@ impl Track { fn load_edn <'a, 'e> (app: &'a mut App, args: &[Edn<'e>]) -> Usually<&'a mut Self> { let ppq = app.timebase.ppq() as usize; let mut name = app.new_track_name(); - let mut gain = 0.0f64; + let mut _gain = 0.0f64; let mut devices: Vec = vec![]; let mut phrases: Vec = vec![]; edn!(edn in args { @@ -105,7 +105,7 @@ impl Track { name = String::from(*n); } if let Some(Edn::Double(g)) = map.get(&Edn::Key(":gain")) { - gain = f64::from(*g) + _gain = f64::from(*g) } }, Edn::List(args) => match args.get(0) { @@ -132,7 +132,7 @@ impl Track { track.add_device(device)?; } if let Some(device) = track.devices.get(0) { - device.connect_midi_in(0, &track.midi_out.clone_unowned())?; + //device.connect_midi_in(0, &track.midi_out.clone_unowned())?; } if let Some(device) = track.devices.get(track.devices.len() - 1) { if let Some(ref left) = left { diff --git a/src/jack.rs b/src/jack.rs index 459cbae1..54dc48a6 100644 --- a/src/jack.rs +++ b/src/jack.rs @@ -30,6 +30,55 @@ pub use ::_jack::{ Unowned }; +pub enum JackClient { + Inactive(Client), + Active(DynamicAsyncClient), +} + +impl JackClient { + pub fn client (&self) -> &Client { + match self { + Self::Inactive(ref client) => + client, + Self::Active(ref client) => + client.as_client(), + } + } + pub fn transport (&self) -> Transport { + match self { + Self::Inactive(client) => + client.transport(), + Self::Active(client) => + client.as_client().transport(), + } + } + fn register_port (&self, name: &str, spec: PS) -> Usually> { + Ok(match self { + Self::Inactive(client) => + client.register_port(name, spec)?, + Self::Active(client) => + client.as_client().register_port(name, spec)?, + }) + } + pub fn activate ( + self, + state: &Arc>, + mut process: impl FnMut(&Arc>, &Client, &ProcessScope)->Control + Send + 'static + ) -> Usually { + Ok(match self { + Self::Active(_) => self, + Self::Inactive(client) => Self::Active(client.activate_async( + Notifications(Box::new(move|_|{/*TODO*/}) + as Box), + ClosureProcessHandler::new(Box::new({ + let state = state.clone(); + move|c: &Client, s: &ProcessScope|process(&state, c, s) + }) as BoxedProcessHandler) + )?) + }) + } +} + pub type DynamicAsyncClient = AsyncClient; diff --git a/src/main.rs b/src/main.rs index 9810e988..54ae1bc2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,43 +21,25 @@ mod edn; use crate::{core::*, model::*}; /// Application entrypoint. -fn main () -> Usually<()> { - let controller = ["nanoKEY Studio.*capture.*"]; - let soundsystem = ["Komplete.+:playback_FL", "Komplete.+:playback_FR"]; +pub fn main () -> Usually<()> { + let mut app = App::new()?; + app + .connect_to_midi_ins(&["nanoKEY Studio.*capture.*"])? + .connect_to_audio_outs(&["Komplete.+:playback_FL", "Komplete.+:playback_FR"])? + .load_edn(include_str!("../demos/project.edn"))?; + run(app.activate()?); // Start main loop - App::new()?.run(Some(|app: Arc>|{ - let mut state = app.write().unwrap(); - // Start JACK and setup device graph - let jack = jack_run("tek", &app)?; - let client = jack.as_client(); - state.transport = Some(client.transport()); - state.midi_in = Some(client.register_port("midi-in", MidiIn)?); - controller - .iter() - .map(|name|client - .ports(Some(name), None, PortFlags::empty()) - .iter() - .map(|name|{ - if let Some(port) = client.port_by_name(name) { - client.connect_ports(&port, &state.midi_in.as_ref().unwrap())?; - } - Ok(()) - }) - .collect::>()) - .collect::>()?; - state.audio_outs = soundsystem - .iter() - .map(|name|client - .ports(Some(name), None, PortFlags::empty()) - .get(0) - .map(|name|client.port_by_name(name))) - .flatten() - .filter_map(|x|x) - .map(Arc::new) - .collect(); - state.jack = Some(jack); - // Load project - state.load_edn(include_str!("../demos/project.edn"))?; - Ok(()) - })) + //App::new()?.run(Some(|app: Arc>|{ + //let mut state = app.write().unwrap(); + //// Start JACK and setup device graph + //let jack = jack_run("tek", &app)?; + //let client = jack.as_client(); + //state.transport = Some(client.transport()); + //state.midi_in = Some(client.register_port("midi-in", MidiIn)?); + //state.jack = Some(jack); + //// Load project + //state.load_edn(include_str!("../demos/project.edn"))?; + //Ok(()) + //})) + Ok(()) } diff --git a/src/model.rs b/src/model.rs index 40087fea..287d8025 100644 --- a/src/model.rs +++ b/src/model.rs @@ -15,12 +15,11 @@ 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, + pub jack: Option, /// Main MIDI controller. pub midi_in: Option>, /// Main audio outputs. @@ -77,15 +76,36 @@ impl App { pub fn new () -> Usually { let xdg = Arc::new(microxdg::XdgApp::new("tek")?); let first_run = crate::config::AppPaths::new(&xdg)?.should_create(); + let jack = JackClient::Inactive(Client::new("tek", ClientOptions::NO_START_SERVER)?.0); + let transport = jack.transport(); Ok(Self { + arranger_mode: false, + audio_outs: vec![], + chain_mode: false, + chunk_size: 0, + entered: true, + jack: Some(jack), + metronome: false, + midi_in: None, modal: first_run.then(||crate::config::SetupModal(Some(xdg.clone())).boxed()), - xdg: Some(xdg), - track_cursor: 1, + note_cursor: 0, + note_start: 2, + play_started: None, + playhead: 0, + playing: None, + quant: 24, scene_cursor: 1, - note_start: 2, - time_zoom: 12, - quant: 24, - ..Self::default() + scenes: vec![], + section: AppSection::default(), + seq_mode: false, + time_cursor: 0, + time_start: 0, + time_zoom: 12, + timebase: Arc::new(Timebase::default()), + track_cursor: 1, + tracks: vec![], + transport: Some(transport), + xdg: Some(xdg), }) } } @@ -111,14 +131,23 @@ process!(App |self, _client, scope| { Control::Continue }); impl App { + pub fn activate (mut self) -> Usually>> { + let jack = self.jack.take().expect("no jack client"); + let state = Arc::new(RwLock::new(self)); + state.write().unwrap().jack = Some(jack.activate(&state, Self::process)?); + Ok(state) + } + pub fn process (state: &Arc>, client: &Client, scope: &ProcessScope) -> Control { + state.write().unwrap().process(client, scope) + } 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(); + self.chunk_size = scope.n_frames() as usize; let transport = self.transport.as_ref().unwrap().query().unwrap(); self.playhead = transport.pos.frame() as usize; let mut reset = false; @@ -146,15 +175,10 @@ impl App { self.audio_outs.get(index).map(|x|x.clone()) } 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)?) + self.jack.as_ref().unwrap().client() } pub fn toggle_play (&mut self) -> Usually<()> { - self.playing = match self.playing.expect("after jack init") { + self.playing = match self.playing.expect("1st frame has not been processed yet") { TransportState::Stopped => { self.transport.as_ref().unwrap().start()?; Some(TransportState::Starting) @@ -308,6 +332,39 @@ impl App { //phrase //} //} + pub fn connect_to_midi_ins (&mut self, ports: &[&str]) -> Usually<&mut Self> { + let client = self.client(); + let midi_in = client.register_port("midi-in", MidiIn)?; + ports + .iter() + .map(|name|client + .ports(Some(name), None, PortFlags::empty()) + .iter() + .map(|name|{ + if let Some(port) = client.port_by_name(name) { + client.connect_ports(&port, &midi_in)?; + } + Ok(()) + }) + .collect::>()) + .collect::>()?; + self.midi_in = Some(midi_in); + Ok(self) + } + pub fn connect_to_audio_outs (&mut self, ports: &[&str]) -> Usually<&mut Self> { + let client = self.client(); + self.audio_outs = ports + .iter() + .map(|name|client + .ports(Some(name), None, PortFlags::empty()) + .get(0) + .map(|name|client.port_by_name(name))) + .flatten() + .filter_map(|x|x) + .map(Arc::new) + .collect(); + Ok(self) + } } struct Selection<'a> {