lavalink-client
Version:
Easy, flexible and feature-rich lavalink@v4 Client. Both for Beginners and Proficients.
696 lines (695 loc) • 33.5 kB
JavaScript
import { audioOutputsData } from "./Constants.js";
/**
* The FilterManager for each player
*/
export class FilterManager {
/** The Equalizer bands currently applied to the Lavalink Server */
equalizerBands = [];
/** Private Util for the instaFix Filters option */
filterUpdatedState = false;
/** All "Active" / "disabled" Player Filters */
filters = {
volume: false,
vaporwave: false,
custom: false,
nightcore: false,
rotation: false,
karaoke: false,
tremolo: false,
vibrato: false,
lowPass: false,
lavalinkFilterPlugin: {
echo: false,
reverb: false,
},
lavalinkLavaDspxPlugin: {
lowPass: false,
highPass: false,
normalization: false,
echo: false,
},
audioOutput: "stereo",
};
/** The Filter Data sent to Lavalink, only if the filter is enabled (ofc.) */
data = {
lowPass: {
smoothing: 0
},
karaoke: {
level: 0,
monoLevel: 0,
filterBand: 0,
filterWidth: 0
},
timescale: {
speed: 1, // 0 = x
pitch: 1, // 0 = x
rate: 1 // 0 = x
},
rotation: {
rotationHz: 0
},
tremolo: {
frequency: 0, // 0 < x
depth: 0 // 0 < x = 1
},
vibrato: {
frequency: 0, // 0 < x <= 14
depth: 0 // 0 < x <= 1
},
pluginFilters: {
"lavalink-filter-plugin": {
echo: {
delay: 0, // in seconds
decay: 0 // 0 < 1
},
reverb: {
delays: [], // [0.037, 0.042, 0.048, 0.053]
gains: [] // [0.84, 0.83, 0.82, 0.81]
}
},
"high-pass": { // Cuts off frequencies lower than the specified {cutoffFrequency}.
// "cutoffFrequency": 1475, // Integer, higher than zero, in Hz.
// "boostFactor": 1.0 // Float, higher than 0.0. This alters volume output. A value of 1.0 means no volume change.
},
"low-pass": { // Cuts off frequencies higher than the specified {cutoffFrequency}.
// "cutoffFrequency": 284, // Integer, higher than zero, in Hz.
// "boostFactor": 1.24389 // Float, higher than 0.0. This alters volume output. A value of 1.0 means no volume change.
},
"normalization": { // Attenuates peaking where peaks are defined as having a higher value than {maxAmplitude}.
// "maxAmplitude": 0.6327, // Float, within the range of 0.0 - 1.0. A value of 0.0 mutes the output.
// "adaptive": true // false
},
"echo": { // Self-explanatory; provides an echo effect.
// "echoLength": 0.5649, // Float, higher than 0.0, in seconds (1.0 = 1 second).
// "decay": 0.4649 // Float, within the range of 0.0 - 1.0. A value of 1.0 means no decay, and a value of 0.0 means
},
},
channelMix: audioOutputsData.stereo,
/*distortion: {
sinOffset: 0,
sinScale: 1,
cosOffset: 0,
cosScale: 1,
tanOffset: 0,
tanScale: 1,
offset: 0,
scale: 1
}*/
};
/** The Player assigned to this Filter Manager */
player;
/** The Constructor for the FilterManager */
constructor(player) {
/** Assign the player to the filter manager */
this.player = player;
}
/**
* Apply Player filters for lavalink filter sending data, if the filter is enabled / not
*/
async applyPlayerFilters() {
const sendData = { ...this.data };
this.checkFiltersState();
if (!this.filters.volume)
delete sendData.volume;
if (!this.filters.tremolo)
delete sendData.tremolo;
if (!this.filters.vibrato)
delete sendData.vibrato;
if (!this.filters.lavalinkFilterPlugin.echo)
delete sendData.pluginFilters?.["lavalink-filter-plugin"]?.echo;
if (!this.filters.lavalinkFilterPlugin.reverb)
delete sendData.pluginFilters?.["lavalink-filter-plugin"]?.reverb;
if (!this.filters.lavalinkLavaDspxPlugin.echo)
delete sendData.pluginFilters?.echo;
if (!this.filters.lavalinkLavaDspxPlugin.normalization)
delete sendData.pluginFilters?.normalization;
if (!this.filters.lavalinkLavaDspxPlugin.highPass)
delete sendData.pluginFilters?.["high-pass"];
if (!this.filters.lavalinkLavaDspxPlugin.lowPass)
delete sendData.pluginFilters?.["low-pass"];
if (sendData.pluginFilters?.["lavalink-filter-plugin"] && Object.values(sendData.pluginFilters?.["lavalink-filter-plugin"]).length === 0)
delete sendData.pluginFilters["lavalink-filter-plugin"];
if (sendData.pluginFilters && Object.values(sendData.pluginFilters).length === 0)
delete sendData.pluginFilters;
if (!this.filters.lowPass)
delete sendData.lowPass;
if (!this.filters.karaoke)
delete sendData.karaoke;
if (!this.filters.rotation)
delete sendData.rotation;
if (this.filters.audioOutput === "stereo")
delete sendData.channelMix;
if (Object.values(this.data.timescale).every(v => v === 1))
delete sendData.timescale;
if (!this.player.node.sessionId)
throw new Error("The Lavalink-Node is either not ready or not up to date");
sendData.equalizer = [...this.equalizerBands];
if (sendData.equalizer.length === 0)
delete sendData.equalizer;
for (const key of Object.keys(sendData)) {
// delete disabled filters
if (key === "pluginFilters") {
// for(const key of [...Object.keys(sendData.pluginFilters)]) {
// // if (this.player.node.info && !this.player.node.info?.plugins?.find?.(v => v.name === key)) delete sendData[key];
// }
}
else if (this.player.node.info && !this.player.node.info?.filters?.includes?.(key))
delete sendData[key];
}
const now = performance.now();
if (this.player.options.instaUpdateFiltersFix === true)
this.filterUpdatedState = true;
await this.player.node.updatePlayer({
guildId: this.player.guildId,
playerOptions: {
filters: sendData,
}
});
this.player.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
return;
}
/**
* Checks if the filters are correctly stated (active / not-active)
* @param oldFilterTimescale
* @returns
*/
checkFiltersState(oldFilterTimescale) {
this.filters.rotation = this.data.rotation.rotationHz !== 0;
this.filters.vibrato = this.data.vibrato.frequency !== 0 || this.data.vibrato.depth !== 0;
this.filters.tremolo = this.data.tremolo.frequency !== 0 || this.data.tremolo.depth !== 0;
const lavalinkFilterData = (this.data.pluginFilters?.["lavalink-filter-plugin"] || { echo: { decay: this.data.pluginFilters?.echo?.decay && !this.data.pluginFilters?.echo?.echoLength ? this.data.pluginFilters.echo.decay : 0, delay: this.data.pluginFilters?.echo?.delay || 0 }, reverb: { gains: [], delays: [], ...(this.data.pluginFilters.reverb) } });
this.filters.lavalinkFilterPlugin.echo = lavalinkFilterData.echo.decay !== 0 || lavalinkFilterData.echo.delay !== 0;
this.filters.lavalinkFilterPlugin.reverb = lavalinkFilterData.reverb?.delays?.length !== 0 || lavalinkFilterData.reverb?.gains?.length !== 0;
this.filters.lavalinkLavaDspxPlugin.highPass = Object.values(this.data.pluginFilters["high-pass"] || {}).length > 0;
this.filters.lavalinkLavaDspxPlugin.lowPass = Object.values(this.data.pluginFilters["low-pass"] || {}).length > 0;
this.filters.lavalinkLavaDspxPlugin.normalization = Object.values(this.data.pluginFilters.normalization || {}).length > 0;
this.filters.lavalinkLavaDspxPlugin.echo = Object.values(this.data.pluginFilters.echo || {}).length > 0 && typeof this.data.pluginFilters?.echo?.delay === "undefined";
this.filters.lowPass = this.data.lowPass.smoothing !== 0;
this.filters.karaoke = Object.values(this.data.karaoke).some(v => v !== 0);
if ((this.filters.nightcore || this.filters.vaporwave) && oldFilterTimescale) {
if (oldFilterTimescale.pitch !== this.data.timescale.pitch || oldFilterTimescale.rate !== this.data.timescale.rate || oldFilterTimescale.speed !== this.data.timescale.speed) {
this.filters.custom = Object.values(this.data.timescale).some(v => v !== 1);
this.filters.nightcore = false;
this.filters.vaporwave = false;
}
}
return true;
}
/**
* Reset all Filters
*/
async resetFilters() {
this.filters.lavalinkLavaDspxPlugin.echo = false;
this.filters.lavalinkLavaDspxPlugin.normalization = false;
this.filters.lavalinkLavaDspxPlugin.highPass = false;
this.filters.lavalinkLavaDspxPlugin.lowPass = false;
this.filters.lavalinkFilterPlugin.echo = false;
this.filters.lavalinkFilterPlugin.reverb = false;
this.filters.nightcore = false;
this.filters.lowPass = false;
this.filters.rotation = false;
this.filters.tremolo = false;
this.filters.vibrato = false;
this.filters.karaoke = false;
this.filters.karaoke = false;
this.filters.volume = false;
this.filters.audioOutput = "stereo";
// reset all filter datas
for (const [key, value] of Object.entries({
volume: 1,
lowPass: {
smoothing: 0
},
karaoke: {
level: 0,
monoLevel: 0,
filterBand: 0,
filterWidth: 0
},
timescale: {
speed: 1, // 0 = x
pitch: 1, // 0 = x
rate: 1 // 0 = x
},
pluginFilters: {
"lavalink-filter-plugin": {
echo: {
// delay: 0, // in seconds
// decay: 0 // 0 < 1
},
reverb: {
// delays: [], // [0.037, 0.042, 0.048, 0.053]
// gains: [] // [0.84, 0.83, 0.82, 0.81]
}
},
"high-pass": { // Cuts off frequencies lower than the specified {cutoffFrequency}.
// "cutoffFrequency": 1475, // Integer, higher than zero, in Hz.
// "boostFactor": 1.0 // Float, higher than 0.0. This alters volume output. A value of 1.0 means no volume change.
},
"low-pass": { // Cuts off frequencies higher than the specified {cutoffFrequency}.
// "cutoffFrequency": 284, // Integer, higher than zero, in Hz.
// "boostFactor": 1.24389 // Float, higher than 0.0. This alters volume output. A value of 1.0 means no volume change.
},
"normalization": { // Attenuates peaking where peaks are defined as having a higher value than {maxAmplitude}.
// "maxAmplitude": 0.6327, // Float, within the range of 0.0 - 1.0. A value of 0.0 mutes the output.
// "adaptive": true // false
},
"echo": { // Self-explanatory; provides an echo effect.
// "echoLength": 0.5649, // Float, higher than 0.0, in seconds (1.0 = 1 second).
// "decay": 0.4649 // Float, within the range of 0.0 - 1.0. A value of 1.0 means no decay, and a value of 0.0 means
},
},
rotation: {
rotationHz: 0
},
tremolo: {
frequency: 0, // 0 < x
depth: 0 // 0 < x = 1
},
vibrato: {
frequency: 0, // 0 < x = 14
depth: 0 // 0 < x = 1
},
channelMix: audioOutputsData.stereo,
})) {
this.data[key] = value;
}
await this.applyPlayerFilters();
return this.filters;
}
/**
* Set the Filter Volume
* @param volume
* @returns
*/
async setVolume(volume) {
if (volume < 0 || volume > 5)
throw new SyntaxError("Volume-Filter must be between 0 and 5");
this.data.volume = volume;
await this.applyPlayerFilters();
return this.filters.volume;
}
/**
* Set the AudioOutput Filter
* @param type
* @returns
*/
async setAudioOutput(type) {
if (this.player.node.info && !this.player.node.info?.filters?.includes("channelMix"))
throw new Error("Node#Info#filters does not include the 'channelMix' Filter (Node has it not enable)");
if (!type || !audioOutputsData[type])
throw "Invalid audio type added, must be 'mono' / 'stereo' / 'left' / 'right'";
this.data.channelMix = audioOutputsData[type];
this.filters.audioOutput = type;
await this.applyPlayerFilters();
return this.filters.audioOutput;
}
/**
* Set custom filter.timescale#speed . This method disabled both: nightcore & vaporwave. use 1 to reset it to normal
* @param speed
* @returns
*/
async setSpeed(speed = 1) {
if (this.player.node.info && !this.player.node.info?.filters?.includes("timescale"))
throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
// reset nightcore / vaporwave filter if enabled
if (this.filters.nightcore || this.filters.vaporwave) {
this.data.timescale.pitch = 1;
this.data.timescale.speed = 1;
this.data.timescale.rate = 1;
this.filters.nightcore = false;
this.filters.vaporwave = false;
}
this.data.timescale.speed = speed;
// check if custom filter is active / not
this.isCustomFilterActive();
await this.applyPlayerFilters();
return this.filters.custom;
}
/**
* Set custom filter.timescale#pitch . This method disabled both: nightcore & vaporwave. use 1 to reset it to normal
* @param speed
* @returns
*/
async setPitch(pitch = 1) {
if (this.player.node.info && !this.player.node.info?.filters?.includes("timescale"))
throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
// reset nightcore / vaporwave filter if enabled
if (this.filters.nightcore || this.filters.vaporwave) {
this.data.timescale.pitch = 1;
this.data.timescale.speed = 1;
this.data.timescale.rate = 1;
this.filters.nightcore = false;
this.filters.vaporwave = false;
}
this.data.timescale.pitch = pitch;
// check if custom filter is active / not
this.isCustomFilterActive();
await this.applyPlayerFilters();
return this.filters.custom;
}
/**
* Set custom filter.timescale#rate . This method disabled both: nightcore & vaporwave. use 1 to reset it to normal
* @param speed
* @returns
*/
async setRate(rate = 1) {
if (this.player.node.info && !this.player.node.info?.filters?.includes("timescale"))
throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
// reset nightcore / vaporwave filter if enabled
if (this.filters.nightcore || this.filters.vaporwave) {
this.data.timescale.pitch = 1;
this.data.timescale.speed = 1;
this.data.timescale.rate = 1;
this.filters.nightcore = false;
this.filters.vaporwave = false;
}
this.data.timescale.rate = rate;
// check if custom filter is active / not
this.isCustomFilterActive();
await this.applyPlayerFilters();
return this.filters.custom;
}
/**
* Enables / Disables the rotation effect, (Optional: provide your Own Data)
* @param rotationHz
* @returns
*/
async toggleRotation(rotationHz = 0.2) {
if (this.player.node.info && !this.player.node.info?.filters?.includes("rotation"))
throw new Error("Node#Info#filters does not include the 'rotation' Filter (Node has it not enable)");
this.data.rotation.rotationHz = this.filters.rotation ? 0 : rotationHz;
this.filters.rotation = !this.filters.rotation;
await this.applyPlayerFilters();
return this.filters.rotation;
}
/**
* Enables / Disables the Vibrato effect, (Optional: provide your Own Data)
* @param frequency
* @param depth
* @returns
*/
async toggleVibrato(frequency = 10, depth = 1) {
if (this.player.node.info && !this.player.node.info?.filters?.includes("vibrato"))
throw new Error("Node#Info#filters does not include the 'vibrato' Filter (Node has it not enable)");
this.data.vibrato.frequency = this.filters.vibrato ? 0 : frequency;
this.data.vibrato.depth = this.filters.vibrato ? 0 : depth;
this.filters.vibrato = !this.filters.vibrato;
await this.applyPlayerFilters();
return this.filters.vibrato;
}
/**
* Enables / Disables the Tremolo effect, (Optional: provide your Own Data)
* @param frequency
* @param depth
* @returns
*/
async toggleTremolo(frequency = 4, depth = 0.8) {
if (this.player.node.info && !this.player.node.info?.filters?.includes("tremolo"))
throw new Error("Node#Info#filters does not include the 'tremolo' Filter (Node has it not enable)");
this.data.tremolo.frequency = this.filters.tremolo ? 0 : frequency;
this.data.tremolo.depth = this.filters.tremolo ? 0 : depth;
this.filters.tremolo = !this.filters.tremolo;
await this.applyPlayerFilters();
return this.filters.tremolo;
}
/**
* Enables / Disables the LowPass effect, (Optional: provide your Own Data)
* @param smoothing
* @returns
*/
async toggleLowPass(smoothing = 20) {
if (this.player.node.info && !this.player.node.info?.filters?.includes("lowPass"))
throw new Error("Node#Info#filters does not include the 'lowPass' Filter (Node has it not enable)");
this.data.lowPass.smoothing = this.filters.lowPass ? 0 : smoothing;
this.filters.lowPass = !this.filters.lowPass;
await this.applyPlayerFilters();
return this.filters.lowPass;
}
lavalinkLavaDspxPlugin = {
/**
* Enables / Disables the LowPass effect, (Optional: provide your Own Data)
* @param boostFactor
* @param cutoffFrequency
* @returns
*/
toggleLowPass: async (boostFactor = 1.0, cutoffFrequency = 80) => {
if (this.player.node.info && !this.player.node.info?.plugins?.find(v => v.name === "lavadspx-plugin"))
throw new Error("Node#Info#plugins does not include the lavadspx plugin");
if (this.player.node.info && !this.player.node.info?.filters?.includes("low-pass"))
throw new Error("Node#Info#filters does not include the 'low-pass' Filter (Node has it not enable)");
if (!this.data)
this.data = {};
if (!this.data.pluginFilters)
this.data.pluginFilters = {};
if (!this.data.pluginFilters["low-pass"])
this.data.pluginFilters["low-pass"] = {};
if (this.filters.lavalinkLavaDspxPlugin.lowPass) {
delete this.data.pluginFilters["low-pass"];
}
else {
this.data.pluginFilters["low-pass"] = {
boostFactor: boostFactor,
cutoffFrequency: cutoffFrequency
};
}
this.filters.lavalinkLavaDspxPlugin.lowPass = !this.filters.lavalinkLavaDspxPlugin.lowPass;
await this.applyPlayerFilters();
return this.filters.lavalinkLavaDspxPlugin.lowPass;
},
/**
* Enables / Disables the HighPass effect, (Optional: provide your Own Data)
* @param boostFactor
* @param cutoffFrequency
* @returns
*/
toggleHighPass: async (boostFactor = 1.0, cutoffFrequency = 80) => {
if (this.player.node.info && !this.player.node.info?.plugins?.find(v => v.name === "lavadspx-plugin"))
throw new Error("Node#Info#plugins does not include the lavadspx plugin");
if (this.player.node.info && !this.player.node.info?.filters?.includes("high-pass"))
throw new Error("Node#Info#filters does not include the 'high-pass' Filter (Node has it not enable)");
if (!this.data)
this.data = {};
if (!this.data.pluginFilters)
this.data.pluginFilters = {};
if (!this.data.pluginFilters["high-pass"])
this.data.pluginFilters["high-pass"] = {};
if (this.filters.lavalinkLavaDspxPlugin.highPass) {
delete this.data.pluginFilters["high-pass"];
}
else {
this.data.pluginFilters["high-pass"] = {
boostFactor: boostFactor,
cutoffFrequency: cutoffFrequency
};
}
this.filters.lavalinkLavaDspxPlugin.highPass = !this.filters.lavalinkLavaDspxPlugin.highPass;
await this.applyPlayerFilters();
return this.filters.lavalinkLavaDspxPlugin.highPass;
},
/**
* Enables / Disables the Normalization effect.
* @param {number} [maxAmplitude=0.75] - The maximum amplitude of the audio.
* @param {boolean} [adaptive=true] - Whether to use adaptive normalization or not.
* @returns {Promise<boolean>} - The state of the filter after execution.
*/
toggleNormalization: async (maxAmplitude = 0.75, adaptive = true) => {
if (this.player.node.info && !this.player.node.info?.plugins?.find(v => v.name === "lavadspx-plugin"))
throw new Error("Node#Info#plugins does not include the lavadspx plugin");
if (this.player.node.info && !this.player.node.info?.filters?.includes("normalization"))
throw new Error("Node#Info#filters does not include the 'normalization' Filter (Node has it not enable)");
if (!this.data)
this.data = {};
if (!this.data.pluginFilters)
this.data.pluginFilters = {};
if (!this.data.pluginFilters.normalization)
this.data.pluginFilters.normalization = {};
if (this.filters.lavalinkLavaDspxPlugin.normalization) {
delete this.data.pluginFilters.normalization;
}
else {
this.data.pluginFilters.normalization = {
adaptive: adaptive,
maxAmplitude: maxAmplitude
};
}
this.filters.lavalinkLavaDspxPlugin.normalization = !this.filters.lavalinkLavaDspxPlugin.normalization;
await this.applyPlayerFilters();
return this.filters.lavalinkLavaDspxPlugin.normalization;
},
/**
* Enables / Disables the Echo effect, IMPORTANT! Only works with the correct Lavalink Plugin installed. (Optional: provide your Own Data)
* @param {number} [decay=0.5] - The decay of the echo effect.
* @param {number} [echoLength=0.5] - The length of the echo effect.
* @returns {Promise<boolean>} - The state of the filter after execution.
*/
toggleEcho: async (decay = 0.5, echoLength = 0.5) => {
if (this.player.node.info && !this.player.node.info?.plugins?.find(v => v.name === "lavadspx-plugin"))
throw new Error("Node#Info#plugins does not include the lavadspx plugin");
if (this.player.node.info && !this.player.node.info?.filters?.includes("echo"))
throw new Error("Node#Info#filters does not include the 'echo' Filter (Node has it not enable)");
if (!this.data)
this.data = {};
if (!this.data.pluginFilters)
this.data.pluginFilters = {};
if (!this.data.pluginFilters.echo)
this.data.pluginFilters.echo = {};
if (this.filters.lavalinkLavaDspxPlugin.echo) {
delete this.data.pluginFilters.echo;
}
else {
this.data.pluginFilters.echo = {
decay: decay,
echoLength: echoLength
};
}
this.filters.lavalinkLavaDspxPlugin.echo = !this.filters.lavalinkLavaDspxPlugin.echo;
await this.applyPlayerFilters();
return this.filters.lavalinkLavaDspxPlugin.echo;
}
};
lavalinkFilterPlugin = {
/**
* Enables / Disables the Echo effect, IMPORTANT! Only works with the correct Lavalink Plugin installed. (Optional: provide your Own Data)
* @param delay
* @param decay
* @returns
*/
toggleEcho: async (delay = 4, decay = 0.8) => {
if (this.player.node.info && !this.player.node.info?.plugins?.find(v => v.name === "lavalink-filter-plugin"))
throw new Error("Node#Info#plugins does not include the lavalink-filter-plugin plugin");
if (this.player.node.info && !this.player.node.info?.filters?.includes("echo"))
throw new Error("Node#Info#filters does not include the 'echo' Filter (Node has it not enable aka not installed!)");
if (!this.data)
this.data = {};
if (!this.data.pluginFilters)
this.data.pluginFilters = {};
if (!this.data.pluginFilters["lavalink-filter-plugin"])
this.data.pluginFilters["lavalink-filter-plugin"] = { echo: { decay: 0, delay: 0 }, reverb: { delays: [], gains: [] } };
if (!this.data.pluginFilters["lavalink-filter-plugin"].echo)
this.data.pluginFilters["lavalink-filter-plugin"].echo = { decay: 0, delay: 0 };
this.data.pluginFilters["lavalink-filter-plugin"].echo.delay = this.filters.lavalinkFilterPlugin.echo ? 0 : delay;
this.data.pluginFilters["lavalink-filter-plugin"].echo.decay = this.filters.lavalinkFilterPlugin.echo ? 0 : decay;
this.filters.lavalinkFilterPlugin.echo = !this.filters.lavalinkFilterPlugin.echo;
await this.applyPlayerFilters();
return this.filters.lavalinkFilterPlugin.echo;
},
/**
* Enables / Disables the Echo effect, IMPORTANT! Only works with the correct Lavalink Plugin installed. (Optional: provide your Own Data)
* @param delays
* @param gains
* @returns
*/
toggleReverb: async (delays = [0.037, 0.042, 0.048, 0.053], gains = [0.84, 0.83, 0.82, 0.81]) => {
if (this.player.node.info && !this.player.node.info?.plugins?.find(v => v.name === "lavalink-filter-plugin"))
throw new Error("Node#Info#plugins does not include the lavalink-filter-plugin plugin");
if (this.player.node.info && !this.player.node.info?.filters?.includes("reverb"))
throw new Error("Node#Info#filters does not include the 'reverb' Filter (Node has it not enable aka not installed!)");
if (!this.data)
this.data = {};
if (!this.data.pluginFilters)
this.data.pluginFilters = {};
if (!this.data.pluginFilters["lavalink-filter-plugin"])
this.data.pluginFilters["lavalink-filter-plugin"] = { echo: { decay: 0, delay: 0 }, reverb: { delays: [], gains: [] } };
if (!this.data.pluginFilters["lavalink-filter-plugin"].reverb)
this.data.pluginFilters["lavalink-filter-plugin"].reverb = { delays: [], gains: [] };
this.data.pluginFilters["lavalink-filter-plugin"].reverb.delays = this.filters.lavalinkFilterPlugin.reverb ? [] : delays;
this.data.pluginFilters["lavalink-filter-plugin"].reverb.gains = this.filters.lavalinkFilterPlugin.reverb ? [] : gains;
this.filters.lavalinkFilterPlugin.reverb = !this.filters.lavalinkFilterPlugin.reverb;
await this.applyPlayerFilters();
return this.filters.lavalinkFilterPlugin.reverb;
}
};
/**
* Enables / Disables a Nightcore-like filter Effect. Disables/Overrides both: custom and Vaporwave Filter
* @param speed
* @param pitch
* @param rate
* @returns
*/
async toggleNightcore(speed = 1.289999523162842, pitch = 1.289999523162842, rate = 0.9365999523162842) {
if (this.player.node.info && !this.player.node.info?.filters?.includes("timescale"))
throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
this.data.timescale.speed = this.filters.nightcore ? 1 : speed;
this.data.timescale.pitch = this.filters.nightcore ? 1 : pitch;
this.data.timescale.rate = this.filters.nightcore ? 1 : rate;
this.filters.nightcore = !this.filters.nightcore;
this.filters.vaporwave = false;
this.filters.custom = false;
await this.applyPlayerFilters();
return this.filters.nightcore;
}
/**
* Enables / Disables a Vaporwave-like filter Effect. Disables/Overrides both: custom and nightcore Filter
* @param speed
* @param pitch
* @param rate
* @returns
*/
async toggleVaporwave(speed = 0.8500000238418579, pitch = 0.800000011920929, rate = 1) {
if (this.player.node.info && !this.player.node.info?.filters?.includes("timescale"))
throw new Error("Node#Info#filters does not include the 'timescale' Filter (Node has it not enable)");
this.data.timescale.speed = this.filters.vaporwave ? 1 : speed;
this.data.timescale.pitch = this.filters.vaporwave ? 1 : pitch;
this.data.timescale.rate = this.filters.vaporwave ? 1 : rate;
this.filters.vaporwave = !this.filters.vaporwave;
this.filters.nightcore = false;
this.filters.custom = false;
await this.applyPlayerFilters();
return this.filters.vaporwave;
}
/**
* Enable / Disables a Karaoke like Filter Effect
* @param level
* @param monoLevel
* @param filterBand
* @param filterWidth
* @returns
*/
async toggleKaraoke(level = 1, monoLevel = 1, filterBand = 220, filterWidth = 100) {
if (this.player.node.info && !this.player.node.info?.filters?.includes("karaoke"))
throw new Error("Node#Info#filters does not include the 'karaoke' Filter (Node has it not enable)");
this.data.karaoke.level = this.filters.karaoke ? 0 : level;
this.data.karaoke.monoLevel = this.filters.karaoke ? 0 : monoLevel;
this.data.karaoke.filterBand = this.filters.karaoke ? 0 : filterBand;
this.data.karaoke.filterWidth = this.filters.karaoke ? 0 : filterWidth;
this.filters.karaoke = !this.filters.karaoke;
await this.applyPlayerFilters();
return this.filters.karaoke;
}
/** Function to find out if currently there is a custom timescamle etc. filter applied */
isCustomFilterActive() {
this.filters.custom = !this.filters.nightcore && !this.filters.vaporwave && Object.values(this.data.timescale).some(d => d !== 1);
return this.filters.custom;
}
/**
* Sets the players equalizer band on-top of the existing ones.
* @param bands
*/
async setEQ(bands) {
if (!Array.isArray(bands))
bands = [bands];
if (!bands.length || !bands.every((band) => JSON.stringify(Object.keys(band).sort()) === '["band","gain"]'))
throw new TypeError("Bands must be a non-empty object array containing 'band' and 'gain' properties.");
for (const { band, gain } of bands)
this.equalizerBands[band] = { band, gain };
if (!this.player.node.sessionId)
throw new Error("The Lavalink-Node is either not ready or not up to date");
const now = performance.now();
if (this.player.options.instaUpdateFiltersFix === true)
this.filterUpdatedState = true;
await this.player.node.updatePlayer({
guildId: this.player.guildId,
playerOptions: {
filters: { equalizer: this.equalizerBands }
}
});
this.player.ping.lavalink = Math.round((performance.now() - now) / 10) / 100;
return this;
}
/** Clears the equalizer bands. */
async clearEQ() {
return this.setEQ(new Array(15).fill(0.0).map((gain, band) => ({ band, gain })));
}
}