Audio Float32 to Int16 conversion in javascript

2.3k views Asked by At

I am trying to create a noise cancellation filter in WebRtc using my C library compiled into wasm and called from Javascript.

I am able to capture the pcm audio using WebAudioApi and process the frame with wasm.

My library accepts input in int16 only.

Here is my code : I tried 2 Methods

Method 1 :

navigator.mediaDevices.getUserMedia(constraints).then(function success(stream) {
var audiocontext;
var audiosource;
var audiopreprocessnode;

 audiocontext = new (window.AudioContext || window.webkitAudioContext)();
 audiosource = audiocontext.createMediaStreamSource(stream);
 audioPreprocessNode = audioCtx.createScriptProcessor(2048,1,1);

 audiosource.connect(audioPreprocessNode);
 audioPreprocessNode.connect(audioCtx.destination);
 
 audioPreprocessNode.onaudioprocess = function(e) {
 
 var input = new Int16Array(e.inputbuffer.getChannelData(0));
 
 console.log(input.length); // prints 4096

 var denoised_array = Module["_denoise"](input);

 var output = new Float32Array(denoised_array);
 
 console.log(output.length);  // prints 2048
 
 e.outputBuffer.getChannelData(0).set(output); 
 
 
 }
   
}

Advantages of the method is It preserves the no of bytes , so no data loss will be there

But when i convert back it to Float32Array the float values goes beyond tha audiobuffer limit value [-1,1]. So no audio data being pushed.

Method 2:


function floatTo16Bit(inputArray){
    var output = new Int16Array(2048);
    for (var i = 0; i < inputArray.length; i++){
        var s = Math.max(-1, Math.min(1, inputArray[i]));
        output[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;
    }
    return output;
}
function int16ToFloat32(inputArray) {
    var output = new Float32Array(2048);
    for (var i = 0; i < 2048; i++) {
        var int = inputArray[i];
        var float = (int >= 0x8000) ? -(0x10000 - int) / 0x8000 : int / 0x7FFF;
        output[i] = float;
    }
    return output;
}

navigator.mediaDevices.getUserMedia(constraints).then(function success(stream) {
var audiocontext;
var audiosource;
var audiopreprocessnode;

 audiocontext = new (window.AudioContext || window.webkitAudioContext)();
 audiosource = audiocontext.createMediaStreamSource(stream);
 audioPreprocessNode = audioCtx.createScriptProcessor(2048,1,1);

 audiosource.connect(audioPreprocessNode);
 audioPreprocessNode.connect(audioCtx.destination);
 
 audioPreprocessNode.onaudioprocess = function(e) {
 
 var input = floatTo16Bit(e.inputbuffer.getChannelData(0));
 
 console.log(input.length); // prints 2048

 var denoised_array = Module["_denoise"](input);

 var output = int16ToFloat32(denoised_array);
 
 console.log(output.length);  // prints 2048
 
 e.outputBuffer.getChannelData(0).set(output); 
 
 
 }
   
}

Advantages of this method is it converts back values within the range[-1,1].

But audio quality(distortion) suffer a lot due to loss of bytes.

Is there anyway to preserver the bytes and convert float32 - int16 and back efficiently.

Anyhelp would be greatfull.

1

There are 1 answers

4
Raymond Toy On

Not sure what you mean by "preserve the bytes", but the conversion from float32 to int16 will necessarily lose information. But it shouldn't be so much that there's huge distortion. After all, CDs are only 16 bits.

For simplicity, I'd convert float to int16 by clamping the float to [-1, 1], as you've done, and then just multiplying by 32768 and taking the integer part. This gives a signed 16-bit integer.

To go the other way, just divide the int16 value by 32768. (Again, this assumes you have signed 16-bit numbers.)