magmastream
Version:
A user-friendly Lavalink client designed for NodeJS.
637 lines (636 loc) • 28.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Filters = void 0;
const filtersEqualizers_1 = require("../utils/filtersEqualizers");
const Enums_1 = require("./Enums");
const MagmastreamError_1 = require("./MagmastreamError");
class Filters {
distortion;
equalizer;
karaoke;
rotation;
timescale;
vibrato;
reverb;
volume;
bassBoostlevel;
filtersStatus;
manager;
player;
constructor(player, manager) {
this.distortion = null;
this.equalizer = [];
this.karaoke = null;
this.rotation = null;
this.timescale = null;
this.vibrato = null;
this.reverb = null;
this.volume = 1.0;
this.bassBoostlevel = 0;
// Initialize filter status
this.filtersStatus = Object.values(Enums_1.AvailableFilters).reduce((acc, filter) => {
acc[filter] = false;
return acc;
}, {});
this.manager = manager;
this.player = player;
}
/**
* Updates the player's audio filters.
*
* This method sends a request to the player's node to update the filter settings
* based on the current properties of the `Filters` instance. The filters include
* distortion, equalizer, karaoke, rotation, timescale, vibrato, and volume. Once
* the request is sent, it ensures that the player's audio output reflects the
* changes in filter settings.
*
* @returns {Promise<this>} - Returns a promise that resolves to the current instance
* of the Filters class for method chaining.
*/
async updateFilters() {
const { distortion, equalizer, karaoke, rotation, timescale, vibrato, volume } = this;
try {
await this.player.node.rest.updatePlayer({
data: {
filters: {
distortion,
equalizer,
karaoke,
rotation,
timescale,
vibrato,
volume,
},
},
guildId: this.player.guildId,
});
}
catch (err) {
const error = err instanceof MagmastreamError_1.MagmaStreamError
? err
: new MagmastreamError_1.MagmaStreamError({
code: Enums_1.MagmaStreamErrorCode.FILTER_APPLY_FAILED,
message: `Failed to apply filters to player "${this.player.guildId}".`,
cause: err instanceof Error ? err : undefined,
context: { nodeId: this.player.node.options.identifier },
});
console.log(error);
}
return this;
}
/**
* Applies a specific filter to the player.
*
* This method allows you to set the value of a specific filter property.
* The filter property must be a valid key of the Filters object.
*
* @param {{ property: T; value: Filters[T] }} filter - An object containing the filter property and value.
* @param {boolean} [updateFilters=true] - Whether to update the filters after applying the filter.
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async applyFilter(filter, updateFilters = true) {
this[filter.property] = filter.value;
if (updateFilters) {
await this.updateFilters();
}
return this;
}
emitPlayersTasteUpdate(oldState) {
this.manager.emit(Enums_1.ManagerEventTypes.PlayerStateUpdate, oldState, this, {
changeType: Enums_1.PlayerStateEventTypes.FilterChange,
details: { action: "change" },
});
}
/**
* Sets the status of a specific filter.
*
* This method updates the filter status to either true or false, indicating whether
* the filter is applied or not. This helps track which filters are active.
*
* @param {AvailableFilters} filter - The filter to update.
* @param {boolean} status - The status to set (true for active, false for inactive).
* @returns {this} - Returns the current instance of the Filters class for method chaining.
*/
setFilterStatus(filter, status) {
this.filtersStatus[filter] = status;
return this;
}
/**
* Retrieves the status of a specific filter.
*
* This method returns whether a specific filter is currently applied or not.
*
* @param {AvailableFilters} filter - The filter to check.
* @returns {boolean} - Returns true if the filter is active, false otherwise.
*/
getFilterStatus(filter) {
return this.filtersStatus[filter];
}
/**
* Clears all filters applied to the audio.
*
* This method resets all filter settings to their default values and removes any
* active filters from the player.
*
* @returns {this} - Returns the current instance of the Filters class for method chaining.
*/
async clearFilters() {
const oldPlayer = { ...this };
this.filtersStatus = Object.values(Enums_1.AvailableFilters).reduce((acc, filter) => {
acc[filter] = false;
return acc;
}, {});
this.player.filters = new Filters(this.player, this.manager);
await this.setEqualizer([]);
await this.setDistortion(null);
await this.setKaraoke(null);
await this.setRotation(null);
await this.setTimescale(null);
await this.setVibrato(null);
await this.updateFilters();
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Sets the own equalizer bands on the audio.
*
* This method adjusts the equalization curve of the player's audio output,
* allowing you to control the frequency response.
*
* @param {Band[]} [bands] - The equalizer bands to apply (band, gain).
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async setEqualizer(bands) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "equalizer", value: bands });
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Sets the own karaoke options to the audio.
*
* This method adjusts the audio so that it sounds like a karaoke song, with the
* original vocals removed. Note that not all songs can be successfully made into
* karaoke tracks, and some tracks may not sound as good.
*
* @param {KaraokeOptions} [karaoke] - The karaoke settings to apply (level, monoLevel, filterBand, filterWidth).
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async setKaraoke(karaoke) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "karaoke", value: karaoke ?? null });
this.setFilterStatus(Enums_1.AvailableFilters.SetKaraoke, !!karaoke);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Sets the own timescale options to the audio.
*
* This method adjusts the speed and pitch of the audio, allowing you to control the playback speed.
*
* @param {TimescaleOptions} [timescale] - The timescale settings to apply (speed and pitch).
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async setTimescale(timescale) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "timescale", value: timescale ?? null });
this.setFilterStatus(Enums_1.AvailableFilters.SetTimescale, !!timescale);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Sets the own vibrato options to the audio.
*
* This method applies a vibrato effect to the audio, which adds a wavering,
* pulsing quality to the sound. The effect is created by rapidly varying the
* pitch of the audio.
*
* @param {VibratoOptions} [vibrato] - The vibrato settings to apply (frequency, depth).
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async setVibrato(vibrato) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "vibrato", value: vibrato ?? null });
this.setFilterStatus(Enums_1.AvailableFilters.Vibrato, !!vibrato);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Sets the own rotation options effect to the audio.
*
* This method applies a rotation effect to the audio, which simulates the sound
* moving around the listener's head. This effect can create a dynamic and immersive
* audio experience by altering the directionality of the sound.
*
* @param {RotationOptions} [rotation] - The rotation settings to apply (rotationHz).
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async setRotation(rotation) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "rotation", value: rotation ?? null });
this.setFilterStatus(Enums_1.AvailableFilters.SetRotation, !!rotation);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Sets the own distortion options effect to the audio.
*
* This method applies a distortion effect to the audio, which adds a rougher,
* more intense quality to the sound. The effect is created by altering the
* audio signal to create a more jagged, irregular waveform.
*
* @param {DistortionOptions} [distortion] - The distortion settings to apply (sinOffset, sinScale, cosOffset, cosScale, tanOffset, tanScale, offset, scale).
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async setDistortion(distortion) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "distortion", value: distortion ?? null });
this.setFilterStatus(Enums_1.AvailableFilters.SetDistortion, !!distortion);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Sets the bass boost level on the audio.
*
* This method scales the gain of a predefined equalizer curve to the specified level.
* The curve is designed to emphasize or reduce low frequencies, creating a bass-heavy
* or bass-reduced effect.
*
* @param {number} level - The level of bass boost to apply. The value ranges from -3 to 3,
* where negative values reduce bass, 0 disables the effect,
* and positive values increase bass.
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*
* @example
* // Apply different levels of bass boost or reduction:
* await player.bassBoost(3); // Maximum Bass Boost
* await player.bassBoost(2); // Medium Bass Boost
* await player.bassBoost(1); // Mild Bass Boost
* await player.bassBoost(0); // No Effect (Disabled)
* await player.bassBoost(-1); // Mild Bass Reduction
* await player.bassBoost(-2); // Medium Bass Reduction
* await player.bassBoost(-3); // Maximum Bass Removal
*/
async bassBoost(stage) {
const oldPlayer = { ...this };
// Ensure stage is between -3 and 3
stage = Math.max(-3, Math.min(3, stage));
// Map stage (-3 to 3) to range (-1.0 to 1.0)
const level = stage / 3; // Converts -3 to 3 → -1.0 to 1.0
// Generate a dynamic equalizer by scaling bassBoostEqualizer
const equalizer = filtersEqualizers_1.bassBoostEqualizer.map((band) => ({
band: band.band,
gain: band.gain * level,
}));
await this.applyFilter({ property: "equalizer", value: equalizer });
this.setFilterStatus(Enums_1.AvailableFilters.BassBoost, stage !== 0);
this.bassBoostlevel = stage;
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Toggles the chipmunk effect on the audio.
*
* This method applies or removes a chipmunk effect by adjusting the timescale settings.
* When enabled, it increases the speed, pitch, and rate of the audio, resulting in a high-pitched, fast playback
* similar to the sound of a chipmunk.
*
* @param {boolean} status - Whether to enable or disable the chipmunk effect.
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async chipmunk(status) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "timescale", value: status ? { speed: 1.5, pitch: 1.5, rate: 1.5 } : null });
this.setFilterStatus(Enums_1.AvailableFilters.Chipmunk, status);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Toggles the "China" effect on the audio.
*
* This method applies or removes a filter that reduces the pitch of the audio by half,
* without changing the speed or rate. This creates a "hollow" or "echoey" sound.
*
* @param {boolean} status - Whether to enable or disable the "China" effect.
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async china(status) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "timescale", value: status ? { speed: 1.0, pitch: 0.5, rate: 1.0 } : null });
this.setFilterStatus(Enums_1.AvailableFilters.China, status);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Toggles the 8D audio effect on the audio.
*
* This method applies or removes an 8D audio effect by adjusting the rotation settings.
* When enabled, it creates a sensation of the audio moving around the listener's head,
* providing an immersive audio experience.
*
* @param {boolean} status - Whether to enable or disable the 8D effect.
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async eightD(status) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "rotation", value: status ? { rotationHz: 0.2 } : null });
this.setFilterStatus(Enums_1.AvailableFilters.EightD, status);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Toggles the nightcore effect on the audio.
*
* This method applies or removes a nightcore effect by adjusting the timescale settings.
* When enabled, it increases the speed and pitch of the audio, giving it a more
* upbeat and energetic feel.
*
* @param {boolean} status - Whether to enable or disable the nightcore effect.
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async nightcore(status) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "timescale", value: status ? { speed: 1.1, pitch: 1.125, rate: 1.05 } : null });
this.setFilterStatus(Enums_1.AvailableFilters.Nightcore, status);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Toggles the slowmo effect on the audio.
*
* This method applies or removes a slowmo effect by adjusting the timescale settings.
* When enabled, it slows down the audio while keeping the pitch the same, giving it
* a more relaxed and calming feel.
*
* @param {boolean} status - Whether to enable or disable the slowmo effect.
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async slowmo(status) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "timescale", value: status ? { speed: 0.7, pitch: 1.0, rate: 0.8 } : null });
this.setFilterStatus(Enums_1.AvailableFilters.Slowmo, status);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Toggles a soft equalizer effect to the audio.
*
* This method applies or removes a soft equalizer effect by adjusting the equalizer settings.
* When enabled, it reduces the bass and treble frequencies, giving the audio a softer and more
* mellow sound.
*
* @param {boolean} status - Whether to enable or disable the soft equalizer effect.
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async soft(status) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "equalizer", value: status ? filtersEqualizers_1.softEqualizer : [] });
this.setFilterStatus(Enums_1.AvailableFilters.Soft, status);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Toggles the TV equalizer effect on the audio.
*
* This method applies or removes a TV equalizer effect by adjusting the equalizer settings.
* When enabled, it enhances specific frequency bands to mimic the audio characteristics
* typically found in television audio outputs.
*
* @param {boolean} status - Whether to enable or disable the TV equalizer effect.
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async tv(status) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "equalizer", value: status ? filtersEqualizers_1.tvEqualizer : [] });
this.setFilterStatus(Enums_1.AvailableFilters.TV, status);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Toggles the treble/bass equalizer effect on the audio.
*
* This method applies or removes a treble/bass equalizer effect by adjusting the equalizer settings.
* When enabled, it enhances the treble and bass frequencies, giving the audio a more balanced sound.
*
* @param {boolean} status - Whether to enable or disable the treble/bass equalizer effect.
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async trebleBass(status) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "equalizer", value: status ? filtersEqualizers_1.trebleBassEqualizer : [] });
this.setFilterStatus(Enums_1.AvailableFilters.TrebleBass, status);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Toggles the vaporwave effect on the audio.
*
* This method applies or removes a vaporwave effect by adjusting the equalizer settings.
* When enabled, it gives the audio a dreamy and nostalgic feel, characteristic of the vaporwave genre.
*
* @param {boolean} status - Whether to enable or disable the vaporwave effect.
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async vaporwave(status) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "equalizer", value: status ? filtersEqualizers_1.vaporwaveEqualizer : [] });
this.setFilterStatus(Enums_1.AvailableFilters.Vaporwave, status);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Toggles the distortion effect on the audio.
*
* This method applies or removes a distortion effect by adjusting the distortion settings.
* When enabled, it adds a rougher, more intense quality to the sound by altering the
* audio signal to create a more jagged, irregular waveform.
*
* @param {boolean} status - Whether to enable or disable the distortion effect.
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async distort(status) {
const oldPlayer = { ...this };
if (status) {
await this.setDistortion({
sinOffset: 0,
sinScale: 0.2,
cosOffset: 0,
cosScale: 0.2,
tanOffset: 0,
tanScale: 0.2,
offset: 0,
scale: 1.2,
});
this.setFilterStatus(Enums_1.AvailableFilters.Distort, true);
}
else {
await this.setDistortion();
this.setFilterStatus(Enums_1.AvailableFilters.Distort, false);
}
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Toggles the party effect on the audio.
*
* This method applies or removes a party effect by adjusting the equalizer settings.
* When enabled, it enhances the bass and treble frequencies, providing a more energetic and lively sound.
*
* @param {boolean} status - Whether to enable or disable the party effect.
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async pop(status) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "equalizer", value: status ? filtersEqualizers_1.popEqualizer : [] });
this.setFilterStatus(Enums_1.AvailableFilters.Pop, status);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Toggles a party effect on the audio.
*
* This method applies a party effect to audio.
* @param {boolean} status - Whether to enable or disable the party effect.
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async party(status) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "equalizer", value: status ? filtersEqualizers_1.popEqualizer : [] });
this.setFilterStatus(Enums_1.AvailableFilters.Party, status);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Toggles earrape effect on the audio.
*
* This method applies earrape effect to audio.
* @param {boolean} status - Whether to enable or disable the earrape effect.
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async earrape(status) {
const oldPlayer = { ...this };
if (status) {
await this.player.setVolume(200);
this.setFilterStatus(Enums_1.AvailableFilters.Earrape, true);
}
else {
await this.player.setVolume(100);
this.setFilterStatus(Enums_1.AvailableFilters.Earrape, false);
}
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Toggles electronic effect on the audio.
*
* This method applies electronic effect to audio.
* @param {boolean} status - Whether to enable or disable the electronic effect.
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async electronic(status) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "equalizer", value: status ? filtersEqualizers_1.electronicEqualizer : [] });
this.setFilterStatus(Enums_1.AvailableFilters.Electronic, status);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Toggles radio effect on the audio.
*
* This method applies radio effect to audio.
* @param {boolean} status - Whether to enable or disable the radio effect.
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async radio(status) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "equalizer", value: status ? filtersEqualizers_1.radioEqualizer : [] });
this.setFilterStatus(Enums_1.AvailableFilters.Radio, status);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Toggles a tremolo effect on the audio.
*
* This method applies a tremolo effect to audio.
* @param {boolean} status - Whether to enable or disable the tremolo effect.
* @returns {this} - Returns the current instance of the Filters class for method chaining.
*/
async tremolo(status) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "vibrato", value: status ? { frequency: 5, depth: 0.5 } : null });
this.setFilterStatus(Enums_1.AvailableFilters.Tremolo, status);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Toggless a darthvader effect on the audio.
*
* This method applies a darthvader effect to audio.
* @param {boolean} status - Whether to enable or disable the darthvader effect.
* @returns {this} - Returns the current instance of the Filters class for method chaining.
*/
async darthvader(status) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "timescale", value: status ? { speed: 1.0, pitch: 0.5, rate: 1.0 } : null });
this.setFilterStatus(Enums_1.AvailableFilters.Darthvader, status);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Toggles a daycore effect on the audio.
*
* This method applies a daycore effect to audio.
* @param {boolean} status - Whether to enable or disable the daycore effect.
* @returns {this} - Returns the current instance of the Filters class for method chaining.
*/
async daycore(status) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "timescale", value: status ? { speed: 0.7, pitch: 0.8, rate: 0.8 } : null });
this.setFilterStatus(Enums_1.AvailableFilters.Daycore, status);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Toggles a doubletime effect on the audio.
*
* This method applies a doubletime effect to audio.
* @param {boolean} status - Whether to enable or disable the doubletime effect.
* @returns {this} - Returns the current instance of the Filters class for method chaining
*/
async doubletime(status) {
const oldPlayer = { ...this };
await this.applyFilter({ property: "timescale", value: status ? { speed: 2.0, pitch: 1.0, rate: 2.0 } : null });
this.setFilterStatus(Enums_1.AvailableFilters.Doubletime, status);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
/**
* Toggles the demon effect on the audio.
*
* This method applies or removes a demon effect by adjusting the equalizer,
* timescale, and reverb settings. When enabled, it creates a deeper and more
* intense sound by lowering the pitch and adding reverb to the audio.
*
* @param {boolean} status - Whether to enable or disable the demon effect.
* @returns {Promise<this>} - Returns the current instance of the Filters class for method chaining.
*/
async demon(status) {
const oldPlayer = { ...this };
const filters = status
? {
equalizer: filtersEqualizers_1.demonEqualizer,
timescale: { pitch: 0.8 },
reverb: { wet: 0.7, dry: 0.3, roomSize: 0.8, damping: 0.5 },
}
: {
equalizer: [],
timescale: null,
reverb: null,
};
await Promise.all(Object.entries(filters).map(([property, value]) => this.applyFilter({ property: property, value })));
this.setFilterStatus(Enums_1.AvailableFilters.Demon, status);
this.emitPlayersTasteUpdate(oldPlayer);
return this;
}
}
exports.Filters = Filters;