From 0ee9e58dc7e21d51b2694317423cf2fe4af5a0c1 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Wed, 5 Jun 2024 22:19:28 +0300 Subject: [PATCH] use mutex instead of msg passing between input and render threads --- src/engine.rs | 152 ++++++++++++++++++++-------------------- src/sequencer/render.rs | 27 ++++++- 2 files changed, 100 insertions(+), 79 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 52547bd7..57e27e65 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -25,111 +25,109 @@ pub enum Event { } pub struct Engine { - stdout: Stdout, exited: Arc, sender: Sender, receiver: Receiver, - input_thread: JoinHandle<()>, pub jack_client: Jack, } +pub fn jack_client ( + name: &str, + notifications: N, + handler: BoxedProcessHandler +) -> Result, Box> { + let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?; + Ok(client.activate_async(notifications, ClosureProcessHandler::new(handler))?) +} + impl Engine { pub fn new (name: Option<&str>) -> Result> { let (sender, receiver) = mpsc::channel::(); let exited = Arc::new(AtomicBool::new(false)); - - let jack_client = { - let sender = sender.clone(); - let exited = exited.clone(); - let (client, _status) = Client::new( + Ok(Self { + jack_client: jack_client( name.unwrap_or("blinkenlive"), - ClientOptions::NO_START_SERVER - )?; - let handler = ClosureProcessHandler::new( - Box::new(move |_client: &Client, _ps: &ProcessScope| -> Control { - if exited.fetch_and(true, Ordering::Relaxed) { - Control::Quit - } else { - sender.send(Event::Update).unwrap(); - Control::Continue - } - }) as BoxedProcessHandler - ); + Notifications(sender.clone()), + Box::new({ + let sender = sender.clone(); + let exited = exited.clone(); + move |_client: &Client, _ps: &ProcessScope| -> Control { + if exited.fetch_and(true, Ordering::Relaxed) { + Control::Quit + } else { + sender.send(Event::Update).unwrap(); + Control::Continue + } + }}))?, + exited, + sender, + receiver, + }) + } - client.activate_async(Notifications, handler)? - }; + pub fn run ( + &mut self, mut state: T, + ) -> Result<(), Box> { + + let state = Arc::new(Mutex::new(state)); let input_thread = { - let sender = sender.clone(); - let exited = exited.clone(); + let state = state.clone(); + let sender = self.sender.clone(); + let exited = self.exited.clone(); let poll = std::time::Duration::from_millis(100); - spawn(move || { - loop { - // Exit if flag is set - if exited.fetch_and(true, Ordering::Relaxed) { + spawn(move || loop { + // Exit if flag is set + if exited.fetch_and(true, Ordering::Relaxed) { + break + } + // Listen for events and send them to the main thread + if event::poll(poll).is_ok() { + let event = event::read().unwrap(); + let mut state = state.lock().unwrap(); + if state.handle(&Event::Input(event)).is_err() { break } - // Listen for events and send them to the main thread - if event::poll(poll).is_ok() { - let event = event::read().unwrap(); - if sender.send(Event::Input(event)).is_err() { - break - } - } } }) }; - Ok(Self { - stdout: stdout(), - exited, - sender, - receiver, - jack_client, - input_thread, - }) - } + let render_thread = { + stdout().queue(EnterAlternateScreen)?.flush()?; + enable_raw_mode()?; + let mut terminal = ratatui::Terminal::new(CrosstermBackend::new(stdout()))?; + let sleep = std::time::Duration::from_millis(16); + let exited = self.exited.clone(); + spawn(move || loop { + terminal.draw(|frame|{ + let area = frame.size(); + frame.render_widget( + &*state.lock().unwrap(), + area + ); + }); + if state.lock().unwrap().exited() { + exited.store(true, Ordering::Relaxed); + break + } + std::thread::sleep(sleep); + }) + }; + + render_thread.join(); + + stdout() + .queue(crossterm::terminal::LeaveAlternateScreen)? + .flush()?; + crossterm::terminal::disable_raw_mode()?; - pub fn run ( - &mut self, mut state: T, - ) -> Result<(), Box> { - stdout().queue(EnterAlternateScreen)?.flush(); - enable_raw_mode()?; - let mut terminal = ratatui::Terminal::new(CrosstermBackend::new(stdout()))?; - let sleep = std::time::Duration::from_millis(20); - loop { - //stdout() - //.queue(crossterm::terminal::BeginSynchronizedUpdate)? - //.queue(Clear(ClearType::All))?; - terminal.draw(|frame|{ - let area = frame.size(); - frame.render_widget(&state, area); - }); - //render(state, stdout, (0, 0))?; - //stdout() - //.queue(crossterm::terminal::EndSynchronizedUpdate)? - //.flush()?; - // Handle event if present (`None` redraws) - if let event = self.receiver.recv()? { - state.handle(&event)?; - } - if state.exited() { - self.exited.store(true, Ordering::Relaxed); - stdout() - .queue(crossterm::terminal::LeaveAlternateScreen)? - .flush()?; - crossterm::terminal::disable_raw_mode()?; - break - } - //std::thread::sleep(sleep); - } Ok(()) } } -pub struct Notifications; +pub struct Notifications(mpsc::Sender); impl NotificationHandler for Notifications { fn thread_init (&self, _: &Client) { diff --git a/src/sequencer/render.rs b/src/sequencer/render.rs index 7c070488..c3b8bbd7 100644 --- a/src/sequencer/render.rs +++ b/src/sequencer/render.rs @@ -18,7 +18,7 @@ const KEYS: [&'static str; 6] = [ impl WidgetRef for Sequencer { fn render_ref (&self, area: Rect, buf: &mut Buffer) { render_sequence_header(area, buf); - render_sequence_keys(area, buf, &self.sequence); + render_sequence_keys(area, buf, &self.transport.query().unwrap(), &self.sequence); render_sequence_cursor(area, buf, self.cursor); let cursor = self.cursor; } @@ -34,9 +34,25 @@ fn render_sequence_header ( } fn render_sequence_keys ( - area: Rect, buf: &mut Buffer, sequence: &Arc>>>> + area: Rect, + buf: &mut Buffer, + transport: &::jack::TransportStatePosition, + sequence: &Arc>>>> ) { + //let transport = state.transport.query()?; + let frame = transport.pos.frame(); + let rate = transport.pos.frame_rate().unwrap(); + let second = (frame as f64) / (rate as f64); + let minute = second / 60f64; + let bpm = 120f64; + let div = 4; + let beats = minute * bpm; + let bars = beats as u32 / div as u32; + let beat = beats as u32 % div as u32 + 1; + let beat_sub = beats % 1.0; + let sequence = sequence.lock().unwrap(); + for key in 0..12 { buf.set_string(area.x - 3, area.y + 1 + key, KEYS[(key % 6) as usize], Style::default().dim()); @@ -53,6 +69,13 @@ fn render_sequence_keys ( buf.set_string(area.x + 48, area.y + 1 + key, "┊", Style::default().black().dim()); for step in 0..64 { + + //let bg = if x as u32 == (beat - 1) * 16 + (beat_sub * 16.0) as u32 { + //crossterm::style::Color::Black + //} else { + //crossterm::style::Color::Reset + //}; + let top = sequence[(key * 2) as usize][step].is_some(); let bottom = sequence[(key * 2 + 1) as usize][step].is_some(); match (top, bottom) {