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