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