mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +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>> {
|
||||
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();
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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') => {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
14
src/main.rs
14
src/main.rs
|
|
@ -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")?),
|
||||
]))
|
||||
}
|
||||
|
|
|
|||
237
src/time.rs
237
src/time.rs
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue