From 05eb18d0addd3f9f267cdd8c72817b16796419c4 Mon Sep 17 00:00:00 2001 From: xdrm-brackets Date: Fri, 6 Apr 2018 00:54:13 +0200 Subject: [PATCH] [audio-first-try] --- webpack/lib/audio-manager.js | 242 +++++++++++++++++++++++++++++++++++ webpack/setup.js | 4 + 2 files changed, 246 insertions(+) create mode 100644 webpack/lib/audio-manager.js diff --git a/webpack/lib/audio-manager.js b/webpack/lib/audio-manager.js new file mode 100644 index 0000000..f924944 --- /dev/null +++ b/webpack/lib/audio-manager.js @@ -0,0 +1,242 @@ +export default class AudioManager{ + + static get BUFFER_SIZE(){ return 2048; } + + + constructor(){ + + /* (1) Build Audio Context */ + this.ctx = new (window.AudioContext || window.webkitAudioContext)(); + this.gain = this.ctx.createGain(); + + /* (3) Create input (typically recorder) */ + this.input = null; + + /* (4) Create network I/O controllers (WebSocket) */ + this.network = { + out: this.ctx.createScriptProcessor(AudioManager.BUFFER_SIZE, 1, 1), + in: null // will contain NETWORK:IN source node + }; + + /* (5) Bind network routines */ + this.network.out.onaudioprocess = this.send.bind(this); + + /* (5) Specify node chains */ + this.node = { + input: [ this.ctx.createAnalyser() ], // INPUT connects to it + netout: [] // INPUT chains through it until NETOUT + }; + + /* (6) Create output + bind gain */ + this.output = this.ctx.destination; + + /* (7) Create websocket connection */ + this.ws = new WebSocket('wss://ws.douscord.xdrm.io/audio/2'); + let self = this; + + /* (8) Manage websocket requests */ + this._ws = { + stack: [], + send(_data){ + if( self.ws.readyState !== 1 ) // not connected -> stack + return self._ws.stack.push(_data); + + self.ws.send(_data); + }, + }; + + /* (9) Manage websocket message stack */ + this.ws.onopen = function(){ + while( this._ws.stack.length > 0 ) + this.ws.send(this._ws.stack.shift()); + }.bind(this); + + + /* (10) Manage websocket responses */ + this.ws.onmessage = function(_blob){ + + if( !(_blob instanceof Blob) ) + return console.warn('NIQUE'); + + let fr = new FileReader(); + + fr.onload = function(){ + let buf16 = new Int16Array(this.result); + self.receive(bu16).bind(self); + }; + + fr.readAsArrayBuffer(_blob); + + } + + + + } + + + /* (1) Binds an input stream + * + ---------------------------------------------------------*/ + bind(){ + + let current_node = null; + + + /* (1) Bind INPUT ------> NETWORK:OUT circuit + ---------------------------------------------------------*/ + current_node = this.input; + + /* (1) Connect INPUT to input list */ + for( let node of this.node.input ) + current_node.connect(node); + + /* (2) Chain INPUT to input chain */ + for( let node of this.node.netout ){ + current_node.connect(node); + current_node = node; + } + + /* (3) Finally connect to NETWORK:OUT */ + current_node.connect(this.network.out); + + + /* (2) Bind NETWORK:IN ------> OUTPUT circuit + ---------------------------------------------------------*/ + // WILL BE DONE ON receive() + + /* (1) Finally connect to OUTPUT */ + current_node.connect(this.gain); + this.gain.connect(this.output); + + } + + + /* (2) Binds an input stream + * + ---------------------------------------------------------*/ + bindRecorderStream(_stream){ + + /* (1) Bind audio stream */ + console.warn(_stream); + this.input = this.ctx.createMediaStreamSource(_stream); + + } + + + /* (3) Sharing process implementation + * + ---------------------------------------------------------*/ + send(_audioprocess){ + /*DEBUG*///console.warn('time of', 16*2048/8, 'bytes in ', new Date().getTime()-window.timer); + /*DEBUG*///window.timer = new Date().getTime(); + + let buf32 = new Float32Array(AudioManager.BUFFER_SIZE); + _audioprocess.inputBuffer.copyFromChannel(buf32, 0); + + let buf16 = this.f32toi16(buf32); + + this._ws.send(buf16); + + } + + receive(_buffer){ + + /* (1) Convert to Float32Array */ + let buf32 = this.i16tof32(_buffer); + + /* (2) Create source node */ + this.network.in = this.ctx.createBufferSource(); + + /* (3) Create buffer and dump data */ + let input_buffer = this.ctx.createBuffer(1, AudioManager.BUFFER_SIZE, this.ctx.sampleRate); + input_buffer.getChannelData(0).set(buf32); + + /* (4) Pass buffer to source node */ + this.network.in.buffer = input_buffer; + + /* (5) Connect and play audio */ + this.network.in.connect(this.gain); + + } + + + /* (4) Convert Float32Array to Int16Array + * + * @buf32 Input + * + * @return buf16 Converted output + * + ---------------------------------------------------------*/ + f32toi16(buf32){ + + /* (1) Initialise output */ + let buf16 = new Int16Array(buf32.length); + + /* (2) Initialize loop */ + let i = 0, l = buf32.length; + + /* (3) Convert each value */ + for( ; i < l ; i++ ) + buf16[i] = (buf32[i] < 0) ? 0x8000 * buf32[i] : 0x7FFF * buf32[i]; + + return buf16; + } + + + /* (2) Convert Int16Array to Float32Array + * + * @buf16 Input + * + * @return buf32 Converted output + * + ---------------------------------------------------------*/ + i16tof32(buf16){ + + /* (1) Initialise output */ + let buf32 = new Float32Array(buf16.length); + + /* (2) Initialize loop */ + let i = 0, l = buf16.length; + + /* (3) Convert each value */ + for( ; i < l ; i++ ) + buf32[i] = (buf16[i] >= 0x8000) ? -(0x10000 * buf16[i])/0x8000 : buf16[i] / 0x7FFF; + + return buf32; + } + + + /* (x) Access microphone + launch all + * + ---------------------------------------------------------*/ + launch(){ + + window.recorder = null; + + if( navigator.mediaDevices && navigator.mediaDevices.getUserMedia ){ + + navigator.mediaDevices.getUserMedia({ audio: true }) + .then( stream => { + + recorder = new MediaRecorder(stream); + this.bindRecorderStream(stream); + this.bind(); + + recorder.onstart = () => console.warn('start'); + recorder.onstop = () => { + recorder.stream.getTracks().map( t => t.stop() ); + }; + + // start recording + recorder.start(); + + }) + .catch( e => console.warn('error getting audio stream', e) ); + + }else + console.warn('getUserMedia() not supported'); + + } + + +} \ No newline at end of file diff --git a/webpack/setup.js b/webpack/setup.js index 14a749b..d576453 100644 --- a/webpack/setup.js +++ b/webpack/setup.js @@ -62,6 +62,10 @@ Notification.requestPermission(); window.DEBUG_MOD = false; +// audio management +window.AudioManager = new (require('./lib/audio-manager.js').default)(); + +