mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
refactor sampler, flatten arranger
This commit is contained in:
parent
a9d22bd26f
commit
9f70441627
28 changed files with 1816 additions and 1836 deletions
|
|
@ -1,9 +1,5 @@
|
|||
use crate::*;
|
||||
|
||||
mod model_track; pub use self::model_track::*;
|
||||
mod model_scene; pub use self::model_scene::*;
|
||||
mod model_select; pub use self::model_select::*;
|
||||
|
||||
#[derive(Default, Debug)] pub struct Tek {
|
||||
/// Must not be dropped for the duration of the process
|
||||
pub jack: Jack,
|
||||
|
|
@ -99,6 +95,83 @@ impl Tek {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn tracks_add (
|
||||
&mut self, count: usize, width: Option<usize>,
|
||||
midi_from: &[PortConnect], midi_to: &[PortConnect],
|
||||
) -> Usually<()> {
|
||||
let jack = self.jack().clone();
|
||||
let track_color_1 = ItemColor::random();
|
||||
let track_color_2 = ItemColor::random();
|
||||
for i in 0..count {
|
||||
let color = track_color_1.mix(track_color_2, i as f32 / count as f32).into();
|
||||
let mut track = self.track_add(None, Some(color), midi_from, midi_to)?.1;
|
||||
if let Some(width) = width {
|
||||
track.width = width;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn track_add (
|
||||
&mut self, name: Option<&str>, color: Option<ItemPalette>,
|
||||
midi_froms: &[PortConnect],
|
||||
midi_tos: &[PortConnect],
|
||||
) -> Usually<(usize, &mut Track)> {
|
||||
let name = name.map_or_else(||self.track_next_name(), |x|x.to_string().into());
|
||||
let mut track = Track {
|
||||
width: (name.len() + 2).max(12),
|
||||
color: color.unwrap_or_else(ItemPalette::random),
|
||||
player: MidiPlayer::new(
|
||||
&format!("{name}"),
|
||||
self.jack(),
|
||||
Some(self.clock()),
|
||||
None,
|
||||
midi_froms,
|
||||
midi_tos
|
||||
)?,
|
||||
name,
|
||||
..Default::default()
|
||||
};
|
||||
self.tracks_mut().push(track);
|
||||
let len = self.tracks().len();
|
||||
let index = len - 1;
|
||||
for scene in self.scenes_mut().iter_mut() {
|
||||
while scene.clips.len() < len {
|
||||
scene.clips.push(None);
|
||||
}
|
||||
}
|
||||
Ok((index, &mut self.tracks_mut()[index]))
|
||||
}
|
||||
pub fn track_del (&mut self, index: usize) {
|
||||
self.tracks_mut().remove(index);
|
||||
for scene in self.scenes_mut().iter_mut() {
|
||||
scene.clips.remove(index);
|
||||
}
|
||||
}
|
||||
pub fn scenes_add (&mut self, n: usize) -> Usually<()> {
|
||||
let scene_color_1 = ItemColor::random();
|
||||
let scene_color_2 = ItemColor::random();
|
||||
for i in 0..n {
|
||||
let _ = self.scene_add(None, Some(
|
||||
scene_color_1.mix(scene_color_2, i as f32 / n as f32).into()
|
||||
))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn scene_add (&mut self, name: Option<&str>, color: Option<ItemPalette>)
|
||||
-> Usually<(usize, &mut Scene)>
|
||||
{
|
||||
let scene = Scene {
|
||||
name: name.map_or_else(||self.scene_default_name(), |x|x.to_string().into()),
|
||||
clips: vec![None;self.tracks().len()],
|
||||
color: color.unwrap_or_else(ItemPalette::random),
|
||||
};
|
||||
self.scenes_mut().push(scene);
|
||||
let index = self.scenes().len() - 1;
|
||||
Ok((index, &mut self.scenes_mut()[index]))
|
||||
}
|
||||
pub fn scene_default_name (&self) -> Arc<str> {
|
||||
format!("Sc{:3>}", self.scenes().len() + 1).into()
|
||||
}
|
||||
}
|
||||
|
||||
has_size!(<TuiOut>|self: Tek|&self.size);
|
||||
|
|
@ -120,7 +193,171 @@ has_editor!(|self: Tek|{
|
|||
is_editing = self.editing.load(Relaxed);
|
||||
});
|
||||
|
||||
//has_sampler!(|self: Tek|{
|
||||
//sampler = self.sampler;
|
||||
//index = self.editor.as_ref().map(|e|e.note_pos()).unwrap_or(0);
|
||||
//});
|
||||
pub trait HasSelection {
|
||||
fn selected (&self) -> &Selection;
|
||||
fn selected_mut (&mut self) -> &mut Selection;
|
||||
}
|
||||
|
||||
/// Represents the current user selection in the arranger
|
||||
#[derive(PartialEq, Clone, Copy, Debug, Default)] pub enum Selection {
|
||||
/// The whole mix is selected
|
||||
#[default] Mix,
|
||||
/// A track is selected.
|
||||
Track(usize),
|
||||
/// A scene is selected.
|
||||
Scene(usize),
|
||||
/// A clip (track × scene) is selected.
|
||||
Clip(usize, usize),
|
||||
}
|
||||
|
||||
/// Focus identification methods
|
||||
impl Selection {
|
||||
fn is_mix (&self) -> bool { matches!(self, Self::Mix) }
|
||||
fn is_track (&self) -> bool { matches!(self, Self::Track(_)) }
|
||||
fn is_scene (&self) -> bool { matches!(self, Self::Scene(_)) }
|
||||
fn is_clip (&self) -> bool { matches!(self, Self::Clip(_, _)) }
|
||||
pub fn track (&self) -> Option<usize> {
|
||||
use Selection::*;
|
||||
match self { Clip(t, _) => Some(*t), Track(t) => Some(*t), _ => None }
|
||||
}
|
||||
pub fn scene (&self) -> Option<usize> {
|
||||
use Selection::*;
|
||||
match self { Clip(_, s) => Some(*s), Scene(s) => Some(*s), _ => None }
|
||||
}
|
||||
pub fn describe (&self, tracks: &[Track], scenes: &[Scene]) -> Arc<str> {
|
||||
format!("{}", match self {
|
||||
Self::Mix => "Everything".to_string(),
|
||||
Self::Track(t) => tracks.get(*t).map(|track|format!("T{t}: {}", &track.name))
|
||||
.unwrap_or_else(||"T??".into()),
|
||||
Self::Scene(s) => scenes.get(*s).map(|scene|format!("S{s}: {}", &scene.name))
|
||||
.unwrap_or_else(||"S??".into()),
|
||||
Self::Clip(t, s) => match (tracks.get(*t), scenes.get(*s)) {
|
||||
(Some(_), Some(scene)) => match scene.clip(*t) {
|
||||
Some(clip) => format!("T{t} S{s} C{}", &clip.read().unwrap().name),
|
||||
None => format!("T{t} S{s}: Empty")
|
||||
},
|
||||
_ => format!("T{t} S{s}: Empty"),
|
||||
}
|
||||
}).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSelection for Tek {
|
||||
fn selected (&self) -> &Selection { &self.selected }
|
||||
fn selected_mut (&mut self) -> &mut Selection { &mut self.selected }
|
||||
}
|
||||
|
||||
pub trait HasTracks: HasSelection + HasClock + HasJack + HasEditor + Send + Sync {
|
||||
fn midi_ins (&self) -> &Vec<JackMidiIn>;
|
||||
fn midi_outs (&self) -> &Vec<JackMidiOut>;
|
||||
fn tracks (&self) -> &Vec<Track>;
|
||||
fn tracks_mut (&mut self) -> &mut Vec<Track>;
|
||||
fn track_longest (&self) -> usize {
|
||||
self.tracks().iter().map(|s|s.name.len()).fold(0, usize::max)
|
||||
}
|
||||
const WIDTH_OFFSET: usize = 1;
|
||||
fn track_next_name (&self) -> Arc<str> {
|
||||
format!("Track{:02}", self.tracks().len() + 1).into()
|
||||
}
|
||||
fn track (&self) -> Option<&Track> {
|
||||
self.selected().track().and_then(|s|self.tracks().get(s))
|
||||
}
|
||||
fn track_mut (&mut self) -> Option<&mut Track> {
|
||||
self.selected().track().and_then(|s|self.tracks_mut().get_mut(s))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)] pub struct Track {
|
||||
/// Name of track
|
||||
pub name: Arc<str>,
|
||||
/// Preferred width of track column
|
||||
pub width: usize,
|
||||
/// Identifying color of track
|
||||
pub color: ItemPalette,
|
||||
/// MIDI player state
|
||||
pub player: MidiPlayer,
|
||||
/// Device chain
|
||||
pub devices: Vec<Box<dyn Device>>,
|
||||
/// Inputs of 1st device
|
||||
pub audio_ins: Vec<JackAudioIn>,
|
||||
/// Outputs of last device
|
||||
pub audio_outs: Vec<JackAudioOut>,
|
||||
}
|
||||
|
||||
has_clock!(|self: Track|self.player.clock);
|
||||
|
||||
has_player!(|self: Track|self.player);
|
||||
|
||||
impl Track {
|
||||
const MIN_WIDTH: usize = 9;
|
||||
fn width_inc (&mut self) { self.width += 1; }
|
||||
fn width_dec (&mut self) { if self.width > Track::MIN_WIDTH { self.width -= 1; } }
|
||||
}
|
||||
|
||||
impl HasTracks for Tek {
|
||||
fn midi_ins (&self) -> &Vec<JackMidiIn> { &self.midi_ins }
|
||||
fn midi_outs (&self) -> &Vec<JackMidiOut> { &self.midi_outs }
|
||||
fn tracks (&self) -> &Vec<Track> { &self.tracks }
|
||||
fn tracks_mut (&mut self) -> &mut Vec<Track> { &mut self.tracks }
|
||||
}
|
||||
|
||||
pub trait HasScenes: HasSelection + HasEditor + Send + Sync {
|
||||
fn scenes (&self) -> &Vec<Scene>;
|
||||
fn scenes_mut (&mut self) -> &mut Vec<Scene>;
|
||||
fn scene_longest (&self) -> usize {
|
||||
self.scenes().iter().map(|s|s.name.len()).fold(0, usize::max)
|
||||
}
|
||||
fn scene (&self) -> Option<&Scene> {
|
||||
self.selected().scene().and_then(|s|self.scenes().get(s))
|
||||
}
|
||||
fn scene_mut (&mut self) -> Option<&mut Scene> {
|
||||
self.selected().scene().and_then(|s|self.scenes_mut().get_mut(s))
|
||||
}
|
||||
fn scene_del (&mut self, index: usize) {
|
||||
self.selected().scene().and_then(|s|Some(self.scenes_mut().remove(index)));
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)] pub struct Scene {
|
||||
/// Name of scene
|
||||
pub name: Arc<str>,
|
||||
/// Clips in scene, one per track
|
||||
pub clips: Vec<Option<Arc<RwLock<MidiClip>>>>,
|
||||
/// Identifying color of scene
|
||||
pub color: ItemPalette,
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
/// Returns the pulse length of the longest clip in the scene
|
||||
fn pulses (&self) -> usize {
|
||||
self.clips.iter().fold(0, |a, p|{
|
||||
a.max(p.as_ref().map(|q|q.read().unwrap().length).unwrap_or(0))
|
||||
})
|
||||
}
|
||||
/// Returns true if all clips in the scene are
|
||||
/// currently playing on the given collection of tracks.
|
||||
fn is_playing (&self, tracks: &[Track]) -> bool {
|
||||
self.clips.iter().any(|clip|clip.is_some()) && self.clips.iter().enumerate()
|
||||
.all(|(track_index, clip)|match clip {
|
||||
Some(c) => tracks
|
||||
.get(track_index)
|
||||
.map(|track|{
|
||||
if let Some((_, Some(clip))) = track.player().play_clip() {
|
||||
*clip.read().unwrap() == *c.read().unwrap()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.unwrap_or(false),
|
||||
None => true
|
||||
})
|
||||
}
|
||||
pub fn clip (&self, index: usize) -> Option<&Arc<RwLock<MidiClip>>> {
|
||||
match self.clips.get(index) { Some(Some(clip)) => Some(clip), _ => None }
|
||||
}
|
||||
}
|
||||
|
||||
impl HasScenes for Tek {
|
||||
fn scenes (&self) -> &Vec<Scene> { &self.scenes }
|
||||
fn scenes_mut (&mut self) -> &mut Vec<Scene> { &mut self.scenes }
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue