use crate::*; #[derive(Debug)] pub struct Pool { pub visible: bool, /// Collection of clips pub clips: Arc>>>>, /// Selected clip pub clip: AtomicUsize, /// Mode switch pub mode: Option, /// Embedded file browser pub browser: Option, } //take!(BrowserCommand |state: Pool, iter|Ok(state.browser.as_ref() //.map(|p|Take::take(p, iter)) //.transpose()? //.flatten())); impl Default for Pool { fn default () -> Self { //use PoolMode::*; Self { visible: true, clips: Arc::from(RwLock::from(vec![])), clip: 0.into(), mode: None, browser: None, } } } impl Pool { pub fn clip_index (&self) -> usize { self.clip.load(Relaxed) } pub fn set_clip_index (&self, value: usize) { self.clip.store(value, Relaxed); } pub fn mode (&self) -> &Option { &self.mode } pub fn mode_mut (&mut self) -> &mut Option { &mut self.mode } pub fn begin_clip_length (&mut self) { let length = self.clips()[self.clip_index()].read().unwrap().length; *self.mode_mut() = Some(PoolMode::Length( self.clip_index(), length, ClipLengthFocus::Bar )); } pub fn begin_clip_rename (&mut self) { let name = self.clips()[self.clip_index()].read().unwrap().name.clone(); *self.mode_mut() = Some(PoolMode::Rename( self.clip_index(), name )); } pub fn begin_import (&mut self) -> Usually<()> { *self.mode_mut() = Some(PoolMode::Import( self.clip_index(), Browser::new(None)? )); Ok(()) } pub fn begin_export (&mut self) -> Usually<()> { *self.mode_mut() = Some(PoolMode::Export( self.clip_index(), Browser::new(None)? )); Ok(()) } pub fn new_clip (&self) -> MidiClip { MidiClip::new("Clip", true, 4 * PPQ, None, Some(ItemTheme::random())) } pub fn cloned_clip (&self) -> MidiClip { let index = self.clip_index(); let mut clip = self.clips()[index].read().unwrap().duplicate(); clip.color = ItemTheme::random_near(clip.color, 0.25); clip } pub fn add_new_clip (&self) -> (usize, Arc>) { let clip = Arc::new(RwLock::new(self.new_clip())); let index = { let mut clips = self.clips.write().unwrap(); clips.push(clip.clone()); clips.len().saturating_sub(1) }; self.clip.store(index, Relaxed); (index, clip) } pub fn delete_clip (&mut self, clip: &MidiClip) -> bool { let index = self.clips.read().unwrap().iter().position(|x|*x.read().unwrap()==*clip); if let Some(index) = index { self.clips.write().unwrap().remove(index); return true } false } } /// Modes for clip pool #[derive(Debug, Clone)] pub enum PoolMode { /// Renaming a pattern Rename(usize, Arc), /// Editing the length of a pattern Length(usize, usize, ClipLengthFocus), /// Load clip from disk Import(usize, Browser), /// Save clip to disk Export(usize, Browser), } /// Focused field of `ClipLength` #[derive(Copy, Clone, Debug)] pub enum ClipLengthFocus { /// Editing the number of bars Bar, /// Editing the number of beats Beat, /// Editing the number of ticks Tick, } impl ClipLengthFocus { pub fn next (&mut self) { use ClipLengthFocus::*; *self = match self { Bar => Beat, Beat => Tick, Tick => Bar, } } pub fn prev (&mut self) { use ClipLengthFocus::*; *self = match self { Bar => Tick, Beat => Bar, Tick => Beat, } } } /// Displays and edits clip length. #[derive(Clone)] pub struct ClipLength { /// Pulses per beat (quaver) ppq: usize, /// Beats per bar bpb: usize, /// Length of clip in pulses pulses: usize, /// Selected subdivision pub focus: Option, } impl ClipLength { pub fn _new (pulses: usize, focus: Option) -> Self { Self { ppq: PPQ, bpb: 4, pulses, focus } } pub fn bars (&self) -> usize { self.pulses / (self.bpb * self.ppq) } pub fn beats (&self) -> usize { (self.pulses % (self.bpb * self.ppq)) / self.ppq } pub fn ticks (&self) -> usize { self.pulses % self.ppq } pub fn bars_string (&self) -> Arc { format!("{}", self.bars()).into() } pub fn beats_string (&self) -> Arc { format!("{}", self.beats()).into() } pub fn ticks_string (&self) -> Arc { format!("{:>02}", self.ticks()).into() } } pub type ClipPool = Vec>>; pub trait HasClips { fn clips <'a> (&'a self) -> std::sync::RwLockReadGuard<'a, ClipPool>; fn clips_mut <'a> (&'a self) -> std::sync::RwLockWriteGuard<'a, ClipPool>; fn add_clip (&self) -> (usize, Arc>) { let clip = Arc::new(RwLock::new(MidiClip::new("Clip", true, 384, None, None))); self.clips_mut().push(clip.clone()); (self.clips().len() - 1, clip) } } #[macro_export] macro_rules! has_clips { (|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => { impl $(<$($L),*$($T $(: $U)?),*>)? HasClips for $Struct $(<$($L),*$($T),*>)? { fn clips <'a> (&'a $self) -> std::sync::RwLockReadGuard<'a, ClipPool> { $cb.read().unwrap() } fn clips_mut <'a> (&'a $self) -> std::sync::RwLockWriteGuard<'a, ClipPool> { $cb.write().unwrap() } } } } has_clips!(|self: Pool|self.clips); has_clip!(|self: Pool|self.clips().get(self.clip_index()).map(|c|c.clone())); from!(|clip:&Arc>|Pool = { let model = Self::default(); model.clips.write().unwrap().push(clip.clone()); model.clip.store(1, Relaxed); model });