Skip to content

Commit 40c8c41

Browse files
committed
Add latency controls to async player
1 parent cb6f11e commit 40c8c41

File tree

4 files changed

+37
-27
lines changed

4 files changed

+37
-27
lines changed

caw/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "caw"
3-
version = "0.4.1"
3+
version = "0.5.0"
44
description = "A framework for building software-defined modular synthesizers"
55
authors = ["Stephen Sherratt <[email protected]>"]
66
license = "MIT"
@@ -26,13 +26,13 @@ caw_modules = { version = "0.2", path = "../modules" }
2626
caw_patches = { version = "0.2", path = "../patches" }
2727
caw_utils = { version = "0.2", path = "../utils" }
2828
caw_builder_proc_macros = { version = "0.1", path = "../builder-proc-macros" }
29-
caw_player = { version = "0.3", path = "../player", optional = true }
29+
caw_player = { version = "0.4", path = "../player", optional = true }
3030
caw_midi = { version = "0.2", path = "../midi", optional = true }
3131
caw_midi_live = { version = "0.2", path = "../midi-live", optional = true }
3232
caw_midi_file = { version = "0.2", path = "../midi-file", optional = true }
3333
caw_midi_serial = { version = "0.2", path = "../midi-serial", optional = true }
3434
caw_audio_file = { version = "0.2", path = "../audio-file", optional = true }
35-
caw_interactive = { version = "0.3", path = "../interactive", optional = true }
35+
caw_interactive = { version = "0.4", path = "../interactive", optional = true }
3636

3737
[dev-dependencies]
3838
anyhow = "1.0"

interactive/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "caw_interactive"
3-
version = "0.3.3"
3+
version = "0.4.0"
44
description = "Interactive keyboard and mouse control, and visualization for the caw synthesizer framework"
55
authors = ["Stephen Sherratt <[email protected]>"]
66
license = "MIT"
@@ -11,7 +11,7 @@ edition = "2021"
1111

1212
[dependencies]
1313
anyhow = "1.0"
14-
caw_player = { version = "0.3", path = "../player" }
14+
caw_player = { version = "0.4", path = "../player" }
1515
caw_core = { version = "0.3", path = "../core" }
1616
caw_computer_keyboard = { version = "0.2", path = "../computer-keyboard" }
1717
line_2d = "0.5"

player/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "caw_player"
3-
version = "0.3.1"
3+
version = "0.4.0"
44
description = "Play audio from the caw synthesizer framework"
55
authors = ["Stephen Sherratt <[email protected]>"]
66
license = "MIT"

