mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
sampler buzzes
This commit is contained in:
parent
faac61180b
commit
edc363c55b
6 changed files with 79 additions and 30 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue