Skip to content

Commit

Permalink
voice: allow for streaming silence to avoid audio glitches with repea…
Browse files Browse the repository at this point in the history
…ted pausing/resuming (#2354)
  • Loading branch information
amishshah committed Aug 14, 2018
1 parent e666574 commit e0f5216
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 8 deletions.
31 changes: 23 additions & 8 deletions src/client/voice/dispatcher/StreamDispatcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const VolumeInterface = require('../util/VolumeInterface');
const { Writable } = require('stream');

const secretbox = require('../util/Secretbox');
const Silence = require('../util/Silence');

const FRAME_LENGTH = 20;
const CHANNELS = 2;
Expand Down Expand Up @@ -41,6 +42,7 @@ class StreamDispatcher extends Writable {
this.player = player;
this.streamOptions = streamOptions;
this.streams = streams;
this.streams.silence = new Silence();

this._nonce = 0;
this._nonceBuffer = Buffer.alloc(24);
Expand All @@ -59,6 +61,7 @@ class StreamDispatcher extends Writable {
this.broadcast = this.streams.broadcast;

this._pausedTime = 0;
this._silentPausedTime = 0;
this.count = 0;

this.on('finish', () => {
Expand Down Expand Up @@ -119,12 +122,17 @@ class StreamDispatcher extends Writable {

/**
* Pauses playback
* @param {boolean} [silence=false] Whether to play silence while paused to prevent audio glitches
*/
pause() {
pause(silence = false) {
if (this.paused) return;
if (this.streams.opus) this.streams.opus.unpipe(this);
if (this._writeCallback) this._writeCallback();
this._setSpeaking(false);
if (silence) {
this.streams.silence.pipe(this);
this._silence = true;
} else {
this._setSpeaking(false);
}
this.pausedSince = Date.now();
}

Expand All @@ -138,15 +146,23 @@ class StreamDispatcher extends Writable {
* Total time that this dispatcher has been paused
* @type {number}
*/
get pausedTime() { return this._pausedTime + (this.paused ? Date.now() - this.pausedSince : 0); }
get pausedTime() {
return this._silentPausedTime + this._pausedTime + (this.paused ? Date.now() - this.pausedSince : 0);
}

/**
* Resumes playback
*/
resume() {
if (!this.pausedSince) return;
this.streams.silence.unpipe(this);
if (this.streams.opus) this.streams.opus.pipe(this);
this._pausedTime += Date.now() - this.pausedSince;
if (this._silence) {
this._silentPausedTime += Date.now() - this.pausedSince;
this._silence = false;
} else {
this._pausedTime += Date.now() - this.pausedSince;
}
this.pausedSince = null;
if (typeof this._writeCallback === 'function') this._writeCallback();
}
Expand Down Expand Up @@ -207,11 +223,10 @@ class StreamDispatcher extends Writable {
this._writeCallback = null;
done();
};
if (this.pausedSince) return;
if (!this.streams.broadcast) {
const next = FRAME_LENGTH + (this.count * FRAME_LENGTH) - (Date.now() - this.startTime - this.pausedTime);
const next = FRAME_LENGTH + (this.count * FRAME_LENGTH) - (Date.now() - this.startTime - this._pausedTime);
setTimeout(() => {
if (!this.pausedSince && this._writeCallback) this._writeCallback();
if ((!this.pausedSince || this._silence) && this._writeCallback) this._writeCallback();
}, next);
}
this._sdata.sequence++;
Expand Down
11 changes: 11 additions & 0 deletions src/client/voice/util/Silence.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const { Readable } = require('stream');

const SILENCE_FRAME = Buffer.from([0xF8, 0xFF, 0xFE]);

class Silence extends Readable {
_read() {
this.push(SILENCE_FRAME);
}
}

module.exports = Silence;

0 comments on commit e0f5216

Please sign in to comment.