From be1f5064c24e68967eadbfcc65af0ee1ef4e994c Mon Sep 17 00:00:00 2001 From: hydrabolt Date: Sat, 7 Nov 2015 17:21:05 +0000 Subject: [PATCH] AWESOME MAGIC --- lib/Voice/AudioEncoder.js | 48 ++++++++++ lib/Voice/VoiceConnection.js | 136 ++++++++++++++-------------- lib/Voice/VoicePacket.js | 26 ++++++ src/Voice/AudioEncoder.js | 48 ++++++++++ src/Voice/VoiceConnection.js | 167 +++++++++++++++++++---------------- src/Voice/VoicePacket.js | 26 ++++++ 6 files changed, 311 insertions(+), 140 deletions(-) create mode 100644 lib/Voice/AudioEncoder.js create mode 100644 lib/Voice/VoicePacket.js create mode 100644 src/Voice/AudioEncoder.js create mode 100644 src/Voice/VoicePacket.js diff --git a/lib/Voice/AudioEncoder.js b/lib/Voice/AudioEncoder.js new file mode 100644 index 000000000..8ae098286 --- /dev/null +++ b/lib/Voice/AudioEncoder.js @@ -0,0 +1,48 @@ +"use strict"; + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var cpoc = require("child_process"); +var opus = require("node-opus"); +var VoicePacket = require("./VoicePacket.js"); + +var AudioEncoder = (function () { + function AudioEncoder() { + _classCallCheck(this, AudioEncoder); + + this.opus = new opus.OpusEncoder(48000, 1); + } + + AudioEncoder.prototype.opusBuffer = function opusBuffer(buffer) { + + return this.opus.encode(buffer, 1920); + }; + + AudioEncoder.prototype.encodeFile = function encodeFile(file) { + var callback = arguments.length <= 1 || arguments[1] === undefined ? function (err, buffer) {} : arguments[1]; + + var self = this; + return new Promise(function (resolve, reject) { + var enc = cpoc.spawn("ffmpeg", ["-i", file, "-f", "s16le", "-ar", "48000", "-ac", "1", "-af", "volume=1", "pipe:1"]); + + enc.stdout.on("readable", function () { + callback(null, enc.stdout); + resolve(enc.stdout); + }); + + enc.stdout.on("end", function () { + callback("end"); + reject("end"); + }); + + enc.stdout.on("close", function () { + callback("close"); + reject("close"); + }); + }); + }; + + return AudioEncoder; +})(); + +module.exports = AudioEncoder; \ No newline at end of file diff --git a/lib/Voice/VoiceConnection.js b/lib/Voice/VoiceConnection.js index ca811dcb4..8c37a1e8d 100644 --- a/lib/Voice/VoiceConnection.js +++ b/lib/Voice/VoiceConnection.js @@ -9,23 +9,9 @@ var Lame = require("lame"); var Opus = require('node-opus'); var Wav = require('wav'); var fs = require("fs"); - -function VoicePacket(packet, sequence, timestamp, ssrc) { - var audioBuffer = packet; - var retBuff = new Buffer(audioBuffer.length + 12); - retBuff.fill(0); - retBuff[0] = 0x80; - retBuff[1] = 0x78; - retBuff.writeUIntBE(sequence, 2, 2); - retBuff.writeUIntBE(timestamp, 4, 4); - retBuff.writeUIntBE(ssrc, 8, 4); - - for (var i = 0; i < audioBuffer.length; i++) { - retBuff[i + 12] = audioBuffer[i]; - } - - return retBuff; -} +var ffmpeg = require('fluent-ffmpeg'); +var AudioEncoder = require("./AudioEncoder.js"); +var VoicePacket = require("./VoicePacket.js"); var VoiceConnection = (function () { function VoiceConnection(channel, client, session, token, server, endpoint) { @@ -40,69 +26,91 @@ var VoiceConnection = (function () { this.vWS = null; // vWS means voice websocket this.ready = false; this.vWSData = {}; + this.opus = new Opus.OpusEncoder(48000, 1); + this.encoder = new AudioEncoder(); + this.udp = null; this.init(); } - VoiceConnection.prototype.test = function test() { + VoiceConnection.prototype.playRawStream = function playRawStream(stream) { + var self = this; + self.playing = true; + var startTime = Date.now(); - var cnt = 0; - function sendAudio(sequence, timestamp, opusEncoder, wavOutput, udpClient, vWS) { - cnt++; - var buff = wavOutput.read(1920); - if (buff && buff.length === 1920) { - sequence + 20 < 65535 ? sequence += 1 : sequence = 0; - timestamp + 9600 < 4294967295 ? timestamp += 960 : timestamp = 0; + var sequence = 0; + var time = 0; + var count = 0; - var encoded = opusEncoder.encode(buff, 1920); - var audioPacket = VoicePacket(encoded, sequence, timestamp, self.vWSData.ssrc); + var length = 20; - var nextTime = startTime + cnt * 20; + function send() { + try { + count++; + sequence + 10 < 65535 ? sequence += 1 : sequence = 0; + time + 9600 < 4294967295 ? time += 960 : time = 0; + + self.sendBuffer(stream.read(1920), sequence, time, function (e) { + console.log(e); + }); + + var nextTime = startTime + count * length; - udpClient.send(audioPacket, 0, audioPacket.length, self.vWSData.port, self.endpoint, function (err) {}); setTimeout(function () { - sendAudio(sequence, timestamp, opusEncoder, wavOutput, udpClient, vWS); - }, 20 + (nextTime - new Date().getTime())); - } else { - var speaking = { - "op": 5, - "d": { - "speaking": false, - "delay": 0 - } - }; - vWS.send(JSON.stringify(speaking)); - } + send(); + }, length + (nextTime - Date.now())); + } catch (e) {} } - var speaking = { - "op": 5, - "d": { - "speaking": true, - "delay": 0 + self.vWS.send(JSON.stringify({ + op: 5, + d: { + speaking: true, + delay: 0 } - }; + })); + send(); + }; - var vWS = self.vWS; - var stream = fs.createReadStream("c:/users/amish/desktop/audio.wav"); + VoiceConnection.prototype.sendPacket = function sendPacket(packet) { + var callback = arguments.length <= 1 || arguments[1] === undefined ? function (err) {} : arguments[1]; - vWS.send(JSON.stringify(speaking)); + var self = this; + self.playing = true; + try { + self.udp.send(packet, 0, packet.length, self.vWSData.port, self.endpoint, callback); + } catch (e) { + self.playing = false; + callback(e); + return false; + } + }; - var rate = 48000; - var opusEncoder = new Opus.OpusEncoder(48000, 1); - var wavReader = new Wav.Reader(); - wavReader.on('format', function (format) { - console.log(format); - }); - var wavOutput = stream.pipe(wavReader); - - var sequence = 0; - var timestamp = 0; - - wavOutput.on('readable', function () { - console.log("readable!"); - sendAudio(sequence, timestamp, opusEncoder, wavOutput, self.udp, vWS); + VoiceConnection.prototype.sendBuffer = function sendBuffer(buffer, sequence, timestamp, callback) { + var self = this; + self.playing = true; + try { + + var buffer = self.encoder.opusBuffer(buffer); + var packet = new VoicePacket(buffer, sequence, timestamp, self.vWSData.ssrc); + + return self.sendPacket(packet, callback); + } catch (e) { + self.playing = false; + console.log("etype", e.stack); + return false; + } + }; + + VoiceConnection.prototype.test = function test() { + var self = this; + this.encoder.encodeFile("C:/users/amish/desktop/audio.mp3")["catch"](error).then(function (stream) { + + self.playRawStream(stream); }); + function error() { + console.log("ERROR!"); + } }; VoiceConnection.prototype.init = function init() { diff --git a/lib/Voice/VoicePacket.js b/lib/Voice/VoicePacket.js new file mode 100644 index 000000000..f432bf241 --- /dev/null +++ b/lib/Voice/VoicePacket.js @@ -0,0 +1,26 @@ +"use strict"; + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var VoicePacket = function VoicePacket(data, sequence, time, ssrc) { + _classCallCheck(this, VoicePacket); + + var audioBuffer = data, + returnBuffer = new Buffer(audioBuffer.length + 12); + + returnBuffer.fill(0); + returnBuffer[0] = 0x80; + returnBuffer[1] = 0x78; + + returnBuffer.writeUIntBE(sequence, 2, 2); + returnBuffer.writeUIntBE(time, 4, 4); + returnBuffer.writeUIntBE(ssrc, 8, 4); + + for (var i = 0; i < audioBuffer.length; i++) { + returnBuffer[i + 12] = audioBuffer[i]; + } + + return returnBuffer; +}; + +module.exports = VoicePacket; \ No newline at end of file diff --git a/src/Voice/AudioEncoder.js b/src/Voice/AudioEncoder.js new file mode 100644 index 000000000..ac7f21232 --- /dev/null +++ b/src/Voice/AudioEncoder.js @@ -0,0 +1,48 @@ +"use strict"; + +var cpoc = require("child_process"); +var opus = require("node-opus"); +var VoicePacket = require("./VoicePacket.js"); + +class AudioEncoder{ + constructor(){ + this.opus = new opus.OpusEncoder(48000, 1); + } + + opusBuffer(buffer){ + + return this.opus.encode(buffer, 1920); + + } + + encodeFile(file, callback=function(err, buffer){}){ + var self = this; + return new Promise((resolve, reject) => { + var enc = cpoc.spawn("ffmpeg" , [ + "-i", file, + "-f", "s16le", + "-ar", "48000", + "-ac", "1", + "-af", "volume=1", + "pipe:1" + ]); + + enc.stdout.on("readable", function() { + callback(null, enc.stdout); + resolve(enc.stdout) + }); + + enc.stdout.on("end", function() { + callback("end"); + reject("end"); + }); + + enc.stdout.on("close", function() { + callback("close"); + reject("close"); + }); + }); + } +} + +module.exports = AudioEncoder; \ No newline at end of file diff --git a/src/Voice/VoiceConnection.js b/src/Voice/VoiceConnection.js index 799e7b99e..1aa5e6607 100644 --- a/src/Voice/VoiceConnection.js +++ b/src/Voice/VoiceConnection.js @@ -7,23 +7,9 @@ var Lame = require("lame"); var Opus = require('node-opus'); var Wav = require('wav'); var fs = require("fs"); - -function VoicePacket(packet, sequence, timestamp, ssrc) { - var audioBuffer = packet; - var retBuff = new Buffer(audioBuffer.length + 12); - retBuff.fill(0); - retBuff[0] = 0x80; - retBuff[1] = 0x78; - retBuff.writeUIntBE(sequence, 2, 2); - retBuff.writeUIntBE(timestamp, 4, 4); - retBuff.writeUIntBE(ssrc, 8, 4); - - for (var i = 0; i < audioBuffer.length; i++) { - retBuff[i + 12] = audioBuffer[i]; - } - - return retBuff; -} +var ffmpeg = require('fluent-ffmpeg'); +var AudioEncoder = require("./AudioEncoder.js"); +var VoicePacket = require("./VoicePacket.js"); class VoiceConnection { constructor(channel, client, session, token, server, endpoint) { @@ -36,71 +22,100 @@ class VoiceConnection { this.vWS = null; // vWS means voice websocket this.ready = false; this.vWSData = {}; + this.opus = new Opus.OpusEncoder(48000, 1); + this.encoder = new AudioEncoder(); + this.udp = null; this.init(); } + + playRawStream(stream){ + + var self = this; + self.playing = true; + + var startTime = Date.now(); + var sequence = 0; + var time = 0; + var count = 0; + + var length = 20; + + function send(){ + try{ + count++; + sequence + 10 < 65535 ? sequence += 1 : sequence = 0; + time + 9600 < 4294967295 ? time += 960 : time = 0; + + self.sendBuffer(stream.read(1920), sequence, time, (e) => { + console.log(e); + }); + + var nextTime = startTime + (count * length); + + setTimeout(function() { + send(); + }, length + (nextTime - Date.now())); + + }catch(e){ + + } + } + + self.vWS.send(JSON.stringify({ + op : 5, + d : { + speaking : true, + delay : 0 + } + })); + send(); + + + } + + sendPacket(packet, callback=function(err){}){ + var self = this; + self.playing = true; + try{ + self.udp.send(packet, 0, packet.length, self.vWSData.port, self.endpoint, callback); + + }catch(e){ + self.playing = false; + callback(e); + return false; + } + } + + sendBuffer(buffer, sequence, timestamp, callback){ + var self = this; + self.playing = true; + try{ + + var buffer = self.encoder.opusBuffer(buffer); + var packet = new VoicePacket(buffer, sequence, timestamp, self.vWSData.ssrc); + + return self.sendPacket(packet, callback); + + }catch(e){ + self.playing = false; + console.log("etype", e.stack); + return false; + } + } test() { var self = this; - var startTime = Date.now(); - var cnt = 0; - function sendAudio(sequence, timestamp, opusEncoder, wavOutput, udpClient, vWS) { - cnt++; - var buff = wavOutput.read(1920); - if (buff && buff.length === 1920) { - sequence + 20 < 65535 ? sequence += 1 : sequence = 0; - timestamp + 9600 < 4294967295 ? timestamp += 960 : timestamp = 0; - - var encoded = opusEncoder.encode(buff, 1920); - var audioPacket = VoicePacket(encoded, sequence, timestamp, self.vWSData.ssrc); - - var nextTime = startTime + cnt * 20; - - udpClient.send(audioPacket, 0, audioPacket.length, self.vWSData.port, self.endpoint, function (err) { }); - setTimeout(function () { - sendAudio(sequence, timestamp, opusEncoder, wavOutput, udpClient, vWS); - }, 20 + (nextTime - new Date().getTime())); - } else { - var speaking = { - "op": 5, - "d": { - "speaking": false, - "delay": 0 - } - } - vWS.send(JSON.stringify(speaking)); - } + this.encoder + .encodeFile("C:/users/amish/desktop/audio.mp3") + .catch(error) + .then( stream => { + + self.playRawStream(stream); + + } ); + function error(){ + console.log("ERROR!"); } - - var speaking = { - "op": 5, - "d": { - "speaking": true, - "delay": 0 - } - } - - var vWS = self.vWS; - var stream = fs.createReadStream("c:/users/amish/desktop/audio.wav"); - - vWS.send(JSON.stringify(speaking)); - - var rate = 48000; - var opusEncoder = new Opus.OpusEncoder(48000, 1); - var wavReader = new Wav.Reader(); -wavReader.on('format', function (format) { -console.log(format); -}); - var wavOutput = stream.pipe(wavReader); - - var sequence = 0; - var timestamp = 0; - - wavOutput.on('readable', function () { - console.log("readable!"); - sendAudio(sequence, timestamp, opusEncoder, wavOutput, self.udp, vWS); - }); - - } init() { diff --git a/src/Voice/VoicePacket.js b/src/Voice/VoicePacket.js new file mode 100644 index 000000000..d5a107e42 --- /dev/null +++ b/src/Voice/VoicePacket.js @@ -0,0 +1,26 @@ +"use strict"; + +class VoicePacket{ + constructor(data, sequence, time, ssrc){ + + var audioBuffer = data, + returnBuffer = new Buffer(audioBuffer.length + 12); + + returnBuffer.fill(0); + returnBuffer[0] = 0x80; + returnBuffer[1] = 0x78; + + returnBuffer.writeUIntBE(sequence, 2, 2); + returnBuffer.writeUIntBE(time, 4, 4); + returnBuffer.writeUIntBE(ssrc, 8, 4); + + for (var i=0; i