diff --git a/crates/tek/src/groovebox.rs b/crates/tek/src/groovebox.rs index 86649d19..3266a170 100644 --- a/crates/tek/src/groovebox.rs +++ b/crates/tek/src/groovebox.rs @@ -61,7 +61,6 @@ render!(|self:GrooveboxTui|{ let phrase_w = if w > 60 { 20 } else if w > 40 { 15 } else { 10 }; let pool_w = if self.pool.visible { phrase_w } else { 0 }; let sampler_w = 11; - let note_pt = self.editor.note_point(); Fill::wh(lay!([ &self.size, Fill::wh(Align::s(Fixed::h(2, GrooveboxStatus::from(self)))), @@ -82,18 +81,8 @@ render!(|self:GrooveboxTui|{ Tui::split_w(false, pool_w, Tui::pull_y(1, Fill::h(Align::e(PoolView(&self.pool)))), Tui::split_e(false, sampler_w, Fill::wh(col!([ - &format!("L/{:>+9.3}", self.sampler.input_meter[0]), - &format!("R/{:>+9.3}", self.sampler.input_meter[1]), - Fill::wh(col!(note in (self.editor.note_lo().load(Relaxed)..=self.editor.note_hi()).rev() => { - Tui::bg( - if note == note_pt { TuiTheme::g(64) } else { Color::Reset }, - if let Some(sample) = &self.sampler.mapped[note] { - todo!() - } else { - Tui::fg(TuiTheme::g(160), format!("{note:3} (none)")) - } - ) - })), + Meters(self.sampler.input_meter.as_ref()), + GrooveboxSamples(self), ])), Fill::h(&self.editor)) ) ), @@ -102,6 +91,34 @@ render!(|self:GrooveboxTui|{ ])) }); +struct Meters<'a>(&'a[f32]); +render!(|self: Meters<'a>|col!([ + &format!("L/{:>+9.3}", self.0[0]), + &format!("R/{:>+9.3}", self.0[1]), +])); + +struct GrooveboxSamples<'a>(&'a GrooveboxTui); +render!(|self: GrooveboxSamples<'a>|{ + let note_lo = self.0.editor.note_lo().load(Relaxed); + let note_pt = self.0.editor.note_point(); + let note_hi = self.0.editor.note_hi(); + Fill::wh(col!(note in (note_lo..=note_hi).rev() => { + let mut bg = if note == note_pt { TuiTheme::g(64) } else { Color::Reset }; + let mut fg = TuiTheme::g(160); + if let Some((index, _)) = self.0.sampler.recording { + if note == index { + bg = Color::Rgb(64,16,0); + fg = Color::Rgb(224,64,32) + } + } + Tui::bg(bg, if let Some(sample) = &self.0.sampler.mapped[note] { + todo!() + } else { + Tui::fg(fg, format!("{note:3} (none) ")) + }) + })) +}); + pub enum GrooveboxCommand { History(isize), Clock(ClockCommand), @@ -137,7 +154,7 @@ input_to_command!(GrooveboxCommand: |state: GrooveboxTui, input|match input key_pat!(Char('0')) => Enqueue(Some(state.pool.phrases()[0].clone())), key_pat!(Shift-Char('R')) => Sampler( - SamplerCommand::Record(u7::from(state.editor.note_point() as u8)) + SamplerCommand::RecordBegin(u7::from(state.editor.note_point() as u8)) ), // e: Toggle between editing currently playing or other phrase diff --git a/crates/tek/src/sampler.rs b/crates/tek/src/sampler.rs index d692b955..85434e82 100644 --- a/crates/tek/src/sampler.rs +++ b/crates/tek/src/sampler.rs @@ -39,6 +39,7 @@ pub struct Sampler { pub jack: Arc>, pub name: String, pub mapped: [Option>>;128], + pub recording: Option<(usize, Sample)>, pub unmapped: Vec>>, pub voices: Arc>>, pub midi_in: Port, @@ -68,8 +69,38 @@ impl Sampler { voices: Arc::new(RwLock::new(vec![])), buffer: vec![vec![0.0;16384];2], output_gain: 0.5, + recording: None, }) } + pub fn cancel_recording (&mut self) { + self.recording = None; + } + pub fn begin_recording (&mut self, index: usize) { + self.recording = Some( + (index, Sample::new("(new)", 0, 0, vec![vec![];self.audio_ins.len()])) + ); + } + pub fn finish_recording (&mut self) -> Option>> { + let recording = self.recording.take(); + if let Some((index, sample)) = recording { + let old = self.mapped[index].clone(); + self.mapped[index] = Some(Arc::new(RwLock::new(sample))); + old + } else { + None + } + } + pub fn record_chunk (&mut self, chunks: &[&[f32]]) { + if let Some((_, sample)) = &mut self.recording { + if chunks.len() != sample.channels.len() { + panic!() + } + for (chunk, channel) in chunks.iter().zip(sample.channels.iter_mut()) { + channel.extend_from_slice(chunk) + } + sample.end += chunks[0].len(); + } + } } pub struct SamplerTui { pub state: Sampler, diff --git a/crates/tek/src/sampler/sampler_control.rs b/crates/tek/src/sampler/sampler_control.rs index 7963fcad..6e490fef 100644 --- a/crates/tek/src/sampler/sampler_control.rs +++ b/crates/tek/src/sampler/sampler_control.rs @@ -11,7 +11,9 @@ pub enum SamplerTuiCommand { } pub enum SamplerCommand { - Record(u7), + RecordBegin(u7), + RecordCancel, + RecordFinish, SetSample(u7, Option>>), SetGain(f32), NoteOn(u7, u7), @@ -78,10 +80,17 @@ command!(|self: SamplerCommand, state: Sampler|match self { state.mapped[i] = sample; Some(Self::SetSample(index, old)) }, - Self::Record(index) => { - let i = index.as_int() as usize; - let old = state.mapped[i].clone(); - Some(Self::SetSample(index, old)) + Self::RecordBegin(index) => { + state.begin_recording(index.as_int() as usize); + None + }, + Self::RecordCancel => { + state.cancel_recording(); + None + }, + Self::RecordFinish => { + state.finish_recording(); + None }, _ => todo!() });