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) {
Control::Quit
} else {
sender.send(Event::Update);
sender.send(Event::Update).unwrap();
Control::Continue
}
}) as BoxedProcessHandler

View file

@ -178,7 +178,7 @@ fn run_all () -> Result<(), Box<dyn Error>> {
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 {

View file

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

View file

@ -12,67 +12,114 @@ pub const ACTIONS: [(&'static str, &'static str);2] = [
];
pub struct Sampler {
exited: bool,
exited: Arc<AtomicBool>,
jack: Jack<Notifications>,
samples: Vec<Sample>,
samples: Arc<Mutex<Vec<Sample>>>,
selected_sample: usize,
selected_column: usize,
}
impl Sampler {
pub fn new () -> Result<Self, Box<dyn Error>> {
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 Box<dyn FnMut(&Client, &ProcessScope)->Control + 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<AudioOut>,
name: String,
rate: u32,
gain: f64,
channels: u8,
data: Vec<Vec<u8>>,
data: Vec<Vec<f32>>,
trigger: (u8, u8),
playing: Option<usize>,
}
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 {
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)
}
}

View file

@ -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<dyn Error>> {
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())

View file

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