mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
wip: refactor pt.36 (10e) fewer structs, more field duplication
This commit is contained in:
parent
7af5bbd02b
commit
92bcd19925
11 changed files with 136 additions and 65 deletions
|
|
@ -1,10 +1,12 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub trait HasPlayer: HasJack {
|
pub trait HasPlayer: HasJack {
|
||||||
fn player (&self) -> &MIDIPlayer;
|
fn player (&self) -> &impl PlayerApi;
|
||||||
fn player_mut (&mut self) -> &mut MIDIPlayer;
|
fn player_mut (&mut self) -> &mut impl PlayerApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait PlayerApi: MidiInputApi + MidiOutputApi {}
|
||||||
|
|
||||||
pub trait HasMidiBuffer {
|
pub trait HasMidiBuffer {
|
||||||
fn midi_buffer (&self) -> &mut Vec<Vec<Vec<u8>>>;
|
fn midi_buffer (&self) -> &mut Vec<Vec<Vec<u8>>>;
|
||||||
|
|
||||||
|
|
@ -25,9 +27,16 @@ pub trait HasMidiBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait HasPhrase: ClockApi + PlayheadApi + HasMidiBuffer {
|
pub trait HasPhrase: ClockApi + PlayheadApi + HasMidiBuffer {
|
||||||
fn phrase (&self) -> &Option<(Instant, Arc<RwLock<Phrase>>)>;
|
fn phrase (&self)
|
||||||
fn next_phrase (&self) -> &Option<(Instant, Arc<RwLock<Phrase>>)>;
|
-> &Option<(Instant, Arc<RwLock<Phrase>>)>;
|
||||||
fn next_phrase_mut (&mut self) -> &mut 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) {
|
fn switchover (&mut self, scope: &ProcessScope) {
|
||||||
if self.is_rolling() {
|
if self.is_rolling() {
|
||||||
let sample0 = scope.last_frame_time() as usize;
|
let sample0 = scope.last_frame_time() as usize;
|
||||||
|
|
@ -41,7 +50,7 @@ pub trait HasPhrase: ClockApi + PlayheadApi + HasMidiBuffer {
|
||||||
let skipped = sample0 - start;
|
let skipped = sample0 - start;
|
||||||
// Switch over to enqueued phrase
|
// Switch over to enqueued phrase
|
||||||
let started = Instant::from_sample(&self.timebase(), start as f64);
|
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?)
|
// Unset enqueuement (TODO: where to implement looping?)
|
||||||
*self.next_phrase_mut() = None
|
*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>>>) {
|
fn enqueue_next (&mut self, phrase: Option<&Arc<RwLock<Phrase>>>) {
|
||||||
let start = self.next_launch_pulse();
|
let start = self.next_launch_pulse();
|
||||||
self.next_phrase = Some((
|
*self.next_phrase_mut() = Some((
|
||||||
Instant::from_pulse(&self.timebase(), start as f64),
|
Instant::from_pulse(&self.timebase(), start as f64),
|
||||||
phrase.map(|p|p.clone())
|
phrase.map(|p|p.clone())
|
||||||
));
|
));
|
||||||
self.reset = true;
|
*self.reset_mut() = true;
|
||||||
}
|
}
|
||||||
fn pulses_since_start (&self) -> Option<f64> {
|
fn pulses_since_start (&self) -> Option<f64> {
|
||||||
if let Some((started, Some(_))) = self.phrase.as_ref() {
|
if let Some((started, Some(_))) = self.phrase().as_ref() {
|
||||||
Some(self.clock().current.pulse.get() - started.pulse.get())
|
Some(self.current().pulse.get() - started.pulse.get())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MidiInputApi: ClockApi + PlayheadApi + HasMidiBuffer + HasPhrase {
|
pub trait MidiInputApi: PlayheadApi + HasMidiBuffer + HasPhrase {
|
||||||
fn midi_ins (&self) -> &Vec<Port<MidiIn>>;
|
fn midi_ins (&self) -> &Vec<Port<MidiIn>>;
|
||||||
fn midi_ins_mut (&self) -> &mut Vec<Port<MidiIn>>;
|
fn midi_ins_mut (&self) -> &mut Vec<Port<MidiIn>>;
|
||||||
fn has_midi_ins (&self) -> bool {
|
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 (&self) -> &Vec<Port<MidiOut>>;
|
||||||
|
|
||||||
fn midi_outs_mut (&self) -> &mut 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
|
// First sample to populate. Greater than 0 means that the first
|
||||||
// pulse of the phrase falls somewhere in the middle of the chunk.
|
// pulse of the phrase falls somewhere in the middle of the chunk.
|
||||||
let sample = started.sample.get() as usize;
|
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);
|
let sample = sample0.saturating_sub(sample);
|
||||||
// Iterator that emits sample (index into output buffer at which to write MIDI event)
|
// 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
|
// 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.
|
// 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.
|
// Notes active during current chunk.
|
||||||
let notes = &mut self.notes_out().write().unwrap();
|
let notes = &mut self.notes_out().write().unwrap();
|
||||||
for (sample, pulse) in pulses {
|
for (sample, pulse) in pulses {
|
||||||
|
|
@ -34,11 +34,9 @@ impl<T: PlayheadApi> Command<T> for PlayheadCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PlayheadApi: ClockApi {
|
pub trait PlayheadApi: ClockApi {
|
||||||
|
|
||||||
fn transport (&self) -> &jack::Transport;
|
fn transport (&self) -> &jack::Transport;
|
||||||
|
/// Playback state
|
||||||
fn playing (&self) -> &RwLock<Option<TransportState>>;
|
fn playing (&self) -> &RwLock<Option<TransportState>>;
|
||||||
|
|
||||||
/// Global sample and usec at which playback started
|
/// Global sample and usec at which playback started
|
||||||
fn started (&self) -> &RwLock<Option<(usize, usize)>>;
|
fn started (&self) -> &RwLock<Option<(usize, usize)>>;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,12 +66,12 @@ pub trait ArrangerSceneApi: Sized {
|
||||||
|
|
||||||
/// Returns true if all phrases in the scene are
|
/// Returns true if all phrases in the scene are
|
||||||
/// currently playing on the given collection of tracks.
|
/// 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()
|
self.clips().iter().any(|clip|clip.is_some()) && self.clips().iter().enumerate()
|
||||||
.all(|(track_index, clip)|match clip {
|
.all(|(track_index, clip)|match clip {
|
||||||
Some(clip) => tracks
|
Some(clip) => tracks
|
||||||
.get(track_index)
|
.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()
|
*phrase.read().unwrap() == *clip.read().unwrap()
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ pub trait ArrangerTrackApi: Sized {
|
||||||
/// Identifying color of track
|
/// Identifying color of track
|
||||||
fn color (&self) -> ItemColor;
|
fn color (&self) -> ItemColor;
|
||||||
/// The MIDI player for the track
|
/// The MIDI player for the track
|
||||||
fn player (&self) -> MIDIPlayer;
|
fn player (&self) -> &impl PlayerApi;
|
||||||
|
|
||||||
fn longest_name (tracks: &[Self]) -> usize {
|
fn longest_name (tracks: &[Self]) -> usize {
|
||||||
tracks.iter().map(|s|s.name().read().unwrap().len()).fold(0, usize::max)
|
tracks.iter().map(|s|s.name().read().unwrap().len()).fold(0, usize::max)
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,14 @@ pub(crate) use tek_core::jack::{
|
||||||
|
|
||||||
submod! {
|
submod! {
|
||||||
//api_jack
|
//api_jack
|
||||||
|
|
||||||
api_clip
|
api_clip
|
||||||
api_scene
|
api_scene
|
||||||
api_track
|
api_track
|
||||||
api_clock
|
api_clock
|
||||||
api_jack
|
api_jack
|
||||||
|
api_player
|
||||||
api_playhead
|
api_playhead
|
||||||
api_pool
|
api_pool
|
||||||
api_sequencer
|
|
||||||
//api_mixer
|
//api_mixer
|
||||||
//api_channel
|
//api_channel
|
||||||
//api_plugin
|
//api_plugin
|
||||||
|
|
@ -28,11 +27,11 @@ submod! {
|
||||||
//api_sampler_sample
|
//api_sampler_sample
|
||||||
//api_sampler_voice
|
//api_sampler_voice
|
||||||
|
|
||||||
model_scene
|
//model_scene
|
||||||
model_track
|
//model_track
|
||||||
//model_clock
|
//model_clock
|
||||||
model_phrase
|
model_phrase
|
||||||
model_player
|
//model_player
|
||||||
model_pool
|
model_pool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ pub struct MIDIPlayer {
|
||||||
/// Global timebase
|
/// Global timebase
|
||||||
pub clock: Arc<Clock>,
|
pub clock: Arc<Clock>,
|
||||||
/// Start time and phrase being played
|
/// 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
|
/// Start time and next phrase
|
||||||
pub next_phrase: Option<(Instant, Option<Arc<RwLock<Phrase>>>)>,
|
pub next_phrase: Option<(Instant, Option<Arc<RwLock<Phrase>>>)>,
|
||||||
/// Play input through output.
|
/// Play input through output.
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,5 @@
|
||||||
use crate::*;
|
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 {
|
impl ArrangerScene {
|
||||||
|
|
||||||
//TODO
|
//TODO
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1 @@
|
||||||
use crate::*;
|
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
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -244,11 +244,15 @@ impl Command<ArrangerApp<Tui>> for ArrangerViewCommand {
|
||||||
/// Root view for standalone `tek_arranger`
|
/// Root view for standalone `tek_arranger`
|
||||||
pub struct ArrangerView<E: Engine> {
|
pub struct ArrangerView<E: Engine> {
|
||||||
jack: Arc<RwLock<JackClient>>,
|
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,
|
transport: jack::Transport,
|
||||||
metronome: bool,
|
metronome: bool,
|
||||||
transport: TransportModel,
|
|
||||||
phrases: Vec<Arc<RwLock<Phrase>>>,
|
phrases: Vec<Arc<RwLock<Phrase>>>,
|
||||||
|
phrase: usize,
|
||||||
tracks: Vec<ArrangerTrack>,
|
tracks: Vec<ArrangerTrack>,
|
||||||
scenes: Vec<ArrangerScene>,
|
scenes: Vec<ArrangerScene>,
|
||||||
name: Arc<RwLock<String>>,
|
name: Arc<RwLock<String>>,
|
||||||
|
|
@ -257,6 +261,7 @@ pub struct ArrangerView<E: Engine> {
|
||||||
selected: ArrangerSelection,
|
selected: ArrangerSelection,
|
||||||
mode: ArrangerMode,
|
mode: ArrangerMode,
|
||||||
color: ItemColor,
|
color: ItemColor,
|
||||||
|
editor: PhraseEditor<E>,
|
||||||
focused: bool,
|
focused: bool,
|
||||||
entered: bool,
|
entered: bool,
|
||||||
size: Measure<E>,
|
size: Measure<E>,
|
||||||
|
|
@ -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]>>,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,14 +73,42 @@ impl InputToCommand<Tui, SequencerApp<Tui>> for SequencerAppCommand {
|
||||||
/// Root view for standalone `tek_sequencer`.
|
/// Root view for standalone `tek_sequencer`.
|
||||||
pub struct SequencerView<E: Engine> {
|
pub struct SequencerView<E: Engine> {
|
||||||
jack: Arc<RwLock<JackClient>>,
|
jack: Arc<RwLock<JackClient>>,
|
||||||
|
playing: RwLock<Option<TransportState>>,
|
||||||
|
started: RwLock<Option<(usize, usize)>>,
|
||||||
|
current: Instant,
|
||||||
|
quant: Quantize,
|
||||||
|
sync: LaunchSync,
|
||||||
clock: Arc<Clock>,
|
clock: Arc<Clock>,
|
||||||
transport: jack::Transport,
|
transport: jack::Transport,
|
||||||
metronome: bool,
|
metronome: bool,
|
||||||
phrases: Vec<Arc<RwLock<Phrase>>>,
|
phrases: Vec<Arc<RwLock<Phrase>>>,
|
||||||
player: MIDIPlayer,
|
view_phrase: usize,
|
||||||
phrases: PhrasePoolView<E>,
|
|
||||||
editor: PhraseEditor<E>,
|
editor: PhraseEditor<E>,
|
||||||
split: u16,
|
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
|
/// Sections in the sequencer app that may be focused
|
||||||
|
|
|
||||||
|
|
@ -129,8 +129,16 @@ impl Command<TransportApp<Tui>> for TransportCommand {
|
||||||
pub struct TransportView<E: Engine> {
|
pub struct TransportView<E: Engine> {
|
||||||
_engine: PhantomData<E>,
|
_engine: PhantomData<E>,
|
||||||
jack: Arc<RwLock<JackClient>>,
|
jack: Arc<RwLock<JackClient>>,
|
||||||
/// Current sample rate, tempo, and PPQ.
|
/// Playback state
|
||||||
clock: Arc<Clock>,
|
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.
|
/// JACK transport handle.
|
||||||
transport: jack::Transport,
|
transport: jack::Transport,
|
||||||
/// Enable metronome?
|
/// Enable metronome?
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue