tek/crates/tek_sequencer/src/arranger.rs

584 lines
20 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use crate::*;
/// Root level object for standalone `tek_arranger`
pub struct Arranger<E: Engine> {
/// Which view is focused
pub focus_cursor: (usize, usize),
/// Controls the JACK transport.
pub transport: Option<Arc<RwLock<TransportToolbar<E>>>>,
/// Contains all the sequencers.
pub arrangement: Arrangement<E>,
/// Pool of all phrases in the arrangement
pub phrases: Arc<RwLock<PhrasePool<E>>>,
/// Phrase editor view
pub editor: PhraseEditor<E>,
/// This allows the sequencer view to be moved or hidden.
pub show_sequencer: Option<tek_core::Direction>,
}
/// Sections in the arranger app that may be focused
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum ArrangerFocus { Transport, Arrangement, PhrasePool, PhraseEditor }
/// Represents the tracks and scenes of the composition.
pub struct Arrangement<E: Engine> {
/// Name of arranger
pub name: Arc<RwLock<String>>,
/// Collection of phrases.
pub phrases: Arc<RwLock<PhrasePool<E>>>,
/// Collection of tracks.
pub tracks: Vec<ArrangementTrack<E>>,
/// Collection of scenes.
pub scenes: Vec<Scene>,
/// Currently selected element.
pub selected: ArrangementFocus,
/// Display mode of arranger
pub mode: ArrangementViewMode,
/// Whether the arranger is currently focused
pub focused: bool
}
/// Represents a track in the arrangement
pub struct ArrangementTrack<E: Engine> {
/// Name of track
pub name: Arc<RwLock<String>>,
/// Inputs
pub inputs: Vec<()>,
/// MIDI player/recorder
pub player: PhrasePlayer<E>,
/// Outputs
pub outputs: Vec<()>,
/// Preferred width of track column
pub width: usize,
/// Identifying color of track
pub color: Color,
}
#[derive(Default)]
pub struct Scene {
/// Name of scene
pub name: Arc<RwLock<String>>,
/// Clips in scene, one per track
pub clips: Vec<Option<Arc<RwLock<Phrase>>>>,
/// Identifying color of scene
pub color: Color,
}
#[derive(PartialEq, Clone, Copy)]
/// Represents the current user selection in the arranger
pub enum ArrangementFocus {
/// The whole mix is selected
Mix,
/// A track is selected.
Track(usize),
/// A scene is selected.
Scene(usize),
/// A clip (track × scene) is selected.
Clip(usize, usize),
}
/// Display mode of arranger
#[derive(PartialEq)]
pub enum ArrangementViewMode {
Horizontal,
Vertical(usize),
}
/// Arrangement, rendered vertically (session/grid mode).
pub struct VerticalArranger<'a, E: Engine>(
pub &'a Arrangement<E>, pub usize
);
/// Arrangement, rendered horizontally (arrangement/track mode).
pub struct HorizontalArranger<'a, E: Engine>(
pub &'a Arrangement<E>
);
impl<E: Engine> Arranger<E> {
pub fn edit_phrase (&mut self) {
self.editor.phrase = self.arrangement.phrase().clone();
self.focus(ArrangerFocus::PhraseEditor);
}
pub fn toggle_play (&mut self) -> Perhaps<bool> {
match self.transport {
Some(ref mut transport) => { transport.write().unwrap().toggle_play()?; },
None => { return Ok(None) }
}
Ok(Some(true))
}
}
/// Focus layout of arranger app
impl<E: Engine> FocusGrid<ArrangerFocus> for Arranger<E> {
fn cursor (&self) -> (usize, usize) { self.focus_cursor }
fn cursor_mut (&mut self) -> &mut (usize, usize) { &mut self.focus_cursor }
fn layout (&self) -> &[&[ArrangerFocus]] { &[
&[ArrangerFocus::Transport],
&[ArrangerFocus::Arrangement, ArrangerFocus::Arrangement],
&[ArrangerFocus::PhrasePool, ArrangerFocus::PhraseEditor],
] }
fn update_focus (&mut self) {
let focused = *self.focused();
if let Some(transport) = self.transport.as_ref() {
transport.write().unwrap().focused =
focused == ArrangerFocus::Transport
}
self.arrangement.focused =
focused == ArrangerFocus::Arrangement;
self.phrases.write().unwrap().focused =
focused == ArrangerFocus::PhrasePool;
self.editor.focused =
focused == ArrangerFocus::PhraseEditor;
}
}
/// General methods for arrangement
impl<E: Engine> Arrangement<E> {
pub fn new (name: &str, phrases: &Arc<RwLock<PhrasePool<E>>>) -> Self {
Self {
name: Arc::new(RwLock::new(name.into())),
mode: ArrangementViewMode::Vertical(2),
selected: ArrangementFocus::Clip(0, 0),
phrases: phrases.clone(),
scenes: vec![],
tracks: vec![],
focused: false
}
}
pub fn activate (&mut self) {
match self.selected {
ArrangementFocus::Scene(s) => {
for (t, track) in self.tracks.iter_mut().enumerate() {
track.player.phrase = self.scenes[s].clips[t].clone();
track.player.reset = true;
}
},
ArrangementFocus::Clip(t, s) => {
self.tracks[t].player.phrase = self.scenes[s].clips[t].clone();
self.tracks[t].player.reset = true;
},
_ => {}
}
}
pub fn delete (&mut self) {
match self.selected {
ArrangementFocus::Track(t) => self.track_del(),
ArrangementFocus::Scene(s) => self.scene_del(),
ArrangementFocus::Clip(t, s) => self.phrase_del(),
_ => {}
}
self.show_phrase()
}
pub fn increment (&mut self) {
match self.selected {
ArrangementFocus::Track(t) => self.track_width_inc(),
ArrangementFocus::Scene(s) => self.scene_next(),
ArrangementFocus::Clip(t, s) => self.phrase_next(),
_ => {}
}
self.show_phrase()
}
pub fn decrement (&mut self) {
match self.selected {
ArrangementFocus::Track(t) => self.track_width_dec(),
ArrangementFocus::Scene(s) => self.scene_prev(),
ArrangementFocus::Clip(t, s) => self.phrase_prev(),
_ => {}
}
self.show_phrase()
}
pub fn is_first_row (&self) -> bool {
let selected = self.selected;
selected.is_mix() || selected.is_track()
}
pub fn is_last_row (&self) -> bool {
let selected = self.selected;
(self.scenes.len() == 0 && (selected.is_mix() || selected.is_track())) || match selected {
ArrangementFocus::Scene(s) => s == self.scenes.len() - 1,
ArrangementFocus::Clip(_, s) => s == self.scenes.len() - 1,
_ => false
}
}
pub fn toggle_loop (&mut self) {
if let Some(phrase) = self.phrase() {
phrase.write().unwrap().toggle_loop()
}
}
pub fn go_up (&mut self) {
match self.mode {
ArrangementViewMode::Horizontal => self.track_prev(),
_ => self.scene_prev(),
};
self.show_phrase();
}
pub fn go_down (&mut self) {
match self.mode {
ArrangementViewMode::Horizontal => self.track_next(),
_ => self.scene_next(),
};
self.show_phrase();
}
pub fn go_left (&mut self) {
match self.mode {
ArrangementViewMode::Horizontal => self.scene_prev(),
_ => self.track_prev(),
};
self.show_phrase();
}
pub fn go_right (&mut self) {
match self.mode {
ArrangementViewMode::Horizontal => self.scene_next(),
_ => self.track_next(),
};
self.show_phrase();
}
pub fn move_back (&mut self) {
todo!("arrangement: move back")
}
pub fn move_forward (&mut self) {
todo!("arrangement: move forward")
}
pub fn rename_selected (&mut self) {
todo!("arrangement: rename selected")
}
}
/// Methods for tracks in arrangement
impl<E: Engine> Arrangement<E> {
pub fn track (&self) -> Option<&ArrangementTrack<E>> {
self.selected.track().map(|t|self.tracks.get(t)).flatten()
}
pub fn track_mut (&mut self) -> Option<&mut ArrangementTrack<E>> {
self.selected.track().map(|t|self.tracks.get_mut(t)).flatten()
}
pub fn track_width_inc (&mut self) {
self.track_mut().map(|t|t.width_inc());
}
pub fn track_width_dec (&mut self) {
self.track_mut().map(|t|t.width_dec());
}
pub fn track_next (&mut self) {
self.selected.track_next(self.tracks.len() - 1)
}
pub fn track_prev (&mut self) {
self.selected.track_prev()
}
pub fn track_add (&mut self, name: Option<&str>, color: Option<Color>) -> Usually<&mut ArrangementTrack<E>> {
self.tracks.push(name.map_or_else(
|| ArrangementTrack::new(&self.track_default_name(), color),
|name| ArrangementTrack::new(name, color),
));
let index = self.tracks.len() - 1;
Ok(&mut self.tracks[index])
}
pub fn track_del (&mut self) {
unimplemented!("Arranger::track_del");
}
pub fn track_default_name (&self) -> String {
format!("Track {}", self.tracks.len() + 1)
}
pub fn track_widths (&self) -> Vec<(usize, usize)> {
let mut widths = vec![];
let mut total = 0;
for track in self.tracks.iter() {
let width = track.width;
widths.push((width, total));
total += width;
}
widths.push((0, total));
widths
}
}
/// Methods for scenes in arrangement
impl<E: Engine> Arrangement<E> {
pub fn scene (&self) -> Option<&Scene> {
self.selected.scene().map(|s|self.scenes.get(s)).flatten()
}
pub fn scene_mut (&mut self) -> Option<&mut Scene> {
self.selected.scene().map(|s|self.scenes.get_mut(s)).flatten()
}
pub fn scene_next (&mut self) {
self.selected.scene_next(self.scenes.len() - 1)
}
pub fn scene_prev (&mut self) {
self.selected.scene_prev()
}
pub fn scene_add (&mut self, name: Option<&str>, color: Option<Color>) -> Usually<&mut Scene> {
let clips = vec![None;self.tracks.len()];
let name = name.map(|x|x.to_string()).unwrap_or_else(||self.scene_default_name());
self.scenes.push(Scene::new(name, clips, color));
let index = self.scenes.len() - 1;
Ok(&mut self.scenes[index])
}
pub fn scene_del (&mut self) {
unimplemented!("Arranger::scene_del");
}
pub fn scene_default_name (&self) -> String {
format!("Scene {}", self.scenes.len() + 1)
}
}
/// Methods for phrases in arrangement
impl<E: Engine> Arrangement<E> {
pub fn sequencer (&self) -> Option<&ArrangementTrack<E>> {
self.selected.track()
.map(|track|self.tracks.get(track))
.flatten()
}
pub fn sequencer_mut (&mut self) -> Option<&mut ArrangementTrack<E>> {
self.selected.track()
.map(|track|self.tracks.get_mut(track))
.flatten()
}
pub fn show_phrase (&mut self) {
let (scene, track) = (self.selected.scene(), self.selected.track());
if let (Some(scene_index), Some(track_index)) = (scene, track) {
let scene = self.scenes.get(scene_index);
let track = self.tracks.get_mut(track_index);
if let (Some(scene), Some(track)) = (scene, track) {
track.player.phrase = scene.clips[track_index].clone()
}
}
}
pub fn phrase (&self) -> Option<Arc<RwLock<Phrase>>> {
self.scene()?.clips.get(self.selected.track()?)?.clone()
}
pub fn phrase_del (&mut self) {
let track_index = self.selected.track();
let scene_index = self.selected.scene();
track_index
.and_then(|index|self.tracks.get_mut(index).map(|track|(index, track)))
.map(|(track_index, _)|{
scene_index
.and_then(|index|self.scenes.get_mut(index))
.map(|scene|scene.clips[track_index] = None);
});
}
pub fn phrase_put (&mut self) {
if let ArrangementFocus::Clip(track, scene) = self.selected {
self.scenes[scene].clips[track] = Some(self.phrases.read().unwrap().phrase().clone());
}
}
pub fn phrase_get (&mut self) {
if let ArrangementFocus::Clip(track, scene) = self.selected {
if let Some(phrase) = &self.scenes[scene].clips[track] {
let mut phrases = self.phrases.write().unwrap();
if let Some(index) = phrases.index_of(&*phrase.read().unwrap()) {
phrases.phrase = index;
}
}
}
}
pub fn phrase_next (&mut self) {
if let ArrangementFocus::Clip(track, scene) = self.selected {
if let Some(ref mut phrase) = self.scenes[scene].clips[track] {
let phrases = self.phrases.read().unwrap();
let index = phrases.index_of(&*phrase.read().unwrap());
if let Some(index) = index {
if index < phrases.phrases.len().saturating_sub(1) {
*phrase = phrases.phrases[index + 1].clone();
}
}
}
}
}
pub fn phrase_prev (&mut self) {
if let ArrangementFocus::Clip(track, scene) = self.selected {
if let Some(ref mut phrase) = self.scenes[scene].clips[track] {
let phrases = self.phrases.read().unwrap();
let index = phrases.index_of(&*phrase.read().unwrap());
if let Some(index) = index {
if index > 0 {
*phrase = phrases.phrases[index - 1].clone();
}
}
}
}
}
}
impl<E: Engine> ArrangementTrack<E> {
pub fn new (name: &str, color: Option<Color>) -> Self {
Self {
name: Arc::new(RwLock::new(name.into())),
inputs: vec![],
player: PhrasePlayer::new(),
outputs: vec![],
width: name.len() + 2,
color: color.unwrap_or_else(random_color)
}
}
pub fn longest_name (tracks: &[Self]) -> usize {
tracks.iter()
.map(|s|s.name.read().unwrap().len())
.fold(0, usize::max)
}
pub fn width_inc (&mut self) {
self.width += 1;
}
pub fn width_dec (&mut self) {
if self.width > 1 {
self.width -= 1;
}
}
}
/// Focus identification methods
impl ArrangementFocus {
pub fn description <E: Engine> (
&self,
tracks: &Vec<ArrangementTrack<E>>,
scenes: &Vec<Scene>,
) -> String {
format!("Selected: {}", match self {
Self::Mix => format!("Everything"),
Self::Track(t) => if let Some(track) = tracks.get(*t) {
format!("T{t}: {}", &track.name.read().unwrap())
} else {
format!("T??")
},
Self::Scene(s) => if let Some(scene) = scenes.get(*s) {
format!("S{s}: {}", &scene.name.read().unwrap())
} else {
format!("S??")
},
Self::Clip(t, s) => if let (Some(track), Some(scene)) = (
tracks.get(*t),
scenes.get(*s),
) {
if let Some(clip) = scene.clip(*t) {
format!("T{t} S{s} C{}", &clip.read().unwrap().name)
} else {
format!("T{t} S{s}: Empty")
}
} else {
format!("T{t} S{s}: Empty")
}
})
}
pub fn is_mix (&self) -> bool {
match self { Self::Mix => true, _ => false }
}
pub fn is_track (&self) -> bool {
match self { Self::Track(_) => true, _ => false }
}
pub fn is_scene (&self) -> bool {
match self { Self::Scene(_) => true, _ => false }
}
pub fn is_clip (&self) -> bool {
match self { Self::Clip(_, _) => true, _ => false }
}
pub fn track (&self) -> Option<usize> {
match self {
Self::Clip(t, _) => Some(*t),
Self::Track(t) => Some(*t),
_ => None
}
}
pub fn track_next (&mut self, last_track: usize) {
*self = match self {
Self::Mix => Self::Track(0),
Self::Track(t) => Self::Track(last_track.min(*t + 1)),
Self::Scene(s) => Self::Clip(0, *s),
Self::Clip(t, s) => Self::Clip(last_track.min(*t + 1), *s),
}
}
pub fn track_prev (&mut self) {
*self = match self {
Self::Mix => Self::Mix,
Self::Scene(s) => Self::Scene(*s),
Self::Track(t) => if *t == 0 {
Self::Mix
} else {
Self::Track(*t - 1)
},
Self::Clip(t, s) => if *t == 0 {
Self::Scene(*s)
} else {
Self::Clip(t.saturating_sub(1), *s)
}
}
}
pub fn scene (&self) -> Option<usize> {
match self {
Self::Clip(_, s) => Some(*s),
Self::Scene(s) => Some(*s),
_ => None
}
}
pub fn scene_next (&mut self, last_scene: usize) {
*self = match self {
Self::Mix => Self::Scene(0),
Self::Track(t) => Self::Clip(*t, 0),
Self::Scene(s) => Self::Scene(last_scene.min(*s + 1)),
Self::Clip(t, s) => Self::Clip(*t, last_scene.min(*s + 1)),
}
}
pub fn scene_prev (&mut self) {
*self = match self {
Self::Mix => Self::Mix,
Self::Track(t) => Self::Track(*t),
Self::Scene(s) => if *s == 0 {
Self::Mix
} else {
Self::Scene(*s - 1)
},
Self::Clip(t, s) => if *s == 0 {
Self::Track(*t)
} else {
Self::Clip(*t, s.saturating_sub(1))
}
}
}
}
/// Arranger display mode can be cycled
impl ArrangementViewMode {
/// Cycle arranger display mode
pub fn to_next (&mut self) {
*self = match self {
Self::Horizontal => Self::Vertical(1),
Self::Vertical(1) => Self::Vertical(2),
Self::Vertical(2) => Self::Vertical(2),
Self::Vertical(0) => Self::Horizontal,
Self::Vertical(_) => Self::Vertical(0),
}
}
}
impl Scene {
pub fn new (
name: impl AsRef<str>,
clips: impl AsRef<[Option<Arc<RwLock<Phrase>>>]>,
color: Option<Color>,
) -> Self {
Self {
name: Arc::new(RwLock::new(name.as_ref().into())),
clips: clips.as_ref().iter().map(|x|x.clone()).collect(),
color: color.unwrap_or_else(random_color),
}
}
/// Returns the pulse length of the longest phrase in the scene
pub fn pulses <E: Engine> (&self, tracks: &[ArrangementTrack<E>]) -> 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 phrases in the scene are currently playing
pub fn is_playing <E: Engine> (&self, tracks: &[ArrangementTrack<E>]) -> bool {
self.clips.iter().enumerate().all(|(track_index, clip)|match clip {
Some(clip) => tracks
.get(track_index)
.map(|track|if let Some(phrase) = &track.player.phrase {
*phrase.read().unwrap() == *clip.read().unwrap()
} else {
false
})
.unwrap_or(false),
None => true
})
}
pub fn ppqs <E: Engine> (tracks: &[ArrangementTrack<E>], scenes: &[Self]) -> Vec<(usize, usize)> {
let mut total = 0;
let mut scenes: Vec<(usize, usize)> = scenes.iter().map(|scene|{
let pulses = scene.pulses(tracks).max(PPQ);
total = total + pulses;
(pulses, total - pulses)
}).collect();
scenes.push((0, total));
scenes
}
pub fn longest_name (scenes: &[Self]) -> usize {
scenes.iter()
.map(|s|s.name.read().unwrap().len())
.fold(0, usize::max)
}
pub fn clip (&self, index: usize) -> Option<&Arc<RwLock<Phrase>>> {
if let Some(Some(clip)) = self.clips.get(index) {
Some(clip)
} else {
None
}
}
}