Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ ringbuf = "0.4"
hound = "3.5"
log = "0.4"
env_logger = "0.11"
libc = "0.2"

[build-dependencies]
cc = "1"
32 changes: 4 additions & 28 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,9 @@ pub(crate) fn init_logging() {
});
}

/// Suppress noisy ALSA/JACK error messages.
/// ALSA: set a no-op error handler (persistent).
/// JACK: redirect stderr to /dev/null during cpal operations.
/// The stderr redirect is done once and stays in effect — JACK re-probes on every
/// cpal call, so we keep fd 2 pointed at /dev/null and route our own output through
/// the saved original stderr fd.
/// Suppress noisy ALSA/JACK error messages without affecting Python's stderr.
/// ALSA: set a no-op error handler (persistent, does not touch stderr).
/// JACK: set JACK_NO_START_SERVER=1 to prevent JACK from trying to start a server.
#[cfg(target_os = "linux")]
fn suppress_backend_noise() {
extern "C" {
Expand All @@ -47,36 +44,15 @@ fn suppress_backend_noise() {
snd_lib_error_set_handler(pyspeaker_silent_alsa_handler as *const std::ffi::c_void);
}

// Prevent JACK from trying to start a server
// Prevent JACK from trying to start a server (suppresses connection messages)
if std::env::var("JACK_NO_START_SERVER").is_err() {
std::env::set_var("JACK_NO_START_SERVER", "1");
}

// Redirect fd 2 (stderr) to /dev/null to suppress JACK connection messages.
// Save the original stderr so Python/log can still write to it.
unsafe {
let devnull = libc::open(c"/dev/null".as_ptr(), libc::O_WRONLY);
if devnull >= 0 {
// Save original stderr to a new fd
let saved = libc::dup(2);
// Point fd 2 at /dev/null (suppresses JACK noise)
libc::dup2(devnull, 2);
libc::close(devnull);
// Point fd 1 (stdout) stays as-is, Python print works
// Redirect env_logger to original stderr via saved fd
if saved >= 0 {
SAVED_STDERR_FD.store(saved, std::sync::atomic::Ordering::SeqCst);
}
}
}
}

#[cfg(not(target_os = "linux"))]
fn suppress_backend_noise() {}

#[cfg(target_os = "linux")]
static SAVED_STDERR_FD: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(-1);

/// List all available audio output devices.
#[pyfunction]
fn list_output_devices(py: Python<'_>) -> PyResult<Vec<Py<pyo3::types::PyDict>>> {
Expand Down