Class WasapiPlayer
Modern WASAPI audio player with zero-copy buffer access, MMCSS thread priority, and IAudioClient3 low-latency support. Created via WasapiPlayerBuilder.
public class WasapiPlayer : IWavePlayer, IDisposable, IWavePosition, IWaveLatency, IAsyncDisposable
- Inheritance
-
WasapiPlayer
- Implements
- Inherited Members
- Extension Methods
Properties
AverageLatency
The steady-state latency from a sample being queued for output to it being emitted by the audio hardware, assuming uninterrupted playback. This is a property of the buffer configuration and the driver, not of the current playback state.
public TimeSpan AverageLatency { get; }
Property Value
Remarks
Uses the latency negotiated with the audio engine at Init(IWaveProvider) time. For the
IAudioClient3 low-latency path this is the actual engine period selected by the
driver, not the value originally requested.
CurrentLatency
The time that has elapsed since the sample currently emerging from the hardware was queued for output. In steady state this is approximately equal to AverageLatency; it differs during start-up, after an underrun, or when the host is filling buffers irregularly. Implementations that cannot meaningfully distinguish from the average are permitted to return AverageLatency.
public TimeSpan CurrentLatency { get; }
Property Value
Remarks
Derived from IAudioClient::GetCurrentPadding, which reports frames still in the
endpoint buffer waiting to be rendered. Falls back to AverageLatency when
not playing or when the audio client has not yet been initialised.
DeviceFriendlyName
The friendly name of the device this player was created on (e.g. "Speakers (Realtek High Definition Audio)"), captured at construction. Useful for display and logging. Null when following the default device with automatic stream routing — see DeviceId.
public string DeviceFriendlyName { get; }
Property Value
DeviceId
The endpoint ID of the device this player was created on, captured at construction — the same string accepted by GetDevice(string). Persist it to reopen the same physical device later (for example after the endpoint is disabled and re-enabled, such as an HDMI display returning from standby).
public string DeviceId { get; }
Property Value
Remarks
Null when following the default device with automatic stream routing (WithDefaultDeviceStreamRouting()): there is no fixed endpoint, and Windows may transparently reroute the stream at any time. To discover the current default endpoint in that mode, resolve it yourself via GetDefaultAudioEndpoint(DataFlow, Role) and track changes with an MMDeviceNotificationClient.
DeviceMixFormat
The device's preferred mix format (shared mode). This is always supported in shared mode.
public WaveFormat DeviceMixFormat { get; }
Property Value
DeviceVolume
Device endpoint volume — controls the master volume for the audio device, affecting all applications playing through it. Includes per-channel levels, mute, volume step control, dB range information, and change notifications. Use with care: changes are system-wide and visible to the user. Available in both shared and exclusive modes.
public AudioEndpointVolume DeviceVolume { get; }
Property Value
Exceptions
- InvalidOperationException
Thrown when following the default device with automatic stream routing: there is no fixed endpoint, so endpoint-wide volume has no meaning. Use Volume/SessionVolume for per-application volume instead.
IsMuted
Gets or sets the session mute state. This is the mute toggle for your application in the Windows volume mixer. Delegates to SessionVolume.
public bool IsMuted { get; set; }
Property Value
LatencyMilliseconds
The latency in milliseconds actually in use after Init(IWaveProvider). In low-latency mode this is derived from the engine period the device granted, so it may differ from the value requested via WithLatency(int).
public int LatencyMilliseconds { get; }
Property Value
LowLatencyActive
Whether IAudioClient3 low-latency shared mode is actually in use after Init(IWaveProvider). This is only ever true when WithLowLatency(bool) was requested and the device, share mode, and source format allowed it. When low latency was requested but could not be honoured, playback silently falls back to standard shared mode and this remains false — check it to find out what you actually got.
public bool LowLatencyActive { get; }
Property Value
LowLatencyUnavailableReason
When low latency was requested via WithLowLatency(bool) but could not be honoured, a short human-readable explanation of why (e.g. a sample-rate mismatch that would require resampling). Null when low latency is active or was never requested.
public string LowLatencyUnavailableReason { get; }
Property Value
OutputWaveFormat
The output format being sent to the audio device.
public WaveFormat OutputWaveFormat { get; }
Property Value
PlaybackState
Current playback state.
public PlaybackState PlaybackState { get; }
Property Value
SessionVolume
Per-session volume and mute control. This is the volume slider shown for your application in the Windows volume mixer. Use this for simple volume/mute control that only affects your application.
public SimpleAudioVolume SessionVolume { get; }
Property Value
Remarks
When following the default device with automatic stream routing (no fixed endpoint) this is obtained from the audio client itself, so it is only available after Init(IWaveProvider).
StreamVolume
Per-stream, per-channel volume control (0.0 to 1.0 per channel). Use this for balance, pan, or independent channel level adjustments. Only available in shared mode.
public AudioStreamVolume StreamVolume { get; }
Property Value
Exceptions
- InvalidOperationException
Thrown when the player is using exclusive mode, where per-stream volume is not available.
Volume
Gets or sets the session volume (0.0 to 1.0). This controls your application's volume as shown in the Windows volume mixer, without affecting other applications. Delegates to SessionVolume.
public float Volume { get; set; }
Property Value
Methods
Dispose()
Stops playback (blocking) and releases all resources.
public void Dispose()
DisposeAsync()
Stops playback without blocking the calling thread, then releases all resources. Prefer this over Dispose() in async or UI contexts where blocking is undesirable.
public ValueTask DisposeAsync()
Returns
GetPlaybackCapability(WaveFormat)
Reports, without opening the stream, what Init(IWaveProvider) would do for a source of the given format: whether playback is possible at all, whether low latency would actually engage, the format the device would receive, and any latency-free conversions (bit depth / channels) that would be inserted. Call this before Init(IWaveProvider) to validate the chosen options.
public WasapiPlaybackCapability GetPlaybackCapability(WaveFormat sourceFormat)
Parameters
sourceFormatWaveFormatThe format of the source you intend to play.
Returns
GetPosition()
Gets the current position in bytes from the wave output device. (This is not the same as the position within your reader stream.)
public long GetPosition()
Returns
GetSupportedExclusiveFormat(WaveFormat)
Finds a supported exclusive-mode format for this device, trying the preferred format first, then falling back through standard sample rates, bit depths, and multi-channel speaker configurations from the Windows Driver Kit (ksmedia.h). Use this to discover what format to provide to Init(IWaveProvider) when using exclusive mode.
public WaveFormatExtensible GetSupportedExclusiveFormat(WaveFormat preferredFormat)
Parameters
preferredFormatWaveFormatThe format you'd ideally like to use.
Returns
- WaveFormatExtensible
A supported WaveFormatExtensible, or null if no supported format was found.
Init(IWaveProvider)
Initialize for playing the specified audio source.
public void Init(IWaveProvider source)
Parameters
sourceIWaveProvider
Remarks
The source's bit depth and channel count are adapted automatically (without resampling) to a format the device supports: in standard shared mode the audio engine does this for you; in exclusive and IAudioClient3 low-latency modes WasapiPlayer inserts the necessary conversion (PCM↔float, mono↔stereo). Sample rate is never changed — converting it would add latency — so exclusive mode throws if the device cannot accept the source's sample rate, and low latency silently falls back to standard shared mode (see LowLatencyActive and LowLatencyUnavailableReason). When the source already matches the device format, playback is zero-copy with no conversion inserted.
IsFormatSupported(WaveFormat)
Checks whether the specified format is supported by the device in the current share mode.
public bool IsFormatSupported(WaveFormat format)
Parameters
formatWaveFormat
Returns
IsFormatSupported(WaveFormat, out WaveFormat)
Checks whether the specified format is supported by the device in the current share mode.
public bool IsFormatSupported(WaveFormat format, out WaveFormat closestMatch)
Parameters
formatWaveFormatThe format to check.
closestMatchWaveFormatIn shared mode, the closest supported format if the exact format isn't supported. Always null in exclusive mode.
Returns
- bool
True if the format is supported.
Pause()
Pause playback without flushing buffers.
public void Pause()
Play()
Begin playback.
public void Play()
Stop()
Stop playback and flush buffers.
public void Stop()
Events
PlaybackStopped
Raised when playback stops, either because the source ended or an error occurred. If a SynchronizationContext was captured at construction, this event is raised on that context (e.g. the UI thread).
public event EventHandler<StoppedEventArgs> PlaybackStopped