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: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Timestamps now stay monotonic across device and graph changes.
- **ALSA**: A nonzero but sub-millisecond stream timeout is no longer treated as a non-blocking poll.
- **PipeWire**: Streams for a specific device no longer auto-reroute if it disappears.
- **visionOS**: The CoreAudio backend now builds.
- **WASAPI**: Reported buffer sizes are no longer off by one frame.
- **WASAPI**: `Stream::drop`, `play`, and `pause` no longer panic when the device is lost.
Expand Down
6 changes: 6 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ pub enum ErrorKind {

/// The active audio route changed and the stream was automatically rerouted.
/// The stream remains active and no rebuild is required.
///
/// Only fires for a stream built from the default device, on backends that support following
/// it. A stream built from a specific device does not follow a replacement; if that device
/// disappears, it reports [`DeviceNotAvailable`] instead.
///
/// [`DeviceNotAvailable`]: ErrorKind::DeviceNotAvailable
DeviceChanged,

/// The requested audio device is not available.
Expand Down
12 changes: 8 additions & 4 deletions src/host/pipewire/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,10 @@ impl DeviceTrait for Device {
last_quantum: last_quantum_clone,
start,
connect_automatically: device.connect_automatically.load(Ordering::Relaxed),
is_default_device: matches!(
device.class(),
Class::DefaultSink | Class::DefaultInput | Class::DefaultOutput
),
},
data_callback,
error_callback,
Expand All @@ -423,7 +427,6 @@ impl DeviceTrait for Device {
error_callback,
pending_device_changed,
invalidated,
is_default_device,
} = stream_data;

let default_monitor = if let Some(key) = device.default_metadata_key() {
Expand All @@ -446,7 +449,6 @@ impl DeviceTrait for Device {
} else {
None
};
is_default_device.store(default_monitor.is_some(), Ordering::Relaxed);
let stream_clone = stream.clone();
let mainloop_rc1 = mainloop.clone();
let error_callback_cmd = error_callback.clone();
Expand Down Expand Up @@ -575,6 +577,10 @@ impl DeviceTrait for Device {
last_quantum: last_quantum_clone,
start,
connect_automatically: device.connect_automatically.load(Ordering::Relaxed),
is_default_device: matches!(
device.class(),
Class::DefaultSink | Class::DefaultInput | Class::DefaultOutput
),
},
data_callback,
error_callback,
Expand All @@ -599,7 +605,6 @@ impl DeviceTrait for Device {
error_callback,
pending_device_changed,
invalidated,
is_default_device,
} = stream_data;

let default_monitor = if let Some(key) = device.default_metadata_key() {
Expand All @@ -622,7 +627,6 @@ impl DeviceTrait for Device {
} else {
None
};
is_default_device.store(default_monitor.is_some(), Ordering::Relaxed);
let stream_clone = stream.clone();
let mainloop_rc1 = mainloop.clone();
let error_callback_cmd = error_callback.clone();
Expand Down
22 changes: 13 additions & 9 deletions src/host/pipewire/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ pub struct UserData<D> {
format: AudioInfoRaw,
last_quantum: Arc<AtomicU64>,
start: Instant,
is_default_device: Arc<AtomicBool>,
is_default_device: bool,
has_connected: bool,
invalidated: Arc<AtomicBool>,
pending_device_changed: Arc<AtomicBool>,
Expand Down Expand Up @@ -253,7 +253,7 @@ impl<D> UserData<D> {
StreamState::Unconnected => {
// Let the metadata monitor fire for default-device streams
if self.has_connected
&& !self.is_default_device.load(Ordering::Relaxed)
&& !self.is_default_device
&& !self.invalidated.swap(true, Ordering::Relaxed)
{
emit_error(
Expand Down Expand Up @@ -376,7 +376,6 @@ pub struct StreamData<D> {
pub error_callback: ErrorCallbackArc,
pub pending_device_changed: Arc<AtomicBool>,
pub invalidated: Arc<AtomicBool>,
pub is_default_device: Arc<AtomicBool>,
}

/// Fallback timestamp using elapsed time since stream creation.
Expand Down Expand Up @@ -525,6 +524,7 @@ pub struct ConnectParams {
pub last_quantum: Arc<AtomicU64>,
pub start: Instant,
pub connect_automatically: bool,
pub is_default_device: bool,
}

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

let mainloop = MainLoopRc::new(None)?;
Expand All @@ -553,7 +554,6 @@ where
let invalidated = Arc::new(AtomicBool::new(false));

let pending_device_changed = Arc::new(AtomicBool::new(false));
let is_default_device = Arc::new(AtomicBool::new(false));

let core_monitor = {
let invalidated_core = invalidated.clone();
Expand Down Expand Up @@ -582,7 +582,7 @@ where
last_quantum,
start,
invalidated: invalidated.clone(),
is_default_device: is_default_device.clone(),
is_default_device,
has_connected: false,
pending_device_changed: pending_device_changed.clone(),
#[cfg(feature = "realtime")]
Expand Down Expand Up @@ -756,6 +756,9 @@ where
if connect_automatically {
flags |= StreamFlags::AUTOCONNECT;
}
if !is_default_device {
flags |= StreamFlags::DONT_RECONNECT;
}

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

Expand All @@ -769,7 +772,6 @@ where
error_callback: error_callback_out,
pending_device_changed,
invalidated,
is_default_device,
})
}

Expand All @@ -789,6 +791,7 @@ where
last_quantum,
start,
connect_automatically,
is_default_device,
} = params;

let mainloop = MainLoopRc::new(None)?;
Expand All @@ -799,7 +802,6 @@ where
let invalidated = Arc::new(AtomicBool::new(false));

let pending_device_changed = Arc::new(AtomicBool::new(false));
let is_default_device = Arc::new(AtomicBool::new(false));

let core_monitor = {
let invalidated_core = invalidated.clone();
Expand Down Expand Up @@ -828,7 +830,7 @@ where
last_quantum,
start,
invalidated: invalidated.clone(),
is_default_device: is_default_device.clone(),
is_default_device,
has_connected: false,
pending_device_changed: pending_device_changed.clone(),
#[cfg(feature = "realtime")]
Expand Down Expand Up @@ -985,6 +987,9 @@ where
if connect_automatically {
flags |= StreamFlags::AUTOCONNECT;
}
if !is_default_device {
flags |= StreamFlags::DONT_RECONNECT;
}

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

Expand All @@ -998,6 +1003,5 @@ where
error_callback: error_callback_out,
pending_device_changed,
invalidated,
is_default_device,
})
}
16 changes: 16 additions & 0 deletions src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,27 @@ pub trait HostTrait {
/// The default input audio device on the system.
///
/// Returns `None` if no input device is available.
///
/// Some backends reroute a stream built from this device to the new default device when it
/// changes; capture continues there, and [`ErrorKind::DeviceChanged`] is reported. Other
/// backends report [`ErrorKind::DeviceNotAvailable`] instead, and the caller must rebuild the
/// stream.
///
/// [`ErrorKind::DeviceChanged`]: crate::ErrorKind::DeviceChanged
/// [`ErrorKind::DeviceNotAvailable`]: crate::ErrorKind::DeviceNotAvailable
fn default_input_device(&self) -> Option<Self::Device>;

/// The default output audio device on the system.
///
/// Returns `None` if no output device is available.
///
/// Some backends reroute a stream built from this device to the new default device when it
/// changes; playback continues there, and [`ErrorKind::DeviceChanged`] is reported. Other
/// backends report [`ErrorKind::DeviceNotAvailable`] instead, and the caller must rebuild the
/// stream.
///
/// [`ErrorKind::DeviceChanged`]: crate::ErrorKind::DeviceChanged
/// [`ErrorKind::DeviceNotAvailable`]: crate::ErrorKind::DeviceNotAvailable
fn default_output_device(&self) -> Option<Self::Device>;

/// An iterator yielding all `Device`s currently available to the system that support one or more
Expand Down
Loading