mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-10-08 18:56:42 +02:00
This commit is contained in:
parent
186b81f29c
commit
ca774cca41
21 changed files with 2160 additions and 2191 deletions
295
src/proj.rs
295
src/proj.rs
|
|
@ -3,14 +3,12 @@ impl_as_ref!(Option<Project>: |self: App|todo!());
|
|||
impl_as_mut!(Option<Project>: |self: App|todo!());
|
||||
pub trait HasProject: AsRef<Option<Project>> + AsMut<Option<Project>> {}
|
||||
impl<T: AsRef<Option<Project>> + AsMut<Option<Project>>> HasProject for T {}
|
||||
|
||||
/// Arranger.
|
||||
///
|
||||
/// ```
|
||||
/// let arranger = tek::Project::default();
|
||||
/// ```
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Project {
|
||||
#[derive(Default, Debug)] pub struct Project {
|
||||
/// JACK client handle.
|
||||
pub jack: Jack<'static>,
|
||||
/// Project name.
|
||||
|
|
@ -27,33 +25,33 @@ pub struct Project {
|
|||
/// Selected UI element
|
||||
pub selection: Selection,
|
||||
/// Contains all recently created clips.
|
||||
pub pool: Pool,
|
||||
#[cfg(feature = "sequencer")] pub clips: Pool<MidiClip>,
|
||||
/// Contains all recently created clips.
|
||||
#[cfg(feature = "sampler")] pub samples: Pool<Sample>,
|
||||
/// Source of time
|
||||
#[cfg(feature = "clock")] pub clock: Clock,
|
||||
#[cfg(feature = "clock")] pub clock: Clock,
|
||||
/// Allows one MIDI clip to be edited
|
||||
#[cfg(feature = "editor")] pub editor: Option<MidiEditor>,
|
||||
#[cfg(feature = "editor")] pub editor: Option<MidiEditor>,
|
||||
/// List of global midi inputs
|
||||
#[cfg(feature = "port")] pub midi_ins: Vec<MidiInput>,
|
||||
#[cfg(feature = "port")] pub midi_ins: Vec<MidiInput>,
|
||||
/// List of global midi outputs
|
||||
#[cfg(feature = "port")] pub midi_outs: Vec<MidiOutput>,
|
||||
#[cfg(feature = "port")] pub midi_outs: Vec<MidiOutput>,
|
||||
/// List of global audio inputs
|
||||
#[cfg(feature = "port")] pub audio_ins: Vec<AudioInput>,
|
||||
#[cfg(feature = "port")] pub audio_ins: Vec<AudioInput>,
|
||||
/// List of global audio outputs
|
||||
#[cfg(feature = "port")] pub audio_outs: Vec<AudioOutput>,
|
||||
#[cfg(feature = "port")] pub audio_outs: Vec<AudioOutput>,
|
||||
/// Last track number (to avoid duplicate port names)
|
||||
#[cfg(feature = "track")] pub track_last: usize,
|
||||
#[cfg(feature = "track")] pub track_last: usize,
|
||||
/// List of tracks
|
||||
#[cfg(feature = "track")] pub tracks: Vec<Track>,
|
||||
#[cfg(feature = "track")] pub tracks: Vec<Track>,
|
||||
/// Scroll offset of tracks
|
||||
#[cfg(feature = "track")] pub track_scroll: usize,
|
||||
#[cfg(feature = "track")] pub track_scroll: usize,
|
||||
/// List of scenes
|
||||
#[cfg(feature = "scene")] pub scenes: Vec<Scene>,
|
||||
#[cfg(feature = "scene")] pub scenes: Vec<Scene>,
|
||||
/// Scroll offset of scenes
|
||||
#[cfg(feature = "scene")] pub scene_scroll: usize,
|
||||
#[cfg(feature = "scene")] pub scene_scroll: usize,
|
||||
}
|
||||
|
||||
impl Project {
|
||||
|
||||
/// Create a new arrangement.
|
||||
pub fn new (
|
||||
jack: &Jack<'static>,
|
||||
|
|
@ -109,7 +107,7 @@ impl Project {
|
|||
//1 + self.devices_with_sizes().last().map(|(_, _, _, _, y)|y as u16).unwrap_or(0)
|
||||
}
|
||||
}
|
||||
|
||||
impl HasJack<'static> for Project { fn jack (&self) -> &Jack<'static> { &self.jack } }
|
||||
impl_has!(Jack<'static>: |self: Project| self.jack);
|
||||
impl_has!(Sizer: |self: Project| self.size);
|
||||
impl_has!(Clock: |self: Project| self.clock);
|
||||
|
|
@ -119,16 +117,21 @@ 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<MidiEditor>: |self: Project| &self.editor);
|
||||
impl_as_mut!(Option<MidiEditor>: |self: Project| &mut self.editor);
|
||||
impl HasJack<'static> for Project {
|
||||
fn jack (&self) -> &Jack<'static> { &self.jack }
|
||||
}
|
||||
impl HasClips for Project {
|
||||
fn clip (&self) -> Option<Arc<RwLock<MidiClip>>> { todo!() }
|
||||
fn clip_mut (&self) -> &mut Option<Arc<RwLock<MidiClip>>> { todo!() }
|
||||
fn clips (&self) -> RwLockReadGuard<'_, Clips> { todo!() }
|
||||
fn clips_mut (&self) -> RwLockWriteGuard<'_, Clips> { todo!() }
|
||||
}
|
||||
|
||||
//impl HasClip for Project {
|
||||
//fn clip (&self) -> Option<RwLockReadGuard<'_, Option<Arc<RwLock<MidiClip>>>>> {
|
||||
//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<Arc<RwLock<MidiClip>>>> {
|
||||
//self.as_ref().0.write()
|
||||
//}
|
||||
//}
|
||||
pub fn swap_value <T: Clone + PartialEq, U> (
|
||||
target: &mut T, value: &T, returned: impl Fn(T)->U
|
||||
) -> Perhaps<U> {
|
||||
|
|
@ -140,7 +143,6 @@ pub fn swap_value <T: Clone + PartialEq, U> (
|
|||
Ok(Some(returned(value)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle_bool <U> (
|
||||
target: &mut bool, value: &Option<bool>, returned: impl Fn(Option<bool>)->U
|
||||
) -> Perhaps<U> {
|
||||
|
|
@ -152,3 +154,238 @@ pub fn toggle_bool <U> (
|
|||
Ok(Some(returned(Some(value))))
|
||||
}
|
||||
}
|
||||
#[derive(Default, Debug)] pub struct Slot<T>(pub Arc<RwLock<Option<Arc<RwLock<T>>>>>);
|
||||
pub type SampleSlot = Slot<Sample>;
|
||||
pub type ClipSlot = Slot<MidiClip>;
|
||||
impl<T> Clone for Slot<T> { fn clone (&self) -> Self { Slot(self.0.clone()) } }
|
||||
impl<T> Slot<T> {
|
||||
pub fn new (value: impl Into<Option<T>>) -> Self {
|
||||
Self(Arc::new(RwLock::new(value.into().map(|value|Arc::new(RwLock::new(value))))))
|
||||
}
|
||||
pub fn read (&self) -> ArcRwLockReadGuard<RawRwLock, Option<Arc<RwLock<T>>>> {
|
||||
self.0.read_arc()
|
||||
}
|
||||
pub fn write (&self) -> ArcRwLockWriteGuard<RawRwLock, Option<Arc<RwLock<T>>>> {
|
||||
self.0.write_arc()
|
||||
}
|
||||
pub fn set_to (&mut self, value: Self) {
|
||||
self.0 = value.0;
|
||||
}
|
||||
pub fn set (&self, value: Option<T>) -> Option<Arc<RwLock<T>>> {
|
||||
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 <U> (&self, cb: impl FnOnce(&T)->U) -> Option<U> {
|
||||
self.read().as_ref().map(|x|cb(&*x.read()))
|
||||
}
|
||||
}
|
||||
impl Slot<MidiClip> {
|
||||
pub fn eval <'a> (state: &App, src: impl Language) -> Perhaps<ClipSlot> {
|
||||
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<RwLock<MidiClip>> = Arc::new(RwLock::new(Default::default()));
|
||||
//state.scenes()[*scene].clips[*track] = Some(clip.clone());
|
||||
//clip
|
||||
//}),
|
||||
|
||||
_ => return err!("not a clip: {word}")
|
||||
|
||||
}))
|
||||
}
|
||||
}
|
||||
impl Slot<Sample> {
|
||||
pub fn eval <'a> (_: &App, _: impl Language) -> Perhaps<SampleSlot> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
#[derive(Default, Debug)] pub struct Pool<T>(pub Arc<RwLock<Vec<Slot<T>>>>);
|
||||
impl<T> Clone for Pool<T> { fn clone (&self) -> Self { Pool(self.0.clone()) } }
|
||||
pub type SamplePool = Pool<Sample>;
|
||||
pub type ClipPool = Pool<MidiClip>;
|
||||
impl<T> Pool<T> {
|
||||
pub fn slot (&self, index: usize) -> Slot<T> {
|
||||
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<Arc<RwLock<T>>> {
|
||||
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<ArcRwLockReadGuard<RawRwLock, Option<Arc<RwLock<T>>>>> {
|
||||
self.0.read_arc().get(index).map(|clip|clip.read())
|
||||
}
|
||||
pub fn set (&self, index: usize, value: Option<T>) {
|
||||
self.write()[index] = Slot::new(value);
|
||||
}
|
||||
pub fn read (&self) -> ArcRwLockReadGuard<RawRwLock, Vec<Slot<T>>> {
|
||||
self.0.read_arc()
|
||||
}
|
||||
pub fn write (&self) -> ArcRwLockWriteGuard<RawRwLock, Vec<Slot<T>>> {
|
||||
self.0.write_arc()
|
||||
}
|
||||
pub fn append (&self, value: Option<T>) -> Slot<T> {
|
||||
let slot = Slot::new(value);
|
||||
self.0.write().push(slot.clone());
|
||||
slot
|
||||
}
|
||||
}
|
||||
impl Pool<MidiClip> {
|
||||
pub fn eval <'a> (_: &App, _: impl Language) -> Perhaps<ClipPool> {
|
||||
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<Sample> {
|
||||
pub fn eval <'a> (_: &App, _: impl Language) -> Perhaps<SamplePool> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
impl<T> From<Vec<Option<T>>> for Pool<T> {
|
||||
fn from (values: Vec<Option<T>>) -> 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
|
||||
Rename(usize, Arc<str>),
|
||||
/// Editing the length of a pattern
|
||||
Length(usize, usize, ClipLengthFocus),
|
||||
/// Load clip from disk
|
||||
Import(usize, Browse),
|
||||
/// Save clip to disk
|
||||
Export(usize, Browse),
|
||||
}
|
||||
/// 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, Debug, Default)] 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
|
||||
}
|
||||
}
|
||||
//impl_from!(Pool<MidiClip>: |clip:&Arc<RwLock<MidiClip>>|{
|
||||
//let model = Self::default();
|
||||
//model.clips.try_write().unwrap().push(clip.clone());
|
||||
//model.clip.store(1, Relaxed);
|
||||
//model
|
||||
//});
|
||||
//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_import (&mut self) -> Usually<()> {
|
||||
//*self.mode_mut() = Some(PoolMode::Import(
|
||||
//self.clip_index(),
|
||||
//Browse::new(None)?
|
||||
//));
|
||||
//Ok(())
|
||||
//}
|
||||
//pub fn begin_export (&mut self) -> Usually<()> {
|
||||
//*self.mode_mut() = Some(PoolMode::Export(
|
||||
//self.clip_index(),
|
||||
//Browse::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.read()[index].read().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.try_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.try_read().unwrap().iter().position(|x|*x.try_read().unwrap()==*clip);
|
||||
//if let Some(index) = index {
|
||||
//self.clips.try_write().unwrap().remove(index);
|
||||
//return true
|
||||
//}
|
||||
//false
|
||||
//}
|
||||
//fn _clip_new (&self) -> MidiClip { self.new_clip() }
|
||||
//fn _clip_cloned (&self) -> MidiClip { self.cloned_clip() }
|
||||
//fn _clip_index_current (&self) -> usize { 0 }
|
||||
//fn _clip_index_after (&self) -> usize { 0 }
|
||||
//fn _clip_index_previous (&self) -> usize { 0 }
|
||||
//fn _clip_index_next (&self) -> usize { 0 }
|
||||
//fn _color_random (&self) -> ItemColor { ItemColor::random() }
|
||||
//}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue