mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
wip: f64 timebase (sloooo)
This commit is contained in:
parent
c4d8692b71
commit
4055662bbd
12 changed files with 206 additions and 142 deletions
82
Cargo.lock
generated
82
Cargo.lock
generated
|
|
@ -84,6 +84,12 @@ dependencies = [
|
||||||
"windows-sys 0.48.0",
|
"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]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
|
|
@ -299,6 +305,16 @@ version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
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]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.29.0"
|
version = "0.29.0"
|
||||||
|
|
@ -534,6 +550,70 @@ dependencies = [
|
||||||
"num-traits",
|
"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]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.19"
|
version = "0.2.19"
|
||||||
|
|
@ -893,10 +973,12 @@ dependencies = [
|
||||||
name = "tek"
|
name = "tek"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"atomic_float",
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"better-panic",
|
"better-panic",
|
||||||
"clap",
|
"clap",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
|
"fraction",
|
||||||
"jack",
|
"jack",
|
||||||
"livi",
|
"livi",
|
||||||
"microxdg",
|
"microxdg",
|
||||||
|
|
|
||||||
|
|
@ -19,3 +19,5 @@ livi = "0.7.4"
|
||||||
#atomic_enum = "0.3.0"
|
#atomic_enum = "0.3.0"
|
||||||
wavers = "1.4.3"
|
wavers = "1.4.3"
|
||||||
music-math = "0.1.1"
|
music-math = "0.1.1"
|
||||||
|
atomic_float = "1.0.0"
|
||||||
|
fraction = "0.15.3"
|
||||||
|
|
|
||||||
119
src/core/time.rs
119
src/core/time.rs
|
|
@ -2,101 +2,96 @@ use crate::core::*;
|
||||||
|
|
||||||
pub struct Timebase {
|
pub struct Timebase {
|
||||||
/// Frames per second
|
/// Frames per second
|
||||||
pub rate: AtomicUsize,
|
pub rate: ::atomic_float::AtomicF64,
|
||||||
/// Beats per minute
|
/// Beats per minute
|
||||||
pub bpm: AtomicUsize,
|
pub bpm: ::atomic_float::AtomicF64,
|
||||||
/// Ticks per beat
|
/// Ticks per beat
|
||||||
pub ppq: AtomicUsize,
|
pub ppq: ::atomic_float::AtomicF64,
|
||||||
}
|
|
||||||
|
|
||||||
/// NoteDuration in musical terms. Has definite usec value
|
|
||||||
/// for given bpm and sample rate.
|
|
||||||
pub enum NoteDuration {
|
|
||||||
Nth(usize, usize),
|
|
||||||
Dotted(Box<Self>),
|
|
||||||
Tuplet(usize, Box<Self>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Timebase {
|
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)
|
self.rate.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
#[inline] fn frame_usec (&self) -> usize {
|
#[inline] fn frame_usec (&self) -> f64 {
|
||||||
1_000_000 / self.rate()
|
1_000_000 as f64 / self.rate() as f64
|
||||||
}
|
}
|
||||||
#[inline] pub fn frame_to_usec (&self, frame: usize) -> usize {
|
#[inline] pub fn frames_usecs (&self, frame: f64) -> f64 {
|
||||||
frame * 1000000 / self.rate()
|
frame * self.frame_usec()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline] pub fn bpm (&self) -> usize {
|
#[inline] pub fn bpm (&self) -> f64 {
|
||||||
self.bpm.load(Ordering::Relaxed)
|
self.bpm.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
#[inline] fn beat_usec (&self) -> usize {
|
#[inline] fn beat_usec (&self) -> f64 {
|
||||||
60_000_000_000_000 / self.bpm()
|
60_000_000_000_000f64 / self.bpm() as f64
|
||||||
}
|
|
||||||
#[inline] fn beats_per_second (&self) -> f64 {
|
|
||||||
self.bpm() as f64 / 60000000.0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline] pub fn ppq (&self) -> usize {
|
#[inline] pub fn ppq (&self) -> f64 {
|
||||||
self.ppq.load(Ordering::Relaxed)
|
self.ppq.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
#[inline] fn pulse_usec (&self) -> usize {
|
#[inline] fn pulse_usec (&self) -> f64 {
|
||||||
self.beat_usec() / self.ppq()
|
self.beat_usec() / self.ppq() as f64
|
||||||
}
|
}
|
||||||
#[inline] fn pulse_frame (&self) -> usize {
|
#[inline] fn pulse_frame (&self) -> f64 {
|
||||||
self.pulse_usec() / self.frame_usec()
|
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
|
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()
|
frames / self.pulse_frame()
|
||||||
}
|
}
|
||||||
#[inline] pub fn frames_per_tick (&self) -> f64 {
|
#[inline] pub fn usec_per_step (&self, divisor: f64) -> 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 {
|
|
||||||
self.beat_usec() / divisor
|
self.beat_usec() / divisor
|
||||||
}
|
}
|
||||||
|
#[inline] pub fn note_to_usec (&self, (num, den): (f64, f64)) -> f64 {
|
||||||
#[inline] pub fn note_to_usec (&self, note: &NoteDuration) -> usize {
|
4.0 * self.beat_usec() * num / den
|
||||||
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_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))
|
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 step = self.note_to_usec(step);
|
||||||
let time = time / step;
|
(time / step, time % step)
|
||||||
let offset = time % step;
|
|
||||||
(time, offset)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline] pub fn quantize_into <T, U> (&self, step: &NoteDuration, events: U) -> Vec<(usize, T)>
|
#[inline] pub fn quantize_into <E, T> (
|
||||||
where U: std::iter::Iterator<Item=(usize, T)> + Sized
|
&self, step: (f64, f64), events: E
|
||||||
|
) -> Vec<(f64, T)>
|
||||||
|
where E: std::iter::Iterator<Item=(f64, T)> + Sized
|
||||||
{
|
{
|
||||||
|
let step = (step.0.into(), step.1.into());
|
||||||
events
|
events
|
||||||
.map(|(time, event)|(self.quantize(step, time).0, event))
|
.map(|(time, event)|(self.quantize(step, time).0, event))
|
||||||
.collect()
|
.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 start_frame = start % quant;
|
||||||
let end_frame = end % quant;
|
let end_frame = end % quant;
|
||||||
let fpt = self.frames_per_tick();
|
let fpt = self.frames_per_tick();
|
||||||
|
|
@ -106,24 +101,24 @@ impl Timebase {
|
||||||
let last_jitter = (frame - 1.0).max(0.0) % fpt;
|
let last_jitter = (frame - 1.0).max(0.0) % fpt;
|
||||||
let next_jitter = frame + 1.0 % fpt;
|
let next_jitter = frame + 1.0 % fpt;
|
||||||
if jitter <= last_jitter && jitter <= next_jitter {
|
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 {
|
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);
|
add_frame(frame as f64);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut frame = start_frame;
|
let mut frame = start_frame as usize;
|
||||||
loop {
|
loop {
|
||||||
add_frame(frame as f64);
|
add_frame(frame as f64);
|
||||||
frame = frame + 1;
|
frame = frame + 1;
|
||||||
if frame >= quant {
|
if frame >= quant as usize {
|
||||||
frame = 0;
|
frame = 0;
|
||||||
loop {
|
loop {
|
||||||
add_frame(frame as f64);
|
add_frame(frame as f64);
|
||||||
frame = frame + 1;
|
frame = frame + 1;
|
||||||
if frame >= end_frame.saturating_sub(1) {
|
if frame >= (end_frame as usize).saturating_sub(1) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ impl Launcher {
|
||||||
) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
|
) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
|
||||||
let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
||||||
let transport = client.transport();
|
let transport = client.transport();
|
||||||
let ppq = timebase.ppq() as u32;
|
let ppq = timebase.ppq() as usize;
|
||||||
DynamicDevice::new(render, handle, process, Self {
|
DynamicDevice::new(render, handle, process, Self {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
view: LauncherView::Chains,
|
view: LauncherView::Chains,
|
||||||
|
|
@ -187,7 +187,7 @@ pub fn render (state: &Launcher, buf: &mut Buffer, mut area: Rect) -> Usually<Re
|
||||||
draw_rec(buf, x + 12, y, state.recording);
|
draw_rec(buf, x + 12, y, state.recording);
|
||||||
draw_mon(buf, x + 19, y, state.monitoring);
|
draw_mon(buf, x + 19, y, state.monitoring);
|
||||||
draw_dub(buf, x + 26, y, state.overdub);
|
draw_dub(buf, x + 26, y, state.overdub);
|
||||||
draw_bpm(buf, x + 33, y, state.timebase.bpm());
|
draw_bpm(buf, x + 33, y, state.timebase.bpm() as usize);
|
||||||
draw_timer(buf, x + width - 1, y, &state.timebase, state.position);
|
draw_timer(buf, x + width - 1, y, &state.timebase, state.position);
|
||||||
}
|
}
|
||||||
let mut y = y + 1;
|
let mut y = y + 1;
|
||||||
|
|
@ -226,12 +226,12 @@ fn draw_section_sequencer (state: &Launcher, buf: &mut Buffer, area: Rect) -> Us
|
||||||
timer(buf, x+5, y, sequencer.time_start, sequencer.time_start + area.width as usize, 0);
|
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)?;
|
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)) {
|
if let Some(Some(phrase)) = state.phrase_id().map(|id|sequencer.phrases.get(id)) {
|
||||||
let ppq = sequencer.timebase.ppq() as u32;
|
let ppq = sequencer.timebase.ppq() as usize;
|
||||||
let zoom = sequencer.time_zoom as u32;
|
let zoom = sequencer.time_zoom;
|
||||||
let t0 = sequencer.time_start as u32;
|
let t0 = sequencer.time_start;
|
||||||
let t1 = t0 + area.width as u32;
|
let t1 = t0 + area.width as usize;
|
||||||
let n0 = sequencer.note_start as u32;
|
let n0 = sequencer.note_start;
|
||||||
let n1 = n0 + area.height as u32;
|
let n1 = n0 + area.height as usize;
|
||||||
lanes(buf, x, y + 1, &phrase, ppq, zoom, t0, t1, n0, n1);
|
lanes(buf, x, y + 1, &phrase, ppq, zoom, t0, t1, n0, n1);
|
||||||
}
|
}
|
||||||
let cursor_style = match view {
|
let cursor_style = match view {
|
||||||
|
|
|
||||||
|
|
@ -50,9 +50,10 @@ fn note_add (s: &mut Sequencer) -> Usually<bool> {
|
||||||
if s.sequence.is_none() {
|
if s.sequence.is_none() {
|
||||||
return Ok(false)
|
return Ok(false)
|
||||||
}
|
}
|
||||||
let step = (s.time_start + s.time_cursor) as u32;
|
let ppq = s.timebase.ppq() as usize;
|
||||||
let start = (step as usize * s.timebase.ppq() / s.time_zoom) as u32;
|
let step = s.time_start + s.time_cursor;
|
||||||
let end = ((step + 1) as usize * s.timebase.ppq() / s.time_zoom) as u32;
|
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 key = u7::from_int_lossy((s.note_cursor + s.note_start) as u8);
|
||||||
let note_on = MidiMessage::NoteOn { key, vel: 100.into() };
|
let note_on = MidiMessage::NoteOn { key, vel: 100.into() };
|
||||||
let note_off = MidiMessage::NoteOff { key, vel: 100.into() };
|
let note_off = MidiMessage::NoteOff { key, vel: 100.into() };
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,12 @@ pub fn draw (
|
||||||
if let Some(phrase) = s.phrase() {
|
if let Some(phrase) = s.phrase() {
|
||||||
lanes(buf, x, y,
|
lanes(buf, x, y,
|
||||||
phrase,
|
phrase,
|
||||||
s.timebase.ppq() as u32,
|
s.timebase.ppq() as usize,
|
||||||
s.time_zoom as u32,
|
s.time_zoom,
|
||||||
s.time_start as u32,
|
s.time_start,
|
||||||
s.time_start as u32 + area.width as u32,
|
s.time_start + area.width as usize,
|
||||||
s.note_start as u32,
|
s.note_start,
|
||||||
s.note_start as u32 + area.height as u32,
|
s.note_start + area.height as usize,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
cursor(
|
cursor(
|
||||||
|
|
@ -76,17 +76,17 @@ pub fn lanes (
|
||||||
x: u16,
|
x: u16,
|
||||||
y: u16,
|
y: u16,
|
||||||
phrase: &Phrase,
|
phrase: &Phrase,
|
||||||
ppq: u32,
|
ppq: usize,
|
||||||
time_zoom: u32,
|
time_zoom: usize,
|
||||||
time0: u32,
|
time0: usize,
|
||||||
time1: u32,
|
time1: usize,
|
||||||
note0: u32,
|
note0: usize,
|
||||||
note1: u32,
|
note1: usize,
|
||||||
) {
|
) {
|
||||||
let bg = Style::default();
|
let bg = Style::default();
|
||||||
let (bw, wh) = (bg.dim(), bg.white());
|
let (bw, wh) = (bg.dim(), bg.white());
|
||||||
for step in time0..time1 {
|
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,);
|
let (a, b) = ((step + 0) * ppq / time_zoom, (step + 1) * ppq / time_zoom,);
|
||||||
if step % 4 == 0 {
|
if step % 4 == 0 {
|
||||||
"|".blit(buf, x as u16, y - 1, Some(Style::default().dim()));
|
"|".blit(buf, x as u16, y - 1, Some(Style::default().dim()));
|
||||||
|
|
@ -95,7 +95,7 @@ pub fn lanes (
|
||||||
format!("{}", step / time_zoom / 4 + 1)
|
format!("{}", step / time_zoom / 4 + 1)
|
||||||
.blit(buf, x as u16, y - 1, Some(Style::default().bold().not_dim()));
|
.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 {
|
for k in 0..h {
|
||||||
let (character, style) = match (
|
let (character, style) = match (
|
||||||
contains_note_on(phrase, u7::from_int_lossy((note0 + k * 2 + 0) as u8), a, b),
|
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, true) => ("▄", wh),
|
||||||
(false, false) => ("·", bw),
|
(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));
|
character.blit(buf, x as u16, y as u16, Some(style));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ impl Sequencer {
|
||||||
timebase: timebase.clone(),
|
timebase: timebase.clone(),
|
||||||
sequence: Some(0),
|
sequence: Some(0),
|
||||||
phrases: phrases.unwrap_or_else(||vec![
|
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,
|
transport,
|
||||||
|
|
@ -95,18 +95,13 @@ impl PortList for Sequencer {
|
||||||
}
|
}
|
||||||
fn render (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
fn render (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||||
let Rect { x, y, width, height } = area;
|
let Rect { x, y, width, height } = area;
|
||||||
let pos = s.transport.query().unwrap().pos;
|
let header = draw_header(s, buf, area)?;
|
||||||
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 piano = match s.view {
|
let piano = match s.view {
|
||||||
SequencerView::Tiny => Rect { x, y, width, height: 0 },
|
SequencerView::Tiny => Rect { x, y, width, height: 0 },
|
||||||
SequencerView::Compact => Rect { x, y, width, height: 0 },
|
SequencerView::Compact => Rect { x, y, width, height: 0 },
|
||||||
SequencerView::Vertical => self::vertical::draw(s, buf, Rect {
|
SequencerView::Vertical => self::vertical::draw(s, buf, Rect {
|
||||||
x, y: y + header.height, width, height,
|
x, y: y + header.height, width, height,
|
||||||
}, steps)?,
|
})?,
|
||||||
SequencerView::Horizontal => self::horizontal::draw(s, buf, Rect {
|
SequencerView::Horizontal => self::horizontal::draw(s, buf, Rect {
|
||||||
x, y: y + header.height, width, height,
|
x, y: y + header.height, width, height,
|
||||||
})?,
|
})?,
|
||||||
|
|
@ -117,7 +112,7 @@ fn render (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||||
height: header.height + piano.height
|
height: header.height + piano.height
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
pub fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect, _: usize) -> Usually<Rect> {
|
pub fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||||
let Rect { x, y, width, .. } = area;
|
let Rect { x, y, width, .. } = area;
|
||||||
let style = Style::default().gray();
|
let style = Style::default().gray();
|
||||||
crate::device::transport::draw_play_stop(buf, x + 2, y + 1, &s.playing);
|
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<Rect>
|
||||||
}
|
}
|
||||||
Ok(Rect { x, y, width: 14, height: 14 })
|
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 (_, (_, events)) in sequence.notes.range(start..end).enumerate() {
|
||||||
for event in events.iter() {
|
for event in events.iter() {
|
||||||
match event {
|
match event {
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,21 @@
|
||||||
use crate::core::*;
|
use crate::core::*;
|
||||||
|
|
||||||
pub type PhraseData = BTreeMap<u32, Vec<MidiMessage>>;
|
pub type PhraseData = BTreeMap<usize, Vec<MidiMessage>>;
|
||||||
|
|
||||||
pub type MIDIChunk = [Option<Vec<Vec<u8>>>];
|
pub type MIDIMessage = Vec<u8>;
|
||||||
|
|
||||||
|
pub type MIDIChunk = [Option<Vec<MIDIMessage>>];
|
||||||
|
|
||||||
pub struct Phrase {
|
pub struct Phrase {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub length: u32,
|
pub length: usize,
|
||||||
pub notes: PhraseData,
|
pub notes: PhraseData,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Phrase {
|
impl Phrase {
|
||||||
pub fn new (name: &str, length: u32, notes: Option<PhraseData>) -> Self {
|
pub fn new (name: &str, length: usize, notes: Option<PhraseData>) -> Self {
|
||||||
Self { name: name.to_string(), length, notes: notes.unwrap_or(BTreeMap::new()) }
|
Self { name: name.to_string(), length, notes: notes.unwrap_or(BTreeMap::new()) }
|
||||||
}
|
}
|
||||||
pub fn frames (&self, timebase: &Arc<Timebase>) -> usize {
|
|
||||||
timebase.pulses_frames(self.length as usize)
|
|
||||||
}
|
|
||||||
pub fn frame_to_pulse (&self, timebase: &Arc<Timebase>, frame: usize) -> usize {
|
|
||||||
timebase.frames_pulses(frame) % self.length as usize
|
|
||||||
}
|
|
||||||
/** Write a chunk of MIDI events to an output port. */
|
/** Write a chunk of MIDI events to an output port. */
|
||||||
pub fn chunk_out (
|
pub fn chunk_out (
|
||||||
&self,
|
&self,
|
||||||
|
|
@ -29,9 +25,13 @@ impl Phrase {
|
||||||
frame0: usize,
|
frame0: usize,
|
||||||
frames: 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() {
|
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() {
|
if events.is_none() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -67,7 +67,7 @@ impl Phrase {
|
||||||
) {
|
) {
|
||||||
for RawMidi { time, bytes } in input {
|
for RawMidi { time, bytes } in input {
|
||||||
let time = time as usize;
|
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 LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() {
|
||||||
if let MidiMessage::NoteOn { key, vel: _ } = message {
|
if let MidiMessage::NoteOn { key, vel: _ } = message {
|
||||||
notes_on[key.as_int() as usize] = true;
|
notes_on[key.as_int() as usize] = true;
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ pub fn process (state: &mut Sequencer, _: &Client, scope: &ProcessScope) -> Cont
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// Play from input to monitor, and record into phrase.
|
// 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 steps = usecs / state.timebase.usec_per_step(state.time_zoom as usize);
|
||||||
//let step = steps % state.steps;
|
//let step = steps % state.steps;
|
||||||
//let tick = (step * state.timebase.ppq() / state.time_zoom) as u32;
|
//let tick = (step * state.timebase.ppq() / state.time_zoom) as u32;
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,10 @@ pub fn draw (
|
||||||
s: &Sequencer,
|
s: &Sequencer,
|
||||||
buf: &mut Buffer,
|
buf: &mut Buffer,
|
||||||
mut area: Rect,
|
mut area: Rect,
|
||||||
beat: usize
|
|
||||||
) -> Usually<Rect> {
|
) -> Usually<Rect> {
|
||||||
area.x = area.x + 13;
|
area.x = area.x + 13;
|
||||||
keys(s, buf, area, beat);
|
keys(s, buf, area, 0);
|
||||||
steps(s, buf, area, beat);
|
steps(s, buf, area, 0);
|
||||||
playhead(s, buf, area.x, area.y);
|
playhead(s, buf, area.x, area.y);
|
||||||
Ok(area)
|
Ok(area)
|
||||||
}
|
}
|
||||||
|
|
@ -18,7 +17,7 @@ pub fn steps (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) {
|
||||||
if s.sequence.is_none() {
|
if s.sequence.is_none() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let ppq = s.timebase.ppq() as u32;
|
let ppq = s.timebase.ppq() as usize;
|
||||||
let bg = Style::default();
|
let bg = Style::default();
|
||||||
let bw = bg.dim();
|
let bw = bg.dim();
|
||||||
let wh = bg.white();
|
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);
|
let key = ::midly::num::u7::from_int_lossy(k as u8);
|
||||||
if step % 2 == 0 {
|
if step % 2 == 0 {
|
||||||
let (a, b, c) = (
|
let (a, b, c) = (
|
||||||
(step + 0) as u32 * ppq / s.time_zoom as u32,
|
(step + 0) as usize * ppq / s.time_zoom as usize,
|
||||||
(step + 1) as u32 * ppq / s.time_zoom as u32,
|
(step + 1) as usize * ppq / s.time_zoom as usize,
|
||||||
(step + 2) as u32 * ppq / s.time_zoom as u32,
|
(step + 2) as usize * ppq / s.time_zoom as usize,
|
||||||
);
|
);
|
||||||
let (character, style) = match (
|
let (character, style) = match (
|
||||||
contains_note_on(&s.phrases[s.sequence.unwrap()], key, a, b),
|
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) {
|
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;
|
let Rect { x, y, .. } = area;
|
||||||
for key in s.note_start..s.note_start+area.width as usize {
|
for key in s.note_start..s.note_start+area.width as usize {
|
||||||
let x = x + (5 + key - s.note_start) as u16;
|
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 mut is_on = s.notes_on[key as usize];
|
||||||
let step = beat;
|
let step = beat;
|
||||||
let (a, b, c) = (
|
let (a, b, c) = (
|
||||||
(step + 0) as u32 * ppq / s.time_zoom as u32,
|
(step + 0) as usize * ppq / s.time_zoom as usize,
|
||||||
(step + 1) as u32 * ppq / s.time_zoom as u32,
|
(step + 1) as usize * ppq / s.time_zoom as usize,
|
||||||
(step + 2) as u32 * ppq / s.time_zoom as u32,
|
(step + 2) as usize * ppq / s.time_zoom as usize,
|
||||||
);
|
);
|
||||||
let key = ::midly::num::u7::from(key as u8);
|
let key = ::midly::num::u7::from(key as u8);
|
||||||
is_on = is_on || contains_note_on(&s.phrases[s.sequence.unwrap()], key, a, b);
|
is_on = is_on || contains_note_on(&s.phrases[s.sequence.unwrap()], key, a, b);
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,10 @@ use crate::core::*;
|
||||||
use crate::layout::*;
|
use crate::layout::*;
|
||||||
|
|
||||||
pub fn draw_timer (buf: &mut Buffer, x: u16, y: u16, timebase: &Arc<Timebase>, frame: usize) {
|
pub fn draw_timer (buf: &mut Buffer, x: u16, y: u16, timebase: &Arc<Timebase>, 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 (beats, ticks) = (tick / timebase.ppq(), tick % timebase.ppq());
|
||||||
let (bars, beats) = (beats / 4, beats % 4);
|
let (bars, beats) = (beats / 4.0, beats % 4.0);
|
||||||
let timer = format!("{}.{}.{ticks:02}", bars + 1, beats + 1);
|
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()));
|
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) {
|
pub fn draw_play_stop (buf: &mut Buffer, x: u16, y: u16, state: &TransportState) {
|
||||||
|
|
@ -66,17 +66,11 @@ pub struct Transport {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transport {
|
impl Transport {
|
||||||
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
|
pub fn new (name: &str, timebase: &Arc<Timebase>) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
|
||||||
let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
||||||
let transport = client.transport();
|
let transport = client.transport();
|
||||||
DynamicDevice::new(render, handle, process, Self {
|
DynamicDevice::new(render, handle, process, Self {
|
||||||
name: name.into(),
|
name: name.into(), timebase: timebase.clone(), transport
|
||||||
timebase: Arc::new(Timebase {
|
|
||||||
rate: AtomicUsize::new(client.sample_rate()),
|
|
||||||
bpm: AtomicUsize::new(113000),
|
|
||||||
ppq: AtomicUsize::new(96),
|
|
||||||
}),
|
|
||||||
transport
|
|
||||||
}).activate(client)
|
}).activate(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -128,8 +122,8 @@ pub fn render (state: &Transport, buf: &mut Buffer, mut area: Rect)
|
||||||
"REC",
|
"REC",
|
||||||
"DUB",
|
"DUB",
|
||||||
&format!("BPM {:03}.{:03}",
|
&format!("BPM {:03}.{:03}",
|
||||||
state.timebase.bpm() / 1000,
|
state.timebase.bpm() / 1000.0,
|
||||||
state.timebase.bpm() % 1000,
|
state.timebase.bpm() % 1000.0,
|
||||||
),
|
),
|
||||||
"0.0+00",
|
"0.0+00",
|
||||||
"0:00.000",
|
"0:00.000",
|
||||||
|
|
|
||||||
|
|
@ -43,12 +43,8 @@ fn main () -> Result<(), Box<dyn Error>> {
|
||||||
let input = ".*nanoKEY.*";
|
let input = ".*nanoKEY.*";
|
||||||
let output = ["Komplete.*:playback_FL", "Komplete.*:playback_FR"];
|
let output = ["Komplete.*:playback_FL", "Komplete.*:playback_FR"];
|
||||||
let (client, _) = Client::new("init", ClientOptions::NO_START_SERVER)?;
|
let (client, _) = Client::new("init", ClientOptions::NO_START_SERVER)?;
|
||||||
let timebase = Arc::new(Timebase {
|
let timebase = Arc::new(Timebase::new(client.sample_rate() as f64, 125000.0, 96.0));
|
||||||
rate: AtomicUsize::new(client.sample_rate()),
|
let ppq = timebase.ppq() as usize;
|
||||||
bpm: AtomicUsize::new(125000000),
|
|
||||||
ppq: AtomicUsize::new(96),
|
|
||||||
});
|
|
||||||
let ppq = timebase.ppq() as u32;
|
|
||||||
macro_rules! play {
|
macro_rules! play {
|
||||||
($t1:expr => [ $($msg:expr),* $(,)? ]) => {
|
($t1:expr => [ $($msg:expr),* $(,)? ]) => {
|
||||||
( $t1 * ppq / 4, vec![ $($msg),* ] )
|
( $t1 * ppq / 4, vec![ $($msg),* ] )
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue