UNPKG

magmastream

Version:

A user-friendly Lavalink client designed for NodeJS.

637 lines (636 loc) 28.2 kB
"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;