UNPKG

@skybloxsystems/ticket-bot

Version:
129 lines (113 loc) 3.98 kB
// Based on discord.js' old volume system const { Transform } = require('stream'); /** * Transforms a stream of PCM volume. * @memberof core * @extends TransformStream */ class VolumeTransformer extends Transform { /** * @memberof core * @param {Object} options Any optional TransformStream options plus some extra: * @param {string} options.type The type of transformer: s16le (signed 16-bit little-endian), s16be, s32le, s32be * @param {number} [options.volume=1] The output volume of the stream * @example * // Half the volume of a signed 16-bit little-endian PCM stream * input * .pipe(new prism.VolumeTransformer({ type: 's16le', volume: 0.5 })) * .pipe(writeStream); */ constructor(options = {}) { super(options); switch (options.type) { case 's16le': this._readInt = (buffer, index) => buffer.readInt16LE(index); this._writeInt = (buffer, int, index) => buffer.writeInt16LE(int, index); this._bits = 16; break; case 's16be': this._readInt = (buffer, index) => buffer.readInt16BE(index); this._writeInt = (buffer, int, index) => buffer.writeInt16BE(int, index); this._bits = 16; break; case 's32le': this._readInt = (buffer, index) => buffer.readInt32LE(index); this._writeInt = (buffer, int, index) => buffer.writeInt32LE(int, index); this._bits = 32; break; case 's32be': this._readInt = (buffer, index) => buffer.readInt32BE(index); this._writeInt = (buffer, int, index) => buffer.writeInt32BE(int, index); this._bits = 32; break; default: throw new Error('VolumeTransformer type should be one of s16le, s16be, s32le, s32be'); } this._bytes = this._bits / 8; this._extremum = Math.pow(2, this._bits - 1); this.volume = typeof options.volume === 'undefined' ? 1 : options.volume; this._chunk = Buffer.alloc(0); } _readInt(buffer, index) { return index; } _writeInt(buffer, int, index) { return index; } _transform(chunk, encoding, done) { // If the volume is 1, act like a passthrough stream if (this.volume === 1) { this.push(chunk); return done(); } const { _bytes, _extremum } = this; chunk = this._chunk = Buffer.concat([this._chunk, chunk]); if (chunk.length < _bytes) return done(); const complete = Math.floor(chunk.length / _bytes) * _bytes; for (let i = 0; i < complete; i += _bytes) { const int = Math.min(_extremum - 1, Math.max(-_extremum, Math.floor(this.volume * this._readInt(chunk, i)))); this._writeInt(chunk, int, i); } this._chunk = chunk.slice(complete); this.push(chunk.slice(0, complete)); return done(); } _destroy(err, cb) { super._destroy(err, cb); this._chunk = null; } /** * Sets the volume relative to the input stream - i.e. 1 is normal, 0.5 is half, 2 is double. * @param {number} volume The volume that you want to set */ setVolume(volume) { this.volume = volume; } /** * Sets the volume in decibels. * @param {number} db The decibels */ setVolumeDecibels(db) { this.setVolume(Math.pow(10, db / 20)); } /** * Sets the volume so that a perceived value of 0.5 is half the perceived volume etc. * @param {number} value The value for the volume */ setVolumeLogarithmic(value) { this.setVolume(Math.pow(value, 1.660964)); } /** * The current volume of the stream in decibels * @readonly * @type {number} */ get volumeDecibels() { return Math.log10(this.volume) * 20; } /** * The current volume of the stream from a logarithmic scale * @readonly * @type {number} */ get volumeLogarithmic() { return Math.pow(this.volume, 1 / 1.660964); } } module.exports = VolumeTransformer;