diff --git a/crates/app/src/api.rs b/crates/app/src/api.rs index 157fd843..7d5d0851 100644 --- a/crates/app/src/api.rs +++ b/crates/app/src/api.rs @@ -53,12 +53,62 @@ impl Tek { matches!(self.pool.as_ref().map(|p|p.mode.as_ref()).flatten(), Some(PoolMode::Length(..))) } fn editor_pitch (&self) -> Option { - (self.editor().map(|e|e.note_pos()).unwrap() as u8).into() + Some((self.editor().map(|e|e.note_pos()).unwrap() as u8).into()) } - fn w_sidebar (&self) -> Option { - self.w_sidebar() + /// Width of display + pub(crate) fn w (&self) -> u16 { + self.size.w() as u16 } - fn scene_count (&self) -> Option { + /// Width allocated for sidebar. + pub(crate) fn w_sidebar (&self) -> u16 { + self.w() / if self.is_editing() { 16 } else { 8 } as u16 + } + /// Width taken by all tracks. + pub(crate) fn w_tracks (&self) -> u16 { + self.tracks_with_sizes().last().map(|(_, _, _, x)|x as u16).unwrap_or(0) + } + /// Width available to display tracks. + pub(crate) fn w_tracks_area (&self) -> u16 { + self.w().saturating_sub(2 * self.w_sidebar()) + } + /// Height of display + pub(crate) fn h (&self) -> u16 { + self.size.h() as u16 + } + /// Height available to display track headers. + pub(crate) fn h_tracks_area (&self) -> u16 { + 5 // FIXME + //self.h().saturating_sub(self.h_inputs() + self.h_outputs()) + } + /// Height available to display tracks. + pub(crate) fn h_scenes_area (&self) -> u16 { + //15 + self.h().saturating_sub( + self.h_inputs() + + self.h_outputs() + + self.h_devices() + + 13 // FIXME + ) + } + /// Height taken by all scenes. + pub(crate) fn h_scenes (&self) -> u16 { + self.scenes_with_sizes(self.is_editing(), Self::H_SCENE, Self::H_EDITOR).last() + .map(|(_, _, _, y)|y as u16).unwrap_or(0) + } + /// Height taken by all inputs. + pub(crate) fn h_inputs (&self) -> u16 { + self.inputs_with_sizes().last().map(|(_, _, _, _, y)|y as u16).unwrap_or(0) + } + /// Height taken by all outputs. + pub(crate) fn h_outputs (&self) -> u16 { + self.outputs_with_sizes().last().map(|(_, _, _, _, y)|y as u16).unwrap_or(0) + } + /// Height taken by visible device slots. + pub(crate) fn h_devices (&self) -> u16 { + 2 + //1 + self.devices_with_sizes().last().map(|(_, _, _, _, y)|y as u16).unwrap_or(0) + } + fn scene_count (&self) -> usize { self.scenes.len() } fn scene_selected (&self) -> Option { @@ -70,7 +120,7 @@ impl Tek { fn scene_select_prev (&self) -> Selection { self.selected.scene_prev() } - fn track_count (&self) -> Option { + fn track_count (&self) -> usize { self.tracks.len() } fn track_selected (&self) -> Option { @@ -139,64 +189,70 @@ impl MidiPool { #[tengri_proc::expose] impl MidiEditor { fn time_lock (&self) -> bool { - self.time_lock().get() + self.get_time_lock() } - fn time_lock_toggle (&self) -> bool { - !self.time_lock().get() + fn time_lock_toggled (&self) -> bool { + !self.get_time_lock() } + fn note_length (&self) -> usize { - self.note_len() + self.get_note_len() } + fn note_pos (&self) -> usize { - self.note_pos() + self.get_note_pos() } fn note_pos_next (&self) -> usize { - self.note_pos() + 1 + self.get_note_pos() + 1 } fn note_pos_next_octave (&self) -> usize { - self.note_pos() + 12 + self.get_note_pos() + 12 } fn note_pos_prev (&self) -> usize { - self.note_pos().saturating_sub(1) + self.get_note_pos().saturating_sub(1) } fn note_pos_prev_octave (&self) -> usize { - self.note_pos().saturating_sub(12) + self.get_note_pos().saturating_sub(12) } + fn note_len (&self) -> usize { - self.note_len() + self.get_note_len() } fn note_len_next (&self) -> usize { - self.note_len() + 1 + self.get_note_len() + 1 } fn note_len_prev (&self) -> usize { - self.note_len().saturating_sub(1) + self.get_note_len().saturating_sub(1) } + fn note_range (&self) -> usize { - self.note_axis() + self.get_note_axis() } fn note_range_next (&self) -> usize { - self.note_axis() + 1 + self.get_note_axis() + 1 } fn note_range_prev (&self) -> usize { - self.note_axis().saturating_sub(1) + self.get_note_axis().saturating_sub(1) } + fn time_pos (&self) -> usize { - self.time_pos() + self.get_time_pos() } fn time_pos_next (&self) -> usize { - self.time_pos() + self.time_zoom().get() + self.get_time_pos() + self.time_zoom() } fn time_pos_prev (&self) -> usize { - self.time_pos().saturating_sub(self.time_zoom().get()) + self.get_time_pos().saturating_sub(self.time_zoom()) } + fn time_zoom (&self) -> usize { - self.time_zoom() + self.get_time_zoom() } fn time_zoom_next (&self) -> usize { - self.time_zoom() + 1 + self.get_time_zoom() + 1 } fn time_zoom_prev (&self) -> usize { - self.time_zoom().get().saturating_sub(1).max(1) + self.get_time_zoom().saturating_sub(1).max(1) } } @@ -277,17 +333,17 @@ impl TekCommand { #[tengri_proc::command(Tek)] impl InputCommand { - fn add (&self, state: &mut tek) -> option { + fn add (&self, state: &mut Tek) -> Option { state.midi_in_add()?; - none + None } } #[tengri_proc::command(Tek)] impl OutputCommand { - fn add (&self, state: &mut tek) -> option { + fn add (&self, state: &mut Tek) -> Option { state.midi_out_add()?; - none + None } } diff --git a/crates/app/src/model/editor.rs b/crates/app/src/model/editor.rs index 355b0e8c..9a9d5836 100644 --- a/crates/app/src/model/editor.rs +++ b/crates/app/src/model/editor.rs @@ -93,12 +93,13 @@ impl MidiEditor { let (color, length) = if let Some(clip) = self.clip().as_ref().map(|p|p.read().unwrap()) { (clip.color, clip.length) } else { (ItemTheme::G[64], 0) }; - let time_pos = self.time_pos(); - let time_zoom = self.time_zoom().get(); - let time_lock = if self.time_lock().get() { "[lock]" } else { " " }; - let note_pos = format!("{:>3}", self.note_pos()); - let note_name = format!("{:4}", Note::pitch_to_name(self.note_pos())); - let note_len = format!("{:>4}", self.note_len()); + let time_pos = self.get_time_pos(); + let time_zoom = self.get_time_zoom(); + let time_lock = if self.get_time_lock() { "[lock]" } else { " " }; + let note_pos = self.get_note_pos(); + let note_name = format!("{:4}", Note::pitch_to_name(note_pos)); + let note_pos = format!("{:>3}", note_pos); + let note_len = format!("{:>4}", self.get_note_len()); Bsp::e( FieldH(color, "Time", format!("{length}/{time_zoom}+{time_pos} {time_lock}")), FieldH(color, "Note", format!("{note_name} {note_pos} {note_len}")), @@ -120,15 +121,12 @@ impl NoteRange for MidiEditor { } impl NotePoint for MidiEditor { - fn note_len (&self) -> usize { self.mode.note_len() } - fn set_note_len (&self, x: usize) -> usize { self.mode.set_note_len(x) } - fn note_pos (&self) -> usize { self.mode.note_pos() } - fn set_note_pos (&self, x: usize) -> usize { self.mode.set_note_pos(x) } + fn note_len (&self) -> &AtomicUsize { self.mode.note_len() } + fn note_pos (&self) -> &AtomicUsize { self.mode.note_pos() } } impl TimePoint for MidiEditor { - fn time_pos (&self) -> usize { self.mode.time_pos() } - fn set_time_pos (&self, x: usize) -> usize { self.mode.set_time_pos(x) } + fn time_pos (&self) -> &AtomicUsize { self.mode.time_pos() } } impl MidiViewer for MidiEditor { diff --git a/crates/app/src/view.rs b/crates/app/src/view.rs index 8b251e7e..d1377bee 100644 --- a/crates/app/src/view.rs +++ b/crates/app/src/view.rs @@ -39,7 +39,7 @@ impl Tek { self.sampler().map(|s|s.view_grid()) } fn view_sample_viewer (&self) -> impl Content + use<'_> { - self.sampler().map(|s|s.view_sample(self.editor().unwrap().note_pos())) + self.sampler().map(|s|s.view_sample(self.editor().unwrap().get_note_pos())) } fn view_dialog (&self) -> impl Content + use<'_> { When::new(self.dialog.is_some(), Bsp::b( @@ -115,69 +115,6 @@ impl Tek { /// Default editor height. pub(crate) const H_EDITOR: usize = 15; - /// Width of display - pub(crate) fn w (&self) -> u16 { - self.size.w() as u16 - } - - pub(crate) fn w_sidebar (&self) -> u16 { - self.w() / if self.is_editing() { 16 } else { 8 } as u16 - } - - /// Width taken by all tracks. - pub(crate) fn w_tracks (&self) -> u16 { - self.tracks_with_sizes().last().map(|(_, _, _, x)|x as u16).unwrap_or(0) - } - - /// Width available to display tracks. - pub(crate) fn w_tracks_area (&self) -> u16 { - self.w().saturating_sub(2 * self.w_sidebar()) - } - - /// Height of display - pub(crate) fn h (&self) -> u16 { - self.size.h() as u16 - } - - /// Height available to display track headers. - pub(crate) fn h_tracks_area (&self) -> u16 { - 5 // FIXME - //self.h().saturating_sub(self.h_inputs() + self.h_outputs()) - } - - /// Height available to display tracks. - pub(crate) fn h_scenes_area (&self) -> u16 { - //15 - self.h().saturating_sub( - self.h_inputs() + - self.h_outputs() + - self.h_devices() + - 13 // FIXME - ) - } - - /// Height taken by all inputs. - pub(crate) fn h_inputs (&self) -> u16 { - self.inputs_with_sizes().last().map(|(_, _, _, _, y)|y as u16).unwrap_or(0) - } - - /// Height taken by all outputs. - pub(crate) fn h_outputs (&self) -> u16 { - self.outputs_with_sizes().last().map(|(_, _, _, _, y)|y as u16).unwrap_or(0) - } - - /// Height taken by visible device slots. - pub(crate) fn h_devices (&self) -> u16 { - 2 - //1 + self.devices_with_sizes().last().map(|(_, _, _, _, y)|y as u16).unwrap_or(0) - } - - /// Height taken by all scenes. - pub(crate) fn h_scenes (&self) -> u16 { - self.scenes_with_sizes(self.is_editing(), Self::H_SCENE, Self::H_EDITOR).last() - .map(|(_, _, _, y)|y as u16).unwrap_or(0) - } - pub(crate) fn inputs_with_sizes (&self) -> impl PortsSizes<'_> { let mut y = 0; self.midi_ins.iter().enumerate().map(move|(i, input)|{ @@ -1118,9 +1055,9 @@ impl PianoHorizontal { } } fn notes (&self) -> impl Content { - let time_start = self.time_start().get(); - let note_lo = self.note_lo().get(); - let note_hi = self.note_hi(); + let time_start = self.get_time_start(); + let note_lo = self.get_note_lo(); + let note_hi = self.get_note_hi(); let buffer = self.buffer.clone(); ThunkRender::new(move|to: &mut TuiOut|{ let source = buffer.read().unwrap(); @@ -1149,14 +1086,14 @@ impl PianoHorizontal { }) } fn cursor (&self) -> impl Content { - let style = Some(Style::default().fg(self.color.lightest.rgb)); - let note_hi = self.note_hi(); - let note_lo = self.note_lo().get(); - let note_pos = self.note_pos(); - let note_len = self.note_len(); - let time_pos = self.time_pos(); - let time_start = self.time_start().get(); - let time_zoom = self.time_zoom().get(); + let note_hi = self.get_note_hi(); + let note_lo = self.get_note_lo(); + let note_pos = self.get_note_pos(); + let note_len = self.get_note_len(); + let time_pos = self.get_time_pos(); + let time_start = self.get_time_start(); + let time_zoom = self.get_time_zoom(); + let style = Some(Style::default().fg(self.color.lightest.rgb)); ThunkRender::new(move|to: &mut TuiOut|{ let [x0, y0, w, _] = to.area().xywh(); for (_area_y, screen_y, note) in note_y_iter(note_lo, note_hi, y0) { @@ -1182,9 +1119,9 @@ impl PianoHorizontal { fn keys (&self) -> impl Content { let state = self; let color = state.color; - let note_lo = state.note_lo().get(); - let note_hi = state.note_hi(); - let note_pos = state.note_pos(); + let note_lo = state.get_note_lo(); + let note_hi = state.get_note_hi(); + let note_pos = state.get_note_pos(); let key_style = Some(Style::default().fg(Rgb(192, 192, 192)).bg(Rgb(0, 0, 0))); let off_style = Some(Style::default().fg(Tui::g(255))); let on_style = Some(Style::default().fg(Rgb(255,0,0)).bg(color.base.rgb).bold()); @@ -1234,15 +1171,12 @@ impl NoteRange for PianoHorizontal { } impl NotePoint for PianoHorizontal { - fn note_len (&self) -> usize { self.point.note_len() } - fn set_note_len (&self, x: usize) -> usize { self.point.set_note_len(x) } - fn note_pos (&self) -> usize { self.point.note_pos() } - fn set_note_pos (&self, x: usize) -> usize { self.point.set_note_pos(x) } + fn note_len (&self) -> &AtomicUsize { self.point.note_len() } + fn note_pos (&self) -> &AtomicUsize { self.point.note_pos() } } impl TimePoint for PianoHorizontal { - fn time_pos (&self) -> usize { self.point.time_pos() } - fn set_time_pos (&self, x: usize) -> usize { self.point.set_time_pos(x) } + fn time_pos (&self) -> &AtomicUsize { self.point.time_pos() } } impl MidiViewer for PianoHorizontal { diff --git a/crates/device/src/sampler/sampler_model.rs b/crates/device/src/sampler/sampler_model.rs index 3c0d1c4c..2a55e143 100644 --- a/crates/device/src/sampler/sampler_model.rs +++ b/crates/device/src/sampler/sampler_model.rs @@ -93,13 +93,19 @@ impl NoteRange for Sampler { } impl NotePoint for Sampler { - fn note_len (&self) -> usize { - 0 /*TODO?*/ + fn note_len (&self) -> &AtomicUsize { + unreachable!(); + } + fn get_note_len (&self) -> usize { + 0 } fn set_note_len (&self, x: usize) -> usize { 0 /*TODO?*/ } - fn note_pos (&self) -> usize { + fn note_pos (&self) -> &AtomicUsize { + &self.note_pt + } + fn get_note_pos (&self) -> usize { self.note_pt.load(Relaxed) } fn set_note_pos (&self, x: usize) -> usize { diff --git a/crates/device/src/sampler/sampler_view.rs b/crates/device/src/sampler/sampler_view.rs index 15875aa1..2b5c3fad 100644 --- a/crates/device/src/sampler/sampler_view.rs +++ b/crates/device/src/sampler/sampler_view.rs @@ -56,9 +56,9 @@ impl Sampler { pub fn view_list <'a, T: NotePoint + NoteRange> ( &'a self, compact: bool, editor: &T ) -> impl Content + 'a { - let note_lo = editor.note_lo().load(Relaxed); - let note_pt = editor.note_pos(); - let note_hi = editor.note_hi(); + let note_lo = editor.get_note_lo(); + let note_pt = editor.get_note_pos(); + let note_hi = editor.get_note_hi(); Fixed::x(12, Map::south( 1, move||(note_lo..=note_hi).rev(), diff --git a/crates/device/src/sequencer/seq_view.rs b/crates/device/src/sequencer/seq_view.rs index ae6440b3..5635363e 100644 --- a/crates/device/src/sequencer/seq_view.rs +++ b/crates/device/src/sequencer/seq_view.rs @@ -11,9 +11,9 @@ pub trait MidiViewer: HasSize + MidiRange + MidiPoint + Debug + Send + S } /// Make sure cursor is within note range fn autoscroll (&self) { - let note_pos = self.note_pos().min(127); - let note_lo = self.note_lo().get(); - let note_hi = self.note_hi(); + let note_pos = self.get_note_pos().min(127); + let note_lo = self.get_note_lo(); + let note_hi = self.get_note_hi(); if note_pos < note_lo { self.note_lo().set(note_pos); } else if note_pos > note_hi { @@ -23,9 +23,9 @@ pub trait MidiViewer: HasSize + MidiRange + MidiPoint + Debug + Send + S /// Make sure time range is within display fn autozoom (&self) { if self.time_lock().get() { - let time_len = self.time_len().get(); - let time_axis = self.time_axis().get(); - let time_zoom = self.time_zoom().get(); + let time_len = self.get_time_len(); + let time_axis = self.get_time_axis(); + let time_zoom = self.get_time_zoom(); loop { let time_zoom = self.time_zoom().get(); let time_area = time_axis * time_zoom; diff --git a/crates/engine/src/note/note_point.rs b/crates/engine/src/note/note_point.rs index 8de6ca51..310cef87 100644 --- a/crates/engine/src/note/note_point.rs +++ b/crates/engine/src/note/note_point.rs @@ -20,53 +20,60 @@ impl Default for MidiPointModel { } } -pub trait NotePoint { - /// Get the current length of the note cursor. - fn note_len (&self) -> usize; - /// Set the length of the note cursor, returning the previous value. - fn set_note_len (&self, x: usize) -> usize; - /// Get the current pitch of the note cursor. - fn note_pos (&self) -> usize; - /// Set the current pitch fo the note cursor, returning the previous value. - fn set_note_pos (&self, x: usize) -> usize; -} - -pub trait TimePoint { - /// Get the current time position of the note cursor. - fn time_pos (&self) -> usize; - /// Set the current time position of the note cursor, returning the previous value. - fn set_time_pos (&self, x: usize) -> usize; -} - -pub trait MidiPoint: NotePoint + TimePoint { - /// Get the current end of the note cursor. - fn note_end (&self) -> usize { - self.time_pos() + self.note_len() - } -} - -impl MidiPoint for T {} - impl NotePoint for MidiPointModel { - fn note_len (&self) -> usize { - self.note_len.load(Relaxed) + fn note_len (&self) -> &AtomicUsize { + &self.note_len } - fn set_note_len (&self, x: usize) -> usize { - self.note_len.swap(x, Relaxed) - } - fn note_pos (&self) -> usize { - self.note_pos.load(Relaxed).min(127) - } - fn set_note_pos (&self, x: usize) -> usize { - self.note_pos.swap(x.min(127), Relaxed) + fn note_pos (&self) -> &AtomicUsize { + &self.note_pos } } impl TimePoint for MidiPointModel { - fn time_pos (&self) -> usize { - self.time_pos.load(Relaxed) - } - fn set_time_pos (&self, x: usize) -> usize { - self.time_pos.swap(x, Relaxed) + fn time_pos (&self) -> &AtomicUsize { + self.time_pos.as_ref() } } + +pub trait NotePoint { + fn note_len (&self) -> &AtomicUsize; + /// Get the current length of the note cursor. + fn get_note_len (&self) -> usize { + self.note_len().load(Relaxed) + } + /// Set the length of the note cursor, returning the previous value. + fn set_note_len (&self, x: usize) -> usize { + self.note_len().swap(x, Relaxed) + } + + fn note_pos (&self) -> &AtomicUsize; + /// Get the current pitch of the note cursor. + fn get_note_pos (&self) -> usize { + self.note_pos().load(Relaxed).min(127) + } + /// Set the current pitch fo the note cursor, returning the previous value. + fn set_note_pos (&self, x: usize) -> usize { + self.note_pos().swap(x.min(127), Relaxed) + } +} + +pub trait TimePoint { + fn time_pos (&self) -> &AtomicUsize; + /// Get the current time position of the note cursor. + fn get_time_pos (&self) -> usize { + self.time_pos().load(Relaxed) + } + /// Set the current time position of the note cursor, returning the previous value. + fn set_time_pos (&self, x: usize) -> usize { + self.time_pos().swap(x, Relaxed) + } +} + +pub trait MidiPoint: NotePoint + TimePoint { + /// Get the current end of the note cursor. + fn get_note_end (&self) -> usize { + self.get_time_pos() + self.get_note_len() + } +} + +impl MidiPoint for T {} diff --git a/crates/engine/src/note/note_range.rs b/crates/engine/src/note/note_range.rs index 308a4ae2..6804bd7a 100644 --- a/crates/engine/src/note/note_range.rs +++ b/crates/engine/src/note/note_range.rs @@ -1,4 +1,5 @@ use crate::*; +use std::sync::atomic::Ordering; #[derive(Debug, Clone)] pub struct MidiRangeModel { @@ -28,20 +29,44 @@ from!(|data:(usize, bool)|MidiRangeModel = Self { }); pub trait TimeRange { - fn time_len (&self) -> &AtomicUsize; - fn time_zoom (&self) -> &AtomicUsize; - fn time_lock (&self) -> &AtomicBool; + fn time_len (&self) -> &AtomicUsize; + fn get_time_len (&self) -> usize { + self.time_len().load(Ordering::Relaxed) + } + fn time_zoom (&self) -> &AtomicUsize; + fn get_time_zoom (&self) -> usize { + self.time_zoom().load(Ordering::Relaxed) + } + fn time_lock (&self) -> &AtomicBool; + fn get_time_lock (&self) -> bool { + self.time_lock().load(Ordering::Relaxed) + } fn time_start (&self) -> &AtomicUsize; - fn time_axis (&self) -> &AtomicUsize; - fn time_end (&self) -> usize { + fn get_time_start (&self) -> usize { + self.time_start().load(Ordering::Relaxed) + } + fn time_axis (&self) -> &AtomicUsize; + fn get_time_axis (&self) -> usize { + self.time_axis().load(Ordering::Relaxed) + } + fn get_time_end (&self) -> usize { self.time_start().get() + self.time_axis().get() * self.time_zoom().get() } } pub trait NoteRange { - fn note_lo (&self) -> &AtomicUsize; - fn note_axis (&self) -> &AtomicUsize; - fn note_hi (&self) -> usize { + fn note_lo (&self) -> &AtomicUsize; + fn get_note_lo (&self) -> usize { + self.note_lo().load(Ordering::Relaxed) + } + fn set_note_lo (&self, x: usize) -> usize { + self.note_lo().swap(x, Ordering::Relaxed) + } + fn note_axis (&self) -> &AtomicUsize; + fn get_note_axis (&self) -> usize { + self.note_axis().load(Ordering::Relaxed) + } + fn get_note_hi (&self) -> usize { (self.note_lo().get() + self.note_axis().get().saturating_sub(1)).min(127) } }