From 5b4d56ff850504f37f5b9901151f66be6904a0a3 Mon Sep 17 00:00:00 2001 From: edwloef Date: Sat, 22 Feb 2025 11:20:02 +0100 Subject: [PATCH] set realtime priority for stream threads in alsa and wasapi 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? --- Cargo.toml | 2 ++ src/host/alsa/mod.rs | 23 +++++++++++++++++++++++ src/host/wasapi/stream.rs | 34 +++++++++++++++++++++------------- 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ecf4cf002..3339df0bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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] diff --git a/src/host/alsa/mod.rs b/src/host/alsa/mod.rs index 027b12ca7..fd17f765e 100644 --- a/src/host/alsa/mod.rs +++ b/src/host/alsa/mod.rs @@ -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; @@ -589,6 +590,17 @@ fn input_stream_worker( error_callback: &mut (dyn FnMut(StreamError) + Send + 'static), timeout: Option, ) { + 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 = @@ -640,6 +652,17 @@ fn output_stream_worker( error_callback: &mut (dyn FnMut(StreamError) + Send + 'static), timeout: Option, ) { + 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 = diff --git a/src/host/wasapi/stream.rs b/src/host/wasapi/stream.rs index 72c7836b2..5baf7f30c 100644 --- a/src/host/wasapi/stream.rs +++ b/src/host/wasapi/stream.rs @@ -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}; @@ -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) { @@ -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) { @@ -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,