player/src/lib.rs

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@ impl ToF32 for f64 {
2828
#[derive(Debug, Clone, Copy)]
2929
pub struct ConfigSync {
3030
/// default: 0.01
31-
pub target_latency_s: f32,
31+
pub system_latency_s: f32,
3232
}
3333

3434
impl Default for ConfigSync {
3535
fn default() -> Self {
3636
Self {
37-
target_latency_s: 0.01,
37+
system_latency_s: 0.01,
3838
}
3939
}
4040
}
@@ -65,13 +65,13 @@ impl Player {
6565

6666
fn choose_config(
6767
&self,
68-
config: ConfigSync,
68+
system_latency_s: f32,
6969
) -> anyhow::Result<StreamConfig> {
7070
let default_config = self.device.default_output_config()?;
7171
let sample_rate = default_config.sample_rate();
7272
let channels = 2;
7373
let ideal_buffer_size =
74-
(sample_rate.0 as f32 * config.target_latency_s) as u32 * channels;
74+
(sample_rate.0 as f32 * system_latency_s) as u32 * channels;
7575
// Round down to a multiple of 4. It's not clear why this is necessary but alsa complains
7676
// if the buffer size is not evenly divisible by 4.
7777
let ideal_buffer_size = ideal_buffer_size & (!3);
@@ -103,11 +103,11 @@ impl Player {
103103
>,
104104
recv_sync_command_done: mpsc::Receiver<SyncCommandDone>,
105105
config: ConfigSync,
106-
) -> anyhow::Result<cpal::Stream>
106+
) -> anyhow::Result<(Stream, StreamConfig)>
107107
where
108108
T: ToF32 + Send + Sync + Copy + 'static,
109109
{
110-
let config = self.choose_config(config)?;
110+
let config = self.choose_config(config.system_latency_s)?;
111111
log::info!("sample rate: {}", config.sample_rate.0);
112112
log::info!("num channels: {}", config.channels);
113113
log::info!("buffer size: {:?}", config.buffer_size);
@@ -137,7 +137,7 @@ impl Player {
137137
|err| eprintln!("stream error: {}", err),
138138
None,
139139
)?;
140-
Ok(stream)
140+
Ok((stream, config))
141141
}
142142

143143
fn play_signal_sync_mono_callback_raw<T, S, F>(
@@ -160,13 +160,12 @@ impl Player {
160160
mpsc::channel::<SyncCommandDone>();
161161
// buffer for sending samples from main thread to cpal thread
162162
let buffer = Arc::new(RwLock::new(Vec::new()));
163-
let stream = self.make_stream_sync_mono(
163+
let (stream, config) = self.make_stream_sync_mono(
164164
Arc::clone(&buffer),
165165
send_sync_command_request_num_samples,
166166
recv_sync_command_done,
167167
config,
168168
)?;
169-
let config = self.choose_config(config)?;
170169
stream.play()?;
171170
let mut ctx = SigCtx {
172171
sample_rate_hz: config.sample_rate.0 as f32,
@@ -238,12 +237,12 @@ impl Player {
238237
>,
239238
recv_sync_command_done: mpsc::Receiver<SyncCommandDone>,
240239
config: ConfigSync,
241-
) -> anyhow::Result<cpal::Stream>
240+
) -> anyhow::Result<(Stream, StreamConfig)>
242241
where
243242
TL: ToF32 + Send + Sync + Copy + 'static,
244243
TR: ToF32 + Send + Sync + Copy + 'static,
245244
{
246-
let config = self.choose_config(config)?;
245+
let config = self.choose_config(config.system_latency_s)?;
247246
log::info!("sample rate: {}", config.sample_rate.0);
248247
log::info!("num channels: {}", config.channels);
249248
log::info!("buffer size: {:?}", config.buffer_size);
@@ -275,7 +274,7 @@ impl Player {
275274
|err| eprintln!("stream error: {}", err),
276275
None,
277276
)?;
278-
Ok(stream)
277+
Ok((stream, config))
279278
}
280279

281280
fn play_signal_sync_stereo_callback_raw<TL, TR, SL, SR, F>(
@@ -303,13 +302,12 @@ impl Player {
303302
left: Arc::new(RwLock::new(Vec::new())),
304303
right: Arc::new(RwLock::new(Vec::new())),
305304
};
306-
let stream = self.make_stream_sync_stereo(
305+
let (stream, config) = self.make_stream_sync_stereo(
307306
buffer.map_ref(Arc::clone, Arc::clone),
308307
send_sync_command_request_num_samples,
309308
recv_sync_command_done,
310309
config,
311310
)?;
312-
let config = self.choose_config(config)?;
313311
stream.play()?;
314312
let mut ctx = SigCtx {
315313
sample_rate_hz: config.sample_rate.0 as f32,
@@ -390,8 +388,7 @@ impl Player {
390388
left: Arc::new(Mutex::new(VecDeque::new())),
391389
right: Arc::new(Mutex::new(VecDeque::new())),
392390
};
393-
let mut stream_config = self.device.default_output_config()?.config();
394-
stream_config.channels = 2;
391+
let stream_config = self.choose_config(config.system_latency_s)?;
395392
log::info!("sample rate: {}", stream_config.sample_rate.0);
396393
log::info!("num channels: {}", stream_config.channels);
397394
log::info!("buffer size: {:?}", stream_config.buffer_size);
@@ -404,6 +401,13 @@ impl Player {
404401
|l| l.lock().expect("main thread has stopped"),
405402
|r| r.lock().expect("main thread has stopped"),
406403
);
404+
if data.len() / 2 > queue.left.len() {
405+
log::warn!(
406+
"Too few samples in queue. Needs {}, has {}.",
407+
data.len() / 2,
408+
queue.left.len()
409+
);
410+
}
407411
for output_by_channel in
408412
data.chunks_mut(stream_config.channels as usize)
409413
{
@@ -418,7 +422,7 @@ impl Player {
418422
None,
419423
)?;
420424
stream.play()?;
421-
let max_num_samples = (config.max_latency_s
425+
let max_num_samples = (config.queue_latency_s
422426
* stream_config.sample_rate.0 as f32)
423427
as usize;
424428
Ok(PlayerAsyncStereo {
@@ -436,7 +440,7 @@ impl Player {
436440
config: ConfigAsync,
437441
) -> anyhow::Result<PlayerAsyncMono> {
438442
let queue = Arc::new(Mutex::new(VecDeque::new()));
439-
let stream_config = self.device.default_output_config()?.config();
443+
let stream_config = self.choose_config(config.system_latency_s)?;
440444
log::info!("sample rate: {}", stream_config.sample_rate.0);
441445
log::info!("num channels: {}", stream_config.channels);
442446
log::info!("buffer size: {:?}", stream_config.buffer_size);
@@ -461,7 +465,7 @@ impl Player {
461465
None,
462466
)?;
463467
stream.play()?;
464-
let max_num_samples = (config.max_latency_s
468+
let max_num_samples = (config.queue_latency_s
465469
* stream_config.sample_rate.0 as f32)
466470
as usize;
467471
Ok(PlayerAsyncMono {
@@ -479,12 +483,18 @@ type StereoSharedAsyncQueue =
479483
Stereo<Arc<Mutex<VecDeque<f32>>>, Arc<Mutex<VecDeque<f32>>>>;
480484

481485
pub struct ConfigAsync {
482-
pub max_latency_s: f32,
486+
/// default: 1.0 / 30.0
487+
pub queue_latency_s: f32,
488+
/// default: 0.01
489+
pub system_latency_s: f32,
483490
}
484491

485492
impl Default for ConfigAsync {
486493
fn default() -> Self {
487-
Self { max_latency_s: 0.1 }
494+
Self {
495+
queue_latency_s: 1.0 / 30.0,
496+
system_latency_s: 0.01,
497+
}
488498
}
489499
}
490500

0 commit comments

Comments
 (0)