From edc363c55bb223df265dd50080eb23f8777df1b8 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Mon, 3 Jun 2024 21:03:55 +0300 Subject: [PATCH] sampler buzzes --- src/engine.rs | 2 +- src/main.rs | 2 +- src/sampler/handle.rs | 4 +- src/sampler/mod.rs | 91 ++++++++++++++++++++++++++++++++----------- src/sampler/render.rs | 7 ++-- src/transport/mod.rs | 3 +- 6 files changed, 79 insertions(+), 30 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 0a58ba5f..dc8aff7e 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -47,7 +47,7 @@ impl Engine { if exited.fetch_and(true, Ordering::Relaxed) { Control::Quit } else { - sender.send(Event::Update); + sender.send(Event::Update).unwrap(); Control::Continue } }) as BoxedProcessHandler diff --git a/src/main.rs b/src/main.rs index f264d894..03908ebd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -178,7 +178,7 @@ fn run_all () -> Result<(), Box> { if key.modifiers == KeyModifiers::SHIFT { state.transport.play_from_start_or_stop_and_rewind(); } else { - state.transport.play_or_pause(); + state.transport.play_or_pause().unwrap(); } }, KeyCode::Tab => match state.mode { diff --git a/src/sampler/handle.rs b/src/sampler/handle.rs index d613ebb2..c4f5cf95 100644 --- a/src/sampler/handle.rs +++ b/src/sampler/handle.rs @@ -15,12 +15,12 @@ pub fn handle ( } }, KeyCode::Down => { - state.selected_sample = (state.selected_sample + 1) % state.samples.len(); + state.selected_sample = (state.selected_sample + 1) % state.samples.lock().unwrap().len(); println!("{}", state.selected_sample); }, KeyCode::Up => { if state.selected_sample == 0 { - state.selected_sample = state.samples.len() - 1; + state.selected_sample = state.samples.lock().unwrap().len() - 1; } else { state.selected_sample = state.selected_sample - 1; } diff --git a/src/sampler/mod.rs b/src/sampler/mod.rs index d2d4a9e6..a5767b0c 100644 --- a/src/sampler/mod.rs +++ b/src/sampler/mod.rs @@ -12,67 +12,114 @@ pub const ACTIONS: [(&'static str, &'static str);2] = [ ]; pub struct Sampler { - exited: bool, + exited: Arc, jack: Jack, - samples: Vec, + samples: Arc>>, selected_sample: usize, selected_column: usize, } impl Sampler { pub fn new () -> Result> { + let exited = Arc::new(AtomicBool::new(false)); let (client, status) = Client::new( - "bloop-sampler", + "blinkenlive-sampler", ClientOptions::NO_START_SERVER )?; - let jack = client.activate_async( - Notifications, - ClosureProcessHandler::new(Box::new( - move |_: &Client, _: &ProcessScope| -> Control { - Control::Continue - }) as BoxControl + Send> - ) - )?; + let samples = vec![ + Sample::new("Kick", &client, 1, 35)?, + Sample::new("Snare", &client, 1, 38)?, + ]; + let samples = Arc::new(Mutex::new(samples)); + let input = client.register_port("trigger", ::jack::MidiIn::default())?; + let handler: BoxedProcessHandler = Box::new({ + let exited = exited.clone(); + let samples = samples.clone(); + Box::new(move |_: &Client, scope: &ProcessScope| -> Control { + if exited.fetch_and(true, Ordering::Relaxed) { + return Control::Quit + } + let mut samples = samples.lock().unwrap(); + for event in input.iter(scope) { + let len = 3.min(event.bytes.len()); + let mut data = [0; 3]; + data[..len].copy_from_slice(&event.bytes[..len]); + if (data[0] >> 4) == 0b1001 { // note on + let channel = data[0] & 0b00001111; + let note = data[1]; + let velocity = data[2]; + for sample in samples.iter_mut() { + if /*sample.trigger.0 == channel &&*/ sample.trigger.1 == note { + sample.play(velocity); + } + } + } + for sample in samples.iter_mut() { + if let Some(playing) = sample.playing { + for (index, value) in sample.port.as_mut_slice(scope).iter_mut().enumerate() { + *value = *sample.data[0].get(playing + index).unwrap_or(&0f32); + } + if playing + scope.n_frames() as usize > sample.data[0].len() { + sample.playing = None + } else { + sample.playing = Some(playing + scope.n_frames() as usize) + } + } + } + } + Control::Continue + }) + }); Ok(Self { - exited: false, + exited, selected_sample: 0, selected_column: 0, - samples: vec![ - Sample::new("Kick")?, - Sample::new("Snare")?, - ], - jack, + samples, + jack: client.activate_async( + self::jack::Notifications, + ClosureProcessHandler::new(handler) + )?, }) } } pub struct Sample { + port: Port, name: String, rate: u32, gain: f64, channels: u8, - data: Vec>, + data: Vec>, + trigger: (u8, u8), + playing: Option, } impl Sample { - pub fn new (name: &str) -> Result> { + pub fn new (name: &str, client: &Client, channel: u8, note: u8) -> Result> { Ok(Self { + port: client.register_port(name, ::jack::AudioOut::default())?, name: name.into(), rate: 44100, channels: 1, gain: 0.0, - data: vec![vec![]] + data: vec![vec![1.0, 0.0, 0.0, 0.0]], + trigger: (channel, note), + playing: None }) } + fn play (&mut self, velocity: u8) { + self.playing = Some(0) + } + } impl Exitable for Sampler { fn exit (&mut self) { - self.exited = true + self.exited.store(true, Ordering::Relaxed) } fn exited (&self) -> bool { - self.exited + self.exited.fetch_and(true, Ordering::Relaxed) } } diff --git a/src/sampler/render.rs b/src/sampler/render.rs index dbec8338..dff0fa3d 100644 --- a/src/sampler/render.rs +++ b/src/sampler/render.rs @@ -20,13 +20,14 @@ fn render_table ( stdout.queue(move_to(0, 3))?.queue( Print(" Name Rate Trigger Route") )?; - for (i, sample) in state.samples.iter().enumerate() { + for (i, sample) in state.samples.lock().unwrap().iter().enumerate() { let row = 4 + i as u16; for (j, (column, field)) in [ (0, format!(" {:7} ", sample.name)), (9, format!(" {:.1}Hz ", sample.rate)), - (18, format!(" MIDI C10 36 ")), + (18, format!(" MIDI C{} {} ", sample.trigger.0, sample.trigger.1)), (33, format!(" {:.1}dB -> Output ", sample.gain)), + (50, format!(" {} ", sample.playing.unwrap_or(0))), ].into_iter().enumerate() { stdout.queue(move_to(column, row))?; if state.selected_sample == i && state.selected_column == j { @@ -45,7 +46,7 @@ fn render_meters ( offset: (u16, u16), ) -> Result<(), Box> { let move_to = |col, row| crossterm::cursor::MoveTo(offset.0 + col, offset.1 + row); - for (i, sample) in state.samples.iter().enumerate() { + for (i, sample) in state.samples.lock().iter().enumerate() { let row = 4 + i as u16; stdout.queue(move_to(32, row))?.queue( PrintStyledContent("▁".green()) diff --git a/src/transport/mod.rs b/src/transport/mod.rs index ab2b863d..769f5eee 100644 --- a/src/transport/mod.rs +++ b/src/transport/mod.rs @@ -6,7 +6,8 @@ pub use self::jack::*; pub use self::render::*; use crate::prelude::*; -pub const ACTIONS: [(&'static str, &'static str);3] = [ +pub const ACTIONS: [(&'static str, &'static str);4] = [ + ("?", "Toggle help"), ("(Shift-)Tab", "Switch pane"), ("Arrows", "Navigate"), ("(Shift-)Space", "⯈ Play/pause"),