Skip to content
Shiny.NET
Commercial SupportGuaranteed response times on Shiny bug fixes, straight from the maintainers. Plans from US$1,500 a year.See the plans

Real-time Effects

Shiny.Audio can apply real-time DSP to the microphone (pitch, echo, reverb, filters and more) as you capture or record. It’s all in the base Shiny.Audio package, so there is nothing extra to install.

The native capture APIs do not offer a consistent feature set. No platform supports pitch shift on capture at all; Windows and iOS offer echo and reverb; Android and Linux offer neither. On the other hand, every backend already normalizes capture to the same thing — 16 kHz mono PCM16 — so a managed DSP chain at that point behaves identically on iOS, Android, Windows, Linux and the browser, and is testable without a device.

Pitch Echo Reverb Filters
iOS / macOS (AVAudioEngine) ✅ ✅ ✅ ✅
Windows (AudioGraph) ❌ ✅ ✅ ✅
Android (AudioRecord) ❌ ❌ ❌ ❌
Linux (Pulse/PipeWire) ❌ ❌ ❌ ❌
Browser (Web Audio) ❌ ✅ ✅ ✅

This is the part worth getting right. An effect is an object you own — not a snapshot of settings passed at start. Build a chain, keep the references, and mutate them; the audio thread picks up each change on the next buffer.

using Shiny.Audio;
var chain = new AudioEffectChain();
var pitch = chain.Add(new PitchShiftEffect { Semitones = 0 }); // Add returns the instance
var echo = chain.Add(new EchoEffect { DelayMs = 250, Mix = 0.35f, Enabled = false });
var stream = await audio.Source.StartCaptureAsync(new AudioCaptureOptions { Effects = chain });
// All of this applies mid-capture, from any thread — no restart, nothing to "apply":
pitch.Semitones = 5; // change a value
echo.Enabled = true; // switch one effect on
chain.Enabled = false; // master bypass

There are three levels of on/off:

Control Scope
chain.Enabled Everything — master bypass
effect.Enabled One effect in or out of circuit
effect.Mix Wet/dry blend, on effects that have one

Composition can change while audio flows too — Add and Remove publish a new array atomically, and the audio thread snapshots it once per buffer, so it never sees a half-built chain.

Two things would otherwise make live control unusable, and both are handled for you:

  • Parameters are ramped, not stepped. Assigning a new value moves it across ~15 ms instead of jumping, so dragging a slider doesn’t put a discontinuity in the waveform.
  • Bypass is crossfaded over ~12 ms rather than hard-switched, so flipping a switch mid-take is inaudible. An effect that has fully faded out is also reset, so switching it back on doesn’t replay a stale reverb tail from a minute ago.

Every parameter is a float and every setter clamps to its valid range, so you can bind a slider directly without validating.

new GainEffect { Gain = 1f }; // also GainDb
new NoiseGateEffect { ThresholdDb = -45f, AttackMs = 5f, ReleaseMs = 150f };
new BiquadFilterEffect { Type = BiquadFilterType.LowPass, Frequency = 1000f, Q = 0.707f };
BiquadFilterEffect.Telephone(); // 1.2 kHz band-pass
new DistortionEffect { Drive = 8f, Mix = 1f };
new RingModEffect { Frequency = 50f, Mix = 1f }; // 20–80 Hz = robot voice
new EchoEffect { DelayMs = 250f, Feedback = 0.35f, Mix = 0.35f };
new ChorusEffect { RateHz = 0.8f, DepthMs = 6f, Mix = 0.5f, Feedback = 0f };
new ReverbEffect { RoomSize = 0.6f, Damping = 0.4f, Mix = 0.35f };
new PitchShiftEffect { Semitones = 0f }; // ±24

ReverbEffect is a Freeverb — eight damped comb filters into four allpass diffusers, with the classic tunings scaled to the session’s sample rate. PitchShiftEffect writes into a delay line at normal speed while two read heads sweep it at the pitch ratio, crossfading as they wrap; that crossfade is what changes pitch while holding duration exactly.

var chain = AudioEffectPresets.Create(AudioEffectPreset.Robot);

Robot, Chipmunk, DeepVoice, Cathedral, Telephone, Megaphone, Ensemble. A preset returns an ordinary chain, so you can keep adjusting it:

var chain = AudioEffectPresets.Create(AudioEffectPreset.Robot);
var ring = chain.Effects.OfType<RingModEffect>().First();
ring.Frequency = 80f;

Latency. Everything except pitch shift is latency-free. PitchShiftEffect adds up to one crossfade window (~50 ms), and beyond roughly ±7 semitones a voice starts to sound obviously processed.

Cost. All of it is cheap at 16 kHz mono — comfortable on a Raspberry Pi.

Fidelity. Capture is 16 kHz mono, so reverb and pitch sound thinner than they would on full-bandwidth stereo. These are voice effects, not mastering tools.

Metering. When effects are set on AudioCaptureOptions, they run before the level is computed, so IAudioSource.InputLevelChanged reflects the processed signal — a meter shows what you will actually hear.

Effects apply to IAudioSource capture (AudioCaptureOptions.Effects) and to IAudioRecorder (AudioRecordingOptions.Effects), and to files you already have through AudioEffectProcessor. The live monitor and duplex calls don’t run an effect chain.