sampler buzzes

This commit is contained in:
🪞👃🪞 2024-06-03 21:03:55 +03:00
parent faac61180b
commit edc363c55b
6 changed files with 79 additions and 30 deletions

View file

@ -47,7 +47,7 @@ impl Engine {
if exited.fetch_and(true, Ordering::Relaxed) { if exited.fetch_and(true, Ordering::Relaxed) {
Control::Quit Control::Quit
} else { } else {
sender.send(Event::Update); sender.send(Event::Update).unwrap();
Control::Continue Control::Continue
} }
}) as BoxedProcessHandler }) as BoxedProcessHandler

View file

@ -178,7 +178,7 @@ fn run_all () -> Result<(), Box<dyn Error>> {
if key.modifiers == KeyModifiers::SHIFT { if key.modifiers == KeyModifiers::SHIFT {
state.transport.play_from_start_or_stop_and_rewind(); state.transport.play_from_start_or_stop_and_rewind();
} else { } else {
state.transport.play_or_pause(); state.transport.play_or_pause().unwrap();
} }
}, },
KeyCode::Tab => match state.mode { KeyCode::Tab => match state.mode {

View file

@ -15,12 +15,12 @@ pub fn handle (
} }
}, },
KeyCode::Down => { 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); println!("{}", state.selected_sample);
}, },
KeyCode::Up => { KeyCode::Up => {
if state.selected_sample == 0 { if state.selected_sample == 0 {
state.selected_sample = state.samples.len() - 1; state.selected_sample = state.samples.lock().unwrap().len() - 1;
} else { } else {
state.selected_sample = state.selected_sample - 1; state.selected_sample = state.selected_sample - 1;
} }

View file

@ -12,67 +12,114 @@ pub const ACTIONS: [(&'static str, &'static str);2] = [
]; ];
pub struct Sampler { pub struct Sampler {
exited: bool, exited: Arc<AtomicBool>,
jack: Jack<Notifications>, jack: Jack<Notifications>,
samples: Vec<Sample>, samples: Arc<Mutex<Vec<Sample>>>,
selected_sample: usize, selected_sample: usize,
selected_column: usize, selected_column: usize,
} }
impl Sampler { impl Sampler {
pub fn new () -> Result<Self, Box<dyn Error>> { pub fn new () -> Result<Self, Box<dyn Error>> {
let exited = Arc::new(AtomicBool::new(false));
let (client, status) = Client::new( let (client, status) = Client::new(
"bloop-sampler", "blinkenlive-sampler",
ClientOptions::NO_START_SERVER ClientOptions::NO_START_SERVER
)?; )?;
let jack = client.activate_async( let samples = vec![
Notifications, Sample::new("Kick", &client, 1, 35)?,
ClosureProcessHandler::new(Box::new( Sample::new("Snare", &client, 1, 38)?,
move |_: &Client, _: &ProcessScope| -> Control { ];
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 Control::Continue
}) as Box<dyn FnMut(&Client, &ProcessScope)->Control + Send> })
) });
)?;
Ok(Self { Ok(Self {
exited: false, exited,
selected_sample: 0, selected_sample: 0,
selected_column: 0, selected_column: 0,
samples: vec![ samples,
Sample::new("Kick")?, jack: client.activate_async(
Sample::new("Snare")?, self::jack::Notifications,
], ClosureProcessHandler::new(handler)
jack, )?,
}) })
} }
} }
pub struct Sample { pub struct Sample {
port: Port<AudioOut>,
name: String, name: String,
rate: u32, rate: u32,
gain: f64, gain: f64,
channels: u8, channels: u8,
data: Vec<Vec<u8>>, data: Vec<Vec<f32>>,
trigger: (u8, u8),
playing: Option<usize>,
} }
impl Sample { impl Sample {
pub fn new (name: &str) -> Result<Self, Box<dyn Error>> { pub fn new (name: &str, client: &Client, channel: u8, note: u8) -> Result<Self, Box<dyn Error>> {
Ok(Self { Ok(Self {
port: client.register_port(name, ::jack::AudioOut::default())?,
name: name.into(), name: name.into(),
rate: 44100, rate: 44100,
channels: 1, channels: 1,
gain: 0.0, 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 { impl Exitable for Sampler {
fn exit (&mut self) { fn exit (&mut self) {
self.exited = true self.exited.store(true, Ordering::Relaxed)
} }
fn exited (&self) -> bool { fn exited (&self) -> bool {
self.exited self.exited.fetch_and(true, Ordering::Relaxed)
} }
} }

View file

@ -20,13 +20,14 @@ fn render_table (
stdout.queue(move_to(0, 3))?.queue( stdout.queue(move_to(0, 3))?.queue(
Print(" Name Rate Trigger Route") 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; let row = 4 + i as u16;
for (j, (column, field)) in [ for (j, (column, field)) in [
(0, format!(" {:7} ", sample.name)), (0, format!(" {:7} ", sample.name)),
(9, format!(" {:.1}Hz ", sample.rate)), (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)), (33, format!(" {:.1}dB -> Output ", sample.gain)),
(50, format!(" {} ", sample.playing.unwrap_or(0))),
].into_iter().enumerate() { ].into_iter().enumerate() {
stdout.queue(move_to(column, row))?; stdout.queue(move_to(column, row))?;
if state.selected_sample == i && state.selected_column == j { if state.selected_sample == i && state.selected_column == j {
@ -45,7 +46,7 @@ fn render_meters (
offset: (u16, u16), offset: (u16, u16),
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
let move_to = |col, row| crossterm::cursor::MoveTo(offset.0 + col, offset.1 + row); 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; let row = 4 + i as u16;
stdout.queue(move_to(32, row))?.queue( stdout.queue(move_to(32, row))?.queue(
PrintStyledContent("".green()) PrintStyledContent("".green())

View file

@ -6,7 +6,8 @@ pub use self::jack::*;
pub use self::render::*; pub use self::render::*;
use crate::prelude::*; 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"), ("(Shift-)Tab", "Switch pane"),
("Arrows", "Navigate"), ("Arrows", "Navigate"),
("(Shift-)Space", "⯈ Play/pause"), ("(Shift-)Space", "⯈ Play/pause"),