wip: separate PhrasePlayer vs PhraseEditor

This commit is contained in:
🪞👃🪞 2024-10-08 12:05:47 +03:00
parent 7668a6f339
commit 25e54eba4e
8 changed files with 491 additions and 636 deletions

View file

@ -3,23 +3,20 @@ use crate::*;
/// Root level object for standalone `tek_arranger`
pub struct Arranger<E: Engine> {
/// Controls the JACK transport.
pub transport: Option<Arc<RwLock<TransportToolbar<E>>>>,
pub transport: Option<Arc<RwLock<TransportToolbar<E>>>>,
/// Contains all the sequencers.
pub arrangement: Arrangement<E>,
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>,
pub show_sequencer: Option<tek_core::Direction>,
/// Index of currently focused component
pub focus: usize,
/// Focus target that passes events down to sequencer
pub sequencer_proxy: SequencerProxy<E>,
pub focus: usize,
/// Slot for modal dialog displayed on top of app.
pub modal: Option<Box<dyn ContentComponent<E>>>,
pub phrases: Arc<RwLock<PhrasePool<E>>>,
pub editor: PhraseEditor<E>,
pub modal: Option<Box<dyn ContentComponent<E>>>,
}
/// Represents the tracks and scenes of the composition.
pub struct Arrangement<E: Engine> {
/// Name of arranger
@ -27,23 +24,77 @@ pub struct Arrangement<E: Engine> {
/// Collection of phrases.
pub phrases: Arc<RwLock<PhrasePool<E>>>,
/// Collection of tracks.
pub tracks: Vec<Sequencer<E>>,
pub tracks: Vec<ArrangementTrack<E>>,
/// Collection of scenes.
pub scenes: Vec<Scene>,
/// Currently selected element.
pub selected: ArrangerFocus,
pub selected: ArrangementFocus,
/// Display mode of arranger
pub mode: ArrangerViewMode,
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<()>,
}
#[derive(Default)]
pub struct Scene {
pub name: Arc<RwLock<String>>,
pub clips: Vec<Option<usize>>,
}
#[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),
}
/// A collection of phrases to play on each track.
pub struct VerticalArranger<'a, E: Engine>(
pub &'a Arrangement<E>, pub usize
);
pub struct VerticalArrangerGrid<'a>(
pub u16, pub &'a [(usize, usize)], pub &'a [(usize, usize)]
);
pub struct VerticalArrangerCursor<'a>(
pub bool, pub ArrangementFocus, pub u16, pub &'a [(usize, usize)], pub &'a [(usize, usize)],
);
pub struct HorizontalArranger<'a, E: Engine>(
pub &'a Arrangement<E>
);
pub struct ArrangerRenameModal<E: Engine> {
_engine: std::marker::PhantomData<E>,
pub done: bool,
pub target: ArrangementFocus,
pub value: String,
pub result: Arc<RwLock<String>>,
pub cursor: usize
}
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: ArrangerViewMode::Vertical(2),
selected: ArrangerFocus::Clip(0, 0),
mode: ArrangementViewMode::Vertical(2),
selected: ArrangementFocus::Clip(0, 0),
phrases: phrases.clone(),
scenes: vec![],
tracks: vec![],
@ -52,25 +103,25 @@ impl<E: Engine> Arrangement<E> {
}
pub fn activate (&mut self) {
match self.selected {
ArrangerFocus::Scene(s) => {
ArrangementFocus::Scene(s) => {
for (track_index, track) in self.tracks.iter_mut().enumerate() {
track.playing_phrase = self.scenes[s].clips[track_index];
track.reset = true;
track.player.phrase = self.scenes[s].clips[track_index];
track.player.reset = true;
}
},
ArrangerFocus::Clip(t, s) => {
self.tracks[t].playing_phrase = self.scenes[s].clips[t];
self.tracks[t].reset = true;
ArrangementFocus::Clip(t, s) => {
self.tracks[t].player.phrase = self.scenes[s].clips[t];
self.tracks[t].player.reset = true;
},
_ => {}
}
}
pub fn sequencer (&self) -> Option<&Sequencer<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 Sequencer<E>> {
pub fn sequencer_mut (&mut self) -> Option<&mut ArrangementTrack<E>> {
self.selected.track()
.map(|track|self.tracks.get_mut(track))
.flatten()
@ -81,7 +132,7 @@ impl<E: Engine> Arrangement<E> {
let scene = self.scenes.get(scene_index);
let track = self.tracks.get_mut(track_index);
if let (Some(scene), Some(track)) = (scene, track) {
track.viewing_phrase = scene.clips[track_index]
track.player.phrase = scene.clips[track_index]
}
}
}
@ -92,17 +143,17 @@ impl<E: Engine> Arrangement<E> {
pub fn is_last_row (&self) -> bool {
let selected = self.selected;
(self.scenes.len() == 0 && (selected.is_mix() || selected.is_track())) || match selected {
ArrangerFocus::Scene(s) =>
ArrangementFocus::Scene(s) =>
s == self.scenes.len() - 1,
ArrangerFocus::Clip(_, s) =>
ArrangementFocus::Clip(_, s) =>
s == self.scenes.len() - 1,
_ => false
}
}
pub fn track (&self) -> Option<&Sequencer<E>> {
pub fn track (&self) -> Option<&PhrasePlayer<E>> {
self.selected.track().map(|t|self.tracks.get(t)).flatten()
}
pub fn track_mut (&mut self) -> Option<&mut Sequencer<E>> {
pub fn track_mut (&mut self) -> Option<&mut PhrasePlayer<E>> {
self.selected.track().map(|t|self.tracks.get_mut(t)).flatten()
}
pub fn track_next (&mut self) {
@ -111,10 +162,10 @@ impl<E: Engine> Arrangement<E> {
pub fn track_prev (&mut self) {
self.selected.track_prev()
}
pub fn track_add (&mut self, name: Option<&str>) -> Usually<&mut Sequencer<E>> {
pub fn track_add (&mut self, name: Option<&str>) -> Usually<&mut ArrangementTrack<E>> {
self.tracks.push(name.map_or_else(
|| Sequencer::new(&self.track_default_name()),
|name| Sequencer::new(name),
|| PhrasePlayer::new(&self.track_default_name()),
|name| PhrasePlayer::new(name),
));
let index = self.tracks.len() - 1;
Ok(&mut self.tracks[index])
@ -214,27 +265,32 @@ impl<E: Engine> Arrangement<E> {
});
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
#[derive(PartialEq, Clone, Copy)]
/// Represents the current user selection in the arranger
pub enum ArrangerFocus {
/** 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),
impl<E: Engine> ArrangementTrack<E> {
pub fn longest_name (tracks: &[Self]) -> usize {
tracks.iter()
.map(|s|s.name.read().unwrap().len())
.fold(0, usize::max)
}
pub fn clip_name_lengths (tracks: &[Self]) -> Vec<(usize, usize)> {
let mut total = 0;
let mut lengths: Vec<(usize, usize)> = tracks.iter().map(|track|{
let len = 4 + track.phrases
.iter()
.fold(track.name.read().unwrap().len(), |len, phrase|{
len.max(phrase.read().unwrap().name.read().unwrap().len())
});
total = total + len;
(len, total - len)
}).collect();
lengths.push((0, total));
lengths
}
}
/// Focus identification methods
impl ArrangerFocus {
impl ArrangementFocus {
pub fn description <E: Engine> (
&self,
tracks: &Vec<Sequencer<E>>,
tracks: &Vec<ArrangementTrack<E>>,
scenes: &Vec<Scene>,
) -> String {
format!("Selected: {}", match self {
@ -342,14 +398,8 @@ impl ArrangerFocus {
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
/// Display mode of arranger
#[derive(PartialEq)]
pub enum ArrangerViewMode { Horizontal, Vertical(usize) }
/// Arranger display mode can be cycled
impl ArrangerViewMode {
impl ArrangementViewMode {
/// Cycle arranger display mode
pub fn to_next (&mut self) {
*self = match self {
@ -361,39 +411,8 @@ impl ArrangerViewMode {
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
pub struct VerticalArranger<'a, E: Engine>(
pub &'a Arrangement<E>, pub usize
);
pub struct VerticalArrangerGrid<'a>(
pub u16, pub &'a [(usize, usize)], pub &'a [(usize, usize)]
);
pub struct VerticalArrangerCursor<'a>(
pub bool, pub ArrangerFocus, pub u16, pub &'a [(usize, usize)], pub &'a [(usize, usize)],
);
///////////////////////////////////////////////////////////////////////////////////////////////////
pub struct HorizontalArranger<'a, E: Engine>(
pub &'a Arrangement<E>
);
///////////////////////////////////////////////////////////////////////////////////////////////////
/// Appears on first run (i.e. if state dir is missing).
pub struct ArrangerRenameModal<E: Engine> {
_engine: std::marker::PhantomData<E>,
pub done: bool,
pub target: ArrangerFocus,
pub value: String,
pub result: Arc<RwLock<String>>,
pub cursor: usize
}
impl<E: Engine> ArrangerRenameModal<E> {
pub fn new (target: ArrangerFocus, value: &Arc<RwLock<String>>) -> Self {
pub fn new (target: ArrangementFocus, value: &Arc<RwLock<String>>) -> Self {
Self {
_engine: Default::default(),
done: false,
@ -404,52 +423,19 @@ impl<E: Engine> ArrangerRenameModal<E> {
}
}
}
impl<E: Engine + Send> Exit for ArrangerRenameModal<E> {
fn exited (&self) -> bool { self.done }
fn exit (&mut self) { self.done = true }
}
///////////////////////////////////////////////////////////////////////////////////////////////////
/// A collection of phrases to play on each track.
#[derive(Default)]
pub struct Scene {
pub name: Arc<RwLock<String>>,
pub clips: Vec<Option<usize>>,
}
impl Scene {
pub fn from_edn <'a, 'e> (args: &[Edn<'e>]) -> Usually<Self> {
let mut name = None;
let mut clips = vec![];
edn!(edn in args {
Edn::Map(map) => {
let key = map.get(&Edn::Key(":name"));
if let Some(Edn::Str(n)) = key {
name = Some(*n);
} else {
panic!("unexpected key in scene '{name:?}': {key:?}")
}
},
Edn::Symbol("_") => {
clips.push(None);
},
Edn::Int(i) => {
clips.push(Some(*i as usize));
},
_ => panic!("unexpected in scene '{name:?}': {edn:?}")
});
let scene = Self::new(name.unwrap_or(""), clips);
Ok(scene)
}
pub fn new (name: impl AsRef<str>, clips: impl AsRef<[Option<usize>]>) -> Self {
let name = Arc::new(RwLock::new(name.as_ref().into()));
let clips = clips.as_ref().iter().map(|x|x.clone()).collect();
Self { name, clips, }
Self {
name: Arc::new(RwLock::new(name.as_ref().into())),
clips: clips.as_ref().iter().map(|x|x.clone()).collect(),
}
}
/// Returns the pulse length of the longest phrase in the scene
pub fn pulses <E: Engine> (&self, tracks: &[Sequencer<E>]) -> usize {
pub fn pulses <E: Engine> (&self, tracks: &[ArrangementTrack<E>]) -> usize {
self.clips.iter().enumerate()
.filter_map(|(i, c)|c
.map(|c|tracks
@ -462,14 +448,29 @@ impl Scene {
.fold(0, |a, p|a.max(p.read().unwrap().length))
}
/// Returns true if all phrases in the scene are currently playing
pub fn is_playing <E: Engine> (&self, tracks: &[Sequencer<E>]) -> bool {
pub fn is_playing <E: Engine> (&self, tracks: &[ArrangementTrack<E>]) -> bool {
self.clips.iter().enumerate()
.all(|(track_index, phrase_index)|match phrase_index {
Some(i) => tracks
.get(track_index)
.map(|track|track.playing_phrase == Some(*i))
.map(|track|track.player.phrase == Some(*i))
.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)
}
}