use crate::prelude::*; pub struct Voice { sample: Arc, position: usize, } impl Voice { fn chunk (&mut self, frames: usize) -> Option>> { let mut chunk = vec![]; if self.position < self.sample.end { let start = self.position.min(self.sample.end); let end = (self.position + frames).min(self.sample.end); for channel in self.sample.channels.iter() { chunk.push(channel[start..end].into()); }; self.position = self.position + frames; Some(chunk) } else { None } } } pub struct Sample { pub name: String, pub start: usize, pub end: usize, pub channels: Vec>, } impl Sample { pub fn new (name: &str, start: usize, end: usize, channels: Vec>) -> Arc { Arc::new(Self { name: name.to_string(), start, end, channels }) } fn play (self: &Arc) -> Voice { Voice { sample: self.clone(), position: self.start } } } pub struct Sampler { name: String, cursor: (usize, usize), samples: BTreeMap>, voices: Vec, midi_in: Port, audio_ins: Vec>, audio_outs: Vec>, } impl Sampler { pub fn new ( name: &str, samples: Option>>, ) -> Result, Box> { let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?; DynamicDevice::new(render, handle, Self::process, Self { name: name.into(), cursor: (0, 0), samples: samples.unwrap_or(BTreeMap::new()), voices: vec![], midi_in: client.register_port("midi", ::jack::MidiIn::default())?, audio_ins: vec![ client.register_port("recL", ::jack::AudioIn::default())?, client.register_port("recR", ::jack::AudioIn::default())?, ], audio_outs: vec![ client.register_port("outL", ::jack::AudioOut::default())?, client.register_port("outR", ::jack::AudioOut::default())?, ], }).activate(client) } pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { // Output buffer. This will be copied to the audio outs. let channel_count = self.audio_outs.len(); let mut mixed = vec![vec![0.0;scope.n_frames() as usize];channel_count]; // Emit next chunk of each currently playing voice, // dropping voices that have reached their ends. let mut voices = vec![]; std::mem::swap(&mut voices, &mut self.voices); loop { if voices.len() < 1 { break } let mut voice = voices.swap_remove(0); if let Some(chunk) = voice.chunk(scope.n_frames() as usize) { for (i, channel) in chunk.iter().enumerate() { let buffer = &mut mixed[i % channel_count]; for (i, sample) in channel.iter().enumerate() { buffer[i] += sample; } } self.voices.push(voice); } } // process midi in // add new voices // emit new voices starting from midi event frames // for (i, port) in self.audio_outs.iter_mut().enumerate() { let buffer = &mixed[i]; for (i, value) in port.as_mut_slice(scope).iter_mut().enumerate() { *value = *buffer.get(i).unwrap_or(&0.0); } } Control::Continue //for event in self.midi_in.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 self.samples.iter_mut() { //if sample.trigger.0 == channel && sample.trigger.1 == note { //sample.play(velocity); //} //} //} //for sample in self.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) //} //} //} //} } fn load_sample (&mut self, _path: &str) {} } impl PortList for Sampler { fn midi_ins (&self) -> Usually> { Ok(vec![self.midi_in.name()?]) } fn audio_ins (&self) -> Usually> { let mut ports = vec![]; for port in self.audio_ins.iter() { ports.push(port.name()?); } Ok(ports) } fn audio_outs (&self) -> Usually> { let mut ports = vec![]; for port in self.audio_outs.iter() { ports.push(port.name()?); } Ok(ports) } } pub fn render (state: &Sampler, buf: &mut Buffer, Rect { x, y, height, .. }: Rect) -> Usually { let width = 40; let style = Style::default().gray(); format!(" {} ({})", state.name, state.voices.len()).blit(buf, x+1, y, Some(style.white().bold().not_dim())); for (i, (note, sample)) in state.samples.iter().enumerate() { let style = if i == state.cursor.0 { Style::default().green() } else { Style::default() }; let i = i as u16; let y1 = y+1+i; if y1 >= y + height { break } if i as usize == state.cursor.0 { "⯈".blit(buf, x+1, y1, Some(style.bold())); } let label1 = format!("{note:3} {:10}", sample.name); let label2 = format!("{:>7} {:>7}", sample.start, sample.end); label1.blit(buf, x+2, y1, Some(style.bold())); label2.blit(buf, x+3+label1.len()as u16, y1, Some(style)); } Ok(Rect { x, y, width, height }) } //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 (state: &mut Sampler, event: &AppEvent) -> Usually { Ok(handle_keymap(state, event, KEYMAP)?) } pub const KEYMAP: &'static [KeyBinding] = keymap!(Sampler { [Up, NONE, "cursor_up", "move cursor up", cursor_up], [Down, NONE, "cursor_down", "move cursor down", cursor_down], [Enter, NONE, "select", "select item under cursor", select], }); fn cursor_up (state: &mut Sampler) -> Usually { state.cursor.0 = if state.cursor.0 == 0 { state.samples.len() - 1 } else { state.cursor.0 - 1 }; Ok(true) } fn cursor_down (state: &mut Sampler) -> Usually { state.cursor.0 = (state.cursor.0 + 1) % state.samples.len(); Ok(true) } fn select (state: &mut Sampler) -> Usually { for (i, sample) in state.samples.values().enumerate() { if i == state.cursor.0 { state.voices.push(sample.play()) } } Ok(true) }