Skip to main content

Variable: AudioFilters

const AudioFilters: {
bass: AudioFilter;
compressor: AudioFilter;
crossfeed: AudioFilter;
custom: AudioFilter;
dynamicNormalizer: AudioFilter;
equalizer: AudioFilter;
graphicEqualizer: AudioFilter;
highpass: AudioFilter;
limiter: AudioFilter;
loudnorm: AudioFilter;
lowpass: AudioFilter;
treble: AudioFilter;
volume: AudioFilter;
};

Defined in: packages/player/src/filters.ts:748

Factories for the audio filters this library ships bindings for.

Where the ranges come from​

Every factory validates its arguments and throws invalid-state rather than letting mpv reject the whole chain with a generic message. Values the caller omits are simply not written, so ffmpeg's own defaults apply.

Unless a doc comment says otherwise, a bound here is read off the AVOption table of the filter in FFmpeg n8.1.2 — the tree our engine binaries are actually built from (packages/player/android/libmpv.gradle ffmpegVersion, packages/player/ios/libmpv.pin LIBMPV_FFMPEG_VERSION). Every citation below is libavfilter/<file>:<line> at the n8.1.2 tag. The AVOption table is the truth; doc/filters.texi is prose about it and is cited only where it says something the table cannot.

Re-audited in full against n8.1.2 (2026-08-14). Nothing in the wrapped surface moved between n6.0 — which these bounds were originally taken from, and which the engine has not shipped since the rnmedia.5/rnmedia.4 engine move — and n8.1.2: the only changes to these seven option tables are a cosmetic .unit = designated-initializer refactor and loudnorm gaining a stats_file option this API does not expose. What was stale was the label on the ranges, not the ranges.

Where a range is OURS​

Three bounds in this file are deliberately not ffmpeg's, and each says so at its own definition:

