annotate WAVE.js @ 1357:f764e8e40a96

Fixed loudness. Now passes all EBU 3341 tests for Integrated loudness. Fixed WAVE decoder error for non-mono sources.
author Nicholas Jillings <nickjillings@users.noreply.github.com>
date Tue, 26 Jan 2016 11:01:55 +0000
parents 41574c5bc5ee
children 1b038c226444 c0022a09c4f6
rev   line source
nickjillings@1353 1 // Decode and perform WAVE file byte level manipulation
nickjillings@1353 2
nickjillings@1353 3 find_subarray = function(arr,subarr) {
nickjillings@1353 4 var arr_length = arr.length;
nickjillings@1353 5 var subarr_length = subarr.length;
nickjillings@1353 6 var last_check_index = arr_length - subarr_length;
nickjillings@1353 7
nickjillings@1353 8 positionLoop:
nickjillings@1353 9 for (var i=0; i <= last_check_index; i++)
nickjillings@1353 10 {
nickjillings@1353 11 for (var j=0; j< subarr_length; j++)
nickjillings@1353 12 {
nickjillings@1353 13 if (arr[i + j] !== subarr[j]) {
nickjillings@1353 14 continue positionLoop;
nickjillings@1353 15 }
nickjillings@1353 16 }
nickjillings@1353 17 return i;
nickjillings@1353 18 }
nickjillings@1353 19 return -1;
nickjillings@1353 20 };
nickjillings@1353 21
nickjillings@1353 22 function WAVE()
nickjillings@1353 23 {
nickjillings@1353 24 // The WAVE file object
nickjillings@1353 25 this.status == 'WAVE_DECLARED'
nickjillings@1353 26
nickjillings@1353 27 this.decoded_data = null;
nickjillings@1353 28
nickjillings@1353 29 this.RIFF = String(); //ChunkID
nickjillings@1353 30 this.size; //ChunkSize
nickjillings@1353 31 this.FT_Header; //Format
nickjillings@1353 32 this.fmt_marker; //Subchunk1ID
nickjillings@1353 33 this.formatDataLength; //Subchunk1Size
nickjillings@1353 34 this.type; //AudioFormat
nickjillings@1353 35 this.num_channels; //NumChannels
nickjillings@1353 36 this.sample_rate; //SampleRate
nickjillings@1353 37 this.byte_rate; //ByteRate
nickjillings@1353 38 this.block_align; //BlockAlign
nickjillings@1353 39 this.bits_per_sample; //BitsPerSample
nickjillings@1353 40 this.data_header; //Subchunk2ID
nickjillings@1353 41 this.data_size; //Subchunk2Size
nickjillings@1353 42 this.num_samples;
nickjillings@1353 43
nickjillings@1353 44 this.open = function(IOArrayBuffer)
nickjillings@1353 45 {
nickjillings@1353 46 var IOView8 = new Uint8Array(IOArrayBuffer);
nickjillings@1353 47 IOView8.subarray(0,4).forEach(function(i){
nickjillings@1353 48 var char = String.fromCharCode(i);
nickjillings@1353 49 this.RIFF = this.RIFF.concat(char);
nickjillings@1353 50 },this);
nickjillings@1353 51 if (this.RIFF != 'RIFF')
nickjillings@1353 52 {
nickjillings@1353 53 console.log('WAVE ERR - Not a RIFF file');
nickjillings@1353 54 return 1;
nickjillings@1353 55 }
nickjillings@1353 56 this.size = 0;
nickjillings@1353 57 IOView8.subarray(4,8).forEach(function(i,a){this.size += Number(i)<<(8*a);},this);
nickjillings@1353 58 this.FT_Header = String();
nickjillings@1353 59 IOView8.subarray(8,12).forEach(function(i){this.FT_Header = this.FT_Header.concat(String.fromCharCode(i));},this);
nickjillings@1353 60 this.fmt_marker = String();
nickjillings@1353 61 IOView8.subarray(12,16).forEach(function(i){this.fmt_marker = this.fmt_marker.concat(String.fromCharCode(i));},this);
nickjillings@1353 62 this.formatDataLength = 0;
nickjillings@1353 63 IOView8.subarray(16,20).forEach(function(i,a){this.formatDataLength += Number(i)<<(8*a);},this);
nickjillings@1353 64 this.type = 0;
nickjillings@1353 65 IOView8.subarray(20,22).forEach(function(i,a){this.type += Number(i)<<(8*a);},this);
nickjillings@1353 66 this.num_channels = 0;
nickjillings@1353 67 IOView8.subarray(22,24).forEach(function(i,a){this.num_channels += Number(i)<<(8*a);},this);
nickjillings@1353 68 this.sample_rate = 0;
nickjillings@1353 69 IOView8.subarray(24,28).forEach(function(i,a){this.sample_rate += Number(i)<<(8*a);},this);
nickjillings@1353 70 this.byte_rate = 0;
nickjillings@1353 71 IOView8.subarray(28,32).forEach(function(i,a){this.byte_rate += Number(i)<<(8*a);},this);
nickjillings@1353 72 this.block_align = 0;
nickjillings@1353 73 IOView8.subarray(32,34).forEach(function(i,a){this.block_align += Number(i)<<(8*a);},this);
nickjillings@1353 74 this.bits_per_sample = 0;
nickjillings@1353 75 IOView8.subarray(34,36).forEach(function(i,a){this.bits_per_sample += Number(i)<<(8*a);},this);
nickjillings@1353 76
nickjillings@1353 77 // Find the data header first
nickjillings@1353 78 var data_start = find_subarray(IOView8,[100, 97, 116, 97]);
nickjillings@1353 79
nickjillings@1353 80 this.data_header = String();
nickjillings@1353 81 IOView8.subarray(data_start,data_start+4).forEach(function(i){this.data_header = this.data_header.concat(String.fromCharCode(i));},this);
nickjillings@1353 82 this.data_size = 0;
nickjillings@1353 83 IOView8.subarray(data_start+4,data_start+8).forEach(function(i,a){this.data_size += Number(i)<<(8*a);},this);
nickjillings@1353 84
nickjillings@1353 85 this.num_samples = this.data_size / this.block_align;
nickjillings@1353 86
nickjillings@1353 87 this.decoded_data = [];
nickjillings@1353 88 if (this.type != 1 && this.type != 3) {
nickjillings@1353 89 console.log("Neither PCM nor IEEE float, cannot decode");
nickjillings@1353 90 return 1;
nickjillings@1353 91 }
nickjillings@1353 92 for (var c=0; c<this.num_channels; c++)
nickjillings@1353 93 {
nickjillings@1353 94 this.decoded_data.push(new Float32Array(this.num_samples));
nickjillings@1353 95 }
nickjillings@1353 96 var sampleDataOffset = data_start+8;
nickjillings@1353 97
nickjillings@1353 98 // Now need to decode the data from sampleDataOffset
nickjillings@1353 99 // Data is always interleved
nickjillings@1353 100 var data_view;
nickjillings@1353 101 if (this.type == 3)
nickjillings@1353 102 {
nickjillings@1353 103 // Already in float
nickjillings@1353 104 if (this.bits_per_sample == 32) {
nickjillings@1353 105 data_view = new Float32Array(IOArrayBuffer.slice(sampleDataOffset,sampleDataOffset+this.data_size));
nickjillings@1353 106 } else if (this.bits_per_sample == 64) {
nickjillings@1353 107 data_view = new Float64Array(IOArrayBuffer.slice(sampleDataOffset,sampleDataOffset+this.data_size));
nickjillings@1353 108 }
nickjillings@1353 109 } else if (this.type == 1)
nickjillings@1353 110 {
nickjillings@1357 111 data_view = new Float32Array(this.num_samples*this.num_channels);
nickjillings@1353 112 integerConvert(new Uint8Array(IOArrayBuffer.slice(sampleDataOffset,sampleDataOffset+this.data_size)),data_view,this.bits_per_sample/8);
nickjillings@1353 113 }
nickjillings@1353 114 deInterlace(data_view,this.decoded_data);
nickjillings@1353 115 return 0;
nickjillings@1353 116 };
nickjillings@1353 117 }
nickjillings@1353 118
nickjillings@1353 119 function deInterlace(src_array, dst_array)
nickjillings@1353 120 {
nickjillings@1353 121 var number = src_array.length;
nickjillings@1353 122 var channels = dst_array.length;
nickjillings@1353 123 var channel_index = 0;
nickjillings@1353 124 var dst_index = 0;
nickjillings@1353 125 for (var n=0; n<number; n++)
nickjillings@1353 126 {
nickjillings@1353 127 dst_array[channel_index][dst_index] = src_array[n];
nickjillings@1353 128 channel_index++;
nickjillings@1353 129 if (channel_index >= channels) {
nickjillings@1353 130 channel_index = 0;
nickjillings@1353 131 dst_index++;
nickjillings@1353 132 }
nickjillings@1353 133 }
nickjillings@1353 134 }
nickjillings@1353 135
nickjillings@1353 136 function integerConvert(srcView,dstView,srcBytes)
nickjillings@1353 137 {
nickjillings@1353 138 //Convert integers of a Uint8Array of certain byte length into a Float32Array
nickjillings@1353 139 var number = dstView.length;
nickjillings@1353 140 var outBits = srcBytes*8;
nickjillings@1353 141 var endShift = 32 - outBits;
nickjillings@1353 142 if (srcView.length != dstView.length*srcBytes)
nickjillings@1353 143 {
nickjillings@1353 144 return -1;
nickjillings@1353 145 }
nickjillings@1353 146 for (var n=0; n<number; n++)
nickjillings@1353 147 {
nickjillings@1353 148 var intData;
nickjillings@1353 149 var srcIndex = n*srcBytes;
nickjillings@1353 150 srcView.subarray(srcIndex,srcIndex+srcBytes).forEach(function(i,a){intData += Number(i)<<(8*a);},this);
nickjillings@1353 151 intData = (intData << (endShift));
nickjillings@1353 152 dstView[n] = intData / 2147483648;
nickjillings@1353 153 }
nickjillings@1353 154 }