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..f9e36ef9c 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,11 @@ 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, } /// Describes the minimum and maximum supported buffer size for the device @@ -498,6 +519,7 @@ impl SupportedStreamConfig { channels: self.channels, sample_rate: self.sample_rate, buffer_size: BufferSize::Default, + audio_processing: AudioProcessing::Default, } } }