nicholas@2224: // Decode and perform WAVE file byte level manipulation nicholas@2683: /*globals console, Uint8Array, Float32Array, Float64Array */ nicholas@2224: nicholas@2683: function find_subarray(arr, subarr) { nicholas@2224: var arr_length = arr.length; nicholas@2224: var subarr_length = subarr.length; nicholas@2224: var last_check_index = arr_length - subarr_length; nicholas@2538: nicholas@2224: positionLoop: nicholas@2538: for (var i = 0; i <= last_check_index; i++) { nicholas@2538: for (var j = 0; j < subarr_length; j++) { nicholas@2538: if (arr[i + j] !== subarr[j]) { nicholas@2538: continue positionLoop; nicholas@2538: } nicholas@2224: } nicholas@2538: return i; nicholas@2224: } nicholas@2224: return -1; nicholas@2683: } nicholas@2224: nicholas@2224: function convertToInteger(arr) { nicholas@2224: var value = 0; nicholas@2538: for (var i = 0; i < arr.length; i++) { nicholas@2538: value += arr[i] << (i * 8); nicholas@2224: } nicholas@2224: return value; nicholas@2224: } nicholas@2538: nicholas@2224: function convertToString(arr) { nicholas@2224: var str = ""; nicholas@2538: for (var i = 0; i < arr.length; i++) { nicholas@2224: str = str.concat(String.fromCharCode(arr[i])); nicholas@2224: } nicholas@2224: return str; nicholas@2224: } nicholas@2224: nicholas@2538: function WAVE() { nicholas@2224: // The WAVE file object nicholas@2683: this.status = 'WAVE_DECLARED'; nicholas@2538: nicholas@2224: this.decoded_data = null; nicholas@2538: nicholas@2224: this.RIFF = String(); //ChunkID nicholas@2683: this.size = undefined; //ChunkSize nicholas@2683: this.FT_Header = undefined; //Format nicholas@2683: this.fmt_marker = undefined; //Subchunk1ID nicholas@2683: this.formatDataLength = undefined; //Subchunk1Size nicholas@2683: this.type = undefined; //AudioFormat nicholas@2683: this.num_channels = undefined; //NumChannels nicholas@2683: this.sample_rate = undefined; //SampleRate nicholas@2683: this.byte_rate = undefined; //ByteRate nicholas@2683: this.block_align = undefined; //BlockAlign nicholas@2683: this.bits_per_sample = undefined; //BitsPerSample nicholas@2683: this.data_header = undefined; //Subchunk2ID nicholas@2683: this.data_size = undefined; //Subchunk2Size nicholas@2683: this.num_samples = undefined; nicholas@2538: nicholas@2538: this.open = function (IOArrayBuffer) { nicholas@2224: var IOView8 = new Uint8Array(IOArrayBuffer); nicholas@2538: this.RIFF = convertToString(IOView8.subarray(0, 4)); nicholas@2538: if (this.RIFF != 'RIFF') { nicholas@2224: console.log('WAVE ERR - Not a RIFF file'); nicholas@2224: return 1; nicholas@2224: } nicholas@2538: this.size = convertToInteger(IOView8.subarray(4, 8)); nicholas@2538: this.FT_Header = convertToString(IOView8.subarray(8, 12)); nicholas@2538: this.fmt_marker = convertToString(IOView8.subarray(12, 16)); nicholas@2538: this.formatDataLength = convertToInteger(IOView8.subarray(16, 20)); nicholas@2538: this.type = convertToInteger(IOView8.subarray(20, 22)); nicholas@2538: this.num_channels = convertToInteger(IOView8.subarray(22, 24)); nicholas@2538: this.sample_rate = convertToInteger(IOView8.subarray(24, 28)); nicholas@2538: this.byte_rate = convertToInteger(IOView8.subarray(28, 32)); nicholas@2538: this.block_align = convertToInteger(IOView8.subarray(32, 34)); nicholas@2538: this.bits_per_sample = convertToInteger(IOView8.subarray(34, 36)); nicholas@2538: nicholas@2224: // Find the data header first nicholas@2538: var data_start = find_subarray(IOView8, [100, 97, 116, 97]); nicholas@2538: nicholas@2538: this.data_header = convertToString(IOView8.subarray(data_start, data_start + 4)); nicholas@2538: this.data_size = convertToInteger(IOView8.subarray(data_start + 4, data_start + 8)); nicholas@2538: nicholas@2224: this.num_samples = this.data_size / this.block_align; nicholas@2538: nicholas@2224: this.decoded_data = []; nicholas@2224: if (this.type != 1 && this.type != 3) { nicholas@2224: console.log("Neither PCM nor IEEE float, cannot decode"); nicholas@2224: return 1; nicholas@2224: } nicholas@2538: for (var c = 0; c < this.num_channels; c++) { nicholas@2224: this.decoded_data.push(new Float32Array(this.num_samples)); nicholas@2224: } nicholas@2538: var sampleDataOffset = data_start + 8; nicholas@2538: nicholas@2224: // Now need to decode the data from sampleDataOffset nicholas@2224: // Data is always interleved nicholas@2224: var data_view; nicholas@2538: if (this.type == 3) { nicholas@2224: // Already in float nicholas@2224: if (this.bits_per_sample == 32) { nicholas@2538: data_view = new Float32Array(IOArrayBuffer.slice(sampleDataOffset, sampleDataOffset + this.data_size)); nicholas@2224: } else if (this.bits_per_sample == 64) { nicholas@2538: data_view = new Float64Array(IOArrayBuffer.slice(sampleDataOffset, sampleDataOffset + this.data_size)); nicholas@2224: } nicholas@2538: } else if (this.type == 1) { nicholas@2538: data_view = new Float32Array(this.num_samples * this.num_channels); nicholas@2538: integerConvert(new Uint8Array(IOArrayBuffer.slice(sampleDataOffset, sampleDataOffset + this.data_size)), data_view, this.bits_per_sample / 8); nicholas@2224: } nicholas@2538: deInterlace(data_view, this.decoded_data); nicholas@2224: return 0; nicholas@2224: }; nicholas@2224: } nicholas@2224: nicholas@2538: function deInterlace(src_array, dst_array) { nicholas@2224: var number = src_array.length; nicholas@2224: var channels = dst_array.length; nicholas@2224: var channel_index = 0; nicholas@2224: var dst_index = 0; nicholas@2538: for (var n = 0; n < number; n++) { nicholas@2224: dst_array[channel_index][dst_index] = src_array[n]; nicholas@2224: channel_index++; nicholas@2224: if (channel_index >= channels) { nicholas@2224: channel_index = 0; nicholas@2224: dst_index++; nicholas@2224: } nicholas@2224: } nicholas@2224: } nicholas@2224: nicholas@2538: function integerConvert(srcView, dstView, srcBytes) { nicholas@2224: //Convert integers of a Uint8Array of certain byte length into a Float32Array nicholas@2224: var number = dstView.length; nicholas@2538: var outBits = srcBytes * 8; nicholas@2224: var endShift = 32 - outBits; nicholas@2538: if (srcView.length != dstView.length * srcBytes) { nicholas@2224: return -1; nicholas@2224: } nicholas@2538: for (var n = 0; n < number; n++) { nicholas@2538: var srcIndex = n * srcBytes; nicholas@2538: var intData = convertToInteger(srcView.subarray(srcIndex, srcIndex + srcBytes)); nicholas@2224: intData = (intData << (endShift)); nicholas@2224: dstView[n] = intData / 2147483648; nicholas@2224: } nicholas@2538: }