Anything not listed here — including firequalizer and anequalizer, which are compiled in but configured through ffmpeg expression strings rather than scalars (af_anequalizer.c:84's params is one AV_OPT_TYPE_STRING) — is reachable through AudioFilters.custom.

Type Declaration​

bass()​

readonly bass(options: ShelfOptions): AudioFilter;

Low-shelf boost/cut (ffmpeg bass).

Parameters​

ParameterType
optionsShelfOptions

Returns​

AudioFilter

Example​

AudioFilters.bass({ frequency: 110, gain: 6 })

compressor()​

readonly compressor(options?: CompressorOptions): AudioFilter;

Dynamic-range compressor (ffmpeg acompressor).

Parameters​

ParameterType
optionsCompressorOptions

Returns​

AudioFilter

crossfeed()​

readonly crossfeed(options?: CrossfeedOptions): AudioFilter;

Headphone crossfeed (ffmpeg crossfeed) — bleeds a filtered copy of each channel into the other so hard-panned mixes stop feeling like two separate sounds inside the head.

Parameters​

ParameterType
optionsCrossfeedOptions

Returns​

AudioFilter

custom()​

readonly custom(name: string, options?: Readonly<Record<string, string | number | boolean>>): AudioFilter;

Any other filter mpv or libavfilter knows, by name.

The escape hatch that keeps this module thin: firequalizer, anequalizer, aformat, anull and mpv's own builtins are all reachable without waiting for a typed wrapper. Options are written in object key order; values are stringified as-is (numbers via String).

Parameters​

ParameterType
namestring
optionsReadonly<Record<string, string | number | boolean>>

Returns​

AudioFilter

Example​

// Arbitrary-curve linear-phase EQ, ffmpeg expression syntax
AudioFilters.custom('firequalizer', {
gain_entry: 'entry(100,-3);entry(1000,0);entry(10000,4)',
})

dynamicNormalizer()​

readonly dynamicNormalizer(options?: DynamicNormalizerOptions): AudioFilter;

Sliding-window loudness normaliser (ffmpeg dynaudnorm).

Prefer this over AudioFilters.loudnorm for live playback: it works at the stream's own sample rate, whereas loudnorm forces the whole chain through 192 kHz.

frameLengthMs and gaussSize map to AV_OPT_TYPE_INT options (af_dynaudnorm.c:130-133) and are required to be integers here, because av_opt_set would otherwise round a fractional value silently. gaussSize must additionally be odd: that rejection is ours — ffmpeg logs "filter size %d is invalid. Changing to an odd value." and ORs the low bit in (af_dynaudnorm.c:165-167), i.e. it runs a different filter from the one that was asked for, with the warning buried in mpv's log.

Parameters​

ParameterType
optionsDynamicNormalizerOptions

Returns​

AudioFilter

equalizer()​

readonly equalizer(options: EqualizerOptions): AudioFilter;

A single parametric peaking band (ffmpeg equalizer).

The building block of a parametric EQ: stack several to shape a curve.

Parameters​

ParameterType
optionsEqualizerOptions

Returns​

AudioFilter

Example​

AudioFilters.equalizer({ frequency: 5000, width: 2, gain: -12 })

graphicEqualizer()​

readonly graphicEqualizer(options: GraphicEqualizerOptions): AudioFilter;

18-band graphic equaliser (ffmpeg superequalizer), gains in dB.

Band centres are GRAPHIC_EQUALIZER_BANDS. This is an FFT filter: it costs more CPU than a stack of AudioFilters.equalizer bands but gives a flat, phase-consistent response and maps 1:1 onto a slider UI.

Parameters​

ParameterType
optionsGraphicEqualizerOptions

Returns​

AudioFilter

Example​

// +12 dB on the bottom two bands, flat elsewhere
AudioFilters.graphicEqualizer({
gainsDb: [12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
})

highpass()​

readonly highpass(options: PassOptions): AudioFilter;

Two-pole high-pass (ffmpeg highpass).

A 30–40 Hz high-pass is the cheapest way to stop a bass boost from wasting excursion on inaudible rumble.

Parameters​

ParameterType
optionsPassOptions

Returns​

AudioFilter

limiter()​

readonly limiter(options?: LimiterOptions): AudioFilter;

Look-ahead brickwall limiter (ffmpeg alimiter).

The honest tail of any chain that boosts: EQ gain plus ReplayGain can push peaks past full scale, and this is what stops that becoming clipping.

Parameters​

ParameterType
optionsLimiterOptions

Returns​

AudioFilter

Remarks​

What it costs when it is not working, which is most of the time — worth knowing, because equalizerPresetChain appends one by default:

  • Below full scale it is sample-identical. The gain reduction is a single running scalar that starts at 1 and is only ever moved by a sample whose magnitude exceeds limit; nothing else in the path scales (af_alimiter.c:102,180,236,239,269-275,289). No colouration, no compression of programme material, no level change.
  • It delays the signal by attack, and does not compensate. The look-ahead buffer is attack × sampleRate samples — 240 samples, 5.0 ms at 48 kHz on the default 5 ms attack — and ffmpeg's latency option, which trims the primed silence and flushes the tail, defaults to off and is not exposed here (af_alimiter.c:376-379). So a chain that has just been built emits 5 ms of silence first, and the last 5 ms of the stream is never flushed. Inaudible for playback; it is still the one thing that is not transparent.
  • It runs in double (FILTER_SINGLE_SAMPLEFMT(AV_SAMPLE_FMT_DBL)), so libavfilter inserts aresample around it. Lossless, not free.

loudnorm()​

readonly loudnorm(options?: LoudnormOptions): AudioFilter;

EBU R128 loudness normalisation (ffmpeg loudnorm).

Prefer Player.setLoudnessNormalization — the managed toggle over this same filter, which coexists with a chain set through setAudioFilters instead of being clobbered by it, and whose TSDoc carries the full cost sheet. This factory remains for hand-built chains that need options the toggle does not expose (linear, offset).

Expensive on mobile, by construction. In its single-pass (dynamic) mode loudnorm advertises exactly one input sample rate — 192 000 Hz (FFmpeg 8.1.2 af_loudnorm.c:740,752; doc/filters.texi: "the audio stream will be upsampled to 192 kHz") — so libavfilter resamples the stream up to 192 kHz for this filter and back down afterwards, and the filter buffers 3 s of lookahead (af_loudnorm.c:697,775). Note linear is inert in live playback: ffmpeg's linear mode requires all four measured_* values from a prior analysis pass (af_loudnorm.c:820-825), which a live player cannot have. If all you want is "make quiet tracks louder", AudioFilters.dynamicNormalizer or Player.setReplayGain costs a fraction of it.

Parameters​

ParameterType
optionsLoudnormOptions

Returns​

AudioFilter

lowpass()​

readonly lowpass(options: PassOptions): AudioFilter;

Two-pole low-pass (ffmpeg lowpass).

Parameters​

ParameterType
optionsPassOptions

Returns​

AudioFilter

treble()​

readonly treble(options: ShelfOptions): AudioFilter;

High-shelf boost/cut (ffmpeg treble).

Parameters​

ParameterType
optionsShelfOptions

Returns​

AudioFilter

volume()​

readonly volume(options: VolumeOptions): AudioFilter;

Fixed gain (ffmpeg volume), in dB.

This is chain-domain gain, distinct from Player.setVolume (mpv's output volume) and from ReplayGain. Its job here is headroom: put AudioFilters.volume({ gainDb: -6 }) at the head of a chain whose EQ boosts 6 dB and nothing clips.

The ±100 dB bound is this library's, not libavfilter's — see VolumeOptions.gainDb.

Parameters​

ParameterType
optionsVolumeOptions

Returns​

AudioFilter