Feat: fix sequencer precision regression
Some checks failed
Deploy Website / deploy (push) Failing after 4s
Some checks failed
Deploy Website / deploy (push) Failing after 4s
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -1825,7 +1825,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "doux"
|
name = "doux"
|
||||||
version = "0.0.15"
|
version = "0.0.15"
|
||||||
source = "git+https://github.com/sova-org/doux?tag=v0.0.15#1f11f795b877d9c15f65d88eb5576e8149092b17"
|
source = "git+https://github.com/sova-org/doux?tag=v0.0.15#29d8f055612f6141d7546d72b91e60026937b0fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"clap",
|
"clap",
|
||||||
|
|||||||
@@ -292,7 +292,7 @@ impl Plugin for CagirePlugin {
|
|||||||
fill: false,
|
fill: false,
|
||||||
nudge_secs: 0.0,
|
nudge_secs: 0.0,
|
||||||
current_time_us: 0,
|
current_time_us: 0,
|
||||||
audio_sample_pos: self.sample_pos,
|
corrected_audio_pos: self.sample_pos as f64,
|
||||||
sr: self.sample_rate as f64,
|
sr: self.sample_rate as f64,
|
||||||
mouse_x: 0.5,
|
mouse_x: 0.5,
|
||||||
mouse_y: 0.5,
|
mouse_y: 0.5,
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
#![windows_subsystem = "windows"]
|
#![windows_subsystem = "windows"]
|
||||||
|
|
||||||
use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU32, AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU32, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use arc_swap::ArcSwap;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use doux::EngineMetrics;
|
use doux::EngineMetrics;
|
||||||
use eframe::NativeOptions;
|
use eframe::NativeOptions;
|
||||||
|
|
||||||
use cagire::engine::{
|
use cagire::engine::{
|
||||||
build_stream, AnalysisHandle, AudioStreamConfig, LinkState, ScopeBuffer,
|
build_stream, AnalysisHandle, AudioRef, AudioStreamConfig, LinkState, ScopeBuffer,
|
||||||
SequencerHandle, SpectrumBuffer,
|
SequencerHandle, SpectrumBuffer,
|
||||||
};
|
};
|
||||||
use cagire::terminal::{create_terminal, FontChoice, TerminalType};
|
use cagire::terminal::{create_terminal, FontChoice, TerminalType};
|
||||||
@@ -48,7 +49,7 @@ struct CagireDesktop {
|
|||||||
metrics: Arc<EngineMetrics>,
|
metrics: Arc<EngineMetrics>,
|
||||||
scope_buffer: Arc<ScopeBuffer>,
|
scope_buffer: Arc<ScopeBuffer>,
|
||||||
spectrum_buffer: Arc<SpectrumBuffer>,
|
spectrum_buffer: Arc<SpectrumBuffer>,
|
||||||
audio_sample_pos: Arc<AtomicU64>,
|
audio_ref: Arc<ArcSwap<AudioRef>>,
|
||||||
sample_rate_shared: Arc<AtomicU32>,
|
sample_rate_shared: Arc<AtomicU32>,
|
||||||
_stream: Option<cpal::Stream>,
|
_stream: Option<cpal::Stream>,
|
||||||
_input_stream: Option<cpal::Stream>,
|
_input_stream: Option<cpal::Stream>,
|
||||||
@@ -94,7 +95,7 @@ impl CagireDesktop {
|
|||||||
metrics: b.metrics,
|
metrics: b.metrics,
|
||||||
scope_buffer: b.scope_buffer,
|
scope_buffer: b.scope_buffer,
|
||||||
spectrum_buffer: b.spectrum_buffer,
|
spectrum_buffer: b.spectrum_buffer,
|
||||||
audio_sample_pos: b.audio_sample_pos,
|
audio_ref: b.audio_ref,
|
||||||
sample_rate_shared: b.sample_rate_shared,
|
sample_rate_shared: b.sample_rate_shared,
|
||||||
_stream: b.stream,
|
_stream: b.stream,
|
||||||
_input_stream: b.input_stream,
|
_input_stream: b.input_stream,
|
||||||
@@ -152,7 +153,11 @@ impl CagireDesktop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.audio_sample_pos.store(0, Ordering::Release);
|
self.audio_ref.store(Arc::new(AudioRef {
|
||||||
|
sample_pos: 0,
|
||||||
|
timestamp: std::time::Instant::now(),
|
||||||
|
sample_rate: 44100.0,
|
||||||
|
}));
|
||||||
|
|
||||||
let preload_entries: Vec<(String, std::path::PathBuf)> = restart_samples
|
let preload_entries: Vec<(String, std::path::PathBuf)> = restart_samples
|
||||||
.iter()
|
.iter()
|
||||||
@@ -166,7 +171,7 @@ impl CagireDesktop {
|
|||||||
Arc::clone(&self.spectrum_buffer),
|
Arc::clone(&self.spectrum_buffer),
|
||||||
Arc::clone(&self.metrics),
|
Arc::clone(&self.metrics),
|
||||||
restart_samples,
|
restart_samples,
|
||||||
Arc::clone(&self.audio_sample_pos),
|
Arc::clone(&self.audio_ref),
|
||||||
new_error_tx,
|
new_error_tx,
|
||||||
&self.app.audio.config.sample_paths,
|
&self.app.audio.config.sample_paths,
|
||||||
Arc::clone(&self.device_lost),
|
Arc::clone(&self.device_lost),
|
||||||
|
|||||||
@@ -1,13 +1,22 @@
|
|||||||
//! Audio output stream (cpal) and FFT spectrum analysis.
|
//! Audio output stream (cpal) and FFT spectrum analysis.
|
||||||
|
|
||||||
|
use arc_swap::ArcSwap;
|
||||||
use ringbuf::{traits::*, HeapRb};
|
use ringbuf::{traits::*, HeapRb};
|
||||||
use rustfft::{num_complex::Complex, FftPlanner};
|
use rustfft::{num_complex::Complex, FftPlanner};
|
||||||
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread::{self, JoinHandle};
|
use std::thread::{self, JoinHandle};
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
#[cfg(feature = "cli")]
|
/// Timestamped audio position reference for jitter-free tick interpolation.
|
||||||
use std::sync::atomic::AtomicU64;
|
/// Published by the audio callback after each `process_block`, read by the
|
||||||
|
/// sequencer to compute the correct sample position at any instant.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AudioRef {
|
||||||
|
pub sample_pos: u64,
|
||||||
|
pub timestamp: Instant,
|
||||||
|
pub sample_rate: f64,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ScopeBuffer {
|
pub struct ScopeBuffer {
|
||||||
pub samples: [AtomicU32; 256],
|
pub samples: [AtomicU32; 256],
|
||||||
@@ -303,7 +312,7 @@ pub fn build_stream(
|
|||||||
spectrum_buffer: Arc<SpectrumBuffer>,
|
spectrum_buffer: Arc<SpectrumBuffer>,
|
||||||
metrics: Arc<EngineMetrics>,
|
metrics: Arc<EngineMetrics>,
|
||||||
initial_samples: Vec<doux::sampling::SampleEntry>,
|
initial_samples: Vec<doux::sampling::SampleEntry>,
|
||||||
audio_sample_pos: Arc<AtomicU64>,
|
audio_ref: Arc<ArcSwap<AudioRef>>,
|
||||||
error_tx: Sender<String>,
|
error_tx: Sender<String>,
|
||||||
sample_paths: &[std::path::PathBuf],
|
sample_paths: &[std::path::PathBuf],
|
||||||
device_lost: Arc<AtomicBool>,
|
device_lost: Arc<AtomicBool>,
|
||||||
@@ -438,6 +447,7 @@ pub fn build_stream(
|
|||||||
let mut rt_set = false;
|
let mut rt_set = false;
|
||||||
let mut live_scratch = vec![0.0f32; 4096];
|
let mut live_scratch = vec![0.0f32; 4096];
|
||||||
let mut input_consumer = input_consumer;
|
let mut input_consumer = input_consumer;
|
||||||
|
let mut current_pos: u64 = 0;
|
||||||
|
|
||||||
let stream = device
|
let stream = device
|
||||||
.build_output_stream(
|
.build_output_stream(
|
||||||
@@ -454,8 +464,6 @@ pub fn build_stream(
|
|||||||
let buffer_samples = data.len() / channels;
|
let buffer_samples = data.len() / channels;
|
||||||
let buffer_time_ns = (buffer_samples as f64 / sr as f64 * 1e9) as u64;
|
let buffer_time_ns = (buffer_samples as f64 / sr as f64 * 1e9) as u64;
|
||||||
|
|
||||||
audio_sample_pos.fetch_add(buffer_samples as u64, Ordering::Release);
|
|
||||||
|
|
||||||
while let Ok(cmd) = audio_rx.try_recv() {
|
while let Ok(cmd) = audio_rx.try_recv() {
|
||||||
match cmd {
|
match cmd {
|
||||||
AudioCommand::Evaluate { cmd, tick } => {
|
AudioCommand::Evaluate { cmd, tick } => {
|
||||||
@@ -497,6 +505,16 @@ pub fn build_stream(
|
|||||||
|
|
||||||
engine.metrics.load.set_buffer_time(buffer_time_ns);
|
engine.metrics.load.set_buffer_time(buffer_time_ns);
|
||||||
engine.process_block(data, &[], &live_scratch[..raw_len]);
|
engine.process_block(data, &[], &live_scratch[..raw_len]);
|
||||||
|
|
||||||
|
// Publish accurate audio reference AFTER process_block
|
||||||
|
// so sample_pos matches doux's internal tick exactly.
|
||||||
|
current_pos += buffer_samples as u64;
|
||||||
|
audio_ref.store(Arc::new(AudioRef {
|
||||||
|
sample_pos: current_pos,
|
||||||
|
timestamp: Instant::now(),
|
||||||
|
sample_rate: sr as f64,
|
||||||
|
}));
|
||||||
|
|
||||||
scope_buffer.write(data);
|
scope_buffer.write(data);
|
||||||
|
|
||||||
// Feed mono mix to analysis thread via ring buffer (non-blocking)
|
// Feed mono mix to analysis thread via ring buffer (non-blocking)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ mod timing;
|
|||||||
|
|
||||||
pub use timing::{next_boundary, substeps_in_window, SyncTime};
|
pub use timing::{next_boundary, substeps_in_window, SyncTime};
|
||||||
|
|
||||||
pub use audio::{preload_sample_heads, AnalysisHandle, ScopeBuffer, SpectrumBuffer};
|
pub use audio::{preload_sample_heads, AnalysisHandle, AudioRef, ScopeBuffer, SpectrumBuffer};
|
||||||
|
|
||||||
// Re-exported for the plugin crate (not used by the terminal binary).
|
// Re-exported for the plugin crate (not used by the terminal binary).
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ use rand::SeedableRng;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
#[cfg(feature = "desktop")]
|
#[cfg(feature = "desktop")]
|
||||||
use std::sync::atomic::AtomicU32;
|
use std::sync::atomic::AtomicU32;
|
||||||
use std::sync::atomic::{AtomicI64, AtomicU64};
|
use std::sync::atomic::AtomicI64;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::thread::{self, JoinHandle};
|
use std::thread::{self, JoinHandle};
|
||||||
|
|
||||||
|
use super::audio::AudioRef;
|
||||||
|
|
||||||
use super::dispatcher::{dispatcher_loop, MidiDispatch, TimedMidiCommand};
|
use super::dispatcher::{dispatcher_loop, MidiDispatch, TimedMidiCommand};
|
||||||
use super::realtime::set_realtime_priority;
|
use super::realtime::set_realtime_priority;
|
||||||
use super::{next_boundary, substeps_in_window, LinkState, SyncTime};
|
use super::{next_boundary, substeps_in_window, LinkState, SyncTime};
|
||||||
@@ -329,7 +331,7 @@ impl AudioState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct SequencerConfig {
|
pub struct SequencerConfig {
|
||||||
pub audio_sample_pos: Arc<AtomicU64>,
|
pub audio_ref: Arc<ArcSwap<AudioRef>>,
|
||||||
pub sample_rate: Arc<std::sync::atomic::AtomicU32>,
|
pub sample_rate: Arc<std::sync::atomic::AtomicU32>,
|
||||||
pub cc_access: Option<Arc<dyn CcAccess>>,
|
pub cc_access: Option<Arc<dyn CcAccess>>,
|
||||||
pub variables: Variables,
|
pub variables: Variables,
|
||||||
@@ -393,7 +395,7 @@ pub fn spawn_sequencer(
|
|||||||
shared_state_clone,
|
shared_state_clone,
|
||||||
live_keys,
|
live_keys,
|
||||||
nudge_us,
|
nudge_us,
|
||||||
config.audio_sample_pos,
|
config.audio_ref,
|
||||||
config.sample_rate,
|
config.sample_rate,
|
||||||
config.cc_access,
|
config.cc_access,
|
||||||
variables,
|
variables,
|
||||||
@@ -502,7 +504,7 @@ pub struct TickInput {
|
|||||||
pub fill: bool,
|
pub fill: bool,
|
||||||
pub nudge_secs: f64,
|
pub nudge_secs: f64,
|
||||||
pub current_time_us: SyncTime,
|
pub current_time_us: SyncTime,
|
||||||
pub audio_sample_pos: u64,
|
pub corrected_audio_pos: f64,
|
||||||
pub sr: f64,
|
pub sr: f64,
|
||||||
pub mouse_x: f64,
|
pub mouse_x: f64,
|
||||||
pub mouse_y: f64,
|
pub mouse_y: f64,
|
||||||
@@ -765,7 +767,7 @@ impl SequencerState {
|
|||||||
input.fill,
|
input.fill,
|
||||||
input.nudge_secs,
|
input.nudge_secs,
|
||||||
input.current_time_us,
|
input.current_time_us,
|
||||||
input.audio_sample_pos,
|
input.corrected_audio_pos,
|
||||||
input.sr,
|
input.sr,
|
||||||
input.mouse_x,
|
input.mouse_x,
|
||||||
input.mouse_y,
|
input.mouse_y,
|
||||||
@@ -780,7 +782,7 @@ impl SequencerState {
|
|||||||
input.quantum,
|
input.quantum,
|
||||||
input.fill,
|
input.fill,
|
||||||
input.nudge_secs,
|
input.nudge_secs,
|
||||||
input.audio_sample_pos,
|
input.corrected_audio_pos,
|
||||||
input.sr,
|
input.sr,
|
||||||
input.mouse_x,
|
input.mouse_x,
|
||||||
input.mouse_y,
|
input.mouse_y,
|
||||||
@@ -934,7 +936,7 @@ impl SequencerState {
|
|||||||
fill: bool,
|
fill: bool,
|
||||||
nudge_secs: f64,
|
nudge_secs: f64,
|
||||||
_current_time_us: SyncTime,
|
_current_time_us: SyncTime,
|
||||||
audio_sample_pos: u64,
|
corrected_audio_pos: f64,
|
||||||
sr: f64,
|
sr: f64,
|
||||||
mouse_x: f64,
|
mouse_x: f64,
|
||||||
mouse_y: f64,
|
mouse_y: f64,
|
||||||
@@ -983,7 +985,7 @@ impl SequencerState {
|
|||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
let event_tick = Some(audio_sample_pos + (time_delta * sr).round() as u64);
|
let event_tick = Some((corrected_audio_pos + time_delta * sr).round() as u64);
|
||||||
|
|
||||||
if let Some(step) = pattern.steps.get(step_idx) {
|
if let Some(step) = pattern.steps.get(step_idx) {
|
||||||
let resolved_script = pattern.resolve_script(step_idx);
|
let resolved_script = pattern.resolve_script(step_idx);
|
||||||
@@ -1095,7 +1097,7 @@ impl SequencerState {
|
|||||||
quantum: f64,
|
quantum: f64,
|
||||||
fill: bool,
|
fill: bool,
|
||||||
nudge_secs: f64,
|
nudge_secs: f64,
|
||||||
audio_sample_pos: u64,
|
corrected_audio_pos: f64,
|
||||||
sr: f64,
|
sr: f64,
|
||||||
mouse_x: f64,
|
mouse_x: f64,
|
||||||
mouse_y: f64,
|
mouse_y: f64,
|
||||||
@@ -1117,7 +1119,7 @@ impl SequencerState {
|
|||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
let event_tick = Some(audio_sample_pos + (time_delta * sr).round() as u64);
|
let event_tick = Some((corrected_audio_pos + time_delta * sr).round() as u64);
|
||||||
|
|
||||||
let step_in_cycle = self.script_step % self.script_length;
|
let step_in_cycle = self.script_step % self.script_length;
|
||||||
|
|
||||||
@@ -1263,7 +1265,7 @@ fn sequencer_loop(
|
|||||||
shared_state: Arc<ArcSwap<SharedSequencerState>>,
|
shared_state: Arc<ArcSwap<SharedSequencerState>>,
|
||||||
live_keys: Arc<LiveKeyState>,
|
live_keys: Arc<LiveKeyState>,
|
||||||
nudge_us: Arc<AtomicI64>,
|
nudge_us: Arc<AtomicI64>,
|
||||||
audio_sample_pos: Arc<AtomicU64>,
|
audio_ref: Arc<ArcSwap<AudioRef>>,
|
||||||
sample_rate: Arc<std::sync::atomic::AtomicU32>,
|
sample_rate: Arc<std::sync::atomic::AtomicU32>,
|
||||||
cc_access: Option<Arc<dyn CcAccess>>,
|
cc_access: Option<Arc<dyn CcAccess>>,
|
||||||
variables: Variables,
|
variables: Variables,
|
||||||
@@ -1330,7 +1332,9 @@ fn sequencer_loop(
|
|||||||
let lookahead_end = beat + lookahead_beats;
|
let lookahead_end = beat + lookahead_beats;
|
||||||
|
|
||||||
let sr = sample_rate.load(Ordering::Relaxed) as f64;
|
let sr = sample_rate.load(Ordering::Relaxed) as f64;
|
||||||
let audio_samples = audio_sample_pos.load(Ordering::Acquire);
|
let ref_snapshot = audio_ref.load();
|
||||||
|
let elapsed_secs = ref_snapshot.timestamp.elapsed().as_secs_f64();
|
||||||
|
let corrected_pos = ref_snapshot.sample_pos as f64 + elapsed_secs * ref_snapshot.sample_rate;
|
||||||
let input = TickInput {
|
let input = TickInput {
|
||||||
commands,
|
commands,
|
||||||
playing: is_playing,
|
playing: is_playing,
|
||||||
@@ -1341,7 +1345,7 @@ fn sequencer_loop(
|
|||||||
fill: live_keys.fill(),
|
fill: live_keys.fill(),
|
||||||
nudge_secs: nudge_us.load(Ordering::Relaxed) as f64 / 1_000_000.0,
|
nudge_secs: nudge_us.load(Ordering::Relaxed) as f64 / 1_000_000.0,
|
||||||
current_time_us,
|
current_time_us,
|
||||||
audio_sample_pos: audio_samples,
|
corrected_audio_pos: corrected_pos,
|
||||||
sr,
|
sr,
|
||||||
#[cfg(feature = "desktop")]
|
#[cfg(feature = "desktop")]
|
||||||
mouse_x: f32::from_bits(mouse_x.load(Ordering::Relaxed)) as f64,
|
mouse_x: f32::from_bits(mouse_x.load(Ordering::Relaxed)) as f64,
|
||||||
@@ -1563,7 +1567,7 @@ mod tests {
|
|||||||
fill: false,
|
fill: false,
|
||||||
nudge_secs: 0.0,
|
nudge_secs: 0.0,
|
||||||
current_time_us: 0,
|
current_time_us: 0,
|
||||||
audio_sample_pos: 0,
|
corrected_audio_pos: 0.0,
|
||||||
sr: 48000.0,
|
sr: 48000.0,
|
||||||
mouse_x: 0.5,
|
mouse_x: 0.5,
|
||||||
mouse_y: 0.5,
|
mouse_y: 0.5,
|
||||||
@@ -1582,7 +1586,7 @@ mod tests {
|
|||||||
fill: false,
|
fill: false,
|
||||||
nudge_secs: 0.0,
|
nudge_secs: 0.0,
|
||||||
current_time_us: 0,
|
current_time_us: 0,
|
||||||
audio_sample_pos: 0,
|
corrected_audio_pos: 0.0,
|
||||||
sr: 48000.0,
|
sr: 48000.0,
|
||||||
mouse_x: 0.5,
|
mouse_x: 0.5,
|
||||||
mouse_y: 0.5,
|
mouse_y: 0.5,
|
||||||
|
|||||||
24
src/init.rs
24
src/init.rs
@@ -1,14 +1,16 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU32, AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU32, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use arc_swap::ArcSwap;
|
||||||
use doux::EngineMetrics;
|
use doux::EngineMetrics;
|
||||||
|
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::engine::{
|
use crate::engine::{
|
||||||
build_stream, preload_sample_heads, spawn_sequencer, AnalysisHandle, AudioStreamConfig,
|
build_stream, preload_sample_heads, spawn_sequencer, AnalysisHandle, AudioRef,
|
||||||
LinkState, PatternChange, ScopeBuffer, SequencerConfig, SequencerHandle,
|
AudioStreamConfig, LinkState, PatternChange, ScopeBuffer, SequencerConfig,
|
||||||
SpectrumBuffer,
|
SequencerHandle, SpectrumBuffer,
|
||||||
};
|
};
|
||||||
use crate::midi;
|
use crate::midi;
|
||||||
use crate::model;
|
use crate::model;
|
||||||
@@ -36,7 +38,7 @@ pub struct Init {
|
|||||||
pub metrics: Arc<EngineMetrics>,
|
pub metrics: Arc<EngineMetrics>,
|
||||||
pub scope_buffer: Arc<ScopeBuffer>,
|
pub scope_buffer: Arc<ScopeBuffer>,
|
||||||
pub spectrum_buffer: Arc<SpectrumBuffer>,
|
pub spectrum_buffer: Arc<SpectrumBuffer>,
|
||||||
pub audio_sample_pos: Arc<AtomicU64>,
|
pub audio_ref: Arc<ArcSwap<AudioRef>>,
|
||||||
pub sample_rate_shared: Arc<AtomicU32>,
|
pub sample_rate_shared: Arc<AtomicU32>,
|
||||||
pub stream: Option<cpal::Stream>,
|
pub stream: Option<cpal::Stream>,
|
||||||
pub input_stream: Option<cpal::Stream>,
|
pub input_stream: Option<cpal::Stream>,
|
||||||
@@ -151,7 +153,11 @@ pub fn init(args: InitArgs) -> Init {
|
|||||||
let scope_buffer = Arc::new(ScopeBuffer::new());
|
let scope_buffer = Arc::new(ScopeBuffer::new());
|
||||||
let spectrum_buffer = Arc::new(SpectrumBuffer::new());
|
let spectrum_buffer = Arc::new(SpectrumBuffer::new());
|
||||||
|
|
||||||
let audio_sample_pos = Arc::new(AtomicU64::new(0));
|
let audio_ref = Arc::new(ArcSwap::from_pointee(AudioRef {
|
||||||
|
sample_pos: 0,
|
||||||
|
timestamp: Instant::now(),
|
||||||
|
sample_rate: 44100.0,
|
||||||
|
}));
|
||||||
let sample_rate_shared = Arc::new(AtomicU32::new(44100));
|
let sample_rate_shared = Arc::new(AtomicU32::new(44100));
|
||||||
let mut initial_samples = Vec::new();
|
let mut initial_samples = Vec::new();
|
||||||
for path in &app.audio.config.sample_paths {
|
for path in &app.audio.config.sample_paths {
|
||||||
@@ -177,7 +183,7 @@ pub fn init(args: InitArgs) -> Init {
|
|||||||
let mouse_down = Arc::new(AtomicU32::new(0.0_f32.to_bits()));
|
let mouse_down = Arc::new(AtomicU32::new(0.0_f32.to_bits()));
|
||||||
|
|
||||||
let seq_config = SequencerConfig {
|
let seq_config = SequencerConfig {
|
||||||
audio_sample_pos: Arc::clone(&audio_sample_pos),
|
audio_ref: Arc::clone(&audio_ref),
|
||||||
sample_rate: Arc::clone(&sample_rate_shared),
|
sample_rate: Arc::clone(&sample_rate_shared),
|
||||||
cc_access: Some(Arc::new(app.midi.cc_memory.clone()) as Arc<dyn model::CcAccess>),
|
cc_access: Some(Arc::new(app.midi.cc_memory.clone()) as Arc<dyn model::CcAccess>),
|
||||||
variables: Arc::clone(&app.variables),
|
variables: Arc::clone(&app.variables),
|
||||||
@@ -218,7 +224,7 @@ pub fn init(args: InitArgs) -> Init {
|
|||||||
Arc::clone(&spectrum_buffer),
|
Arc::clone(&spectrum_buffer),
|
||||||
Arc::clone(&metrics),
|
Arc::clone(&metrics),
|
||||||
initial_samples,
|
initial_samples,
|
||||||
Arc::clone(&audio_sample_pos),
|
Arc::clone(&audio_ref),
|
||||||
stream_error_tx,
|
stream_error_tx,
|
||||||
&app.audio.config.sample_paths,
|
&app.audio.config.sample_paths,
|
||||||
Arc::clone(&device_lost),
|
Arc::clone(&device_lost),
|
||||||
@@ -262,7 +268,7 @@ pub fn init(args: InitArgs) -> Init {
|
|||||||
metrics,
|
metrics,
|
||||||
scope_buffer,
|
scope_buffer,
|
||||||
spectrum_buffer,
|
spectrum_buffer,
|
||||||
audio_sample_pos,
|
audio_ref,
|
||||||
sample_rate_shared,
|
sample_rate_shared,
|
||||||
stream,
|
stream,
|
||||||
input_stream,
|
input_stream,
|
||||||
|
|||||||
10
src/main.rs
10
src/main.rs
@@ -96,7 +96,7 @@ fn main() -> io::Result<()> {
|
|||||||
let metrics = b.metrics;
|
let metrics = b.metrics;
|
||||||
let scope_buffer = b.scope_buffer;
|
let scope_buffer = b.scope_buffer;
|
||||||
let spectrum_buffer = b.spectrum_buffer;
|
let spectrum_buffer = b.spectrum_buffer;
|
||||||
let audio_sample_pos = b.audio_sample_pos;
|
let audio_ref = b.audio_ref;
|
||||||
let sample_rate_shared = b.sample_rate_shared;
|
let sample_rate_shared = b.sample_rate_shared;
|
||||||
let mut _stream = b.stream;
|
let mut _stream = b.stream;
|
||||||
let mut _input_stream = b.input_stream;
|
let mut _input_stream = b.input_stream;
|
||||||
@@ -148,7 +148,11 @@ fn main() -> io::Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
audio_sample_pos.store(0, Ordering::Relaxed);
|
audio_ref.store(Arc::new(engine::AudioRef {
|
||||||
|
sample_pos: 0,
|
||||||
|
timestamp: std::time::Instant::now(),
|
||||||
|
sample_rate: 44100.0,
|
||||||
|
}));
|
||||||
|
|
||||||
let preload_entries: Vec<(String, std::path::PathBuf)> = restart_samples
|
let preload_entries: Vec<(String, std::path::PathBuf)> = restart_samples
|
||||||
.iter()
|
.iter()
|
||||||
@@ -162,7 +166,7 @@ fn main() -> io::Result<()> {
|
|||||||
Arc::clone(&spectrum_buffer),
|
Arc::clone(&spectrum_buffer),
|
||||||
Arc::clone(&metrics),
|
Arc::clone(&metrics),
|
||||||
restart_samples,
|
restart_samples,
|
||||||
Arc::clone(&audio_sample_pos),
|
Arc::clone(&audio_ref),
|
||||||
new_error_tx,
|
new_error_tx,
|
||||||
&app.audio.config.sample_paths,
|
&app.audio.config.sample_paths,
|
||||||
Arc::clone(&device_lost),
|
Arc::clone(&device_lost),
|
||||||
|
|||||||
Reference in New Issue
Block a user