diff --git a/src/device/launcher.rs b/src/device/launcher.rs index 6dfa709a..e3e1c086 100644 --- a/src/device/launcher.rs +++ b/src/device/launcher.rs @@ -1,13 +1,18 @@ use crate::prelude::*; pub struct Launcher { - name: String, - timebase: Arc, - cursor: (usize, usize), - tracks: Vec>, - chains: Vec>, - scenes: Vec, - show_help: bool, - view: LauncherView, + name: String, + timebase: Arc, + transport: Transport, + playing: TransportState, + monitoring: bool, + recording: bool, + overdub: bool, + cursor: (usize, usize), + tracks: Vec>, + chains: Vec>, + scenes: Vec, + show_help: bool, + view: LauncherView, } pub enum LauncherView { Tracks, @@ -27,16 +32,24 @@ impl Scene { } } impl Launcher { - pub fn new ( - name: &str, - timebase: &Arc, - ) -> Result, Box> { - Ok(DynamicDevice::new(render, handle, process, Self { - name: name.into(), - view: LauncherView::Tracks, - timebase: timebase.clone(), - cursor: (1, 2), - scenes: vec![ + pub fn new (name: &str,) -> Result, Box> { + let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?; + let transport = client.transport(); + let timebase = Arc::new(Timebase { + rate: AtomicUsize::new(client.sample_rate()), + tempo: AtomicUsize::new(113000), + ppq: AtomicUsize::new(96), + }); + DynamicDevice::new(render, handle, process, Self { + name: name.into(), + view: LauncherView::Tracks, + playing: transport.query_state()?, + monitoring: true, + recording: false, + overdub: true, + transport, + cursor: (1, 2), + scenes: vec![ Scene::new(&"Scene#01", &[Some(0), None, None, None]), Scene::new(&"Scene#02", &[None, None, None, None]), Scene::new(&"Scene#03", &[None, None, None, None]), @@ -46,13 +59,13 @@ impl Launcher { Scene::new(&"Scene#07", &[None, None, None, None]), Scene::new(&"Scene#08", &[None, None, None, None]), ], - tracks: vec![ - Sequencer::new("Drum", timebase)?, - Sequencer::new("Bass", timebase)?, - Sequencer::new("Pads", timebase)?, - Sequencer::new("Lead", timebase)?, + tracks: vec![ + Sequencer::new("Drum", &timebase)?, + Sequencer::new("Bass", &timebase)?, + Sequencer::new("Pads", &timebase)?, + Sequencer::new("Lead", &timebase)?, ], - chains: vec![ + chains: vec![ Chain::new("Chain#0000", vec![ Box::new(Plugin::new("Plugin#000")?), ])?, @@ -66,8 +79,9 @@ impl Launcher { Box::new(Plugin::new("Plugin#003")?), ])?, ], + timebase, show_help: true - })) + }).activate(client) } fn cols (&self) -> usize { (self.tracks.len() + 1) as usize @@ -111,26 +125,32 @@ impl Launcher { } } impl DevicePorts for Launcher {} -pub fn process (_: &mut Launcher, _: &Client, _: &ProcessScope) -> Control { +pub fn process (state: &mut Launcher, _: &Client, _: &ProcessScope) -> Control { + state.playing = state.transport.query_state().unwrap(); Control::Continue } pub fn render (state: &Launcher, buf: &mut Buffer, area: Rect) -> Usually { let Rect { x, y, width, height } = area; - let track_area = Rect { x: x, y: y, width, height: 22 }; - let seq_area = Rect { x: x, y: y+21, width, height: 20 }; - let chain_area = Rect { x: x, y: y+40, width, height: 22 }; + crate::device::sequencer::draw_timer(buf, x + width - 1, y, 0, 0, 0, 0); + crate::device::sequencer::draw_play_stop(buf, x + 1, y, &state.playing); + crate::device::sequencer::draw_rec(buf, x + 12, y, state.recording); + crate::device::sequencer::draw_mon(buf, x + 19, y, state.monitoring); + crate::device::sequencer::draw_dub(buf, x + 26, y, state.overdub); + let track_area = Rect { x: x, y: y+1, width, height: 22 }; + let seq_area = Rect { x: x, y: y+22, width, height: 20 }; + let chain_area = Rect { x: x, y: y+41, width, height: 21 }; let separator = format!("├{}┤", "-".repeat((width - 2).into())); - let scenes = draw_scenes(state, buf, x, y); - separator.blit(buf, x, y + 2, Some(Style::default().dim())); - separator.blit(buf, x, y + 4, Some(Style::default().dim())); - separator.blit(buf, x, y + 21, Some(Style::default().dim())); - separator.blit(buf, x, y + 40, Some(Style::default().dim())); - let (w, mut highlight) = draw_tracks(state, buf, x, y); + let scenes = draw_scenes(state, buf, x, y + 1); + separator.blit(buf, x, y + 3, Some(Style::default().dim())); + separator.blit(buf, x, y + 5, Some(Style::default().dim())); + separator.blit(buf, x, y + 22, Some(Style::default().dim())); + separator.blit(buf, x, y + 41, Some(Style::default().dim())); + let (w, mut highlight) = draw_tracks(state, buf, track_area.x, track_area.y); if state.col() == 0 { highlight = Some(scenes); } - draw_crossings(state, buf, x + w - 2, y); - draw_box(buf, area); + draw_crossings(state, buf, x + w - 2, y + 1); + draw_box(buf, Rect { x, y: y + 1, width, height: height - 1 }); let style = Some(Style::default().green().dim()); crate::device::chain::draw_as_row( &*state.chains[0].state(), buf, chain_area, style @@ -138,7 +158,7 @@ pub fn render (state: &Launcher, buf: &mut Buffer, area: Rect) -> Usually match state.view { LauncherView::Tracks => draw_box_styled(buf, track_area, style), LauncherView::Sequencer => draw_box_styled(buf, seq_area, style), - LauncherView::Chains => draw_box_styled(buf, Rect { height: 19, ..chain_area }, style), + LauncherView::Chains => draw_box_styled(buf, Rect { height: 18, ..chain_area }, style), }; draw_highlight(state, buf, &highlight); draw_sequencer(state, buf, seq_area.x, seq_area.y + 1, seq_area.width, seq_area.height - 2)?; @@ -305,10 +325,15 @@ pub fn handle (state: &mut Launcher, event: &AppEvent) -> Usually { }) } pub const KEYMAP: &'static [KeyBinding] = keymap!(Launcher { - [Char('r'), NONE, "rename", "rename current element", rename], - [F(1), NONE, "toggle_help", "toggle help", toggle_help], - [Tab, SHIFT, "focus_prev", "focus previous area", focus_prev], - [Tab, NONE, "focus_next", "focus next area", focus_next], + [Char('n'), NONE, "rename", "rename current element", rename], + [F(1), NONE, "toggle_help", "toggle help", toggle_help], + [Tab, SHIFT, "focus_prev", "focus previous area", focus_prev], + [Tab, NONE, "focus_next", "focus next area", focus_next], + [Char(' '), NONE, "play_toggle", "play or pause", play_toggle], + [Char('r'), NONE, "record_toggle", "toggle recording", record_toggle], + [Char('d'), NONE, "overdub_toggle", "toggle overdub", overdub_toggle], + [Char('m'), NONE, "monitor_toggle", "toggle input monitoring", monitor_toggle], + //[Char(' '), SHIFT, "play_start", "play from start", play_start], }); pub const KEYMAP_TRACKS: &'static [KeyBinding] = keymap!(Launcher { [Up, NONE, "cursor_up", "move cursor up", cursor_up], @@ -389,3 +414,32 @@ fn clip_prev (state: &mut Launcher) -> Usually { } Ok(true) } +fn play_toggle (s: &mut Launcher) -> Usually { + s.playing = match s.playing { + TransportState::Stopped => { + s.transport.start()?; + TransportState::Starting + }, + _ => { + s.transport.stop()?; + s.transport.locate(0)?; + TransportState::Stopped + }, + }; + Ok(true) +} +fn play_start (s: &mut Launcher) -> Usually { + unimplemented!() +} +fn record_toggle (s: &mut Launcher) -> Usually { + s.recording = !s.recording; + Ok(true) +} +fn overdub_toggle (s: &mut Launcher) -> Usually { + s.overdub = !s.overdub; + Ok(true) +} +fn monitor_toggle (s: &mut Launcher) -> Usually { + s.monitoring = !s.monitoring; + Ok(true) +} diff --git a/src/device/sequencer.rs b/src/device/sequencer.rs index db9ff714..697f5c46 100644 --- a/src/device/sequencer.rs +++ b/src/device/sequencer.rs @@ -112,7 +112,7 @@ impl Sequencer { }).activate(client) } - fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { + pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { // Update time let mut sequence = &mut self.sequences[self.sequence].notes; @@ -267,24 +267,15 @@ fn render (s: &Sequencer, buf: &mut Buffer, mut area: Rect) -> Usually { })) } -fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) -> Usually { +pub fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) -> Usually { + let Rect { x, y, width, .. } = area; let rep = beat / s.steps; let step = beat % s.steps; let reps = s.steps / s.resolution; let steps = s.steps % s.resolution; - let Rect { x, y, width, .. } = area; + draw_timer(buf, x + width - 2, y + 1, rep, step, reps, steps); let style = Style::default().gray(); - let timer = format!("{rep}.{step:02} / {reps}.{steps}"); - timer.blit(buf, x + width - 2 - timer.len() as u16, y + 1, Some(style.bold().not_dim())); - match s.playing { - TransportState::Rolling => "▶ PLAYING", - TransportState::Starting => "READY ...", - TransportState::Stopped => "⏹ STOPPED", - }.blit(buf, x + 2, y + 1, Some(match s.playing { - TransportState::Stopped => style.dim().bold(), - TransportState::Starting => style.not_dim().bold(), - TransportState::Rolling => style.not_dim().white().bold() - })); + draw_play_stop(buf, x + 2, y + 1, &s.playing); let separator = format!("├{}┤", "-".repeat((area.width - 2).into())); separator.blit(buf, x, y + 2, Some(style.dim())); draw_rec(buf, x + 13, y + 1, s.recording); @@ -293,23 +284,41 @@ fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) -> Usu let clips = draw_clips(s, buf, area)?; Ok(Rect { x, y, width: area.width, height: 3 }) } - -fn draw_rec (buf: &mut Buffer, x: u16, y: u16, on: bool) { +pub fn draw_timer ( + buf: &mut Buffer, x: u16, y: u16, rep: usize, step: usize, reps: usize, steps: usize +) { + let style = Style::default().gray(); + let timer = format!("{rep}.{step:02} / {reps}.{steps}"); + timer.blit(buf, x - timer.len() as u16, y, Some(style.bold().not_dim())); +} +pub fn draw_play_stop (buf: &mut Buffer, x: u16, y: u16, state: &TransportState) { + let style = Style::default().gray(); + match state { + TransportState::Rolling => "▶ PLAYING", + TransportState::Starting => "READY ...", + TransportState::Stopped => "⏹ STOPPED", + }.blit(buf, x, y, Some(match state { + TransportState::Stopped => style.dim().bold(), + TransportState::Starting => style.not_dim().bold(), + TransportState::Rolling => style.not_dim().white().bold() + })); +} +pub fn draw_rec (buf: &mut Buffer, x: u16, y: u16, on: bool) { "⏺ REC".blit(buf, x, y, Some(if on { Style::default().bold().red() } else { Style::default().bold().dim() })) } -fn draw_dub (buf: &mut Buffer, x: u16, y: u16, on: bool) { - "⏺ DUB".blit(buf, x + 20, y + 1, Some(if on { +pub fn draw_dub (buf: &mut Buffer, x: u16, y: u16, on: bool) { + "⏺ DUB".blit(buf, x, y, Some(if on { Style::default().bold().yellow() } else { Style::default().bold().dim() })) } -fn draw_mon (buf: &mut Buffer, x: u16, y: u16, on: bool) { - "⏺ MON".blit(buf, x + 27, y + 1, Some(if on { +pub fn draw_mon (buf: &mut Buffer, x: u16, y: u16, on: bool) { + "⏺ MON".blit(buf, x, y, Some(if on { Style::default().bold().green() } else { Style::default().bold().dim() diff --git a/src/main.rs b/src/main.rs index 0137d74b..729cb73d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,5 @@ fn main () -> Result<(), Box> { let _cli = cli::Cli::parse(); let xdg = microxdg::XdgApp::new("dawdle")?; crate::config::create_dirs(&xdg)?; - let transport = crate::device::Transport::new("Transport")?; - let timebase = transport.state.lock().unwrap().timebase(); - run(Launcher::new("Launcher#0", &timebase)?) + run(Launcher::new("Launcher#0")?) }