@discord-player/equalizer
Version:
PCM Equalizer implementation for Discord Player
1,179 lines (1,158 loc) • 120 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
// src/index.ts
var src_exports = {};
__export(src_exports, {
AF_NIGHTCORE_RATE: () => AF_NIGHTCORE_RATE,
AF_VAPORWAVE_RATE: () => AF_VAPORWAVE_RATE,
AudioFilter: () => AudioFilter,
AudioFilters: () => AudioFilters,
BASS_EQ_BANDS: () => BASS_EQ_BANDS,
BiquadFilter: () => BiquadFilter,
BiquadStream: () => BiquadStream,
ChannelProcessor: () => ChannelProcessor,
Coefficients: () => Coefficients,
Equalizer: () => Equalizer,
EqualizerCoefficients: () => EqualizerCoefficients,
EqualizerConfiguration: () => EqualizerConfiguration,
EqualizerStream: () => EqualizerStream,
FilterType: () => FilterType,
FiltersChain: () => FiltersChain,
Frequency: () => Frequency,
MonoStereoTransformer: () => MonoStereoTransformer,
PCMAudioTransformer: () => transformers_exports,
PCMResampler: () => PCMResampler,
PCMTransformer: () => PCMTransformer,
Q_BUTTERWORTH: () => Q_BUTTERWORTH,
VolumeTransformer: () => VolumeTransformer,
version: () => version
});
module.exports = __toCommonJS(src_exports);
// src/biquad/Coefficients.ts
var FilterType = {
SinglePoleLowPassApprox: 0,
SinglePoleLowPass: 1,
LowPass: 2,
HighPass: 3,
BandPass: 4,
Notch: 5,
AllPass: 6,
LowShelf: 7,
HighShelf: 8,
PeakingEQ: 9
};
var Q_BUTTERWORTH = Math.SQRT1_2;
var _Coefficients = class _Coefficients {
constructor(data) {
// Denominator coefficients
__publicField(this, "a1", 0);
__publicField(this, "a2", 0);
// Nominator coefficients
__publicField(this, "b0", 0);
__publicField(this, "b1", 0);
__publicField(this, "b2", 0);
if (data) {
this.a1 = data.a1;
this.a2 = data.a2;
this.b0 = data.b0;
this.b1 = data.b1;
this.b2 = data.b2;
}
}
static from(filter, samplingFreq, cutoffFreq, Q, dbGain = -10) {
if (2 * cutoffFreq > samplingFreq) {
throw new Error(`Cutoff frequency is too big!`);
}
if (Q < 0) {
throw new Error(`Q may not be negative`);
}
const omega = 2 * Math.PI * cutoffFreq / samplingFreq;
const bqf = typeof filter === "string" ? FilterType[filter] : filter;
switch (bqf) {
case FilterType.SinglePoleLowPassApprox: {
const alpha = omega / (omega + 1);
return new _Coefficients({
a1: alpha - 1,
a2: 0,
b0: alpha,
b1: 0,
b2: 0
});
}
case FilterType.SinglePoleLowPass: {
const omega_t = Math.tan(omega / 2);
const a0 = 1 + omega_t;
return new _Coefficients({
a1: (omega_t - 1) / a0,
a2: 0,
b0: omega_t / a0,
b1: omega_t / a0,
b2: 0
});
}
case FilterType.LowPass: {
const omega_s = Math.sin(omega);
const omega_c = Math.cos(omega);
const alpha = omega_s / (2 * Q);
const b0 = (1 - omega_c) * 0.5;
const b1 = 1 - omega_c;
const b2 = (1 - omega_c) * 0.5;
const a0 = 1 + alpha;
const a1 = -2 * omega_c;
const a2 = 1 - alpha;
const div = 1 / a0;
return new _Coefficients({
a1: a1 * div,
a2: a2 * div,
b0: b0 * div,
b1: b1 * div,
b2: b2 * div
});
}
case FilterType.HighPass: {
const omega_s = Math.sin(omega);
const omega_c = Math.cos(omega);
const alpha = omega_s / (2 * Q);
const b0 = (1 + omega_c) * 0.5;
const b1 = -(1 + omega_c);
const b2 = (1 + omega_c) * 0.5;
const a0 = 1 + alpha;
const a1 = -2 * omega_c;
const a2 = 1 - alpha;
const div = 1 / a0;
return new _Coefficients({
a1: a1 * div,
a2: a2 * div,
b0: b0 * div,
b1: b1 * div,
b2: b2 * div
});
}
case FilterType.Notch: {
const omega_s = Math.sin(omega);
const omega_c = Math.cos(omega);
const alpha = omega_s / (2 * Q);
const b0 = 1;
const b1 = -2 * omega_c;
const b2 = 1;
const a0 = 1 + alpha;
const a1 = -2 * omega_c;
const a2 = 1 - alpha;
const div = 1 / a0;
return new _Coefficients({
a1: a1 * div,
a2: a2 * div,
b0: b0 * div,
b1: b1 * div,
b2: b2 * div
});
}
case FilterType.BandPass: {
const omega_s = Math.sin(omega);
const omega_c = Math.cos(omega);
const alpha = omega_s / (2 * Q);
const b0 = omega_s / 2;
const b1 = 0;
const b2 = -(omega_s / 2);
const a0 = 1 + alpha;
const a1 = -2 * omega_c;
const a2 = 1 - alpha;
const div = 1 / a0;
return new _Coefficients({
a1: a1 * div,
a2: a2 * div,
b0: b0 * div,
b1: b1 * div,
b2: b2 * div
});
}
case FilterType.AllPass: {
const omega_s = Math.sin(omega);
const omega_c = Math.cos(omega);
const alpha = omega_s / (2 * Q);
const b0 = 1 - alpha;
const b1 = -2 * omega_c;
const b2 = 1 + alpha;
const a0 = 1 + alpha;
const a1 = -2 * omega_c;
const a2 = 1 - alpha;
return new _Coefficients({
a1: a1 / a0,
a2: a2 / a0,
b0: b0 / a0,
b1: b1 / a0,
b2: b2 / a0
});
}
case FilterType.LowShelf: {
const a = Math.pow(10, dbGain / 40);
const omega_s = Math.sin(omega);
const omega_c = Math.cos(omega);
const alpha = omega_s / (2 * Q);
const b0 = a * (a + 1 - (a - 1) * omega_c + 2 * alpha * Math.sqrt(a));
const b1 = 2 * a * (a - 1 - (a + 1) * omega_c);
const b2 = a * (a + 1 - (a - 1) * omega_c - 2 * alpha * Math.sqrt(a));
const a0 = a + 1 + (a - 1) * omega_c + 2 * alpha * Math.sqrt(a);
const a1 = -2 * (a - 1 + (a + 1) * omega_c);
const a2 = a + 1 + (a - 1) * omega_c - 2 * alpha * Math.sqrt(a);
return new _Coefficients({
a1: a1 / a0,
a2: a2 / a0,
b0: b0 / a0,
b1: b1 / a0,
b2: b2 / a0
});
}
case FilterType.HighShelf: {
const a = Math.pow(10, dbGain / 40);
const omega_s = Math.sin(omega);
const omega_c = Math.cos(omega);
const alpha = omega_s / (2 * Q);
const b0 = a * (a + 1 + (a - 1) * omega_c + 2 * alpha * Math.sqrt(a));
const b1 = -2 * a * (a - 1 + (a + 1) * omega_c);
const b2 = a * (a + 1 + (a - 1) * omega_c - 2 * alpha * Math.sqrt(a));
const a0 = a + 1 - (a - 1) * omega_c + 2 * alpha * Math.sqrt(a);
const a1 = 2 * (a - 1 - (a + 1) * omega_c);
const a2 = a + 1 - (a - 1) * omega_c - 2 * alpha * Math.sqrt(a);
return new _Coefficients({
a1: a1 / a0,
a2: a2 / a0,
b0: b0 / a0,
b1: b1 / a0,
b2: b2 / a0
});
}
case FilterType.PeakingEQ: {
const a = Math.pow(10, dbGain / 40);
const omega_s = Math.sin(omega);
const omega_c = Math.cos(omega);
const alpha = omega_s / (2 * Q);
const b0 = 1 + alpha * a;
const b1 = -2 * omega_c;
const b2 = 1 - alpha * a;
const a0 = 1 + alpha / a;
const a1 = -2 * omega_c;
const a2 = 1 - alpha / a;
return new _Coefficients({
a1: a1 / a0,
a2: a2 / a0,
b0: b0 / a0,
b1: b1 / a0,
b2: b2 / a0
});
}
default:
throw new TypeError(`Invalid filter type "${filter}"`);
}
}
};
__name(_Coefficients, "Coefficients");
var Coefficients = _Coefficients;
// src/biquad/Biquad.ts
var _BiquadFilter = class _BiquadFilter {
constructor(coefficients) {
this.coefficients = coefficients;
__publicField(this, "x1", 0);
__publicField(this, "x2", 0);
__publicField(this, "y1", 0);
__publicField(this, "y2", 0);
__publicField(this, "s1", 0);
__publicField(this, "s2", 0);
}
setFilter(filter, options) {
const coefficients = Coefficients.from(
filter,
options.fs,
options.f0,
options.Q,
options.gain
);
this.update(coefficients);
}
update(coefficients) {
this.coefficients = coefficients;
}
replace(coefficients) {
this.coefficients = coefficients;
}
reset() {
this.x1 = 0;
this.x2 = 0;
this.y1 = 0;
this.y2 = 0;
this.s1 = 0;
this.s2 = 0;
}
run(input) {
const { a1, a2, b0, b1, b2 } = this.coefficients;
const out = b0 * input + b1 * this.x1 + b2 * this.x2 - a1 * this.y1 - a2 * this.y2;
this.x2 = this.x1;
this.x1 = input;
this.y2 = this.y1;
this.y1 = out;
return out;
}
runTransposed(input) {
const { a1, a2, b0, b1, b2 } = this.coefficients;
const out = this.s1 + b0 * input;
this.s1 = this.s2 + b1 * input - a1 * out;
this.s2 = b2 * input - a2 * out;
return out;
}
};
__name(_BiquadFilter, "BiquadFilter");
var BiquadFilter = _BiquadFilter;
// src/utils/Frequency.ts
var _Frequency = class _Frequency {
constructor(__val) {
this.__val = __val;
if (typeof __val !== "number" || isNaN(__val) || __val === Infinity)
throw new TypeError("Frequency value must be a number");
if (this.__val < 0)
throw new Error(`Frequency value cannot be negative (${__val})`);
}
khz() {
return this.__val * 1e3;
}
mhz() {
return this.__val * 1e6;
}
hz() {
return this.__val;
}
dt() {
return 1 / this.__val;
}
valueOf() {
return this.__val;
}
toString() {
return `${this.__val}Hz`;
}
toJSON() {
return this.toString();
}
};
__name(_Frequency, "Frequency");
var Frequency = _Frequency;
// src/utils/PCMTransformer.ts
var import_stream = require("stream");
var _PCMTransformer = class _PCMTransformer extends import_stream.Transform {
constructor(options = {}) {
super(options);
__publicField(this, "type", "s16le");
__publicField(this, "bits");
__publicField(this, "bytes");
__publicField(this, "extremum");
__publicField(this, "disabled", false);
__publicField(this, "sampleRate", 48e3);
__publicField(this, "onUpdate", /* @__PURE__ */ __name(() => {
}, "onUpdate"));
options.type ?? (options.type = "s16le");
this.disabled = !!options.disabled;
if (typeof options.sampleRate === "number" && options.sampleRate > 0) {
this.sampleRate = options.sampleRate;
}
switch (options.type) {
case "s16be":
case "s16le":
this.type = options.type;
this.bits = 16;
break;
case "s32be":
case "s32le":
this.type = options.type;
this.bits = 32;
break;
default:
throw new TypeError(
`Expected type to be one of ${["s16be", "s16le", "s32be", "s32le"].join(", ")}, got "${options.type}"`
);
}
this.bytes = this.bits / 8;
this.extremum = Math.pow(2, this.bits - 1);
}
disable() {
this.disabled = true;
}
enable() {
this.disabled = false;
}
toggle() {
this.disabled = !this.disabled;
}
_readInt(buffer, index) {
const method = `readInt${this.type.substring(1).toUpperCase()}`;
return buffer[method](index);
}
_writeInt(buffer, int, index) {
const method = `writeInt${this.type.substring(1).toUpperCase()}`;
return buffer[method](int, index);
}
clamp(val, max = this.extremum - 1, min = -this.extremum) {
return Math.min(max, Math.max(min, val));
}
setSampleRate(rate) {
this.sampleRate = rate;
return;
}
};
__name(_PCMTransformer, "PCMTransformer");
var PCMTransformer = _PCMTransformer;
// src/biquad/BiquadStream.ts
var _BiquadStream = class _BiquadStream extends PCMTransformer {
constructor(options = {}) {
super(options);
__publicField(this, "biquad");
__publicField(this, "cutoff", 80);
__publicField(this, "gain", 0);
__publicField(this, "biquadFilter");
__publicField(this, "Q", Q_BUTTERWORTH);
if ("cutoff" in options) this.cutoff = options.cutoff;
if ("gain" in options) this.gain = options.gain;
if ("Q" in options) this.Q = options.Q;
if ("biquadFilter" in options) {
if (typeof options.biquadFilter === "string" || typeof options.biquadFilter === "number")
this.biquadFilter = options.filter;
if (this.biquadFilter != null) {
this.biquad = new BiquadFilter(
Coefficients.from(
this.biquadFilter,
this.sampleRate,
this.cutoff,
this.Q,
this.gain
)
);
}
}
}
get filters() {
return this.biquadFilter;
}
set filters(f) {
if (f == null || typeof f === "string" || typeof f === "number") {
this.update({ filter: f });
} else {
throw new TypeError(`Invalid biquad filter type "${f}"`);
}
}
getFilterName() {
if (this.biquadFilter == null) return null;
if (typeof this.biquadFilter === "string") return this.biquadFilter;
return Object.entries(FilterType).find(
(r) => r[1] === this.biquadFilter
)?.[0];
}
update(options) {
if ("cutoff" in options) this.cutoff = options.cutoff;
if ("gain" in options) this.gain = options.gain;
if ("Q" in options) this.Q = options.Q;
if ("filter" in options) this.biquadFilter = options.filter;
if (this.biquadFilter != null) {
this.biquad = new BiquadFilter(
Coefficients.from(
this.biquadFilter,
this.sampleRate,
this.cutoff,
this.Q,
this.gain
)
);
}
this.onUpdate?.();
}
setFilter(filter) {
this.update({ filter });
}
setQ(Q) {
this.update({ Q });
}
setCutoff(f0) {
this.update({ cutoff: f0 });
}
setGain(dB) {
this.update({ gain: dB });
}
_transform(chunk, encoding, callback) {
if (this.disabled || !this.biquad) {
this.push(chunk);
return callback();
}
const endIndex = Math.floor(chunk.length / 2) * 2;
const { bytes } = this;
for (let sampleIndex = 0; sampleIndex < endIndex; sampleIndex += bytes) {
const int = this._readInt(chunk, sampleIndex);
const result = this.biquad.run(int);
this._writeInt(chunk, this.clamp(result), sampleIndex);
}
this.push(chunk);
return callback();
}
};
__name(_BiquadStream, "BiquadStream");
var BiquadStream = _BiquadStream;
// src/equalizer/ChannelProcessor.ts
var _ChannelProcessor = class _ChannelProcessor {
constructor(bandMultipliers) {
__publicField(this, "history");
__publicField(this, "bandMultipliers");
__publicField(this, "current");
__publicField(this, "m1");
__publicField(this, "m2");
this.history = new Array(Equalizer.BAND_COUNT * 6).fill(0);
this.bandMultipliers = bandMultipliers;
this.current = 0;
this.m1 = 2;
this.m2 = 1;
}
processInt(int) {
let result = int * 0.25;
for (let bandIndex = 0; bandIndex < Equalizer.BAND_COUNT; bandIndex++) {
const x = bandIndex * 6;
const y = x + 3;
const coefficients = Equalizer.Coefficients48000[bandIndex];
const bandResult = coefficients.alpha * (int - this.history[x + this.m2]) + coefficients.gamma * this.history[y + this.m1] - coefficients.beta * this.history[y + this.m2];
this.history[x + this.current] = int;
this.history[y + this.current] = bandResult;
result += bandResult * this.bandMultipliers[bandIndex];
}
const val = result * 4;
return val;
}
process(samples, extremum = 131072, bytes = 2, readInt, writeInt) {
const endIndex = Math.floor(samples.length / 2) * 2;
for (let sampleIndex = 0; sampleIndex < endIndex; sampleIndex += bytes) {
const sample = readInt?.(samples, sampleIndex) ?? samples.readInt16LE(sampleIndex);
const result = this.processInt(sample);
const val = Math.min(extremum - 1, Math.max(-extremum, result));
writeInt?.(samples, val, sampleIndex) ?? samples.writeInt16LE(val, sampleIndex);
this.step();
}
return samples;
}
step() {
if (++this.current === 3) {
this.current = 0;
}
if (++this.m1 === 3) {
this.m1 = 0;
}
if (++this.m2 === 3) {
this.m2 = 0;
}
}
reset() {
this.history.fill(0);
}
};
__name(_ChannelProcessor, "ChannelProcessor");
var ChannelProcessor = _ChannelProcessor;
// src/equalizer/Coefficients.ts
var _EqualizerCoefficients = class _EqualizerCoefficients {
constructor(beta, alpha, gamma) {
this.beta = beta;
this.alpha = alpha;
this.gamma = gamma;
}
setBeta(v) {
this.beta = v;
}
setAlpha(v) {
this.alpha = v;
}
setGamma(v) {
this.gamma = v;
}
toJSON() {
const { alpha, beta, gamma } = this;
return { alpha, beta, gamma };
}
};
__name(_EqualizerCoefficients, "EqualizerCoefficients");
var EqualizerCoefficients = _EqualizerCoefficients;
// src/equalizer/EqualizerConfiguration.ts
var _EqualizerConfiguration = class _EqualizerConfiguration {
constructor(bandMultipliers) {
this.bandMultipliers = bandMultipliers;
}
setGain(band, value) {
if (this.isValidBand(band)) {
this.bandMultipliers[band] = Math.max(Math.min(value, 1), -0.25);
}
}
getGain(band) {
if (this.isValidBand(band)) {
return this.bandMultipliers[band];
} else {
return 0;
}
}
isValidBand(band) {
return band >= 0 && band < this.bandMultipliers.length;
}
};
__name(_EqualizerConfiguration, "EqualizerConfiguration");
var EqualizerConfiguration = _EqualizerConfiguration;
// src/equalizer/Equalizer.ts
var _Equalizer = class _Equalizer extends EqualizerConfiguration {
constructor(channelCount, bandMultipliers) {
super(bandMultipliers);
__publicField(this, "channels", []);
__publicField(this, "channelCount");
this.channelCount = channelCount;
this.channels = this.createChannelProcessor();
}
createChannelProcessor() {
return Array.from({ length: this.channelCount }, () => {
return new ChannelProcessor(this.bandMultipliers);
});
}
process(input) {
return this.channels.map((c, i) => {
const { data, extremum, readInt, writeInt, bytes } = input[i];
return c.process(data, extremum, bytes, readInt, writeInt);
});
}
};
__name(_Equalizer, "Equalizer");
__publicField(_Equalizer, "BAND_COUNT", 15);
__publicField(_Equalizer, "SAMPLE_RATE", 48e3);
__publicField(_Equalizer, "Coefficients48000", [
new EqualizerCoefficients(0.99847546664, 76226668143e-14, 1.9984647656),
new EqualizerCoefficients(0.99756184654, 0.0012190767289, 1.9975344645),
new EqualizerCoefficients(0.99616261379, 0.0019186931041, 1.9960947369),
new EqualizerCoefficients(0.99391578543, 0.0030421072865, 1.9937449618),
new EqualizerCoefficients(0.99028307215, 0.0048584639242, 1.9898465702),
new EqualizerCoefficients(0.98485897264, 0.0075705136795, 1.9837962543),
new EqualizerCoefficients(0.97588512657, 0.012057436715, 1.9731772447),
new EqualizerCoefficients(0.96228521814, 0.018857390928, 1.9556164694),
new EqualizerCoefficients(0.94080933132, 0.029595334338, 1.9242054384),
new EqualizerCoefficients(0.90702059196, 0.046489704022, 1.8653476166),
new EqualizerCoefficients(0.85868004289, 0.070659978553, 1.7600401337),
new EqualizerCoefficients(0.78409610788, 0.10795194606, 1.5450725522),
new EqualizerCoefficients(0.68332861002, 0.15833569499, 1.1426447155),
new EqualizerCoefficients(
0.55267518228,
0.22366240886,
0.40186190803
),
new EqualizerCoefficients(
0.41811888447,
0.29094055777,
-0.70905944223
)
]);
var Equalizer = _Equalizer;
// src/equalizer/EqualizerStream.ts
var _EqualizerStream = class _EqualizerStream extends PCMTransformer {
constructor(options) {
super(options);
__publicField(this, "bandMultipliers", new Array(Equalizer.BAND_COUNT).fill(0));
__publicField(this, "equalizer");
options = Object.assign(
{},
{
bandMultiplier: [],
channels: 1
},
options || {}
);
this.equalizer = new Equalizer(options.channels || 1, this.bandMultipliers);
if (Array.isArray(options.bandMultiplier))
this._processBands(options.bandMultiplier);
}
_processBands(multiplier) {
for (const mul of multiplier) {
if (mul.band > Equalizer.BAND_COUNT - 1 || mul.band < 0)
throw new RangeError(
`Band value out of range. Expected >0 & <${Equalizer.BAND_COUNT - 1}, received "${mul.band}"`
);
this.equalizer.setGain(mul.band, mul.gain);
}
this.onUpdate?.();
}
_transform(chunk, encoding, callback) {
if (this.disabled) {
this.push(chunk);
return callback();
}
this.equalizer.process(
[
{
data: chunk,
extremum: this.extremum,
readInt: /* @__PURE__ */ __name((b, idx) => this._readInt(b, idx), "readInt"),
writeInt: /* @__PURE__ */ __name((b, i, idx) => this._writeInt(b, i, idx), "writeInt"),
bytes: this.bytes
}
]
);
this.push(chunk);
return callback();
}
getEQ() {
return this.bandMultipliers.map((m, i) => ({
band: i,
gain: m
}));
}
setEQ(bands) {
this._processBands(bands);
}
resetEQ() {
this._processBands(
Array.from(
{
length: Equalizer.BAND_COUNT
},
(_, i) => ({
band: i,
gain: 0
})
)
);
}
};
__name(_EqualizerStream, "EqualizerStream");
var EqualizerStream = _EqualizerStream;
// src/audio/MonoStereoTransformer.ts
var _MonoStereoTransformer = class _MonoStereoTransformer extends PCMTransformer {
constructor(options) {
super(options);
__publicField(this, "strategy");
if (!["m2s", "s2m"].includes(options?.strategy)) {
throw new TypeError(`Strategy must be "m2s" or "s2m"`);
}
this.strategy = options.strategy;
}
setStrategy(strategy) {
this.strategy = strategy;
}
_transform(chunk, encoding, callback) {
if (this.disabled) {
this.push(chunk);
return callback();
}
const len = Math.floor(chunk.length / 2) * 2;
if (this.strategy === "m2s") {
this.push(this.toStereo(chunk, len));
} else {
this.push(this.toMono(chunk, len));
}
return callback();
}
toStereo(sample, len) {
const bytes = this.bytes;
const stereoBuffer = Buffer.alloc(len * 2);
for (let i = 0; i < len; i += bytes) {
stereoBuffer[i * 2 + 0] = sample[i];
stereoBuffer[i * 2 + 1] = sample[i + 1];
stereoBuffer[i * 2 + 2] = sample[i];
stereoBuffer[i * 2 + 3] = sample[i + 1];
}
return stereoBuffer;
}
toMono(sample, len) {
const bytes = this.bytes;
const monoBuffer = Buffer.alloc(Math.floor(len / 2));
for (let i = 0; i < len; i += bytes) {
monoBuffer[i] = sample[i * 2 + 0];
monoBuffer[i + 1] = sample[i * 2 + 1];
}
return monoBuffer;
}
};
__name(_MonoStereoTransformer, "MonoStereoTransformer");
var MonoStereoTransformer = _MonoStereoTransformer;
// src/audio/transformers/index.ts
var transformers_exports = {};
__export(transformers_exports, {
applyBiquad: () => applyBiquad,
applyEqualization: () => applyEqualization,
applyPulsator: () => applyPulsator,
applyTremolo: () => applyTremolo,
applyVibrato: () => applyVibrato,
applyVolume: () => applyVolume
});
// src/audio/transformers/biquad.ts
function applyBiquad(filterer, int) {
return filterer.run(int);
}
__name(applyBiquad, "applyBiquad");
// src/audio/transformers/dsp.ts
function applyPulsator(config, int, channel) {
const sin = Math.sin(config.x);
const currentChannelVal = channel === 0 ? sin : -sin;
const res = int * (currentChannelVal + 1) / 2;
config.x += config.dI;
return res;
}
__name(applyPulsator, "applyPulsator");
function applyTremolo(config, int, sampleRate) {
const fOffset = 1 - config.depth;
const modSignal = fOffset + config.depth * Math.sin(config.phase);
config.phase += 2 * Math.PI / sampleRate * config.frequency;
return modSignal * int;
}
__name(applyTremolo, "applyTremolo");
function applyVibrato(config, int, sampleRate) {
const fOffset = 1 - config.depth;
const modSignal = fOffset + config.depth * Math.sin(2 * Math.PI * config.phase);
config.phase += 2 * Math.PI / sampleRate * config.frequency;
return modSignal * int;
}
__name(applyVibrato, "applyVibrato");
function applyVolume(vol, int) {
return vol * int;
}
__name(applyVolume, "applyVolume");
// src/audio/transformers/equalizer.ts
function applyEqualization(eq, int) {
const processor = eq.channels[0];
const result = processor.processInt(int);
processor.step();
return result;
}
__name(applyEqualization, "applyEqualization");
// src/audio/AudioFilter.ts
var AudioFilters = {
"8D": "8D",
Tremolo: "Tremolo",
Vibrato: "Vibrato"
};
var AF_NIGHTCORE_RATE = 1.3;
var AF_VAPORWAVE_RATE = 0.8;
var BASS_EQ_BANDS = Array.from(
{ length: 3 },
(_, i) => ({
band: i,
gain: 0.25
})
);
var _AudioFilter = class _AudioFilter extends PCMTransformer {
constructor(options) {
super(options);
__publicField(this, "filters", []);
__publicField(this, "targetSampleRate", this.sampleRate);
__publicField(this, "totalSamples", 0);
__publicField(this, "_processedSamples", 0);
__publicField(this, "pulsatorConfig", {
hz: 0.02,
x: 0,
dI: 3926990816987241e-21
});
__publicField(this, "tremoloConfig", {
phase: 0,
depth: 0.5,
frequency: 5
});
__publicField(this, "vibratoConfig", {
phase: 0,
depth: 0.5,
frequency: 5
});
if (options && Array.isArray(options.filters)) {
this.setFilters(options.filters);
}
this.onUpdate?.();
}
setTargetSampleRate(rate) {
this.targetSampleRate = rate || this.sampleRate;
return;
}
setPulsator(hz) {
hz /= 4;
this.pulsatorConfig.hz = hz;
const samplesPerCycle = this.targetSampleRate / (hz * 2 * Math.PI);
this.pulsatorConfig.dI = hz === 0 ? 0 : 1 / samplesPerCycle;
this.onUpdate?.();
}
get pulsator() {
return this.pulsatorConfig.hz;
}
setTremolo({
depth = this.tremoloConfig.depth,
frequency = this.tremoloConfig.frequency,
phase = this.tremoloConfig.phase
}) {
if (typeof depth === "number") this.tremoloConfig.depth = depth;
if (typeof frequency === "number") this.tremoloConfig.frequency = frequency;
if (typeof phase === "number") this.tremoloConfig.phase = phase;
this.onUpdate?.();
}
setVibrato({
depth = this.vibratoConfig.depth,
frequency = this.vibratoConfig.frequency,
phase = this.vibratoConfig.phase
}) {
if (typeof depth === "number") this.vibratoConfig.depth = depth;
if (typeof frequency === "number") this.vibratoConfig.frequency = frequency;
if (typeof phase === "number") this.vibratoConfig.phase = phase;
this.onUpdate?.();
}
get tremolo() {
return this.tremoloConfig;
}
setFilters(filters) {
if (!Array.isArray(filters) || !filters.every((r) => r in AudioFilters)) {
return false;
}
this.filters = filters;
this.onUpdate?.();
return true;
}
// TODO
seek(duration) {
throw new Error("Not Implemented");
}
_transform(chunk, encoding, callback) {
this._processedSamples++;
this.totalSamples += chunk.length / this.bits;
if (this.disabled || !this.filters.length) {
return callback(null, chunk);
}
const len = Math.floor(chunk.length / 2) * 2;
const { bytes } = this;
let L = false;
for (let i = 0; i < len; i += bytes) {
const int = this._readInt(chunk, i);
const value = this.applyFilters(int, +(L = !L));
this._writeInt(chunk, this.clamp(value), i);
}
this.push(chunk);
return callback();
}
get currentSampleRate() {
return this.targetSampleRate || this.sampleRate;
}
get estimatedDuration() {
return this.totalSamples / this.targetSampleRate * 1e3;
}
get currentDuration() {
return this._processedSamples * 1e3 / this.targetSampleRate;
}
applyFilters(byte, channel) {
if (this.filters.length) {
for (const filter of this.filters) {
if (filter === "8D") {
byte = applyPulsator(this.pulsatorConfig, byte, channel);
}
if (filter === "Tremolo") {
byte = applyTremolo(this.tremoloConfig, byte, this.currentSampleRate);
}
if (filter === "Vibrato") {
byte = applyVibrato(this.vibratoConfig, byte, this.currentSampleRate);
}
}
}
return byte;
}
};
__name(_AudioFilter, "AudioFilter");
var AudioFilter = _AudioFilter;
// src/audio/PCMResampler.ts
var _PCMResampler = class _PCMResampler extends PCMTransformer {
constructor(options) {
super(options);
__publicField(this, "targetSampleRate", this.sampleRate);
if (options?.targetSampleRate)
this.targetSampleRate = options.targetSampleRate;
}
get AF_NIGHTCORE() {
return 64e3;
}
get AF_VAPORWAVE() {
return 32e3;
}
setTargetSampleRate(rate) {
if (rate === "NIGHTCORE" || rate === "VAPORWAVE") rate = this[`AF_${rate}`];
if (typeof rate !== "number") return false;
this.targetSampleRate = rate;
this.onUpdate?.();
return true;
}
// TODO: enable this
_transform(chunk, _, cb) {
if (this.disabled || this.sampleRate === this.targetSampleRate) {
this.push(chunk);
return cb();
}
this.push(chunk);
cb();
}
};
__name(_PCMResampler, "PCMResampler");
var PCMResampler = _PCMResampler;
// src/audio/VolumeTransformer.ts
var _VolumeTransformer = class _VolumeTransformer extends PCMTransformer {
constructor(options) {
super(options);
__publicField(this, "_volume", 1);
if (typeof options?.volume === "number") {
this.setVolume(options.volume);
}
}
get volumeApprox() {
return this._volume * 100;
}
get volume() {
return Math.floor(this.volumeApprox);
}
set volume(volume) {
this.setVolume(volume);
}
setVolume(volume) {
if (typeof volume !== "number" || isNaN(volume))
throw new Error(
`Expected volume amount to be a number, received ${typeof volume}!`
);
if (volume < 0) volume = 0;
if (!isFinite(volume)) volume = 100;
this._volume = volume / 100;
this.onUpdate?.();
return true;
}
_transform(chunk, encoding, callback) {
if (this.disabled || this._volume === 1) {
this.push(chunk);
return callback();
}
const len = Math.floor(chunk.length / 2) * 2;
const { bytes } = this;
for (let i = 0; i < len; i += bytes) {
const int = this._readInt(chunk, i);
const amp = this.clamp(int * this._volume);
this._writeInt(chunk, amp, i);
}
this.push(chunk);
return callback();
}
toString() {
return `${this.volume}%`;
}
};
__name(_VolumeTransformer, "VolumeTransformer");
var VolumeTransformer = _VolumeTransformer;
// src/FiltersChainBuilder.ts
var import_stream2 = require("stream");
var _FiltersChain = class _FiltersChain {
constructor(presets = {}) {
this.presets = presets;
__publicField(this, "equalizer", null);
__publicField(this, "filters", null);
__publicField(this, "biquad", null);
__publicField(this, "volume", null);
__publicField(this, "resampler", null);
__publicField(this, "destination", null);
__publicField(this, "source", null);
__publicField(this, "onUpdate", /* @__PURE__ */ __name(() => null, "onUpdate"));
__publicField(this, "onError", /* @__PURE__ */ __name(() => null, "onError"));
}
create(src, presets = this.presets) {
this.destroy();
this.source = src;
const equalizerStream = !presets.equalizer?.disabled ? new EqualizerStream(presets.equalizer) : null;
const dspStream = !presets.dsp?.disabled ? new AudioFilter(presets.dsp) : null;
const biquadStream = !presets.biquad?.disabled ? new BiquadStream(presets.biquad) : null;
const volumeTransformer = !presets.volume?.disabled ? new VolumeTransformer(presets.volume) : null;
this.equalizer = equalizerStream;
this.filters = dspStream;
this.biquad = biquadStream;
this.volume = volumeTransformer;
if (equalizerStream) equalizerStream.onUpdate = this.onUpdate;
if (dspStream) dspStream.onUpdate = this.onUpdate;
if (biquadStream) biquadStream.onUpdate = this.onUpdate;
if (volumeTransformer) volumeTransformer.onUpdate = this.onUpdate;
const chains = [
src,
equalizerStream,
dspStream,
biquadStream,
volumeTransformer
].filter(Boolean);
if (!chains.length) return src;
this.destination = (0, import_stream2.pipeline)(...chains, (err) => {
if (err) {
this.destroy();
if (!err.message.includes("ERR_STREAM_PREMATURE_CLOSE"))
this.onError(err);
}
});
this.destination.once("close", this.destroy.bind(this));
return this.destination;
}
destroy() {
this.equalizer?.destroy();
this.biquad?.destroy();
this.filters?.destroy();
this.volume?.destroy();
this.destination?.destroy();
this.source?.destroy();
this.equalizer?.removeAllListeners();
this.biquad?.removeAllListeners();
this.filters?.removeAllListeners();
this.volume?.removeAllListeners();
this.destination?.removeAllListeners();
this.source?.removeAllListeners();
this.equalizer = null;
this.biquad = null;
this.filters = null;
this.volume = null;
this.destination = null;
this.source = null;
}
};
__name(_FiltersChain, "FiltersChain");
var FiltersChain = _FiltersChain;
// src/version.ts
var version = (
/* @__MACRO__ getVersion */
"7.1.0"
);
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
AF_NIGHTCORE_RATE,
AF_VAPORWAVE_RATE,
AudioFilter,
AudioFilters,
BASS_EQ_BANDS,
BiquadFilter,
BiquadStream,
ChannelProcessor,
Coefficients,
Equalizer,
EqualizerCoefficients,
EqualizerConfiguration,
EqualizerStream,
FilterType,
FiltersChain,
Frequency,
MonoStereoTransformer,
PCMAudioTransformer,
PCMResampler,
PCMTransformer,
Q_BUTTERWORTH,
VolumeTransformer,
version
});
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL2luZGV4LnRzIiwgIi4uL3NyYy9iaXF1YWQvQ29lZmZpY2llbnRzLnRzIiwgIi4uL3NyYy9iaXF1YWQvQmlxdWFkLnRzIiwgIi4uL3NyYy91dGlscy9GcmVxdWVuY3kudHMiLCAiLi4vc3JjL3V0aWxzL1BDTVRyYW5zZm9ybWVyLnRzIiwgIi4uL3NyYy9iaXF1YWQvQmlxdWFkU3RyZWFtLnRzIiwgIi4uL3NyYy9lcXVhbGl6ZXIvQ2hhbm5lbFByb2Nlc3Nvci50cyIsICIuLi9zcmMvZXF1YWxpemVyL0NvZWZmaWNpZW50cy50cyIsICIuLi9zcmMvZXF1YWxpemVyL0VxdWFsaXplckNvbmZpZ3VyYXRpb24udHMiLCAiLi4vc3JjL2VxdWFsaXplci9FcXVhbGl6ZXIudHMiLCAiLi4vc3JjL2VxdWFsaXplci9FcXVhbGl6ZXJTdHJlYW0udHMiLCAiLi4vc3JjL2F1ZGlvL01vbm9TdGVyZW9UcmFuc2Zvcm1lci50cyIsICIuLi9zcmMvYXVkaW8vdHJhbnNmb3JtZXJzL2luZGV4LnRzIiwgIi4uL3NyYy9hdWRpby90cmFuc2Zvcm1lcnMvYmlxdWFkLnRzIiwgIi4uL3NyYy9hdWRpby90cmFuc2Zvcm1lcnMvZHNwLnRzIiwgIi4uL3NyYy9hdWRpby90cmFuc2Zvcm1lcnMvZXF1YWxpemVyLnRzIiwgIi4uL3NyYy9hdWRpby9BdWRpb0ZpbHRlci50cyIsICIuLi9zcmMvYXVkaW8vUENNUmVzYW1wbGVyLnRzIiwgIi4uL3NyYy9hdWRpby9Wb2x1bWVUcmFuc2Zvcm1lci50cyIsICIuLi9zcmMvRmlsdGVyc0NoYWluQnVpbGRlci50cyIsICIuLi9zcmMvdmVyc2lvbi50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiZXhwb3J0ICogZnJvbSAnLi9iaXF1YWQnO1xuZXhwb3J0ICogZnJvbSAnLi9lcXVhbGl6ZXInO1xuZXhwb3J0ICogZnJvbSAnLi91dGlscyc7XG5leHBvcnQgKiBmcm9tICcuL2F1ZGlvJztcbmV4cG9ydCAqIGZyb20gJy4vRmlsdGVyc0NoYWluQnVpbGRlcic7XG5leHBvcnQgeyB2ZXJzaW9uIH0gZnJvbSAnLi92ZXJzaW9uJzsiLCAiZXhwb3J0IGNvbnN0IEZpbHRlclR5cGUgPSB7XG4gIFNpbmdsZVBvbGVMb3dQYXNzQXBwcm94OiAwLFxuICBTaW5nbGVQb2xlTG93UGFzczogMSxcbiAgTG93UGFzczogMixcbiAgSGlnaFBhc3M6IDMsXG4gIEJhbmRQYXNzOiA0LFxuICBOb3RjaDogNSxcbiAgQWxsUGFzczogNixcbiAgTG93U2hlbGY6IDcsXG4gIEhpZ2hTaGVsZjogOCxcbiAgUGVha2luZ0VROiA5XG59IGFzIGNvbnN0O1xuXG5leHBvcnQgdHlwZSBCaXF1YWRGaWx0ZXJzID1cbmtleW9mIHR5cGVvZiBGaWx0ZXJUeXBlIHxcbih0eXBlb2YgRmlsdGVyVHlwZSlba2V5b2YgdHlwZW9mIEZpbHRlclR5cGVdO1xuXG5pbnRlcmZhY2UgQ29lZmZpY2llbnRzSW5pdCB7XG4gIGExOiBudW1iZXI7XG4gIGEyOiBudW1iZXI7XG4gIGIwOiBudW1iZXI7XG4gIGIxOiBudW1iZXI7XG4gIGIyOiBudW1iZXI7XG59XG5cbmV4cG9ydCBjb25zdCBRX0JVVFRFUldPUlRIID0gTWF0aC5TUVJUMV8yO1xuXG5leHBvcnQgY2xhc3MgQ29lZmZpY2llbnRzIHtcbiAgLy8gRGVub21pbmF0b3IgY29lZmZpY2llbnRzXG4gIHB1YmxpYyBhMSA9IDA7XG4gIHB1YmxpYyBhMiA9IDA7XG5cbiAgLy8gTm9taW5hdG9yIGNvZWZmaWNpZW50c1xuICBwdWJsaWMgYjAgPSAwO1xuICBwdWJsaWMgYjEgPSAwO1xuICBwdWJsaWMgYjIgPSAwO1xuXG4gIHB1YmxpYyBjb25zdHJ1Y3RvcihkYXRhPzogQ29lZmZpY2llbnRzSW5pdCkge1xuICAgIGlmIChkYXRhKSB7XG4gICAgICB0aGlzLmExID0gZGF0YS5hMTtcbiAgICAgIHRoaXMuYTIgPSBkYXRhLmEyO1xuICAgICAgdGhpcy5iMCA9IGRhdGEuYjA7XG4gICAgICB0aGlzLmIxID0gZGF0YS5iMTtcbiAgICAgIHRoaXMuYjIgPSBkYXRhLmIyO1xuICAgIH1cbiAgfVxuXG4gIHB1YmxpYyBzdGF0aWMgZnJvbShcbiAgZmlsdGVyOiBCaXF1YWRGaWx0ZXJzLFxuICBzYW1wbGluZ0ZyZXE6IG51bWJlcixcbiAgY3V0b2ZmRnJlcTogbnVtYmVyLFxuICBROiBudW1iZXIsXG4gIGRiR2FpbiA9IC0xMClcbiAge1xuICAgIGlmICgyLjAgKiBjdXRvZmZGcmVxID4gc2FtcGxpbmdGcmVxKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYEN1dG9mZiBmcmVxdWVuY3kgaXMgdG9vIGJpZyFgKTtcbiAgICB9XG5cbiAgICBpZiAoUSA8IDApIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgUSBtYXkgbm90IGJlIG5lZ2F0aXZlYCk7XG4gICAgfVxuXG4gICAgY29uc3Qgb21lZ2EgPSAyLjAgKiBNYXRoLlBJICogY3V0b2ZmRnJlcSAvIHNhbXBsaW5nRnJlcTtcblxuICAgIGNvbnN0IGJxZiA9IHR5cGVvZiBmaWx0ZXIgPT09ICdzdHJpbmcnID8gRmlsdGVyVHlwZVtmaWx0ZXJdIDogZmlsdGVyO1xuXG4gICAgc3dpdGNoIChicWYpIHtcbiAgICAgIGNhc2UgRmlsdGVyVHlwZS5TaW5nbGVQb2xlTG93UGFzc0FwcHJveDp7XG4gICAgICAgICAgY29uc3QgYWxwaGEgPSBvbWVnYSAvIChvbWVnYSArIDEuMCk7XG5cbiAgICAgICAgICByZXR1cm4gbmV3IENvZWZmaWNpZW50cyh7XG4gICAgICAgICAgICBhMTogYWxwaGEgLSAxLjAsXG4gICAgICAgICAgICBhMjogMC4wLFxuICAgICAgICAgICAgYjA6IGFscGhhLFxuICAgICAgICAgICAgYjE6IDAuMCxcbiAgICAgICAgICAgIGIyOiAwLjBcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgY2FzZSBGaWx0ZXJUeXBlLlNpbmdsZVBvbGVMb3dQYXNzOntcbiAgICAgICAgICBjb25zdCBvbWVnYV90ID0gTWF0aC50YW4ob21lZ2EgLyAyLjApO1xuICAgICAgICAgIGNvbnN0IGEwID0gMS4wICsgb21lZ2FfdDtcblxuICAgICAgICAgIHJldHVybiBuZXcgQ29lZmZpY2llbnRzKHtcbiAgICAgICAgICAgIGExOiAob21lZ2FfdCAtIDEuMCkgLyBhMCxcbiAgICAgICAgICAgIGEyOiAwLjAsXG4gICAgICAgICAgICBiMDogb21lZ2FfdCAvIGEwLFxuICAgICAgICAgICAgYjE6IG9tZWdhX3QgLyBhMCxcbiAgICAgICAgICAgIGIyOiAwLjBcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgY2FzZSBGaWx0ZXJUeXBlLkxvd1Bhc3M6e1xuICAgICAgICAgIGNvbnN0IG9tZWdhX3MgPSBNYXRoLnNpbihvbWVnYSk7XG4gICAgICAgICAgY29uc3Qgb21lZ2FfYyA9IE1hdGguY29zKG9tZWdhKTtcbiAgICAgICAgICBjb25zdCBhbHBoYSA9IG9tZWdhX3MgLyAoMi4wICogUSk7XG5cbiAgICAgICAgICBjb25zdCBiMCA9ICgxLjAgLSBvbWVnYV9jKSAqIDAuNTtcbiAgICAgICAgICBjb25zdCBiMSA9IDEuMCAtIG9tZWdhX2M7XG4gICAgICAgICAgY29uc3QgYjIgPSAoMS4wIC0gb21lZ2FfYykgKiAwLjU7XG4gICAgICAgICAgY29uc3QgYTAgPSAxLjAgKyBhbHBoYTtcbiAgICAgICAgICBjb25zdCBhMSA9IC0yLjAgKiBvbWVnYV9jO1xuICAgICAgICAgIGNvbnN0IGEyID0gMS4wIC0gYWxwaGE7XG5cbiAgICAgICAgICBjb25zdCBkaXYgPSAxLjAgLyBhMDtcblxuICAgICAgICAgIHJldHVybiBuZXcgQ29lZmZpY2llbnRzKHtcbiAgICAgICAgICAgIGExOiBhMSAqIGRpdixcbiAgICAgICAgICAgIGEyOiBhMiAqIGRpdixcbiAgICAgICAgICAgIGIwOiBiMCAqIGRpdixcbiAgICAgICAgICAgIGIxOiBiMSAqIGRpdixcbiAgICAgICAgICAgIGIyOiBiMiAqIGRpdlxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICBjYXNlIEZpbHRlclR5cGUuSGlnaFBhc3M6e1xuICAgICAgICAgIGNvbnN0IG9tZWdhX3MgPSBNYXRoLnNpbihvbWVnYSk7XG4gICAgICAgICAgY29uc3Qgb21lZ2FfYyA9IE1hdGguY29zKG9tZWdhKTtcbiAgICAgICAgICBjb25zdCBhbHBoYSA9IG9tZWdhX3MgLyAoMi4wICogUSk7XG5cbiAgICAgICAgICBjb25zdCBiMCA9ICgxLjAgKyBvbWVnYV9jKSAqIDAuNTtcbiAgICAgICAgICBjb25zdCBiMSA9IC0oMS4wICsgb21lZ2FfYyk7XG4gICAgICAgICAgY29uc3QgYjIgPSAoMS4wICsgb21lZ2FfYykgKiAwLjU7XG4gICAgICAgICAgY29uc3QgYTAgPSAxLjAgKyBhbHBoYTtcbiAgICAgICAgICBjb25zdCBhMSA9IC0yLjAgKiBvbWVnYV9jO1xuICAgICAgICAgIGNvbnN0IGEyID0gMS4wIC0gYWxwaGE7XG5cbiAgICAgICAgICBjb25zdCBkaXYgPSAxLjAgLyBhMDtcblxuICAgICAgICAgIHJldHVybiBuZXcgQ29lZmZpY2llbnRzKHtcbiAgICAgICAgICAgIGExOiBhMSAqIGRpdixcbiAgICAgICAgICAgIGEyOiBhMiAqIGRpdixcbiAgICAgICAgICAgIGIwOiBiMCAqIGRpdixcbiAgICAgICAgICAgIGIxOiBiMSAqIGRpdixcbiAgICAgICAgICAgIGIyOiBiMiAqIGRpdlxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICBjYXNlIEZpbHRlclR5cGUuTm90Y2g6e1xuICAgICAgICAgIGNvbnN0IG9tZWdhX3MgPSBNYXRoLnNpbihvbWVnYSk7XG4gICAgICAgICAgY29uc3Qgb21lZ2FfYyA9IE1hdGguY29zKG9tZWdhKTtcbiAgICAgICAgICBjb25zdCBhbHBoYSA9IG9tZWdhX3MgLyAoMi4wICogUSk7XG5cbiAgICAgICAgICBjb25zdCBiMCA9IDEuMDtcbiAgICAgICAgICBjb25zdCBiMSA9IC0yLjAgKiBvbWVnYV9jO1xuICAgICAgICAgIGNvbnN0IGIyID0gMS4wO1xuICAgICAgICAgIGNvbnN0IGEwID0gMS4wICsgYWxwaGE7XG4gICAgICAgICAgY29uc3QgYTEgPSAtMi4wICogb21lZ2FfYztcbiAgICAgICAgICBjb25zdCBhMiA9IDEuMCAtIGFscGhhO1xuXG4gICAgICAgICAgY29uc3QgZGl2ID0gMS4wIC8gYTA7XG5cbiAgICAgICAgICByZXR1cm4gbmV3IENvZWZmaWNpZW50cyh7XG4gICAgICAgICAgICBhMTogYTEgKiBkaXYsXG4gICAgICAgICAgICBhMjogYTIgKiBkaXYsXG4gICAgICAgICAgICBiMDogYjAgKiBkaXYsXG4gICAgICAgICAgICBiMTogYjEgKiBkaXYsXG4gICAgICAgICAgICBiMjogYjIgKiBkaXZcbiAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgICAgY2FzZSBGaWx0ZXJUeXBlLkJhbmRQYXNzOntcbiAgICAgICAgICBjb25zdCBvbWVnYV9zID0gTWF0aC5zaW4ob21lZ2EpO1xuICAgICAgICAgIGNvbnN0IG9tZWdhX2MgPSBNYXRoLmNvcyhvbWVnYSk7XG4gICAgICAgICAgY29uc3QgYWxwaGEgPSBvbWVnYV9zIC8gKDIuMCAqIFEpO1xuXG4gICAgICAgICAgY29uc3QgYjAgPSBvbWVnYV9zIC8gMi4wO1xuICAgICAgICAgIGNvbnN0IGIxID0gMDtcbiAgICAgICAgICBjb25zdCBiMiA9IC0ob21lZ2FfcyAvIDIuMCk7XG4gICAgICAgICAgY29uc3QgYTAgPSAxLjAgKyBhbHBoYTtcbiAgICAgICAgICBjb25zdCBhMSA9IC0yLjAgKiBvbWVnYV9jO1xuICAgICAgICAgIGNvbnN0IGEyID0gMS4wIC0gYWxwaGE7XG5cbiAgICAgICAgICBjb25zdCBkaXYgPSAxLjAgLyBhMDtcblxuICAgICAgICAgIHJldHVybiBuZXcgQ29lZmZpY2llbnRzKHtcbiAgICAgICAgICAgIGExOiBhMSAqIGRpdixcbiAgICAgICAgICAgIGEyOiBhMiAqIGRpdixcbiAgICAgICAgICAgIGIwOiBiMCAqIGRpdixcbiAgICAgICAgICAgIGIxOiBiMSAqIGRpdixcbiAgICAgICAgICAgIGIyOiBiMiAqIGRpdlxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICBjYXNlIEZpbHRlclR5cGUuQWxsUGFzczp7XG4gICAgICAgICAgY29uc3Qgb21lZ2FfcyA9IE1hdGguc2luKG9tZWdhKTtcbiAgICAgICAgICBjb25zdCBvbWVnYV9jID0gTWF0aC5jb3Mob21lZ2EpO1xuICAgICAgICAgIGNvbnN0IGFscGhhID0gb21lZ2FfcyAvICgyLjAgKiBRKTtcblxuICAgICAgICAgIGNvbnN0IGIwID0gMS4wIC0gYWxwaGE7XG4gICAgICAgICAgY29uc3QgYjEgPSAtMi4wICogb21lZ2FfYztcbiAgICAgICAgICBjb25zdCBiMiA9IDEuMCArIGFscGhhO1xuICAgICAgICAgIGNvbnN0IGEwID0gMS4wICsgYWxwaGE7XG4gICAgICAgICAgY29uc3QgYTEgPSAtMi4wICogb21lZ2FfYztcbiAgICAgICAgICBjb25zdCBhMiA9IDEuMCAtIGFscGhhO1xuXG4gICAgICAgICAgcmV0dXJuIG5ldyBDb2VmZmljaWVudHMoe1xuICAgICAgICAgICAgYTE6IGExIC8gYTAsXG4gICAgICAgICAgICBhMjogYTIgLyBhMCxcbiAgICAgICAgICAgIGIwOiBiMCAvIGEwLFxuICAgICAgICAgICAgYjE6IGIxIC8gYTAsXG4gICAgICAgICAgICBiMjogYjIgLyBhMFxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICBjYXNlIEZpbHRlclR5cGUuTG93U2hlbGY6e1xuICAgICAgICAgIGNvbnN0IGEgPSBNYXRoLnBvdygxMC4wLCBkYkdhaW4gLyA0MC4wKTtcbiAgICAgICAgICBjb25zdCBvbWVnYV9zID0gTWF0aC5zaW4ob21lZ2EpO1xuICAgICAgICAgIGNvbnN0IG9tZWdhX2MgPSBNYXRoLmNvcyhvbWVnYSk7XG4gICAgICAgICAgY29uc3QgYWxwaGEgPSBvbWVnYV9zIC8gKDIuMCAqIFEpO1xuXG4gICAgICAgICAgY29uc3QgYjAgPVxuICAgICAgICAgIGEgKiAoYSArIDEuMCAtIChhIC0gMS4wKSAqIG9tZWdhX2MgKyAyLjAgKiBhbHBoYSAqIE1hdGguc3FydChhKSk7XG4gICAgICAgICAgY29uc3QgYjEgPSAyLjAgKiBhICogKGEgLSAxLjAgLSAoYSArIDEuMCkgKiBvbWVnYV9jKTtcbiAgICAgICAgICBjb25zdCBiMiA9XG4gICAgICAgICAgYSAqIChhICsgMS4wIC0gKGEgLSAxLjApICogb21lZ2FfYyAtIDIuMCAqIGFscGhhICogTWF0aC5zcXJ0KGEpKTtcbiAgICAgICAgICBjb25zdCBhMCA9IGEgKyAxLjAgKyAoYSAtIDEuMCkgKiBvbWVnYV9jICsgMi4wICogYWxwaGEgKiBNYXRoLnNxcnQoYSk7XG4gICAgICAgICAgY29uc3QgYTEgPSAtMi4wICogKGEgLSAxLjAgKyAoYSArIDEuMCkgKiBvbWVnYV9jKTtcbiAgICAgICAgICBjb25zdCBhMiA9IGEgKyAxLjAgKyAoYSAtIDEuMCkgKiBvbWVnYV9jIC0gMi4wICogYWxwaGEgKiBNYXRoLnNxcnQoYSk7XG5cbiAgICAgICAgICByZXR1cm4gbmV3IENvZWZmaWNpZW50cyh7XG4gICAgICAgICAgICBhMTogYTEgLyBhMCxcbiAgICAgICAgICAgIGEyOiBhMiAvIGEwLFxuICAgICAgICAgICAgYjA6IGIwIC8gYTAsXG4gICAgICAgICAgICBiMTogYjEgLyBhMCxcbiAgICAgICAgICAgIGIyOiBiMiAvIGEwXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIGNhc2UgRmlsdGVyVHlwZS5IaWdoU2hlbGY6e1xuICAgICAgICAgIGNvbnN0IGEgPSBNYXRoLnBvdygxMC4wLCBkYkdhaW4gLyA0MC4wKTtcbiAgICAgICAgICBjb25zdCBvbWVnYV9zID0gTWF0aC5zaW4ob21lZ2EpO1xuICAgICAgICAgIGNvbnN0IG9tZWdhX2MgPSBNYXRoLmNvcyhvbWVnYSk7XG4gICAgICAgICAgY29uc3QgYWxwaGEgPSBvbWVnYV9zIC8gKDIuMCAqIFEpO1xuXG4gICAgICAgICAgY29uc3QgYjAgPVxuICAgICAgICAgIGEgKiAoYSArIDEuMCArIChhIC0gMS4wKSAqIG9tZWdhX2MgKyAyLjAgKiBhbHBoYSAqIE1hdGguc3FydChhKSk7XG4gICAgICAgICAgY29uc3QgYjEgPSAtMi4wICogYSAqIChhIC0gMS4wICsgKGEgKyAxLjApICogb21lZ2FfYyk7XG4gICAgICAgICAgY29uc3QgYjIgPVxuICAgICAgICAgIGEgKiAoYSArIDEuMCArIChhIC0gMS4wKSAqIG9tZWdhX2MgLSAyLjAgKiBhbHBoYSAqIE1hdGguc3FydChhKSk7XG4gICAgICAgICAgY29uc3QgYTAgPSBhICsgMS4wIC0gKGEgLSAxLjApICogb21lZ2FfYyArIDIuMCAqIGFscGhhICogTWF0aC5zcXJ0KGEpO1xuICAgICAgICAgIGNvbnN0IGExID0gMi4wICogKGEgLSAxLjAgLSAoYSArIDEuMCkgKiBvbWVnYV9jKTtcbiAgICAgICAgICBjb25zdCBhMiA9IGEgKyAxLjAgLSAoYSAtIDEuMCkgKiBvbWVnYV9jIC0gMi4wICogYWxwaGEgKiBNYXRoLnNxcnQoYSk7XG5cbiAgICAgICAgICByZXR1cm4gbmV3IENvZWZmaWNpZW50cyh7XG4gICAgICAgICAgICBhMTogYTEgLyBhMCxcbiAgICAgICAgICAgIGEyOiBhMiAvIGEwLFxuICAgICAgICAgICAgYjA6IGIwIC8gYTAsXG4gICAgICAgICAgICBiMTogYjEgLyBhMCxcbiAgICAgICAgICAgIGIyOiBiMiAvIGEwXG4gICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgIGNhc2UgRmlsdGVyVHlwZS5QZWFraW5nRVE6e1xuICAgICAgICAgIGNvbnN0IGEgPSBNYXRoLnBvdygxMC4wLCBkYkdhaW4gLyA0MC4wKTtcbiAgICAgICAgICBjb25zdCBvbWVnYV9zID0gTWF0aC5zaW4ob21lZ2EpO1xuICAgICAgICAgIGNvbnN0IG9tZWdhX2MgPSBNYXRoLmNvcyhvbWVnYSk7XG4gICAgICAgICAgY29uc3QgYWxwaGEgPSBvbWVnYV9zIC8gKDIuMCAqIFEpO1xuXG4gICAgICAgICAgY29uc3QgYjAgPSAxLjAgKyBhbHBoYSAqIGE7XG4gICAgICAgICAgY29uc3QgYjEgPSAtMi4wICogb21lZ2FfYztcbiAgICAgICAgICBjb25zdCBiMiA9IDEuMCAtIGFscGhhICogYTtcbiAgICAgICAgICBjb25zdCBhMCA9IDEuMCArIGFscGhhIC8gYTtcbiAgICAgICAgICBjb25zdCBhMSA9IC0yLjAgKiBvbWVnYV9jO1xuICAgICAgICAgIGNvbnN0IGEyID0gMS4wIC0gYWxwaGEgLyBhO1xuXG4gICAgICAgICAgcmV0dXJuIG5ldyBDb2VmZmljaWVudHMoe1xuICAgICAgICAgICAgYTE6IGExIC8gYTAsXG4gICAgICAgICAgICBhMjogYTIgLyBhMCxcbiAgICAgICAgICAgIGIwOiBiMCAvIGEwLFxuICAgICAgICAgICAgYjE6IGIxIC8gYTAsXG4gICAgICAgICAgICBiMjogYjIgLyBhMFxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICBkZWZhdWx0OlxuICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKGBJbnZhbGlkIGZpbHRlciB0eXBlIFwiJHtmaWx0ZXJ9XCJgKTtcbiAgICB9XG4gIH1cbn0iLCAiaW1wb3J0IHsgQmlxdWFkRmlsdGVycywgQ29lZmZpY2llbnRzIH0gZnJvbSAnLi9Db2VmZmljaWVudHMnO1xuXG5leHBvcnQgaW50ZXJmYWNlIEJpcXVhZFNldEZpbHRlclByb3BzIHtcbiAgZjA6IG51bWJlcjtcbiAgZnM6IG51bWJlcjtcbiAgUTogbnVtYmVyO1xuICBnYWluPzogbnVtYmVyO1xufVxuXG5leHBvcnQgY2xhc3MgQmlxdWFkRmlsdGVyIHtcbiAgcHVibGljIHgxID0gMC4wO1xuICBwdWJsaWMgeDIgPSAwLjA7XG4gIHB1YmxpYyB5MSA9IDAuMDtcbiAgcHVibGljIHkyID0gMC4wO1xuICBwdWJsaWMgczEgPSAwLjA7XG4gIHB1YmxpYyBzMiA9IDAuMDtcblxuICBwdWJsaWMgY29uc3RydWN0b3IocHVibGljIGNvZWZmaWNpZW50czogQ29lZmZpY2llbnRzKSB7fVxuXG4gIHB1YmxpYyBzZXRGaWx0ZXIoZmlsdGVyOiBCaXF1YWRGaWx0ZXJzLCBvcHRpb25zOiBCaXF1YWRTZXRGaWx0ZXJQcm9wcykge1xuICAgIGNvbnN0IGNvZWZmaWNpZW50cyA9IENvZWZmaWNpZW50cy5mcm9tKFxuICAgICAgZmlsdGVyLFxuICAgICAgb3B0aW9ucy5mcyxcbiAgICAgIG9wdGlvbnMuZjAsXG4gICAgICBvcHRpb25zLlEsXG4gICAgICBvcHRpb25zLmdhaW5cbiAgICApO1xuXG4gICAgdGhpcy51cGRhdGUoY29lZmZpY2llbnRzKTtcbiAgfVxuXG4gIHB1YmxpYyB1cGRhdGUoY29lZmZpY2llbnRzOiBDb2VmZmljaWVudHMpIHtcbiAgICB0aGlzLmNvZWZmaWNpZW50cyA9IGNvZWZmaWNpZW50cztcbiAgfVxuXG4gIHB1YmxpYyByZXBsYWNlKGNvZWZmaWNpZW50czogQ29lZmZpY2llbnRzKSB7XG4gICAgdGhpcy5jb2VmZmljaWVudHMgPSBjb2VmZmljaWVudHM7XG4gIH1cblxuICBwdWJsaWMgcmVzZXQoKSB7XG4gICAgdGhpcy54MSA9IDAuMDtcbiAgICB0aGlzLngyID0gMC4wO1xuICAgIHRoaXMueTEgPSAwLjA7XG4gICAgdGhpcy55MiA9IDAuMDtcbiAgICB0aGlzLnMxID0gMC4wO1xuICAgIHRoaXMuczIgPSAwLjA7XG4gIH1cblxuICBwdWJsaWMgcnVuKGlucHV0OiBudW1iZXIpIHtcbiAgICBjb25zdCB7IGExLCBhMiwgYjAsIGIxLCBiMiB9ID0gdGhpcy5jb2VmZmljaWVudHM7XG5cbiAgICBjb25zdCBvdXQgPVxuICAgIGIwICogaW5wdXQgKyBiMSAqIHRoaXMueDEgKyBiMiAqIHRoaXMueDIgLSBhMSAqIHRoaXMueTEgLSBhMiAqIHRoaXMueTI7XG5cbiAgICB0aGlzLngyID0gdGhpcy54MTtcbiAgICB0aGlzLngxID0gaW5wdXQ7XG4gICAgdGhpcy55MiA9IHRoaXMueTE7XG4gICAgdGhpcy55MSA9IG91dDtcblxuICAgIHJldHVybiBvdXQ7XG4gIH1cblxuICBwdWJsaWMgcnVuVHJhbnNwb3NlZChpbnB1dDogbnVtYmVyKSB7XG4gICAgY29uc3QgeyBhMSwgYTIsIGIwLCBiMSwgYjIgfSA9IHRoaXMuY29lZmZpY2llbnRzO1xuXG4gICAgY29uc3Qgb3V0ID0gdGhpcy5zMSArIGIwICogaW5wdXQ7XG5cbiAgICB0aGlzLnMxID0gdGhpcy5zMiArIGIxICogaW5wdXQgLSBhMSAqIG91dDtcbiAgICB0aGlzLnMyID0gYjIgKiBpbnB1dCAtIGEyICogb3V0O1xuXG4gICAgcmV0dXJuIG91dDtcbiAgfVxufSIsICJleHBvcnQgY2xhc3MgRnJlcXVlbmN5IHtcbiAgcHVibGljIGNvbnN0cnVjdG9yKHByaXZhdGUgX192YWw6IG51bWJlcikge1xuICAgIGlmICh0eXBlb2YgX192YWwgIT09ICdudW1iZXInIHx8IGlzTmFOKF9fdmFsKSB8fCBfX3ZhbCA9PT0gSW5maW5pdHkpXG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcignRnJlcXVlbmN5IHZhbHVlIG11c3QgYmUgYSBudW1iZXInKTtcbiAgICBpZiAodGhpcy5fX3ZhbCA8IDApXG4gICAgdGhyb3cgbmV3IEVycm9yKGBGcmVxdWVuY3kgdmFsdWUgY2Fubm90IGJlIG5lZ2F0aXZlICgke19fdmFsfSlgKTtcbiAgfVxuXG4gIHB1YmxpYyBraHooKSB7XG4gICAgcmV0dXJuIHRoaXMuX192YWwgKiAxMDAwLjA7XG4gIH1cblxuICBwdWJsaWMgbWh6KCkge1xuICAgIHJldHVybiB0aGlzLl9fdmFsICogMV8wMDBfMDAwLjA7XG4gIH1cblxuICBwdWJsaWMgaHooKSB7XG4gICAgcmV0dXJuIHRoaXMuX192YWw7XG4gIH1cblxuICBwdWJsaWMgZHQoKSB7XG4gICAgcmV0dXJuIDEuMCAvIHRoaXMuX192YWw7XG4gIH1cblxuICBwdWJsaWMgdmFsdWVPZigpIHtcbiAgICByZXR1cm4gdGhpcy5fX3ZhbDtcbiAgfVxuXG4gIHB1YmxpYyB0b1N0cmluZygpIHtcbiAgICByZXR1cm4gYCR7dGhpcy5fX3ZhbH1IemA7XG4gIH1cblxuICBwdWJsaWMgdG9KU09OKCkge1xuICAgIHJldHVybiB0aGlzLnRvU3RyaW5nKCk7XG4gIH1cbn0iLCAiaW1wb3J0IHsgVHJhbnNmb3JtLCBUcmFuc2Zvcm1PcHRpb25zIH0gZnJvbSAnc3RyZWFtJztcblxuZXhwb3J0IHR5cGUgUENNVHlwZSA9IGBzJHsxNiB8IDMyfSR7J2wnI