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