diff --git a/src/midi_edit.rs b/src/midi_edit.rs index 2703e8fc..bb08e820 100644 --- a/src/midi_edit.rs +++ b/src/midi_edit.rs @@ -1,13 +1,9 @@ #![allow(unused)] use crate::*; -pub trait HasEditor: AsRef> + AsMut> { - fn editor (&self) -> &Option { - self.as_ref() - } - fn editor_mut (&mut self) -> &mut Option { - self.as_mut() - } +impl EditorModel for T {} + +pub trait EditorModel: HasEditor { fn is_editing (&self) -> bool { self.editor().is_some() } @@ -19,8 +15,6 @@ pub trait HasEditor: AsRef> + AsMut> { } } -impl HasEditor for T where T: AsRef> + AsMut> {} - /// Contains state for viewing and editing a clip. /// /// ``` diff --git a/src/midi_mon.rs b/src/midi_mon.rs index bbf7a4ec..0a5fbe83 100644 --- a/src/midi_mon.rs +++ b/src/midi_mon.rs @@ -1,10 +1,5 @@ use crate::*; -pub trait HasMidiBuffers { - fn note_buf_mut (&mut self) -> &mut Vec; - fn midi_buf_mut (&mut self) -> &mut Vec>>; -} - pub trait MidiMonitor: HasMidiIns + HasMidiBuffers { /// Input note flags. fn notes_in (&self) -> &Arc>; diff --git a/src/midi_play.rs b/src/midi_play.rs index 8fcab188..f732ae51 100644 --- a/src/midi_play.rs +++ b/src/midi_play.rs @@ -1,12 +1,25 @@ use crate::*; -pub trait HasPlayClip: HasClock { - fn reset (&self) -> bool; - fn reset_mut (&mut self) -> &mut bool; - fn play_clip (&self) -> &Option<(Moment, Option>>)>; - fn play_clip_mut (&mut self) -> &mut Option<(Moment, Option>>)>; - fn next_clip (&self) -> &Option<(Moment, Option>>)>; - fn next_clip_mut (&mut self) -> &mut Option<(Moment, Option>>)>; +#[cfg(feature="clip")] impl HasPlayClip for Sequencer { + fn reset (&self) -> bool { self.reset } + fn reset_mut (&mut self) -> &mut bool { &mut self.reset } + fn play_clip (&self) -> &Option<(Moment, Option>>)> { + &self.play_clip + } + fn play_clip_mut (&mut self) -> &mut Option<(Moment, Option>>)> { + &mut self.play_clip + } + fn next_clip (&self) -> &Option<(Moment, Option>>)> { + &self.next_clip + } + fn next_clip_mut (&mut self) -> &mut Option<(Moment, Option>>)> { + &mut self.next_clip + } +} + +impl PlaybackModel for T {} + +pub trait PlaybackModel: HasPlayClip { fn pulses_since_start (&self) -> Option { if let Some((started, Some(_))) = self.play_clip().as_ref() { @@ -88,20 +101,3 @@ pub trait HasPlayClip: HasClock { field_v(color, "Next:", format!("{} {}", time, name)) } } - -#[cfg(feature="clip")] impl HasPlayClip for Sequencer { - fn reset (&self) -> bool { self.reset } - fn reset_mut (&mut self) -> &mut bool { &mut self.reset } - fn play_clip (&self) -> &Option<(Moment, Option>>)> { - &self.play_clip - } - fn play_clip_mut (&mut self) -> &mut Option<(Moment, Option>>)> { - &mut self.play_clip - } - fn next_clip (&self) -> &Option<(Moment, Option>>)> { - &self.next_clip - } - fn next_clip_mut (&mut self) -> &mut Option<(Moment, Option>>)> { - &mut self.next_clip - } -} diff --git a/src/midi_seq.rs b/src/midi_seq.rs index ddec83ce..e8da23f7 100644 --- a/src/midi_seq.rs +++ b/src/midi_seq.rs @@ -1,12 +1,5 @@ use crate::*; -impl +AsMut> HasSequencer for T {} - -pub trait HasSequencer: AsRef + AsMut { - fn sequencer_mut (&mut self) -> &mut Sequencer { self.as_mut() } - fn sequencer (&self) -> &Sequencer { self.as_ref() } -} - /// Contains state for playing a clip /// /// ``` diff --git a/src/proj.rs b/src/proj.rs index 7417fbee..99bb095c 100644 --- a/src/proj.rs +++ b/src/proj.rs @@ -1,11 +1,10 @@ use crate::*; -pub trait HasProject: AsRef> + AsMut> {} -impl> + AsMut>> HasProject for T {} /// Arranger. /// /// ``` /// let arranger = tek::Project::default(); /// ``` +#[namespace(ClipPool ClipPool::eval)] #[derive(Default, Debug)] pub struct Project { /// JACK client handle. pub jack: Jack<'static>, @@ -204,6 +203,35 @@ impl ClipLength { self.pulses % self.ppq } } + +impl HasTrackScroll for &mut Project { + fn track_scroll (&self) -> usize { + self.track_scroll + } + fn tracks_last (&self) -> usize { + self.track_last + } + fn tracks_last_mut (&mut self) -> &mut usize { + &mut self.track_last + } +} + +impl HasTrackScroll for Project { + fn track_scroll (&self) -> usize { + self.track_scroll + } + fn tracks_last (&self) -> usize { + self.track_last + } + fn tracks_last_mut (&mut self) -> &mut usize { + &mut self.track_last + } +} + +impl HasSceneScroll for Project { + fn scene_scroll (&self) -> usize { self.scene_scroll } +} + //impl_from!(Pool: |clip:&Arc>|{ //let model = Self::default(); //model.clips.try_write().unwrap().push(clip.clone()); diff --git a/src/proj_clock.rs b/src/proj_clock.rs index 27a78bf1..56ea1c9e 100644 --- a/src/proj_clock.rs +++ b/src/proj_clock.rs @@ -46,17 +46,6 @@ impl App { #[cfg(feature = "port")] pub click_out: Arc>>, } -impl + AsMut> HasClock for T {} - -pub trait HasClock: AsRef + AsMut { - fn clock (&self) -> &Clock { - self.as_ref() - } - fn clock_mut (&mut self) -> &mut Clock { - self.as_mut() - } -} - //impl Act for ClockCmd { //fn act (&self, state: &mut T) -> Perhaps { //self.act(state.clock_mut()) // awesome diff --git a/src/proj_device.rs b/src/proj_device.rs index 52aa013c..9f6153d9 100644 --- a/src/proj_device.rs +++ b/src/proj_device.rs @@ -189,12 +189,3 @@ impl> + AsMut>> HasDevices for T { self.as_mut() } } - -pub trait HasDevices: AsRef> + AsMut> { - fn devices (&self) -> &Vec { - self.as_ref() - } - fn devices_mut (&mut self) -> &mut Vec { - self.as_mut() - } -} diff --git a/src/proj_scene.rs b/src/proj_scene.rs index 06f65e74..e3a91663 100644 --- a/src/proj_scene.rs +++ b/src/proj_scene.rs @@ -48,17 +48,6 @@ impl Scene { } -impl + AsMutOpt + Send + Sync> HasScene for T {} - -pub trait HasScene: AsRefOpt + AsMutOpt { - fn scene (&self) -> Option<&Scene> { - self.as_ref_opt() - } - fn scene_mut (&mut self) -> Option<&mut Scene> { - self.as_mut_opt() - } -} - pub type SceneWith<'a, T> = (usize, &'a Scene, usize, usize, T); def_sizes_iter!(ScenesSizes => Scene); @@ -66,53 +55,3 @@ impl_as_ref!(Vec: |self: App| self.project.as_ref()); impl_as_mut!(Vec: |self: App| self.project.as_mut()); impl_as_ref_opt!(Scene: |self: App| self.project.as_ref_opt()); impl_as_mut_opt!(Scene: |self: App| self.project.as_mut_opt()); - -impl> + AsMut>> HasScenes for T {} - -pub trait HasScenes: AsRef> + AsMut> { - fn scenes (&self) -> &Vec { - self.as_ref() - } - fn scenes_mut (&mut self) -> &mut Vec { - self.as_mut() - } - /// Generate the default name for a new scene - fn scenes_default_name (&self) -> Arc { - format!("s{:3>}", self.scenes().len() + 1).into() - } - fn scenes_longest_name (&self) -> usize { - self.scenes().iter().map(|s|s.name.len()).fold(0, usize::max) - } - /// Add multiple scenes - fn scenes_add_many (&mut self, n: usize) - -> Usually<()> where Self: HasTracks - { - let scene_color_1 = ItemColor::random(); - let scene_color_2 = ItemColor::random(); - for i in 0..n { - let _ = self.scenes_add_one(None, Some( - scene_color_1.mix(scene_color_2, i as f32 / n as f32).into() - ))?; - } - Ok(()) - } - /// Add a scene - fn scenes_add_one (&mut self, name: Option>, color: Option) - -> Usually<(usize, &mut Scene)> - where - Self: HasTracks - { - let scene = Scene { - name: name.map_or_else(||self.scenes_default_name(), |x|x.to_string().into()), - clips: Pool::from(vec![None;self.tracks().len()]), - color: color.unwrap_or_else(ItemTheme::random), - }; - self.scenes_mut().push(scene); - let index = self.scenes().len() - 1; - Ok((index, &mut self.scenes_mut()[index])) - } -} - -pub trait HasSceneScroll: HasScenes { fn scene_scroll (&self) -> usize; } -impl HasSceneScroll for Project { fn scene_scroll (&self) -> usize { self.scene_scroll } } -impl HasSceneScroll for App { fn scene_scroll (&self) -> usize { self.project.scene_scroll() } } diff --git a/src/proj_select.rs b/src/proj_select.rs index 632babc7..4098f30f 100644 --- a/src/proj_select.rs +++ b/src/proj_select.rs @@ -4,40 +4,6 @@ impl_as_ref_opt!(Track: |self: Project| self.selected_track()); impl_as_mut_opt!(Track: |self: Project| self.selected_track_mut()); impl_as_ref_opt!(Track: |self: App| self.project.as_ref_opt()); impl_as_mut_opt!(Track: |self: App| self.project.as_mut_opt()); -impl +AsMut> HasSelection for T {} - -pub trait HasSelection: AsRef + AsMut { - fn selection (&self) -> &Selection { - self.as_ref() - } - fn selection_mut (&mut self) -> &mut Selection { - self.as_mut() - } - /// Get the active track - #[cfg(feature = "track")] fn selected_track (&self) -> Option<&Track> where Self: HasTracks { - let index = self.selection().track()?; - self.tracks().get(index) - } - /// Get a mutable reference to the active track - #[cfg(feature = "track")] fn selected_track_mut (&mut self) -> Option<&mut Track> where Self: HasTracks { - let index = self.selection().track()?; - self.tracks_mut().get_mut(index) - } - /// Get the active scene - #[cfg(feature = "scene")] fn selected_scene (&self) -> Option<&Scene> where Self: HasScenes { - let index = self.selection().scene()?; - self.scenes().get(index) - } - /// Get a mutable reference to the active scene - #[cfg(feature = "scene")] fn selected_scene_mut (&mut self) -> Option<&mut Scene> where Self: HasScenes { - let index = self.selection().scene()?; - self.scenes_mut().get_mut(index) - } - /// Get the active clip - #[cfg(feature = "clip")] fn selected_clip (&self) -> Option> where Self: HasScenes + HasTracks { - self.selected_scene()?.clips.read().get(self.selection().track()?).cloned() - } -} /// Represents the current user selection in the arranger #[derive(PartialEq, Clone, Copy, Debug, Default)] @@ -188,3 +154,32 @@ impl Selection { } } + +impl SelectionModel for T {} + +pub trait SelectionModel: HasSelection { + /// Get the active track + #[cfg(feature = "track")] fn selected_track (&self) -> Option<&Track> where Self: HasTracks { + let index = self.selection().track()?; + self.tracks().get(index) + } + /// Get a mutable reference to the active track + #[cfg(feature = "track")] fn selected_track_mut (&mut self) -> Option<&mut Track> where Self: HasTracks { + let index = self.selection().track()?; + self.tracks_mut().get_mut(index) + } + /// Get the active scene + #[cfg(feature = "scene")] fn selected_scene (&self) -> Option<&Scene> where Self: HasScenes { + let index = self.selection().scene()?; + self.scenes().get(index) + } + /// Get a mutable reference to the active scene + #[cfg(feature = "scene")] fn selected_scene_mut (&mut self) -> Option<&mut Scene> where Self: HasScenes { + let index = self.selection().scene()?; + self.scenes_mut().get_mut(index) + } + /// Get the active clip + #[cfg(feature = "clip")] fn selected_clip (&self) -> Option> where Self: HasScenes + HasTracks { + self.selected_scene()?.clips.read().get(self.selection().track()?).cloned() + } +} diff --git a/src/proj_track.rs b/src/proj_track.rs index d66d8e52..5573c7fe 100644 --- a/src/proj_track.rs +++ b/src/proj_track.rs @@ -80,148 +80,8 @@ impl HasWidth for Track { } } -impl + AsMutOpt> HasTrack for T {} - -pub trait HasTrack: AsRefOpt + AsMutOpt { - fn track (&self) -> Option<&Track> { - self.as_ref_opt() - } - fn track_mut (&mut self) -> Option<&mut Track> { - self.as_mut_opt() - } -} - def_sizes_iter!(TracksSizes => Track); impl_has!(Vec: |self: Project| self.tracks); impl_as_ref!(Vec: |self: App| self.project.as_ref()); impl_as_mut!(Vec: |self: App| self.project.as_mut()); impl_has!(Sequencer: |self: Track| self.sequencer); - -impl> + AsMut> + HasClock + HasTrackScroll> HasTracks for T {} - -pub trait HasTracks: AsRef> + AsMut> + HasClock + HasTrackScroll { - /// Spacing between tracks. - const TRACK_SPACING: usize = 0; - - /// Read-only reference to collection of [Track]s. - fn tracks (&self) -> &Vec { - self.as_ref() - } - - /// Mutable reference to collection of [Track]s. - fn tracks_mut (&mut self) -> &mut Vec { - self.as_mut() - } - - /// Run audio callbacks for every track and every device - fn tracks_jack_process (&mut self, client: &Client, scope: &ProcessScope) -> Control - where Self: HasJack<'static> - { - for track in self.tracks_mut().iter_mut() { - if Control::Quit == Audio::process(&mut track.sequencer, client, scope) { - return Control::Quit - } - for device in track.devices.iter_mut() { - if Control::Quit == DeviceAudio(device).process(client, scope) { - return Control::Quit - } - } - } - Control::Continue - } - - /// Add multiple tracks - fn tracks_add_many ( - &mut self, - count: usize, - width: Option, - mins: Arc<[Connect]>, - mouts: Arc<[Connect]>, - ) -> Usually<()> - where Self: HasJack<'static> + HasScenes - { - let track_color_1 = ItemColor::random(); - let track_color_2 = ItemColor::random(); - for i in 0..count { - let color = track_color_1.mix(track_color_2, i as f32 / count as f32).into(); - let track = self.tracks_add_one(None, Some(color), mins.clone(), mouts.clone())?.1; - if let Some(width) = width { - track.width = width; - } - } - Ok(()) - } - - /// Add a track - fn tracks_add_one ( - &mut self, - name: Option>, - color: Option, - mins: Arc<[Connect]>, - mouts: Arc<[Connect]>, - ) -> Usually<(usize, &mut Track)> - where Self: HasJack<'static> + HasScenes - { - let name: Arc = name.map_or_else( - ||format!("trk{}", self.tracks_last()).into(), - |x|x.to_string().into() - ); - *self.tracks_last_mut() += 1; - let track = Track::new_added(self.jack(), Some(self.clock()), &name, color, None, mins, mouts)?; - self.tracks_mut().push(track); - let len = self.tracks().len(); - let index = len - 1; - for scene in self.scenes_mut().iter_mut() { - while scene.clips.len() < len { - scene.clips.append(None); - } - } - Ok((index, &mut self.tracks_mut()[index])) - } - - fn track_longest_name (&self) -> usize { - self.tracks().iter().map(|s|s.name.len()).fold(0, usize::max) - } -} - -pub trait HasTrackScroll { - fn track_scroll (&self) -> usize; - fn tracks_last (&self) -> usize; - fn tracks_last_mut (&mut self) -> &mut usize; -} - -impl HasTrackScroll for &mut Project { - fn track_scroll (&self) -> usize { - self.track_scroll - } - fn tracks_last (&self) -> usize { - self.track_last - } - fn tracks_last_mut (&mut self) -> &mut usize { - &mut self.track_last - } -} - -impl HasTrackScroll for Project { - fn track_scroll (&self) -> usize { - self.track_scroll - } - fn tracks_last (&self) -> usize { - self.track_last - } - fn tracks_last_mut (&mut self) -> &mut usize { - &mut self.track_last - } -} - -impl HasTrackScroll for App { - fn track_scroll (&self) -> usize { - self.project.track_scroll() - } - fn tracks_last (&self) -> usize { - self.project.track_last - } - fn tracks_last_mut (&mut self) -> &mut usize { - &mut self.project.track_last - } -} diff --git a/src/smpl.rs b/src/smpl.rs index 5337ec7c..d5e69c50 100644 --- a/src/smpl.rs +++ b/src/smpl.rs @@ -58,15 +58,6 @@ pub(crate) use symphonia::{ #[cfg(feature = "meter")] pub output_meters: Vec, } -pub trait HasSampler: AsRef + AsMut { - fn sampler (&self) -> &Sampler { - self.as_ref() - } - fn sampler_mut (&mut self) -> &mut Sampler { - self.as_mut() - } -} - impl + AsMut> HasSampler for T {} impl_audio!(Sampler: sampler_jack_process); diff --git a/src/tek.edn b/src/tek.edn index 48bec674..3fd26f6f 100644 --- a/src/tek.edn +++ b/src/tek.edn @@ -1,5 +1,5 @@ (mode Menu (name Main Menu) (info Welcome to Tek!) (keys Y Confirm) - (view (bar/s 2 Transport (bar/n 2 StatusBar (align/c (bar/s 2 (align/x (bg (g 12) Logo)) Menu)))))) + (view (bar/s 2 Transport (bar/n 2 StatusBar (align/c (bar/s 2 (align/x Logo) Menu)))))) (mode Transport (name Transport) (info JACK transport controller.) (keys Clock Global) (view Transport)) @@ -24,7 +24,7 @@ (keys Global Clock Sampler I IFine Y Page EditorView EditorAdd EditorDel) (view (bsp/w MetersOut (bsp/e MetersIn (bsp/w GrooveMeta GrooveEditor))))) -(view Logo (text tek! 0.4.0 )) +(view Logo (bg (g 255) (pad/xy 1 0 (bg (g 0) (fg (g 255) (text tek! 0.4.0 )))))) (view Browse (bsp/s (pad/xy 3 1 BrowseTitle) (fg (g 96) browser))) (view PortsOut (bsp/s (align/w (text L-AUDIO-OUT)) (bsp/e (text MIDI-OUT) (align/e (text AUDIO-OUT-R))))) (view PortsIn (bsp/s (align/w (text L-AUDIO-IN)) (bsp/e (text MIDI-IN) (align/e (text AUDIO-IN-R))))) @@ -36,13 +36,14 @@ (bg (g 20) (full/x (pad/x 1 (bar/s 1 (text Mode) ModeList)))))) (view Transport (bg (g 0) (pad/x 1 (bar/e 4 PlayPause (bar/e 16 Bpm (bar/e 16 Beat Time)))))) -(keys Global (@f1 (dialog Help)) - (@f2 (project new)) - (@f3 (dialog History)) - (@f6 (dialog :save)) - (@f8 (dialog :options)) - (@f9 (dialog :load)) - (@f10 (dialog :quit))) +(keys Global (@f1 (enter Help)) + (@f2 (project new)) + (@f3 (enter History)) + (@f6 (enter Save)) + (@f8 (enter Options)) + (@f9 (enter Load)) + (@f10 (enter Quit)) + (@f12 (enter Devtools))) (keys Back (@escape cancel)) (keys Confirm (@enter confirm)) diff --git a/src/tek.rs b/src/tek.rs index 90ce4736..0bdb0ad1 100644 --- a/src/tek.rs +++ b/src/tek.rs @@ -45,6 +45,7 @@ mod tek_bind; pub use self::tek_bind::*; mod tek_cli; pub use self::tek_cli::*; mod tek_conf; pub use self::tek_conf::*; mod tek_ctrl; pub use self::tek_ctrl::*; +mod tek_has; pub use self::tek_has::*; mod tek_mode; pub use self::tek_mode::*; mod tek_pool; pub use self::tek_pool::*; mod tek_view; pub use self::tek_view::*; @@ -461,14 +462,6 @@ impl ControlAxis { } } -pub trait HasWidth { - const MIN_WIDTH: usize; - /// Increment track width. - fn width_inc (&mut self); - /// Decrement track width, down to a hardcoded minimum of [Self::MIN_WIDTH]. - fn width_dec (&mut self); -} - /// Various possible dialog modes. /// /// ``` @@ -538,7 +531,7 @@ impl Dialog { MenuItem::new(" Create session ", [ AppCmd::Dialog { dialog: Dialog::None }, - AppCmd::Mode { command: ModeCmd::Reset { modes: vec!["Arranger".into()] } }, + AppCmd::Mode { modes: vec!["Arranger".into()] }, AppCmd::Tracks { command: TracksCmd::Add }, AppCmd::Scenes { command: ScenesCmd::Add }, AppCmd::MidiClip { command: MidiClipCmd::Upsert }, @@ -790,3 +783,19 @@ impl App { if let Dialog::Browse(_, ref b) = self.dialog { Some(b) } else { None } } } + +impl HasTrackScroll for App { + fn track_scroll (&self) -> usize { + self.project.track_scroll() + } + fn tracks_last (&self) -> usize { + self.project.track_last + } + fn tracks_last_mut (&mut self) -> &mut usize { + &mut self.project.track_last + } +} + +impl HasSceneScroll for App { + fn scene_scroll (&self) -> usize { self.project.scene_scroll() } +} diff --git a/src/tek_ctrl.rs b/src/tek_ctrl.rs index 6618d7cf..799f2a56 100644 --- a/src/tek_ctrl.rs +++ b/src/tek_ctrl.rs @@ -4,8 +4,6 @@ impl_from!(AppCmd: |x: AxisCmd| AppCmd::Axis { command: x }); impl_from!(AppCmd: |x: BrowseCmd| AppCmd::Browse { command: x }); impl_from!(AppCmd: |x: MidiClipCmd| AppCmd::MidiClip { command: x }); impl_from!(AppCmd: |x: MidiEditCmd| AppCmd::MidiEdit { command: x }); -impl_from!(AppCmd: |x: ModeCmd| AppCmd::Mode { command: x }); -//impl_from!(AppCmd: |x: PoolCmd| AppCmd::Pool { command: x }); impl_from!(AppCmd: |x: ProjectCmd| AppCmd::Project { command: x }); impl_from!(AppCmd: |x: SceneCmd| AppCmd::Scene { command: x }); impl_from!(AppCmd: |x: ScenesCmd| AppCmd::Scenes { command: x }); @@ -18,6 +16,25 @@ impl_from!(AppCmd: |x: TracksCmd| AppCmd::Tracks { command: x }); self.exit.exit(); Ok(None) } + /// Switch to another set of contextual modes. + #[command(Mode = "mode")] pub fn cmd_mode (&mut self, modes: Vec>) -> Perhaps { + *self.modes_mut() = modes.into(); + Ok(None) + } + /// Enter a contextual mode. + #[command(Enter = "enter")] pub fn cmd_mode_enter (&mut self, mode: Arc) -> Perhaps { + self.modes_mut().push(mode); + Ok(Some(AppCmd::Leave)) + } + /// Leave a contextual mode. + #[command(Leave = "leave")] fn cmd_mode_leave (&mut self) -> Perhaps { + let mode = self.modes_mut().pop(); + Ok(mode.map(|mode|AppCmd::Enter { mode })) + } + /// Navigate along a contextual axis. + #[command(Axis = "axis")] pub fn cmd_axis (&mut self, command: AxisCmd) -> Perhaps { + Ok(command.dispatch(self)?.map(Into::into)) + } /// Cancel current selection. #[command(Cancel = "cancel")] pub fn cmd_cancel (&mut self) -> Perhaps { todo!("cancel") @@ -44,27 +61,22 @@ impl_from!(AppCmd: |x: TracksCmd| AppCmd::Tracks { command: x }); std::mem::swap(&mut self.dialog, &mut dialog); Ok(Some(AppCmd::Dialog { dialog })) } - #[command(Mode = "mode")] pub fn cmd_mode (&mut self, command: ModeCmd) -> Perhaps { - Ok(command.dispatch(self)?.map(Into::into)) - } - #[command(Project = "session")] pub fn cmd_session (&mut self, command: ProjectCmd) -> Perhaps { - Ok(command.dispatch(self)?.map(Into::into)) - } - #[command(Axis = "axis")] pub fn cmd_axis (&mut self, command: AxisCmd) -> Perhaps { - Ok(command.dispatch(self)?.map(Into::into)) - } + /// Command the file browser. #[command(Browse = "browse")] pub fn cmd_browse (&mut self, command: BrowseCmd) -> Perhaps { Ok(command.dispatch(self)?.map(Into::into)) } + /// Commands that alter the current session. + #[command(Project = "session")] pub fn cmd_session (&mut self, command: ProjectCmd) -> Perhaps { + Ok(command.dispatch(self)?.map(Into::into)) + } + /// Commands pertaining to MIDI clips. #[command(MidiClip = "clip")] pub fn cmd_midi_clip (&mut self, command: MidiClipCmd) -> Perhaps { Ok(command.dispatch(self)?.map(Into::into)) } + /// Commands pertaining to the MIDI clip editor. #[command(MidiEdit = "edit")] pub fn cmd_midi_edit (&mut self, command: MidiEditCmd) -> Perhaps { Ok(command.dispatch(self)?.map(Into::into)) } - //#[command(Pool = "pool")] pub fn cmd_pool (&mut self, command: PoolCmd) -> Perhaps { - //Ok(command.dispatch(self)?.map(Into::into)) - //} #[command(Scene = "scene")] pub fn cmd_scene (&mut self, command: SceneCmd) -> Perhaps { Ok(command.dispatch(self)?.map(Into::into)) } @@ -77,6 +89,9 @@ impl_from!(AppCmd: |x: TracksCmd| AppCmd::Tracks { command: x }); #[command(Tracks = "tracks")] pub fn cmd_tracks (&mut self, command: TracksCmd) -> Perhaps { Ok(command.dispatch(self)?.map(Into::into)) } + //#[command(Pool = "pool")] pub fn cmd_pool (&mut self, command: PoolCmd) -> Perhaps { + //Ok(command.dispatch(self)?.map(Into::into)) + //} } impl ProjectControl for T {} @@ -142,29 +157,17 @@ impl ClockControl for T where T: HasClock } } -impl TracksControl for T where T: HasTracks + HasScenes + HasSelection + HasJack<'static> - + Namespace - + Namespace> - + Namespace> - + Namespace>> - + Namespace> - + Namespace>> + Namespace -> TracksControl for T {} +{} #[tek_proc::commands(TracksCmd)] pub trait TracksControl: HasTracks + HasScenes + HasSelection + HasJack<'static> - + Namespace - + Namespace> - + Namespace> - + Namespace>> - + Namespace> - + Namespace>> + Namespace { /// Stop all playing clips @@ -190,7 +193,7 @@ impl Perhaps - where Self: HasScenes + HasJack<'static> + where Self: HasScenes + HasJack<'static> + Sized { let (_index, _) = self.tracks_add_one(None, None, [].into(), [].into())?; Ok(None) @@ -212,6 +215,76 @@ impl Control + where Self: HasJack<'static> + { + for track in self.tracks_mut().iter_mut() { + if Control::Quit == Audio::process(&mut track.sequencer, client, scope) { + return Control::Quit + } + for device in track.devices.iter_mut() { + if Control::Quit == DeviceAudio(device).process(client, scope) { + return Control::Quit + } + } + } + Control::Continue + } + + /// Add multiple tracks + fn tracks_add_many ( + &mut self, + count: usize, + width: Option, + mins: Arc<[Connect]>, + mouts: Arc<[Connect]>, + ) -> Usually<()> + where Self: HasJack<'static> + HasScenes + { + let track_color_1 = ItemColor::random(); + let track_color_2 = ItemColor::random(); + for i in 0..count { + let color = track_color_1.mix(track_color_2, i as f32 / count as f32).into(); + let track = self.tracks_add_one(None, Some(color), mins.clone(), mouts.clone())?.1; + if let Some(width) = width { + track.width = width; + } + } + Ok(()) + } + + /// Add a track + fn tracks_add_one ( + &mut self, + name: Option>, + color: Option, + mins: Arc<[Connect]>, + mouts: Arc<[Connect]>, + ) -> Usually<(usize, &mut Track)> + where Self: HasJack<'static> + HasScenes + { + let name: Arc = name.map_or_else( + ||format!("trk{}", self.tracks_last()).into(), + |x|x.to_string().into() + ); + *self.tracks_last_mut() += 1; + let track = Track::new_added(self.jack(), Some(self.clock()), &name, color, None, mins, mouts)?; + self.tracks_mut().push(track); + let len = self.tracks().len(); + let index = len - 1; + for scene in self.scenes_mut().iter_mut() { + while scene.clips.len() < len { + scene.clips.append(None); + } + } + Ok((index, &mut self.tracks_mut()[index])) + } + + fn track_longest_name (&self) -> usize { + self.tracks().iter().map(|s|s.name.len()).fold(0, usize::max) + } } impl - + Namespace - + Namespace> - + Namespace>> > ScenesControl for T {} #[tek_proc::commands(ScenesCmd)] pub trait ScenesControl: HasScenes @@ -293,11 +363,8 @@ impl - + Namespace - + Namespace> - + Namespace>> { - #[command(Add = "add")] fn scenes_add (&mut self) -> Perhaps { + #[command(Add = "add")] fn scenes_add (&mut self) -> Perhaps where Self: Sized { let (_index, _) = self.scenes_add_one(None, None)?; Ok(None) } @@ -318,6 +385,41 @@ impl Arc { + format!("s{:3>}", self.scenes().len() + 1).into() + } + fn scenes_longest_name (&self) -> usize { + self.scenes().iter().map(|s|s.name.len()).fold(0, usize::max) + } + /// Add multiple scenes + fn scenes_add_many (&mut self, n: usize) + -> Usually<()> where Self: HasTracks + { + let scene_color_1 = ItemColor::random(); + let scene_color_2 = ItemColor::random(); + for i in 0..n { + let _ = self.scenes_add_one(None, Some( + scene_color_1.mix(scene_color_2, i as f32 / n as f32).into() + ))?; + } + Ok(()) + } + /// Add a scene + fn scenes_add_one (&mut self, name: Option>, color: Option) + -> Usually<(usize, &mut Scene)> + where + Self: HasTracks + { + let scene = Scene { + name: name.map_or_else(||self.scenes_default_name(), |x|x.to_string().into()), + clips: Pool::from(vec![None;self.tracks().len()]), + color: color.unwrap_or_else(ItemTheme::random), + }; + self.scenes_mut().push(scene); + let index = self.scenes().len() - 1; + Ok((index, &mut self.scenes_mut()[index])) + } } impl ModeControl for T where T: HasModeStack - + Namespace>> + Namespace> + Namespace -{} -#[tek_proc::commands(ModeCmd)] pub trait ModeControl: HasModeStack - + Namespace>> + Namespace> + Namespace -{ - #[command(Reset = "reset")] fn mode_reset (&mut self, modes: Vec>) -> Perhaps { - *self.modes_mut() = modes.into(); - Ok(None) - } - #[command(Push = "push")] fn mode_push (&mut self, mode: Arc) -> Perhaps { - self.modes_mut().push(mode); - Ok(Some(ModeCmd::Pop)) - } - #[command(Pop = "pop")] fn mode_pop (&mut self) -> Perhaps { - let mode = self.modes_mut().pop(); - Ok(mode.map(|mode|ModeCmd::Push { mode })) - } -} - -pub trait HasClip { - fn clip (&self) -> Option>>>> { - self.clip_slot().map(|slot|slot.read()) - } - fn clip_mut (&self) -> Option>>>> { - self.clip_slot().map(|slot|slot.write()) - } - fn clip_slot (&self) -> Option>; -} - impl MidiClipControl for T where T: HasClip + Namespace> + Namespace> {} #[tek_proc::commands(MidiClipCmd)] pub trait MidiClipControl: HasClip diff --git a/src/tek_has.rs b/src/tek_has.rs new file mode 100644 index 00000000..b74506a6 --- /dev/null +++ b/src/tek_has.rs @@ -0,0 +1,198 @@ +use crate::*; + +pub trait HasModeStack { + fn modes (&self) -> RwLockReadGuard<'_, Vec>>; + fn modes_mut (&mut self) -> RwLockWriteGuard<'_, Vec>>; +} + +impl HasEditor for T where T: + AsRef> + AsMut> {} +pub trait HasEditor: + AsRef> + AsMut> +{ + fn editor (&self) -> &Option { + self.as_ref() + } + fn editor_mut (&mut self) -> &mut Option { + self.as_mut() + } +} + +pub trait HasMidiBuffers { + fn note_buf_mut (&mut self) -> &mut Vec; + fn midi_buf_mut (&mut self) -> &mut Vec>>; +} + +pub trait HasPlayClip: HasClock { + fn reset (&self) -> bool; + fn reset_mut (&mut self) -> &mut bool; + fn play_clip (&self) -> &Option<(Moment, Option>>)>; + fn play_clip_mut (&mut self) -> &mut Option<(Moment, Option>>)>; + fn next_clip (&self) -> &Option<(Moment, Option>>)>; + fn next_clip_mut (&mut self) -> &mut Option<(Moment, Option>>)>; +} + +impl HasSequencer for T where T: + AsRef + AsMut {} +pub trait HasSequencer: + AsRef + AsMut +{ + fn sequencer (&self) -> &Sequencer { + self.as_ref() + } + fn sequencer_mut (&mut self) -> &mut Sequencer { + self.as_mut() + } +} + +impl HasClock for T where T: + AsRef + AsMut {} + +pub trait HasClock: + AsRef + AsMut +{ + fn clock (&self) -> &Clock { + self.as_ref() + } + fn clock_mut (&mut self) -> &mut Clock { + self.as_mut() + } +} + +pub trait HasDevices: + AsRef> + AsMut> +{ + fn devices (&self) -> &Vec { + self.as_ref() + } + fn devices_mut (&mut self) -> &mut Vec { + self.as_mut() + } +} + +pub trait HasProject: + AsRef> + AsMut> +{} + +impl HasProject for T where T: + AsRef> + AsMut> +{} + +pub trait HasSceneScroll: HasScenes { + fn scene_scroll (&self) -> usize; +} + +impl HasScene for T where T: + AsRefOpt + AsMutOpt + Send + Sync +{} + +pub trait HasScene: + AsRefOpt + AsMutOpt +{ + fn scene (&self) -> Option<&Scene> { + self.as_ref_opt() + } + fn scene_mut (&mut self) -> Option<&mut Scene> { + self.as_mut_opt() + } +} + +impl HasScenes for T where T: + AsRef> + AsMut> +{} + +pub trait HasScenes: AsRef> + AsMut> { + fn scenes (&self) -> &Vec { + self.as_ref() + } + fn scenes_mut (&mut self) -> &mut Vec { + self.as_mut() + } +} + +impl HasSelection for T where T: + AsRef + AsMut +{} + +pub trait HasSelection: AsRef + AsMut { + fn selection (&self) -> &Selection { + self.as_ref() + } + fn selection_mut (&mut self) -> &mut Selection { + self.as_mut() + } +} + +impl HasTrack for T where T: + AsRefOpt + AsMutOpt +{} + +pub trait HasTrack: AsRefOpt + AsMutOpt { + fn track (&self) -> Option<&Track> { + self.as_ref_opt() + } + fn track_mut (&mut self) -> Option<&mut Track> { + self.as_mut_opt() + } +} + +impl HasTracks for T where T: + AsRef> + AsMut> + HasClock + HasTrackScroll +{} + +pub trait HasTracks: AsRef> + AsMut> + HasClock + HasTrackScroll { + /// Read-only reference to collection of [Track]s. + fn tracks (&self) -> &Vec { + self.as_ref() + } + /// Mutable reference to collection of [Track]s. + fn tracks_mut (&mut self) -> &mut Vec { + self.as_mut() + } +} + +pub trait HasTrackScroll { + fn track_scroll (&self) -> usize; + fn tracks_last (&self) -> usize; + fn tracks_last_mut (&mut self) -> &mut usize; +} + +pub trait HasSampler: AsRef + AsMut { + fn sampler (&self) -> &Sampler { + self.as_ref() + } + fn sampler_mut (&mut self) -> &mut Sampler { + self.as_mut() + } +} + +pub trait HasClip { + fn clip_slot (&self) -> Option>; + fn clip (&self) -> Option>>>> { + self.clip_slot().map(|slot|slot.read()) + } + fn clip_mut (&self) -> Option>>>> { + self.clip_slot().map(|slot|slot.write()) + } +} + +pub trait HasWidth { + const MIN_WIDTH: usize; + /// Increment track width. + fn width_inc (&mut self); + /// Decrement track width, down to a hardcoded minimum of [Self::MIN_WIDTH]. + fn width_dec (&mut self); +} + +pub trait HasClipsSize { + /// Size of clips area. + fn clips_size (&self) -> &Sizer; + /// Draw content annotated with value of [Self::clips_size]. + fn with_clips_size (&self, show: bool, content: impl Draw) -> impl Draw { + let size = self.clips_size(); + above( + when(show, fg(Green, east!(size.w() as usize, "x", size.h() as usize)).align_se()), + size.of(content.align_c().full_wh()) + ) + } +} diff --git a/src/tek_mode.rs b/src/tek_mode.rs index b4aa8970..0cfb6ad2 100644 --- a/src/tek_mode.rs +++ b/src/tek_mode.rs @@ -116,11 +116,6 @@ impl AsMut for Mode { } } -pub trait HasModeStack { - fn modes (&self) -> RwLockReadGuard<'_, Vec>>; - fn modes_mut (&mut self) -> RwLockWriteGuard<'_, Vec>>; -} - impl HasModeStack for App { fn modes (&self) -> RwLockReadGuard<'_, Vec>> { self.modes.read() diff --git a/src/tek_pool.rs b/src/tek_pool.rs index 308ea183..8272455d 100644 --- a/src/tek_pool.rs +++ b/src/tek_pool.rs @@ -41,7 +41,7 @@ impl Pool { } impl Pool { - pub fn eval <'a> (_: &App, _: impl Language) -> Perhaps { + pub fn eval <'a, T> (_: &T, _: impl Language) -> Perhaps { todo!() } pub fn remove (&self, filter: &MidiClip) { diff --git a/src/tek_view.rs b/src/tek_view.rs index c46c6fb5..8f4e6473 100644 --- a/src/tek_view.rs +++ b/src/tek_view.rs @@ -600,18 +600,6 @@ impl OctaveVertical { impl HasClipsSize for App { fn clips_size (&self) -> &Sizer { &self.project.size_inner } } impl HasClipsSize for Project { fn clips_size (&self) -> &Sizer { &self.size_inner } } -pub trait HasClipsSize { - /// Size of clips area. - fn clips_size (&self) -> &Sizer; - /// Draw content annotated with value of [Self::clips_size]. - fn with_clips_size (&self, show: bool, content: impl Draw) -> impl Draw { - let size = self.clips_size(); - above( - when(show, fg(Green, east!(size.w() as usize, "x", size.h() as usize)).align_se()), - size.of(content.align_c().full_wh()) - ) - } -} impl MidiEditor { pub fn clip_status (&self) -> impl Draw + '_ { @@ -668,6 +656,8 @@ impl TracksView for T where T: pub trait TracksView: HasTracks + ScenesView + HasMidiIns + HasMidiOuts + HasTrack { + /// Spacing between tracks. + const TRACK_SPACING: usize = 0; /// Iterate over tracks with their corresponding sizes. fn tracks_with_sizes (&self) -> impl TracksSizes<'_> { @@ -880,7 +870,7 @@ pub trait ScenesView: HasEditor + HasSelection + HasSceneScroll + HasClipsSize + fn w_side (&self) -> u16; fn w_mid (&self) -> u16; - fn view_scenes_names (&self) -> impl Draw { + fn view_scenes_names (&self) -> impl Draw where Self: Sized { let select = self.selection(); let editor = self.editor(); let editing = self.is_editing();