use crate::*; /// The sampler plugin plays sounds. pub struct Sampler { pub name: String, pub cursor: (usize, usize), pub editing: Option>>, pub mapped: BTreeMap>>, pub unmapped: Vec>>, pub voices: Arc>>, pub ports: JackPorts, pub buffer: Vec>, pub modal: Arc>>>, pub output_gain: f32 } impl Handle for Sampler { fn handle (&mut self, e: &E) -> Usually { handle_keymap(self, event, KEYMAP_SAMPLER) } } impl Render for Sampler { fn render (&self, to: &mut Tui) -> Perhaps { tui_render_sampler(self, to) } } process!(Sampler = Sampler::process); /// Key bindings for sampler device. pub const KEYMAP_SAMPLER: &'static [KeyBinding] = keymap!(Sampler { [Up, NONE, "/sampler/cursor/up", "move cursor up", |state: &mut Sampler| { state.cursor.0 = if state.cursor.0 == 0 { state.mapped.len() + state.unmapped.len() - 1 } else { state.cursor.0 - 1 }; Ok(true) }], [Down, NONE, "/sampler/cursor/down", "move cursor down", |state: &mut Sampler| { state.cursor.0 = (state.cursor.0 + 1) % (state.mapped.len() + state.unmapped.len()); Ok(true) }], [Char('p'), NONE, "/sampler/play", "play current sample", |state: &mut Sampler| { if let Some(sample) = state.sample() { state.voices.write().unwrap().push(Sample::play(sample, 0, &100.into())); } Ok(true) }], [Char('a'), NONE, "/sampler/add", "add a new sample", |state: &mut Sampler| { let sample = Arc::new(RwLock::new(Sample::new("", 0, 0, vec![]))); *state.modal.lock().unwrap() = Some(Exit::boxed(AddSampleModal::new(&sample, &state.voices)?)); state.unmapped.push(sample); Ok(true) }], [Char('r'), NONE, "/sampler/replace", "replace selected sample", |state: &mut Sampler| { if let Some(sample) = state.sample() { *state.modal.lock().unwrap() = Some(Exit::boxed(AddSampleModal::new(&sample, &state.voices)?)); } Ok(true) }], [Enter, NONE, "/sampler/edit", "edit selected sample", |state: &mut Sampler| { if let Some(sample) = state.sample() { state.editing = Some(sample.clone()); } Ok(true) }], }); impl Sampler { pub fn from_edn <'e, E: Engine> (args: &[Edn<'e>]) -> Usually> { let mut name = String::new(); let mut dir = String::new(); let mut samples = BTreeMap::new(); edn!(edn in args { Edn::Map(map) => { if let Some(Edn::Str(n)) = map.get(&Edn::Key(":name")) { name = String::from(*n); } if let Some(Edn::Str(n)) = map.get(&Edn::Key(":dir")) { dir = String::from(*n); } }, Edn::List(args) => match args.get(0) { Some(Edn::Symbol("sample")) => { let (midi, sample) = Sample::from_edn(&dir, &args[1..])?; if let Some(midi) = midi { samples.insert(midi, sample); } else { panic!("sample without midi binding: {}", sample.read().unwrap().name); } }, _ => panic!("unexpected in sampler {name}: {args:?}") }, _ => panic!("unexpected in sampler {name}: {edn:?}") }); Self::new(&name, Some(samples)) } pub fn new ( name: &str, mapped: Option>>> ) -> Usually> { Jack::new(name)? .midi_in("midi") .audio_in("recL") .audio_in("recR") .audio_out("outL") .audio_out("outR") .run(|ports|Box::new(Self { name: name.into(), cursor: (0, 0), editing: None, mapped: mapped.unwrap_or_else(||BTreeMap::new()), unmapped: vec![], voices: Arc::new(RwLock::new(vec![])), ports, buffer: vec![vec![0.0;16384];2], output_gain: 0.5, modal: Default::default() })) } /// Immutable reference to sample at cursor. pub fn sample (&self) -> Option<&Arc>> { for (i, sample) in self.mapped.values().enumerate() { if i == self.cursor.0 { return Some(sample) } } for (i, sample) in self.unmapped.iter().enumerate() { if i + self.mapped.len() == self.cursor.0 { return Some(sample) } } None } pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { self.process_midi_in(scope); self.clear_output_buffer(); self.process_audio_out(scope); self.write_output_buffer(scope); Control::Continue } /// Create [Voice]s from [Sample]s in response to MIDI input. fn process_midi_in (&mut self, scope: &ProcessScope) { for RawMidi { time, bytes } in self.ports.midi_ins.get("midi").unwrap().iter(scope) { if let LiveEvent::Midi { message, .. } = LiveEvent::parse(bytes).unwrap() { if let MidiMessage::NoteOn { ref key, ref vel } = message { if let Some(sample) = self.mapped.get(key) { self.voices.write().unwrap().push(Sample::play(sample, time as usize, vel)); } } } } } /// Zero the output buffer. fn clear_output_buffer (&mut self) { for buffer in self.buffer.iter_mut() { buffer.fill(0.0); } } /// Mix all currently playing samples into the output. fn process_audio_out (&mut self, scope: &ProcessScope) { let channel_count = self.buffer.len(); self.voices.write().unwrap().retain_mut(|voice|{ for index in 0..scope.n_frames() as usize { if let Some(frame) = voice.next() { for (channel, sample) in frame.iter().enumerate() { // Averaging mixer: //self.buffer[channel % channel_count][index] = ( //(self.buffer[channel % channel_count][index] + sample * self.output_gain) / 2.0 //); self.buffer[channel % channel_count][index] += sample * self.output_gain; } } else { return false } } return true }); } /// Write output buffer to output ports. fn write_output_buffer (&mut self, scope: &ProcessScope) { for (i, port) in self.ports.audio_outs.values_mut().enumerate() { let buffer = &self.buffer[i]; for (i, value) in port.as_mut_slice(scope).iter_mut().enumerate() { *value = *buffer.get(i).unwrap_or(&0.0); } } } }