diff --git a/crates/tek/src/api/jack.rs b/crates/tek/src/api/jack.rs index 99b0edcf..79a80d54 100644 --- a/crates/tek/src/api/jack.rs +++ b/crates/tek/src/api/jack.rs @@ -24,6 +24,17 @@ pub trait Audio: Send + Sync { } } +#[macro_export] macro_rules! from_jack { + (|$jack:ident|$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)? $cb:expr) => { + impl $(<$($L),*$($T $(: $U)?),*>)? TryFrom<&Arc>> for $Struct $(<$($L),*$($T),*>)? { + type Error = Box; + fn try_from ($jack: &Arc>) -> Usually { + Ok($cb) + } + } + }; +} + pub trait HasMidiIns { fn midi_ins (&self) -> &Vec>; fn midi_ins_mut (&mut self) -> &mut Vec>; diff --git a/crates/tek/src/layout/cond.rs b/crates/tek/src/layout/cond.rs index c4a6314d..7966f536 100644 --- a/crates/tek/src/layout/cond.rs +++ b/crates/tek/src/layout/cond.rs @@ -1,5 +1,11 @@ use crate::*; +pub enum Cond, B: Render> { + _Unused(E), + When(bool, A), + Either(bool, A, B) +} + impl> LayoutCond for R {} pub trait LayoutCond: Render + Sized { diff --git a/crates/tek/src/tui/app_groovebox.rs b/crates/tek/src/tui/app_groovebox.rs index 654198f3..72aadb16 100644 --- a/crates/tek/src/tui/app_groovebox.rs +++ b/crates/tek/src/tui/app_groovebox.rs @@ -11,7 +11,7 @@ impl TryFrom<&Arc>> for GrooveboxTui { sequencer, sampler: SamplerTui::try_from(jack)?, split: 16, - focus: GrooveboxFocus::Sequencer, + focus: GrooveboxFocus::Sampler, }) } } diff --git a/crates/tek/src/tui/app_sampler.rs b/crates/tek/src/tui/app_sampler.rs index d61db7aa..dd551f04 100644 --- a/crates/tek/src/tui/app_sampler.rs +++ b/crates/tek/src/tui/app_sampler.rs @@ -8,8 +8,44 @@ use symphonia::core::probe::Hint; use symphonia::core::audio::SampleBuffer; use symphonia::default::get_codecs; +pub struct SamplerTui { + pub state: Sampler, + pub cursor: (usize, usize), + pub editing: Option>>, + pub mode: Option, +} +pub enum SamplerMode { + // Load sample from path + Import(usize, FileBrowser), +} +from_jack!(|jack|SamplerTui{ + let midi_in = jack.read().unwrap().client().register_port("in", MidiIn::default())?; + let audio_outs = vec![ + jack.read().unwrap().client().register_port("outL", AudioOut::default())?, + jack.read().unwrap().client().register_port("outR", AudioOut::default())?, + ]; + Self { + cursor: (0, 0), + editing: None, + mode: None, + state: Sampler { + jack: jack.clone(), + name: "Sampler".into(), + mapped: BTreeMap::new(), + unmapped: vec![], + voices: Arc::new(RwLock::new(vec![])), + buffer: vec![vec![0.0;16384];2], + output_gain: 0.5, + midi_in, + audio_outs, + }, + } +}); +handle!(|self:SamplerTui,input|SamplerCommand::execute_with_state(self, input)); pub enum SamplerCommand { Import(FileBrowserCommand), + SelectNote(usize), + SelectField(usize), SetName(String), SetNote(u7, Arc>), SetGain(f32), @@ -18,7 +54,34 @@ pub enum SamplerCommand { } input_to_command!(SamplerCommand:|state:SamplerTui,input|match state.mode { Some(SamplerMode::Import(..)) => Self::Import(FileBrowserCommand::input_to_command(state, input)?), - _ => return None + _ => todo!() + //_ => match input.event() { + //key_pat!(KeyCode::Up) => state.cursor.0 = if state.cursor.0 == 0 { + //mapped.len() + unmapped.len() - 1 + //} else { + //state.cursor.0 - 1 + //}, + //key_pat!(KeyCode::Down) => { + //state.cursor.0 = (state.cursor.0 + 1) % (mapped.len() + unmapped.len()); + //}, + //key_pat!(KeyCode::Char('p')) => if let Some(sample) = self.sample() { + //voices.write().unwrap().push(Sample::play(sample, 0, &100.into())); + //}, + //key_pat!(KeyCode::Char('a')) => { + //let sample = Arc::new(RwLock::new(Sample::new("", 0, 0, vec![]))); + //self.mode = None;//Some(Exit::boxed(AddSampleModal::new(&sample, &voices)?)); + //unmapped.push(sample); + //}, + //key_pat!(KeyCode::Char('r')) => if let Some(sample) = self.sample() { + //self.mode = None;//Some(Exit::boxed(AddSampleModal::new(&sample, &voices)?)); + //}, + //key_pat!(KeyCode::Enter) => if let Some(sample) = self.sample() { + //self.editing = Some(sample.clone()); + //}, + //_ => { + //return Ok(None) + //} + //} }); input_to_command!(FileBrowserCommand:|state:SamplerTui,input|match input { _ => return None @@ -35,46 +98,23 @@ command!(|self:SamplerCommand,state:SamplerTui|match self { command!(|self:FileBrowserCommand,state:SamplerTui|match self { _ => todo!() }); - -impl TryFrom<&Arc>> for SamplerTui { - type Error = Box; - fn try_from (jack: &Arc>) -> Usually { - let midi_in = jack.read().unwrap().client().register_port("in", MidiIn::default())?; - let audio_outs = vec![ - jack.read().unwrap().client().register_port("outL", AudioOut::default())?, - jack.read().unwrap().client().register_port("outR", AudioOut::default())?, - ]; - Ok(Self { - cursor: (0, 0), - editing: None, - mode: None, - state: Sampler { - jack: jack.clone(), - name: "Sampler".into(), - mapped: BTreeMap::new(), - unmapped: vec![], - voices: Arc::new(RwLock::new(vec![])), - buffer: vec![vec![0.0;16384];2], - output_gain: 0.5, - midi_in, - audio_outs, - }, - }) +impl SamplerTui { + /// Immutable reference to sample at cursor. + pub fn sample (&self) -> Option<&Arc>> { + for (i, sample) in self.state.mapped.values().enumerate() { + if i == self.cursor.0 { + return Some(sample) + } + } + for (i, sample) in self.state.unmapped.iter().enumerate() { + if i + self.state.mapped.len() == self.cursor.0 { + return Some(sample) + } + } + None } } -pub struct SamplerTui { - pub state: Sampler, - pub cursor: (usize, usize), - pub editing: Option>>, - pub mode: Option, -} - -enum SamplerMode { - // Load sample from path - Import(usize, FileBrowser), -} - audio!(|self: SamplerTui, _client, scope|{ self.state.process_midi_in(scope); self.state.clear_output_buffer(); @@ -82,6 +122,7 @@ audio!(|self: SamplerTui, _client, scope|{ self.state.write_output_buffer(scope); Control::Continue }); + render!(|self: SamplerTui|Tui::min_y(10, Fill::wh(lay!([ Fill::wh(render(|to|{ // border @@ -113,58 +154,6 @@ render!(|self: SamplerTui|Tui::min_y(10, Fill::wh(lay!([ ])))); -handle!(|self:SamplerTui,from|{ - let cursor = &mut self.cursor; - let unmapped = &mut self.state.unmapped; - let mapped = &self.state.mapped; - let voices = &self.state.voices; - match from.event() { - key_pat!(KeyCode::Up) => cursor.0 = if cursor.0 == 0 { - mapped.len() + unmapped.len() - 1 - } else { - cursor.0 - 1 - }, - key_pat!(KeyCode::Down) => { - cursor.0 = (cursor.0 + 1) % (mapped.len() + unmapped.len()); - }, - key_pat!(KeyCode::Char('p')) => if let Some(sample) = self.sample() { - voices.write().unwrap().push(Sample::play(sample, 0, &100.into())); - }, - key_pat!(KeyCode::Char('a')) => { - let sample = Arc::new(RwLock::new(Sample::new("", 0, 0, vec![]))); - self.mode = None;//Some(Exit::boxed(AddSampleModal::new(&sample, &voices)?)); - unmapped.push(sample); - }, - key_pat!(KeyCode::Char('r')) => if let Some(sample) = self.sample() { - self.mode = None;//Some(Exit::boxed(AddSampleModal::new(&sample, &voices)?)); - }, - key_pat!(KeyCode::Enter) => if let Some(sample) = self.sample() { - self.editing = Some(sample.clone()); - }, - _ => { - return Ok(None) - } - } - Ok(Some(true)) -}); - -impl SamplerTui { - /// Immutable reference to sample at cursor. - pub fn sample (&self) -> Option<&Arc>> { - for (i, sample) in self.state.mapped.values().enumerate() { - if i == self.cursor.0 { - return Some(sample) - } - } - for (i, sample) in self.state.unmapped.iter().enumerate() { - if i + self.state.mapped.len() == self.cursor.0 { - return Some(sample) - } - } - None - } -} - pub struct AddSampleModal { exited: bool, dir: PathBuf,