diff --git a/src/device/launcher.rs b/src/device/launcher.rs index 27b386cd..81bba439 100644 --- a/src/device/launcher.rs +++ b/src/device/launcher.rs @@ -7,6 +7,7 @@ pub struct Launcher { monitoring: bool, recording: bool, overdub: bool, + position: usize, cursor: (usize, usize), tracks: Vec>, chains: Vec>, @@ -14,6 +15,7 @@ pub struct Launcher { show_help: bool, view: LauncherView, } +#[derive(PartialEq)] pub enum LauncherView { Tracks, Sequencer, @@ -49,6 +51,7 @@ impl Launcher { overdub: true, transport, cursor: (1, 2), + position: 0, scenes: vec![ Scene::new(&"Scene#01", &[Some(0), None, None, None]), Scene::new(&"Scene#02", &[None, None, None, None]), @@ -126,16 +129,19 @@ impl Launcher { } impl DevicePorts for Launcher {} pub fn process (state: &mut Launcher, _: &Client, _: &ProcessScope) -> Control { - state.playing = state.transport.query_state().unwrap(); + let transport = state.transport.query().unwrap(); + state.playing = transport.state; + state.position = transport.pos.frame() as usize; Control::Continue } pub fn render (state: &Launcher, buf: &mut Buffer, area: Rect) -> Usually { let Rect { x, y, width, height } = area; - 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); + draw_bpm(buf, x + 33, y, state.timebase.tempo()); + draw_timer(buf, x + width - 1, y, &state.timebase, state.position); 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 }; @@ -154,20 +160,28 @@ pub fn render (state: &Launcher, buf: &mut Buffer, area: Rect) -> Usually let style = Some(Style::default().green().dim()); let chain = &*state.chains[0].state(); let (_, plugins) = crate::device::chain::draw_as_row(chain, buf, chain_area, style)?; - 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: 18, ..chain_area }, style), - }; + + if state.view == LauncherView::Tracks { + draw_box_styled(buf, track_area, style); + } draw_highlight(state, buf, &track_highlight, match state.view { LauncherView::Tracks => Style::default().green().not_dim(), _ => Style::default().green().dim() }); + + if state.view == LauncherView::Chains { + draw_box_styled(buf, Rect { height: 18, ..chain_area }, style); + } draw_highlight(state, buf, &Some(plugins[chain.focus]), match state.view { LauncherView::Chains => Style::default().green().not_dim(), _ => Style::default().green().dim() }); + + if state.view == LauncherView::Sequencer { + draw_box_styled(buf, seq_area, style); + } draw_sequencer(state, buf, seq_area.x, seq_area.y + 1, seq_area.width, seq_area.height - 2)?; + if state.show_help { let style = Some(Style::default().bold().white().not_dim().on_black().italic()); let hide = "[Left/Right] Track [Up/Down] Scene [,/.] Value [F1] Toggle help "; @@ -175,6 +189,20 @@ pub fn render (state: &Launcher, buf: &mut Buffer, area: Rect) -> Usually } Ok(area) } +fn draw_bpm (buf: &mut Buffer, x: u16, y: u16, tempo: usize) { + let style = Style::default().not_dim(); + "BPM" + .blit(buf, x, y, Some(style)); + format!("{:03}.{:03}", tempo / 1000, tempo % 1000) + .blit(buf, x + 4, y, Some(style.bold())); +} +fn draw_timer (buf: &mut Buffer, x: u16, y: u16, timebase: &Arc, frame: usize) { + let tick = (frame as f64 / timebase.frames_per_tick()) as usize; + let (beats, ticks) = (tick / timebase.ppq(), tick % timebase.ppq()); + let (bars, beats) = (beats / 4, beats % 4); + let timer = format!("{}.{}.{ticks:02}", bars + 1, beats + 1); + timer.blit(buf, x - timer.len() as u16, y, Some(Style::default().not_dim())); +} fn draw_scenes ( state: &Launcher, buf: &mut Buffer, x: u16, y: u16, ) -> Rect { diff --git a/src/device/sequencer.rs b/src/device/sequencer.rs index 697f5c46..7e6b2a24 100644 --- a/src/device/sequencer.rs +++ b/src/device/sequencer.rs @@ -131,7 +131,7 @@ impl Sequencer { // Read from sequence into output buffer if self.playing == TransportState::Rolling { let frame = transport.pos.frame() as usize; - let quant = self.timebase.fpb() as usize * self.steps / self.resolution; + let quant = self.timebase.frames_per_beat() as usize * self.steps / self.resolution; let ticks = self.timebase.frames_to_ticks(frame, frame + frames, quant); for (time, tick) in ticks.iter() { if let Some(events) = sequence.get(&(*tick as u32)) { @@ -235,13 +235,13 @@ fn render (s: &Sequencer, buf: &mut Buffer, mut area: Rect) -> Usually { let Rect { x, y, width, .. } = area; let (time0, time1) = s.time_axis; let (note0, note1) = s.note_axis; - let pos = s.transport.query().unwrap().pos; - let frame = pos.frame(); - let usecs = s.timebase.frame_to_usec(frame as usize); - let usec_per_step = s.timebase.usec_per_step(s.resolution as usize); - let steps = usecs / usec_per_step; + let pos = s.transport.query().unwrap().pos; + let frame = pos.frame(); + let usecs = s.timebase.frame_to_usec(frame as usize); + let ustep = s.timebase.usec_per_step(s.resolution as usize); + let steps = usecs / ustep; let header = draw_header(s, buf, area, steps)?; - let piano = match s.mode { + let piano = match s.mode { SequencerView::Tiny => Rect { x, y, width, height: 0 }, SequencerView::Compact => @@ -273,7 +273,7 @@ pub fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) -> let step = beat % s.steps; let reps = s.steps / s.resolution; let steps = s.steps % s.resolution; - draw_timer(buf, x + width - 2, y + 1, rep, step, reps, steps); + draw_timer(buf, x + width - 2, y + 1, &format!("{rep}.{step:02} / {reps}.{steps}")); let style = Style::default().gray(); draw_play_stop(buf, x + 2, y + 1, &s.playing); let separator = format!("├{}┤", "-".repeat((area.width - 2).into())); @@ -285,11 +285,10 @@ pub fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) -> Ok(Rect { x, y, width: area.width, height: 3 }) } pub fn draw_timer ( - buf: &mut Buffer, x: u16, y: u16, rep: usize, step: usize, reps: usize, steps: usize + buf: &mut Buffer, x: u16, y: u16, timer: &str ) { - 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())); + let style = Some(Style::default().gray().bold().not_dim()); + timer.blit(buf, x - timer.len() as u16, y, style); } pub fn draw_play_stop (buf: &mut Buffer, x: u16, y: u16, state: &TransportState) { let style = Style::default().gray(); @@ -554,13 +553,13 @@ fn quantize_prev (s: &mut Sequencer) -> Usually { let mut s = sequencer.state.lock().unwrap(); s.rate = Hz(48000); s.tempo = Tempo(240_000); - println!("F/S = {:.03}", s.fps()); - println!("B/S = {:.03}", s.bps()); - println!("F/B = {:.03}", s.fpb()); - println!("T/B = {:.03}", s.tpb()); - println!("F/T = {:.03}", s.fpt()); - println!("F/L = {:.03}", s.fpl()); - println!("T/L = {:.03}", s.tpl()); + println!("F/S = {:.03}", s.frames_per_second()); + println!("B/S = {:.03}", s.beats_per_secon()); + println!("F/B = {:.03}", s.frames_per_beat()); + println!("T/B = {:.03}", s.ticks_per_beat()); + println!("F/T = {:.03}", s.frames_per_tick()); + println!("F/L = {:.03}", s.frames_per_loop()); + println!("T/L = {:.03}", s.ticks_per_loop()); let fpt = s.fpt(); let frames_per_chunk = 240; let chunk = |chunk: usize| s.frames_to_ticks( diff --git a/src/time.rs b/src/time.rs index b33aadcc..5e6854e1 100644 --- a/src/time.rs +++ b/src/time.rs @@ -32,48 +32,71 @@ pub enum NoteDuration { } impl Timebase { - pub fn rate (&self) -> usize { + #[inline] pub fn rate (&self) -> usize { self.rate.load(Ordering::Relaxed) } - pub fn tempo (&self) -> usize { + #[inline] pub fn tempo (&self) -> usize { self.tempo.load(Ordering::Relaxed) } - pub fn ppq (&self) -> usize { + #[inline] pub fn ppq (&self) -> usize { self.ppq.load(Ordering::Relaxed) } - - /// Beats per second - #[inline] fn bps (&self) -> f64 { + #[inline] fn beats_per_second (&self) -> f64 { self.tempo() as f64 / 60000.0 } - /// Frames per second - #[inline] fn fps (&self) -> usize { + #[inline] fn frames_per_second (&self) -> usize { self.rate() } - /// Frames per beat - #[inline] pub fn fpb (&self) -> f64 { - self.fps() as f64 / self.bps() + #[inline] pub fn frames_per_beat (&self) -> f64 { + self.frames_per_second() as f64 / self.beats_per_second() } - /// Frames per tick FIXME double times - #[inline] fn fpt (&self) -> f64 { - self.fps() as f64 / self.tps() + #[inline] pub fn frames_per_tick (&self) -> f64 { + self.frames_per_second() as f64 / self.ticks_per_second() } - /// Frames per loop - #[inline] fn fpl (&self, steps: f64, steps_per_beat: f64) -> f64 { - self.fpb() * steps / steps_per_beat + #[inline] fn frames_per_loop (&self, steps: f64, steps_per_beat: f64) -> f64 { + self.frames_per_beat() * steps / steps_per_beat } - /// Ticks per beat - #[inline] fn tpb (&self) -> f64 { + #[inline] fn ticks_per_beat (&self) -> f64 { self.ppq.load(Ordering::Relaxed) as f64 } - /// Ticks per second - #[inline] fn tps (&self) -> f64 { - self.bps() * self.tpb() + #[inline] fn ticks_per_second (&self) -> f64 { + self.beats_per_second() * self.ticks_per_beat() + } + #[inline] pub fn frame_to_usec (&self, frame: usize) -> usize { + frame * 1000000 / self.rate() + } + #[inline] pub fn usec_to_frame (&self, usec: usize) -> usize { + usec * self.rate() / 1000 + } + #[inline] pub fn usec_per_bar (&self, beats_per_bar: usize) -> usize { + self.usec_per_beat() * beats_per_bar + } + #[inline] pub fn usec_per_beat (&self) -> usize { + 60_000_000_000 / self.tempo() + } + #[inline] pub fn usec_per_step (&self, divisor: usize) -> usize { + self.usec_per_beat() / divisor + } + #[inline] pub fn usec_per_tick (&self) -> usize { + self.usec_per_beat() / self.ppq() + } + #[inline] pub fn note_to_usec (&self, note: &NoteDuration) -> usize { + match note { + NoteDuration::Nth(time, flies) => + self.usec_per_beat() * *time / *flies, + NoteDuration::Dotted(note) => + self.note_to_usec(note) * 3 / 2, + NoteDuration::Tuplet(n, note) => + self.note_to_usec(note) * 2 / *n, + } + } + #[inline] pub fn note_to_frame (&self, note: &NoteDuration) -> usize { + self.usec_to_frame(self.note_to_usec(note)) } pub fn frames_to_ticks (&self, start: usize, end: usize, fpl: usize) -> Vec<(usize, usize)> { let start_frame = start % fpl; let end_frame = end % fpl; - let fpt = self.fpt(); + let fpt = self.frames_per_tick(); let mut ticks = vec![]; let mut add_frame = |frame: f64|{ let jitter = frame.rem_euclid(fpt); @@ -107,48 +130,8 @@ impl Timebase { } ticks } - - #[inline] - pub fn frame_to_usec (&self, frame: usize) -> usize { - frame * 1000000 / self.rate() - } - #[inline] - pub fn usec_to_frame (&self, usec: usize) -> usize { - usec * self.rate() / 1000 - } - #[inline] - pub fn usec_per_bar (&self, beats_per_bar: usize) -> usize { - self.usec_per_beat() * beats_per_bar - } - #[inline] - pub fn usec_per_beat (&self) -> usize { - 60_000_000_000 / self.tempo() - } - #[inline] - pub fn usec_per_step (&self, divisor: usize) -> usize { - self.usec_per_beat() / divisor - } - #[inline] - pub fn usec_per_tick (&self) -> usize { - self.usec_per_beat() / self.ppq() - } - #[inline] - pub fn usec_per_note (&self, note: &NoteDuration) -> usize { - match note { - NoteDuration::Nth(time, flies) => - self.usec_per_beat() * *time / *flies, - NoteDuration::Dotted(note) => - self.usec_per_note(note) * 3 / 2, - NoteDuration::Tuplet(n, note) => - self.usec_per_note(note) * 2 / *n, - } - } - #[inline] - pub fn frame_per_note (&self, note: &NoteDuration) -> usize { - self.usec_to_frame(self.usec_per_note(note)) - } pub fn quantize (&self, step: &NoteDuration, time: usize) -> (usize, usize) { - let step = self.usec_per_note(step); + let step = self.note_to_usec(step); let time = time / step; let offset = time % step; (time, offset)