diff --git a/demos/project.edn b/demos/project.edn index 288d577d..863978b3 100644 --- a/demos/project.edn +++ b/demos/project.edn @@ -1,7 +1,7 @@ (bpm 150) (midi-in "nanoKEY Studio.*capture.*") -(audio-out "Komplete.+:playback_FL", "Komplete.+:playback_FR") +(audio-out "Built-.+:playback_FL", "Built-.+:playback_FR") (scene { :name "Intro" } 0 0 _ _) (scene { :name "Hook" } 1 1 0 _) diff --git a/src/devices/sampler.rs b/src/devices/sampler.rs index 115213f7..387400b2 100644 --- a/src/devices/sampler.rs +++ b/src/devices/sampler.rs @@ -11,7 +11,7 @@ pub struct Sampler { pub editing: Option>>, pub mapped: BTreeMap>>, pub unmapped: Vec>>, - pub voices: Vec, + pub voices: Arc>>, pub ports: JackPorts, pub buffer: Vec>, pub output_gain: f32 @@ -20,7 +20,7 @@ pub struct Sampler { render!(Sampler |self, buf, area| { let Rect { x, y, height, .. } = area; let style = Style::default().gray(); - let title = format!(" {} ({})", self.name, self.voices.len()); + let title = format!(" {} ({})", self.name, self.voices.read().unwrap().len()); title.blit(buf, x+1, y, Some(style.white().bold().not_dim()))?; let mut width = title.len() + 2; let mut y1 = 1; @@ -63,7 +63,7 @@ handle!(Sampler |self, event| handle_keymap(self, event, KEYMAP_SAMPLER)); /// Key bindings for sampler device. pub const KEYMAP_SAMPLER: &'static [KeyBinding] = keymap!(Sampler { - [Up, NONE, "cursor_up", "move cursor up", |state: &mut 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 { @@ -71,29 +71,29 @@ pub const KEYMAP_SAMPLER: &'static [KeyBinding] = keymap!(Sampler { }; Ok(true) }], - [Down, NONE, "cursor_down", "move cursor down", |state: &mut Sampler| { + [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('t'), NONE, "sample_play", "play current sample", |state: &mut Sampler| { + [Char('t'), NONE, "/sampler/play", "play current sample", |state: &mut Sampler| { if let Some(sample) = state.sample() { - state.voices.push(Sample::play(sample, 0, &100.into())) + state.voices.write().unwrap().push(Sample::play(sample, 0, &100.into())); } Ok(true) }], - [Char('a'), NONE, "sample_add", "add a new sample", |state: &mut Sampler| { - let sample = Sample::new("", 0, 0, vec![]); - *MODAL.lock().unwrap() = Some(Exit::boxed(AddSampleModal::new(&sample)?)); + [Char('a'), NONE, "/sampler/add", "add a new sample", |state: &mut Sampler| { + let sample = Arc::new(RwLock::new(Sample::new("", 0, 0, vec![]))); + *MODAL.lock().unwrap() = Some(Exit::boxed(AddSampleModal::new(&sample, &state.voices)?)); state.unmapped.push(sample); Ok(true) }], - [Char('r'), NONE, "sample_replace", "replace selected sample", |state: &mut Sampler| { + [Char('r'), NONE, "/sampler/replace", "replace selected sample", |state: &mut Sampler| { if let Some(sample) = state.sample() { - *MODAL.lock().unwrap() = Some(Exit::boxed(AddSampleModal::new(&sample)?)); + *MODAL.lock().unwrap() = Some(Exit::boxed(AddSampleModal::new(&sample, &state.voices)?)); } Ok(true) }], - [Enter, NONE, "sample_edit", "edit selected sample", |state: &mut Sampler| { + [Enter, NONE, "/sampler/edit", "edit selected sample", |state: &mut Sampler| { if let Some(sample) = state.sample() { state.editing = Some(sample.clone()); } @@ -117,7 +117,7 @@ impl Sampler { editing: None, mapped: mapped.unwrap_or_else(||BTreeMap::new()), unmapped: vec![], - voices: vec![], + voices: Arc::new(RwLock::new(vec![])), ports, buffer: vec![vec![0.0;16384];2], output_gain: 0.5, @@ -153,7 +153,7 @@ impl Sampler { 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.push(Sample::play(sample, time as usize, vel)); + self.voices.write().unwrap().push(Sample::play(sample, time as usize, vel)); } } } @@ -170,7 +170,7 @@ impl Sampler { /// Mix all currently playing samples into the output. fn process_audio_out (&mut self, scope: &ProcessScope) { let channel_count = self.buffer.len(); - self.voices.retain_mut(|voice|{ + 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() { diff --git a/src/devices/sampler/add_sample.rs b/src/devices/sampler/add_sample.rs index 24704f13..b05aef58 100644 --- a/src/devices/sampler/add_sample.rs +++ b/src/devices/sampler/add_sample.rs @@ -17,6 +17,7 @@ pub struct AddSampleModal { cursor: usize, offset: usize, sample: Arc>, + voices: Arc>>, _search: Option, } @@ -65,7 +66,10 @@ handle!(AddSampleModal |self,e|{ }); impl AddSampleModal { - pub fn new (sample: &Arc>) -> Usually { + pub fn new ( + sample: &Arc>, + voices: &Arc>> + ) -> Usually { let dir = std::env::current_dir()?; let (subdirs, files) = scan(&dir)?; Ok(Self { @@ -76,6 +80,7 @@ impl AddSampleModal { cursor: 0, offset: 0, sample: sample.clone(), + voices: voices.clone(), _search: None }) } @@ -93,6 +98,12 @@ impl AddSampleModal { } fn try_preview (&mut self) -> Usually<()> { if let Some(path) = self.cursor_file() { + if let Ok(sample) = Sample::from_file(&path) { + *self.sample.write().unwrap() = sample; + self.voices.write().unwrap().push( + Sample::play(&self.sample, 0, &u7::from(100u8)) + ); + } //load_sample(&path)?; //let src = std::fs::File::open(&path)?; //let mss = MediaSourceStream::new(Box::new(src), Default::default()); @@ -198,9 +209,9 @@ fn scan (dir: &PathBuf) -> Usually<(Vec, Vec)> { } impl Sample { - fn from_file (path: &PathBuf) -> Usually>> { + fn from_file (path: &PathBuf) -> Usually { let mut sample = Self::default(); - sample.name = path.to_string_lossy().into(); + sample.name = path.file_name().unwrap().to_string_lossy().into(); // Use file extension if present let mut hint = Hint::new(); if let Some(ext) = path.extension() { @@ -256,6 +267,7 @@ impl Sample { Err(err) => return Err(err.into()), }; }; - Ok(Arc::new(RwLock::new(sample))) + sample.end = sample.channels.iter().fold(0, |l, c|l + c.len()); + Ok(sample) } } diff --git a/src/devices/sampler/sample.rs b/src/devices/sampler/sample.rs index 4116d869..5b17d93c 100644 --- a/src/devices/sampler/sample.rs +++ b/src/devices/sampler/sample.rs @@ -2,7 +2,7 @@ use crate::core::*; use super::*; /// A sound sample. -#[derive(Default)] +#[derive(Default, Debug)] pub struct Sample { pub name: String, pub start: usize, @@ -12,10 +12,8 @@ pub struct Sample { } impl Sample { - pub fn new ( - name: &str, start: usize, end: usize, channels: Vec> - ) -> Arc> { - Arc::new(RwLock::new(Self { name: name.to_string(), start, end, channels, rate: None })) + pub fn new (name: &str, start: usize, end: usize, channels: Vec>) -> Self { + Self { name: name.to_string(), start, end, channels, rate: None } } pub fn play (sample: &Arc>, after: usize, velocity: &u7) -> Voice { Voice { diff --git a/src/edn.rs b/src/edn.rs index 10200ed1..887159ad 100644 --- a/src/edn.rs +++ b/src/edn.rs @@ -295,7 +295,7 @@ impl Sample { _ => panic!("unexpected in sample {name}"), }); let (end, data) = read_sample_data(&format!("{dir}/{file}"))?; - Ok((midi, Self::new(&name, start, end, data))) + Ok((midi, Arc::new(RwLock::new(Self::new(&name, start, end, data))))) } }