mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
209 lines
6 KiB
Rust
209 lines
6 KiB
Rust
use crate::*;
|
|
|
|
#[derive(Debug)]
|
|
pub struct Pool {
|
|
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>,
|
|
/// Embedded file browser
|
|
pub browser: Option<Browser>,
|
|
}
|
|
//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<PoolMode> {
|
|
&self.mode
|
|
}
|
|
pub fn mode_mut (&mut self) -> &mut Option<PoolMode> {
|
|
&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<RwLock<MidiClip>>) {
|
|
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<str>),
|
|
/// 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<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()
|
|
}
|
|
}
|
|
|
|
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()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
has_clips!(|self: Pool|self.clips);
|
|
|
|
has_clip!(|self: Pool|self.clips().get(self.clip_index()).map(|c|c.clone()));
|
|
|
|
from!(|clip:&Arc<RwLock<MidiClip>>|Pool = {
|
|
let model = Self::default();
|
|
model.clips.write().unwrap().push(clip.clone());
|
|
model.clip.store(1, Relaxed);
|
|
model
|
|
});
|