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@1353
|
111 data_view = new Float32Array(this.num_samples);
|
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 } |