From 5afed6f05500e35575f4e22bd92fb1c37bdc79d2 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Fri, 7 Jun 2024 18:16:23 +0300 Subject: [PATCH] group devices --- src/device.rs | 44 ++++++ src/{ => device}/looper.rs | 0 src/{ => device}/mixer.rs | 0 src/{ => device}/sampler.rs | 3 +- src/{ => device}/sequencer.rs | 46 +++--- src/{ => device}/transport.rs | 0 src/main.rs | 261 ++++++++-------------------------- src/render.rs | 60 ++------ 8 files changed, 147 insertions(+), 267 deletions(-) create mode 100644 src/device.rs rename src/{ => device}/looper.rs (100%) rename src/{ => device}/mixer.rs (100%) rename src/{ => device}/sampler.rs (99%) rename src/{ => device}/sequencer.rs (92%) rename src/{ => device}/transport.rs (100%) diff --git a/src/device.rs b/src/device.rs new file mode 100644 index 00000000..39598de1 --- /dev/null +++ b/src/device.rs @@ -0,0 +1,44 @@ +pub mod transport; +pub mod sequencer; +pub mod sampler; +pub mod mixer; +pub mod looper; + +use crate::prelude::*; +use self::transport::*; +use self::sequencer::*; +use self::sampler::*; +use self::mixer::*; +use self::looper::*; + +pub enum Device { + Transport(self::transport::Transport), + Sequencer(self::sequencer::Sequencer), + Sampler(self::sampler::Sampler), + Mixer(self::mixer::Mixer), + Looper(self::looper::Looper), +} + +impl WidgetRef for Device { + fn render_ref (&self, area: Rect, buf: &mut Buffer) { + match self { + Self::Transport(device) => device.render(area, buf), + Self::Sequencer(device) => device.render(area, buf), + Self::Sampler(device) => device.render(area, buf), + Self::Mixer(device) => device.render(area, buf), + Self::Looper(device) => device.render(area, buf), + } + } +} + +impl HandleInput for Device { + fn handle (&mut self, event: &crate::engine::Event) -> Result<(), Box> { + match self { + Self::Transport(device) => device.handle(event), + Self::Sequencer(device) => device.handle(event), + Self::Sampler(device) => device.handle(event), + Self::Mixer(device) => device.handle(event), + Self::Looper(device) => device.handle(event), + } + } +} diff --git a/src/looper.rs b/src/device/looper.rs similarity index 100% rename from src/looper.rs rename to src/device/looper.rs diff --git a/src/mixer.rs b/src/device/mixer.rs similarity index 100% rename from src/mixer.rs rename to src/device/mixer.rs diff --git a/src/sampler.rs b/src/device/sampler.rs similarity index 99% rename from src/sampler.rs rename to src/device/sampler.rs index e6be2cad..37b02c22 100644 --- a/src/sampler.rs +++ b/src/device/sampler.rs @@ -17,7 +17,7 @@ impl Sampler { pub fn new () -> Result> { let exited = Arc::new(AtomicBool::new(false)); let (client, status) = Client::new( - "blinkenlive-sampler", + "Sampler#000", ClientOptions::NO_START_SERVER )?; let samples = vec![ @@ -271,4 +271,3 @@ impl NotificationHandler for Notifications { Control::Continue } } - diff --git a/src/sequencer.rs b/src/device/sequencer.rs similarity index 92% rename from src/sequencer.rs rename to src/device/sequencer.rs index ea583467..6f7a9d73 100644 --- a/src/sequencer.rs +++ b/src/device/sequencer.rs @@ -9,6 +9,7 @@ pub const ACTIONS: [(&'static str, &'static str);4] = [ ]; pub struct Sequencer { + name: Arc, exited: Arc, sequence: Arc>>>>, cursor: (u16, u16, u16), @@ -26,7 +27,8 @@ impl Sequencer { pub fn new (name: Option<&str>) -> Result> { let exited = Arc::new(AtomicBool::new(false)); - let (client, _status) = Client::new(name.unwrap_or("sequencer"), ClientOptions::NO_START_SERVER)?; + let name = name.unwrap_or("sequencer"); + let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?; let mut port = client.register_port("sequence", ::jack::MidiOut::default())?; let sequence: Arc>>>> = Arc::new(Mutex::new(vec![vec![None;64];128])); let beats = 4; @@ -49,6 +51,7 @@ impl Sequencer { frame_steps[*frame] = Some(index); } Ok(Self { + name: name.into(), exited: exited.clone(), sequence: sequence.clone(), cursor: (11, 0, 0), @@ -184,21 +187,18 @@ const KEYS: [&'static str; 6] = [ impl WidgetRef for Sequencer { fn render_ref (&self, area: Rect, buf: &mut Buffer) { + draw_leaf(buf, area, 1, 0, &format!("{} ", &self.name)); + draw_leaf(buf, area, 3, 0, "Channel: 01"); + draw_leaf(buf, area, 5, 0, "Zoom: 1/64"); + draw_leaf(buf, area, 7, 0, "Rate: 1/1"); + + draw_leaf(buf, area, 10, 0, "Inputs: "); + draw_leaf(buf, area, 13, 0, "Outputs: "); { let mut area = area.clone(); area.height = 18; draw_box(buf, area); } - draw_leaf(buf, area, 3, 0, "Channel: 01"); - draw_leaf(buf, area, 5, 0, "Zoom: 1/64"); - draw_leaf(buf, area, 7, 0, "Rate: 1/1"); - { - let title = "(no inputs) -> Melody#000 -> (no outputs)"; - let mut area = area.clone(); - area.x = (area.width - title.len() as u16) / 2; - area.width = title.len() as u16; - draw_leaf(buf, area, 1, 0, title); - } { let mut area = area.inner(&Margin { horizontal: 1, vertical: 3, }); area.x = area.x + 14; @@ -212,10 +212,10 @@ impl WidgetRef for Sequencer { fn draw_sequence_header ( area: Rect, buf: &mut Buffer ) { - buf.set_string(area.x + 3, area.y, "|1.1.", Style::default().dim()); - buf.set_string(area.x + 3 + 16, area.y, "|1.2.", Style::default().dim()); - buf.set_string(area.x + 3 + 32, area.y, "|1.3.", Style::default().dim()); - buf.set_string(area.x + 3 + 48, area.y, "|1.4.", Style::default().dim()); + buf.set_string(area.x + 3, area.y, "╭1.1.", Style::default().dim()); + buf.set_string(area.x + 3 + 16, area.y, "╭1.2.", Style::default().dim()); + buf.set_string(area.x + 3 + 32, area.y, "╭1.3.", Style::default().dim()); + buf.set_string(area.x + 3 + 48, area.y, "╭1.4.", Style::default().dim()); } fn draw_sequence_keys ( @@ -224,11 +224,10 @@ fn draw_sequence_keys ( transport: &::jack::TransportStatePosition, sequence: &Arc>>>> ) { - buf.set_string(area.x + 2, area.y, "╭", Style::default().black()); - buf.set_string(area.x + 2, area.y + 13, "╰", Style::default().black()); - buf.set_string(area.x + 2 + 65, area.y, "╮", Style::default().black()); - buf.set_string(area.x + 2 + 65, area.y + 13, "╯", Style::default().black()); - //let transport = state.transport.query()?; + //buf.set_string(area.x + 2, area.y, "╭", Style::default().dim()); + //buf.set_string(area.x + 2, area.y + 13, "╰", Style::default().dim()); + buf.set_string(area.x + 2 + 65, area.y, "╮", Style::default().dim()); + buf.set_string(area.x + 2 + 65, area.y + 13, "╯", Style::default().dim()); let frame = transport.pos.frame(); let rate = transport.pos.frame_rate().unwrap(); let second = (frame as f64) / (rate as f64); @@ -240,6 +239,9 @@ fn draw_sequence_keys ( let beat = beats as u32 % div as u32 + 1; let beat_sub = beats % 1.0; let sequence = sequence.lock().unwrap(); + buf.set_string( + area.x + area.width - 25, area.y - 2, format!("{bars:04}:{beat:02}.{:02}", (beat_sub * 16.0) as u32), Style::default() + ); for key in 0..12 { buf.set_string(area.x, area.y + 1 + key, KEYS[(key % 6) as usize], Style::default().black()); @@ -268,10 +270,10 @@ fn draw_sequence_keys ( }, (false, false) => if step % 16 == 0 { buf.set_string(area.x + 3 + step as u16, area.y + 1 + key, "┊", - Style::default().black().dim().bg(bg)) + Style::default().black().bg(bg)) } else { buf.set_string(area.x + 3 + step as u16, area.y + 1 + key, "·", - Style::default().black().dim().bg(bg)) + Style::default().black().bg(bg)) }, } } diff --git a/src/transport.rs b/src/device/transport.rs similarity index 100% rename from src/transport.rs rename to src/device/transport.rs diff --git a/src/main.rs b/src/main.rs index cbea56d9..3d2f6c26 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,13 +6,9 @@ use clap::{Parser, Subcommand}; use std::error::Error; pub mod cli; +pub mod device; pub mod prelude; pub mod engine; -pub mod transport; -pub mod mixer; -pub mod looper; -pub mod sampler; -pub mod sequencer; pub mod render; pub mod config; @@ -23,60 +19,57 @@ fn main () -> Result<(), Box> { let cli = cli::Cli::parse(); let xdg = microxdg::XdgApp::new("dawdle")?; crate::config::create_dirs(&xdg)?; - if let Some(command) = cli.command { - run_one(&command) - } else { - run_all() - } -} - -fn run_one (command: &cli::Command) -> Result<(), Box> { let mut engine = crate::engine::Engine::new(None)?; - match command { - cli::Command::Transport => engine.run( - transport::Transport::new(engine.jack_client.as_client())?, + match cli.command { + Some(cli::Command::Transport) => engine.run( + crate::device::transport::Transport::new(engine.jack_client.as_client())?, ), - cli::Command::Mixer => engine.run( - mixer::Mixer::new()?, + Some(cli::Command::Mixer) => engine.run( + crate::device::mixer::Mixer::new()?, ), - cli::Command::Looper => engine.run( - looper::Looper::new()?, + Some(cli::Command::Looper) => engine.run( + crate::device::looper::Looper::new()?, ), - cli::Command::Sampler => engine.run( - sampler::Sampler::new()?, + Some(cli::Command::Sampler) => engine.run( + crate::device::sampler::Sampler::new()?, ), - cli::Command::Sequencer => engine.run( - sequencer::Sequencer::new(Some("sequencer"))?, + Some(cli::Command::Sequencer) => engine.run( + crate::device::sequencer::Sequencer::new(Some("Sequencer"))?, ), + None => engine.run(App { + exited: false, + mode: Mode::Sequencer, + transport: crate::device::transport::Transport::new( + engine.jack_client.as_client() + )?, + focus: 0, + devices: vec![ + crate::device::Device::Sequencer( + crate::device::sequencer::Sequencer::new(Some("Melody#000"))? + ), + crate::device::Device::Sequencer( + crate::device::sequencer::Sequencer::new(Some("Rhythm#000"))? + ), + crate::device::Device::Sampler( + crate::device::sampler::Sampler::new()? + ), + crate::device::Device::Mixer( + crate::device::mixer::Mixer::new()? + ), + crate::device::Device::Looper( + crate::device::looper::Looper::new()? + ), + ] + }) } } -fn run_all () -> Result<(), Box> { - let mut engine = crate::engine::Engine::new(None)?; - engine.run(App { - exited: false, - mode: Mode::Sequencer, - transport: transport::Transport::new(engine.jack_client.as_client())?, - sequencers: vec![ - sequencer::Sequencer::new(Some("Melody#000"))?, - sequencer::Sequencer::new(Some("Rhythm#000"))?, - ], - mixer: mixer::Mixer::new()?, - looper: looper::Looper::new()?, - sampler: sampler::Sampler::new()?, - actions: vec![], - }) -} - struct App { exited: bool, mode: Mode, - transport: crate::transport::Transport, - mixer: crate::mixer::Mixer, - looper: crate::looper::Looper, - sampler: crate::sampler::Sampler, - sequencers: Vec, - actions: Vec<(&'static str, &'static str)>, + transport: crate::device::transport::Transport, + focus: usize, + devices: Vec, } #[derive(PartialEq)] @@ -99,117 +92,23 @@ impl Exitable for App { impl WidgetRef for App { fn render_ref (&self, area: Rect, buffer: &mut Buffer) { - use ratatui::{widgets::*, style::Stylize}; + use ratatui::style::Stylize; + let mut constraints = vec![ + Constraint::Length(4), + Constraint::Max(18), + Constraint::Max(18), + Constraint::Max(0), + Constraint::Max(0), + Constraint::Max(0), + ]; let areas = Layout::default() .direction(Direction::Vertical) - .constraints([ - Constraint::Length(4), - Constraint::Max(18), - Constraint::Max(18), - ].clone()) + .constraints(&constraints) .split(area); - self.transport.render(area, buffer); - self.sequencers[0].render(areas[1], buffer); - self.sequencers[1].render(areas[2], buffer); - - //buffer.set_string( - //area.x, area.y + 5, - //format!(" [ ] Melody#000 │1.1 │1.2 │1.3 │1.4 │ → None "), - //Style::default() - //); - //buffer.set_string( - //area.x, area.y + 6, - //format!(" Variation: │A │B │C │D │E │F │G │H │"), - //Style::default() - //); - //buffer.set_string( - //area.x, area.y + 7, - //format!(" [x] Chain 𝄆 A A B A 𝄇"), - //Style::default() - //); - - //buffer.set_string( - //area.x, area.y, - //format!("│"), - //Style::default().black() - //); - //buffer.set_string( - //area.x + area.width - 1, area.y, - //format!("│"), - //Style::default().black() - //); - //buffer.set_string( - //area.x, area.y + 1, - //format!("╰{}╯", "─".repeat((area.width - 2) as usize)), - //Style::default().black() - //); - - ////Paragraph::new("|Project: The Witty Gerbils - Sha Na Na\n|Rewind |Play |BPM▐▔▔▔▔▔▔▔▔▔▔▔▔▔▔▌\n▐ ⯈ Play/Pause ▌\n▐▁▁▁▁▁▁▁▁▁▁▁▁▁▁▌") - //Paragraph::new("|Project: The Witty Gerbils - Sha Na Na\n|Rewind |Play |Beat 1.1.0 |BPM 113.000 |Time: 123.456 |Rate: 44100Hz |Frame: 2000000000") - //.render(transport_area, buffer); - - //self.transport.render(main[0].inner(&Margin { - //vertical: 0, - //horizontal: 1, - //}), buffer); - //buffer.set_string( - //area.x, main[1].y-1, - //format!("╭{}╮", "─".repeat((area.width - 2) as usize)), - //Style::default().black() - //); - //Paragraph::new("│|Melody #000\n│\n│|Port:\n│|Channel:\n│\n│\n│\n│\n│\n│\n│\n│\n│\n│|Variation:") - //.render(side[1], buffer); - //self.sequencer.render(main[1].inner(&Margin { - //vertical: 0, - //horizontal: 1, - //}), buffer); - //buffer.set_string( - //area.x, main[2].y+1, - //format!("╰{}╯", "─".repeat((area.width - 2) as usize)), - //Style::default().black() - //); - - //Block::default() - //.title("Sampler") - //.borders(Borders::ALL) - //.render(rows[2], buffer); - - //Block::default() - //.title("Mixer") - //.borders(Borders::ALL) - //.render(rows[3], buffer); - - //Block::default() - //.title("Looper") - //.borders(Borders::ALL) - //.render(rows[4], buffer); - - - //let mut offset = (0, 0); - //let (w, h) = render::render_toolbar_vertical(stdout, (offset.0, offset.1 + 1), &actions)?; - //offset.0 = offset.0 + 20; - - //transport::render(&mut state.transport, stdout, (offset.0 + 1, 1))?; - //render::render_box(stdout, Some("Transport"), offset.0, 0, 70, 4, - //state.mode == Mode::Transport)?; - - //sequencer::render(&mut state.sequencer, stdout, (offset.0 + 1, 3))?; - //render::render_box(stdout, Some("Sequencer"), offset.0, 5, 70, 6, - //state.mode == Mode::Sequencer)?; - - //sampler::render(&mut state.sampler, stdout, (offset.0 + 1, 10))?; - //render::render_box(stdout, Some("Sampler"), offset.0, 12, 70, 4, - //state.mode == Mode::Sampler)?; - - //mixer::render(&mut state.mixer, stdout, (offset.0 + 1, 18))?; - //render::render_box(stdout, Some("Mixer"), offset.0, 17, 70, 9, - //state.mode == Mode::Mixer)?; - - //looper::render(&mut state.looper, stdout, (offset.0 + 1, 28))?; - //render::render_box(stdout, Some("Looper"), offset.0, 27, 70, 6, - //state.mode == Mode::Looper)?; - - //Ok(()) + self.transport.render(areas[0], buffer); + for (index, device) in self.devices.iter().enumerate() { + device.render(areas[index + 1], buffer); + } } } @@ -231,54 +130,20 @@ impl HandleInput for App { } }, KeyCode::Tab => { - match self.mode { - Mode::Transport => self.mode = Mode::Sequencer, - Mode::Sequencer => self.mode = Mode::Sampler, - Mode::Sampler => self.mode = Mode::Mixer, - Mode::Mixer => self.mode = Mode::Looper, - Mode::Looper => self.mode = Mode::Transport, - _ => {} - }; - self.actions.clear(); - self.actions.extend_from_slice(&transport::ACTIONS); - match self.mode { - Mode::Transport => {}, - Mode::Mixer => self.actions.extend_from_slice(&mixer::ACTIONS), - Mode::Looper => self.actions.extend_from_slice(&looper::ACTIONS), - Mode::Sampler => self.actions.extend_from_slice(&sampler::ACTIONS), - Mode::Sequencer => self.actions.extend_from_slice(&sequencer::ACTIONS), + self.focus = self.focus + 1; + if self.focus >= self.devices.len() { + self.focus = 0; } }, KeyCode::BackTab => { - match self.mode { - Mode::Transport => self.mode = Mode::Looper, - Mode::Sequencer => self.mode = Mode::Transport, - Mode::Sampler => self.mode = Mode::Sequencer, - Mode::Mixer => self.mode = Mode::Sampler, - Mode::Looper => self.mode = Mode::Mixer, - _ => {} - }; - self.actions.clear(); - self.actions.extend_from_slice(&transport::ACTIONS); - match self.mode { - Mode::Transport => {}, - Mode::Mixer => self.actions.extend_from_slice(&mixer::ACTIONS), - Mode::Looper => self.actions.extend_from_slice(&looper::ACTIONS), - Mode::Sampler => self.actions.extend_from_slice(&sampler::ACTIONS), - Mode::Sequencer => self.actions.extend_from_slice(&sequencer::ACTIONS), + if self.focus == 0 { + self.focus = self.devices.len() - 1; + } else { + self.focus = self.focus - 1; } }, - _ => match self.mode { - Mode::Transport => self.transport - .handle(&event)?, - Mode::Mixer => self.mixer - .handle(&event)?, - Mode::Looper => self.looper - .handle(&event)?, - Mode::Sampler => self.sampler - .handle(&event)?, - Mode::Sequencer => self.sequencers[0] - .handle(&event)?, + _ => { + self.devices[self.focus].handle(&event)? } } } diff --git a/src/render.rs b/src/render.rs index 724363d4..f2a1002f 100644 --- a/src/render.rs +++ b/src/render.rs @@ -29,57 +29,27 @@ impl<'a> WidgetRef for ActionBar<'a> { pub fn draw_leaf (buffer: &mut Buffer, area: Rect, y: u16, x: u16, text: &str) { use ratatui::style::{Style, Stylize}; - buffer.set_string( - area.x + x, area.y + y, - format!("│"), - Style::default().black().dim() - ); - buffer.set_string( - area.x + x + 1, area.y + y, - format!("{text}"), - Style::default() - ); - buffer.set_string( - area.x + x + text.len() as u16 + 1, area.y + y, - format!("│"), - Style::default().black().dim() - ); - buffer.set_string( - area.x + x, area.y + 1 + y, - format!("╰{}╯", "─".repeat(text.len() as usize)), - Style::default().black() - ); + let border = Style::default().gray().dim(); + let label = Style::default(); + let side = String::from("│"); + let bottom = format!("╰{}╯", "─".repeat(text.len() as usize)); + buffer.set_string(area.x + x, area.y + y, &side, border); + buffer.set_string(area.x + x + 1, area.y + y, format!("{text}"), label); + buffer.set_string(area.x + x + text.len() as u16 + 1, area.y + y, &side, border); + buffer.set_string(area.x + x, area.y + 1 + y, bottom, border); } pub fn draw_box (buffer: &mut Buffer, area: Rect) { use ratatui::style::{Style, Stylize}; - buffer.set_string( - area.x, area.y, - format!("╭{}╮", "─".repeat((area.width - 2).into())),//.repeat(area.width.saturating_sub(2).into())), - Style::default().black() - ); + let border = Style::default().gray().dim(); + let top = format!("╭{}╮", "─".repeat((area.width - 2).into())); + let bottom = format!("╰{}╯", "─".repeat((area.width - 2).into())); + buffer.set_string(area.x, area.y, top, border); for y in (area.y + 1)..(area.y + area.height - 1) { - buffer.set_string( - area.x, y, - format!("│"), - Style::default().black().dim() - ); - buffer.set_string( - area.x + area.width - 1, y, - format!("│"), - Style::default().black().dim() - ); + buffer.set_string(area.x, y, format!("│"), border); + buffer.set_string(area.x + area.width - 1, y, format!("│"), border); } - buffer.set_string( - area.x, area.y + area.height - 1, - format!("╰{}╯", "─".repeat((area.width - 2).into())),//.repeat(area.width.saturating_sub(2).into())), - Style::default().black() - ); - //buffer.set_string( - //area.x, area.y + area.height, - //format!("╰{}╯", "─".repeat(area.width.saturating_sub(2).into())), - //Style::default().black() - //); + buffer.set_string(area.x, area.y + area.height - 1, bottom, border); } pub fn render_toolbar_vertical (