From ec5928dda6afe5c9fbdb495cce957ef901f5cced Mon Sep 17 00:00:00 2001 From: Infiniwave <61062461+infiniwave@users.noreply.github.com> Date: Sat, 9 May 2026 12:57:18 -0500 Subject: [PATCH 1/2] feat(WASAPI): allow setting raw stream mode --- src/host/asio/stream.rs | 1 + src/host/wasapi/device.rs | 33 +++++++++++++++++++++++++++++---- src/lib.rs | 18 ++++++++++++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/host/asio/stream.rs b/src/host/asio/stream.rs index e64459f57..f99bc6ff0 100644 --- a/src/host/asio/stream.rs +++ b/src/host/asio/stream.rs @@ -993,6 +993,7 @@ fn check_config( channels, sample_rate, buffer_size, + .. } = config; // Validate buffer size if `Fixed` is specified. This is necessary because ASIO's diff --git a/src/host/wasapi/device.rs b/src/host/wasapi/device.rs index 563dd9249..53d636147 100644 --- a/src/host/wasapi/device.rs +++ b/src/host/wasapi/device.rs @@ -1,10 +1,10 @@ use crate::{ error::ResultExt, host::{com::ComString, ErrorCallbackArc}, - BufferSize, Data, DeviceDescription, DeviceDescriptionBuilder, DeviceDirection, DeviceId, - DeviceType, Error, ErrorKind, FrameCount, InputCallbackInfo, InterfaceType, OutputCallbackInfo, - SampleFormat, SampleRate, StreamConfig, SupportedBufferSize, SupportedStreamConfig, - SupportedStreamConfigRange, COMMON_SAMPLE_RATES, + AudioProcessing, BufferSize, Data, DeviceDescription, DeviceDescriptionBuilder, + DeviceDirection, DeviceId, DeviceType, Error, ErrorKind, FrameCount, InputCallbackInfo, + InterfaceType, OutputCallbackInfo, SampleFormat, SampleRate, StreamConfig, SupportedBufferSize, + SupportedStreamConfig, SupportedStreamConfigRange, COMMON_SAMPLE_RATES, }; impl From for DeviceDirection { @@ -665,6 +665,7 @@ impl Device { channels: format.channels, sample_rate, buffer_size: BufferSize::Default, + audio_processing: AudioProcessing::Default, }, sample_format, ) { @@ -783,6 +784,18 @@ impl Device { .build_audioclient(activation_timeout) .context("failed to build audio client")?; + if config.audio_processing == AudioProcessing::PreferRaw { + if let Ok(audio_client2) = audio_client.cast::() { + // Disable audio processing if `PreferRaw` is set and the interface supports it. + let props = Audio::AudioClientProperties { + cbSize: std::mem::size_of::() as u32, + Options: Audio::AUDCLNT_STREAMOPTIONS_RAW, + ..Default::default() + }; + audio_client2.SetClientProperties(&props).ok(); + } + } + // Note: Buffer size validation is not needed here - `IAudioClient::Initialize` // will return `AUDCLNT_E_BUFFER_SIZE_ERROR` if the buffer size is not supported. let buffer_duration = buffer_size_to_duration(&config.buffer_size, config.sample_rate); @@ -898,6 +911,18 @@ impl Device { .build_audioclient(activation_timeout) .context("failed to build audio client")?; + if config.audio_processing == AudioProcessing::PreferRaw { + if let Ok(audio_client2) = audio_client.cast::() { + // Disable audio processing if `PreferRaw` is set and the interface supports it. + let props = Audio::AudioClientProperties { + cbSize: std::mem::size_of::() as u32, + Options: Audio::AUDCLNT_STREAMOPTIONS_RAW, + ..Default::default() + }; + audio_client2.SetClientProperties(&props).ok(); + } + } + // Note: Buffer size validation is not needed here - `IAudioClient::Initialize` // will return `AUDCLNT_E_BUFFER_SIZE_ERROR` if the buffer size is not supported. let buffer_duration = buffer_size_to_duration(&config.buffer_size, config.sample_rate); diff --git a/src/lib.rs b/src/lib.rs index 37d0adbd8..033448507 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -344,6 +344,22 @@ pub enum BufferSize { Fixed(FrameCount), } +/// Specifies whether audio signal processing should be applied to the stream. +/// +/// On Windows, the audio stack applies default signal processing (e.g. echo cancellation, +/// noise suppression, automatic gain control) to audio streams. Using the [`AudioProcessing::PreferRaw`] +/// option allows applications to request that such processing be bypassed. +/// +/// If raw streams are not supported (e.g. on older Windows versions), the default behavior +/// will be used. This option has no effect on platforms that do not apply system audio processing +/// or do not provide a way to bypass it. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub enum AudioProcessing { + #[default] + Default, + PreferRaw, +} + #[cfg(all(target_arch = "wasm32", feature = "wasm-bindgen"))] impl wasm_bindgen::describe::WasmDescribe for BufferSize { fn describe() { @@ -387,6 +403,7 @@ pub struct StreamConfig { pub channels: ChannelCount, pub sample_rate: SampleRate, pub buffer_size: BufferSize, + pub audio_processing: AudioProcessing, } /// Describes the minimum and maximum supported buffer size for the device @@ -498,6 +515,7 @@ impl SupportedStreamConfig { channels: self.channels, sample_rate: self.sample_rate, buffer_size: BufferSize::Default, + audio_processing: AudioProcessing::Default, } } } From 91dc03537647d66e6f7a274f39d75046093a2229 Mon Sep 17 00:00:00 2001 From: Infiniwave <61062461+infiniwave@users.noreply.github.com> Date: Sat, 9 May 2026 13:25:26 -0500 Subject: [PATCH 2/2] fix: skip option on WASM --- src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 033448507..f9e36ef9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -403,6 +403,10 @@ pub struct StreamConfig { pub channels: ChannelCount, pub sample_rate: SampleRate, pub buffer_size: BufferSize, + #[cfg_attr( + all(target_arch = "wasm32", feature = "wasm-bindgen"), + wasm_bindgen(skip) + )] pub audio_processing: AudioProcessing, }