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>>, selected_sample: usize, selected_column: usize, } impl Sampler { pub fn new (name: &str) -> Result, Box> { 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, name: String, rate: u32, gain: f64, channels: u8, data: Vec>, trigger: (u8, u8), playing: Option, } impl Sample { 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![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 { 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> { //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> { //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) -> Result<(), Box> { //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:?}"); //} //} //} Ok(()) }