mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
212 lines
7.6 KiB
Rust
212 lines
7.6 KiB
Rust
use crate::prelude::*;
|
|
|
|
pub const ACTIONS: [(&'static str, &'static str);2] = [
|
|
("Enter", "Play sample"),
|
|
("Ins/Del", "Add/remove sample"),
|
|
];
|
|
|
|
pub struct Sampler {
|
|
name: String,
|
|
input: ::jack::Port<::jack::MidiIn>,
|
|
samples: Arc<Mutex<Vec<Sample>>>,
|
|
selected_sample: usize,
|
|
selected_column: usize,
|
|
}
|
|
|
|
impl Sampler {
|
|
pub fn new (name: &str) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
|
|
let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
|
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())?;
|
|
Ok(DynamicDevice::new(render, handle, process, Self {
|
|
name: name.into(),
|
|
input,
|
|
selected_sample: 0,
|
|
selected_column: 0,
|
|
samples,
|
|
}))
|
|
}
|
|
}
|
|
|
|
pub fn process (
|
|
state: &mut Sampler,
|
|
_: &Client,
|
|
scope: &ProcessScope,
|
|
) -> Control {
|
|
let mut samples = state.samples.lock().unwrap();
|
|
for event in state.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
|
|
}
|
|
|
|
pub struct Sample {
|
|
port: Port<AudioOut>,
|
|
name: String,
|
|
rate: u32,
|
|
gain: f64,
|
|
channels: u8,
|
|
data: Vec<Vec<f32>>,
|
|
trigger: (u8, u8),
|
|
playing: Option<usize>,
|
|
}
|
|
|
|
impl Sample {
|
|
|
|
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![1.0, 0.0, 0.0, 0.0]],
|
|
trigger: (channel, note),
|
|
playing: None
|
|
})
|
|
}
|
|
|
|
fn play (&mut self, _velocity: u8) {
|
|
self.playing = Some(0)
|
|
}
|
|
|
|
}
|
|
|
|
pub fn render (state: &Sampler, buf: &mut Buffer, Rect { x, y, .. }: Rect)
|
|
-> Usually<Rect>
|
|
{
|
|
let style = Style::default().gray();
|
|
draw_box(buf, Rect { x, y: y, width: 40, height: 12 });
|
|
buf.set_string(x + 1, y + 1, &format!(" {} │", state.name), style.white().bold());
|
|
buf.set_string(x + 0, y + 2, &format!("├--------------------------------------┤"), style.dim());
|
|
buf.set_string(x + 2, y + 3, &format!("C4 Sample#000"), style.bold());
|
|
buf.set_string(x + 2, y + 4, &format!(" {:.03}s",
|
|
100000.0/44100.0), style);
|
|
buf.set_string(x + 2, y + 5, &format!("C#4 Sample#001"), style.bold());
|
|
buf.set_string(x + 2, y + 6, &format!(" {:.03}-{:.03}s",
|
|
50000.0/44100.0, 100000.0/44100.0), style);
|
|
buf.set_string(x + 2, y + 7, &format!("D4 Sample#002"), style.bold());
|
|
buf.set_string(x + 2, y + 8, &format!(" {:.03}-{:.03}/{:.03}s",
|
|
0.0, 50000.0/44100.0, 100000.0/44100.0), style);
|
|
buf.set_string(x + 2, y + 9, &format!("D#4 Sample#003"), style.bold());
|
|
buf.set_string(x + 2, y + 10, &format!(" {:.03}-[{:.03}-{:.03}]/{:.03}s ",
|
|
10000.0/44100.0, 25000.0/44100.0, 50000.0/44100.0, 100000.0/44100.0), style);
|
|
//buf.set_string(x + 1, y + 7 + 6, &format!(" Inputs... │ Outputs... "), style.dim());
|
|
//render_table(state, stdout, offset)?;
|
|
//render_meters(state, stdout, offset)?;
|
|
Ok(Rect { x, y, width: 40, height: 11 })
|
|
}
|
|
|
|
//fn render_table (
|
|
//state: &mut Sampler,
|
|
//stdout: &mut Stdout,
|
|
//offset: (u16, u16),
|
|
//) -> Result<(), Box<dyn Error>> {
|
|
//let move_to = |col, row| crossterm::cursor::MoveTo(offset.0 + col, offset.1 + row);
|
|
//stdout.queue(move_to(0, 3))?.queue(
|
|
//Print(" Name Rate Trigger Route")
|
|
//)?;
|
|
//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 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 {
|
|
//stdout.queue(PrintStyledContent(field.to_string().bold().reverse()))?;
|
|
//} else {
|
|
//stdout.queue(PrintStyledContent(field.to_string().bold()))?;
|
|
//}
|
|
//}
|
|
//}
|
|
//Ok(())
|
|
//}
|
|
|
|
//fn render_meters (
|
|
//state: &mut Sampler,
|
|
//stdout: &mut Stdout,
|
|
//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.lock().iter().enumerate() {
|
|
//let row = 4 + i as u16;
|
|
//stdout.queue(move_to(32, row))?.queue(
|
|
//PrintStyledContent("▁".green())
|
|
//)?;
|
|
//}
|
|
//Ok(())
|
|
//}
|
|
|
|
pub fn handle (_: &mut Sampler, _: &AppEvent) -> Usually<bool> {
|
|
Ok(false)
|
|
//if let Event::Input(crossterm::event::Event::Key(event)) = event {
|
|
//match event.code {
|
|
//KeyCode::Char('c') => {
|
|
//if event.modifiers == KeyModifiers::CONTROL {
|
|
//state.exit();
|
|
//}
|
|
//},
|
|
//KeyCode::Down => {
|
|
//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.lock().unwrap().len() - 1;
|
|
//} else {
|
|
//state.selected_sample = state.selected_sample - 1;
|
|
//}
|
|
//println!("{}", state.selected_sample);
|
|
//},
|
|
//KeyCode::Left => {
|
|
//if state.selected_column == 0 {
|
|
//state.selected_column = 6
|
|
//} else {
|
|
//state.selected_column = state.selected_column - 1;
|
|
//}
|
|
//},
|
|
//KeyCode::Right => {
|
|
//if state.selected_column == 6 {
|
|
//state.selected_column = 0
|
|
//} else {
|
|
//state.selected_column = state.selected_column + 1;
|
|
//}
|
|
//},
|
|
//_ => {
|
|
//println!("{event:?}");
|
|
//}
|
|
//}
|
|
//}
|
|
}
|