From e34a8953575027399b02cdca005ef8ee1dab0777 Mon Sep 17 00:00:00 2001 From: unspeaker Date: Fri, 13 Dec 2024 00:06:25 +0100 Subject: [PATCH] move PhrasePlayerModel to api/ --- crates/tek/src/api/player.rs | 209 +++++++++++++++++++++++----- crates/tek/src/core.rs | 2 - crates/tek/src/core/edn.rs | 3 +- crates/tek/src/core/pitch.rs | 1 - crates/tek/src/layout.rs | 2 - crates/tek/src/lib.rs | 2 +- crates/tek/src/tui.rs | 1 - crates/tek/src/tui/app_sequencer.rs | 2 +- crates/tek/src/tui/app_transport.rs | 10 +- crates/tek/src/tui/phrase_player.rs | 146 ------------------- 10 files changed, 185 insertions(+), 193 deletions(-) delete mode 100644 crates/tek/src/tui/phrase_player.rs diff --git a/crates/tek/src/api/player.rs b/crates/tek/src/api/player.rs index f1a6809e..4f338961 100644 --- a/crates/tek/src/api/player.rs +++ b/crates/tek/src/api/player.rs @@ -5,42 +5,102 @@ pub trait HasPlayer { fn player_mut (&mut self) -> &mut impl MidiPlayerApi; } -pub trait MidiPlayerApi: MidiRecordApi + MidiPlaybackApi + Send + Sync {} +/// Contains state for playing a phrase +pub struct PhrasePlayerModel { + /// State of clock and playhead + pub(crate) clock: ClockModel, + /// Start time and phrase being played + pub(crate) play_phrase: Option<(Moment, Option>>)>, + /// Start time and next phrase + pub(crate) next_phrase: Option<(Moment, Option>>)>, + /// Play input through output. + pub(crate) monitoring: bool, + /// Write input to sequence. + pub(crate) recording: bool, + /// Overdub input to sequence. + pub(crate) overdub: bool, + /// Send all notes off + pub(crate) reset: bool, // TODO?: after Some(nframes) + /// Record from MIDI ports to current sequence. + pub midi_ins: Vec>, + /// Play from current sequence to MIDI ports + pub midi_outs: Vec>, + /// Notes currently held at input + pub(crate) notes_in: Arc>, + /// Notes currently held at output + pub(crate) notes_out: Arc>, + /// MIDI output buffer + pub note_buf: Vec, +} -pub trait HasPlayPhrase: HasClock { - fn reset (&self) -> bool; - fn reset_mut (&mut self) -> &mut bool; - fn play_phrase (&self) -> &Option<(Moment, Option>>)>; - fn play_phrase_mut (&mut self) -> &mut Option<(Moment, Option>>)>; - fn next_phrase (&self) -> &Option<(Moment, Option>>)>; - fn next_phrase_mut (&mut self) -> &mut Option<(Moment, Option>>)>; - fn pulses_since_start (&self) -> Option { - if let Some((started, Some(_))) = self.play_phrase().as_ref() { - let elapsed = self.clock().playhead.pulse.get() - started.pulse.get(); - Some(elapsed) - } else { - None - } - } - fn pulses_since_start_looped (&self) -> Option { - if let Some((started, Some(phrase))) = self.play_phrase().as_ref() { - let elapsed = self.clock().playhead.pulse.get() - started.pulse.get(); - let length = phrase.read().unwrap().length.max(1); // prevent div0 on empty phrase - let elapsed = (elapsed as usize % length) as f64; - Some(elapsed) - } else { - None - } - } - fn enqueue_next (&mut self, phrase: Option<&Arc>>) { - let start = self.clock().next_launch_pulse() as f64; - let instant = Moment::from_pulse(&self.clock().timebase(), start); - let phrase = phrase.map(|p|p.clone()); - *self.next_phrase_mut() = Some((instant, phrase)); - *self.reset_mut() = true; +impl std::fmt::Debug for PhrasePlayerModel { + fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + f.debug_struct("PhrasePlayerModel") + .field("clock", &self.clock) + .field("play_phrase", &self.play_phrase) + .field("next_phrase", &self.next_phrase) + .finish() } } +impl From<&ClockModel> for PhrasePlayerModel { + fn from (clock: &ClockModel) -> Self { + Self { + clock: clock.clone(), + midi_ins: vec![], + midi_outs: vec![], + note_buf: vec![0;8], + reset: true, + recording: false, + monitoring: false, + overdub: false, + play_phrase: None, + next_phrase: None, + notes_in: RwLock::new([false;128]).into(), + notes_out: RwLock::new([false;128]).into(), + } + } +} + +impl From<(&ClockModel, &Arc>)> for PhrasePlayerModel { + fn from ((clock, phrase): (&ClockModel, &Arc>)) -> Self { + let mut model = Self::from(clock); + model.play_phrase = Some((Moment::zero(&clock.timebase), Some(phrase.clone()))); + model + } +} + +impl HasClock for PhrasePlayerModel { + fn clock (&self) -> &ClockModel { + &self.clock + } +} + +impl HasMidiIns for PhrasePlayerModel { + fn midi_ins (&self) -> &Vec> { + &self.midi_ins + } + fn midi_ins_mut (&mut self) -> &mut Vec> { + &mut self.midi_ins + } +} + +impl HasMidiOuts for PhrasePlayerModel { + fn midi_outs (&self) -> &Vec> { + &self.midi_outs + } + fn midi_outs_mut (&mut self) -> &mut Vec> { + &mut self.midi_outs + } + fn midi_note (&mut self) -> &mut Vec { + &mut self.note_buf + } +} + +pub trait MidiPlayerApi: MidiRecordApi + MidiPlaybackApi + Send + Sync {} + +impl MidiPlayerApi for PhrasePlayerModel {} + pub trait MidiRecordApi: HasClock + HasPlayPhrase + HasMidiIns { fn notes_in (&self) -> &Arc>; @@ -115,6 +175,30 @@ pub trait MidiRecordApi: HasClock + HasPlayPhrase + HasMidiIns { } } +impl MidiRecordApi for PhrasePlayerModel { + fn recording (&self) -> bool { + self.recording + } + fn recording_mut (&mut self) -> &mut bool { + &mut self.recording + } + fn monitoring (&self) -> bool { + self.monitoring + } + fn monitoring_mut (&mut self) -> &mut bool { + &mut self.monitoring + } + fn overdub (&self) -> bool { + self.overdub + } + fn overdub_mut (&mut self) -> &mut bool { + &mut self.overdub + } + fn notes_in (&self) -> &Arc> { + &self.notes_in + } +} + pub trait MidiPlaybackApi: HasPlayPhrase + HasClock + HasMidiOuts { fn notes_out (&self) -> &Arc>; @@ -248,6 +332,67 @@ pub trait MidiPlaybackApi: HasPlayPhrase + HasClock + HasMidiOuts { } } +impl MidiPlaybackApi for PhrasePlayerModel { + fn notes_out (&self) -> &Arc> { + &self.notes_in + } +} + +pub trait HasPlayPhrase: HasClock { + fn reset (&self) -> bool; + fn reset_mut (&mut self) -> &mut bool; + fn play_phrase (&self) -> &Option<(Moment, Option>>)>; + fn play_phrase_mut (&mut self) -> &mut Option<(Moment, Option>>)>; + fn next_phrase (&self) -> &Option<(Moment, Option>>)>; + fn next_phrase_mut (&mut self) -> &mut Option<(Moment, Option>>)>; + fn pulses_since_start (&self) -> Option { + if let Some((started, Some(_))) = self.play_phrase().as_ref() { + let elapsed = self.clock().playhead.pulse.get() - started.pulse.get(); + Some(elapsed) + } else { + None + } + } + fn pulses_since_start_looped (&self) -> Option { + if let Some((started, Some(phrase))) = self.play_phrase().as_ref() { + let elapsed = self.clock().playhead.pulse.get() - started.pulse.get(); + let length = phrase.read().unwrap().length.max(1); // prevent div0 on empty phrase + let elapsed = (elapsed as usize % length) as f64; + Some(elapsed) + } else { + None + } + } + fn enqueue_next (&mut self, phrase: Option<&Arc>>) { + let start = self.clock().next_launch_pulse() as f64; + let instant = Moment::from_pulse(&self.clock().timebase(), start); + let phrase = phrase.map(|p|p.clone()); + *self.next_phrase_mut() = Some((instant, phrase)); + *self.reset_mut() = true; + } +} + +impl HasPlayPhrase for PhrasePlayerModel { + fn reset (&self) -> bool { + self.reset + } + fn reset_mut (&mut self) -> &mut bool { + &mut self.reset + } + fn play_phrase (&self) -> &Option<(Moment, Option>>)> { + &self.play_phrase + } + fn play_phrase_mut (&mut self) -> &mut Option<(Moment, Option>>)> { + &mut self.play_phrase + } + fn next_phrase (&self) -> &Option<(Moment, Option>>)> { + &self.next_phrase + } + fn next_phrase_mut (&mut self) -> &mut Option<(Moment, Option>>)> { + &mut self.next_phrase + } +} + /// Add "all notes off" to the start of a buffer. pub fn all_notes_off (output: &mut [Vec>]) { let mut buf = vec![]; diff --git a/crates/tek/src/core.rs b/crates/tek/src/core.rs index 8a3cd53f..c22c504e 100644 --- a/crates/tek/src/core.rs +++ b/crates/tek/src/core.rs @@ -1,5 +1,3 @@ -use crate::*; - mod audio; pub(crate) use audio::*; mod color; pub(crate) use color::*; mod command; pub(crate) use command::*; diff --git a/crates/tek/src/core/edn.rs b/crates/tek/src/core/edn.rs index 2709043e..b2c50a53 100644 --- a/crates/tek/src/core/edn.rs +++ b/crates/tek/src/core/edn.rs @@ -1,4 +1,5 @@ -pub use clojure_reader::{edn::{read, Edn}, error::Error as EdnError}; +pub use clojure_reader::edn::Edn; +//pub use clojure_reader::{edn::{read, Edn}, error::Error as EdnError}; /// EDN parsing helper. #[macro_export] macro_rules! edn { diff --git a/crates/tek/src/core/pitch.rs b/crates/tek/src/core/pitch.rs index 6a2ac714..64a2c6d8 100644 --- a/crates/tek/src/core/pitch.rs +++ b/crates/tek/src/core/pitch.rs @@ -1,5 +1,4 @@ use crate::*; -use midly::num::u7; pub fn to_note_name (n: usize) -> &'static str { if n > 127 { diff --git a/crates/tek/src/layout.rs b/crates/tek/src/layout.rs index 168e2022..6b4f538f 100644 --- a/crates/tek/src/layout.rs +++ b/crates/tek/src/layout.rs @@ -1,5 +1,3 @@ -use crate::*; - mod align; pub(crate) use align::*; mod bsp; pub(crate) use bsp::*; mod cond; pub(crate) use cond::*; diff --git a/crates/tek/src/lib.rs b/crates/tek/src/lib.rs index 6faafea0..f13af271 100644 --- a/crates/tek/src/lib.rs +++ b/crates/tek/src/lib.rs @@ -19,7 +19,7 @@ pub(crate) use ratatui::{ pub(crate) use jack; pub(crate) use jack::{ Client, ProcessScope, Control, CycleTimes, - Port, PortSpec, MidiIn, MidiOut, AudioIn, AudioOut, Unowned, + Port, PortSpec, MidiIn, MidiOut, AudioOut, Unowned, Transport, TransportState, MidiIter, RawMidi, contrib::ClosureProcessHandler, }; diff --git a/crates/tek/src/tui.rs b/crates/tek/src/tui.rs index 1a962cc1..eecc74af 100644 --- a/crates/tek/src/tui.rs +++ b/crates/tek/src/tui.rs @@ -22,7 +22,6 @@ mod piano_horizontal; pub(crate) use piano_horizontal::*; mod phrase_length; pub(crate) use phrase_length::*; mod phrase_rename; pub(crate) use phrase_rename::*; mod phrase_list; pub(crate) use phrase_list::*; -mod phrase_player; pub(crate) use phrase_player::*; mod port_select; pub(crate) use port_select::*; //////////////////////////////////////////////////////// diff --git a/crates/tek/src/tui/app_sequencer.rs b/crates/tek/src/tui/app_sequencer.rs index 218d8571..f1a22941 100644 --- a/crates/tek/src/tui/app_sequencer.rs +++ b/crates/tek/src/tui/app_sequencer.rs @@ -1,5 +1,5 @@ use crate::{*, api::ClockCommand::{Play, Pause}}; -use KeyCode::{Tab, BackTab, Char, Enter, Esc}; +use KeyCode::{Tab, BackTab, Char}; use SequencerCommand::*; use SequencerFocus::*; use PhraseCommand::*; diff --git a/crates/tek/src/tui/app_transport.rs b/crates/tek/src/tui/app_transport.rs index 258757d1..f618eb48 100644 --- a/crates/tek/src/tui/app_transport.rs +++ b/crates/tek/src/tui/app_transport.rs @@ -127,12 +127,10 @@ render!(|self: TransportView|{ struct PlayPause(bool); render!(|self: PlayPause|Tui::bg( if self.0{Color::Rgb(0,128,0)}else{Color::Rgb(128,64,0)}, - Tui::outset_x(1, Tui::fixed_x(9, col!(|add|{ - if self.0 { - add(&col!([Tui::fg(Color::Rgb(0, 255, 0), "▶ PLAYING"), ""])) - } else { - add(&col!(["", Tui::fg(Color::Rgb(255, 128, 0), "⏹ STOPPED")])) - } + Tui::outset_x(1, Tui::fixed_x(9, col!(|add|if self.0 { + add(&Tui::fg(Color::Rgb(0, 255, 0), col!(["▶ PLAYING", "▒ ▒ ▒ ▒ ▒"]))) + } else { + add(&Tui::fg(Color::Rgb(255, 128, 0), col!(["▒ ▒ ▒ ▒ ▒", "⏹ STOPPED"]))) }))) )); diff --git a/crates/tek/src/tui/phrase_player.rs b/crates/tek/src/tui/phrase_player.rs deleted file mode 100644 index 0d70dd70..00000000 --- a/crates/tek/src/tui/phrase_player.rs +++ /dev/null @@ -1,146 +0,0 @@ -use crate::*; - -/// Contains state for playing a phrase -pub struct PhrasePlayerModel { - /// State of clock and playhead - pub(crate) clock: ClockModel, - /// Start time and phrase being played - pub(crate) play_phrase: Option<(Moment, Option>>)>, - /// Start time and next phrase - pub(crate) next_phrase: Option<(Moment, Option>>)>, - /// Play input through output. - pub(crate) monitoring: bool, - /// Write input to sequence. - pub(crate) recording: bool, - /// Overdub input to sequence. - pub(crate) overdub: bool, - /// Send all notes off - pub(crate) reset: bool, // TODO?: after Some(nframes) - /// Record from MIDI ports to current sequence. - pub midi_ins: Vec>, - /// Play from current sequence to MIDI ports - pub midi_outs: Vec>, - /// Notes currently held at input - pub(crate) notes_in: Arc>, - /// Notes currently held at output - pub(crate) notes_out: Arc>, - /// MIDI output buffer - pub note_buf: Vec, -} - -impl std::fmt::Debug for PhrasePlayerModel { - fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { - f.debug_struct("PhrasePlayerModel") - .field("clock", &self.clock) - .field("play_phrase", &self.play_phrase) - .field("next_phrase", &self.next_phrase) - .finish() - } -} - -impl From<&ClockModel> for PhrasePlayerModel { - fn from (clock: &ClockModel) -> Self { - Self { - clock: clock.clone(), - midi_ins: vec![], - midi_outs: vec![], - note_buf: vec![0;8], - reset: true, - recording: false, - monitoring: false, - overdub: false, - play_phrase: None, - next_phrase: None, - notes_in: RwLock::new([false;128]).into(), - notes_out: RwLock::new([false;128]).into(), - } - } -} - -impl From<(&ClockModel, &Arc>)> for PhrasePlayerModel { - fn from ((clock, phrase): (&ClockModel, &Arc>)) -> Self { - let mut model = Self::from(clock); - model.play_phrase = Some((Moment::zero(&clock.timebase), Some(phrase.clone()))); - model - } -} - -impl HasClock for PhrasePlayerModel { - fn clock (&self) -> &ClockModel { - &self.clock - } -} - -impl HasPlayPhrase for PhrasePlayerModel { - fn reset (&self) -> bool { - self.reset - } - fn reset_mut (&mut self) -> &mut bool { - &mut self.reset - } - fn play_phrase (&self) -> &Option<(Moment, Option>>)> { - &self.play_phrase - } - fn play_phrase_mut (&mut self) -> &mut Option<(Moment, Option>>)> { - &mut self.play_phrase - } - fn next_phrase (&self) -> &Option<(Moment, Option>>)> { - &self.next_phrase - } - fn next_phrase_mut (&mut self) -> &mut Option<(Moment, Option>>)> { - &mut self.next_phrase - } -} - -impl HasMidiIns for PhrasePlayerModel { - fn midi_ins (&self) -> &Vec> { - &self.midi_ins - } - fn midi_ins_mut (&mut self) -> &mut Vec> { - &mut self.midi_ins - } -} - -impl MidiRecordApi for PhrasePlayerModel { - fn recording (&self) -> bool { - self.recording - } - fn recording_mut (&mut self) -> &mut bool { - &mut self.recording - } - fn monitoring (&self) -> bool { - self.monitoring - } - fn monitoring_mut (&mut self) -> &mut bool { - &mut self.monitoring - } - fn overdub (&self) -> bool { - self.overdub - } - fn overdub_mut (&mut self) -> &mut bool { - &mut self.overdub - } - fn notes_in (&self) -> &Arc> { - &self.notes_in - } -} - -impl HasMidiOuts for PhrasePlayerModel { - fn midi_outs (&self) -> &Vec> { - &self.midi_outs - } - fn midi_outs_mut (&mut self) -> &mut Vec> { - &mut self.midi_outs - } - fn midi_note (&mut self) -> &mut Vec { - &mut self.note_buf - } -} - -impl MidiPlaybackApi for PhrasePlayerModel { - fn notes_out (&self) -> &Arc> { - &self.notes_in - } -} - -impl MidiPlayerApi for PhrasePlayerModel {}