mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +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::*;
|
||||
|
||||
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 {
|
||||
|
|
@ -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)>>;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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]>>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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?
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue