diff --git a/crates/app/edn/sampler_keys.edn b/crates/app/edn/sampler_keys.edn index ef07497e..c8cc0c84 100644 --- a/crates/app/edn/sampler_keys.edn +++ b/crates/app/edn/sampler_keys.edn @@ -10,4 +10,4 @@ (@right select :sample-right) (@d select :sample-right) -(@r record/begin :sample) +(@r record/toggle :sample) diff --git a/crates/app/src/api.rs b/crates/app/src/api.rs index 6ad73389..8d914253 100644 --- a/crates/app/src/api.rs +++ b/crates/app/src/api.rs @@ -24,12 +24,16 @@ expose!([self: Tek] ([isize]) ([Color]) ([Arc>]) - ([u7] (":pitch" (self.editor().map(|e|e.note_pos()).unwrap() as u8).into())) - ([u16] (":w-sidebar" self.w_sidebar())) - ([usize] (":scene-last" self.scenes.len()) - (":track-last" self.tracks.len())) - ([Option] (":scene" self.selected.scene()) - (":track" self.selected.track())) + ([u7] + (":pitch" (self.editor().map(|e|e.note_pos()).unwrap() as u8).into())) + ([u16] + (":w-sidebar" self.w_sidebar())) + ([usize] + (":scene-last" self.scenes.len()) + (":track-last" self.tracks.len())) + ([Option] + (":scene" self.selected.scene()) + (":track" self.selected.track())) ([MaybeClip] (":clip" match self.selected { Selection::Clip(t, s) => self.scenes[s].clips[t].clone(), @@ -108,7 +112,7 @@ impose!([app: Tek] defcom!([self, app: Tek] (TekCommand - (Sampler [cmd: SamplerCommand] cmd_todo!("\n\rtodo: sampler {cmd:?}")) + (Sampler [cmd: SamplerCommand] app.sampler_mut().map(|s|cmd.delegate(s, Self::Sampler)).transpose()?.flatten()) (Scene [cmd: SceneCommand] cmd.delegate(app, Self::Scene)?) (Track [cmd: TrackCommand] cmd.delegate(app, Self::Track)?) (Output [cmd: OutputCommand] cmd.delegate(app, Self::Output)?) diff --git a/crates/app/src/model.rs b/crates/app/src/model.rs index ba3a36cb..bd807637 100644 --- a/crates/app/src/model.rs +++ b/crates/app/src/model.rs @@ -316,7 +316,12 @@ impl Tek { /// Get the first sampler of the active track pub fn sampler (&self) -> Option<&Sampler> { - self.tracks.get(0).map(|t|t.sampler(0)).flatten() + self.track().map(|t|t.sampler(0)).flatten() + } + + /// Get the first sampler of the active track + pub fn sampler_mut (&mut self) -> Option<&mut Sampler> { + self.track_mut().map(|t|t.sampler_mut(0)).flatten() } /// Set the color of the selected entity diff --git a/crates/cli/tek.rs b/crates/cli/tek.rs index 33455dde..15950013 100644 --- a/crates/cli/tek.rs +++ b/crates/cli/tek.rs @@ -149,6 +149,7 @@ impl Cli { _ => vec![] }, scenes, + selected: Selection::Clip(0, 0), ..Default::default() }; if let &LaunchMode::Arranger { scenes, tracks, track_width, .. } = mode { diff --git a/crates/sampler/src/sampler_api.rs b/crates/sampler/src/sampler_api.rs index 066fe666..f80a7247 100644 --- a/crates/sampler/src/sampler_api.rs +++ b/crates/sampler/src/sampler_api.rs @@ -2,15 +2,17 @@ use crate::*; expose!([self: Sampler] ([Arc]) - ([Option>>]) + ([MaybeSample]) ([PathBuf]) ([f32]) - ([u7] (":pitch" (self.note_pos() as u8).into()) // TODO - (":sample" (self.note_pos() as u8).into())) - ([usize] (":sample-up" self.note_pos().min(119) + 8) - (":sample-down" self.note_pos().max(8) - 8) - (":sample-left" self.note_pos().min(126) + 1) - (":sample-right" self.note_pos().max(1) - 1))); + ([u7] + (":pitch" (self.note_pos() as u8).into()) // TODO + (":sample" (self.note_pos() as u8).into())) + ([usize] + (":sample-up" self.note_pos().min(119) + 8) + (":sample-down" self.note_pos().max(8) - 8) + (":sample-left" self.note_pos().min(126) + 1) + (":sample-right" self.note_pos().max(1) - 1))); impose!([state: Sampler] (FileBrowserCommand: @@ -31,7 +33,7 @@ impose!([state: Sampler] Some(Self::RecordCancel)) ("record/finish" [] Some(Self::RecordFinish)) - ("set/sample" [i: u7, s: Option>>] + ("set/sample" [i: u7, s: MaybeSample] Some(Self::SetSample(i.expect("no index"), s.expect("no sampler")))) ("set/start" [i: u7, s: usize] Some(Self::SetStart(i.expect("no index"), s.expect("no start")))) @@ -42,17 +44,21 @@ impose!([state: Sampler] ("note/off" [p: u7] Some(Self::NoteOff(p.expect("no pitch")))))); +macro_rules! cmd { ($cmd:expr) => {{ $cmd; None }}; } +macro_rules! cmd_todo { ($msg:literal) => {{ println!($msg); None }}; } + defcom!([self, state: Sampler] (SamplerCommand - (Select [i: usize] Some(Self::Select(state.set_note_pos(i)))) - (RecordBegin [p: u7] { state.begin_recording(p.as_int() as usize); None }) - (RecordCancel [] { state.cancel_recording(); None }) - (RecordFinish [] { state.finish_recording(); None }) - (SetStart [pitch: u7, frame: usize] { println!("\n\rtodo: {self:?}"); None }) - (SetGain [pitch: u7, gain: f32] { println!("\n\rtodo: {self:?}"); None }) - (NoteOn [pitch: u7, velocity: u7] { println!("\n\rtodo: {self:?}"); None }) - (NoteOff [pitch: u7] { println!("\n\rtodo: {self:?}"); None }) - (Import [cmd: FileBrowserCommand] match cmd { + (Select [i: usize] Some(Self::Select(state.set_note_pos(i)))) + (RecordBegin [p: u7] cmd!(state.begin_recording(p.as_int() as usize))) + (RecordCancel [] cmd!(state.cancel_recording())) + (RecordFinish [] cmd!(state.finish_recording())) + (SetStart [p: u7, frame: usize] cmd_todo!("\n\rtodo: {self:?}")) + (SetGain [p: u7, gain: f32] cmd_todo!("\n\rtodo: {self:?}")) + (NoteOn [p: u7, velocity: u7] cmd_todo!("\n\rtodo: {self:?}")) + (NoteOff [p: u7] cmd_todo!("\n\rtodo: {self:?}")) + (SetSample [p: u7, s: MaybeSample] Some(Self::SetSample(p, state.set_sample(p, s)))) + (Import [c: FileBrowserCommand] match c { FileBrowserCommand::Begin => { //let voices = &state.state.voices; //let sample = Arc::new(RwLock::new(Sample::new("", 0, 0, vec![]))); @@ -60,13 +66,7 @@ defcom!([self, state: Sampler] None }, _ => { - println!("\n\rtodo: import: filebrowser: {cmd:?}"); + println!("\n\rtodo: import: filebrowser: {c:?}"); None } - }) - (SetSample [pitch: u7, sample: Option>>] { - let i = pitch.as_int() as usize; - let old = state.mapped[i].clone(); - state.mapped[i] = sample; - Some(Self::SetSample(pitch, old)) }))); diff --git a/crates/sampler/src/sampler_model.rs b/crates/sampler/src/sampler_model.rs index 37440b04..1cf983ad 100644 --- a/crates/sampler/src/sampler_model.rs +++ b/crates/sampler/src/sampler_model.rs @@ -1,10 +1,12 @@ use crate::*; +pub type MaybeSample = Option>>; + /// The sampler device plays sounds in response to MIDI notes. #[derive(Debug)] pub struct Sampler { pub name: String, - pub mapped: [Option>>;128], + pub mapped: [MaybeSample;128], pub recording: Option<(usize, Arc>)>, pub unmapped: Vec>>, pub voices: Arc>>, @@ -14,7 +16,7 @@ pub struct Sampler { pub audio_outs: Vec, pub buffer: Vec>, pub output_gain: f32, - pub editing: Option>>, + pub editing: MaybeSample, pub mode: Option, /// Size of actual notes area pub size: Measure, @@ -83,7 +85,7 @@ impl Sampler { Arc::new(RwLock::new(Sample::new("Sample", 0, 0, vec![vec![];self.audio_ins.len()]))) )); } - pub fn finish_recording (&mut self) -> Option>> { + pub fn finish_recording (&mut self) -> MaybeSample { let recording = self.recording.take(); if let Some((index, sample)) = recording { let old = self.mapped[index].clone(); @@ -111,6 +113,13 @@ impl Sampler { pub fn cursor (&self) -> (usize, usize) { (self.cursor.0.load(Relaxed), self.cursor.1.load(Relaxed)) } + /// Assign sample to pitch + pub fn set_sample (&mut self, pitch: u7, sample: MaybeSample) -> MaybeSample { + let i = pitch.as_int() as usize; + let old = self.mapped[i].clone(); + self.mapped[i] = sample; + old + } } impl NoteRange for Sampler { @@ -123,8 +132,12 @@ impl NoteRange for Sampler { } impl NotePoint for Sampler { - fn note_len (&self) -> usize { 0/*TODO*/ } - fn set_note_len (&self, x: usize) -> usize { 0 /*TODO*/ } + fn note_len (&self) -> usize { + 0 /*TODO?*/ + } + fn set_note_len (&self, x: usize) -> usize { + 0 /*TODO?*/ + } fn note_pos (&self) -> usize { self.note_pt.load(Relaxed) }