WIP: better precision?
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use arc_swap::ArcSwap;
|
||||
use crossbeam_channel::{bounded, Receiver, Sender, TrySendError};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::atomic::AtomicI64;
|
||||
use std::sync::atomic::{AtomicI64, AtomicU64};
|
||||
use std::sync::Arc;
|
||||
use std::thread::{self, JoinHandle};
|
||||
use std::time::Duration;
|
||||
@@ -40,7 +40,7 @@ impl PatternChange {
|
||||
}
|
||||
|
||||
pub enum AudioCommand {
|
||||
Evaluate(String),
|
||||
Evaluate { cmd: String, time: Option<f64> },
|
||||
Hush,
|
||||
Panic,
|
||||
LoadSamples(Vec<doux::sample::SampleEntry>),
|
||||
@@ -199,6 +199,12 @@ impl AudioState {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SequencerConfig {
|
||||
pub audio_sample_pos: Arc<AtomicU64>,
|
||||
pub sample_rate: Arc<std::sync::atomic::AtomicU32>,
|
||||
pub lookahead_ms: Arc<std::sync::atomic::AtomicU32>,
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn spawn_sequencer(
|
||||
link: Arc<LinkState>,
|
||||
@@ -209,6 +215,7 @@ pub fn spawn_sequencer(
|
||||
quantum: f64,
|
||||
live_keys: Arc<LiveKeyState>,
|
||||
nudge_us: Arc<AtomicI64>,
|
||||
config: SequencerConfig,
|
||||
) -> (SequencerHandle, Receiver<AudioCommand>) {
|
||||
let (cmd_tx, cmd_rx) = bounded::<SeqCommand>(64);
|
||||
let (audio_tx, audio_rx) = bounded::<AudioCommand>(256);
|
||||
@@ -233,6 +240,9 @@ pub fn spawn_sequencer(
|
||||
shared_state_clone,
|
||||
live_keys,
|
||||
nudge_us,
|
||||
config.audio_sample_pos,
|
||||
config.sample_rate,
|
||||
config.lookahead_ms,
|
||||
);
|
||||
})
|
||||
.expect("Failed to spawn sequencer thread");
|
||||
@@ -359,10 +369,18 @@ pub(crate) struct TickInput {
|
||||
pub quantum: f64,
|
||||
pub fill: bool,
|
||||
pub nudge_secs: f64,
|
||||
pub current_time_us: i64,
|
||||
pub engine_time: f64,
|
||||
pub lookahead_secs: f64,
|
||||
}
|
||||
|
||||
pub struct TimestampedCommand {
|
||||
pub cmd: String,
|
||||
pub time: Option<f64>,
|
||||
}
|
||||
|
||||
pub(crate) struct TickOutput {
|
||||
pub audio_commands: Vec<String>,
|
||||
pub audio_commands: Vec<TimestampedCommand>,
|
||||
pub new_tempo: Option<f64>,
|
||||
pub shared_state: SharedSequencerState,
|
||||
}
|
||||
@@ -422,7 +440,7 @@ pub(crate) struct SequencerState {
|
||||
variables: Variables,
|
||||
speed_overrides: HashMap<(usize, usize), f64>,
|
||||
key_cache: KeyCache,
|
||||
buf_audio_commands: Vec<String>,
|
||||
buf_audio_commands: Vec<TimestampedCommand>,
|
||||
}
|
||||
|
||||
impl SequencerState {
|
||||
@@ -516,7 +534,17 @@ impl SequencerState {
|
||||
let stopped = self.deactivate_pending(beat, prev_beat, input.quantum);
|
||||
self.audio_state.pending_stops.retain(|p| !stopped.contains(&p.id));
|
||||
|
||||
let steps = self.execute_steps(beat, prev_beat, input.tempo, input.quantum, input.fill, input.nudge_secs);
|
||||
let steps = self.execute_steps(
|
||||
beat,
|
||||
prev_beat,
|
||||
input.tempo,
|
||||
input.quantum,
|
||||
input.fill,
|
||||
input.nudge_secs,
|
||||
input.current_time_us,
|
||||
input.engine_time,
|
||||
input.lookahead_secs,
|
||||
);
|
||||
|
||||
let vars = self.read_variables(&steps.completed_iterations, &stopped, steps.any_step_fired);
|
||||
self.apply_chain_transitions(vars.chain_transitions);
|
||||
@@ -591,6 +619,7 @@ impl SequencerState {
|
||||
stopped
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn execute_steps(
|
||||
&mut self,
|
||||
beat: f64,
|
||||
@@ -599,6 +628,9 @@ impl SequencerState {
|
||||
quantum: f64,
|
||||
fill: bool,
|
||||
nudge_secs: f64,
|
||||
_current_time_us: i64,
|
||||
engine_time: f64,
|
||||
lookahead_secs: f64,
|
||||
) -> StepResult {
|
||||
self.buf_audio_commands.clear();
|
||||
let mut result = StepResult {
|
||||
@@ -670,9 +702,19 @@ impl SequencerState {
|
||||
(active.bank, active.pattern, source_idx),
|
||||
std::mem::take(&mut trace),
|
||||
);
|
||||
|
||||
let event_time = if lookahead_secs > 0.0 {
|
||||
Some(engine_time + lookahead_secs)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
for cmd in cmds {
|
||||
self.event_count += 1;
|
||||
self.buf_audio_commands.push(cmd);
|
||||
self.buf_audio_commands.push(TimestampedCommand {
|
||||
cmd,
|
||||
time: event_time,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -790,6 +832,9 @@ fn sequencer_loop(
|
||||
shared_state: Arc<ArcSwap<SharedSequencerState>>,
|
||||
live_keys: Arc<LiveKeyState>,
|
||||
nudge_us: Arc<AtomicI64>,
|
||||
audio_sample_pos: Arc<AtomicU64>,
|
||||
sample_rate: Arc<std::sync::atomic::AtomicU32>,
|
||||
lookahead_ms: Arc<std::sync::atomic::AtomicU32>,
|
||||
) {
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
@@ -807,10 +852,15 @@ fn sequencer_loop(
|
||||
}
|
||||
|
||||
let state = link.capture_app_state();
|
||||
let time = link.clock_micros();
|
||||
let beat = state.beat_at_time(time, quantum);
|
||||
let current_time_us = link.clock_micros();
|
||||
let beat = state.beat_at_time(current_time_us, quantum);
|
||||
let tempo = state.tempo();
|
||||
|
||||
let sr = sample_rate.load(Ordering::Relaxed) as f64;
|
||||
let audio_samples = audio_sample_pos.load(Ordering::Relaxed);
|
||||
let engine_time = if sr > 0.0 { audio_samples as f64 / sr } else { 0.0 };
|
||||
let lookahead_secs = lookahead_ms.load(Ordering::Relaxed) as f64 / 1000.0;
|
||||
|
||||
let input = TickInput {
|
||||
commands,
|
||||
playing: playing.load(Ordering::Relaxed),
|
||||
@@ -819,15 +869,18 @@ fn sequencer_loop(
|
||||
quantum,
|
||||
fill: live_keys.fill(),
|
||||
nudge_secs: nudge_us.load(Ordering::Relaxed) as f64 / 1_000_000.0,
|
||||
current_time_us,
|
||||
engine_time,
|
||||
lookahead_secs,
|
||||
};
|
||||
|
||||
let output = seq_state.tick(input);
|
||||
|
||||
for cmd in output.audio_commands {
|
||||
match audio_tx.load().try_send(AudioCommand::Evaluate(cmd)) {
|
||||
for tsc in output.audio_commands {
|
||||
let cmd = AudioCommand::Evaluate { cmd: tsc.cmd, time: tsc.time };
|
||||
match audio_tx.load().try_send(cmd) {
|
||||
Ok(()) => {}
|
||||
Err(TrySendError::Full(_) | TrySendError::Disconnected(_)) => {
|
||||
// Lags one tick in shared state: build_shared_state() already ran
|
||||
seq_state.dropped_events += 1;
|
||||
}
|
||||
}
|
||||
@@ -886,6 +939,9 @@ mod tests {
|
||||
quantum: 4.0,
|
||||
fill: false,
|
||||
nudge_secs: 0.0,
|
||||
current_time_us: 0,
|
||||
engine_time: 0.0,
|
||||
lookahead_secs: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -898,6 +954,9 @@ mod tests {
|
||||
quantum: 4.0,
|
||||
fill: false,
|
||||
nudge_secs: 0.0,
|
||||
current_time_us: 0,
|
||||
engine_time: 0.0,
|
||||
lookahead_secs: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user