Audio Effects
NAudio ships a suite of audio effects in the NAudio.Effects namespace. It is pure managed C# with no platform dependencies, lives in NAudio.Core, and works anywhere NAudio runs (Windows, Linux, macOS). The effects process 32-bit floating point audio, so they slot naturally into the ISampleProvider pipeline.
There are two layers to be aware of:
- The reusable DSP kernels in
NAudio.Dsp(BiQuadFilter,DelayLine,EnvelopeFollower,Oversampler,LinkwitzRileyCrossover,PartitionedConvolver,Lfo, …). These are stateless-ish building blocks you can use directly to build your own processing. - The streaming/effect layer in
NAudio.Effects: theIAudioEffectcontract, theAudioEffectbase class, and theEffectChain/EffectSampleProviderthat adapt effects into the pull-model sample pipeline. The supplied effects (compressor, reverb, delay, …) are built on top of the DSP kernels.
This guide covers the streaming layer. Effects operate on interleaved Span<float> (so for stereo the buffer is L, R, L, R, …), are configured once with a WaveFormat, and never allocate on the steady-state processing path.
Applying a single effect
The quickest way to use an effect is to wrap an existing ISampleProvider in an EffectSampleProvider. The effect is configured with the source's format in the constructor.
var audio = new AudioFileReader("example.mp3");
var reverb = new ReverbEffect { Mix = 0.25f };
var output = new EffectSampleProvider(audio, reverb);
var device = new WaveOutEvent();
device.Init(output);
device.Play();
EffectSampleProvider.Effect gives you back the effect so you can keep tweaking it while it plays.
Building a chain
To run several effects in series, use EffectChain. It is itself an ISampleProvider, applies effects in the order they were added, and offers a fluent Add:
var chain = new EffectChain(audio)
.Add(new GateEffect { ThresholdDb = -50f })
.Add(new CompressorEffect { ThresholdDb = -18f, Ratio = 4f })
.Add(new ReverbEffect { Mix = 0.2f });
device.Init(chain);
device.Play();
Each effect is configured with the source format as it is added, so the chain is ready to read immediately.
Dry/wet mix and bypass
Every effect derived from AudioEffect (which is all of the supplied ones) gives you two controls for free:
Mix— the dry/wet blend,0f(fully dry, effect inaudible) to1f(fully wet). Time-based effects default to a sensible partial mix (e.g.ReverbEffectandDelayEffectdefault to a mostly-dry mix; modulation effects default to 0.5).Bypass—truepasses the input straight through.
Both are click-free: the transition is ramped, so you can toggle bypass or sweep the mix live without a pop. (Even while bypassed the effect keeps running internally with its output discarded, so un-bypassing doesn't click either.)
reverb.Mix = 0.4f; // smoothly crossfades to the new blend
reverb.Bypass = true; // smoothly ramps out, effect stays warm
A tour of the available effects
All of these live in NAudio.Effects. Parameter units are shown in brackets; every effect also has the inherited Mix and Bypass.
EQ / filtering
| Effect | Key parameters |
|---|---|
Equalizer |
Multi-band parametric EQ. Pass EqualizerBands (each has Type, Frequency (Hz), Q, GainDb, ShelfSlope); band Type is one of Peaking, LowShelf, HighShelf, LowPass, HighPass, Notch, BandPass, AllPass. Call Update() after editing bands. |
GraphicEqualizer |
Fixed ISO bands over the Equalizer engine. GraphicEqualizerLayout.TenBandOctave or ThirtyOneBandThirdOctave; SetBandGain(index, dB) / GetBandGain(index). |
MonoMakerEffect |
Frequency (Hz) — sums everything below it to mono (bass-mono). Stereo only. |
DcBlockerEffect |
CutoffFrequency (Hz) — first-order high-pass to remove DC/rumble. |
Dynamics
| Effect | Key parameters |
|---|---|
CompressorEffect |
ThresholdDb, Ratio, KneeDb, AttackMs, ReleaseMs, MakeUpGainDb, Detector (DetectorMode.Peak/Rms), RmsWindowMs; GainReductionDb (read-only meter). |
LimiterEffect |
Brick-wall look-ahead limiter. CeilingDb, ReleaseMs, LookaheadMs, TruePeak, OversampleFactor (1/2/4); GainReductionDb meter. Reports look-ahead via LatencySamples. |
GateEffect |
Gate / downward expander. ThresholdDb, RangeDb, Ratio, HysteresisDb, AttackMs, HoldMs, ReleaseMs; GainReductionDb meter. |
MultibandCompressorEffect |
Linkwitz–Riley crossover into per-band compressors. Construct with crossover frequencies (Hz); edit Bands (each MultibandCompressorBand has ThresholdDb, Ratio, AttackMs, ReleaseMs, MakeUpGainDb, GainReductionDb). |
TransientShaperEffect |
AttackDb, SustainDb (boost/cut the onset vs body), FastMs, SlowMs. |
DeEsserEffect |
Split-band de-esser. CrossoverFrequency (Hz), ThresholdDb, Ratio, AttackMs, ReleaseMs; GainReductionDb meter. |
Level, pan & stereo
| Effect | Key parameters |
|---|---|
GainEffect |
GainDb (dB) / LinearGain; smoothed, click-free level. |
PanEffect |
Pan (-1 … +1), constant-power. Stereo only. |
StereoWidthEffect |
Width (0 = mono, 1 = unchanged, up to 2 = wider) via mid/side. Stereo only. |
Saturation / lo-fi
| Effect | Key parameters |
|---|---|
SaturationEffect |
DriveDb, OutputGainDb, Curve (SaturationCurve.Tanh/Cubic/ArcTan/HardClip), OversampleFactor (1/2/4). |
BitCrusherEffect |
BitDepth (1–32), TargetSampleRate (Hz, 0 = off), Smoothing. |
Delay / modulation
| Effect | Key parameters |
|---|---|
DelayEffect |
DelayMs, Feedback (0 … <1), Damping (0–1), PingPong, TempoSync + Tempo + Division (NoteDivision); EffectiveDelayMs meter. |
ChorusEffect |
BaseDelayMs, DepthMs, RateHz, Feedback. SyncToTempo(bpm, division). |
FlangerEffect |
BaseDelayMs, DepthMs, RateHz, Feedback (may be negative). SyncToTempo(...). |
PhaserEffect |
Stages (1–24), MinFrequency, MaxFrequency (Hz), RateHz, Feedback. SyncToTempo(...). |
TremoloEffect |
Depth (0–1), RateHz, Waveform (LfoWaveform), AutoPan. SyncToTempo(...). |
Reverb
| Effect | Key parameters |
|---|---|
ReverbEffect |
Freeverb (Schroeder–Moorer). RoomSize, Damping, Width (all 0–1). Low CPU baseline. |
FdnReverbEffect |
Feedback-delay-network reverb. DecaySeconds (RT60), Size, Damping, ModulationDepthMs, ModulationRateHz, PreDelayMs, Width. Higher quality. |
ConvolutionReverbEffect |
Partitioned FFT convolution. SetImpulseResponse(float[]) (mono IR for all channels) or SetImpulseResponse(float[][]) (per channel); PartitionSize (power of two). Reports LatencySamples. |
Voice / comms
| Effect | Key parameters |
|---|---|
AutomaticGainControlEffect |
TargetDb, MaxGainDb, MinGainDb, AttackMs, ReleaseMs, RmsWindowMs, UseVoiceDetection; GainDb meter. |
NoiseSuppressionEffect |
Spectral suppressor. Aggressiveness, SpectralFloor, NoiseAdaptation, FrameSize (power of two). Reports LatencySamples. |
ComfortNoiseEffect |
LevelDb, Tone (0 bright … 1 dark). Adds a low noise floor after a gate/suppressor. |
Pitch
| Effect | Key parameters |
|---|---|
PitchShiftEffect |
Phase-vocoder pitch shift (no tempo change). PitchSemitones (±12), FftSize, Oversampling. Reports LatencySamples. |
Editing a chain while it plays
EffectChain is designed to be edited from a UI/control thread while the audio thread is calling Read. Add, Insert, RemoveAt and Move each publish a new immutable array with a single atomic write, so a concurrent Read always sees either the whole pre-edit chain or the whole post-edit chain — never a half-built state — and Read itself takes no lock. Edits are serialized against each other internally, so multiple editor threads are also safe. New effects are configured for the source format on the editing thread before they are published, so configuration never lands on the audio thread.
chain.Add(new DelayEffect()); // append
chain.Insert(0, new GainEffect()); // put a trim at the front
chain.Move(0, 2); // reorder
chain.RemoveAt(1); // drop one
chain.Effects gives a point-in-time snapshot (in processing order) for display.
Latency and Reset
Some effects introduce processing latency — look-ahead limiting, FFT/partitioned designs, pitch shifting. Each reports it through IAudioEffect.LatencySamples (samples per channel, 0 for effects with no inherent delay). EffectChain.LatencySamples sums the chain, which is what you need for delay compensation:
int compensate = chain.LatencySamples;
Reset() clears an effect's internal state — delay lines, filter history, envelopes, reverb tails — so the next block starts as if from silence. Call EffectChain.Reset() when you reuse a chain on a discontinuous signal, e.g. after seeking the source, to avoid hearing the tail of the previous position bleed into the new one:
reader.CurrentTime = TimeSpan.FromSeconds(30);
chain.Reset();
The parameter model
Most effects also implement IParameterized, which exposes their controls as a uniform IReadOnlyList<EffectParameter>. This lets a generic UI, preset system, serializer or automation host drive any effect without effect-specific code.
Each EffectParameter is a thin facade over one of the effect's typed properties — it doesn't store state, its getter/setter forward to the property, so the property stays the single source of truth. A parameter has a Name, Kind (Continuous, Toggle, Choice or Meter), Unit, Minimum/Maximum, DefaultValue, optional Choices, and a Value (clamped on write; ignored for read-only Meter parameters).
var comp = new CompressorEffect();
foreach (var p in comp.Parameters)
Console.WriteLine($"{p.Name}: {p.Value}{p.Unit} ({p.Minimum}..{p.Maximum})");
Note that Bypass and Mix are not in the parameter list — they live on AudioEffect and a generic host surfaces them separately.
Marshalling live edits to the audio thread
When an effect is running behind an audio callback, you don't want a UI thread mutating filter coefficients or resizing delay buffers mid-block. ParameterDispatchQueue (an IParameterDispatch) solves this: it's a lock-free single-producer/single-consumer queue. Attach an effect's parameters to it, and subsequent writes to EffectParameter.Value are clamped and posted rather than applied inline. The audio thread calls Drain() once at the top of each block to apply every pending write where there's no concurrent reader.
var queue = new ParameterDispatchQueue();
queue.Attach(comp); // route comp's parameters through the queue
// control thread: this is now deferred, not applied inline
comp.Parameters[1].Value = 6f; // Ratio
// audio thread, top of the processing block:
queue.Drain();
While attached, the parameter getter optimistically returns the just-requested value so a two-way bound UI doesn't snap back before the audio thread has caught up. Detach restores inline application.
Effects that are not IParameterized
A few effects deliberately do not implement IParameterized because they have a dynamic band list or an impulse-response input that a flat parameter list can't represent. A generic, parameter-driven host should special-case these with bespoke UI:
MultibandCompressorEffect— variable number of bands, each with its own settingsEqualizer— a dynamic list ofEqualizerBandsGraphicEqualizer— a per-layout set of band gains (SetBandGain/GetBandGain)ConvolutionReverbEffect— needs an impulse response, not scalar parameters
(The WPF demo shows how to wrap a band-list effect as a fixed-parameter one — see SevenBandEqEffect below.)
Writing your own effect
Subclass AudioEffect and implement two methods:
OnConfigure(WaveFormat format)— allocate and size your sample-rate-dependent state (delay lines, filters). Called before the first process and again if the format changes. Use theSampleRateandChannelshelpers from the base.ProcessBlock(Span<float> buffer)— transform the interleaved buffer in place into the fully-wet signal. The base class handles dry/wet mixing and bypass around this call, so don't implement those yourself.
Override Reset() (calling base.Reset()) if your effect has internal state to clear, and override LatencySamples if it adds delay. Optionally implement IParameterized to expose your controls.
public sealed class HalfGainEffect : AudioEffect
{
protected override void OnConfigure(WaveFormat format) { }
protected override void ProcessBlock(Span<float> buffer)
{
for (var i = 0; i < buffer.Length; i++)
buffer[i] *= 0.5f;
}
}
Effects are most easily built by composing the NAudio.Dsp kernels (and even other effects). The WPF demo's RealtimeEffectsDemo/CustomEffects folder has two worked examples:
FilterEffect— a cascadable HPF + LPF built directly fromBiQuadFilter/CrossfadingBiQuadFilter, with click-free retuning and anIParameterizedcontrol list.SevenBandEqEffect— wraps the toolkitEqualizer(which takes a dynamic band list) as a fixed seven-band effect that a generic parameter panel can render, by exposing oneEffectParameterper band.