diff --git a/src/_main.rs b/src/_main.rs index d578a487..977a6bb1 100644 --- a/src/_main.rs +++ b/src/_main.rs @@ -62,7 +62,7 @@ impl App { .add(13 * PPQ / 4, MIDIEvent::NoteOn(44, 100)) .add(14 * PPQ / 4, MIDIEvent::NoteOn(44, 100)) .add(15 * PPQ / 4, MIDIEvent::NoteOn(44, 100)); - Ok(Self { + Ok(Self {std::thread::sleep(std::time::Duration::from_millis(10)) jack: Jack::init_from_cli(&options)?, time: 0, root, diff --git a/src/engine/jack.rs b/src/engine/jack.rs index 5445644f..028d5815 100644 --- a/src/engine/jack.rs +++ b/src/engine/jack.rs @@ -23,156 +23,156 @@ use jack::{ Transport }; -pub struct Jack { - client: OptionControl + Send>> - >>, - pub transport: Option, - audio_ins: BTreeMap>, - audio_outs: BTreeMap>, - midi_ins: BTreeMap>, - midi_outs: BTreeMap>, -} +//pub struct Jack { + //client: OptionControl + Send>> + //>>, + //pub transport: Option, + //audio_ins: BTreeMap>, + //audio_outs: BTreeMap>, + //midi_ins: BTreeMap>, + //midi_outs: BTreeMap>, +//} -impl Jack { - pub fn init_from_cli (options: &Cli) - -> Result>, Box> - { - let jack = Self::init(&options.jack_client_name)?; - { - let jack = jack.clone(); - let mut jack = jack.lock().unwrap(); - for port in options.jack_audio_ins.iter() { - jack.add_audio_in(port)?; - } - for port in options.jack_audio_outs.iter() { - jack.add_audio_out(port)?; - } - for port in options.jack_midi_ins.iter() { - jack.add_midi_in(port)?; - } - for port in options.jack_midi_outs.iter() { - jack.add_midi_out(port)?; - } - } - Ok(jack.clone()) - } - fn init (name: &str) - -> Result>, Box> - { - let jack = Arc::new(Mutex::new(Self { - client: None, - transport: None, - audio_ins: BTreeMap::new(), - audio_outs: BTreeMap::new(), - midi_ins: BTreeMap::new(), - midi_outs: BTreeMap::new(), - })); - let (client, status) = Client::new(name, ClientOptions::NO_START_SERVER)?; - println!("Client status: {status:?}"); - let jack1 = jack.clone(); - let mut jack1 = jack1.lock().unwrap(); - let jack2 = jack.clone(); - jack1.transport = Some(client.transport()); - jack1.client = Some(client.activate_async( - Notifications, ClosureProcessHandler::new(Box::new( - move |_client: &Client, _ps: &ProcessScope| -> Control { - let jack = jack2.lock().expect("Failed to lock jack mutex"); - jack.read_inputs(); - jack.write_outputs(); - Control::Continue - } - ) as BoxControl + Send>) - )?); - Ok(jack) - } - fn start (&self) { - } - fn process (&self, _: &Client, ps: &ProcessScope) -> Control { - Control::Continue - } - fn add_audio_in (&mut self, name: &str) -> Result<&mut Self, Box> { - let port = self.client - .as_ref() - .expect("Not initialized.") - .as_client() - .register_port(name, AudioIn::default())?; - self.audio_ins.insert(name.into(), port); - Ok(self) - } - fn add_audio_out (&mut self, name: &str) -> Result<&mut Self, Box> { - let port = self.client - .as_ref() - .expect("Not initialized.") - .as_client() - .register_port(name, AudioOut::default())?; - self.audio_outs.insert(name.into(), port); - Ok(self) - } - fn add_midi_in (&mut self, name: &str) -> Result<&mut Self, Box> { - let port = self.client - .as_ref() - .expect("Not initialized.") - .as_client() - .register_port(name, MidiIn::default())?; - self.midi_ins.insert(name.into(), port); - Ok(self) - } - fn add_midi_out (&mut self, name: &str) -> Result<&mut Self, Box> { - let port = self.client - .as_ref() - .expect("Not initialized.") - .as_client() - .register_port(name, MidiOut::default())?; - self.midi_outs.insert(name.into(), port); - Ok(self) - } - fn read_inputs (&self) { - // read input buffers - //println!("read"); - } - fn write_outputs (&self) { - // clear output buffers - // write output buffers - //println!("write"); - } -} +//impl Jack { + //pub fn init_from_cli (options: &Cli) + //-> Result>, Box> + //{ + //let jack = Self::init(&options.jack_client_name)?; + //{ + //let jack = jack.clone(); + //let mut jack = jack.lock().unwrap(); + //for port in options.jack_audio_ins.iter() { + //jack.add_audio_in(port)?; + //} + //for port in options.jack_audio_outs.iter() { + //jack.add_audio_out(port)?; + //} + //for port in options.jack_midi_ins.iter() { + //jack.add_midi_in(port)?; + //} + //for port in options.jack_midi_outs.iter() { + //jack.add_midi_out(port)?; + //} + //} + //Ok(jack.clone()) + //} + //fn init (name: &str) + //-> Result>, Box> + //{ + //let jack = Arc::new(Mutex::new(Self { + //client: None, + //transport: None, + //audio_ins: BTreeMap::new(), + //audio_outs: BTreeMap::new(), + //midi_ins: BTreeMap::new(), + //midi_outs: BTreeMap::new(), + //})); + //let (client, status) = Client::new(name, ClientOptions::NO_START_SERVER)?; + //println!("Client status: {status:?}"); + //let jack1 = jack.clone(); + //let mut jack1 = jack1.lock().unwrap(); + //let jack2 = jack.clone(); + //jack1.transport = Some(client.transport()); + //jack1.client = Some(client.activate_async( + //Notifications, ClosureProcessHandler::new(Box::new( + //move |_client: &Client, _ps: &ProcessScope| -> Control { + //let jack = jack2.lock().expect("Failed to lock jack mutex"); + //jack.read_inputs(); + //jack.write_outputs(); + //Control::Continue + //} + //) as BoxControl + Send>) + //)?); + //Ok(jack) + //} + //fn start (&self) { + //} + //fn process (&self, _: &Client, ps: &ProcessScope) -> Control { + //Control::Continue + //} + //fn add_audio_in (&mut self, name: &str) -> Result<&mut Self, Box> { + //let port = self.client + //.as_ref() + //.expect("Not initialized.") + //.as_client() + //.register_port(name, AudioIn::default())?; + //self.audio_ins.insert(name.into(), port); + //Ok(self) + //} + //fn add_audio_out (&mut self, name: &str) -> Result<&mut Self, Box> { + //let port = self.client + //.as_ref() + //.expect("Not initialized.") + //.as_client() + //.register_port(name, AudioOut::default())?; + //self.audio_outs.insert(name.into(), port); + //Ok(self) + //} + //fn add_midi_in (&mut self, name: &str) -> Result<&mut Self, Box> { + //let port = self.client + //.as_ref() + //.expect("Not initialized.") + //.as_client() + //.register_port(name, MidiIn::default())?; + //self.midi_ins.insert(name.into(), port); + //Ok(self) + //} + //fn add_midi_out (&mut self, name: &str) -> Result<&mut Self, Box> { + //let port = self.client + //.as_ref() + //.expect("Not initialized.") + //.as_client() + //.register_port(name, MidiOut::default())?; + //self.midi_outs.insert(name.into(), port); + //Ok(self) + //} + //fn read_inputs (&self) { + //// read input buffers + ////println!("read"); + //} + //fn write_outputs (&self) { + //// clear output buffers + //// write output buffers + ////println!("write"); + //} +//} -struct Notifications; +//struct Notifications; -impl NotificationHandler for Notifications { - fn thread_init (&self, _: &Client) { - } +//impl NotificationHandler for Notifications { + //fn thread_init (&self, _: &Client) { + //} - fn shutdown (&mut self, status: ClientStatus, reason: &str) { - } + //fn shutdown (&mut self, status: ClientStatus, reason: &str) { + //} - fn freewheel (&mut self, _: &Client, is_enabled: bool) { - } + //fn freewheel (&mut self, _: &Client, is_enabled: bool) { + //} - fn sample_rate (&mut self, _: &Client, _: Frames) -> Control { - Control::Quit - } + //fn sample_rate (&mut self, _: &Client, _: Frames) -> Control { + //Control::Quit + //} - fn client_registration (&mut self, _: &Client, name: &str, is_reg: bool) { - } + //fn client_registration (&mut self, _: &Client, name: &str, is_reg: bool) { + //} - fn port_registration (&mut self, _: &Client, port_id: PortId, is_reg: bool) { - } + //fn port_registration (&mut self, _: &Client, port_id: PortId, is_reg: bool) { + //} - fn port_rename (&mut self, _: &Client, id: PortId, old: &str, new: &str) -> Control { - Control::Continue - } + //fn port_rename (&mut self, _: &Client, id: PortId, old: &str, new: &str) -> Control { + //Control::Continue + //} - fn ports_connected (&mut self, _: &Client, id_a: PortId, id_b: PortId, are: bool) { - } + //fn ports_connected (&mut self, _: &Client, id_a: PortId, id_b: PortId, are: bool) { + //} - fn graph_reorder (&mut self, _: &Client) -> Control { - Control::Continue - } + //fn graph_reorder (&mut self, _: &Client) -> Control { + //Control::Continue + //} - fn xrun (&mut self, _: &Client) -> Control { - Control::Continue - } -} + //fn xrun (&mut self, _: &Client) -> Control { + //Control::Continue + //} +//} diff --git a/src/main.rs b/src/main.rs index 26f763ac..10a12e76 100644 --- a/src/main.rs +++ b/src/main.rs @@ -214,8 +214,9 @@ pub fn main_loop ( ) -> Result<(), Box> { let mut stdout = stdout(); let sleep = std::time::Duration::from_millis(16); + let poll = std::time::Duration::from_millis(100); crossterm::terminal::enable_raw_mode()?; - let (tx, input) = channel::(); + let (tx, input) = std::sync::mpsc::channel::(); let exited = Arc::new(AtomicBool::new(false)); let exit_input_thread = exited.clone(); spawn(move || { @@ -225,7 +226,7 @@ pub fn main_loop ( break } // Listen for events and send them to the main thread - if crossterm::event::poll(Duration::from_millis(100)).is_ok() { + if crossterm::event::poll(poll).is_ok() { if tx.send(crossterm::event::read().unwrap()).is_err() { break } diff --git a/src/prelude.rs b/src/prelude.rs index 631a0ac3..3ae2258d 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -4,8 +4,9 @@ pub use std::thread::spawn; pub use std::time::Duration; pub use std::sync::{ Arc, + Mutex, atomic::{AtomicBool, Ordering}, - mpsc::{channel, Sender, Receiver} + mpsc::{self, channel, Sender, Receiver} }; pub use crossterm::{ QueueableCommand, @@ -24,10 +25,16 @@ pub use jack::{ ClosureProcessHandler, Control, Frames, + MidiIn, + MidiOut, NotificationHandler, Port, PortId, + ProcessHandler, ProcessScope, + Transport, + TransportState, + TransportStatePosition }; pub type Jack = AsyncClient< N, diff --git a/src/render.rs b/src/render.rs index 8074b79a..c27a73d7 100644 --- a/src/render.rs +++ b/src/render.rs @@ -37,7 +37,7 @@ pub fn render_box ( let edge: String = std::iter::repeat("━").take(w.saturating_sub(2) as usize).collect(); stdout.queue(MoveTo(x, y))?.queue(PrintStyledContent(format!("┏{edge}┓").bold().yellow()))?; if let Some(title) = title { - stdout.queue(MoveTo(x+1, y))?.queue(PrintStyledContent(format!(" {title} ").bold().yellow()))?; + stdout.queue(MoveTo(x+1, y))?.queue(PrintStyledContent(format!(" {title} ").yellow()))?; } for row in y+1..y+h { stdout.queue(MoveTo(x, row))?.queue(PrintStyledContent("┃".bold().yellow()))?; diff --git a/src/transport.rs b/src/transport.rs index 22e141e8..c088c3c1 100644 --- a/src/transport.rs +++ b/src/transport.rs @@ -14,23 +14,62 @@ pub const ACTIONS: [(&'static str, &'static str);3] = [ pub struct Transport { exited: bool, - sample_rate: u64, - position: u64, - bpm: f64, + title: String, + client: ::jack::Client, + transport: ::jack::Transport, + //position: Arc>>, + //poll_thread: std::thread::JoinHandle<()>, } impl Transport { pub fn new () -> Result> { + let (client, status) = Client::new( + "bloop_transport_client", + ClientOptions::NO_START_SERVER + )?; + // Poll transport state every 10ms + //let poll = std::time::Duration::from_millis(16); + //let poll_thread = { + //let transport = transport.clone(); + //let position = position.clone(); + //std::thread::spawn(move || loop { + //std::thread::sleep(poll); + //match transport.query() { + //Ok(state) => *position.lock().unwrap() = Some(state), + //Err(error) => { + //*position.lock().unwrap() = None; + ////println!("{error:?}"); + //} + //} + //}) + //}; Ok(Self { - exited: false, - sample_rate: 48000, - position: 0, - bpm: 120.0 + exited: false, + title: String::from("Untitled project"), + transport: client.transport(), + client, + //position, + //poll_thread }) } + pub fn play_from_start_or_stop_and_rewind (&mut self) { } - pub fn play_or_pause (&mut self) { + + pub fn play_or_pause (&mut self) -> Result<(), Box> { + match self.transport.query_state()? { + TransportState::Stopped => self.play(), + TransportState::Rolling => self.stop(), + _ => Ok(()) + } + } + + pub fn play (&mut self) -> Result<(), Box> { + Ok(self.transport.start()?) + } + + pub fn stop (&mut self) -> Result<(), Box> { + Ok(self.transport.stop()?) } } diff --git a/src/transport/handle.rs b/src/transport/handle.rs index f1c88fff..e5e1baec 100644 --- a/src/transport/handle.rs +++ b/src/transport/handle.rs @@ -1,6 +1,6 @@ use crate::prelude::*; use super::*; -pub fn handle (state: &mut Transport, event: &Event) -> Result<(), Box> { +pub fn handle (state: &mut super::Transport, event: &Event) -> Result<(), Box> { Ok(()) } diff --git a/src/transport/render.rs b/src/transport/render.rs index 5f6e3b02..038a2fdd 100644 --- a/src/transport/render.rs +++ b/src/transport/render.rs @@ -7,31 +7,58 @@ pub fn render ( mut offset: (u16, u16) ) -> Result<(), Box> { let move_to = |col, row| MoveTo(offset.0 + col, offset.1 + row); + let position = state.transport.query(); stdout.queue(move_to( 1, 0))?.queue( Print("Project: ") )?.queue(move_to(10, 0))?.queue( - PrintStyledContent("The Quick Brown Fox - Jumping Over Lazy Dogs".white().bold()) - )?.queue(move_to( 1, 1))?.queue( - Print("Rate: ") - )?.queue(move_to( 7, 1))?.queue( - PrintStyledContent("48000Hz".white().bold()) - )?.queue(move_to(20, 1))?.queue( - Print("BPM: ") - )?.queue(move_to(25, 1))?.queue( - PrintStyledContent("120.34".white().bold()) - )?.queue(move_to(35, 1))?.queue( - Print("Signature: ") - )?.queue(move_to(46, 1))?.queue( - PrintStyledContent("4 / 4".white().bold()) - )?.queue(move_to( 1, 2))?.queue( - Print("Time: ") - )?.queue(move_to( 7, 2))?.queue( - PrintStyledContent("1m 23.456s".white().bold()) - )?.queue(move_to(20, 2))?.queue( - Print("Beat: ") - )?.queue(move_to(26, 2))?.queue( - PrintStyledContent("30x 3/4".white().bold()) + PrintStyledContent(state.title.clone().white().bold()) )?; + + if let Ok(position) = state.transport.query() { + let frame = position.pos.frame(); + let rate = position.pos.frame_rate(); + let bbt = position.pos.bbt(); + stdout + .queue(move_to( 1, 1))?.queue(Print("Frame: "))? + .queue(move_to( 1, 2))?.queue( + PrintStyledContent( + format!("{frame}").white().bold(), + ))? + .queue(move_to(11, 1))?.queue(Print("Rate: "))? + .queue(move_to(11, 2))?.queue( + PrintStyledContent(match rate { + Some(rate) => format!("{rate}Hz"), + None => String::from("(none)"), + }.white().bold()) + )? + .queue(move_to(20, 1))?.queue(Print("Time: "))? + .queue(move_to(20, 2))?.queue( + PrintStyledContent(match rate { + Some(rate) => format!("{:.03}", frame as f64 / rate as f64), + None => String::from("(none)") + }.white().bold()) + )? + .queue(move_to(30, 1))?.queue(Print("BPM: "))? + .queue(move_to(30, 2))?.queue( + PrintStyledContent(match bbt { + Some(bbt) => format!("{:.01}", bbt.bpm), + None => String::from("(none)") + }.white().bold()) + )? + .queue(move_to(39, 1))?.queue(Print("Timesig: "))? + .queue(move_to(39, 2))?.queue( + PrintStyledContent(match bbt { + Some(bbt) => format!("{}/{}", bbt.sig_num, bbt.sig_denom), + None => String::from("(none)") + }.white().bold()) + )? + .queue(move_to(50, 1))?.queue(Print("Beat: "))? + .queue(move_to(50, 2))?.queue( + PrintStyledContent(match bbt { + Some(bbt) => format!("{}.{}.{}", bbt.bar, bbt.beat, bbt.tick), + None => String::from("(none)") + }.white().bold()) + )?; + } Ok(()) } -