[audio-first-try]
This commit is contained in:
parent
ffaa2b0019
commit
05eb18d0ad
|
@ -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<Float32Array> Input
|
||||
*
|
||||
* @return buf16<Int16Array> 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<Int16Array> Input
|
||||
*
|
||||
* @return buf32<Float32Array> 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');
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -62,6 +62,10 @@ Notification.requestPermission();
|
|||
window.DEBUG_MOD = false;
|
||||
|
||||
|
||||
// audio management
|
||||
window.AudioManager = new (require('./lib/audio-manager.js').default)();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue