view WAVE.js @ 1116:c44fbf72f7f2

All interfaces support comment boxes. Comment box identification matches presented tag (for instance, AB will be Comment on fragment A, rather than 1). Tighter buffer loading protocol, audioObjects register with the buffer rather than checking for buffer existence (which can be buggy depending on the buffer state). Buffers now have a state to ensure exact location in loading chain (downloading, decoding, LUFS, ready).
author Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk>
date Fri, 29 Jan 2016 11:11:57 +0000
parents
children c0022a09c4f6
line wrap: on
line source
// Decode and perform WAVE file byte level manipulation

find_subarray = function(arr,subarr) {
    var arr_length = arr.length;
    var subarr_length = subarr.length;
    var last_check_index = arr_length - subarr_length;
    
    positionLoop:
    for (var i=0; i <= last_check_index; i++)
    {
        for (var j=0; j< subarr_length; j++)
        {
            if (arr[i + j] !== subarr[j]) {
                continue positionLoop;
            }
        }
        return i;
    }
    return -1;
};

function WAVE()
{
    // The WAVE file object
    this.status == 'WAVE_DECLARED'
    
    this.decoded_data = null;
    
    this.RIFF = String(); //ChunkID
	this.size; //ChunkSize
	this.FT_Header; //Format
	this.fmt_marker; //Subchunk1ID
	this.formatDataLength; //Subchunk1Size
	this.type; //AudioFormat
	this.num_channels; //NumChannels
	this.sample_rate; //SampleRate
	this.byte_rate; //ByteRate
	this.block_align; //BlockAlign
	this.bits_per_sample; //BitsPerSample
	this.data_header; //Subchunk2ID
	this.data_size; //Subchunk2Size
    this.num_samples;
    
    this.open = function(IOArrayBuffer)
    {
        var IOView8 = new Uint8Array(IOArrayBuffer);
        IOView8.subarray(0,4).forEach(function(i){
            var char = String.fromCharCode(i);
            this.RIFF = this.RIFF.concat(char);
        },this);
        if (this.RIFF != 'RIFF')
        {
            console.log('WAVE ERR - Not a RIFF file');
            return 1;
        }
        this.size = 0;
        IOView8.subarray(4,8).forEach(function(i,a){this.size += Number(i)<<(8*a);},this);
        this.FT_Header = String();
        IOView8.subarray(8,12).forEach(function(i){this.FT_Header = this.FT_Header.concat(String.fromCharCode(i));},this);
        this.fmt_marker = String();
        IOView8.subarray(12,16).forEach(function(i){this.fmt_marker = this.fmt_marker.concat(String.fromCharCode(i));},this);
        this.formatDataLength = 0;
        IOView8.subarray(16,20).forEach(function(i,a){this.formatDataLength += Number(i)<<(8*a);},this);
        this.type = 0;
        IOView8.subarray(20,22).forEach(function(i,a){this.type += Number(i)<<(8*a);},this);
        this.num_channels = 0;
        IOView8.subarray(22,24).forEach(function(i,a){this.num_channels += Number(i)<<(8*a);},this);
        this.sample_rate = 0;
        IOView8.subarray(24,28).forEach(function(i,a){this.sample_rate += Number(i)<<(8*a);},this);
        this.byte_rate = 0;
        IOView8.subarray(28,32).forEach(function(i,a){this.byte_rate += Number(i)<<(8*a);},this);
        this.block_align = 0;
        IOView8.subarray(32,34).forEach(function(i,a){this.block_align += Number(i)<<(8*a);},this);
        this.bits_per_sample = 0;
        IOView8.subarray(34,36).forEach(function(i,a){this.bits_per_sample += Number(i)<<(8*a);},this);
        
        // Find the data header first
        var data_start = find_subarray(IOView8,[100, 97, 116, 97]);
        
        this.data_header = String();
        IOView8.subarray(data_start,data_start+4).forEach(function(i){this.data_header = this.data_header.concat(String.fromCharCode(i));},this);
        this.data_size = 0;
        IOView8.subarray(data_start+4,data_start+8).forEach(function(i,a){this.data_size += Number(i)<<(8*a);},this);
        
        this.num_samples = this.data_size / this.block_align;
        
        this.decoded_data = [];
        if (this.type != 1 && this.type != 3) {
            console.log("Neither PCM nor IEEE float, cannot decode");
            return 1;
        }
        for (var c=0; c<this.num_channels; c++)
        {
            this.decoded_data.push(new Float32Array(this.num_samples));
        }
        var sampleDataOffset = data_start+8;
        
        // Now need to decode the data from sampleDataOffset
        // Data is always interleved
        var data_view;
        if (this.type == 3)
        {
            // Already in float
            if (this.bits_per_sample == 32) {
                data_view = new Float32Array(IOArrayBuffer.slice(sampleDataOffset,sampleDataOffset+this.data_size));
            } else if (this.bits_per_sample == 64) {
                data_view = new Float64Array(IOArrayBuffer.slice(sampleDataOffset,sampleDataOffset+this.data_size));
            }
        } else if (this.type == 1)
        {
            data_view = new Float32Array(this.num_samples*this.num_channels);
            integerConvert(new Uint8Array(IOArrayBuffer.slice(sampleDataOffset,sampleDataOffset+this.data_size)),data_view,this.bits_per_sample/8);
        }
        deInterlace(data_view,this.decoded_data);
        return 0;
    };
}

function deInterlace(src_array, dst_array)
{
    var number = src_array.length;
    var channels = dst_array.length;
    var channel_index = 0;
    var dst_index = 0;
    for (var n=0; n<number; n++)
    {
        dst_array[channel_index][dst_index] = src_array[n];
        channel_index++;
        if (channel_index >= channels) {
            channel_index = 0;
            dst_index++;
        }
    }
}

function integerConvert(srcView,dstView,srcBytes)
{
    //Convert integers of a Uint8Array of certain byte length into a Float32Array
    var number = dstView.length;
    var outBits = srcBytes*8;
    var endShift = 32 - outBits;
    if (srcView.length != dstView.length*srcBytes)
    {
        return -1;
    }
    for (var n=0; n<number; n++)
    {
        var intData;
        var srcIndex = n*srcBytes;
        srcView.subarray(srcIndex,srcIndex+srcBytes).forEach(function(i,a){intData += Number(i)<<(8*a);},this);
        intData = (intData << (endShift));
        dstView[n] = intData / 2147483648;
    }
}