mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
fix warnings (not 55 errors)
This commit is contained in:
parent
a50e022ab6
commit
87c5e47b43
8 changed files with 290 additions and 269 deletions
|
|
@ -21,7 +21,7 @@ use crossterm::event;
|
||||||
pub fn run (device: impl Device + Send + Sync + 'static) -> Result<(), Box<dyn Error>> {
|
pub fn run (device: impl Device + Send + Sync + 'static) -> Result<(), Box<dyn Error>> {
|
||||||
let device = Arc::new(Mutex::new(device));
|
let device = Arc::new(Mutex::new(device));
|
||||||
let exited = Arc::new(AtomicBool::new(false));
|
let exited = Arc::new(AtomicBool::new(false));
|
||||||
let input_thread = {
|
let _input_thread = {
|
||||||
let poll = std::time::Duration::from_millis(100);
|
let poll = std::time::Duration::from_millis(100);
|
||||||
let exited = exited.clone();
|
let exited = exited.clone();
|
||||||
let device = device.clone();
|
let device = device.clone();
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ pub fn render (state: &Mixer, buf: &mut Buffer, mut area: Rect)
|
||||||
draw_box(buf, area);
|
draw_box(buf, area);
|
||||||
let x = area.x + 1;
|
let x = area.x + 1;
|
||||||
let y = area.y + 1;
|
let y = area.y + 1;
|
||||||
let h = area.height - 2;
|
let _h = area.height - 2;
|
||||||
for (i, track) in state.tracks.iter().enumerate() {
|
for (i, track) in state.tracks.iter().enumerate() {
|
||||||
//buf.set_string(
|
//buf.set_string(
|
||||||
//x, y + index as u16,
|
//x, y + index as u16,
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ pub fn process (_: &mut Plugin, _: &Client, _: &ProcessScope) -> Control {
|
||||||
Control::Continue
|
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>
|
-> Usually<Rect>
|
||||||
{
|
{
|
||||||
let style = Style::default().gray();
|
let style = Style::default().gray();
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,7 @@ pub struct Sampler {
|
||||||
|
|
||||||
impl Sampler {
|
impl Sampler {
|
||||||
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
|
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
|
||||||
let exited = Arc::new(AtomicBool::new(false));
|
let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
||||||
let (client, status) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
|
||||||
let samples = vec![
|
let samples = vec![
|
||||||
Sample::new("Kick", &client, 1, 35)?,
|
Sample::new("Kick", &client, 1, 35)?,
|
||||||
Sample::new("Snare", &client, 1, 38)?,
|
Sample::new("Snare", &client, 1, 38)?,
|
||||||
|
|
@ -35,7 +34,7 @@ impl Sampler {
|
||||||
|
|
||||||
pub fn process (
|
pub fn process (
|
||||||
state: &mut Sampler,
|
state: &mut Sampler,
|
||||||
client: &Client,
|
_: &Client,
|
||||||
scope: &ProcessScope,
|
scope: &ProcessScope,
|
||||||
) -> Control {
|
) -> Control {
|
||||||
let mut samples = state.samples.lock().unwrap();
|
let mut samples = state.samples.lock().unwrap();
|
||||||
|
|
@ -48,7 +47,7 @@ pub fn process (
|
||||||
let note = data[1];
|
let note = data[1];
|
||||||
let velocity = data[2];
|
let velocity = data[2];
|
||||||
for sample in samples.iter_mut() {
|
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);
|
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)
|
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>
|
-> Usually<Rect>
|
||||||
{
|
{
|
||||||
let style = Style::default().gray();
|
let style = Style::default().gray();
|
||||||
|
|
@ -170,7 +169,7 @@ pub fn render (state: &Sampler, buf: &mut Buffer, Rect { x, y, width, height }:
|
||||||
//Ok(())
|
//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 {
|
//if let Event::Input(crossterm::event::Event::Key(event)) = event {
|
||||||
//match event.code {
|
//match event.code {
|
||||||
//KeyCode::Char('c') => {
|
//KeyCode::Char('c') => {
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,18 @@ type Sequence = std::collections::BTreeMap<u32, Vec<::midly::MidiMessage>>;
|
||||||
|
|
||||||
pub struct Sequencer {
|
pub struct Sequencer {
|
||||||
name: String,
|
name: String,
|
||||||
|
/// JACK transport handle.
|
||||||
transport: ::jack::Transport,
|
transport: ::jack::Transport,
|
||||||
/// Samples per second
|
/// Holds info about tempo
|
||||||
rate: Hz,
|
timebase: Arc<Mutex<Timebase>>,
|
||||||
/// Beats per minute
|
|
||||||
tempo: Tempo,
|
|
||||||
/// MIDI resolution (a.k.a. PPQ)
|
|
||||||
ticks_per_beat: u64,
|
|
||||||
/// Sequencer resolution, e.g. 16 steps per beat.
|
/// 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 in sequence, e.g. 64 16ths = 4 beat loop.
|
||||||
steps: u64,
|
steps: u64,
|
||||||
|
|
||||||
|
/// JACK MIDI input port that will be created.
|
||||||
input_port: Port<MidiIn>,
|
input_port: Port<MidiIn>,
|
||||||
|
/// Port name patterns to connect to.
|
||||||
input_connect: Vec<String>,
|
input_connect: Vec<String>,
|
||||||
/// Play input through output.
|
/// Play input through output.
|
||||||
monitoring: bool,
|
monitoring: bool,
|
||||||
|
|
@ -30,7 +30,9 @@ pub struct Sequencer {
|
||||||
overdub: bool,
|
overdub: bool,
|
||||||
/// Play sequence to output.
|
/// Play sequence to output.
|
||||||
playing: bool,
|
playing: bool,
|
||||||
|
/// JACK MIDI output port that will be created.
|
||||||
output_port: Port<MidiOut>,
|
output_port: Port<MidiOut>,
|
||||||
|
/// Port name patterns to connect to.
|
||||||
output_connect: Vec<String>,
|
output_connect: Vec<String>,
|
||||||
|
|
||||||
/// Display mode
|
/// Display mode
|
||||||
|
|
@ -54,103 +56,33 @@ enum SequencerView {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sequencer {
|
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)?;
|
let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
||||||
DynamicDevice::new(render, handle, process, Self {
|
DynamicDevice::new(render, handle, process, Self {
|
||||||
name: name.into(),
|
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,
|
mode: SequencerView::Vertical,
|
||||||
note_axis: (36, 68),
|
note_axis: (36, 68),
|
||||||
note_cursor: 0,
|
note_cursor: 0,
|
||||||
time_axis: (0, 64),
|
time_axis: (0, 64),
|
||||||
time_cursor: 0,
|
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)
|
}).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 {
|
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 pos = s.transport.query().unwrap().pos;
|
||||||
let usecs = Frame(pos.frame()).to_usec(&s.rate).0;
|
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 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) {
|
for event in s.input_port.iter(scope) {
|
||||||
match midly::live::LiveEvent::parse(event.bytes).unwrap() {
|
match midly::live::LiveEvent::parse(event.bytes).unwrap() {
|
||||||
|
|
||||||
midly::live::LiveEvent::Midi { channel: _, message } => match message {
|
midly::live::LiveEvent::Midi { channel: _, message } => match message {
|
||||||
|
|
||||||
midly::MidiMessage::NoteOn { key, vel: _ } => {
|
midly::MidiMessage::NoteOn { key, vel: _ } => {
|
||||||
s.notes_on[key.as_int() as usize] = true;
|
s.notes_on[key.as_int() as usize] = true;
|
||||||
if s.sequence.contains_key(&(tick as u32)) {
|
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()]);
|
s.sequence.insert(tick as u32, vec![message.clone()]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
midly::MidiMessage::NoteOff { key, vel: _ } => {
|
midly::MidiMessage::NoteOff { key, vel: _ } => {
|
||||||
s.notes_on[key.as_int() as usize] = false;
|
s.notes_on[key.as_int() as usize] = false;
|
||||||
if s.sequence.contains_key(&(tick as u32)) {
|
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()]);
|
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 frame = pos.frame();
|
||||||
let rate = pos.frame_rate().unwrap();
|
let rate = pos.frame_rate().unwrap();
|
||||||
let usecs = Frame(frame).to_usec(&Hz(rate)).0;
|
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 steps = usecs / usec_per_step;
|
||||||
let header = draw_header(s, buf, area, steps)?;
|
let header = draw_header(s, buf, area, steps)?;
|
||||||
let piano = match s.mode {
|
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> {
|
fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: u64) -> Usually<Rect> {
|
||||||
let rep = beat / s.steps;
|
let rep = beat / s.steps;
|
||||||
let step = beat % s.steps;
|
let step = beat % s.steps;
|
||||||
let reps = s.steps / s.steps_per_beat;
|
let reps = s.steps / s.resolution;
|
||||||
let steps = s.steps % s.steps_per_beat;
|
let steps = s.steps % s.resolution;
|
||||||
let Rect { x, y, .. } = area;
|
let Rect { x, y, .. } = area;
|
||||||
let style = Style::default().gray();
|
let style = Style::default().gray();
|
||||||
buf.set_string(x + 1, y + 1, &format!(" │ {rep}.{step:2} / {reps}.{steps}"), style);
|
buf.set_string(x + 1, y + 1,
|
||||||
buf.set_string(x + 2, y + 1, &format!("{}", &s.name), style.white().bold());
|
&format!(" │ {rep}.{step:2} / {reps}.{steps}"),
|
||||||
buf.set_string(x + 1, y + 2, &format!(" ▶ PLAY │ ⏹ STOP │ │"), style);
|
style);
|
||||||
buf.set_string(x + 2, y + 2, &format!("▶ PLAY"), if s.playing {
|
buf.set_string(x + 2, y + 1,
|
||||||
Style::default().green()
|
&format!("{}", &s.name),
|
||||||
} else {
|
style.white().bold());
|
||||||
Style::default().dim()
|
buf.set_string(x + 1, y + 2,
|
||||||
});
|
&format!(" ▶ PLAY │ ⏹ STOP │ │"),
|
||||||
buf.set_string(x + 24, y + 2, &format!("⏺ REC"), if s.recording {
|
style);
|
||||||
Style::default().red()
|
buf.set_string(x + 2, y + 2,
|
||||||
} else {
|
&format!("▶ PLAY"), if s.playing {
|
||||||
Style::default().dim()
|
Style::default().green()
|
||||||
});
|
} else {
|
||||||
buf.set_string(x + 32, y + 2, &format!("⏺ DUB"), if s.overdub {
|
Style::default().dim()
|
||||||
Style::default().yellow()
|
});
|
||||||
} else {
|
buf.set_string(x + 24, y + 2,
|
||||||
Style::default().dim()
|
&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 })
|
Ok(Rect { x, y, width: 39, height: 4 })
|
||||||
}
|
}
|
||||||
|
|
||||||
const KEY_WHITE: Style = Style {
|
const KEYS_VERTICAL: [&'static str; 6] = [
|
||||||
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 draw_vertical (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: u64) -> Usually<Rect> {
|
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 mut is_on = s.notes_on[key as usize];
|
||||||
let step = beat % s.steps;
|
let step = beat % s.steps;
|
||||||
let (a, b, c) = (
|
let (a, b, c) = (
|
||||||
(step + 0) 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.steps_per_beat 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.steps_per_beat 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);
|
let key = ::midly::num::u7::from(key as u8);
|
||||||
is_on = is_on || contains_note_on(&s.sequence, key, a, b);
|
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 y = y - time0 + step / 2;
|
||||||
let step = step as u64;
|
let step = step as u64;
|
||||||
//buf.set_string(x + 5, y, &" ".repeat(32.max(note1-note0)as usize), bg);
|
//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());
|
buf.set_string(x + 2, y, &format!("{:2} ", step + 1), Style::default());
|
||||||
}
|
}
|
||||||
for k in note0..note1 {
|
for k in note0..note1 {
|
||||||
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 * 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.steps_per_beat 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.steps_per_beat as u32,
|
(step + 2) as u32 * s.ticks_per_beat as u32 / s.resolution as u32,
|
||||||
);
|
);
|
||||||
let (character, style) = match (
|
let (character, style) = match (
|
||||||
contains_note_on(&s.sequence, key, a, b),
|
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;
|
let height = (time1-time0)/2;
|
||||||
buf.set_string(x + 2, y + height + 1, format!(
|
buf.set_string(x + 2, y + height + 1, format!(
|
||||||
"Q 1/{} | N {} ({}-{}) | T {} ({}-{})",
|
"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_cursor,
|
||||||
s.note_axis.0,
|
s.note_axis.0,
|
||||||
s.note_axis.1 - 1,
|
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 })
|
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 {
|
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() {
|
for event in events.iter() {
|
||||||
match event {
|
match event {
|
||||||
::midly::MidiMessage::NoteOn {key,..} => {
|
::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;
|
let height = 32.max(note1 - note0) / 2;
|
||||||
buf.set_string(x + 2, y + height + 1, format!(
|
buf.set_string(x + 2, y + height + 1, format!(
|
||||||
" Q 1/{} | N {} ({}-{}) | T {} ({}-{})",
|
" 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_cursor,
|
||||||
s.note_axis.0,
|
s.note_axis.0,
|
||||||
s.note_axis.1 - 1,
|
s.note_axis.1 - 1,
|
||||||
|
|
@ -496,10 +448,10 @@ fn nop (_: &mut Sequencer) {
|
||||||
fn note_add (s: &mut Sequencer) {
|
fn note_add (s: &mut Sequencer) {
|
||||||
let pos = s.transport.query().unwrap().pos;
|
let pos = s.transport.query().unwrap().pos;
|
||||||
let usecs = Frame(pos.frame()).to_usec(&s.rate).0;
|
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 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 start = (step as u64 * s.ticks_per_beat / s.resolution) as u32;
|
||||||
let end = ((step + 1) as u64 * s.ticks_per_beat / s.steps_per_beat) 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 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_on = ::midly::MidiMessage::NoteOn { key, vel: 100.into() };
|
||||||
let note_off = ::midly::MidiMessage::NoteOff { 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) {
|
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 {
|
match s.mode {
|
||||||
SequencerView::Vertical => note_cursor_dec(s),
|
SequencerView::Vertical => note_cursor_dec(s),
|
||||||
SequencerView::Horizontal => time_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) {
|
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 {
|
match s.mode {
|
||||||
SequencerView::Vertical => note_cursor_inc(s),
|
SequencerView::Vertical => note_cursor_inc(s),
|
||||||
SequencerView::Horizontal => time_cursor_inc(s),
|
SequencerView::Horizontal => time_cursor_inc(s),
|
||||||
_ => unimplemented!()
|
_ => unimplemented!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn cursor_duration_inc (s: &mut Sequencer) {
|
fn cursor_duration_inc (_: &mut Sequencer) {
|
||||||
//s.cursor.2 = s.cursor.2 + 1
|
//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 }
|
//if s.cursor.2 > 0 { s.cursor.2 = s.cursor.2 - 1 }
|
||||||
}
|
}
|
||||||
fn mode_next (s: &mut Sequencer) {
|
fn mode_next (s: &mut Sequencer) {
|
||||||
|
|
@ -595,13 +543,13 @@ fn toggle_overdub (s: &mut Sequencer) {
|
||||||
s.overdub = !s.overdub
|
s.overdub = !s.overdub
|
||||||
}
|
}
|
||||||
fn quantize_next (s: &mut Sequencer) {
|
fn quantize_next (s: &mut Sequencer) {
|
||||||
if s.steps_per_beat < 64 {
|
if s.resolution < 64 {
|
||||||
s.steps_per_beat = s.steps_per_beat * 2
|
s.resolution = s.resolution * 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn quantize_prev (s: &mut Sequencer) {
|
fn quantize_prev (s: &mut Sequencer) {
|
||||||
if s.steps_per_beat > 1 {
|
if s.resolution > 1 {
|
||||||
s.steps_per_beat = s.steps_per_beat / 2
|
s.resolution = s.resolution / 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,23 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
pub struct Transport {
|
pub struct Transport {
|
||||||
name: String,
|
name: String,
|
||||||
transport: ::jack::Transport,
|
/// Holds info about tempo
|
||||||
timesig: (f32, f32),
|
timebase: Arc<Mutex<Timebase>>,
|
||||||
bpm: f64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transport {
|
impl Transport {
|
||||||
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
|
pub fn new (name: &str) -> 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();
|
||||||
DynamicDevice::new(render, handle, process, Self {
|
DynamicDevice::new(render, handle, process, Self {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
transport: client.transport(),
|
timebase: Timebase {
|
||||||
timesig: (4.0, 4.0),
|
rate: transport.query()?.pos.frame_rate(),
|
||||||
bpm: 113.0,
|
tempo: 113000,
|
||||||
|
ppq: 96,
|
||||||
|
},
|
||||||
|
transport
|
||||||
}).activate(client)
|
}).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)
|
pub fn render (state: &Transport, buf: &mut Buffer, mut area: Rect)
|
||||||
-> Usually<Rect>
|
-> Usually<Rect>
|
||||||
{
|
{
|
||||||
area.x = area.width.saturating_sub(80) / 2;
|
//area.x = area.width.saturating_sub(80) / 2;
|
||||||
area.width = area.width.min(80);
|
//area.width = area.width.min(80);
|
||||||
area.height = 5;
|
area.height = 5;
|
||||||
//draw_box(buf, area);
|
//draw_box(buf, area);
|
||||||
let label = Style::default().white().not_dim();
|
let label = Style::default().white().not_dim();
|
||||||
|
|
|
||||||
14
src/main.rs
14
src/main.rs
|
|
@ -21,7 +21,15 @@ fn main () -> Result<(), Box<dyn Error>> {
|
||||||
let xdg = microxdg::XdgApp::new("dawdle")?;
|
let xdg = microxdg::XdgApp::new("dawdle")?;
|
||||||
crate::config::create_dirs(&xdg)?;
|
crate::config::create_dirs(&xdg)?;
|
||||||
//crate::device::run(Sequencer::new("Rhythm#000")?)
|
//crate::device::run(Sequencer::new("Rhythm#000")?)
|
||||||
|
const transport = Transport::new("Transport")?;
|
||||||
|
const timebase = transport.timebase.clone();
|
||||||
crate::device::run(Rows::new(true, vec![
|
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(Columns::new(false, vec![
|
||||||
//Box::new(Chain::new("Chain#00", vec![
|
//Box::new(Chain::new("Chain#00", vec![
|
||||||
//Box::new(Sequencer::new("Rhythm#000")?),
|
//Box::new(Sequencer::new("Rhythm#000")?),
|
||||||
|
|
@ -33,12 +41,6 @@ fn main () -> Result<(), Box<dyn Error>> {
|
||||||
//])?),
|
//])?),
|
||||||
//])),
|
//])),
|
||||||
//Box::new(Mixer::new("Mixer#000")?),
|
//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")?),
|
//Box::new(Sequencer::new("Rhythm#000")?),
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
237
src/time.rs
237
src/time.rs
|
|
@ -1,89 +1,16 @@
|
||||||
/// Number of data frames in a second.
|
pub struct Timebase {
|
||||||
#[derive(Debug)]
|
/// Frames per second
|
||||||
pub struct Hz(pub u32);
|
pub rate: Option<usize>,
|
||||||
|
/// Beats per minute
|
||||||
/// One data frame.
|
pub tempo: Option<usize>,
|
||||||
#[derive(Debug)]
|
/// Ticks per beat
|
||||||
pub struct Frame(pub u32);
|
pub ppq: usize,
|
||||||
|
|
||||||
/// 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>),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Frame {
|
enum QuantizeMode {
|
||||||
#[inline]
|
Forward,
|
||||||
pub fn to_usec (&self, rate: &Hz) -> Usec {
|
Backward,
|
||||||
Usec((self.0 as u64 * 1000000) / rate.0 as u64)
|
Nearest,
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Loop<T> {
|
struct Loop<T> {
|
||||||
|
|
@ -93,3 +20,145 @@ struct Loop<T> {
|
||||||
end: T,
|
end: T,
|
||||||
post_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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue