diff --git a/src/midi_play.rs b/src/midi_play.rs index fc00b079..8fcab188 100644 --- a/src/midi_play.rs +++ b/src/midi_play.rs @@ -28,7 +28,10 @@ pub trait HasPlayClip: HasClock { } fn enqueue_next (&mut self, clip: Option<&Arc>>) { - *self.next_clip_mut() = Some((self.clock().next_launch_instant(), clip.cloned())); + *self.next_clip_mut() = Some(( + self.clock().next_launch_instant(), + clip.cloned() + )); *self.reset_mut() = true; } diff --git a/src/proj.rs b/src/proj.rs index c6298f32..7417fbee 100644 --- a/src/proj.rs +++ b/src/proj.rs @@ -1,6 +1,4 @@ use crate::*; -impl_as_ref!(Option: |self: App|todo!()); -impl_as_mut!(Option: |self: App|todo!()); pub trait HasProject: AsRef> + AsMut> {} impl> + AsMut>> HasProject for T {} /// Arranger. @@ -117,21 +115,18 @@ impl_as_ref_opt!(Scene: |self: Project| self.selected_scene()); impl_as_mut_opt!(Scene: |self: Project| self.selected_scene_mut()); impl_as_ref!(Option: |self: Project| &self.editor); impl_as_mut!(Option: |self: Project| &mut self.editor); -//impl HasClip for Project { - //fn clip (&self) -> Option>>>> { - //match self.selection { - //Selection::Pool(index) => - //self.clips.read(index), - //Selection::TrackClip { track, scene } if let Some(scene) = self.scenes.get(scene) => - //scene.clips.read(track), - //_ => - //None - //} - //} - //fn clip_mut (&self) -> RwLockWriteGuard<'_, Option>>> { - //self.as_ref().0.write() - //} -//} +impl HasClip for Project { + fn clip_slot (&self) -> Option> { + match self.selection { + Selection::Clip(index) => + Some(self.clips.slot(index)), + Selection::TrackClip { track, scene } if let Some(scene) = self.scenes.get(scene) => + Some(scene.clips.slot(track)), + _ => + None + } + } +} pub fn swap_value ( target: &mut T, value: &T, returned: impl Fn(T)->U ) -> Perhaps { @@ -154,118 +149,6 @@ pub fn toggle_bool ( Ok(Some(returned(Some(value)))) } } -#[derive(Default, Debug)] pub struct Slot(pub Arc>>>>); -pub type SampleSlot = Slot; -pub type ClipSlot = Slot; -impl Clone for Slot { fn clone (&self) -> Self { Slot(self.0.clone()) } } -impl Slot { - pub fn new (value: impl Into>) -> Self { - Self(Arc::new(RwLock::new(value.into().map(|value|Arc::new(RwLock::new(value)))))) - } - pub fn read (&self) -> ArcRwLockReadGuard>>> { - self.0.read_arc() - } - pub fn write (&self) -> ArcRwLockWriteGuard>>> { - self.0.write_arc() - } - pub fn set_to (&mut self, value: Self) { - self.0 = value.0; - } - pub fn set (&self, value: Option) -> Option>> { - let mut value = value.map(|value|Arc::new(RwLock::new(value))); - std::mem::swap(&mut*self.0.write(), &mut value); - value - } - pub fn map_inner (&self, cb: impl FnOnce(&T)->U) -> Option { - self.read().as_ref().map(|x|cb(&*x.read())) - } -} -impl Slot { - pub fn eval <'a> (state: &App, src: impl Language) -> Perhaps { - eval_word(src, |word|Ok(match word { - - ":selected" if let Selection::TrackClip { track, scene } = state.selection() => { - state.scenes()[*scene].clips.slot(*track) - }, - - //":selected-or-create" if let Selection::TrackClip { - //track, scene - //} = state.selection() => Some(if let Some(Some(clip)) = state.scenes()[*scene].clips.get(*track) { - //clip.clone() - //} else { - //let clip: Arc> = Arc::new(RwLock::new(Default::default())); - //state.scenes()[*scene].clips[*track] = Some(clip.clone()); - //clip - //}), - - _ => return err!("not a clip: {word}") - - })) - } -} -impl Slot { - pub fn eval <'a> (_: &App, _: impl Language) -> Perhaps { - todo!() - } -} -#[derive(Default, Debug)] pub struct Pool(pub Arc>>>); -impl Clone for Pool { fn clone (&self) -> Self { Pool(self.0.clone()) } } -pub type SamplePool = Pool; -pub type ClipPool = Pool; -impl Pool { - pub fn slot (&self, index: usize) -> Slot { - let mut state = self.0.write(); - while index >= state.len() { - state.push(Slot::new(None)); - } - state[index].clone() - } - pub fn delete (&self, index: usize) -> Option>> { - let mut none = None; - let mut slot = self.slot(index).write(); - std::mem::swap(&mut none, &mut slot); - none - } - pub fn len (&self) -> usize { - self.0.read().len() - } - pub fn get (&self, index: usize) -> Option>>>> { - self.0.read_arc().get(index).map(|clip|clip.read()) - } - pub fn set (&self, index: usize, value: Option) { - self.write()[index] = Slot::new(value); - } - pub fn read (&self) -> ArcRwLockReadGuard>> { - self.0.read_arc() - } - pub fn write (&self) -> ArcRwLockWriteGuard>> { - self.0.write_arc() - } - pub fn append (&self, value: Option) -> Slot { - let slot = Slot::new(value); - self.0.write().push(slot.clone()); - slot - } -} -impl Pool { - pub fn eval <'a> (_: &App, _: impl Language) -> Perhaps { - todo!() - } - pub fn remove (&self, filter: &MidiClip) { - let iter = self.0.write(); - iter.retain(|x|if let Some(x) = &*x.0.read() { x!=filter } else { true }) - } -} -impl Pool { - pub fn eval <'a> (_: &App, _: impl Language) -> Perhaps { - todo!() - } -} -impl From>> for Pool { - fn from (values: Vec>) -> Self { - Self(Arc::new(RwLock::new(values.into_iter().map(Slot::new).collect()))) - } -} /// Modes for clip pool #[derive(Debug, Clone)] pub enum PoolMode { /// Renaming a pattern diff --git a/src/proj_select.rs b/src/proj_select.rs index 8b3700b8..632babc7 100644 --- a/src/proj_select.rs +++ b/src/proj_select.rs @@ -51,14 +51,14 @@ pub enum Selection { Input(usize), /// A MIDI output is selected. Output(usize), + /// A clip is selected. + Clip(usize), + /// A sample is selected. + Sample(usize), /// A scene is selected. #[cfg(feature = "scene")] Scene(usize), /// A track is selected. #[cfg(feature = "track")] Track(usize), - /// A clip is selected. - #[cfg(feature = "track")] Clip(usize), - /// A sample is selected. - #[cfg(feature = "track")] Sample(usize), /// A clip slot (track × scene) is selected. #[cfg(feature = "track")] TrackClip { track: usize, scene: usize }, /// A track's MIDI input connection is selected. diff --git a/src/tek.rs b/src/tek.rs index 7850a7d2..6dba8e47 100644 --- a/src/tek.rs +++ b/src/tek.rs @@ -42,12 +42,13 @@ mod proj_scene; pub use self::proj_scene::*; mod proj_select; pub use self::proj_select::*; mod proj_track; pub use self::proj_track::*; -mod tek_bind; pub use self::tek_bind::*; -mod tek_cli; pub use self::tek_cli::*; -mod tek_config; pub use self::tek_config::*; -mod tek_ctrl; pub use self::tek_ctrl::*; -mod tek_mode; pub use self::tek_mode::*; -mod tek_view; pub use self::tek_view::*; +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_mode; pub use self::tek_mode::*; +mod tek_pool; pub use self::tek_pool::*; +mod tek_view; pub use self::tek_view::*; #[cfg(feature = "lv2")] mod device_lv2; #[cfg(feature = "lv2")] pub use self::device_lv2::*; #[cfg(feature = "lv2_gui")] mod device_lv2_gui; #[cfg(feature = "lv2_gui")] pub use self::device_lv2_gui::*; @@ -366,10 +367,15 @@ impl_has!(Clock: |self: App|self.project.clock); impl_has!(Pool: |self: App|self.project.clips); impl_has!(Pool: |self: App|self.project.samples); impl_has!(Selection: |self: App|self.project.selection); - -impl_as_ref!(Slot: |self: App|todo!()); -impl_as_ref!(Option: |self: App|self.project.as_ref()); -impl_as_mut!(Option: |self: App|self.project.as_mut()); +impl_as_ref!(Option: |self: App|todo!()); +impl_as_mut!(Option: |self: App|todo!()); +impl_as_ref!(Option: |self: App|self.project.as_ref()); +impl_as_mut!(Option: |self: App|self.project.as_mut()); +impl HasClip for App { + fn clip_slot (&self) -> Option> { + self.project.clip_slot() + } +} primitive!(u8: try_to_u8); primitive!(u16: try_to_u16); diff --git a/src/tek_config.rs b/src/tek_conf.rs similarity index 100% rename from src/tek_config.rs rename to src/tek_conf.rs diff --git a/src/tek_ctrl.rs b/src/tek_ctrl.rs index b3d22a13..b0eece53 100644 --- a/src/tek_ctrl.rs +++ b/src/tek_ctrl.rs @@ -175,17 +175,17 @@ impl) + #[command(Launch = "launch")] fn tracks_launch (&mut self, clips: ClipPool) -> Perhaps { - if let Some(clips) = clips { - for (clip, track) in clips.0.read().iter().zip(self.tracks_mut()) { - track.sequencer.enqueue_next(clip); - } - } else { + if clips.len() == 0 { for track in self.tracks_mut().iter_mut() { track.sequencer.enqueue_next(None); } + return Ok(None) + } + for (clip, track) in clips.0.read().iter().zip(self.tracks_mut()) { + track.sequencer.enqueue_next(clip.read().as_ref()); } Ok(None) } @@ -734,16 +734,13 @@ impl ModeControl for T where T: HasModeStack } pub trait HasClip { - fn clip (&self) -> RwLockReadGuard<'_, Option>>>; - fn clip_mut (&self) -> RwLockWriteGuard<'_, Option>>>; -} -impl>> HasClip for T { - fn clip (&self) -> RwLockReadGuard<'_, Option>>> { - self.as_ref().0.read() + fn clip (&self) -> Option>>>> { + self.clip_slot().map(|slot|slot.read()) } - fn clip_mut (&self) -> RwLockWriteGuard<'_, Option>>> { - self.as_ref().0.write() + fn clip_mut (&self) -> Option>>>> { + self.clip_slot().map(|slot|slot.write()) } + fn clip_slot (&self) -> Option>; } impl MidiClipControl for T where T: HasClip @@ -752,20 +749,30 @@ impl MidiClipControl for T where T: HasClip + Namespace> + Namespace> { /// Create a clip at the current position if it does not already exist. - #[command(Upsert = "upsert")] fn clip_upsert (&mut self) -> Perhaps { - if self.clip().is_some() { - Ok(None) + #[command(Upsert = "upsert")] fn clip_upsert ( + &mut self + ) -> Perhaps { + Ok(self.clip_mut().map(|mut clip|if clip.is_none() { + *clip = Some(Default::default()); + None // TODO undo } else { - *self.clip_mut() = Some(Default::default()); - Ok(None) - } + None + }).flatten()) } - #[command(SetColor = "color")] fn clip_set_color (&mut self, color: Option) -> Perhaps { - Ok(self.clip().as_ref().map(|clip|MidiClipCmd::SetColor { color: Some(clip.write().set_color(color)) })) + #[command(SetColor = "color")] fn clip_set_color ( + &mut self, color: Option + ) -> Perhaps { + Ok(self.clip_slot().map_inner_mut(|clip|MidiClipCmd::SetColor { + color: Some(clip.set_color(color)) + })) } /// Toggle looping for the active clip - #[command(SetLoop = "loop")] fn clip_toggle_loop (&mut self, looping: Option) -> Perhaps { - Ok(self.clip().as_ref().map(|clip|MidiClipCmd::SetLoop { looping: Some(clip.write().toggle_loop(looping)) })) + #[command(SetLoop = "loop")] fn clip_toggle_loop ( + &mut self, looping: Option + ) -> Perhaps { + Ok(self.clip_slot().map_inner_mut(|clip|MidiClipCmd::SetLoop { + looping: Some(clip.toggle_loop(looping)) + })) } } diff --git a/src/tek_pool.rs b/src/tek_pool.rs new file mode 100644 index 00000000..308ea183 --- /dev/null +++ b/src/tek_pool.rs @@ -0,0 +1,147 @@ +use crate::*; + +#[derive(Default, Debug)] pub struct Pool(pub Arc>>>); +pub type SamplePool = Pool; +pub type ClipPool = Pool; +impl Clone for Pool { fn clone (&self) -> Self { Pool(self.0.clone()) } } +impl Pool { + pub fn slot (&self, index: usize) -> Slot { + let mut state = self.0.write(); + while index >= state.len() { + state.push(Slot::new(None)); + } + state[index].clone() + } + pub fn delete (&self, index: usize) -> Option>> { + let mut none = None; + let mut slot = self.slot(index).write(); + std::mem::swap(&mut none, &mut slot); + none + } + pub fn len (&self) -> usize { + self.0.read().len() + } + pub fn get (&self, index: usize) -> Option>>>> { + self.0.read_arc().get(index).map(|clip|clip.read()) + } + pub fn set (&self, index: usize, value: Option) { + self.write()[index] = Slot::new(value); + } + pub fn read (&self) -> ArcRwLockReadGuard>> { + self.0.read_arc() + } + pub fn write (&self) -> ArcRwLockWriteGuard>> { + self.0.write_arc() + } + pub fn append (&self, value: Option) -> Slot { + let slot = Slot::new(value); + self.0.write().push(slot.clone()); + slot + } +} + +impl Pool { + pub fn eval <'a> (_: &App, _: impl Language) -> Perhaps { + todo!() + } + pub fn remove (&self, filter: &MidiClip) { + let mut iter = self.0.write(); + iter.retain(|x|if let Some(x) = &*x.0.read() { + &*x.read() != filter + } else { + true + }) + } +} + +impl Pool { + pub fn eval <'a> (_: &App, _: impl Language) -> Perhaps { + todo!() + } +} + +impl From>> for Pool { + fn from (values: Vec>) -> Self { + Self(Arc::new(RwLock::new(values.into_iter().map(Slot::new).collect()))) + } +} + +#[derive(Default, Debug)] pub struct Slot(pub Arc>>>>); +pub type SampleSlot = Slot; +pub type ClipSlot = Slot; + +impl Clone for Slot { + fn clone (&self) -> Self { + Slot(self.0.clone()) + } +} + +impl Slot { + pub fn new (value: impl Into>) -> Self { + Self(Arc::new(RwLock::new(value.into().map(|value|Arc::new(RwLock::new(value)))))) + } + pub fn read (&self) -> ArcRwLockReadGuard>>> { + self.0.read_arc() + } + pub fn write (&self) -> ArcRwLockWriteGuard>>> { + self.0.write_arc() + } + pub fn set_to (&mut self, value: Self) { + self.0 = value.0; + } + pub fn set (&self, value: Option) -> Option>> { + let mut value = value.map(|value|Arc::new(RwLock::new(value))); + std::mem::swap(&mut*self.0.write(), &mut value); + value + } +} +pub trait SlotExt { + fn map_inner (&self, cb: impl FnOnce(&T)->U) -> Option; + fn map_inner_mut (&self, cb: impl FnOnce(&mut T)->U) -> Option; +} +impl SlotExt for Slot { + fn map_inner (&self, cb: impl FnOnce(&T)->U) -> Option { + self.read().as_ref().map(|x|cb(&*x.read())) + } + fn map_inner_mut (&self, cb: impl FnOnce(&mut T)->U) -> Option { + self.read().as_ref().map(|x|cb(&mut*x.write())) + } +} +impl SlotExt for Option> { + fn map_inner (&self, cb: impl FnOnce(&T)->U) -> Option { + self.as_ref().map(|x|x.map_inner(cb))? + } + fn map_inner_mut (&self, cb: impl FnOnce(&mut T)->U) -> Option { + self.as_ref().map(|x|x.map_inner_mut(cb))? + } +} + +impl Slot { + pub fn eval <'a> (state: &App, src: impl Language) -> Perhaps { + eval_word(src, |word|Ok(match word { + + ":selected" if let Selection::TrackClip { track, scene } = state.selection() => { + state.scenes()[*scene].clips.slot(*track) + }, + + //":selected-or-create" if let Selection::TrackClip { + //track, scene + //} = state.selection() => Some(if let Some(Some(clip)) = state.scenes()[*scene].clips.get(*track) { + //clip.clone() + //} else { + //let clip: Arc> = Arc::new(RwLock::new(Default::default())); + //state.scenes()[*scene].clips[*track] = Some(clip.clone()); + //clip + //}), + + _ => return err!("not a clip: {word}") + + })) + } +} + +impl Slot { + pub fn eval <'a> (_: &App, _: impl Language) -> Perhaps { + todo!() + } +}