repo: janusweb action: commit revision: path_from: revision_from: 583a261a527041d6ce30b0a88ecfc778aa4107c7: path_to: revision_to:
commit 583a261a527041d6ce30b0a88ecfc778aa4107c7 Author: James BaicoianuDate: Tue Apr 26 14:07:05 2016 -0700 Switched to using janusplayer diff --git a/scripts/client.js b/scripts/client.js
--- a/scripts/client.js
+++ b/scripts/client.js
@@ -1,4 +1,4 @@
-elation.require(['engine.engine', 'engine.assets', 'engine.things.player', 'engine.things.light_ambient', 'engine.things.light_directional', 'engine.things.light_point', 'janusweb.janusweb', 'janusweb.chat'], function() {
+elation.require(['engine.engine', 'engine.assets', 'engine.things.light_ambient', 'engine.things.light_directional', 'engine.things.light_point', 'janusweb.janusweb', 'janusweb.chat', 'janusweb.janusplayer'], function() {
elation.extend('janusweb.init', function(args) {
if (!args) args = {};
var proto = elation.utils.any(args.protocol, elation.config.get('dependencies.protocol'), document.location.protocol);
@@ -47,11 +47,11 @@ elation.require(['engine.engine', 'engine.assets', 'engine.things.player', 'engi
things: {
player: {
name: 'player',
- type: 'player',
+ type: 'janusplayer',
properties: {
position: [0,0,0],
mass: 10,
- movespeed: 100,
+ movespeed: 5000,
collidable: false
}
},
diff --git a/scripts/external/JanusClientConnection.js b/scripts/external/JanusClientConnection.js
new file mode 100644
index 0000000000000000000000000000000000000000..8a04c427c31d4cae8802245ef806d8bdcb0788af
--- /dev/null
+++ b/scripts/external/JanusClientConnection.js
@@ -0,0 +1,365 @@
+// md5 hash functions
+
+/*
+ * http://www.myersdaily.org/joseph/javascript/md5-text.html
+ */
+(function (global) {
+
+ var md5cycle = function (x, k) {
+ var a = x[0],
+ b = x[1],
+ c = x[2],
+ d = x[3];
+
+ a = ff(a, b, c, d, k[0], 7, -680876936);
+ d = ff(d, a, b, c, k[1], 12, -389564586);
+ c = ff(c, d, a, b, k[2], 17, 606105819);
+ b = ff(b, c, d, a, k[3], 22, -1044525330);
+ a = ff(a, b, c, d, k[4], 7, -176418897);
+ d = ff(d, a, b, c, k[5], 12, 1200080426);
+ c = ff(c, d, a, b, k[6], 17, -1473231341);
+ b = ff(b, c, d, a, k[7], 22, -45705983);
+ a = ff(a, b, c, d, k[8], 7, 1770035416);
+ d = ff(d, a, b, c, k[9], 12, -1958414417);
+ c = ff(c, d, a, b, k[10], 17, -42063);
+ b = ff(b, c, d, a, k[11], 22, -1990404162);
+ a = ff(a, b, c, d, k[12], 7, 1804603682);
+ d = ff(d, a, b, c, k[13], 12, -40341101);
+ c = ff(c, d, a, b, k[14], 17, -1502002290);
+ b = ff(b, c, d, a, k[15], 22, 1236535329);
+
+ a = gg(a, b, c, d, k[1], 5, -165796510);
+ d = gg(d, a, b, c, k[6], 9, -1069501632);
+ c = gg(c, d, a, b, k[11], 14, 643717713);
+ b = gg(b, c, d, a, k[0], 20, -373897302);
+ a = gg(a, b, c, d, k[5], 5, -701558691);
+ d = gg(d, a, b, c, k[10], 9, 38016083);
+ c = gg(c, d, a, b, k[15], 14, -660478335);
+ b = gg(b, c, d, a, k[4], 20, -405537848);
+ a = gg(a, b, c, d, k[9], 5, 568446438);
+ d = gg(d, a, b, c, k[14], 9, -1019803690);
+ c = gg(c, d, a, b, k[3], 14, -187363961);
+ b = gg(b, c, d, a, k[8], 20, 1163531501);
+ a = gg(a, b, c, d, k[13], 5, -1444681467);
+ d = gg(d, a, b, c, k[2], 9, -51403784);
+ c = gg(c, d, a, b, k[7], 14, 1735328473);
+ b = gg(b, c, d, a, k[12], 20, -1926607734);
+
+ a = hh(a, b, c, d, k[5], 4, -378558);
+ d = hh(d, a, b, c, k[8], 11, -2022574463);
+ c = hh(c, d, a, b, k[11], 16, 1839030562);
+ b = hh(b, c, d, a, k[14], 23, -35309556);
+ a = hh(a, b, c, d, k[1], 4, -1530992060);
+ d = hh(d, a, b, c, k[4], 11, 1272893353);
+ c = hh(c, d, a, b, k[7], 16, -155497632);
+ b = hh(b, c, d, a, k[10], 23, -1094730640);
+ a = hh(a, b, c, d, k[13], 4, 681279174);
+ d = hh(d, a, b, c, k[0], 11, -358537222);
+ c = hh(c, d, a, b, k[3], 16, -722521979);
+ b = hh(b, c, d, a, k[6], 23, 76029189);
+ a = hh(a, b, c, d, k[9], 4, -640364487);
+ d = hh(d, a, b, c, k[12], 11, -421815835);
+ c = hh(c, d, a, b, k[15], 16, 530742520);
+ b = hh(b, c, d, a, k[2], 23, -995338651);
+
+ a = ii(a, b, c, d, k[0], 6, -198630844);
+ d = ii(d, a, b, c, k[7], 10, 1126891415);
+ c = ii(c, d, a, b, k[14], 15, -1416354905);
+ b = ii(b, c, d, a, k[5], 21, -57434055);
+ a = ii(a, b, c, d, k[12], 6, 1700485571);
+ d = ii(d, a, b, c, k[3], 10, -1894986606);
+ c = ii(c, d, a, b, k[10], 15, -1051523);
+ b = ii(b, c, d, a, k[1], 21, -2054922799);
+ a = ii(a, b, c, d, k[8], 6, 1873313359);
+ d = ii(d, a, b, c, k[15], 10, -30611744);
+ c = ii(c, d, a, b, k[6], 15, -1560198380);
+ b = ii(b, c, d, a, k[13], 21, 1309151649);
+ a = ii(a, b, c, d, k[4], 6, -145523070);
+ d = ii(d, a, b, c, k[11], 10, -1120210379);
+ c = ii(c, d, a, b, k[2], 15, 718787259);
+ b = ii(b, c, d, a, k[9], 21, -343485551);
+
+ x[0] = add32(a, x[0]);
+ x[1] = add32(b, x[1]);
+ x[2] = add32(c, x[2]);
+ x[3] = add32(d, x[3]);
+
+ }
+
+ var cmn = function (q, a, b, x, s, t) {
+ a = add32(add32(a, q), add32(x, t));
+ return add32((a << s) | (a >>> (32 - s)), b);
+ }
+
+ var ff = function (a, b, c, d, x, s, t) {
+ return cmn((b & c) | ((~b) & d), a, b, x, s, t);
+ }
+
+ var gg = function (a, b, c, d, x, s, t) {
+ return cmn((b & d) | (c & (~d)), a, b, x, s, t);
+ }
+
+ var hh = function (a, b, c, d, x, s, t) {
+ return cmn(b ^ c ^ d, a, b, x, s, t);
+ }
+
+ var ii = function (a, b, c, d, x, s, t) {
+ return cmn(c ^ (b | (~d)), a, b, x, s, t);
+ }
+
+ var md51 = function (s) {
+ var txt = '',
+ n = s.length,
+ state = [1732584193, -271733879, -1732584194, 271733878],
+ i;
+ for (i = 64; i <= s.length; i += 64) {
+ md5cycle(state, md5blk(s.substring(i - 64, i)));
+ }
+ s = s.substring(i - 64);
+ var tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+ for (i = 0; i < s.length; i++)
+ tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);
+ tail[i >> 2] |= 0x80 << ((i % 4) << 3);
+ if (i > 55) {
+ md5cycle(state, tail);
+ for (i = 0; i < 16; i++) tail[i] = 0;
+ }
+ tail[14] = n * 8;
+ md5cycle(state, tail);
+ return state;
+ }
+
+ /* there needs to be support for Unicode here,
+ * unless we pretend that we can redefine the MD-5
+ * algorithm for multi-byte characters (perhaps
+ * by adding every four 16-bit characters and
+ * shortening the sum to 32 bits). Otherwise
+ * I suggest performing MD-5 as if every character
+ * was two bytes--e.g., 0040 0025 = @%--but then
+ * how will an ordinary MD-5 sum be matched?
+ * There is no way to standardize text to something
+ * like UTF-8 before transformation; speed cost is
+ * utterly prohibitive. The JavaScript standard
+ * itself needs to look at this: it should start
+ * providing access to strings as preformed UTF-8
+ * 8-bit unsigned value arrays.
+ */
+ var md5blk = function (s) { /* I figured global was faster. */
+ var md5blks = [],
+ i; /* Andy King said do it this way. */
+ for (i = 0; i < 64; i += 4) {
+ md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);
+ }
+ return md5blks;
+ }
+
+ var hex_chr = '0123456789abcdef'.split('');
+
+ var rhex = function (n) {
+ var s = '',
+ j = 0;
+ for (; j < 4; j++)
+ s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];
+ return s;
+ }
+
+ var hex = function (x) {
+ for (var i = 0; i < x.length; i++)
+ x[i] = rhex(x[i]);
+ return x.join('');
+ }
+
+ var md5 = global.md5 = function (s) {
+ return hex(md51(s));
+ }
+
+ /* this function is much faster,
+ so if possible we use it. Some IEs
+ are the only ones I know of that
+ need the idiotic second function,
+ generated by an if clause. */
+
+ var add32 = function (a, b) {
+ return (a + b) & 0xFFFFFFFF;
+ }
+
+ if (md5('hello') != '5d41402abc4b2a76b9719d911017c592') {
+ var add32 = function (x, y) {
+ var lsw = (x & 0xFFFF) + (y & 0xFFFF),
+ msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+ return (msw << 16) | (lsw & 0xFFFF);
+ }
+ }
+
+})(window);
+
+// event dispatcher by mrdoob https://github.com/mrdoob/eventdispatcher.js
+var EventDispatcher = function () {}
+
+EventDispatcher.prototype = {
+
+ constructor: EventDispatcher,
+
+ apply: function ( object ) {
+
+ object.addEventListener = EventDispatcher.prototype.addEventListener;
+ object.hasEventListener = EventDispatcher.prototype.hasEventListener;
+ object.removeEventListener = EventDispatcher.prototype.removeEventListener;
+ object.dispatchEvent = EventDispatcher.prototype.dispatchEvent;
+
+ },
+
+ addEventListener: function ( type, listener ) {
+
+ if ( this._listeners === undefined ) this._listeners = {};
+
+ var listeners = this._listeners;
+
+ if ( listeners[ type ] === undefined ) {
+
+ listeners[ type ] = [];
+
+ }
+
+ if ( listeners[ type ].indexOf( listener ) === - 1 ) {
+
+ listeners[ type ].push( listener );
+
+ }
+
+ },
+
+ hasEventListener: function ( type, listener ) {
+
+ if ( this._listeners === undefined ) return false;
+
+ var listeners = this._listeners;
+
+ if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) {
+
+ return true;
+
+ }
+
+ return false;
+
+ },
+
+ removeEventListener: function ( type, listener ) {
+
+ if ( this._listeners === undefined ) return;
+
+ var listeners = this._listeners;
+ var listenerArray = listeners[ type ];
+
+ if ( listenerArray !== undefined ) {
+
+ var index = listenerArray.indexOf( listener );
+
+ if ( index !== - 1 ) {
+
+ listenerArray.splice( index, 1 );
+
+ }
+
+ }
+
+ },
+
+ dispatchEvent: function ( event ) {
+
+ if ( this._listeners === undefined ) return;
+
+ var listeners = this._listeners;
+ var listenerArray = listeners[ event.type ];
+
+ if ( listenerArray !== undefined ) {
+
+ event.target = this;
+
+ var array = [];
+ var length = listenerArray.length;
+
+ for ( var i = 0; i < length; i ++ ) {
+
+ array[ i ] = listenerArray[ i ];
+
+ }
+
+ for ( var i = 0; i < length; i ++ ) {
+
+ array[ i ].call( this, event );
+
+ }
+
+ }
+
+ }
+
+};
+// jcc
+var JanusClientConnection = function(opts)
+{
+ this._userId = opts.userId;
+ this._roomUrl = opts.roomUrl;
+ this._version = opts.version;
+ this._websocket = new WebSocket(opts.host, 'binary');
+ this._websocket.onopen = function() {
+ this.sendLogon();
+ this.subscribe(this._roomUrl);
+ this.dispatchEvent({type: 'connect'});
+ }.bind(this)
+ this._websocket.onmessage = this.onMessage.bind(this)
+};
+
+EventDispatcher.prototype.apply(JanusClientConnection.prototype);
+
+JanusClientConnection.prototype.sendLogon = function() {
+ var msgData = {
+ 'method': 'logon',
+ 'data': {
+ 'userId': this._userId,
+ 'version': this._version,
+ 'roomId': md5(this._roomUrl)
+ }
+ }
+ this.send(msgData);
+};
+
+JanusClientConnection.prototype.send = function(msg) {
+ this._websocket.send(JSON.stringify(msg) + '\r\n');
+};
+
+JanusClientConnection.prototype.onMessage = function(msg) {
+ this.dispatchEvent({type: 'message', data: JSON.parse(msg.data)});
+};
+
+JanusClientConnection.prototype.subscribe = function(roomId) {
+ console.log('subscribing to ', roomId);
+ this.send({
+ 'method': 'subscribe',
+ 'data': {
+ 'roomId': md5(roomId)
+ }
+ });
+};
+
+JanusClientConnection.prototype.unsubscribe = function(url) {
+ console.log('unsubscribing from', url);
+ this.send({
+ 'method': 'unsubscribe',
+ 'data': {
+ 'roomId': md5(url)
+ }
+ });
+};
+
+JanusClientConnection.prototype.enter_room = function(url) {
+ this.send({
+ 'method': 'enter_room',
+ 'data': {
+ 'roomId': md5(url)
+ }
+ });
+};
+
diff --git a/scripts/external/JanusVOIP.js b/scripts/external/JanusVOIP.js
new file mode 100644
index 0000000000000000000000000000000000000000..64911d526fae179f8ef409a3700c88d98c9ed796
--- /dev/null
+++ b/scripts/external/JanusVOIP.js
@@ -0,0 +1,430 @@
+// event dispatcher by mrdoob https://github.com/mrdoob/eventdispatcher.js
+var EventDispatcher = function () {}
+
+EventDispatcher.prototype = {
+
+ constructor: EventDispatcher,
+
+ apply: function ( object ) {
+
+ object.addEventListener = EventDispatcher.prototype.addEventListener;
+ object.hasEventListener = EventDispatcher.prototype.hasEventListener;
+ object.removeEventListener = EventDispatcher.prototype.removeEventListener;
+ object.dispatchEvent = EventDispatcher.prototype.dispatchEvent;
+
+ },
+
+ addEventListener: function ( type, listener ) {
+
+ if ( this._listeners === undefined ) this._listeners = {};
+
+ var listeners = this._listeners;
+
+ if ( listeners[ type ] === undefined ) {
+
+ listeners[ type ] = [];
+
+ }
+
+ if ( listeners[ type ].indexOf( listener ) === - 1 ) {
+
+ listeners[ type ].push( listener );
+
+ }
+
+ },
+
+ hasEventListener: function ( type, listener ) {
+
+ if ( this._listeners === undefined ) return false;
+
+ var listeners = this._listeners;
+
+ if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) {
+
+ return true;
+
+ }
+
+ return false;
+
+ },
+
+ removeEventListener: function ( type, listener ) {
+
+ if ( this._listeners === undefined ) return;
+
+ var listeners = this._listeners;
+ var listenerArray = listeners[ type ];
+
+ if ( listenerArray !== undefined ) {
+
+ var index = listenerArray.indexOf( listener );
+
+ if ( index !== - 1 ) {
+
+ listenerArray.splice( index, 1 );
+
+ }
+
+ }
+
+ },
+
+ dispatchEvent: function ( event ) {
+
+ if ( this._listeners === undefined ) return;
+
+ var listeners = this._listeners;
+ var listenerArray = listeners[ event.type ];
+
+ if ( listenerArray !== undefined ) {
+
+ event.target = this;
+
+ var array = [];
+ var length = listenerArray.length;
+
+ for ( var i = 0; i < length; i ++ ) {
+
+ array[ i ] = listenerArray[ i ];
+
+ }
+
+ for ( var i = 0; i < length; i ++ ) {
+
+ array[ i ].call( this, event );
+
+ }
+
+ }
+
+ }
+
+};
+JanusVOIPRecorder = function(args) {
+ if (!args) var args = {};
+ this.sampleRate = args.sampleRate || 11000;
+}
+EventDispatcher.prototype.apply(JanusVOIPRecorder.prototype);
+
+JanusVOIPRecorder.prototype.start = function() {
+ if (!this.context) {
+ this.createContext();
+ }
+ var getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
+ if (!navigator.getUserMedia) {
+ navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
+ }
+
+ navigator.getUserMedia({audio: true}, this.attachContext.bind(this), this.handleFailure.bind(this));
+}
+JanusVOIPRecorder.prototype.stop = function() {
+ if (this.voipsource) {
+ var tracks = this.voipsource.getAudioTracks();
+ tracks.forEach(function(track) {
+ track.stop();
+ });
+ this.voipsource = false;
+ this.dispatchEvent({type: 'voip_stop'});
+ }
+}
+JanusVOIPRecorder.prototype.createContext = function() {
+ var audioContext = window.AudioContext || window.webkitAudioContext;
+ if (!this.context) {
+ this.context = new audioContext();
+ }
+ var context = this.context;
+
+ // retrieve the current sample rate to be used for WAV packaging
+ var sampleRate = context.sampleRate;
+
+ // creates a gain node
+ this.volume = context.createGain();
+
+ /* From the spec: This value controls how frequently the audioprocess event is
+ dispatched and how many sample-frames need to be processed each call.
+ Lower values for buffer size will result in a lower (better) latency.
+ Higher values will be necessary to avoid audio breakup and glitches */
+ var bufferSize = 4096;
+ this.recorder = context.createScriptProcessor(bufferSize, 1, 1);
+ var recorder = this.recorder;
+ // we connect the recorder
+ this.volume.connect (recorder);
+ recorder.connect (context.destination);
+
+ this.recorder = recorder;
+ this.dispatchEvent({type: 'voip_init'});
+}
+JanusVOIPRecorder.prototype.attachContext = function(audiostream) {
+ this.voipsource = audiostream;
+
+ // creates an audio node from the microphone incoming stream
+ var audioInput = this.context.createMediaStreamSource(audiostream);
+
+ // connect the stream to the gain node
+ audioInput.connect(this.volume);
+
+ var recorder = this.recorder,
+ context = this.context;
+ recorder.onaudioprocess = this.processAudio.bind(this, context);
+ var tracks = this.voipsource.getTracks();
+ tracks.forEach(function(track) {
+ track.addEventListener('ended', function() { recorder.onaudioprocess = null; });
+ }.bind(this))
+
+ this.dispatchEvent({type: 'voip_start', element: this});
+}
+JanusVOIPRecorder.prototype.processAudio = function(context, e){
+ var left = e.inputBuffer.getChannelData(0);
+ var resampler = new Resampler(context.sampleRate, this.sampleRate, 1, left);
+ var what = resampler.resampler(left.length);
+ var newbuf = new Uint16Array(resampler.outputBuffer.length);
+ for (var i = 0; i < newbuf.length; i++) {
+ newbuf[i] = Math.floor(resampler.outputBuffer[i] * 32767);
+ }
+ this.dispatchEvent({type: 'voip_data', element: this, data: newbuf});
+}
+JanusVOIPRecorder.prototype.handleFailure = function(err) {
+ this.dispatchEvent({type: 'voip_error', element: this, data: err});
+}
+
+
+JanusVOIPPlayer = function(args) {
+ if (!args) var args = {};
+ this.context = args.context;
+ this.sampleRate = args.sampleRate || 11000;
+ this.bufferTime = args.bufferTime || 1.0;
+ this.audioScale = args.audioScale || 32768;
+
+ this.bufferLength = this.sampleRate * this.bufferTime;
+}
+EventDispatcher.prototype.apply(JanusVOIPPlayer.prototype);
+
+JanusVOIPPlayer.prototype.start = function() {
+ this.rawbuffer = context.createBuffer(2, this.bufferLength, this.sampleRate);
+ this.readoffset = 0;
+ this.writeoffset = 0;
+
+ this.dispatchEvent({type: 'voip_player_init', element: this});
+}
+JanusVOIPPlayer.prototype.speak = function(noise) {
+ var binary_string = window.atob(noise);
+ var len = binary_string.length;
+
+ // Even though this is a mono source, we treat it as stereo so it can be used as poitional audio
+ var bufferLeft = this.rawbuffer.getChannelData(0);
+ var bufferRight = this.rawbuffer.getChannelData(1);
+
+ // Decode the binary string into an unsigned char array
+ var audiodata = new Uint8Array(len);
+ for (var i = 0; i < len; i++) {
+ audiodata[i] = binary_string.charCodeAt(i);
+ }
+
+ // Create a new view into the decoded data which gives us the data as int16_t instead of unsigned chars
+ var audiodata16 = new Int16Array(audiodata.buffer);
+
+ var startoffset = this.writeoffset;
+ // Write the scaled data into our buffer, treating it as a looping ring buffer
+ for (var i = 0; i < audiodata16.length; i++) {
+ var idx = (startoffset + i) % this.bufferLength;
+ bufferLeft[idx] = bufferRight[idx] = (audiodata16[i] / this.audioScale);
+ }
+ this.writeoffset += audiodata16.length;
+
+ this.dispatchEvent({type: 'voip_player_data', element: this, data: {buffer: bufferLeft, start: startoffset, end: this.writeoffset}});
+ if (this.talktimer) {
+ clearTimeout(this.talktimer);
+ }
+ this.talktimer = setTimeout(elation.bind(this, this.stop), this.bufferLength * 500);
+}
+JanusVOIPPlayer.prototype.stop = function() {
+ this.dispatchEvent({type: 'voip_player_stop', element: this});
+}
+JanusVOIPPlayer.prototype.silence = function() {
+ var bufferLeft = this.rawbuffer.getChannelData(0);
+ var bufferRight = this.rawbuffer.getChannelData(1);
+ for (var i = 0; i < bufferLeft.length; i++) {
+ bufferLeft[i] = bufferRight[i] = 0;
+ }
+ this.writeoffset = 0;
+}
+
+
+
+
+
+
+//JavaScript Audio Resampler
+//Copyright (C) 2011-2015 Grant Galitz
+//Released to Public Domain
+function Resampler(fromSampleRate, toSampleRate, channels, inputBuffer) {
+ //Input Sample Rate:
+ this.fromSampleRate = +fromSampleRate;
+ //Output Sample Rate:
+ this.toSampleRate = +toSampleRate;
+ //Number of channels:
+ this.channels = channels | 0;
+ //Type checking the input buffer:
+ if (typeof inputBuffer != "object") {
+ throw(new Error("inputBuffer is not an object."));
+ }
+ if (!(inputBuffer instanceof Array) && !(inputBuffer instanceof Float32Array) && !(inputBuffer instanceof Float64Array)) {
+ throw(new Error("inputBuffer is not an array or a float32 or a float64 array."));
+ }
+ this.inputBuffer = inputBuffer;
+ //Initialize the resampler:
+ this.initialize();
+}
+Resampler.prototype.initialize = function () {
+ //Perform some checks:
+ if (this.fromSampleRate > 0 && this.toSampleRate > 0 && this.channels > 0) {
+ if (this.fromSampleRate == this.toSampleRate) {
+ //Setup a resampler bypass:
+ this.resampler = this.bypassResampler; //Resampler just returns what was passed through.
+ this.ratioWeight = 1;
+ this.outputBuffer = this.inputBuffer;
+ }
+ else {
+ this.ratioWeight = this.fromSampleRate / this.toSampleRate;
+ if (this.fromSampleRate < this.toSampleRate) {
+ /*
+ Use generic linear interpolation if upsampling,
+ as linear interpolation produces a gradient that we want
+ and works fine with two input sample points per output in this case.
+ */
+ this.compileLinearInterpolationFunction();
+ this.lastWeight = 1;
+ }
+ else {
+ /*
+ Custom resampler I wrote that doesn't skip samples
+ like standard linear interpolation in high downsampling.
+ This is more accurate than linear interpolation on downsampling.
+ */
+ this.compileMultiTapFunction();
+ this.tailExists = false;
+ this.lastWeight = 0;
+ }
+ this.initializeBuffers();
+ }
+ }
+ else {
+ throw(new Error("Invalid settings specified for the resampler."));
+ }
+}
+Resampler.prototype.compileLinearInterpolationFunction = function () {
+ var toCompile = "var outputOffset = 0;\
+ if (bufferLength > 0) {\
+ var buffer = this.inputBuffer;\
+ var weight = this.lastWeight;\
+ var firstWeight = 0;\
+ var secondWeight = 0;\
+ var sourceOffset = 0;\
+ var outputOffset = 0;\
+ var outputBuffer = this.outputBuffer;\
+ for (; weight < 1; weight += " + this.ratioWeight + ") {\
+ secondWeight = weight % 1;\
+ firstWeight = 1 - secondWeight;";
+ for (var channel = 0; channel < this.channels; ++channel) {
+ toCompile += "outputBuffer[outputOffset++] = (this.lastOutput[" + channel + "] * firstWeight) + (buffer[" + channel + "] * secondWeight);";
+ }
+ toCompile += "}\
+ weight -= 1;\
+ for (bufferLength -= " + this.channels + ", sourceOffset = Math.floor(weight) * " + this.channels + "; sourceOffset < bufferLength;) {\
+ secondWeight = weight % 1;\
+ firstWeight = 1 - secondWeight;";
+ for (var channel = 0; channel < this.channels; ++channel) {
+ toCompile += "outputBuffer[outputOffset++] = (buffer[sourceOffset" + ((channel > 0) ? (" + " + channel) : "") + "] * firstWeight) + (buffer[sourceOffset + " + (this.channels + channel) + "] * secondWeight);";
+ }
+ toCompile += "weight += " + this.ratioWeight + ";\
+ sourceOffset = Math.floor(weight) * " + this.channels + ";\
+ }";
+ for (var channel = 0; channel < this.channels; ++channel) {
+ toCompile += "this.lastOutput[" + channel + "] = buffer[sourceOffset++];";
+ }
+ toCompile += "this.lastWeight = weight % 1;\
+ }\
+ return outputOffset;";
+ this.resampler = Function("bufferLength", toCompile);
+}
+Resampler.prototype.compileMultiTapFunction = function () {
+ var toCompile = "var outputOffset = 0;\
+ if (bufferLength > 0) {\
+ var buffer = this.inputBuffer;\
+ var weight = 0;";
+ for (var channel = 0; channel < this.channels; ++channel) {
+ toCompile += "var output" + channel + " = 0;"
+ }
+ toCompile += "var actualPosition = 0;\
+ var amountToNext = 0;\
+ var alreadyProcessedTail = !this.tailExists;\
+ this.tailExists = false;\
+ var outputBuffer = this.outputBuffer;\
+ var currentPosition = 0;\
+ do {\
+ if (alreadyProcessedTail) {\
+ weight = " + this.ratioWeight + ";";
+ for (channel = 0; channel < this.channels; ++channel) {
+ toCompile += "output" + channel + " = 0;"
+ }
+ toCompile += "}\
+ else {\
+ weight = this.lastWeight;";
+ for (channel = 0; channel < this.channels; ++channel) {
+ toCompile += "output" + channel + " = this.lastOutput[" + channel + "];"
+ }
+ toCompile += "alreadyProcessedTail = true;\
+ }\
+ while (weight > 0 && actualPosition < bufferLength) {\
+ amountToNext = 1 + actualPosition - currentPosition;\
+ if (weight >= amountToNext) {";
+ for (channel = 0; channel < this.channels; ++channel) {
+ toCompile += "output" + channel + " += buffer[actualPosition++] * amountToNext;"
+ }
+ toCompile += "currentPosition = actualPosition;\
+ weight -= amountToNext;\
+ }\
+ else {";
+ for (channel = 0; channel < this.channels; ++channel) {
+ toCompile += "output" + channel + " += buffer[actualPosition" + ((channel > 0) ? (" + " + channel) : "") + "] * weight;"
+ }
+ toCompile += "currentPosition += weight;\
+ weight = 0;\
+ break;\
+ }\
+ }\
+ if (weight <= 0) {";
+ for (channel = 0; channel < this.channels; ++channel) {
+ toCompile += "outputBuffer[outputOffset++] = output" + channel + " / " + this.ratioWeight + ";"
+ }
+ toCompile += "}\
+ else {\
+ this.lastWeight = weight;";
+ for (channel = 0; channel < this.channels; ++channel) {
+ toCompile += "this.lastOutput[" + channel + "] = output" + channel + ";"
+ }
+ toCompile += "this.tailExists = true;\
+ break;\
+ }\
+ } while (actualPosition < bufferLength);\
+ }\
+ return outputOffset;";
+ this.resampler = Function("bufferLength", toCompile);
+}
+Resampler.prototype.bypassResampler = function (upTo) {
+ return upTo;
+}
+Resampler.prototype.initializeBuffers = function () {
+ //Initialize the internal buffer:
+ var outputBufferSize = (Math.ceil(this.inputBuffer.length * this.toSampleRate / this.fromSampleRate / this.channels * 1.000000476837158203125) * this.channels) + this.channels;
+ try {
+ this.outputBuffer = new Float32Array(outputBufferSize);
+ this.lastOutput = new Float32Array(this.channels);
+ }
+ catch (error) {
+ this.outputBuffer = [];
+ this.lastOutput = [];
+ }
+}
diff --git a/scripts/janusplayer.js b/scripts/janusplayer.js
new file mode 100644
index 0000000000000000000000000000000000000000..247d428448c8c3c747da96adfc9e6e186b83b840
--- /dev/null
+++ b/scripts/janusplayer.js
@@ -0,0 +1,65 @@
+elation.require(['engine.things.player', 'janusweb.JanusVOIP', 'ui.button'], function() {
+ elation.requireCSS('janusweb.janusplayer');
+
+ elation.component.add('engine.things.janusplayer', function() {
+ this.postinit = function() {
+ elation.engine.things.janusplayer.extendclass.postinit.call(this);
+ this.controlstate2 = this.engine.systems.controls.addContext('janusplayer', {
+ 'voip_active': ['keyboard_v,keyboard_shift_v', elation.bind(this, this.activateVOIP)],
+ 'browse_back': ['gamepad_0_button_4', elation.bind(this, this.browseBack)],
+ 'browse_forward': ['gamepad_0_button_5', elation.bind(this, this.browseForward)],
+ });
+ this.voip = new JanusVOIPRecorder({audioScale: 1024});
+ this.voipqueue = [];
+ this.voipbutton = elation.ui.button({append: document.body, classname: 'janusweb_voip', label: 'VOIP'});
+ elation.events.add(this.voipbutton, 'mousedown,touchstart', elation.bind(this.voip, this.voip.start));
+ elation.events.add(this.voipbutton, 'mouseup,touchend', elation.bind(this.voip, this.voip.stop));
+ elation.events.add(this.voip, 'voip_start', elation.bind(this, this.handleVOIPStart));
+ elation.events.add(this.voip, 'voip_stop', elation.bind(this, this.handleVOIPStop));
+ elation.events.add(this.voip, 'voip_data', elation.bind(this, this.handleVOIPData));
+ elation.events.add(this.voip, 'voip_error', elation.bind(this, this.handleVOIPError));
+ }
+ this.enable = function() {
+ elation.engine.things.janusplayer.extendclass.enable.call(this);
+ this.engine.systems.controls.activateContext('janusplayer');
+ }
+ this.disable = function() {
+ this.engine.systems.controls.deactivateContext('janusplayer');
+ elation.engine.things.janusplayer.extendclass.disable.call(this);
+ }
+ this.activateVOIP = function(ev) {
+ var on = (ev.value == 1);
+ if (on) {
+ this.voip.start();
+ } else {
+ this.voip.stop();
+ }
+ }
+ this.handleVOIPStart = function() {
+ this.voipbutton.addclass('state_recording');
+ elation.events.fire('janusvr_voip_start');
+ }
+ this.handleVOIPStop = function() {
+ elation.events.fire('janusvr_voip_stop');
+ this.voipbutton.removeclass('state_recording');
+ }
+ this.handleVOIPData = function(ev) {
+ this.voipqueue.push(ev.data);
+ }
+ this.handleVOIPError = function(ev) {
+ this.voipbutton.addclass('state_error');
+ this.voipbutton.setTitle(ev.data.name + ': ' + ev.data.message);
+ elation.events.fire('janusvr_voip_error');
+ }
+ this.browseBack = function(ev) {
+ if (ev.value == 1) {
+ history.go(-1);
+ }
+ }
+ this.browseForward = function(ev) {
+ if (ev.value == 1) {
+ history.go(1);
+ }
+ }
+ }, elation.engine.things.player);
+});
-----END OF PAGE-----