pub struct AudioClient { /* private fields */ }Expand description
Struct wrapping an IAudioClient.
Implementations§
Source§impl AudioClient
impl AudioClient
Sourcepub fn new_application_loopback_client(
process_id: u32,
include_tree: bool,
) -> Result<Self, WasapiError>
pub fn new_application_loopback_client( process_id: u32, include_tree: bool, ) -> Result<Self, WasapiError>
Creates a loopback capture AudioClient for a specific process.
include_tree is equivalent to PROCESS_LOOPBACK_MODE.
If true, the loopback capture client will capture audio from the target process and all its child processes,
if false only audio from the target process is captured.
On versions of Windows prior to Windows 10, the thread calling this function must called in a COM Single-Threaded Apartment (STA).
Additionally when calling AudioClient::initialize_client on the client returned by this method, the caller must use Direction::Capture, and ShareMode::Shared. Finally calls to AudioClient::get_device_period do not work, however the period passed by the caller to AudioClient::initialize_client is irrelevant.
§Non-functional methods
In process loopback mode, the functionality of the AudioClient is limited. The following methods either do not work, or return incorrect results:
get_mixformatjust returnsNot implemented.is_supportedjust returnsNot implementedeven if the format and mode work.is_supported_exclusive_with_quirksjust returnsUnable to find a supported format.get_device_periodjust returnsNot implemented.calculate_aligned_period_nearjust returnsNot implementedeven for values that would later work.get_buffer_sizereturns huge values like 3131961357 but no error.get_current_paddingjust returnsNot implemented.get_available_space_in_framesjust returnsClient has not been initialisedeven if it has.get_audiorenderclientjust returnsNo such interface supported.get_audiosessioncontroljust returnsNo such interface supported.get_audioclockjust returnsNo such interface supported.
Note that AudioClient::is_aec_supported also needs an initialised client, in this mode as well as in the normal one.
§Example
Marked no_run because it needs a running audio service, which a build
machine may not have. The behaviour is covered by the device backed tests.
use wasapi::{WaveFormat, SampleType, AudioClient, Direction, StreamMode, initialize_mta};
let desired_format = WaveFormat::new(32, 32, &SampleType::Float, 44100, 2, None);
let buffer_duration_hns = 200_000; // 20ms in hundreds of nanoseconds
let autoconvert = true;
let include_tree = false;
let process_id = std::process::id();
initialize_mta().ok().unwrap(); // Don't do this on a UI thread
let mut audio_client = AudioClient::new_application_loopback_client(process_id, include_tree).unwrap();
let mode = StreamMode::EventsShared { autoconvert, buffer_duration_hns };
audio_client.initialize_client(
&desired_format,
&Direction::Capture,
&mode
).unwrap();Sourcepub fn from_iaudioclient(client: IAudioClient, direction: Direction) -> Self
pub fn from_iaudioclient(client: IAudioClient, direction: Direction) -> Self
Creates an AudioClient from an IAudioClient that has already been activated.
This is for the cases where the device is found by some other means than the
DeviceCollection of this crate.
The WinRT MediaDevice and DeviceInformation classes give a device path
that can be passed to
ActivateAudioInterfaceAsync,
and the resulting interface can then be wrapped here.
The streaming code of this crate works the same as for a client from
Device::get_iaudioclient.
The direction is the direction of the endpoint the client was activated from,
not the direction of the stream.
A loopback capture stream is set up by passing Direction::Render here,
and then Direction::Capture to AudioClient::initialize_client,
exactly as for a render device from the DeviceCollection.
Getting this wrong makes AudioClient::initialize_client either
return an error or silently set up the wrong kind of stream.
The caller is responsible for COM being initialised on the thread, see initialize_mta and initialize_sta, and for the interface having been activated on a compatible apartment.
§Non-functional methods
There is no Device behind a client made this way, so everything that needs one is unavailable. This includes the CapabilityProbe, the DataRanges, the device state, and the endpoint properties. Use the WinRT APIs, or whatever provided the interface, for those.
§Example
Marked no_run because it needs a running audio service, which a build
machine may not have.
use wasapi::{AudioClient, Direction, IAudioClient, StreamMode, initialize_mta};
initialize_mta().ok().unwrap(); // Don't do this on a UI thread
// Activate the interface in some other way, for example through
// ActivateAudioInterfaceAsync with a device path from WinRT.
let interface: IAudioClient = activate_somehow();
let mut audio_client = AudioClient::from_iaudioclient(interface, Direction::Render);
let format = audio_client.get_mixformat().unwrap();
let (default_period, _min_period) = audio_client.get_device_period().unwrap();
let mode = StreamMode::EventsShared {
autoconvert: true,
buffer_duration_hns: default_period,
};
audio_client.initialize_client(&format, &Direction::Render, &mode).unwrap();Sourcepub fn as_iaudioclient(&self) -> &IAudioClient
pub fn as_iaudioclient(&self) -> &IAudioClient
Returns the wrapped IAudioClient, for reaching the parts of the interface
that this crate does not wrap.
Every method of the interface is itself unsafe.
It is a reference counted COM pointer, so cloning it is cheap.
An AudioClient caches the direction, the sharing mode, the timing mode and the frame size that AudioClient::initialize_client was called with, and the rest of the crate trusts that cache. Calling the interface directly goes behind it, and nothing detects or reports the divergence.
Never call Initialize on this interface.
Always set the stream up with AudioClient::initialize_client,
and use this interface only for what the crate does not cover.
Initialising it directly leaves the frame size unset,
which makes AudioCaptureClient::read_from_device panic and
AudioRenderClient::write_to_device play noise without reporting an error.
Sourcepub fn get_mixformat(&self) -> Result<WaveFormat, WasapiError>
pub fn get_mixformat(&self) -> Result<WaveFormat, WasapiError>
Get MixFormat of the device. This is the format the device uses in shared mode and should always be accepted.
Sourcepub fn is_supported(
&self,
wave_fmt: &WaveFormat,
sharemode: &ShareMode,
) -> Result<Option<WaveFormat>, WasapiError>
pub fn is_supported( &self, wave_fmt: &WaveFormat, sharemode: &ShareMode, ) -> Result<Option<WaveFormat>, WasapiError>
Check if a format is supported. If it’s directly supported, this returns Ok(None). If not, but a similar format is, then the nearest matching supported format is returned as Ok(Some(WaveFormat)).
NOTE: For exclusive mode, this function may not always give the right result for 1- and 2-channel formats. From the Microsoft documentation:
For exclusive-mode formats, the method queries the device driver. Some device drivers will report that they support a 1-channel or 2-channel PCM format if the format is specified by a stand-alone WAVEFORMATEX structure, but will reject the same format if it is specified by a WAVEFORMATEXTENSIBLE structure. To obtain reliable results from these drivers, exclusive-mode applications should call IsFormatSupported twice for each 1-channel or 2-channel PCM format. One call should use a stand-alone WAVEFORMATEX structure to specify the format, and the other call should use a WAVEFORMATEXTENSIBLE structure to specify the same format.
If the first call fails, use WaveFormat::to_waveformatex to get a copy of the WaveFormat in the simpler WAVEFORMATEX representation. Then call this function again with the new WafeFormat structure. If the driver then reports that the format is supported, use the original WaveFormat structure when calling AudioClient::initialize_client.
Note that WaveFormat::to_waveformatex returns an error for formats that a WAVEFORMATEX cannot describe without ambiguity. A 24 bit format must never be queried as WAVEFORMATEX, since a driver may then accept it and treat it as 24 bit padded in 32 bit containers.
See also the helper function is_supported_exclusive_with_quirks.
Sourcepub fn is_supported_exclusive_with_quirks(
&self,
wave_fmt: &WaveFormat,
) -> Result<WaveFormat, WasapiError>
pub fn is_supported_exclusive_with_quirks( &self, wave_fmt: &WaveFormat, ) -> Result<WaveFormat, WasapiError>
A helper function for checking if a format is supported.
It calls is_supported several times with different options
in order to find a format that the device accepts.
The alternatives it tries are:
- The format as given.
- If one or two channels, try with the format as WAVEFORMATEX. This is skipped for formats that a WAVEFORMATEX cannot describe without ambiguity, such as 24 bit samples, see WaveFormat::to_waveformatex.
- Try with different channel masks, see make_channelmasks:
- If channels <= 8: Recommended mask(s) from ksmedia.h.
- If channels <= 18: Simple mask.
- Zero mask, which assigns no speaker positions. Few devices accept it, but for some it is the only one that works.
If an accepted format is found, this is returned. The returned format carries the mask that was accepted, which may differ from the one that was asked for. An error means no accepted format was found.
Sourcepub fn get_device_period(&self) -> Result<(i64, i64), WasapiError>
pub fn get_device_period(&self) -> Result<(i64, i64), WasapiError>
Get default and minimum periods in 100-nanosecond units
Sourcepub fn calculate_aligned_period_near(
&self,
desired_period: i64,
align_bytes: Option<u32>,
wave_fmt: &WaveFormat,
) -> Result<i64, WasapiError>
pub fn calculate_aligned_period_near( &self, desired_period: i64, align_bytes: Option<u32>, wave_fmt: &WaveFormat, ) -> Result<i64, WasapiError>
Helper function for calculating a period size in 100-nanosecond units that is near a desired value, and always larger than the minimum value supported by the device. The returned value leads to a device buffer size that is aligned both to the frame size of the format, and the optional align_bytes value. This parameter is used for devices that require the buffer size to be a multiple of a certain number of bytes. Give None, Some(0) or Some(1) if the device has no special requirements for the alignment. For example, all devices following the Intel High Definition Audio specification require buffer sizes in multiples of 128 bytes.
See also the playnoise_exclusive example.
Sourcepub fn initialize_client(
&mut self,
wavefmt: &WaveFormat,
direction: &Direction,
stream_mode: &StreamMode,
) -> Result<(), WasapiError>
pub fn initialize_client( &mut self, wavefmt: &WaveFormat, direction: &Direction, stream_mode: &StreamMode, ) -> Result<(), WasapiError>
Initialize an AudioClient for the given direction, sharemode, timing mode and format. This method wraps IAudioClient::Initialize().
§Sharing mode
In WASAPI, sharing mode determines how multiple audio applications interact with the same audio endpoint. There are two primary sharing modes: Shared and Exclusive.
§Shared Mode (ShareMode::Shared)
- Multiple applications can simultaneously access the audio device.
- The system’s audio engine mixes the audio streams from all applications.
- The application has no control over the sample rate and format used by the device.
- The audio engine can perform automatic sample rate and format conversion, meaning that almost any format can be accepted.
§Exclusive Mode (ShareMode::Exclusive)
- Only one application can access the audio device at a time.
- This mode provides lower latency but requires the device to support the exact audio format requested.
- The application can control the sample rate and format used by the device.
§Timing mode
Event-driven mode and polling mode are two different ways of handling audio buffer updates.
§Event-Driven Mode (TimingMode::Events)
- In this mode, the application registers an event handle using AudioClient::set_get_eventhandle().
- The system signals this event whenever a new buffer of audio data is ready to be processed (either for rendering or capture).
- The application’s audio processing thread waits on this event (Handle::wait_for_event()).
- When the event is signaled, the thread wakes up to processes the available data, and then goes back to waiting.
- This mode is generally more efficient because the application only wakes up when there’s work to do.
- It’s suitable for real-time audio applications where low latency is important.
- This mode is not supported by all devices in exclusive mode (but all devices are supported in shared mode).
- In exclusive mode, devices using the standard Windows USB audio driver can have issues with stuttering sound on playback.
§Polling Mode (TimingMode::Polling)
- In this mode, the application periodically calls AudioClient::get_current_padding() (for capture) or AudioClient::get_available_space_in_frames() (for playback) to check how much data is available or required.
- The thread processes the data, and then goes to sleep, for example by calling std::thread::sleep().
- This mode is less efficient and is more prone to glitches when running at low latency.
- In exclusive mode, it supports more devices, and does not have the stuttering issue with USB audio devices.
Sourcepub fn set_get_eventhandle(&self) -> Result<Handle, WasapiError>
pub fn set_get_eventhandle(&self) -> Result<Handle, WasapiError>
Create and return an event handle for an AudioClient. This is required when using an AudioClient initialized for event driven mode, TimingMode::Events.
Sourcepub fn get_buffer_size(&self) -> Result<u32, WasapiError>
pub fn get_buffer_size(&self) -> Result<u32, WasapiError>
Get buffer size in frames, see IAudioClient::GetBufferSize.
Sourcepub fn get_current_padding(&self) -> Result<u32, WasapiError>
pub fn get_current_padding(&self) -> Result<u32, WasapiError>
Get current padding in frames. This represents the number of frames currently in the buffer, for both capture and render devices. The exact meaning depends on how the AudioClient was initialized, see IAudioClient::GetCurrentPadding.
Sourcepub fn get_available_space_in_frames(&self) -> Result<u32, WasapiError>
pub fn get_available_space_in_frames(&self) -> Result<u32, WasapiError>
Get buffer size minus padding in frames. Use this to find out how much free space is available in the buffer.
Sourcepub fn start_stream(&self) -> Result<(), WasapiError>
pub fn start_stream(&self) -> Result<(), WasapiError>
Start the stream on an IAudioClient
For playback, fill the buffer with data before starting the stream, see Rendering a Stream. Use AudioClient::get_available_space_in_frames() to get the number of frames to write. When using TimingMode::Events, the event should then be waited for at the start of the playback loop, before writing more data.
Sourcepub fn stop_stream(&self) -> Result<(), WasapiError>
pub fn stop_stream(&self) -> Result<(), WasapiError>
Stop the stream on an IAudioClient
Sourcepub fn reset_stream(&self) -> Result<(), WasapiError>
pub fn reset_stream(&self) -> Result<(), WasapiError>
Reset the stream on an IAudioClient
Sourcepub fn get_audiorenderclient(&self) -> Result<AudioRenderClient, WasapiError>
pub fn get_audiorenderclient(&self) -> Result<AudioRenderClient, WasapiError>
Get a rendering (playback) client
Sourcepub fn get_audiocaptureclient(&self) -> Result<AudioCaptureClient, WasapiError>
pub fn get_audiocaptureclient(&self) -> Result<AudioCaptureClient, WasapiError>
Get a capture client
Sourcepub fn get_audiosessioncontrol(
&self,
) -> Result<AudioSessionControl, WasapiError>
pub fn get_audiosessioncontrol( &self, ) -> Result<AudioSessionControl, WasapiError>
Get the AudioSessionControl
Sourcepub fn get_audioclock(&self) -> Result<AudioClock, WasapiError>
pub fn get_audioclock(&self) -> Result<AudioClock, WasapiError>
Get the AudioClock
Sourcepub fn get_direction(&self) -> Direction
pub fn get_direction(&self) -> Direction
Get the direction for this AudioClient
Get the sharemode for this AudioClient. The sharemode is decided when the client is initialized.
Sourcepub fn get_timing_mode(&self) -> Option<TimingMode>
pub fn get_timing_mode(&self) -> Option<TimingMode>
Get the timing mode for this AudioClient. The mode is decided when the client is initialized.
Sourcepub fn get_aec_control(
&self,
) -> Result<AcousticEchoCancellationControl, WasapiError>
pub fn get_aec_control( &self, ) -> Result<AcousticEchoCancellationControl, WasapiError>
Get the Acoustic Echo Cancellation Control. If it succeeds, the capture endpoint supports control of the loopback reference endpoint for AEC.
Sourcepub fn get_audio_effects_manager(
&self,
) -> Result<AudioEffectsManager, WasapiError>
pub fn get_audio_effects_manager( &self, ) -> Result<AudioEffectsManager, WasapiError>
Get the Audio Effects Manager.
Sourcepub fn set_properties(
&self,
properties: AudioClientProperties,
) -> Result<(), WasapiError>
pub fn set_properties( &self, properties: AudioClientProperties, ) -> Result<(), WasapiError>
Set properties of the client’s audio stream.
Sourcepub fn is_aec_supported(&self) -> Result<bool, WasapiError>
pub fn is_aec_supported(&self) -> Result<bool, WasapiError>
Check if the Acoustic Echo Cancellation (AEC) is supported.
Sourcepub fn get_buffer_size_limits(
&self,
wavefmt: &WaveFormat,
timing_mode: TimingMode,
) -> Result<(i64, i64), WasapiError>
pub fn get_buffer_size_limits( &self, wavefmt: &WaveFormat, timing_mode: TimingMode, ) -> Result<(i64, i64), WasapiError>
The buffer size limits of the hardware audio engine, in 100-nanosecond units. Only available on endpoints
that support hardware offloaded streams. Other endpoints will fail with AUDCLNT_E_OFFLOAD_MODE_ONLY.
Use AudioClient::is_offload_capable to check beforehand.
This function does not require the AudioClient to be initialized.
WASAPI docs: IAudioClient2::GetBufferSizeLimits
Sourcepub fn is_offload_capable(
&self,
category: StreamCategory,
) -> Result<bool, WasapiError>
pub fn is_offload_capable( &self, category: StreamCategory, ) -> Result<bool, WasapiError>
Whether or not this endpoint supports hardware offloaded audio stream.
WASAPI docs: IAudioClient2::IsOffloadCapable
The shared mode engine’s current format and period in frames.
WASAPI docs: IAudioClient3::GetCurrentSharedModeEnginePeriod
The range of periods (in frames) which the shared mode engine supports for the given format.
WASAPI docs: IAudioClient3::GetSharedModeEnginePeriod
Initialize an AudioClient for a shared mode stream with the given periodicity.
Take period_in_frames from AudioClient::get_shared_mode_engine_period.
Unlike AudioClient::initialize_client, loopback capture and automatic format conversion
are not available here.
WASAPI docs: IAudioClient3::InitializeSharedAudioStream