fix warnings (not 55 errors)

This commit is contained in:
🪞👃🪞 2024-06-20 18:42:08 +03:00
parent a50e022ab6
commit 87c5e47b43
8 changed files with 290 additions and 269 deletions

View file

@ -21,7 +21,7 @@ use crossterm::event;
pub fn run (device: impl Device + Send + Sync + 'static) -> Result<(), Box<dyn Error>> {
let device = Arc::new(Mutex::new(device));
let exited = Arc::new(AtomicBool::new(false));
let input_thread = {
let _input_thread = {
let poll = std::time::Duration::from_millis(100);
let exited = exited.clone();
let device = device.clone();

View file

@ -48,7 +48,7 @@ pub fn render (state: &Mixer, buf: &mut Buffer, mut area: Rect)
draw_box(buf, area);
let x = area.x + 1;
let y = area.y + 1;
let h = area.height - 2;
let _h = area.height - 2;
for (i, track) in state.tracks.iter().enumerate() {
//buf.set_string(
//x, y + index as u16,

View file

@ -16,7 +16,7 @@ pub fn process (_: &mut Plugin, _: &Client, _: &ProcessScope) -> Control {
Control::Continue
}
pub fn render (state: &Plugin, buf: &mut Buffer, Rect { x, y, width, height }: Rect)
pub fn render (state: &Plugin, buf: &mut Buffer, Rect { x, y, .. }: Rect)
-> Usually<Rect>
{
let style = Style::default().gray();

View file

@ -15,8 +15,7 @@ pub struct Sampler {
impl Sampler {
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
let exited = Arc::new(AtomicBool::new(false));
let (client, status) = Client::new(name, ClientOptions::NO_START_SERVER)?;
let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?;
let samples = vec![
Sample::new("Kick", &client, 1, 35)?,
Sample::new("Snare", &client, 1, 38)?,
@ -35,7 +34,7 @@ impl Sampler {
pub fn process (
state: &mut Sampler,
client: &Client,
_: &Client,
scope: &ProcessScope,
) -> Control {
let mut samples = state.samples.lock().unwrap();
@ -48,7 +47,7 @@ pub fn process (
let note = data[1];
let velocity = data[2];
for sample in samples.iter_mut() {
if /*sample.trigger.0 == channel &&*/ sample.trigger.1 == note {
if sample.trigger.0 == channel && sample.trigger.1 == note {
sample.play(velocity);
}
}
@ -95,13 +94,13 @@ impl Sample {
})
}
fn play (&mut self, velocity: u8) {
fn play (&mut self, _velocity: u8) {
self.playing = Some(0)
}
}
pub fn render (state: &Sampler, buf: &mut Buffer, Rect { x, y, width, height }: Rect)
pub fn render (state: &Sampler, buf: &mut Buffer, Rect { x, y, .. }: Rect)
-> Usually<Rect>
{
let style = Style::default().gray();
@ -170,7 +169,7 @@ pub fn render (state: &Sampler, buf: &mut Buffer, Rect { x, y, width, height }:
//Ok(())
//}
pub fn handle (state: &mut Sampler, event: &AppEvent) -> Result<(), Box<dyn Error>> {
pub fn handle (_: &mut Sampler, _: &AppEvent) -> Result<(), Box<dyn Error>> {
//if let Event::Input(crossterm::event::Event::Key(event)) = event {
//match event.code {
//KeyCode::Char('c') => {

View file

@ -5,18 +5,18 @@ type Sequence = std::collections::BTreeMap<u32, Vec<::midly::MidiMessage>>;
pub struct Sequencer {
name: String,
/// JACK transport handle.
transport: ::jack::Transport,
/// Samples per second
rate: Hz,
/// Beats per minute
tempo: Tempo,
/// MIDI resolution (a.k.a. PPQ)
ticks_per_beat: u64,
/// Holds info about tempo
timebase: Arc<Mutex<Timebase>>,
/// Sequencer resolution, e.g. 16 steps per beat.
steps_per_beat: u64,
resolution: u64,
/// Steps in sequence, e.g. 64 16ths = 4 beat loop.
steps: u64,
/// JACK MIDI input port that will be created.
input_port: Port<MidiIn>,
/// Port name patterns to connect to.
input_connect: Vec<String>,
/// Play input through output.
monitoring: bool,
@ -30,7 +30,9 @@ pub struct Sequencer {
overdub: bool,
/// Play sequence to output.
playing: bool,
/// JACK MIDI output port that will be created.
output_port: Port<MidiOut>,
/// Port name patterns to connect to.
output_connect: Vec<String>,
/// Display mode
@ -54,103 +56,33 @@ enum SequencerView {
}
impl Sequencer {
pub fn new (name: &str) -> Usually<DynamicDevice<Self>> {
pub fn new (name: &str, timebase: &Arc<Mutex<Timebase>>) -> Usually<DynamicDevice<Self>> {
let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?;
DynamicDevice::new(render, handle, process, Self {
name: name.into(),
transport: client.transport(),
timebase: timebase.clone(),
steps: 64,
resolution: 8,
input_port: client.register_port("in", MidiIn::default())?,
input_connect: vec!["nanoKEY Studio * (capture): *".into()],
monitoring: true,
notes_on: vec![false;128],
recording: true,
overdub: true,
sequence: std::collections::BTreeMap::new(),
playing: true,
output_port: client.register_port("out", MidiOut::default())?,
output_connect: vec![],
mode: SequencerView::Vertical,
note_axis: (36, 68),
note_cursor: 0,
time_axis: (0, 64),
time_cursor: 0,
rate: Hz(client.sample_rate() as u32),
tempo: Tempo(120000),
ticks_per_beat: 96,
steps_per_beat: 8,
steps: 64,
transport: client.transport(),
input_port: client.register_port("in", MidiIn::default())?,
input_connect: vec!["nanoKEY Studio * (capture): *".into()],
monitoring: true,
notes_on: vec![false;128],
playing: true,
recording: true,
sequence: std::collections::BTreeMap::new(),
overdub: true,
output_port: client.register_port("out", MidiOut::default())?,
output_connect: vec![],
}).activate(client)
}
/// Beats per second
#[inline] fn bps (&self) -> f64 {
self.tempo.0 as f64 / 60000.0
}
/// Frames per second
#[inline] fn fps (&self) -> u64 {
self.rate.0 as u64
}
/// Frames per beat
#[inline] fn fpb (&self) -> f64 {
self.fps() as f64 / self.bps()
}
/// Frames per tick FIXME double times
#[inline] fn fpt (&self) -> f64 {
self.fps() as f64 / self.tps()
}
/// Frames per loop
#[inline] fn fpl (&self) -> f64 {
self.fpb() * self.steps as f64 / self.steps_per_beat as f64
}
/// Ticks per beat
#[inline] fn tpb (&self) -> f64 {
self.ticks_per_beat as f64
}
/// Ticks per second
#[inline] fn tps (&self) -> f64 {
self.bps() * self.tpb()
}
fn frames_to_ticks (&self, start: u64, end: u64) -> Vec<(u64, u64)> {
let fpl = self.fpl() as u64;
let start_frame = start % fpl;
let end_frame = end % fpl;
let fpt = self.fpt();
let mut ticks = vec![];
let mut add_frame = |frame: f64|{
let jitter = frame.rem_euclid(fpt);
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 u64 % (end-start), (frame / fpt) as u64));
};
};
if start_frame < end_frame {
for frame in start_frame..end_frame {
add_frame(frame as f64);
}
} else {
let mut frame = start_frame;
loop {
add_frame(frame as f64);
frame = frame + 1;
if frame >= fpl {
frame = 0;
loop {
add_frame(frame as f64);
frame = frame + 1;
if frame >= end_frame.saturating_sub(1) {
break
}
}
break
}
}
}
ticks
}
}
pub fn process (s: &mut Sequencer, _: &Client, scope: &ProcessScope) -> Control {
@ -165,13 +97,15 @@ fn process_in (s: &mut Sequencer, scope: &ProcessScope) {
}
let pos = s.transport.query().unwrap().pos;
let usecs = Frame(pos.frame()).to_usec(&s.rate).0;
let steps = usecs / s.tempo.usec_per_step(s.steps_per_beat as u64).0;
let steps = usecs / s.tempo.usec_per_step(s.resolution as u64).0;
let step = steps % s.steps;
let tick = step * s.ticks_per_beat / s.steps_per_beat;
let tick = step * s.ticks_per_beat / s.resolution;
for event in s.input_port.iter(scope) {
match midly::live::LiveEvent::parse(event.bytes).unwrap() {
midly::live::LiveEvent::Midi { channel: _, message } => match message {
midly::MidiMessage::NoteOn { key, vel: _ } => {
s.notes_on[key.as_int() as usize] = true;
if s.sequence.contains_key(&(tick as u32)) {
@ -180,6 +114,7 @@ fn process_in (s: &mut Sequencer, scope: &ProcessScope) {
s.sequence.insert(tick as u32, vec![message.clone()]);
}
},
midly::MidiMessage::NoteOff { key, vel: _ } => {
s.notes_on[key.as_int() as usize] = false;
if s.sequence.contains_key(&(tick as u32)) {
@ -188,9 +123,13 @@ fn process_in (s: &mut Sequencer, scope: &ProcessScope) {
s.sequence.insert(tick as u32, vec![message.clone()]);
}
},
_ => {}
},
_ => {}
}
}
}
@ -232,7 +171,7 @@ fn render (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
let frame = pos.frame();
let rate = pos.frame_rate().unwrap();
let usecs = Frame(frame).to_usec(&Hz(rate)).0;
let usec_per_step = s.tempo.usec_per_step(s.steps_per_beat as u64).0;
let usec_per_step = s.tempo.usec_per_step(s.resolution as u64).0;
let steps = usecs / usec_per_step;
let header = draw_header(s, buf, area, steps)?;
let piano = match s.mode {
@ -264,50 +203,42 @@ fn render (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: u64) -> Usually<Rect> {
let rep = beat / s.steps;
let step = beat % s.steps;
let reps = s.steps / s.steps_per_beat;
let steps = s.steps % s.steps_per_beat;
let reps = s.steps / s.resolution;
let steps = s.steps % s.resolution;
let Rect { x, y, .. } = area;
let style = Style::default().gray();
buf.set_string(x + 1, y + 1, &format!("{rep}.{step:2} / {reps}.{steps}"), style);
buf.set_string(x + 2, y + 1, &format!("{}", &s.name), style.white().bold());
buf.set_string(x + 1, y + 2, &format!(" ▶ PLAY │ ⏹ STOP │ │"), style);
buf.set_string(x + 2, y + 2, &format!("▶ PLAY"), if s.playing {
Style::default().green()
} else {
Style::default().dim()
});
buf.set_string(x + 24, y + 2, &format!("⏺ REC"), if s.recording {
Style::default().red()
} else {
Style::default().dim()
});
buf.set_string(x + 32, y + 2, &format!("⏺ DUB"), if s.overdub {
Style::default().yellow()
} else {
Style::default().dim()
});
buf.set_string(x + 1, y + 1,
&format!("{rep}.{step:2} / {reps}.{steps}"),
style);
buf.set_string(x + 2, y + 1,
&format!("{}", &s.name),
style.white().bold());
buf.set_string(x + 1, y + 2,
&format!(" ▶ PLAY │ ⏹ STOP │ │"),
style);
buf.set_string(x + 2, y + 2,
&format!("▶ PLAY"), if s.playing {
Style::default().green()
} else {
Style::default().dim()
});
buf.set_string(x + 24, y + 2,
&format!("⏺ REC"), if s.recording {
Style::default().red()
} else {
Style::default().dim()
});
buf.set_string(x + 32, y + 2,
&format!("⏺ DUB"), if s.overdub {
Style::default().yellow()
} else {
Style::default().dim()
});
Ok(Rect { x, y, width: 39, height: 4 })
}
const KEY_WHITE: Style = Style {
fg: Some(Color::Gray),
bg: None,
underline_color: None,
add_modifier: ::ratatui::style::Modifier::empty(),
sub_modifier: ::ratatui::style::Modifier::empty(),
};
const KEY_BLACK: Style = Style {
fg: Some(Color::Black),
bg: None,
underline_color: None,
add_modifier: ::ratatui::style::Modifier::empty(),
sub_modifier: ::ratatui::style::Modifier::empty(),
};
const KEY_HORIZONTAL_STYLE: [Style;12] = [
KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE,
KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE,
const KEYS_VERTICAL: [&'static str; 6] = [
"", "", "", "", "", "",
];
fn draw_vertical (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: u64) -> Usually<Rect> {
@ -326,9 +257,9 @@ fn draw_vertical (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: u64) -> Usu
let mut is_on = s.notes_on[key as usize];
let step = beat % s.steps;
let (a, b, c) = (
(step + 0) as u32 * s.ticks_per_beat as u32 / s.steps_per_beat as u32,
(step + 1) as u32 * s.ticks_per_beat as u32 / s.steps_per_beat as u32,
(step + 2) as u32 * s.ticks_per_beat as u32 / s.steps_per_beat as u32,
(step + 0) as u32 * s.ticks_per_beat as u32 / s.resolution as u32,
(step + 1) as u32 * s.ticks_per_beat as u32 / s.resolution as u32,
(step + 2) as u32 * s.ticks_per_beat as u32 / s.resolution as u32,
);
let key = ::midly::num::u7::from(key as u8);
is_on = is_on || contains_note_on(&s.sequence, key, a, b);
@ -342,16 +273,16 @@ fn draw_vertical (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: u64) -> Usu
let y = y - time0 + step / 2;
let step = step as u64;
//buf.set_string(x + 5, y, &" ".repeat(32.max(note1-note0)as usize), bg);
if step % s.steps_per_beat == 0 {
if step % s.resolution == 0 {
buf.set_string(x + 2, y, &format!("{:2} ", step + 1), Style::default());
}
for k in note0..note1 {
let key = ::midly::num::u7::from_int_lossy(k as u8);
if step % 2 == 0 {
let (a, b, c) = (
(step + 0) as u32 * s.ticks_per_beat as u32 / s.steps_per_beat as u32,
(step + 1) as u32 * s.ticks_per_beat as u32 / s.steps_per_beat as u32,
(step + 2) as u32 * s.ticks_per_beat as u32 / s.steps_per_beat as u32,
(step + 0) as u32 * s.ticks_per_beat as u32 / s.resolution as u32,
(step + 1) as u32 * s.ticks_per_beat as u32 / s.resolution as u32,
(step + 2) as u32 * s.ticks_per_beat as u32 / s.resolution as u32,
);
let (character, style) = match (
contains_note_on(&s.sequence, key, a, b),
@ -379,7 +310,7 @@ fn draw_vertical (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: u64) -> Usu
let height = (time1-time0)/2;
buf.set_string(x + 2, y + height + 1, format!(
"Q 1/{} | N {} ({}-{}) | T {} ({}-{})",
4 * s.steps_per_beat,
4 * s.resolution,
s.note_axis.0 + s.note_cursor,
s.note_axis.0,
s.note_axis.1 - 1,
@ -396,8 +327,29 @@ fn draw_vertical (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: u64) -> Usu
Ok(Rect { x, y, width: area.width, height })
}
const KEY_WHITE: Style = Style {
fg: Some(Color::Gray),
bg: None,
underline_color: None,
add_modifier: ::ratatui::style::Modifier::empty(),
sub_modifier: ::ratatui::style::Modifier::empty(),
};
const KEY_BLACK: Style = Style {
fg: Some(Color::Black),
bg: None,
underline_color: None,
add_modifier: ::ratatui::style::Modifier::empty(),
sub_modifier: ::ratatui::style::Modifier::empty(),
};
const KEY_HORIZONTAL_STYLE: [Style;12] = [
KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE,
KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE,
];
fn contains_note_on (sequence: &Sequence, k: ::midly::num::u7, start: u32, end: u32) -> bool {
for (i, (t, events)) in sequence.range(start..end).enumerate() {
for (_, (_, events)) in sequence.range(start..end).enumerate() {
for event in events.iter() {
match event {
::midly::MidiMessage::NoteOn {key,..} => {
@ -444,7 +396,7 @@ fn draw_horizontal (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect
let height = 32.max(note1 - note0) / 2;
buf.set_string(x + 2, y + height + 1, format!(
" Q 1/{} | N {} ({}-{}) | T {} ({}-{})",
4 * s.steps_per_beat,
4 * s.resolution,
s.note_axis.0 + s.note_cursor,
s.note_axis.0,
s.note_axis.1 - 1,
@ -496,10 +448,10 @@ fn nop (_: &mut Sequencer) {
fn note_add (s: &mut Sequencer) {
let pos = s.transport.query().unwrap().pos;
let usecs = Frame(pos.frame()).to_usec(&s.rate).0;
let steps = usecs / s.tempo.usec_per_step(s.steps_per_beat as u64).0;
let steps = usecs / s.tempo.usec_per_step(s.resolution as u64).0;
let step = (s.time_axis.0 + s.time_cursor) as u32;
let start = (step as u64 * s.ticks_per_beat / s.steps_per_beat) as u32;
let end = ((step + 1) as u64 * s.ticks_per_beat / s.steps_per_beat) as u32;
let start = (step as u64 * s.ticks_per_beat / s.resolution) as u32;
let end = ((step + 1) as u64 * s.ticks_per_beat / s.resolution) as u32;
let key = ::midly::num::u7::from_int_lossy((s.note_cursor + s.note_axis.0) as u8);
let note_on = ::midly::MidiMessage::NoteOn { key, vel: 100.into() };
let note_off = ::midly::MidiMessage::NoteOff { key, vel: 100.into() };
@ -547,8 +499,6 @@ fn cursor_down (s: &mut Sequencer) {
}
}
fn cursor_left (s: &mut Sequencer) {
let time = s.time_axis.1 - s.time_axis.0;
let note = s.note_axis.1 - s.note_axis.0;
match s.mode {
SequencerView::Vertical => note_cursor_dec(s),
SequencerView::Horizontal => time_cursor_dec(s),
@ -556,18 +506,16 @@ fn cursor_left (s: &mut Sequencer) {
}
}
fn cursor_right (s: &mut Sequencer) {
let time = s.time_axis.1 - s.time_axis.0;
let note = s.note_axis.1 - s.note_axis.0;
match s.mode {
SequencerView::Vertical => note_cursor_inc(s),
SequencerView::Horizontal => time_cursor_inc(s),
_ => unimplemented!()
}
}
fn cursor_duration_inc (s: &mut Sequencer) {
fn cursor_duration_inc (_: &mut Sequencer) {
//s.cursor.2 = s.cursor.2 + 1
}
fn cursor_duration_dec (s: &mut Sequencer) {
fn cursor_duration_dec (_: &mut Sequencer) {
//if s.cursor.2 > 0 { s.cursor.2 = s.cursor.2 - 1 }
}
fn mode_next (s: &mut Sequencer) {
@ -595,13 +543,13 @@ fn toggle_overdub (s: &mut Sequencer) {
s.overdub = !s.overdub
}
fn quantize_next (s: &mut Sequencer) {
if s.steps_per_beat < 64 {
s.steps_per_beat = s.steps_per_beat * 2
if s.resolution < 64 {
s.resolution = s.resolution * 2
}
}
fn quantize_prev (s: &mut Sequencer) {
if s.steps_per_beat > 1 {
s.steps_per_beat = s.steps_per_beat / 2
if s.resolution > 1 {
s.resolution = s.resolution / 2
}
}

View file

@ -1,20 +1,23 @@
use crate::prelude::*;
pub struct Transport {
name: String,
transport: ::jack::Transport,
timesig: (f32, f32),
bpm: f64,
name: String,
/// Holds info about tempo
timebase: Arc<Mutex<Timebase>>,
}
impl Transport {
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?;
let transport = client.transport();
DynamicDevice::new(render, handle, process, Self {
name: name.into(),
transport: client.transport(),
timesig: (4.0, 4.0),
bpm: 113.0,
name: name.into(),
timebase: Timebase {
rate: transport.query()?.pos.frame_rate(),
tempo: 113000,
ppq: 96,
},
transport
}).activate(client)
}
@ -45,8 +48,8 @@ pub fn process (_: &mut Transport, _: &Client, _: &ProcessScope) -> Control {
pub fn render (state: &Transport, buf: &mut Buffer, mut area: Rect)
-> Usually<Rect>
{
area.x = area.width.saturating_sub(80) / 2;
area.width = area.width.min(80);
//area.x = area.width.saturating_sub(80) / 2;
//area.width = area.width.min(80);
area.height = 5;
//draw_box(buf, area);
let label = Style::default().white().not_dim();

View file

@ -21,7 +21,15 @@ fn main () -> Result<(), Box<dyn Error>> {
let xdg = microxdg::XdgApp::new("dawdle")?;
crate::config::create_dirs(&xdg)?;
//crate::device::run(Sequencer::new("Rhythm#000")?)
const transport = Transport::new("Transport")?;
const timebase = transport.timebase.clone();
crate::device::run(Rows::new(true, vec![
Box::new(transport),
Box::new(Columns::new(true, vec![
Box::new(Sequencer::new("Melody#000", &timebase)?),
Box::new(Sequencer::new("Melody#001", &timebase)?),
Box::new(Sequencer::new("Rhythm#000", &timebase)?),
])),
//Box::new(Columns::new(false, vec![
//Box::new(Chain::new("Chain#00", vec![
//Box::new(Sequencer::new("Rhythm#000")?),
@ -33,12 +41,6 @@ fn main () -> Result<(), Box<dyn Error>> {
//])?),
//])),
//Box::new(Mixer::new("Mixer#000")?),
Box::new(Transport::new("Transport")?),
Box::new(Columns::new(true, vec![
Box::new(Sequencer::new("Melody#000")?),
Box::new(Sequencer::new("Melody#001")?),
Box::new(Sequencer::new("Rhythm#000")?),
])),
//Box::new(Sequencer::new("Rhythm#000")?),
]))
}

View file

@ -1,89 +1,16 @@
/// Number of data frames in a second.
#[derive(Debug)]
pub struct Hz(pub u32);
/// One data frame.
#[derive(Debug)]
pub struct Frame(pub u32);
/// One microsecond.
#[derive(Debug)]
pub struct Usec(pub u64);
/// Beats per minute as `120000` = 120.000BPM
#[derive(Debug)]
pub struct Tempo(pub u64);
/// Time signature: N/Mths per bar.
#[derive(Debug)]
pub struct Signature(pub u32, pub u32);
/// NoteDuration in musical terms. Has definite usec value
/// for given bpm and sample rate.
pub enum NoteDuration {
Nth(u16, u16),
Dotted(Box<Self>),
Tuplet(u16, Box<Self>),
pub struct Timebase {
/// Frames per second
pub rate: Option<usize>,
/// Beats per minute
pub tempo: Option<usize>,
/// Ticks per beat
pub ppq: usize,
}
impl Frame {
#[inline]
pub fn to_usec (&self, rate: &Hz) -> Usec {
Usec((self.0 as u64 * 1000000) / rate.0 as u64)
}
}
impl Usec {
#[inline]
pub fn to_frame (&self, rate: &Hz) -> Frame {
Frame(((self.0 * rate.0 as u64) / 1000) as u32)
}
}
impl Tempo {
#[inline]
pub fn usec_per_bar (&self, beats: u64) -> Usec {
Usec(beats * self.usec_per_beat().0)
}
#[inline]
pub fn usec_per_beat (&self) -> Usec {
Usec(60_000_000_000 / self.0 as u64)
}
#[inline]
pub fn usec_per_step (&self, divisor: u64) -> Usec {
Usec(self.usec_per_beat().0 / divisor as u64)
}
#[inline]
pub fn quantize (&self, step: &NoteDuration, time: Usec) -> Usec {
let step = step.to_usec(self);
let t = time.0 / step.0;
Usec(step.0 * t)
}
#[inline]
pub fn quantize_into <T, U> (&self, step: &NoteDuration, events: U)
-> Vec<(Usec, T)>
where U: std::iter::Iterator<Item=(Usec, T)> + Sized
{
events
.map(|(time, event)|(self.quantize(step, time), event))
.collect()
}
}
impl NoteDuration {
fn to_usec (&self, bpm: &Tempo) -> Usec {
Usec(match self {
Self::Nth(time, flies) =>
bpm.usec_per_beat().0 * *time as u64 / *flies as u64,
Self::Dotted(duration) =>
duration.to_usec(bpm).0 * 3 / 2,
Self::Tuplet(n, duration) =>
duration.to_usec(bpm).0 * 2 / *n as u64,
})
}
fn to_frame (&self, bpm: &Tempo, rate: &Hz) -> Frame {
self.to_usec(bpm).to_frame(rate)
}
enum QuantizeMode {
Forward,
Backward,
Nearest,
}
struct Loop<T> {
@ -93,3 +20,145 @@ struct Loop<T> {
end: T,
post_end: T,
}
/// NoteDuration in musical terms. Has definite usec value
/// for given bpm and sample rate.
pub enum NoteDuration {
Nth(u16, u16),
Dotted(Box<Self>),
Tuplet(u16, Box<Self>),
}
impl Timebase {
/// Beats per second
#[inline] fn bps (&self) -> f64 {
self.tempo.0 as f64 / 60000.0
}
/// Frames per second
#[inline] fn fps (&self) -> u64 {
self.rate.0 as u64
}
/// Frames per beat
#[inline] fn fpb (&self) -> f64 {
self.fps() as f64 / self.bps()
}
/// Frames per tick FIXME double times
#[inline] fn fpt (&self) -> f64 {
self.fps() as f64 / self.tps()
}
/// Frames per loop
#[inline] fn fpl (&self) -> f64 {
self.fpb() * self.steps as f64 / self.steps_per_beat as f64
}
/// Ticks per beat
#[inline] fn tpb (&self) -> f64 {
self.ticks_per_beat as f64
}
/// Ticks per second
#[inline] fn tps (&self) -> f64 {
self.bps() * self.tpb()
}
fn frames_to_ticks (&self, start: u64, end: u64) -> Vec<(u64, u64)> {
let fpl = self.fpl() as u64;
let start_frame = start % fpl;
let end_frame = end % fpl;
let fpt = self.fpt();
let mut ticks = vec![];
let mut add_frame = |frame: f64|{
let jitter = frame.rem_euclid(fpt);
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 u64 % (end-start), (frame / fpt) as u64));
};
};
if start_frame < end_frame {
for frame in start_frame..end_frame {
add_frame(frame as f64);
}
} else {
let mut frame = start_frame;
loop {
add_frame(frame as f64);
frame = frame + 1;
if frame >= fpl {
frame = 0;
loop {
add_frame(frame as f64);
frame = frame + 1;
if frame >= end_frame.saturating_sub(1) {
break
}
}
break
}
}
}
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 {
frame * 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,
}
}
pub fn quantize (&self, step: &NoteDuration, time: usize) -> (usize, usize) {
let step = self.usec_per_note(step);
let time = time / step;
let offset = time % step;
(time, offset)
}
pub fn quantize_into <T, U> (&self, step: &NoteDuration, events: U) -> Vec<(usize, T)>
where U: std::iter::Iterator<Item=(usize, T)> + Sized
{
events
.map(|(time, event)|(self.quantize(step, time).0, event))
.collect()
}
}
impl NoteDuration {
fn to_usec (&self, bpm: &Tempo) -> Usec {
Usec(match self {
Self::Nth(time, flies) =>
bpm.usec_per_beat().0 * *time as usize / *flies as usize,
Self::Dotted(duration) =>
duration.to_usec(bpm).0 * 3 / 2,
Self::Tuplet(n, duration) =>
duration.to_usec(bpm).0 * 2 / *n as usize,
})
}
fn to_frame (&self, bpm: &Tempo, rate: &Hz) -> Frame {
self.to_usec(bpm).to_frame(rate)
}
}