VoiceBroadcasting much more efficient

This commit is contained in:
Amish Shah
2016-12-30 13:57:09 +00:00
parent bf4010e89c
commit 91fc6ccb5c
3 changed files with 107 additions and 20 deletions

View File

@@ -1,5 +1,7 @@
const EventEmitter = require('events').EventEmitter; const EventEmitter = require('events').EventEmitter;
const Prism = require('prism-media'); const Prism = require('prism-media');
const OpusEncoders = require('./opus/OpusEngineList');
const Collection = require('../../util/Collection');
const ffmpegArguments = [ const ffmpegArguments = [
'-analyzeduration', '0', '-analyzeduration', '0',
@@ -13,10 +15,42 @@ class VoiceBroadcast extends EventEmitter {
constructor(client) { constructor(client) {
super(); super();
this.client = client; this.client = client;
this.dispatchers = []; this.dispatchers = new Collection();
this.prism = new Prism(); this.prism = new Prism();
this.opusEncoder = OpusEncoders.fetch();
this.currentTranscoder = null; this.currentTranscoder = null;
this.tickInterval = null; this.tickInterval = null;
this._volume = 1;
}
applyVolume(buffer, volume) {
volume = volume || this._volume;
if (volume === 1) return buffer;
const out = new Buffer(buffer.length);
for (let i = 0; i < buffer.length; i += 2) {
if (i >= buffer.length - 1) break;
const uint = Math.min(32767, Math.max(-32767, Math.floor(volume * buffer.readInt16LE(i))));
out.writeInt16LE(uint, i);
}
return out;
}
setVolume(volume) {
this._volume = volume;
}
setVolumeDecibels(db) {
this.setVolume(Math.pow(10, db / 20));
}
setVolumeLogarithmic(value) {
this.setVolume(Math.pow(value, 1.660964));
}
get volume() {
return this._volume;
} }
get _playableStream() { get _playableStream() {
@@ -24,12 +58,30 @@ class VoiceBroadcast extends EventEmitter {
return this.currentTranscoder.transcoder.output || this.currentTranscoder.options.stream; return this.currentTranscoder.transcoder.output || this.currentTranscoder.options.stream;
} }
unregisterDispatcher(dispatcher, old) {
let container = this.dispatchers.get(old || dispatcher.volume);
if (container) {
if (container.delete(dispatcher)) return;
}
for (container of this.dispatchers.values()) {
container.delete(dispatcher);
}
}
registerDispatcher(dispatcher) { registerDispatcher(dispatcher) {
if (!this.dispatchers.includes(dispatcher)) { if (!this.dispatchers.has(dispatcher.volume)) {
this.dispatchers.push(dispatcher); this.dispatchers.set(dispatcher.volume, new Set());
dispatcher.once('end', () => { }
const ind = this.dispatchers.indexOf(dispatcher); const container = this.dispatchers.get(dispatcher.volume);
if (ind > -1) this.dispatchers.splice(ind, 1); if (!container.has(dispatcher)) {
container.add(dispatcher);
dispatcher.once('end', () => this.unregisterDispatcher(dispatcher));
dispatcher.on('volumeChange', (o, n) => {
this.unregisterDispatcher(dispatcher, o);
if (!this.dispatchers.has(n)) {
this.dispatchers.set(n, new Set());
}
this.dispatchers.get(n).add(dispatcher);
}); });
} }
} }
@@ -78,15 +130,19 @@ class VoiceBroadcast extends EventEmitter {
} }
pause() { pause() {
for (const dispatcher of this.dispatchers) { for (const container of this.dispatchers.values()) {
dispatcher.pause(); for (const dispatcher of container.values()) {
dispatcher.pause();
}
} }
clearInterval(this.tickInterval); clearInterval(this.tickInterval);
} }
resume() { resume() {
for (const dispatcher of this.dispatchers) { for (const container of this.dispatchers.values()) {
dispatcher.resume(); for (const dispatcher of container.values()) {
dispatcher.resume();
}
} }
this._startPlaying(); this._startPlaying();
} }
@@ -99,17 +155,37 @@ class VoiceBroadcast extends EventEmitter {
tick() { tick() {
if (!this._playableStream) return; if (!this._playableStream) return;
const stream = this._playableStream; const stream = this._playableStream;
const buffer = stream.read(1920 * 2); const bufferLength = 1920 * 2;
let buffer = stream.read(bufferLength);
for (const dispatcher of this.dispatchers) { if (!buffer) return;
setImmediate(() => dispatcher.process(buffer, true));
if (buffer.length !== bufferLength) {
const newBuffer = new Buffer(bufferLength).fill(0);
buffer.copy(newBuffer);
buffer = newBuffer;
}
buffer = this.applyVolume(buffer);
for (const x of this.dispatchers.entries()) {
const [volume, container] = x;
if (container.size === 0) continue;
setImmediate(() => {
const opusPacket = this.opusEncoder.encode(this.applyVolume(buffer, volume));
for (const dispatcher of container.values()) {
dispatcher.process(buffer, true, opusPacket);
}
});
} }
} }
end() { end() {
this.killCurrentTranscoder(); this.killCurrentTranscoder();
for (const dispatcher of this.dispatchers) { for (const container of this.dispatchers.values()) {
dispatcher.destroy('end', 'broadcast ended'); for (const dispatcher of container.values()) {
dispatcher.destroy('end', 'broadcast ended');
}
} }
} }
} }

View File

@@ -80,6 +80,7 @@ class StreamDispatcher extends EventEmitter {
* @param {number} volume The volume that you want to set * @param {number} volume The volume that you want to set
*/ */
setVolume(volume) { setVolume(volume) {
this.emit('volumeChange', this.streamOptions.volume, volume);
this.streamOptions.volume = volume; this.streamOptions.volume = volume;
} }
@@ -88,7 +89,7 @@ class StreamDispatcher extends EventEmitter {
* @param {number} db The decibels * @param {number} db The decibels
*/ */
setVolumeDecibels(db) { setVolumeDecibels(db) {
this.streamOptions.volume = Math.pow(10, db / 20); this.setVolume(Math.pow(10, db / 20));
} }
/** /**
@@ -96,7 +97,7 @@ class StreamDispatcher extends EventEmitter {
* @param {number} value The value for the volume * @param {number} value The value for the volume
*/ */
setVolumeLogarithmic(value) { setVolumeLogarithmic(value) {
this.streamOptions.volume = Math.pow(value, 1.660964); this.setVolume(Math.pow(value, 1.660964));
} }
/** /**
@@ -128,9 +129,10 @@ class StreamDispatcher extends EventEmitter {
this.emit('speaking', value); this.emit('speaking', value);
} }
sendBuffer(buffer, sequence, timestamp) { sendBuffer(buffer, sequence, timestamp, opusPacket) {
opusPacket = opusPacket || this.player.opusEncoder.encode(buffer);
let repeats = this.passes; let repeats = this.passes;
const packet = this.createPacket(sequence, timestamp, this.player.opusEncoder.encode(buffer)); const packet = this.createPacket(sequence, timestamp, opusPacket);
while (repeats--) { while (repeats--) {
this.player.voiceConnection.sockets.udp.send(packet) this.player.voiceConnection.sockets.udp.send(packet)
.catch(e => this.emit('debug', `Failed to send a packet ${e}`)); .catch(e => this.emit('debug', `Failed to send a packet ${e}`));
@@ -168,7 +170,7 @@ class StreamDispatcher extends EventEmitter {
return out; return out;
} }
process(buffer, controlled) { process(buffer, controlled, packet) {
try { try {
if (this.destroyed) { if (this.destroyed) {
this.setSpeaking(false); this.setSpeaking(false);
@@ -208,6 +210,14 @@ class StreamDispatcher extends EventEmitter {
data.startTime = Date.now(); data.startTime = Date.now();
} }
if (packet) {
data.count++;
data.sequence = data.sequence < 65535 ? data.sequence + 1 : 0;
data.timestamp = data.timestamp + 4294967295 ? data.timestamp + 960 : 0;
this.sendBuffer(null, data.sequence, data.timestamp, packet);
return;
}
const bufferLength = 1920 * data.channels; const bufferLength = 1920 * data.channels;
if (!controlled) { if (!controlled) {
buffer = this.stream.read(bufferLength); buffer = this.stream.read(bufferLength);

View File

@@ -46,6 +46,7 @@ client.on('message', m => {
const com = eval(m.content.split(' ').slice(1).join(' ')); const com = eval(m.content.split(' ').slice(1).join(' '));
m.channel.sendMessage(`\`\`\`\n${com}\`\`\``); m.channel.sendMessage(`\`\`\`\n${com}\`\`\``);
} catch (e) { } catch (e) {
console.log(e);
m.channel.sendMessage(`\`\`\`\n${e}\`\`\``); m.channel.sendMessage(`\`\`\`\n${e}\`\`\``);
} }
} }