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
7 changes: 3 additions & 4 deletions src/host/jack/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ pub type Devices = std::vec::IntoIter<Device>;
///
/// # JACK-Specific Configuration
///
/// Unlike other backends, JACK provides configuration options to control connection and server
/// behavior:
/// JACK provides configuration options to control connection and server behavior:
/// - Port auto-connection via [`set_connect_automatically`](Host::set_connect_automatically)
/// - Server auto-start via [`set_start_server_automatically`](Host::set_start_server_automatically)
#[derive(Debug)]
Expand Down Expand Up @@ -57,8 +56,8 @@ impl Host {
/// ports.
///
/// When enabled (default), output streams connect to system playback ports and input streams
/// connect to system capture ports automatically. When disabled, applications must manually
/// connect ports using JACK tools or APIs.
/// connect to system capture ports automatically. When disabled, users must manually connect
/// ports using JACK tools or APIs.
///
/// Default: `true`
pub fn set_connect_automatically(&mut self, do_connect: bool) {
Expand Down
10 changes: 8 additions & 2 deletions src/host/pipewire/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::{
hash::{Hash, Hasher},
rc::Rc,
sync::{
atomic::{AtomicU64, Ordering},
atomic::{AtomicBool, AtomicU64, Ordering},
mpsc, Arc,
},
thread,
Expand Down Expand Up @@ -82,12 +82,14 @@ pub struct Device {
interface_type: InterfaceType,
address: Option<String>,
driver: Option<String>,
connect_automatically: Arc<AtomicBool>,
}

impl Device {
pub(crate) fn class(&self) -> Class {
self.class
}

fn sink_default() -> Self {
Self {
node_name: "sink_default".to_owned(),
Expand Down Expand Up @@ -371,6 +373,7 @@ impl DeviceTrait for Device {
sample_format,
last_quantum: last_quantum_clone,
start,
connect_automatically: device.connect_automatically.load(Ordering::Relaxed),
},
data_callback,
error_callback,
Expand Down Expand Up @@ -537,6 +540,7 @@ impl DeviceTrait for Device {
sample_format,
last_quantum: last_quantum_clone,
start,
connect_automatically: device.connect_automatically.load(Ordering::Relaxed),
},
data_callback,
error_callback,
Expand Down Expand Up @@ -713,7 +717,7 @@ fn remote_props() -> Option<pw::properties::PropertiesBox> {
Some(props)
}

pub fn init_devices() -> Option<Vec<Device>> {
pub fn init_devices(connect_automatically: Arc<AtomicBool>) -> Option<Vec<Device>> {
let _pw = PwInitGuard::new();
let mainloop = pw::main_loop::MainLoopRc::new(None).ok()?;
let context = pw::context::ContextRc::new(&mainloop, None).ok()?;
Expand Down Expand Up @@ -1029,6 +1033,7 @@ pub fn init_devices() -> Option<Vec<Device>> {
device.quantum = settings.quantum;
device.min_quantum = settings.min_quantum;
device.max_quantum = settings.max_quantum;
device.connect_automatically = connect_automatically.clone();
}

// Resolve each discovered hardware node: global settings apply unless the node
Expand All @@ -1043,6 +1048,7 @@ pub fn init_devices() -> Option<Vec<Device>> {
device.quantum = overrides.quantum.unwrap_or(settings.quantum);
device.min_quantum = settings.min_quantum;
device.max_quantum = settings.max_quantum;
device.connect_automatically = connect_automatically.clone();
device
}),
);
Expand Down
34 changes: 32 additions & 2 deletions src/host/pipewire/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};

use device::{init_devices, Class, Device, Devices};
use stream::PwInitGuard;

Expand All @@ -7,20 +12,45 @@ mod device;
mod stream;
mod utils;

/// The PipeWire host, providing access to PipeWire audio devices.
///
/// # PipeWire-Specific Configuration
///
/// PipeWire provides a configuration option to control graph connection behavior:
/// - Port auto-connection via [`set_connect_automatically`](Host::set_connect_automatically)
pub struct Host {
// Keeps PipeWire initialized for the lifetime of the host, preventing
// pw_deinit() from running between device enumeration and stream creation.
_pw: PwInitGuard,
devices: Vec<Device>,
connect_automatically: Arc<AtomicBool>,
}

impl Host {
pub fn new() -> Result<Self, Error> {
let _pw = PwInitGuard::new();
let devices = init_devices().ok_or_else(|| {
let connect_automatically = Arc::new(AtomicBool::new(true));
let devices = init_devices(connect_automatically.clone()).ok_or_else(|| {
Error::with_message(ErrorKind::HostUnavailable, "PipeWire is not available")
})?;
Ok(Self { _pw, devices })
Ok(Self {
_pw,
devices,
connect_automatically,
})
}

/// Configures whether created streams should automatically connect to system playback/capture
/// nodes via the session manager.
///
/// When enabled (default), PipeWire's session manager links the stream to the appropriate sink
/// or source automatically. When disabled, the stream node is registered in the graph but left
/// unlinked; users must then manually connect ports using PipeWire tools or session manager
/// APIs.
///
/// Default: `true`
pub fn set_connect_automatically(&mut self, connect: bool) {
self.connect_automatically.store(connect, Ordering::Relaxed);
}
}

Expand Down
13 changes: 11 additions & 2 deletions src/host/pipewire/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,7 @@ pub struct ConnectParams {
pub sample_format: SampleFormat,
pub last_quantum: Arc<AtomicU64>,
pub start: Instant,
pub connect_automatically: bool,
}

pub fn connect_output<D, E>(
Expand All @@ -514,6 +515,7 @@ where
sample_format,
last_quantum,
start,
connect_automatically,
} = params;

let mainloop = pw::main_loop::MainLoopRc::new(None)?;
Expand Down Expand Up @@ -718,7 +720,10 @@ where
// RT_PROCESS is intentionally absent: with add_local_listener the process callback always
// runs on this mainloop thread, not the separate data-loop thread RT_PROCESS creates.
// The worker thread is promoted to RT after signalling the main thread (see device.rs).
let flags = pw::stream::StreamFlags::AUTOCONNECT | pw::stream::StreamFlags::MAP_BUFFERS;
let mut flags = pw::stream::StreamFlags::MAP_BUFFERS;
if connect_automatically {
flags |= pw::stream::StreamFlags::AUTOCONNECT;
}

stream.connect(pw::spa::utils::Direction::Output, None, flags, &mut params)?;

Expand Down Expand Up @@ -751,6 +756,7 @@ where
sample_format,
last_quantum,
start,
connect_automatically,
} = params;

let mainloop = pw::main_loop::MainLoopRc::new(None)?;
Expand Down Expand Up @@ -938,7 +944,10 @@ where
// RT_PROCESS is intentionally absent: with add_local_listener the process callback always
// runs on this mainloop thread, not the separate data-loop thread RT_PROCESS creates.
// The worker thread is promoted to RT after signalling the main thread (see device.rs).
let flags = pw::stream::StreamFlags::AUTOCONNECT | pw::stream::StreamFlags::MAP_BUFFERS;
let mut flags = pw::stream::StreamFlags::MAP_BUFFERS;
if connect_automatically {
flags |= pw::stream::StreamFlags::AUTOCONNECT;
}

stream.connect(pw::spa::utils::Direction::Input, None, flags, &mut params)?;

Expand Down
12 changes: 12 additions & 0 deletions src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ pub use self::platform_impl::*;
#[cfg_attr(docsrs, doc(cfg(feature = "jack")))]
pub use crate::host::jack::Host as JackHost;

#[cfg(all(
any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
),
feature = "pipewire",
))]
#[cfg_attr(docsrs, doc(cfg(feature = "pipewire")))]
pub use crate::host::pipewire::Host as PipeWireHost;

#[cfg(feature = "custom")]
pub use crate::host::custom::{Device as CustomDevice, Host as CustomHost, Stream as CustomStream};

Expand Down
Loading