mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
137 lines
4.2 KiB
Rust
137 lines
4.2 KiB
Rust
use crate::*;
|
|
pub type ClipPool = Vec<Arc<RwLock<MidiClip>>>;
|
|
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<RwLock<MidiClip>>) {
|
|
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<RwLock<Vec<Arc<RwLock<MidiClip>>>>>,
|
|
/// Selected clip
|
|
pub clip: AtomicUsize,
|
|
/// Mode switch
|
|
pub mode: Option<PoolMode>,
|
|
}
|
|
/// Modes for clip pool
|
|
#[derive(Debug, Clone)]
|
|
pub enum PoolMode {
|
|
/// Renaming a pattern
|
|
Rename(usize, Arc<str>),
|
|
/// 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<RwLock<MidiClip>>|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<PoolMode> {
|
|
&self.mode
|
|
}
|
|
pub(crate) fn clips_mode_mut (&mut self) -> &mut Option<PoolMode> {
|
|
&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<ClipLengthFocus>,
|
|
}
|
|
impl ClipLength {
|
|
pub fn new (pulses: usize, focus: Option<ClipLengthFocus>) -> 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<str> {
|
|
format!("{}", self.bars()).into()
|
|
}
|
|
pub fn beats_string (&self) -> Arc<str> {
|
|
format!("{}", self.beats()).into()
|
|
}
|
|
pub fn ticks_string (&self) -> Arc<str> {
|
|
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, }
|
|
}
|
|
}
|