mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
wip(p64,e4)
This commit is contained in:
parent
f49823b7a7
commit
fffd830e15
11 changed files with 194 additions and 217 deletions
|
|
@ -2,6 +2,11 @@ use crate::*;
|
|||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ClockCommand {
|
||||
Play(Option<usize>),
|
||||
Pause(Option<usize>),
|
||||
SeekUsec(f64),
|
||||
SeekSample(f64),
|
||||
SeekPulse(f64),
|
||||
SetBpm(f64),
|
||||
SetQuant(f64),
|
||||
SetSync(f64),
|
||||
|
|
@ -11,6 +16,24 @@ impl<T: ClockApi> Command<T> for ClockCommand {
|
|||
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
||||
use ClockCommand::*;
|
||||
Ok(Some(match self {
|
||||
Play(_start) => {
|
||||
todo!()
|
||||
},
|
||||
Pause(_start) => {
|
||||
todo!()
|
||||
},
|
||||
SeekUsec(usec) => {
|
||||
state.current().update_from_usec(usec);
|
||||
return Ok(None)
|
||||
},
|
||||
SeekSample(sample) => {
|
||||
state.current().update_from_sample(sample);
|
||||
return Ok(None)
|
||||
},
|
||||
SeekPulse(pulse) => {
|
||||
state.current().update_from_pulse(pulse);
|
||||
return Ok(None)
|
||||
},
|
||||
SetBpm(bpm) => SetBpm(state.timebase().bpm.set(bpm)),
|
||||
SetQuant(quant) => SetQuant(state.quant().set(quant)),
|
||||
SetSync(sync) => SetSync(state.sync().set(sync)),
|
||||
|
|
@ -19,13 +42,12 @@ impl<T: ClockApi> Command<T> for ClockCommand {
|
|||
}
|
||||
|
||||
pub trait ClockApi: Send + Sync {
|
||||
/// Current moment in time
|
||||
fn current (&self) -> &Arc<Instant>;
|
||||
/// Temporal resolution in all units
|
||||
fn timebase (&self) -> &Arc<Timebase>;
|
||||
/// Note quantization factor
|
||||
fn quant (&self) -> &Quantize;
|
||||
/// Launch quantization factor
|
||||
fn sync (&self) -> &LaunchSync;
|
||||
|
||||
fn timebase (&self) -> &Arc<Timebase> {
|
||||
&self.current().timebase
|
||||
}
|
||||
fn sr (&self) -> &SampleRate {
|
||||
&self.timebase().sr
|
||||
}
|
||||
|
|
@ -36,16 +58,93 @@ pub trait ClockApi: Send + Sync {
|
|||
&self.timebase().ppq
|
||||
}
|
||||
|
||||
/// Note quantization factor
|
||||
fn quant (&self) -> &Arc<Quantize>;
|
||||
fn next_quant (&self) -> f64 {
|
||||
next_note_length(self.quant().get() as usize) as f64
|
||||
}
|
||||
fn prev_quant (&self) -> f64 {
|
||||
prev_note_length(self.quant().get() as usize) as f64
|
||||
}
|
||||
|
||||
/// Launch quantization factor
|
||||
fn sync (&self) -> &Arc<LaunchSync>;
|
||||
fn next_sync (&self) -> f64 {
|
||||
next_note_length(self.sync().get() as usize) as f64
|
||||
}
|
||||
fn prev_sync (&self) -> f64 {
|
||||
prev_note_length(self.sync().get() as usize) as f64
|
||||
}
|
||||
|
||||
fn next_launch_pulse (&self) -> usize {
|
||||
let sync = self.sync().get() as usize;
|
||||
let pulse = self.current().pulse.get() as usize;
|
||||
if pulse % sync == 0 { pulse } else { (pulse / sync + 1) * sync }
|
||||
}
|
||||
|
||||
/// Handle to JACK transport
|
||||
fn transport_handle (&self) -> &Arc<Transport>;
|
||||
/// Playback state
|
||||
fn transport_state (&self) -> &Arc<RwLock<Option<TransportState>>>;
|
||||
/// Global sample and usec at which playback started
|
||||
fn transport_offset (&self) -> &Arc<RwLock<Option<(usize, usize)>>>;
|
||||
|
||||
fn is_stopped (&self) -> bool {
|
||||
*self.transport_state().read().unwrap() == Some(TransportState::Stopped)
|
||||
}
|
||||
fn is_rolling (&self) -> bool {
|
||||
*self.transport_state().read().unwrap() == Some(TransportState::Rolling)
|
||||
}
|
||||
fn toggle_play (&self) -> Usually<()> {
|
||||
let playing = self.transport_state().read().unwrap().expect("1st sample has not been processed yet");
|
||||
let playing = match playing {
|
||||
TransportState::Stopped => {
|
||||
self.transport_handle().start()?;
|
||||
Some(TransportState::Starting)
|
||||
},
|
||||
_ => {
|
||||
self.transport_handle().stop()?;
|
||||
self.transport_handle().locate(0)?;
|
||||
Some(TransportState::Stopped)
|
||||
},
|
||||
};
|
||||
*self.transport_state().write().unwrap() = playing;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Hosts the JACK callback for updating the temporal pointer and playback status.
|
||||
pub struct ClockAudio<'a, T: ClockApi>(pub &'a mut T);
|
||||
|
||||
impl<'a, T: ClockApi> Audio for ClockAudio<'a, T> {
|
||||
#[inline] fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
||||
let state = &mut self.0;
|
||||
let times = scope.cycle_times().unwrap();
|
||||
let CycleTimes { current_frames, current_usecs, next_usecs: _, period_usecs: _ } = times;
|
||||
let _chunk_size = scope.n_frames() as usize;
|
||||
let transport = state.transport_handle().query().unwrap();
|
||||
state.current().sample.set(transport.pos.frame() as f64);
|
||||
let mut playing = state.transport_state().write().unwrap();
|
||||
let mut started = state.transport_offset().write().unwrap();
|
||||
if *playing != Some(transport.state) {
|
||||
match transport.state {
|
||||
TransportState::Rolling => {
|
||||
*started = Some((current_frames as usize, current_usecs as usize))
|
||||
},
|
||||
TransportState::Stopped => {
|
||||
*started = None
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
*playing = Some(transport.state);
|
||||
if *playing == Some(TransportState::Stopped) {
|
||||
*started = None;
|
||||
}
|
||||
state.current().update_from_usec(match *started {
|
||||
Some((_, usecs)) => current_usecs as f64 - usecs as f64,
|
||||
None => 0.
|
||||
});
|
||||
Control::Continue
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ pub trait HasPlayer: JackApi {
|
|||
|
||||
pub trait MidiPlayerApi: MidiInputApi + MidiOutputApi + Send + Sync {}
|
||||
|
||||
pub trait HasPhrase: PlayheadApi {
|
||||
pub trait HasPhrase: ClockApi {
|
||||
fn reset (&self) -> bool;
|
||||
fn reset_mut (&mut self) -> &mut bool;
|
||||
|
||||
|
|
@ -37,7 +37,7 @@ pub trait HasPhrase: PlayheadApi {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait MidiInputApi: PlayheadApi + HasPhrase {
|
||||
pub trait MidiInputApi: ClockApi + HasPhrase {
|
||||
fn midi_ins (&self) -> &Vec<Port<MidiIn>>;
|
||||
fn midi_ins_mut (&self) -> &mut Vec<Port<MidiIn>>;
|
||||
fn has_midi_ins (&self) -> bool {
|
||||
|
|
@ -122,7 +122,7 @@ pub trait MidiInputApi: PlayheadApi + HasPhrase {
|
|||
|
||||
}
|
||||
|
||||
pub trait MidiOutputApi: PlayheadApi + HasPhrase {
|
||||
pub trait MidiOutputApi: ClockApi + HasPhrase {
|
||||
fn midi_outs (&self) -> &Vec<Port<MidiOut>>;
|
||||
|
||||
fn midi_outs_mut (&mut self) -> &mut Vec<Port<MidiOut>>;
|
||||
|
|
@ -165,7 +165,7 @@ pub trait MidiOutputApi: PlayheadApi + HasPhrase {
|
|||
// If no phrase is playing, prepare for switchover immediately
|
||||
next = self.phrase().is_none();
|
||||
let phrase = self.phrase();
|
||||
let started0 = self.started();
|
||||
let started0 = self.transport_offset();
|
||||
let timebase = self.timebase();
|
||||
let notes_out = self.notes_out();
|
||||
let next_phrase = self.next_phrase();
|
||||
|
|
@ -231,7 +231,7 @@ pub trait MidiOutputApi: PlayheadApi + HasPhrase {
|
|||
//let samples = scope.n_frames() as usize;
|
||||
if let Some((start_at, phrase)) = &self.next_phrase() {
|
||||
let start = start_at.sample.get() as usize;
|
||||
let sample = self.started().read().unwrap().unwrap().0;
|
||||
let sample = self.transport_offset().read().unwrap().unwrap().0;
|
||||
// If it's time to switch to the next phrase:
|
||||
if start <= sample0.saturating_sub(sample) {
|
||||
// Samples elapsed since phrase was supposed to start
|
||||
|
|
|
|||
|
|
@ -1,118 +0,0 @@
|
|||
use crate::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum PlayheadCommand {
|
||||
Play(Option<usize>),
|
||||
Pause(Option<usize>),
|
||||
SeekUsec(f64),
|
||||
SeekSample(f64),
|
||||
SeekPulse(f64),
|
||||
}
|
||||
|
||||
impl<T: PlayheadApi> Command<T> for PlayheadCommand {
|
||||
fn execute (self, state: &mut T) -> Perhaps<Self> {
|
||||
use PlayheadCommand::*;
|
||||
match self {
|
||||
Play(_start) => {
|
||||
todo!()
|
||||
},
|
||||
Pause(_start) => {
|
||||
todo!()
|
||||
},
|
||||
SeekUsec(usec) => {
|
||||
state.current().update_from_usec(usec);
|
||||
},
|
||||
SeekSample(sample) => {
|
||||
state.current().update_from_sample(sample);
|
||||
},
|
||||
SeekPulse(pulse) => {
|
||||
state.current().update_from_pulse(pulse);
|
||||
},
|
||||
};
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PlayheadApi: ClockApi {
|
||||
/// Current moment in time
|
||||
fn current (&self) -> &Instant;
|
||||
/// Handle to JACK transport
|
||||
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)>>;
|
||||
|
||||
fn is_stopped (&self) -> bool {
|
||||
*self.playing().read().unwrap() == Some(TransportState::Stopped)
|
||||
}
|
||||
|
||||
fn is_rolling (&self) -> bool {
|
||||
*self.playing().read().unwrap() == Some(TransportState::Rolling)
|
||||
}
|
||||
|
||||
/// Current pulse
|
||||
fn pulse (&self) -> f64 {
|
||||
self.current().pulse.get()
|
||||
}
|
||||
|
||||
fn next_launch_pulse (&self) -> usize {
|
||||
let sync = self.sync().get() as usize;
|
||||
let pulse = self.pulse() as usize;
|
||||
if pulse % sync == 0 { pulse } else { (pulse / sync + 1) * sync }
|
||||
}
|
||||
|
||||
fn toggle_play (&self) -> Usually<()> {
|
||||
let playing = self.playing().read().unwrap().expect("1st sample has not been processed yet");
|
||||
let playing = match playing {
|
||||
TransportState::Stopped => {
|
||||
self.transport().start()?;
|
||||
Some(TransportState::Starting)
|
||||
},
|
||||
_ => {
|
||||
self.transport().stop()?;
|
||||
self.transport().locate(0)?;
|
||||
Some(TransportState::Stopped)
|
||||
},
|
||||
};
|
||||
*self.playing().write().unwrap() = playing;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Hosts the JACK callback for updating the temporal pointer and playback status.
|
||||
pub struct PlayheadAudio<'a, T: PlayheadApi>(pub &'a mut T);
|
||||
|
||||
impl<'a, T: PlayheadApi> Audio for PlayheadAudio<'a, T> {
|
||||
#[inline] fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
||||
let state = &mut self.0;
|
||||
let times = scope.cycle_times().unwrap();
|
||||
let CycleTimes { current_frames, current_usecs, next_usecs: _, period_usecs: _ } = times;
|
||||
let _chunk_size = scope.n_frames() as usize;
|
||||
let transport = state.transport().query().unwrap();
|
||||
state.current().sample.set(transport.pos.frame() as f64);
|
||||
let mut playing = state.playing().write().unwrap();
|
||||
let mut started = state.started().write().unwrap();
|
||||
if *playing != Some(transport.state) {
|
||||
match transport.state {
|
||||
TransportState::Rolling => {
|
||||
*started = Some((current_frames as usize, current_usecs as usize))
|
||||
},
|
||||
TransportState::Stopped => {
|
||||
*started = None
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
*playing = Some(transport.state);
|
||||
if *playing == Some(TransportState::Stopped) {
|
||||
*started = None;
|
||||
}
|
||||
state.current().update_from_usec(match *started {
|
||||
Some((_, usecs)) => current_usecs as f64 - usecs as f64,
|
||||
None => 0.
|
||||
});
|
||||
Control::Continue
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,6 @@ submod! {
|
|||
api_jack
|
||||
api_phrase
|
||||
api_player
|
||||
api_playhead
|
||||
api_scene
|
||||
api_track
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue