use crate::*; 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() } } } } #[derive(Debug)] pub struct MidiPool { pub visible: bool, /// Collection of clips pub clips: Arc>>>>, /// Selected clip pub clip: AtomicUsize, /// Mode switch pub mode: Option, } /// 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, FileBrowser), /// Save clip to disk Export(usize, FileBrowser), } impl Default for MidiPool { fn default () -> Self { Self { visible: true, clips: Arc::from(RwLock::from(vec![])), clip: 0.into(), mode: None, } } } from!(|clip:&Arc>|MidiPool = { let model = Self::default(); model.clips.write().unwrap().push(clip.clone()); model.clip.store(1, Relaxed); model }); has_clips!(|self: MidiPool|self.clips); has_clip!(|self: MidiPool|self.clips().get(self.clip_index()).map(|c|c.clone())); impl MidiPool { pub(crate) fn clip_index (&self) -> usize { self.clip.load(Relaxed) } pub(crate) fn set_clip_index (&self, value: usize) { self.clip.store(value, Relaxed); } pub(crate) fn clips_mode (&self) -> &Option { &self.mode } pub(crate) fn clips_mode_mut (&mut self) -> &mut Option { &mut self.mode } pub fn file_picker (&self) -> Option<&FileBrowser> { match self.mode { Some(PoolMode::Import(_, ref file_picker)) => Some(file_picker), Some(PoolMode::Export(_, ref file_picker)) => Some(file_picker), _ => None } } } /// Displays and edits clip length. #[derive(Clone)] pub struct ClipLength { /// Pulses per beat (quaver) pub ppq: usize, /// Beats per bar pub bpb: usize, /// Length of clip in pulses pub 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() } } /// 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) { *self = match self { Self::Bar => Self::Beat, Self::Beat => Self::Tick, Self::Tick => Self::Bar, } } pub fn prev (&mut self) { *self = match self { Self::Bar => Self::Tick, Self::Beat => Self::Bar, Self::Tick => Self::Beat, } } }