diff --git a/src/client/voice/VoiceConnection.js b/src/client/voice/VoiceConnection.js index f99e3c265..648e3e1e8 100644 --- a/src/client/voice/VoiceConnection.js +++ b/src/client/voice/VoiceConnection.js @@ -423,7 +423,14 @@ class VoiceConnection extends EventEmitter { * @param {User} user The user that has started/stopped speaking * @param {boolean} speaking Whether or not the user is speaking */ - if (this.status === VoiceStatus.CONNECTED) this.emit('speaking', user, speaking); + if (this.status === VoiceStatus.CONNECTED) { + this.emit('speaking', user, speaking); + if (!speaking) { + for (const receiver of this.receivers) { + receiver.packets._stoppedSpeaking(user_id); + } + } + } guild._memberSpeakUpdate(user_id, speaking); } diff --git a/src/client/voice/receiver/PacketHandler.js b/src/client/voice/receiver/PacketHandler.js index 9ebe69d85..346b9e152 100644 --- a/src/client/voice/receiver/PacketHandler.js +++ b/src/client/voice/receiver/PacketHandler.js @@ -11,10 +11,18 @@ class PacketHandler extends EventEmitter { this.streams = new Map(); } - makeStream(user) { - if (this.streams.has(user)) return this.streams.get(user); + _stoppedSpeaking(userID) { + if (this.streams.has(userID)) { + const { stream, end } = this.streams.get(userID); + if (end === 'silence') stream.push(null); + } + } + + makeStream(user, end) { + if (this.streams.has(user)) return this.streams.get(user).stream; const stream = new Readable(); - this.streams.set(user, stream); + stream.on('end', () => this.streams.delete(user)); + this.streams.set(user, { stream, end }); return stream; } @@ -49,8 +57,9 @@ class PacketHandler extends EventEmitter { const ssrc = buffer.readUInt32BE(8); const user = this.userFromSSRC(ssrc); if (!user) return; - const stream = this.streams.get(user.id); + let stream = this.streams.get(user.id); if (!stream) return; + stream = stream.stream; const opusPacket = this.parseBuffer(buffer); if (opusPacket instanceof Error) { this.emit('error', opusPacket); diff --git a/src/client/voice/receiver/Receiver.js b/src/client/voice/receiver/Receiver.js index 032d845c5..8b626be79 100644 --- a/src/client/voice/receiver/Receiver.js +++ b/src/client/voice/receiver/Receiver.js @@ -30,6 +30,8 @@ class VoiceReceiver extends EventEmitter { * @property {string} [mode='opus'] The mode for audio output. This defaults to opus, meaning discord.js won't decode * the packets for you. You can set this to 'pcm' so that the stream's output will be 16-bit little-endian stereo * audio + * @property {string} [end='silence'] When the stream should be destroyed. If `silence`, this will be when the user + * stops talking. Otherwise, if `manual`, this should be handled by you. */ /** @@ -39,10 +41,10 @@ class VoiceReceiver extends EventEmitter { * @param {ReceiveStreamOptions} options Options. * @returns {ReadableStream} */ - createStream(user, { mode = 'opus' } = {}) { + createStream(user, { mode = 'opus', end = 'silence' } = {}) { user = this.connection.client.users.resolve(user); if (!user) throw new Error('VOICE_USER_MISSING'); - const stream = this.packets.makeStream(user.id); + const stream = this.packets.makeStream(user.id, end); if (mode === 'pcm') { const decoder = new prism.opus.Decoder({ channels: 2, rate: 48000, frameSize: 960 }); stream.pipe(decoder);