feat: smooth volume

This commit is contained in:
DevAndromeda 2022-01-26 00:57:52 +05:45
parent 8f5155895a
commit 75a2117602
9 changed files with 1113 additions and 1085 deletions

View file

@ -215,7 +215,7 @@ const queue = player.createQueue(..., {
// only trap youtube source // only trap youtube source
if (source === "youtube") { if (source === "youtube") {
// track here would be youtube track // track here would be youtube track
return (await playdl.stream(track.url)).stream; return (await playdl.stream(track.url, { discordPlayerCompatibility : true })).stream;
// we must return readable stream or void (returning void means telling discord-player to look for default extractor) // we must return readable stream or void (returning void means telling discord-player to look for default extractor)
} }
} }

View file

@ -18,7 +18,7 @@ const queue = player.createQueue(..., {
// only trap youtube source // only trap youtube source
if (source === "youtube") { if (source === "youtube") {
// track here would be youtube track // track here would be youtube track
return (await playdl.stream(track.url)).stream; return (await playdl.stream(track.url, { discordPlayerCompatibility : true })).stream;
// we must return readable stream or void (returning void means telling discord-player to look for default extractor) // we must return readable stream or void (returning void means telling discord-player to look for default extractor)
} }
} }

View file

@ -60,14 +60,14 @@
}, },
"homepage": "https://discord-player.js.org", "homepage": "https://discord-player.js.org",
"dependencies": { "dependencies": {
"@discordjs/voice": "^0.7.5", "@discordjs/voice": "^0.8.0",
"discord-ytdl-core": "^5.0.4", "discord-ytdl-core": "^5.0.4",
"libsodium-wrappers": "^0.7.9", "libsodium-wrappers": "^0.7.9",
"soundcloud-scraper": "^5.0.2", "soundcloud-scraper": "^5.0.2",
"spotify-url-info": "^2.2.3", "spotify-url-info": "^2.2.3",
"tiny-typed-emitter": "^2.1.0", "tiny-typed-emitter": "^2.1.0",
"youtube-sr": "^4.1.9", "youtube-sr": "^4.1.12",
"ytdl-core": "^4.9.1" "ytdl-core": "^4.10.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.16.0", "@babel/cli": "^7.16.0",

View file

@ -153,6 +153,7 @@ class Player extends EventEmitter<PlayerEvents> {
const _meta = queueInitOptions.metadata; const _meta = queueInitOptions.metadata;
delete queueInitOptions["metadata"]; delete queueInitOptions["metadata"];
queueInitOptions.volumeSmoothness ??= 0.1;
queueInitOptions.ytdlOptions ??= this.options.ytdlOptions; queueInitOptions.ytdlOptions ??= this.options.ytdlOptions;
const queue = new Queue(this, guild, queueInitOptions); const queue = new Queue(this, guild, queueInitOptions);
queue.metadata = _meta; queue.metadata = _meta;

View file

@ -702,6 +702,11 @@ class Queue<T = unknown> {
if (options.seek) this._streamTime = options.seek; if (options.seek) this._streamTime = options.seek;
this._filtersUpdate = options.filtersUpdate; this._filtersUpdate = options.filtersUpdate;
if (resource.volume && typeof this.options.volumeSmoothness === "number") {
Reflect.set(resource.volume, "_smoothing", this.options.volumeSmoothness || 0);
}
this.setVolume(this.options.initialVolume); this.setVolume(this.options.initialVolume);
setTimeout(() => { setTimeout(() => {

116
src/VolumeTransformer.ts Normal file
View file

@ -0,0 +1,116 @@
// prism's volume transformer with smooth volume support
import { Transform, TransformOptions } from "stream";
export interface VolumeTransformerOptions extends TransformOptions {
type?: "s16le" | "s16be" | "s32le" | "s32be";
smoothness?: number;
volume?: number;
}
export class VolumeTransformer extends Transform {
private _bits: number;
private _smoothing: number;
private _bytes: number;
private _extremum: number;
private _chunk: Buffer;
public volume: number;
private _targetVolume: number;
constructor(options: VolumeTransformerOptions = {}) {
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._targetVolume = this.volume;
this._chunk = Buffer.alloc(0);
this._smoothing = options.smoothness || 0;
}
_readInt(buffer: Buffer, index: number) {
return index;
}
_writeInt(buffer: Buffer, int: number, index: number) {
return index;
}
_transform(chunk: Buffer, encoding: BufferEncoding, done: () => unknown) {
if (this._smoothing > 0 && this.volume !== this._targetVolume) {
if (this.volume < this._targetVolume) {
this.volume = this.volume + this._smoothing >= this._targetVolume ? this._targetVolume : this.volume + this._smoothing;
} else if (this.volume > this._targetVolume) {
this.volume = this.volume - this._smoothing <= this._targetVolume ? this._targetVolume : this.volume - this._smoothing;
}
}
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: Error, cb: (error: Error) => void) {
super._destroy(err, cb);
this._chunk = null;
}
setVolume(volume: number) {
this._targetVolume = volume;
if (this._smoothing <= 0) this.volume = volume;
}
setVolumeDecibels(db: number) {
this.setVolume(Math.pow(10, db / 20));
}
setVolumeLogarithmic(value: number) {
this.setVolume(Math.pow(value, 1.660964));
}
get volumeDecibels() {
return Math.log10(this.volume) * 20;
}
get volumeLogarithmic() {
return Math.pow(this.volume, 1 / 1.660964);
}
}

4
src/smoothVolume.ts Normal file
View file

@ -0,0 +1,4 @@
import { VolumeTransformer } from "./VolumeTransformer";
// eslint-disable-next-line
(require("prism-media") as typeof import("prism-media")).VolumeTransformer = VolumeTransformer;

View file

@ -135,6 +135,8 @@ export interface PlayerProgressbarOptions {
* @property {number} [bufferingTimeout=3000] Buffering timeout for the stream * @property {number} [bufferingTimeout=3000] Buffering timeout for the stream
* @property {boolean} [spotifyBridge=true] If player should bridge spotify source to youtube * @property {boolean} [spotifyBridge=true] If player should bridge spotify source to youtube
* @property {boolean} [disableVolume=false] If player should disable inline volume * @property {boolean} [disableVolume=false] If player should disable inline volume
* @property {boolean} [volumeSmoothness=0] The volume transition smoothness between volume changes (lower the value to get better result)
* Setting this or leaving this empty will disable this effect. Example: `volumeSmoothness: 0.1`
* @property {Function} [onBeforeCreateStream] Runs before creating stream * @property {Function} [onBeforeCreateStream] Runs before creating stream
*/ */
export interface PlayerOptions { export interface PlayerOptions {
@ -148,6 +150,7 @@ export interface PlayerOptions {
bufferingTimeout?: number; bufferingTimeout?: number;
spotifyBridge?: boolean; spotifyBridge?: boolean;
disableVolume?: boolean; disableVolume?: boolean;
volumeSmoothness?: number;
onBeforeCreateStream?: (track: Track, source: TrackSource, queue: Queue) => Promise<Readable>; onBeforeCreateStream?: (track: Track, source: TrackSource, queue: Queue) => Promise<Readable>;
} }

2059
yarn.lock

File diff suppressed because it is too large Load diff