diff --git a/Cargo.lock b/Cargo.lock index b95dd46d..23f792a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -84,6 +84,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "atomic_float" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c4b08ed8a30ff7320117c190eb4d73d47f0ac0c930ab853b8224cef7cd9a5e7" + [[package]] name = "autocfg" version = "1.3.0" @@ -299,6 +305,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "fraction" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f158e3ff0a1b334408dc9fb811cd99b446986f4d8b741bb08f9df1604085ae7" +dependencies = [ + "lazy_static", + "num", +] + [[package]] name = "gimli" version = "0.29.0" @@ -534,6 +550,70 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -893,10 +973,12 @@ dependencies = [ name = "tek" version = "0.0.0" dependencies = [ + "atomic_float", "backtrace", "better-panic", "clap", "crossterm", + "fraction", "jack", "livi", "microxdg", diff --git a/Cargo.toml b/Cargo.toml index 40f0cac4..dc4f53de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,5 @@ livi = "0.7.4" #atomic_enum = "0.3.0" wavers = "1.4.3" music-math = "0.1.1" +atomic_float = "1.0.0" +fraction = "0.15.3" diff --git a/src/core/time.rs b/src/core/time.rs index a7c4364d..9db24137 100644 --- a/src/core/time.rs +++ b/src/core/time.rs @@ -2,101 +2,96 @@ use crate::core::*; pub struct Timebase { /// Frames per second - pub rate: AtomicUsize, + pub rate: ::atomic_float::AtomicF64, /// Beats per minute - pub bpm: AtomicUsize, + pub bpm: ::atomic_float::AtomicF64, /// Ticks per beat - pub ppq: AtomicUsize, -} - -/// NoteDuration in musical terms. Has definite usec value -/// for given bpm and sample rate. -pub enum NoteDuration { - Nth(usize, usize), - Dotted(Box), - Tuplet(usize, Box), + pub ppq: ::atomic_float::AtomicF64, } impl Timebase { - #[inline] fn rate (&self) -> usize { + pub fn new (rate: f64, bpm: f64, ppq: f64) -> Self { + Self { rate: rate.into(), bpm: bpm.into(), ppq: ppq.into() } + } + #[inline] fn rate (&self) -> f64 { self.rate.load(Ordering::Relaxed) } - #[inline] fn frame_usec (&self) -> usize { - 1_000_000 / self.rate() + #[inline] fn frame_usec (&self) -> f64 { + 1_000_000 as f64 / self.rate() as f64 } - #[inline] pub fn frame_to_usec (&self, frame: usize) -> usize { - frame * 1000000 / self.rate() + #[inline] pub fn frames_usecs (&self, frame: f64) -> f64 { + frame * self.frame_usec() } - #[inline] pub fn bpm (&self) -> usize { + #[inline] pub fn bpm (&self) -> f64 { self.bpm.load(Ordering::Relaxed) } - #[inline] fn beat_usec (&self) -> usize { - 60_000_000_000_000 / self.bpm() - } - #[inline] fn beats_per_second (&self) -> f64 { - self.bpm() as f64 / 60000000.0 + #[inline] fn beat_usec (&self) -> f64 { + 60_000_000_000_000f64 / self.bpm() as f64 } - #[inline] pub fn ppq (&self) -> usize { + #[inline] pub fn ppq (&self) -> f64 { self.ppq.load(Ordering::Relaxed) } - #[inline] fn pulse_usec (&self) -> usize { - self.beat_usec() / self.ppq() + #[inline] fn pulse_usec (&self) -> f64 { + self.beat_usec() / self.ppq() as f64 } - #[inline] fn pulse_frame (&self) -> usize { - self.pulse_usec() / self.frame_usec() + #[inline] fn pulse_frame (&self) -> f64 { + self.pulse_usec() / self.frame_usec() as f64 } - #[inline] pub fn pulses_frames (&self, pulses: usize) -> usize { + #[inline] pub fn pulses_frames (&self, pulses: f64) -> f64 { self.pulse_frame() * pulses } - #[inline] pub fn frames_pulses (&self, frames: usize) -> usize { + #[inline] pub fn frames_pulses (&self, frames: f64) -> f64 { frames / self.pulse_frame() } - #[inline] pub fn frames_per_tick (&self) -> f64 { - self.rate() as f64 / self.ticks_per_second() - } - #[inline] fn ticks_per_second (&self) -> f64 { - self.beats_per_second() * self.ppq() as f64 - } - #[inline] fn usec_to_frame (&self, usec: usize) -> usize { - usec * self.rate() / 1000 - } - #[inline] pub fn usec_per_step (&self, divisor: usize) -> usize { + #[inline] pub fn usec_per_step (&self, divisor: f64) -> f64 { self.beat_usec() / divisor } - - #[inline] pub fn note_to_usec (&self, note: &NoteDuration) -> usize { - match note { - NoteDuration::Nth(time, flies) => - self.beat_usec() * *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_usec (&self, (num, den): (f64, f64)) -> f64 { + 4.0 * self.beat_usec() * num / den } - #[inline] pub fn note_to_frame (&self, note: &NoteDuration) -> usize { + #[inline] fn usec_to_frame (&self, usec: f64) -> f64 { + usec * self.rate() / 1000.0 + } + #[inline] pub fn note_to_frame (&self, note: (f64, f64)) -> f64 { self.usec_to_frame(self.note_to_usec(note)) } - #[inline] pub fn quantize (&self, step: &NoteDuration, time: usize) -> (usize, usize) { + #[inline] pub fn quantize ( + &self, step: (f64, f64), time: f64 + ) -> (f64, f64) { let step = self.note_to_usec(step); - let time = time / step; - let offset = time % step; - (time, offset) + (time / step, time % step) } - #[inline] pub fn quantize_into (&self, step: &NoteDuration, events: U) -> Vec<(usize, T)> - where U: std::iter::Iterator + Sized + #[inline] pub fn quantize_into ( + &self, step: (f64, f64), events: E + ) -> Vec<(f64, T)> + where E: std::iter::Iterator + Sized { + let step = (step.0.into(), step.1.into()); events .map(|(time, event)|(self.quantize(step, time).0, event)) .collect() } - pub fn frames_to_ticks (&self, start: usize, end: usize, quant: usize) -> Vec<(usize, usize)> { + #[inline] fn beats_per_second (&self) -> f64 { + self.bpm() as f64 / 60000000.0 + } + #[inline] fn ticks_per_second (&self) -> f64 { + self.beats_per_second() * self.ppq() as f64 + } + #[inline] pub fn frames_per_tick (&self) -> f64 { + self.rate() as f64 / self.ticks_per_second() + } + pub fn frames_to_ticks ( + &self, + start: f64, + end: f64, + quant: f64, + ) -> Vec<(usize, usize)> { let start_frame = start % quant; let end_frame = end % quant; let fpt = self.frames_per_tick(); @@ -106,24 +101,24 @@ impl Timebase { let last_jitter = (frame - 1.0).max(0.0) % fpt; let next_jitter = frame + 1.0 % fpt; if jitter <= last_jitter && jitter <= next_jitter { - ticks.push((frame as usize % (end-start), (frame / fpt) as usize)); + ticks.push((frame as usize % (end as usize-start as usize), (frame / fpt) as usize)); }; }; if start_frame < end_frame { - for frame in start_frame..end_frame { + for frame in start_frame as usize..end_frame as usize { add_frame(frame as f64); } } else { - let mut frame = start_frame; + let mut frame = start_frame as usize; loop { add_frame(frame as f64); frame = frame + 1; - if frame >= quant { + if frame >= quant as usize { frame = 0; loop { add_frame(frame as f64); frame = frame + 1; - if frame >= end_frame.saturating_sub(1) { + if frame >= (end_frame as usize).saturating_sub(1) { break } } diff --git a/src/device/launcher/mod.rs b/src/device/launcher/mod.rs index f7dbdfc9..485dd8c4 100644 --- a/src/device/launcher/mod.rs +++ b/src/device/launcher/mod.rs @@ -46,7 +46,7 @@ impl Launcher { ) -> Result, Box> { let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?; let transport = client.transport(); - let ppq = timebase.ppq() as u32; + let ppq = timebase.ppq() as usize; DynamicDevice::new(render, handle, process, Self { name: name.into(), view: LauncherView::Chains, @@ -187,7 +187,7 @@ pub fn render (state: &Launcher, buf: &mut Buffer, mut area: Rect) -> Usually Us timer(buf, x+5, y, sequencer.time_start, sequencer.time_start + area.width as usize, 0); keys(buf, Rect { x, y: y + 1, width, height }, sequencer.note_start)?; if let Some(Some(phrase)) = state.phrase_id().map(|id|sequencer.phrases.get(id)) { - let ppq = sequencer.timebase.ppq() as u32; - let zoom = sequencer.time_zoom as u32; - let t0 = sequencer.time_start as u32; - let t1 = t0 + area.width as u32; - let n0 = sequencer.note_start as u32; - let n1 = n0 + area.height as u32; + let ppq = sequencer.timebase.ppq() as usize; + let zoom = sequencer.time_zoom; + let t0 = sequencer.time_start; + let t1 = t0 + area.width as usize; + let n0 = sequencer.note_start; + let n1 = n0 + area.height as usize; lanes(buf, x, y + 1, &phrase, ppq, zoom, t0, t1, n0, n1); } let cursor_style = match view { diff --git a/src/device/sequencer/handle.rs b/src/device/sequencer/handle.rs index fce09111..b4b1a5fe 100644 --- a/src/device/sequencer/handle.rs +++ b/src/device/sequencer/handle.rs @@ -50,9 +50,10 @@ fn note_add (s: &mut Sequencer) -> Usually { if s.sequence.is_none() { return Ok(false) } - let step = (s.time_start + s.time_cursor) as u32; - let start = (step as usize * s.timebase.ppq() / s.time_zoom) as u32; - let end = ((step + 1) as usize * s.timebase.ppq() / s.time_zoom) as u32; + let ppq = s.timebase.ppq() as usize; + let step = s.time_start + s.time_cursor; + let start = step as usize * ppq / s.time_zoom; + let end = (step + 1) as usize * ppq / s.time_zoom; let key = u7::from_int_lossy((s.note_cursor + s.note_start) as u8); let note_on = MidiMessage::NoteOn { key, vel: 100.into() }; let note_off = MidiMessage::NoteOff { key, vel: 100.into() }; diff --git a/src/device/sequencer/horizontal.rs b/src/device/sequencer/horizontal.rs index 67ec9fd0..90fd5b4b 100644 --- a/src/device/sequencer/horizontal.rs +++ b/src/device/sequencer/horizontal.rs @@ -13,12 +13,12 @@ pub fn draw ( if let Some(phrase) = s.phrase() { lanes(buf, x, y, phrase, - s.timebase.ppq() as u32, - s.time_zoom as u32, - s.time_start as u32, - s.time_start as u32 + area.width as u32, - s.note_start as u32, - s.note_start as u32 + area.height as u32, + s.timebase.ppq() as usize, + s.time_zoom, + s.time_start, + s.time_start + area.width as usize, + s.note_start, + s.note_start + area.height as usize, ); } cursor( @@ -76,17 +76,17 @@ pub fn lanes ( x: u16, y: u16, phrase: &Phrase, - ppq: u32, - time_zoom: u32, - time0: u32, - time1: u32, - note0: u32, - note1: u32, + ppq: usize, + time_zoom: usize, + time0: usize, + time1: usize, + note0: usize, + note1: usize, ) { let bg = Style::default(); let (bw, wh) = (bg.dim(), bg.white()); for step in time0..time1 { - let x = x as u32 + 5 + step; + let x = x as usize + 5 + step; let (a, b) = ((step + 0) * ppq / time_zoom, (step + 1) * ppq / time_zoom,); if step % 4 == 0 { "|".blit(buf, x as u16, y - 1, Some(Style::default().dim())); @@ -95,7 +95,7 @@ pub fn lanes ( format!("{}", step / time_zoom / 4 + 1) .blit(buf, x as u16, y - 1, Some(Style::default().bold().not_dim())); } - let h = ((note1-note0)/2).saturating_sub(y as u32); + let h = ((note1-note0)/2).saturating_sub(y as usize); for k in 0..h { let (character, style) = match ( contains_note_on(phrase, u7::from_int_lossy((note0 + k * 2 + 0) as u8), a, b), @@ -106,7 +106,7 @@ pub fn lanes ( (false, true) => ("▄", wh), (false, false) => ("·", bw), }; - let y = y as u32 + k; + let y = y as usize + k; character.blit(buf, x as u16, y as u16, Some(style)); } } diff --git a/src/device/sequencer/mod.rs b/src/device/sequencer/mod.rs index bad538ef..a7bdae73 100644 --- a/src/device/sequencer/mod.rs +++ b/src/device/sequencer/mod.rs @@ -66,7 +66,7 @@ impl Sequencer { timebase: timebase.clone(), sequence: Some(0), phrases: phrases.unwrap_or_else(||vec![ - Phrase::new("Phrase0", 4*timebase.ppq() as u32, None) + Phrase::new("Phrase0", 4 * timebase.ppq() as usize, None) ]), transport, @@ -95,18 +95,13 @@ impl PortList for Sequencer { } fn render (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually { let Rect { x, y, width, height } = area; - 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.time_zoom as usize); - let steps = usecs / ustep; - let header = draw_header(s, buf, area, steps)?; + let header = draw_header(s, buf, area)?; let piano = match s.view { SequencerView::Tiny => Rect { x, y, width, height: 0 }, SequencerView::Compact => Rect { x, y, width, height: 0 }, SequencerView::Vertical => self::vertical::draw(s, buf, Rect { x, y: y + header.height, width, height, - }, steps)?, + })?, SequencerView::Horizontal => self::horizontal::draw(s, buf, Rect { x, y: y + header.height, width, height, })?, @@ -117,7 +112,7 @@ fn render (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually { height: header.height + piano.height })) } -pub fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect, _: usize) -> Usually { +pub fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually { let Rect { x, y, width, .. } = area; let style = Style::default().gray(); crate::device::transport::draw_play_stop(buf, x + 2, y + 1, &s.playing); @@ -149,7 +144,7 @@ pub fn draw_clips (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually } Ok(Rect { x, y, width: 14, height: 14 }) } -pub fn contains_note_on (sequence: &Phrase, k: u7, start: u32, end: u32) -> bool { +pub fn contains_note_on (sequence: &Phrase, k: u7, start: usize, end: usize) -> bool { for (_, (_, events)) in sequence.notes.range(start..end).enumerate() { for event in events.iter() { match event { diff --git a/src/device/sequencer/phrase.rs b/src/device/sequencer/phrase.rs index b31b8897..49960e60 100644 --- a/src/device/sequencer/phrase.rs +++ b/src/device/sequencer/phrase.rs @@ -1,25 +1,21 @@ use crate::core::*; -pub type PhraseData = BTreeMap>; +pub type PhraseData = BTreeMap>; -pub type MIDIChunk = [Option>>]; +pub type MIDIMessage = Vec; + +pub type MIDIChunk = [Option>]; pub struct Phrase { pub name: String, - pub length: u32, + pub length: usize, pub notes: PhraseData, } impl Phrase { - pub fn new (name: &str, length: u32, notes: Option) -> Self { + pub fn new (name: &str, length: usize, notes: Option) -> Self { Self { name: name.to_string(), length, notes: notes.unwrap_or(BTreeMap::new()) } } - pub fn frames (&self, timebase: &Arc) -> usize { - timebase.pulses_frames(self.length as usize) - } - pub fn frame_to_pulse (&self, timebase: &Arc, frame: usize) -> usize { - timebase.frames_pulses(frame) % self.length as usize - } /** Write a chunk of MIDI events to an output port. */ pub fn chunk_out ( &self, @@ -29,9 +25,13 @@ impl Phrase { frame0: usize, frames: usize, ) { - let ticks = timebase.frames_to_ticks(frame0, frame0 + frames, self.frames(timebase)); + let ticks = timebase.frames_to_ticks( + frame0 as f64, + (frame0 + frames) as f64, + timebase.pulses_frames(self.length as f64) + ); for (time, tick) in ticks.iter() { - let events = self.notes.get(&(*tick as u32)); + let events = self.notes.get(&(*tick as usize)); if events.is_none() { continue } @@ -67,7 +67,7 @@ impl Phrase { ) { for RawMidi { time, bytes } in input { let time = time as usize; - let pulse = timebase.frames_pulses(frame0 + time) as u32; + let pulse = timebase.frames_pulses((frame0 + time) as f64) as usize; if let LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() { if let MidiMessage::NoteOn { key, vel: _ } = message { notes_on[key.as_int() as usize] = true; diff --git a/src/device/sequencer/process.rs b/src/device/sequencer/process.rs index 5b7af3ae..1b123397 100644 --- a/src/device/sequencer/process.rs +++ b/src/device/sequencer/process.rs @@ -32,7 +32,7 @@ pub fn process (state: &mut Sequencer, _: &Client, scope: &ProcessScope) -> Cont ); } // Play from input to monitor, and record into phrase. - //let usecs = state.timebase.frame_to_usec(frame); + //let usecs = state.timebase.frames_usecs(frame); //let steps = usecs / state.timebase.usec_per_step(state.time_zoom as usize); //let step = steps % state.steps; //let tick = (step * state.timebase.ppq() / state.time_zoom) as u32; diff --git a/src/device/sequencer/vertical.rs b/src/device/sequencer/vertical.rs index 4af552ff..c5dc5099 100644 --- a/src/device/sequencer/vertical.rs +++ b/src/device/sequencer/vertical.rs @@ -5,11 +5,10 @@ pub fn draw ( s: &Sequencer, buf: &mut Buffer, mut area: Rect, - beat: usize ) -> Usually { area.x = area.x + 13; - keys(s, buf, area, beat); - steps(s, buf, area, beat); + keys(s, buf, area, 0); + steps(s, buf, area, 0); playhead(s, buf, area.x, area.y); Ok(area) } @@ -18,7 +17,7 @@ pub fn steps (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) { if s.sequence.is_none() { return } - let ppq = s.timebase.ppq() as u32; + let ppq = s.timebase.ppq() as usize; let bg = Style::default(); let bw = bg.dim(); let wh = bg.white(); @@ -34,9 +33,9 @@ pub fn steps (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) { let key = ::midly::num::u7::from_int_lossy(k as u8); if step % 2 == 0 { let (a, b, c) = ( - (step + 0) as u32 * ppq / s.time_zoom as u32, - (step + 1) as u32 * ppq / s.time_zoom as u32, - (step + 2) as u32 * ppq / s.time_zoom as u32, + (step + 0) as usize * ppq / s.time_zoom as usize, + (step + 1) as usize * ppq / s.time_zoom as usize, + (step + 2) as usize * ppq / s.time_zoom as usize, ); let (character, style) = match ( contains_note_on(&s.phrases[s.sequence.unwrap()], key, a, b), @@ -71,7 +70,7 @@ pub fn playhead (s: &Sequencer, buf: &mut Buffer, x: u16, y: u16) { } pub fn keys (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) { - let ppq = s.timebase.ppq() as u32; + let ppq = s.timebase.ppq() as usize; let Rect { x, y, .. } = area; for key in s.note_start..s.note_start+area.width as usize { let x = x + (5 + key - s.note_start) as u16; @@ -83,9 +82,9 @@ pub fn keys (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) { let mut is_on = s.notes_on[key as usize]; let step = beat; let (a, b, c) = ( - (step + 0) as u32 * ppq / s.time_zoom as u32, - (step + 1) as u32 * ppq / s.time_zoom as u32, - (step + 2) as u32 * ppq / s.time_zoom as u32, + (step + 0) as usize * ppq / s.time_zoom as usize, + (step + 1) as usize * ppq / s.time_zoom as usize, + (step + 2) as usize * ppq / s.time_zoom as usize, ); let key = ::midly::num::u7::from(key as u8); is_on = is_on || contains_note_on(&s.phrases[s.sequence.unwrap()], key, a, b); diff --git a/src/device/transport.rs b/src/device/transport.rs index fb9d8d06..8570d11b 100644 --- a/src/device/transport.rs +++ b/src/device/transport.rs @@ -2,10 +2,10 @@ use crate::core::*; use crate::layout::*; pub 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 tick = (frame as f64 / timebase.frames_per_tick()); 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); + let (bars, beats) = (beats / 4.0, beats % 4.0); + let timer = format!("{}.{}.{ticks:02}", bars as usize + 1, beats as usize + 1); timer.blit(buf, x - timer.len() as u16, y, Some(Style::default().not_dim())); } pub fn draw_play_stop (buf: &mut Buffer, x: u16, y: u16, state: &TransportState) { @@ -66,17 +66,11 @@ pub struct Transport { } impl Transport { - pub fn new (name: &str) -> Result, Box> { + pub fn new (name: &str, timebase: &Arc) -> Result, Box> { let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?; let transport = client.transport(); DynamicDevice::new(render, handle, process, Self { - name: name.into(), - timebase: Arc::new(Timebase { - rate: AtomicUsize::new(client.sample_rate()), - bpm: AtomicUsize::new(113000), - ppq: AtomicUsize::new(96), - }), - transport + name: name.into(), timebase: timebase.clone(), transport }).activate(client) } @@ -128,8 +122,8 @@ pub fn render (state: &Transport, buf: &mut Buffer, mut area: Rect) "REC", "DUB", &format!("BPM {:03}.{:03}", - state.timebase.bpm() / 1000, - state.timebase.bpm() % 1000, + state.timebase.bpm() / 1000.0, + state.timebase.bpm() % 1000.0, ), "0.0+00", "0:00.000", diff --git a/src/main.rs b/src/main.rs index 797f19a5..5e6d18d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,12 +43,8 @@ fn main () -> Result<(), Box> { let input = ".*nanoKEY.*"; let output = ["Komplete.*:playback_FL", "Komplete.*:playback_FR"]; let (client, _) = Client::new("init", ClientOptions::NO_START_SERVER)?; - let timebase = Arc::new(Timebase { - rate: AtomicUsize::new(client.sample_rate()), - bpm: AtomicUsize::new(125000000), - ppq: AtomicUsize::new(96), - }); - let ppq = timebase.ppq() as u32; + let timebase = Arc::new(Timebase::new(client.sample_rate() as f64, 125000.0, 96.0)); + let ppq = timebase.ppq() as usize; macro_rules! play { ($t1:expr => [ $($msg:expr),* $(,)? ]) => { ( $t1 * ppq / 4, vec![ $($msg),* ] )