Paulie
Paulie

Reputation: 2422

Javascript: Converting from Int16 to Float32

I'm trying to put a WAV file in an AudioBuffer so that I can manipulate it. I've created WAV files from an AudioBuffer before, and that required converting a Float32Array to a DataView containing Int16 values. I used this handy function I picked up:

function floatTo16BitPCM(output, offset, input){
    for (var i = 0; i < input.length; i++, offset+=2){
        var s = Math.max(-1, Math.min(1, input[i]));
        output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
    }
}

Well, all I need to do now is reverse this (the WAV files are loaded from the server, so I don't have the original data anymore). I can't figure out what is actually happening in that function or how the data is transformed.

Upvotes: 3

Views: 5446

Answers (2)

Paulie
Paulie

Reputation: 2422

Here's what seems to work. I load the data in an ajax call with the response type "arraybuffer". Otherwise, the response ends up being a string and it is a mess to work with. Then I convert to a 16-bit array. Then I convert that to a Float32 array in the way that works with the WAV encoding. I also needed to throw away the WAV's header, and some metadata at the end of it.

// These are ready to be copied into an AudioBufferSourceNode's channel data.
var theWavDataInFloat32;

function floatTo16Bit(inputArray, startIndex){
    var output = new Uint16Array(inputArray.length-startIndex);
    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;
}

// This is passed in an unsigned 16-bit integer array. It is converted to a 32-bit float array.
// The first startIndex items are skipped, and only 'length' number of items is converted.
function int16ToFloat32(inputArray, startIndex, length) {
    var output = new Float32Array(inputArray.length-startIndex);
    for (var i = startIndex; i < length; i++) {
        var int = inputArray[i];
        // If the high bit is on, then it is a negative number, and actually counts backwards.
        var float = (int >= 0x8000) ? -(0x10000 - int) / 0x8000 : int / 0x7FFF;
        output[i] = float;
    }
    return output;
}

// TEST
var data = [ 65424, 18, 0, 32700, 33000, 1000, 50000 ];
var testDataInt = new Uint16Array(data);
var testDataFloat = int16ToFloat32(testDataInt, 0, data.length);
var testDataInt2 = floatTo16Bit(testDataFloat, 0);
// At this point testDataInt2 should be pretty close to the original data array (there is a little rounding.)

var xhr = new XMLHttpRequest();
xhr.open('GET', '/my-sound.wav', true);
xhr.responseType = 'arraybuffer';

xhr.onload = function(e) {
    if (this.status === 200) {
        // This retrieves the entire wav file. We're only interested in the data portion.
        // At the beginning is 44 bytes (22 words) of header, and at the end is some metadata about the file.
        // The actual data length is held in bytes 40 - 44.
        var data = new Uint16Array(this.response);
        var length = (data[20] + data[21] * 0x10000) / 2; // The length is in bytes, but the array is 16 bits, so divide by 2.
        theWavDataInFloat32 = int16ToFloat32(data, 22, length);
    }
};

xhr.send();

Upvotes: 12

Jean-Claude Colette
Jean-Claude Colette

Reputation: 937

Each real data of the array input is reduced to the interval [-1, 1]

  • Math.min(1, x) gives x if x<=1 and 1 otherwise
  • Math.max(-1, y) gives y if y>=-1 and -1 otherwise

Then this real number between -1 and 1 is converted to a signed 16-bit integer.

Whether it is positive or negative multiplied by 32767 or -32768 then it retains only the integer part by a cast. This is equivalent to keep only 16 significant bits after the decimal point in the binary representation.

16 bits integers are stored in little Endian on two-byte in buffer, one after the other (according to the offset that advances two into two)

For the reverse operation, simply place two consecutive bytes in an int16. To convert it into a real distinguishing two cases following the sign and dividing by 32768 or 32767. But the operation will be done with data loss.

Upvotes: 3

Related Questions