Skip to content

Commit

Permalink
set realtime priority for stream threads in alsa and wasapi
Browse files Browse the repository at this point in the history
implements #939. As someone who doesn't know a whole lot about
cpal's inner workings it looks to me like these are the only
threads that the library spawns itself, leading me to think these
are the only threads that require manual priority changes. However,
`audio_thread_priority` claims to also support MacOS, so maybe
someone could look into whether that's applicable here?
  • Loading branch information
edwloef committed Feb 22, 2025
1 parent 33b8919 commit 5b4d56f
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 13 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,14 @@ windows = { version = "0.54.0", features = [
"Win32_Media_Multimedia",
"Win32_UI_Shell_PropertiesSystem"
]}
audio_thread_priority = "0.33.0"
asio-sys = { version = "0.2", path = "asio-sys", optional = true }
num-traits = { version = "0.2.6", optional = true }

[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd"))'.dependencies]
alsa = "0.9"
libc = "0.2"
audio_thread_priority = "0.33.0"
jack = { version = "0.13.0", optional = true }

[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies]
Expand Down
23 changes: 23 additions & 0 deletions src/host/alsa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::{
SupportedBufferSize, SupportedStreamConfig, SupportedStreamConfigRange,
SupportedStreamConfigsError,
};
use audio_thread_priority::promote_current_thread_to_real_time;
use std::cell::Cell;
use std::cmp;
use std::convert::TryInto;
Expand Down Expand Up @@ -589,6 +590,17 @@ fn input_stream_worker(
error_callback: &mut (dyn FnMut(StreamError) + Send + 'static),
timeout: Option<Duration>,
) {
let buffer_size = if let BufferSize::Fixed(buffer_size) = stream.conf.buffer_size {
buffer_size
} else {
// if the buffer size isn't fixed, let audio_thread_priority choose a sensible default value
0
};

if let Err(err) = promote_current_thread_to_real_time(buffer_size, stream.conf.sample_rate.0) {
eprintln!("Failed to promote audio thread to real-time priority: {err}");
}

let mut ctxt = StreamWorkerContext::new(&timeout);
loop {
let flow =
Expand Down Expand Up @@ -640,6 +652,17 @@ fn output_stream_worker(
error_callback: &mut (dyn FnMut(StreamError) + Send + 'static),
timeout: Option<Duration>,
) {
let buffer_size = if let BufferSize::Fixed(buffer_size) = stream.conf.buffer_size {
buffer_size
} else {
// if the buffer size isn't fixed, let audio_thread_priority choose a sensible default value
0
};

if let Err(err) = promote_current_thread_to_real_time(buffer_size, stream.conf.sample_rate.0) {
eprintln!("Failed to promote audio thread to real-time priority: {err}");
}

let mut ctxt = StreamWorkerContext::new(&timeout);
loop {
let flow =
Expand Down
34 changes: 21 additions & 13 deletions src/host/wasapi/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{
BackendSpecificError, Data, InputCallbackInfo, OutputCallbackInfo, PauseStreamError,
PlayStreamError, SampleFormat, StreamError,
};
use audio_thread_priority::promote_current_thread_to_real_time;
use std::mem;
use std::ptr;
use std::sync::mpsc::{channel, Receiver, SendError, Sender};
Expand Down Expand Up @@ -269,7 +270,16 @@ fn run_input(
data_callback: &mut dyn FnMut(&Data, &InputCallbackInfo),
error_callback: &mut dyn FnMut(StreamError),
) {
boost_current_thread_priority();
let buffer_size = if let BufferSize::Fixed(buffer_size) = run_ctxt.stream.config.buffer_size {
buffer_size
} else {
// if the buffer size isn't fixed, let audio_thread_priority choose a sensible default value
0
};

if let Err(err) = promote_current_thread_to_real_time(buffer_size, stream.conf.sample_rate.0) {
eprintln!("Failed to promote audio thread to real-time priority: {err}");
}

loop {
match process_commands_and_await_signal(&mut run_ctxt, error_callback) {
Expand Down Expand Up @@ -298,7 +308,16 @@ fn run_output(
data_callback: &mut dyn FnMut(&mut Data, &OutputCallbackInfo),
error_callback: &mut dyn FnMut(StreamError),
) {
boost_current_thread_priority();
let buffer_size = if let BufferSize::Fixed(buffer_size) = run_ctxt.stream.config.buffer_size {
buffer_size
} else {
// if the buffer size isn't fixed, let audio_thread_priority choose a sensible default value
0
};

if let Err(err) = promote_current_thread_to_real_time(buffer_size, stream.conf.sample_rate.0) {
eprintln!("Failed to promote audio thread to real-time priority: {err}");
}

loop {
match process_commands_and_await_signal(&mut run_ctxt, error_callback) {
Expand All @@ -322,17 +341,6 @@ fn run_output(
}
}

fn boost_current_thread_priority() {
unsafe {
let thread_id = Threading::GetCurrentThreadId();

let _ = Threading::SetThreadPriority(
HANDLE(thread_id as isize),
Threading::THREAD_PRIORITY_TIME_CRITICAL,
);
}
}

enum ControlFlow {
Break,
Continue,
Expand Down

0 comments on commit 5b4d56f

Please # to comment.