- TypeScript 100%
| src | ||
| .gitignore | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
nmplayer-equalizer-plugin
A headless 10-band parametric equalizer plugin for the NoMercy Video Player.
The plugin splices a preGain → peaking-filter chain → stereoPanner pipeline
between the player's existing gainNode and the AudioContext destination,
then exposes a small API for adjusting bands, pan, and presets. It emits
typed events on the player's event bus so consumers can wire any UI of their
choice — there is no built-in UI.
Features
- 10 peaking-filter bands at 70 / 180 / 320 / 600 / 1 000 / 3 000 / 6 000 / 12 000 / 14 000 / 16 000 Hz, plus a pre-gain pseudo-band.
- Stereo panner with sticky-zero snapping (±0.05 → 0).
- 19 built-in presets (Classical, Club, Dance, Flat, Pop, Rock, Soft, Live, Techno, …) and support for ad-hoc presets via JSON.
- Persists state through
player.storage(the player's namespacedlocalStoragewrapper). - Idempotent lifecycle: safe to
dispose()and re-use(); teardown restores the player's originalgainNode → destinationwiring. - Strict TypeScript with declaration merging (
player.plugin('equalizer')is fully typed).
Installation
npm install nmplayer-equalizer-plugin
# peer-dep
npm install @nomercy-entertainment/nomercy-video-player
Quick start
import nmplayer from '@nomercy-entertainment/nomercy-video-player';
import { EqualizerPlugin } from 'nmplayer-equalizer-plugin';
const player = nmplayer('myPlayer');
player.setup({
playlist: [/* … */],
// Optional: provide config inline via the merged PlayerConfig key.
equalizer: {
storageKey: 'my-app-equalizer',
autoLoad: true,
},
});
player.registerPlugin('equalizer', new EqualizerPlugin());
player.usePlugin('equalizer');
const eq = player.plugin('equalizer');
eq?.setPreset('Rock');
eq?.setFilter({ frequency: 1000, gain: 3 });
eq?.setPanner(-0.3);
player.on('equalizer:change', ({ bands, pan, selectedPreset }) => {
// drive your UI here
});
Configuration
Options can be passed either to the plugin constructor or via the player's
config object (equalizer key). Constructor options take precedence.
new EqualizerPlugin({
storageKey: 'equalizer-settings', // default
autoLoad: true, // default — load persisted settings on use()
autoSave: true, // default — persist on every change
bands: undefined, // override default band layout
presets: undefined, // override built-in presets
sliderValues: undefined, // override slider ranges
});
| Option | Type | Default | Description |
|---|---|---|---|
storageKey |
string |
'equalizer-settings' |
Key under the player's nmplayer- storage prefix. |
autoLoad |
boolean |
true |
Read persisted state and apply it after the audio chain is connected. |
autoSave |
boolean |
true |
Save state on every setPreGain, setFilter, setPanner, setPreset, reset. |
bands |
EQBand[] |
(built-in 10-band set) | Initial band layout. Index 0 must be { frequency: 'Pre', gain }. |
presets |
EqualizerPreset[] |
(19 built-in presets) | Replaces the built-in preset list. |
sliderValues |
EQSliderValues |
(sensible defaults) | Min/max/step ranges for pan, pre-gain, and band sliders. |
API
Lifecycle (inherited from the player's Plugin base)
| Method | When | Notes |
|---|---|---|
initialize(player) |
On registerPlugin |
Store the player reference. No DOM or audio work. |
use() |
On usePlugin |
Subscribes to 'ready' / 'play'; if the player has already fired 'ready', sets up the chain immediately. |
dispose() |
On player teardown | Removes listeners, disconnects all audio nodes, restores original gainNode → destination. |
Setters
eq.setPreGain(gain: number | string): void
eq.setPanner(pan: number | string): void
eq.setFilter(band: { frequency: number | 'Pre'; gain: number }): void
eq.setPreset(preset: EqualizerPreset | string): void
eq.reset(): void
setPreGainandsetPannersnap to0when the absolute value is ≤ 0.05.setPreGain(0)is unity gain — the AudioParam isvalue + 1internally, mirroring the original reference implementation.setPresetaccepts a preset name, anEqualizerPresetobject, or a JSON string of one. Unknown names return silently.- Manual changes (
setPreGain,setFilter,setPanner) clear the selected preset name.
Getters
eq.getBands(): EQBand[] // includes 'Pre' at index 0
eq.getPan(): number // [-1, 1]
eq.getPreGain(): number // raw slider value (no +1 offset)
eq.getSelectedPresetName(): string | undefined
eq.getPresets(): EqualizerPreset[]
eq.getSliderValues(): EQSliderValues
Slider helpers
For UI components that render bands as sliders:
eq.getBandSliderMin(frequency) // min for that band's slider
eq.getBandSliderMax(frequency) // max for that band's slider
eq.getBandSliderStep(frequency) // step granularity
eq.getBandSliderValue(frequency) // current gain normalized to 0–100
Events
The plugin emits two custom events on the player's event bus.
| Event | Payload | When |
|---|---|---|
equalizer:ready |
undefined |
After the audio chain has been connected and (optional) persisted state applied. |
equalizer:change |
{ bands: EQBand[]; pan: number; selectedPreset?: string } |
After every state-changing call (setPreGain, setFilter, setPanner, setPreset, reset, and the initial load). |
player.on('equalizer:ready', () => console.log('eq online'));
player.on('equalizer:change', data => updateUI(data));
Audio chain
┌──────────────── plugin owns ────────────────┐
HTMLVideoElement ─► MediaElementSource ─► gainNode ─► preGain ─► f1 ─► f2 ─► … ─► fN ─► stereoPanner ─► destination
^^^^^^^^ ^^^^^^^^^^^
player.gainNode AudioContext.destination
gainNode is the player's existing volume-boost node (created by
addGainNode()). The plugin disconnects it from destination, splices in
preGain → filters → panner, and re-terminates at destination. The
plugin never calls createMediaElementSource() itself, because it can only
be called once per <video> element.
If addGainNode() has not yet been called by the time the plugin starts,
the plugin will call it. If you have your own audio graph that runs
in parallel with the player's, set it up before activating this plugin.
Persistence
State is stored as JSON under the configured storageKey via
player.storage, which transparently prefixes keys with nmplayer- and
defaults to localStorage. The persisted shape:
interface EqualizerSettings {
bands: EQBand[];
pan: number;
selectedPreset?: string;
}
Applications that don't want any persistence can pass autoLoad: false and
autoSave: false.
Built-in presets
Custom, Classical, Club, Dance, Flat, Laptop speakers/headphones,
Large hall, Party, Pop, Reggae, Rock, Soft, Ska, Full Bass,
Soft Rock, Full Treble, Full Bass & Treble, Live, Techno.
import { equalizerPresets } from 'nmplayer-equalizer-plugin';
const allNames = equalizerPresets.map(p => p.name);
TypeScript
The plugin uses declaration merging to register itself with the player's type system, so both the registry lookup and the config key are fully typed:
const eq = player.plugin('equalizer'); // EqualizerPlugin | undefined
player.setup({ equalizer: { storageKey: 'x' } }); // typed
All public types (EQBand, EQSliderValues, EqualizerPreset,
EqualizerSettings, EqualizerPluginOptions, EqualizerChangeData,
EqualizerEventMap, SliderRange) are re-exported from the package root.
Development
npm install # install peer + dev deps
npm run build # tsc → dist/
npx vitest # run the test suite (18 specs)
npm run watch # incremental type-check + emit
Compatibility
- Peer dependency:
@nomercy-entertainment/nomercy-video-player ^1.2.0 - Browsers: Any browser with Web Audio (
AudioContext,BiquadFilterNode,StereoPannerNode). All modern evergreen browsers qualify. - Module format: ESM (Node ≥ 18). The package emits
dist/index.jsanddist/index.d.ts.
License
MIT.