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>> { 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();

View file

@ -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,

View file

@ -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();

View file

@ -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') => {

View file

@ -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
} }
} }

View file

@ -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();

View file

@ -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")?),
])) ]))
} }

View file

@ -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)
}
}