wip: refactor pt.36 (10e) fewer structs, more field duplication

This commit is contained in:
🪞👃🪞 2024-11-15 02:21:30 +01:00
parent 7af5bbd02b
commit 92bcd19925
11 changed files with 136 additions and 65 deletions

View file

@ -1,10 +1,12 @@
use crate::*;
pub trait HasPlayer: HasJack {
fn player (&self) -> &MIDIPlayer;
fn player_mut (&mut self) -> &mut MIDIPlayer;
fn player (&self) -> &impl PlayerApi;
fn player_mut (&mut self) -> &mut impl PlayerApi;
}
pub trait PlayerApi: MidiInputApi + MidiOutputApi {}
pub trait HasMidiBuffer {
fn midi_buffer (&self) -> &mut Vec<Vec<Vec<u8>>>;
@ -25,9 +27,16 @@ pub trait HasMidiBuffer {
}
pub trait HasPhrase: ClockApi + PlayheadApi + HasMidiBuffer {
fn phrase (&self) -> &Option<(Instant, Arc<RwLock<Phrase>>)>;
fn next_phrase (&self) -> &Option<(Instant, Arc<RwLock<Phrase>>)>;
fn next_phrase_mut (&mut self) -> &mut Option<(Instant, Arc<RwLock<Phrase>>)>;
fn phrase (&self)
-> &Option<(Instant, Arc<RwLock<Phrase>>)>;
fn phrase_mut (&self)
-> &mut Option<(Instant, Arc<RwLock<Phrase>>)>;
fn next_phrase (&self)
-> &Option<(Instant, Arc<RwLock<Phrase>>)>;
fn next_phrase_mut (&mut self)
-> &mut Option<(Instant, Arc<RwLock<Phrase>>)>;
fn switchover (&mut self, scope: &ProcessScope) {
if self.is_rolling() {
let sample0 = scope.last_frame_time() as usize;
@ -41,7 +50,7 @@ pub trait HasPhrase: ClockApi + PlayheadApi + HasMidiBuffer {
let skipped = sample0 - start;
// Switch over to enqueued phrase
let started = Instant::from_sample(&self.timebase(), start as f64);
self.phrase = Some((started, phrase.clone()));
*self.phrase_mut() = Some((started, phrase.clone()));
// Unset enqueuement (TODO: where to implement looping?)
*self.next_phrase_mut() = None
}
@ -55,22 +64,22 @@ pub trait HasPhrase: ClockApi + PlayheadApi + HasMidiBuffer {
}
fn enqueue_next (&mut self, phrase: Option<&Arc<RwLock<Phrase>>>) {
let start = self.next_launch_pulse();
self.next_phrase = Some((
*self.next_phrase_mut() = Some((
Instant::from_pulse(&self.timebase(), start as f64),
phrase.map(|p|p.clone())
));
self.reset = true;
*self.reset_mut() = true;
}
fn pulses_since_start (&self) -> Option<f64> {
if let Some((started, Some(_))) = self.phrase.as_ref() {
Some(self.clock().current.pulse.get() - started.pulse.get())
if let Some((started, Some(_))) = self.phrase().as_ref() {
Some(self.current().pulse.get() - started.pulse.get())
} else {
None
}
}
}
pub trait MidiInputApi: ClockApi + PlayheadApi + HasMidiBuffer + HasPhrase {
pub trait MidiInputApi: PlayheadApi + HasMidiBuffer + HasPhrase {
fn midi_ins (&self) -> &Vec<Port<MidiIn>>;
fn midi_ins_mut (&self) -> &mut Vec<Port<MidiIn>>;
fn has_midi_ins (&self) -> bool {
@ -147,7 +156,7 @@ pub trait MidiInputApi: ClockApi + PlayheadApi + HasMidiBuffer + HasPhrase {
}
pub trait MidiOutputApi: ClockApi + PlayheadApi + HasMidiBuffer + HasPhrase {
pub trait MidiOutputApi: PlayheadApi + HasMidiBuffer + HasPhrase {
fn midi_outs (&self) -> &Vec<Port<MidiOut>>;
fn midi_outs_mut (&self) -> &mut Vec<Port<MidiOut>>;
@ -172,12 +181,12 @@ pub trait MidiOutputApi: ClockApi + PlayheadApi + HasMidiBuffer + HasPhrase {
// First sample to populate. Greater than 0 means that the first
// pulse of the phrase falls somewhere in the middle of the chunk.
let sample = started.sample.get() as usize;
let sample = sample + self.clock().started.read().unwrap().unwrap().0;
let sample = sample + self.started().read().unwrap().unwrap().0;
let sample = sample0.saturating_sub(sample);
// Iterator that emits sample (index into output buffer at which to write MIDI event)
// paired with pulse (index into phrase from which to take the MIDI event) for each
// sample of the output buffer that corresponds to a MIDI pulse.
let pulses = self.clock().timebase().pulses_between_samples(sample, sample + samples);
let pulses = self.timebase().pulses_between_samples(sample, sample + samples);
// Notes active during current chunk.
let notes = &mut self.notes_out().write().unwrap();
for (sample, pulse) in pulses {

View file

@ -34,11 +34,9 @@ impl<T: PlayheadApi> Command<T> for PlayheadCommand {
}
pub trait PlayheadApi: ClockApi {
fn transport (&self) -> &jack::Transport;
/// Playback state
fn playing (&self) -> &RwLock<Option<TransportState>>;
/// Global sample and usec at which playback started
fn started (&self) -> &RwLock<Option<(usize, usize)>>;

View file

@ -66,12 +66,12 @@ pub trait ArrangerSceneApi: Sized {
/// Returns true if all phrases in the scene are
/// currently playing on the given collection of tracks.
fn is_playing (&self, tracks: &[ArrangerTrack]) -> bool {
fn is_playing <T: ArrangerTrackApi> (&self, tracks: &[T]) -> bool {
self.clips().iter().any(|clip|clip.is_some()) && self.clips().iter().enumerate()
.all(|(track_index, clip)|match clip {
Some(clip) => tracks
.get(track_index)
.map(|track|if let Some((_, Some(phrase))) = &track.player.phrase {
.map(|track|if let Some((_, Some(phrase))) = track.player().phrase() {
*phrase.read().unwrap() == *clip.read().unwrap()
} else {
false

View file

@ -41,7 +41,7 @@ pub trait ArrangerTrackApi: Sized {
/// Identifying color of track
fn color (&self) -> ItemColor;
/// The MIDI player for the track
fn player (&self) -> MIDIPlayer;
fn player (&self) -> &impl PlayerApi;
fn longest_name (tracks: &[Self]) -> usize {
tracks.iter().map(|s|s.name().read().unwrap().len()).fold(0, usize::max)

View file

@ -10,15 +10,14 @@ pub(crate) use tek_core::jack::{
submod! {
//api_jack
api_clip
api_scene
api_track
api_clock
api_jack
api_player
api_playhead
api_pool
api_sequencer
//api_mixer
//api_channel
//api_plugin
@ -28,11 +27,11 @@ submod! {
//api_sampler_sample
//api_sampler_voice
model_scene
model_track
//model_scene
//model_track
//model_clock
model_phrase
model_player
//model_player
model_pool
}

View file

@ -5,7 +5,7 @@ pub struct MIDIPlayer {
/// Global timebase
pub clock: Arc<Clock>,
/// Start time and phrase being played
pub phrase: Option<(Instant, Option<Arc<RwLock<Phrase>>>)>,
pub play_phrase: Option<(Instant, Option<Arc<RwLock<Phrase>>>)>,
/// Start time and next phrase
pub next_phrase: Option<(Instant, Option<Arc<RwLock<Phrase>>>)>,
/// Play input through output.

View file

@ -1,15 +1,5 @@
use crate::*;
#[derive(Default, Debug, Clone)]
pub struct ArrangerScene {
/// 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: ItemColor,
}
impl ArrangerScene {
//TODO

View file

@ -1,13 +1 @@
use crate::*;
#[derive(Debug)]
pub struct ArrangerTrack {
/// Name of track
pub name: Arc<RwLock<String>>,
/// Preferred width of track column
pub width: usize,
/// Identifying color of track
pub color: ItemColor,
/// The MIDI player for the track
pub player: MIDIPlayer
}

View file

@ -244,11 +244,15 @@ impl Command<ArrangerApp<Tui>> for ArrangerViewCommand {
/// Root view for standalone `tek_arranger`
pub struct ArrangerView<E: Engine> {
jack: Arc<RwLock<JackClient>>,
clock: Arc<Clock>,
playing: RwLock<Option<TransportState>>,
started: RwLock<Option<(usize, usize)>>,
current: Instant,
quant: Quantize,
sync: LaunchSync,
transport: jack::Transport,
metronome: bool,
transport: TransportModel,
phrases: Vec<Arc<RwLock<Phrase>>>,
phrase: usize,
tracks: Vec<ArrangerTrack>,
scenes: Vec<ArrangerScene>,
name: Arc<RwLock<String>>,
@ -257,6 +261,7 @@ pub struct ArrangerView<E: Engine> {
selected: ArrangerSelection,
mode: ArrangerMode,
color: ItemColor,
editor: PhraseEditor<E>,
focused: bool,
entered: bool,
size: Measure<E>,
@ -833,14 +838,14 @@ impl Content for ArrangerStatusBar {
type Engine = Tui;
fn content (&self) -> impl Widget<Engine = Tui> {
let label = match self {
Self::Transport => "TRANSPORT",
Self::Transport => "TRANSPORT",
Self::ArrangerMix => "PROJECT",
Self::ArrangerTrack => "TRACK",
Self::ArrangerScene => "SCENE",
Self::ArrangerClip => "CLIP",
Self::PhrasePool => "SEQ LIST",
Self::PhraseView => "VIEW SEQ",
Self::PhraseEdit => "EDIT SEQ",
Self::PhrasePool => "SEQ LIST",
Self::PhraseView => "VIEW SEQ",
Self::PhraseEdit => "EDIT SEQ",
};
let status_bar_bg = TuiTheme::status_bar_bg();
let mode_bg = TuiTheme::mode_bg();
@ -1362,3 +1367,49 @@ pub fn arranger_content_horizontal (
)
)
}
#[derive(Default, Debug, Clone)]
pub struct ArrangerScene {
/// 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: ItemColor,
}
#[derive(Debug)]
pub struct ArrangerTrack {
/// Name of track
pub name: Arc<RwLock<String>>,
/// Preferred width of track column
pub width: usize,
/// Identifying color of track
pub color: ItemColor,
/// The MIDI player for the track
pub player: MIDIPlayer
/// Start time and phrase being played
play_phrase: Option<(Instant, Option<Arc<RwLock<Phrase>>>)>,
/// Start time and next phrase
next_phrase: Option<(Instant, Option<Arc<RwLock<Phrase>>>)>,
/// Play input through output.
monitoring: bool,
/// Write input to sequence.
recording: bool,
/// Overdub input to sequence.
overdub: bool,
/// Send all notes off
reset: bool, // TODO?: after Some(nframes)
/// Record from MIDI ports to current sequence.
midi_inputs: Vec<Port<MidiIn>>,
/// Play from current sequence to MIDI ports
midi_outputs: Vec<Port<MidiOut>>,
/// MIDI output buffer
midi_note: Vec<u8>,
/// MIDI output buffer
midi_chunk: Vec<Vec<Vec<u8>>>,
/// Notes currently held at input
notes_in: Arc<RwLock<[bool; 128]>>,
/// Notes currently held at output
notes_out: Arc<RwLock<[bool; 128]>>,
}

View file

@ -72,15 +72,43 @@ impl InputToCommand<Tui, SequencerApp<Tui>> for SequencerAppCommand {
/// Root view for standalone `tek_sequencer`.
pub struct SequencerView<E: Engine> {
jack: Arc<RwLock<JackClient>>,
clock: Arc<Clock>,
transport: jack::Transport,
metronome: bool,
phrases: Vec<Arc<RwLock<Phrase>>>,
player: MIDIPlayer,
phrases: PhrasePoolView<E>,
editor: PhraseEditor<E>,
split: u16,
jack: Arc<RwLock<JackClient>>,
playing: RwLock<Option<TransportState>>,
started: RwLock<Option<(usize, usize)>>,
current: Instant,
quant: Quantize,
sync: LaunchSync,
clock: Arc<Clock>,
transport: jack::Transport,
metronome: bool,
phrases: Vec<Arc<RwLock<Phrase>>>,
view_phrase: usize,
editor: PhraseEditor<E>,
split: u16,
/// Start time and phrase being played
play_phrase: Option<(Instant, Option<Arc<RwLock<Phrase>>>)>,
/// Start time and next phrase
next_phrase: Option<(Instant, Option<Arc<RwLock<Phrase>>>)>,
/// Play input through output.
monitoring: bool,
/// Write input to sequence.
recording: bool,
/// Overdub input to sequence.
overdub: bool,
/// Send all notes off
reset: bool, // TODO?: after Some(nframes)
/// Record from MIDI ports to current sequence.
midi_inputs: Vec<Port<MidiIn>>,
/// Play from current sequence to MIDI ports
midi_outputs: Vec<Port<MidiOut>>,
/// MIDI output buffer
midi_note: Vec<u8>,
/// MIDI output buffer
midi_chunk: Vec<Vec<Vec<u8>>>,
/// Notes currently held at input
notes_in: Arc<RwLock<[bool; 128]>>,
/// Notes currently held at output
notes_out: Arc<RwLock<[bool; 128]>>,
}
/// Sections in the sequencer app that may be focused

View file

@ -129,8 +129,16 @@ impl Command<TransportApp<Tui>> for TransportCommand {
pub struct TransportView<E: Engine> {
_engine: PhantomData<E>,
jack: Arc<RwLock<JackClient>>,
/// Current sample rate, tempo, and PPQ.
clock: Arc<Clock>,
/// Playback state
playing: RwLock<Option<TransportState>>,
/// Global sample and usec at which playback started
started: RwLock<Option<(usize, usize)>>,
/// Current moment in time
current: Instant,
/// Note quantization factor
quant: Quantize,
/// Launch quantization factor
sync: LaunchSync,
/// JACK transport handle.
transport: jack::Transport,
/// Enable metronome?