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