diff --git a/config/config_groovebox.edn b/config/config_groovebox.edn index 5fa63e9e..3afc185b 100644 --- a/config/config_groovebox.edn +++ b/config/config_groovebox.edn @@ -12,17 +12,9 @@ (layer "./keys_global.edn")) (view (bsp/a :view-dialog (bsp/w :view-meters-output (bsp/e :view-meters-input - (bsp/e - (fill/y (align/n - (bsp/s :view-status-v - (bsp/s :view-editor-status :view-pool)))) - (bsp/w - (fill/y (align/n - (bsp/s :view-midi-ins-status - (bsp/s :view-midi-outs-status - (bsp/s :view-audio-ins-status :view-audio-outs-status))))) - (bsp/n - (fixed/y :h-sample-detail - (bsp/e (fill/y (fixed/x 20 (align/nw :view-sample-status))) - :view-sample-viewer)) - (bsp/e :view-samples-keys :view-editor)))))))) + (bsp/n (fixed/y 6 (bsp/e :view-sample-status :view-sample-viewer)) + (bsp/e + (fill/y (align/n (bsp/s :view-status-v (bsp/s + (bsp/s :view-midi-ports-status :view-audio-ports-status) + (bsp/n :view-editor-status :view-pool))))) + (bsp/e :view-samples-keys :view-editor))))))) diff --git a/crates/app/src/model.rs b/crates/app/src/model.rs index 9696e698..4948c2c1 100644 --- a/crates/app/src/model.rs +++ b/crates/app/src/model.rs @@ -189,9 +189,6 @@ impl App { fn w_sidebar (&self) -> u16 { self.project.w_sidebar(self.editor.is_some()) } - fn h_sample_detail (&self) -> u16 { - 6.max(self.height() as u16 * 3 / 9) - } fn focus_editor (&self) -> bool { self.is_editing() } diff --git a/crates/app/src/view.rs b/crates/app/src/view.rs index 1f617606..2d347a3b 100644 --- a/crates/app/src/view.rs +++ b/crates/app/src/view.rs @@ -19,31 +19,30 @@ impl App { let cache = self.view_cache.read().unwrap(); let theme = self.color; let playing = self.clock().is_rolling(); - Tui::bg(theme.darkest.rgb, Fixed::xy(20, 6, Outer(true, Style::default().fg(Tui::g(96))).enclose( - col!( - Fill::x(Align::w(Bsp::e( - Align::w(Tui::bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) }, - Either::new(false, // TODO - Thunk::new(move||Fixed::x(9, Either::new(playing, - Tui::fg(Rgb(0, 255, 0), " PLAYING "), - Tui::fg(Rgb(255, 128, 0), " STOPPED "))) - ), - Thunk::new(move||Fixed::x(5, Either::new(playing, - Tui::fg(Rgb(0, 255, 0), Bsp::s(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)), - Tui::fg(Rgb(255, 128, 0), Bsp::s(" ▗▄▖ ", " ▝▀▘ ",)))) - ) + Tui::bg(theme.darkest.rgb, Fixed::xy(20, 6, col!( + Fill::x(Align::w(Bsp::e( + Align::w(Tui::bg(if playing { Rgb(0, 128, 0) } else { Rgb(128, 64, 0) }, + Either::new(false, // TODO + Thunk::new(move||Fixed::x(9, Either::new(playing, + Tui::fg(Rgb(0, 255, 0), " PLAYING "), + Tui::fg(Rgb(255, 128, 0), " STOPPED "))) + ), + Thunk::new(move||Fixed::x(5, Either::new(playing, + Tui::fg(Rgb(0, 255, 0), Bsp::s(" 🭍🭑🬽 ", " 🭞🭜🭘 ",)), + Tui::fg(Rgb(255, 128, 0), Bsp::s(" ▗▄▖ ", " ▝▀▘ ",)))) ) - )), - Bsp::s( - FieldH(theme, "Beat", cache.beat.view.clone()), - FieldH(theme, "Time", cache.time.view.clone()), - ), - ))), - Fill::x(Align::w(FieldH(theme, "BPM", cache.bpm.view.clone()))), - Fill::x(Align::w(FieldH(theme, "SR ", cache.sr.view.clone()))), - Fill::x(Align::w(FieldH(theme, "Buf", cache.buf.view.clone()))), - Fill::x(Align::w(FieldH(theme, "Lat", cache.lat.view.clone()))), - )))) + ) + )), + Bsp::s( + FieldH(theme, "Beat", cache.beat.view.clone()), + FieldH(theme, "Time", cache.time.view.clone()), + ), + ))), + Fill::x(Align::w(FieldH(theme, "BPM", cache.bpm.view.clone()))), + Fill::x(Align::w(FieldH(theme, "SR ", cache.sr.view.clone()))), + Fill::x(Align::w(FieldH(theme, "Buf", cache.buf.view.clone()))), + Fill::x(Align::w(FieldH(theme, "Lat", cache.lat.view.clone()))), + ))) } pub fn view_status (&self) -> impl Content + use<'_> { self.update_clock(); @@ -63,43 +62,32 @@ impl App { pub fn view_editor_status (&self) -> impl Content + use<'_> { self.editor().map(|e|Bsp::s(e.clip_status(), e.edit_status())) } - pub fn view_midi_ins_status (&self) -> impl Content + use<'_> { - self.project.get_track().map(|track|{ - let ins = track.sequencer.midi_ins.len() as u16; - Fixed::xy(20, 1 + ins, Outer(true, Style::default().fg(Tui::g(96))).enclose( - Fixed::xy(20, 1 + ins, FieldV(self.color, format!("MIDI ins: "), - Map::south(1, ||track.sequencer.midi_ins.iter(), - |port, index|Fill::x(Align::w(format!(" {index} {}", port.name())))))))) - }) + pub fn view_midi_ports_status (&self) -> impl Content + use<'_> { + self.project.get_track().map(|track|Bsp::s( + Fixed::xy(20, 1 + track.sequencer.midi_ins.len() as u16, FieldV(self.color, + format!("MIDI ins: "), + Map::south(1, ||track.sequencer.midi_ins.iter(), + |port, index|Fill::x(Align::w(format!(" {index} {}", port.name())))))), + Fixed::xy(20, 1 + track.sequencer.midi_outs.len() as u16, FieldV( + self.color, + format!("MIDI outs: "), + Map::south(1, ||track.sequencer.midi_outs.iter(), + |port, index|Fill::x(Align::w(format!(" {index} {}", port.name())))))) + )) } - pub fn view_midi_outs_status (&self) -> impl Content + use<'_> { - self.project.get_track().map(|track|{ - let outs = track.sequencer.midi_outs.len() as u16; - Fixed::xy(20, 1 + outs, Outer(true, Style::default().fg(Tui::g(96))).enclose( - Fixed::xy(20, 1 + outs, FieldV(self.color, format!("MIDI outs: "), - Map::south(1, ||track.sequencer.midi_outs.iter(), - |port, index|Fill::x(Align::w(format!(" {index} {}", port.name())))))))) - }) - } - pub fn view_audio_ins_status (&self) -> impl Content + use<'_> { - self.project.get_track() - .and_then(|track|track.devices.get(0)) - .map(|device|{ - let ins = device.audio_ins().len() as u16; - Fixed::xy(20, 1 + ins, Outer(true, Style::default().fg(Tui::g(96))).enclose( - Fixed::xy(20, 1 + ins, FieldV(self.color, format!("Audio ins: "), - Map::south(1, ||device.audio_ins().iter(), - |port, index|Fill::x(Align::w(format!(" {index} {}", port.name()))))))))}) - } - pub fn view_audio_outs_status (&self) -> impl Content + use<'_> { - self.project.get_track() - .and_then(|track|track.devices.last()) - .map(|device|{ - let outs = device.audio_outs().len() as u16; - Fixed::xy(20, 1 + outs, Outer(true, Style::default().fg(Tui::g(96))).enclose( - Fixed::xy(20, 1 + outs, FieldV(self.color, format!("Audio outs: "), - Map::south(1, ||device.audio_outs().iter(), - |port, index|Fill::x(Align::w(format!(" {index} {}", port.name()))))))))}) + pub fn view_audio_ports_status (&self) -> impl Content + use<'_> { + self.project.get_track().map(|track|Bsp::s( + track.devices.get(0).map(|device| + Fixed::xy(20, 1 + device.audio_ins().len() as u16, FieldV(self.color, + format!("Audio ins: "), + Map::south(1, ||device.audio_ins().iter(), + |port, index|Fill::x(Align::w(format!(" {index} {}", port.name()))))))), + track.devices.last().map(|device| + Fixed::xy(20, 1 + device.audio_outs().len() as u16, FieldV(self.color, + format!("Audio outs:"), + Map::south(1, ||device.audio_outs().iter(), + |port, index|Fill::x(Align::w(format!(" {index} {}", port.name()))))))), + )) } pub fn view_arranger (&self) -> impl Content + use<'_> { ArrangerView::new(&self.project, self.editor.as_ref()) @@ -230,8 +218,8 @@ impl App { pub fn view_pool (&self) -> impl Content + use<'_> { Fixed::x(20, Bsp::s( Fill::x(Align::w(FieldH(self.color, "Clip pool:", ""))), - Fill::y(Align::n(Tui::bg(Rgb(0, 0, 0), Outer(true, Style::default().fg(Tui::g(96))) - .enclose(PoolView(&self.project.pool))))))) + Fill::y(Align::n(Tui::bg(Rgb(0, 0, 0), PoolView(&self.project.pool)))), + )) } pub fn view_samples_keys (&self) -> impl Content + use<'_> { self.project.sampler().map(|s|s.view_list(true, self.editor().unwrap())) diff --git a/crates/device/src/sampler/sampler_api.rs b/crates/device/src/sampler/sampler_api.rs index d7b391f7..ebd62ac7 100644 --- a/crates/device/src/sampler/sampler_api.rs +++ b/crates/device/src/sampler/sampler_api.rs @@ -50,23 +50,21 @@ impl Sampler { #[tengri_proc::command(Sampler)] impl SamplerCommand { - fn record_toggle (sampler: &mut Sampler, slot: usize) -> Perhaps { - let recording = sampler.recording.as_ref().map(|x|x.0); - Self::record_finish(sampler); - // autoslice: continue recording at next slot - if recording != Some(slot) { - Self::record_begin(sampler, slot) + fn record_toggle (sampler: &mut Sampler, sample: usize) -> Perhaps { + if sampler.recording.is_some() { + Self::record_finish(sampler) } else { - Ok(None) + Self::record_begin(sampler, sample) } } - fn record_begin (sampler: &mut Sampler, slot: usize) -> Perhaps { + fn record_begin (sampler: &mut Sampler, pitch: usize) -> Perhaps { sampler.recording = Some(( - slot, + pitch, Arc::new(RwLock::new(Sample::new( "Sample", 0, 0, vec![vec![];sampler.audio_ins.len()] ))) - )); + +)); Ok(None) } fn record_finish (sampler: &mut Sampler) -> Perhaps { @@ -84,39 +82,39 @@ impl SamplerCommand { sampler.recording = None; Ok(None) } - fn play_sample (sampler: &mut Sampler, slot: usize) -> Perhaps { - if let Some(ref sample) = sampler.mapped[slot] { + fn play_sample (sampler: &mut Sampler, pitch: usize) -> Perhaps { + if let Some(ref sample) = sampler.mapped[pitch] { sampler.voices.write().unwrap().push(Sample::play(sample, 0, &u7::from(128))); } Ok(None) } - fn stop_sample (sampler: &mut Sampler, slot: usize) -> Perhaps { + fn stop_sample (sampler: &mut Sampler, pitch: usize) -> Perhaps { todo!(); Ok(None) } //fn select (&self, state: &mut Sampler, i: usize) -> Option { //Self::Select(state.set_note_pos(i)) //} - ///// Assign sample to slot - //fn set (&self, slot: u7, sample: Option>>) -> Option { - //let i = slot.as_int() as usize; + ///// Assign sample to pitch + //fn set (&self, pitch: u7, sample: Option>>) -> Option { + //let i = pitch.as_int() as usize; //let old = self.mapped[i].clone(); //self.mapped[i] = sample; //Some(Self::Set(old)) //} - //fn set_start (&self, state: &mut Sampler, slot: u7, frame: usize) -> Option { + //fn set_start (&self, state: &mut Sampler, pitch: u7, frame: usize) -> Option { //todo!() //} - //fn set_gain (&self, state: &mut Sampler, slot: u7, g: f32) -> Option { + //fn set_gain (&self, state: &mut Sampler, pitch: u7, g: f32) -> Option { //todo!() //} - //fn note_on (&self, state: &mut Sampler, slot: u7, v: u7) -> Option { + //fn note_on (&self, state: &mut Sampler, pitch: u7, v: u7) -> Option { //todo!() //} - //fn note_off (&self, state: &mut Sampler, slot: u7) -> Option { + //fn note_off (&self, state: &mut Sampler, pitch: u7) -> Option { //todo!() //} - //fn set_sample (&self, state: &mut Sampler, slot: u7, s: Option>>) -> Option { + //fn set_sample (&self, state: &mut Sampler, pitch: u7, s: Option>>) -> Option { //Some(Self::SetSample(p, state.set_sample(p, s))) //} //fn import (&self, state: &mut Sampler, c: FileBrowserCommand) -> Option { @@ -171,9 +169,9 @@ impl SamplerCommand { ////("set/gain" [i: u7, g: f32] ////Some(Self::SetGain(i.expect("no index"), g.expect("no gain")))) ////("note/on" [p: u7, v: u7] - ////Some(Self::NoteOn(p.expect("no slot"), v.expect("no velocity")))) + ////Some(Self::NoteOn(p.expect("no pitch"), v.expect("no velocity")))) ////("note/off" [p: u7] - ////Some(Self::NoteOff(p.expect("no slot")))))); + ////Some(Self::NoteOff(p.expect("no pitch")))))); } #[tengri_proc::command(Sampler)] diff --git a/crates/device/src/sampler/sampler_view.rs b/crates/device/src/sampler/sampler_view.rs index bfb344ff..7b21233d 100644 --- a/crates/device/src/sampler/sampler_view.rs +++ b/crates/device/src/sampler/sampler_view.rs @@ -119,13 +119,13 @@ impl Sampler { } pub fn view_sample_status (&self, note_pt: usize) -> impl Content + use<'_> { - Fixed::x(20, Outer(true, Style::default().fg(Tui::g(96))).enclose(draw_info_v(if let Some((_, sample)) = &self.recording { + Fixed::x(20, draw_info_v(if let Some((_, sample)) = &self.recording { Some(sample) } else if let Some(sample) = &self.mapped[note_pt] { Some(sample) } else { None - }))) + })) } pub fn view_status (&self, index: usize) -> impl Content {