diff --git a/config/config_groovebox.edn b/config/config_groovebox.edn index 3f63ce1d..24154b74 100644 --- a/config/config_groovebox.edn +++ b/config/config_groovebox.edn @@ -3,16 +3,15 @@ (info "A sequencer with built-in sampler.") (view - (bsp/a :view-dialog - (bsp/s (fixed/y 1 :view-transport) - (bsp/n (fixed/y 1 :view-status) - (bsp/w :view-meters-output - (bsp/e :view-meters-input - (bsp/n :view-sample-info - (bsp/n (fixed/y 5 :view-sample-viewer) - (bsp/w (fixed/x :w-sidebar :view-pool) - (bsp/e :view-samples-keys - (fill/y :view-editor))))))))))) + (bsp/a :view-dialog + (bsp/s (fixed/y 1 :view-transport) + (bsp/n (fixed/y 1 :view-status) + (bsp/w :view-meters-output + (bsp/e :view-meters-input + (bsp/n (fixed/y 5 :view-sample-viewer) + (bsp/w (fixed/x :w-sidebar :view-pool) + (bsp/e :view-samples-keys + (fill/y :view-editor)))))))))) (keys (layer-if :focus-pool-import "./keys_pool_file.edn") diff --git a/config/keys_editor.edn b/config/keys_editor.edn index 64523ac7..09c225f6 100644 --- a/config/keys_editor.edn +++ b/config/keys_editor.edn @@ -1,24 +1,25 @@ -(@left editor set-time-pos :time-pos-prev) -(@right editor set-time-pos :time-pos-next) +(@up editor note-pos :note-pos-next) +(@down editor note-pos :note-pos-prev) +(@pgup editor note-pos :note-pos-next-octave) +(@pgdn editor note-pos :note-pos-prev-octave) -(@equal editor set-time-zoom :time-zoom-prev) -(@minus editor set-time-zoom :time-zoom-next) -(@plus editor set-time-zoom :time-zoom-next-fine) -(@underscore editor set-time-zoom :time-zoom-prev-fine) +(@comma editor note-len :note-len-prev) +(@period editor note-len :note-len-next) +(@lt editor note-len :note-len-prev) +(@gt editor note-len :note-len-next) -(@z editor set-time-lock) +(@plus editor note-range :note-range-next) +(@underscore editor note-range :note-range-prev) -(@up editor set-note-pos :note-pos-next) -(@down editor set-note-pos :note-pos-prev) -(@pgup editor set-note-pos :note-pos-next-octave) -(@pgdn editor set-note-pos :note-pos-prev-octave) +(@left editor time-pos :time-pos-prev) +(@right editor time-pos :time-pos-next) -(@comma editor set-note-len :note-len-prev) -(@period editor set-note-len :note-len-next) -(@lt editor set-note-len :note-len-prev) -(@gt editor set-note-len :note-len-next) +(@equal editor time-zoom :time-zoom-prev) +(@minus editor time-zoom :time-zoom-next) -(@a editor append-note :true) -(@enter editor append-note :false) -(@del editor delete-note) -(@shift-del editor delete-note) +(@z editor time-lock) + +(@enter editor note-put) +(@shift-enter editor note-append) +(@del editor note-del) +(@shift-del editor note-del) diff --git a/config/keys_global.edn b/config/keys_global.edn index bcb16252..cd9fc7e6 100644 --- a/config/keys_global.edn +++ b/config/keys_global.edn @@ -1,5 +1,5 @@ -(@esc toggle-menu) -(@f1 toggle-help) +(@esc menu) +(@f1 help) (@u undo 1) (@shift-u redo 1) diff --git a/crates/app/src/api.rs b/crates/app/src/api.rs index 3b2acbb6..ce88fe99 100644 --- a/crates/app/src/api.rs +++ b/crates/app/src/api.rs @@ -203,11 +203,11 @@ handle!(TuiIn: |self: App, input|Ok(if let Some(command) = self.config.keys.comm } #[tengri_proc::command(App)] impl AppCommand { - fn toggle_help (app: &mut App) -> Perhaps { + fn toggle_help (app: &mut App, value: bool) -> Perhaps { app.toggle_dialog(Some(Dialog::Help)); Ok(None) } - fn toggle_menu (app: &mut App) -> Perhaps { + fn toggle_menu (app: &mut App, value: bool) -> Perhaps { app.toggle_dialog(Some(Dialog::Menu)); Ok(None) } @@ -280,7 +280,7 @@ handle!(TuiIn: |self: App, input|Ok(if let Some(command) = self.config.keys.comm // update linked sampler after editor action app.sampler_mut().map(|sampler|match command { // autoselect: automatically select sample in sampler - MidiEditCommand::SetNotePos { pos } => { sampler.set_note_pos(pos); }, + MidiEditCommand::NotePos { pos } => { sampler.set_note_pos(pos); }, _ => {} }); undo diff --git a/crates/app/src/view.rs b/crates/app/src/view.rs index 21743eb7..4a2c7859 100644 --- a/crates/app/src/view.rs +++ b/crates/app/src/view.rs @@ -41,15 +41,6 @@ impl App { pub fn view_sample_viewer (&self) -> impl Content + use<'_> { self.sampler().map(|s|s.view_sample(self.editor().unwrap().get_note_pos())) } - pub fn view_sample_info (&self) -> impl Content + use<'_> { - self.sampler().map(|s|s.view_sample_info(self.editor().unwrap().get_note_pos())) - } - pub fn view_meters_input (&self) -> impl Content + use<'_> { - self.sampler().map(|s|s.view_meters_input()) - } - pub fn view_meters_output (&self) -> impl Content + use<'_> { - self.sampler().map(|s|s.view_meters_output()) - } pub fn view_dialog (&self) -> impl Content + use<'_> { When::new(self.dialog.is_some(), Bsp::b( Fill::xy(Tui::fg_bg(Rgb(64,64,64), Rgb(32,32,32), "")), @@ -65,6 +56,12 @@ impl App { ))) )) } + pub fn view_meters_input (&self) -> impl Content + use<'_> { + self.sampler().map(|s|s.view_meters_input()) + } + pub fn view_meters_output (&self) -> impl Content + use<'_> { + self.sampler().map(|s|s.view_meters_output()) + } } impl App { diff --git a/crates/cli/tek.rs b/crates/cli/tek.rs index 1e303f23..b65a8abf 100644 --- a/crates/cli/tek.rs +++ b/crates/cli/tek.rs @@ -111,10 +111,10 @@ impl Cli { _ => todo!("{mode:?}"), }; let config = Configuration::new(&config_path, false)?; - let clock = Clock::new(jack, self.bpm)?; let mut app = App { jack: jack.clone(), color: ItemTheme::random(), + clock: Clock::new(jack, self.bpm)?, pool: match mode { LaunchMode::Sequencer | LaunchMode::Groovebox => clip.as_ref().map(Into::into), LaunchMode::Arranger { .. } => Some(Default::default()), @@ -141,7 +141,7 @@ impl Cli { &name, None, jack, - Some(&clock), + None, clip.as_ref(), midi_froms.as_slice(), midi_tos.as_slice() @@ -152,7 +152,7 @@ impl Cli { &name, None, jack, - Some(&clock), + None, clip.as_ref(), midi_froms.as_slice(), midi_froms.as_slice(), @@ -165,7 +165,6 @@ impl Cli { scenes, selected: Selection::TrackClip { track: 0, scene: 0 }, config, - clock, ..Default::default() }; if let &LaunchMode::Arranger { scenes, tracks, track_width, .. } = mode { diff --git a/crates/device/src/editor/editor_api.rs b/crates/device/src/editor/editor_api.rs index 85748cae..a1c6a6e5 100644 --- a/crates/device/src/editor/editor_api.rs +++ b/crates/device/src/editor/editor_api.rs @@ -4,6 +4,12 @@ use crate::*; fn _todo_opt_clip_stub (&self) -> Option>> { todo!() } + fn time_lock (&self) -> bool { + self.get_time_lock() + } + fn time_lock_toggled (&self) -> bool { + !self.get_time_lock() + } fn note_length (&self) -> usize { self.get_note_len() @@ -49,10 +55,10 @@ use crate::*; self.get_time_pos() } fn time_pos_next (&self) -> usize { - self.get_time_pos() + self.get_note_len() + self.get_time_pos() + self.time_zoom() } fn time_pos_prev (&self) -> usize { - self.get_time_pos().saturating_sub(self.get_note_len()) + self.get_time_pos().saturating_sub(self.time_zoom()) } fn time_zoom (&self) -> usize { @@ -61,37 +67,29 @@ use crate::*; fn time_zoom_next (&self) -> usize { self.get_time_zoom() + 1 } - fn time_zoom_next_fine (&self) -> usize { - self.get_time_zoom() + 1 - } fn time_zoom_prev (&self) -> usize { self.get_time_zoom().saturating_sub(1).max(1) } - fn time_zoom_prev_fine (&self) -> usize { - self.get_time_zoom().saturating_sub(1).max(1) - } - - fn time_lock (&self) -> bool { - self.get_time_lock() - } - fn time_lock_toggled (&self) -> bool { - !self.get_time_lock() - } } #[tengri_proc::command(MidiEditor)] impl MidiEditCommand { - fn append_note (editor: &mut MidiEditor, advance: bool) -> Perhaps { - editor.put_note(advance); + // TODO: 1-9 seek markers that by default start every 8th of the clip + fn note_append (editor: &mut MidiEditor) -> Perhaps { + editor.put_note(true); Ok(None) } - fn delete_note (_editor: &mut MidiEditor) -> Perhaps { + fn note_put (editor: &mut MidiEditor) -> Perhaps { + editor.put_note(false); + Ok(None) + } + fn note_del (_editor: &mut MidiEditor) -> Perhaps { todo!() } - fn set_note_pos (editor: &mut MidiEditor, pos: usize) -> Perhaps { + fn note_pos (editor: &mut MidiEditor, pos: usize) -> Perhaps { editor.set_note_pos(pos.min(127)); Ok(None) } - fn set_note_len (editor: &mut MidiEditor, value: usize) -> Perhaps { + fn note_len (editor: &mut MidiEditor, value: usize) -> Perhaps { //let note_len = editor.get_note_len(); //let time_zoom = editor.get_time_zoom(); editor.set_note_len(value); @@ -100,24 +98,24 @@ use crate::*; //} Ok(None) } - fn set_note_scroll (editor: &mut MidiEditor, value: usize) -> Perhaps { + fn note_scroll (editor: &mut MidiEditor, value: usize) -> Perhaps { editor.set_note_lo(value.min(127)); Ok(None) } - fn set_time_pos (editor: &mut MidiEditor, value: usize) -> Perhaps { + fn time_pos (editor: &mut MidiEditor, value: usize) -> Perhaps { editor.set_time_pos(value); Ok(None) } - fn set_time_scroll (editor: &mut MidiEditor, value: usize) -> Perhaps { + fn time_scroll (editor: &mut MidiEditor, value: usize) -> Perhaps { editor.set_time_start(value); Ok(None) } - fn set_time_zoom (editor: &mut MidiEditor, value: usize) -> Perhaps { + fn time_zoom (editor: &mut MidiEditor, value: usize) -> Perhaps { editor.set_time_zoom(value); editor.redraw(); Ok(None) } - fn set_time_lock (editor: &mut MidiEditor, value: bool) -> Perhaps { + fn time_lock (editor: &mut MidiEditor, value: bool) -> Perhaps { editor.set_time_lock(value); Ok(None) } @@ -125,5 +123,4 @@ use crate::*; editor.set_clip(clip.as_ref()); Ok(None) } - // TODO: 1-9 seek markers that by default start every 8th of the clip } diff --git a/crates/device/src/editor/editor_model.rs b/crates/device/src/editor/editor_model.rs index 90d6d2ee..a92e96ba 100644 --- a/crates/device/src/editor/editor_model.rs +++ b/crates/device/src/editor/editor_model.rs @@ -61,7 +61,7 @@ impl MidiEditor { clip.notes[note_end].push(note_off); } if advance { - self.set_time_pos(note_end + 1); + self.set_time_pos(note_end); } redraw = true; } diff --git a/crates/device/src/sampler/sampler_model.rs b/crates/device/src/sampler/sampler_model.rs index 1d9ec18e..ad429a9d 100644 --- a/crates/device/src/sampler/sampler_model.rs +++ b/crates/device/src/sampler/sampler_model.rs @@ -4,7 +4,7 @@ use crate::*; #[derive(Debug)] pub struct Sampler { /// Name of sampler. - pub name: Arc, + pub name: String, /// Device color. pub color: ItemTheme, /// Audio input ports. Samples get recorded here. @@ -55,7 +55,7 @@ impl Default for Sampler { input_meters: vec![0.0;2], output_meters: vec![0.0;2], audio_outs: vec![], - name: "tek_sampler".into(), + name: "tek_sampler".to_string(), mapped: [const { None };128], unmapped: vec![], voices: Arc::new(RwLock::new(vec![])), diff --git a/crates/device/src/sampler/sampler_view.rs b/crates/device/src/sampler/sampler_view.rs index ee67807b..b8fb9b20 100644 --- a/crates/device/src/sampler/sampler_view.rs +++ b/crates/device/src/sampler/sampler_view.rs @@ -100,46 +100,33 @@ impl Sampler { }))) } - pub fn view_sample_info (&self, note_pt: usize) -> impl Content + use<'_> { - Fill::x(Fixed::y(1, draw_info(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 { + pub fn status (&self, index: usize) -> impl Content { draw_status(self.mapped[index].as_ref()) } pub fn view_meters_input (&self) -> impl Content + use<'_> { - draw_meters(&self.input_meters) + Tui::bg(Black, Fixed::x(2, Map::east(1, ||self.input_meters.iter(), |value, _index|{ + Fill::y(RmsMeter(*value)) + }))) } pub fn view_meters_output (&self) -> impl Content + use<'_> { - draw_meters(&self.output_meters) + Tui::bg(Black, Fixed::x(2, Map::east(1, ||self.output_meters.iter(), |value, _index|{ + Fill::y(RmsMeter(*value)) + }))) } } -fn draw_meters (meters: &[f32]) -> impl Content + use<'_> { - Tui::bg(Black, Fixed::x(2, Map::east(1, ||meters.iter(), |value, _index|{ - Fill::y(RmsMeter(*value)) - }))) -} - fn draw_list_item (sample: &Option>>) -> String { if let Some(sample) = sample { let sample = sample.read().unwrap(); - format!("{:8}", sample.name) - //format!("{:8} {:3} {:6}-{:6}/{:6}", - //sample.name, - //sample.gain, - //sample.start, - //sample.end, - //sample.channels[0].len() - //) + format!("{:8} {:3} {:6}-{:6}/{:6}", + sample.name, + sample.gain, + sample.start, + sample.end, + sample.channels[0].len() + ) } else { String::from("........") } @@ -200,21 +187,6 @@ fn draw_viewer (sample: Option<&Arc>>) -> impl Content + }) } -fn draw_info (sample: Option<&Arc>>) -> impl Content + use<'_> { - When(sample.is_some(), Thunk::new(move||{ - let sample = sample.unwrap().read().unwrap(); - let theme = ItemTheme::G[96]; - row!( - FieldH(theme, "Name", format!("{:<10}", sample.name.clone())), - FieldH(theme, "Length", format!("{:<8}", sample.channels[0].len())), - FieldH(theme, "Start", format!("{:<8}", sample.start)), - FieldH(theme, "End", format!("{:<8}", sample.end)), - FieldH(theme, "Transpose", " 0 "), - FieldH(theme, "Gain", format!("{}", sample.gain)), - ) - })) -} - fn draw_status (sample: Option<&Arc>>) -> impl Content { Tui::bold(true, Tui::fg(Tui::g(224), sample .map(|sample|{ diff --git a/crates/device/src/sequencer/seq_model.rs b/crates/device/src/sequencer/seq_model.rs index 298364ac..60a57b26 100644 --- a/crates/device/src/sequencer/seq_model.rs +++ b/crates/device/src/sequencer/seq_model.rs @@ -18,29 +18,29 @@ pub trait HasSequencer { /// Contains state for playing a clip pub struct Sequencer { /// State of clock and playhead - pub clock: Clock, + pub clock: Clock, /// Start time and clip being played - pub play_clip: Option<(Moment, Option>>)>, + pub play_clip: Option<(Moment, Option>>)>, /// Start time and next clip - pub next_clip: Option<(Moment, Option>>)>, + pub next_clip: Option<(Moment, Option>>)>, /// Play input through output. - pub monitoring: bool, + pub monitoring: bool, /// Write input to sequence. - pub recording: bool, + pub recording: bool, /// Overdub input to sequence. - pub overdub: bool, + pub overdub: bool, /// Send all notes off - pub reset: bool, // TODO?: after Some(nframes) + pub reset: bool, // TODO?: after Some(nframes) /// Record from MIDI ports to current sequence. - pub midi_ins: Vec, + pub midi_ins: Vec, /// Play from current sequence to MIDI ports - pub midi_outs: Vec, + pub midi_outs: Vec, /// Notes currently held at input - pub notes_in: Arc>, + pub notes_in: Arc>, /// Notes currently held at output - pub notes_out: Arc>, + pub notes_out: Arc>, /// MIDI output buffer - pub note_buf: Vec, + pub note_buf: Vec, } impl Default for Sequencer {