diff --git a/CHANGELOG.md b/CHANGELOG.md index 01c3ee5a4..a073ceebe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **AudioWorklet**: Fix stale output when the data callback grows Wasm memory. - **JACK**: Channel enumeration is capped at the physical system port count again. - **WASAPI**: Device enumeration no longer panics if the COM enumerator fails to initialize. +- **WASAPI**: Revert "Default device changes no longer report `DeviceChanged`" as it was misinformed. + Default device streams **do** reroute automatically. ## [0.18.2] - 2026-08-16 diff --git a/src/host/wasapi/stream.rs b/src/host/wasapi/stream.rs index ad106b649..796941145 100644 --- a/src/host/wasapi/stream.rs +++ b/src/host/wasapi/stream.rs @@ -38,12 +38,12 @@ fn get_current_default(flow: Audio::EDataFlow) -> Option { super::device::current_default_endpoint(flow) } -/// Fires a Windows auto-reset event when the system default audio device changes. +/// Fires a Windows auto-reset event when the system default audio device changes, allowing +/// the stream run loop to deliver `ErrorKind::DeviceChanged` to the caller. pub(crate) struct DefaultDeviceMonitor { enumerator: Audio::IMMDeviceEnumerator, client: Audio::IMMNotificationClient, event: Foundation::HANDLE, - pub(crate) flow: Audio::EDataFlow, pub(crate) pending_device_changed: Arc, } @@ -79,7 +79,6 @@ impl DefaultDeviceMonitor { enumerator, client, event, - flow, pending_device_changed, }) } @@ -211,8 +210,9 @@ pub struct Stream { // QueryPerformanceFrequency result, cached at construction (constant for the system lifetime). qpc_frequency: u64, - // Present for default-device streams. Dropped after the run thread joins, ensuring the - // HANDLE is not waited on when it is closed. + // Present for default-device streams; fires `ErrorKind::DeviceChanged` when the system + // default changes. Dropped after the run thread joins, ensuring the HANDLE is not + // waited on when it is closed. _default_device_monitor: Option, // Latch that ensures no callbacks fire before the caller receives the `Stream` handle. @@ -252,12 +252,10 @@ struct RunContext { commands: Receiver, - // Set by a device-change notification callback when SetEvent fails. + // Set by a device-change notification callback when SetEvent fails. The audio loop delivers + // DeviceChanged on its next iteration. pending_device_changed: Option>, - // Set when this stream tracks the default device, rather than a pinned one. - default_device_flow: Option, - // Owned here so the worker thread closes it on exit in a self-join case. pending_scheduled_event: Foundation::HANDLE, } @@ -342,13 +340,11 @@ impl Stream { let pending_device_changed = default_device_monitor .as_ref() .map(|m| m.pending_device_changed.clone()); - let default_device_flow = default_device_monitor.as_ref().map(|m| m.flow); let run_context = RunContext { handles, stream: stream_inner, commands: rx, pending_device_changed, - default_device_flow, pending_scheduled_event, }; @@ -414,13 +410,11 @@ impl Stream { let pending_device_changed = default_device_monitor .as_ref() .map(|m| m.pending_device_changed.clone()); - let default_device_flow = default_device_monitor.as_ref().map(|m| m.flow); let run_context = RunContext { handles, stream: stream_inner, commands: rx, pending_device_changed, - default_device_flow, pending_scheduled_event, }; @@ -727,14 +721,6 @@ fn boost_current_thread_priority( } } -// WASAPI never rebinds the IAudioClient, so report what's actually true instead of DeviceChanged. -fn default_device_change_error(flow: Option) -> Error { - match flow.and_then(get_current_default) { - None => ErrorKind::DeviceNotAvailable.into(), - Some(_) => ErrorKind::StreamInvalidated.into(), - } -} - fn process_commands_and_await_signal( run_context: &mut RunContext, error_callback: &ErrorCallbackArc, @@ -753,7 +739,7 @@ fn process_commands_and_await_signal( if flag.swap(false, Ordering::Relaxed) { emit_error( error_callback, - default_device_change_error(run_context.default_device_flow), + Error::with_message(ErrorKind::DeviceChanged, "Default audio device changed"), ); } } @@ -774,7 +760,7 @@ fn process_commands_and_await_signal( if handle_idx >= 2 { emit_error( error_callback, - default_device_change_error(run_context.default_device_flow), + Error::with_message(ErrorKind::DeviceChanged, "Default audio device changed"), ); return ControlFlow::Continue(false); }