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 @@ -84,6 +84,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- **WASAPI**: `Stream::drop`, `play`, and `pause` no longer panic when the device is lost.
- **WASAPI**: Capture streams no longer report a spurious xrun on the first buffer after starting.
- **WASAPI**: Output streams no longer reject formats that the built-in resampler can convert.
- **WASAPI**: Device enumeration no longer panics if the COM enumerator fails to initialize.
- **WebAudio**: Fix stale audio output when a data callback wrote a partial buffer.
- **WebAudio**: Fix unsound `Send + Sync` on `Stream` when compiled with `+atomics`.
- **WebAudio**: Fix `Host::is_available()` always returning `true`, even in non-window contexts.
Expand Down
45 changes: 26 additions & 19 deletions src/host/wasapi/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,10 @@ impl Device {
DeviceHandle::DefaultInput => Audio::eCapture,
DeviceHandle::Specific(_) => return Ok(None),
};
let enumerator = get_enumerator().0.clone();
let enumerator = get_enumerator()
.context("Failed to get device enumerator")?
.0
.clone();
DefaultDeviceMonitor::new(enumerator, flow).map(Some)
}

Expand Down Expand Up @@ -1153,7 +1156,7 @@ impl Endpoint {
}
}

static ENUMERATOR: OnceLock<Enumerator> = OnceLock::new();
static ENUMERATOR: OnceLock<Result<Enumerator, windows::core::Error>> = OnceLock::new();

/// Returns the current default audio endpoint for `flow`, or `None` if none exists.
///
Expand All @@ -1166,30 +1169,33 @@ pub(super) fn current_default_endpoint(flow: Audio::EDataFlow) -> Option<Audio::
// SAFETY: `get_enumerator()` is a thread-safe singleton initialised at first use.
unsafe {
get_enumerator()
.ok()?
.0
.GetDefaultAudioEndpoint(flow, Audio::eConsole)
.ok()
}
}

fn get_enumerator() -> &'static Enumerator {
ENUMERATOR.get_or_init(|| {
// COM initialization is thread local, but we only need to have COM initialized in the
// thread we create the objects in
com::com_initialized();

// building the devices enumerator object
unsafe {
let enumerator = Com::CoCreateInstance::<_, Audio::IMMDeviceEnumerator>(
&Audio::MMDeviceEnumerator,
None,
Com::CLSCTX_ALL,
)
.unwrap();
fn get_enumerator() -> Result<&'static Enumerator, windows::core::Error> {
ENUMERATOR
.get_or_init(|| {
// COM initialization is thread local, but we only need to have COM initialized in the
// thread we create the objects in
com::com_initialized();

Enumerator(enumerator)
}
})
// SAFETY: `MMDeviceEnumerator` is a well-known in-process COM class; the returned
// interface pointer is only read through the safe `IMMDeviceEnumerator` wrapper.
unsafe {
Com::CoCreateInstance::<_, Audio::IMMDeviceEnumerator>(
&Audio::MMDeviceEnumerator,
None,
Com::CLSCTX_ALL,
)
}
.map(Enumerator)
})
.as_ref()
.map_err(Clone::clone)
}

// Helper function to query a DWORD property from a WASAPI device property store
Expand Down Expand Up @@ -1271,6 +1277,7 @@ impl Devices {
unsafe {
// can fail because of wrong parameters (should never happen) or out of memory
let collection = get_enumerator()
.context("Failed to get device enumerator")?
.0
.EnumAudioEndpoints(Audio::eAll, Audio::DEVICE_STATE_ACTIVE)
.context("Failed to enumerate audio endpoints")?;
Expand Down
Loading