1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 | var self = {};
|
13 |
|
14 | function decoder() {
|
15 | self.onmessage = function (e) {
|
16 | switch (e.data.type) {
|
17 | case "decode":
|
18 | self.decode(e.data.buffer).then(function (audioData) {
|
19 | self.postMessage({
|
20 | type: "decoded",
|
21 | audioData: audioData
|
22 | }, [audioData.buffers]);
|
23 | })["catch"](function (err) {
|
24 | self.postMessage({
|
25 | type: "error",
|
26 | message: err.message
|
27 | });
|
28 | });
|
29 | break;
|
30 | }
|
31 | };
|
32 |
|
33 | var formats = {
|
34 | 1: "lpcm",
|
35 | 3: "lpcm" };
|
36 |
|
37 | self.decode = function (buffer) {
|
38 | return new Promise(function (resolve) {
|
39 | var reader = new BufferReader(buffer);
|
40 |
|
41 | if (reader.readString(4) !== "RIFF") {
|
42 | throw new Error("Invalid WAV file");
|
43 | }
|
44 |
|
45 | reader.readUint32();
|
46 |
|
47 | if (reader.readString(4) !== "WAVE") {
|
48 | throw new Error("Invalid WAV file");
|
49 | }
|
50 |
|
51 | var format = null;
|
52 | var audioData = null;
|
53 |
|
54 | do {
|
55 | var chunkType = reader.readString(4);
|
56 | var chunkSize = reader.readUint32();
|
57 | switch (chunkType) {
|
58 | case "fmt ":
|
59 | format = self.decodeFormat(reader, chunkSize);
|
60 | break;
|
61 | case "data":
|
62 | audioData = self.decodeData(reader, chunkSize, format);
|
63 | break;
|
64 | default:
|
65 | reader.skip(chunkSize);
|
66 | break;
|
67 | }
|
68 | } while (audioData === null);
|
69 |
|
70 | return resolve(audioData);
|
71 | });
|
72 | };
|
73 |
|
74 | self.decodeFormat = function (reader, chunkSize) {
|
75 | var formatId = reader.readUint16();
|
76 |
|
77 | if (!formats.hasOwnProperty(formatId)) {
|
78 | throw new Error("Unsupported format in WAV file");
|
79 | }
|
80 |
|
81 | var format = {
|
82 | formatId: formatId,
|
83 | floatingPoint: formatId === 3,
|
84 | numberOfChannels: reader.readUint16(),
|
85 | sampleRate: reader.readUint32(),
|
86 | byteRate: reader.readUint32(),
|
87 | blockSize: reader.readUint16(),
|
88 | bitsPerSample: reader.readUint16() };
|
89 | reader.skip(chunkSize - 16);
|
90 |
|
91 | return format;
|
92 | };
|
93 |
|
94 | self.decodeData = function (reader, chunkSize, format) {
|
95 | var length = Math.floor(chunkSize / format.blockSize);
|
96 | var channelData = new Array(format.numberOfChannels);
|
97 |
|
98 | for (var ch = 0; ch < format.numberOfChannels; ch++) {
|
99 | channelData[ch] = new Float32Array(length);
|
100 | }
|
101 |
|
102 | reader.readPCM(channelData, length, format);
|
103 |
|
104 | var buffers = channelData.map(function (data) {
|
105 | return data.buffer;
|
106 | });
|
107 |
|
108 | return {
|
109 | numberOfChannels: format.numberOfChannels,
|
110 | length: length,
|
111 | sampleRate: format.sampleRate,
|
112 | buffers: buffers
|
113 | };
|
114 | };
|
115 |
|
116 | function BufferReader(buffer) {
|
117 | this.buffer = buffer;
|
118 | this.view = new DataView(buffer);
|
119 | this.length = buffer.byteLength;
|
120 | this.pos = 0;
|
121 | }
|
122 |
|
123 | BufferReader.prototype.skip = function (n) {
|
124 | for (var i = 0; i < n; i++) {
|
125 | this.view.getUint8(this.pos++);
|
126 | }
|
127 | };
|
128 |
|
129 | BufferReader.prototype.readUint8 = function () {
|
130 | var data = this.view.getUint8(this.pos);
|
131 | this.pos += 1;
|
132 | return data;
|
133 | };
|
134 |
|
135 | BufferReader.prototype.readInt16 = function () {
|
136 | var data = this.view.getInt16(this.pos, true);
|
137 | this.pos += 2;
|
138 | return data;
|
139 | };
|
140 |
|
141 | BufferReader.prototype.readUint16 = function () {
|
142 | var data = this.view.getUint16(this.pos, true);
|
143 | this.pos += 2;
|
144 | return data;
|
145 | };
|
146 |
|
147 | BufferReader.prototype.readUint32 = function () {
|
148 | var data = this.view.getUint32(this.pos, true);
|
149 | this.pos += 4;
|
150 | return data;
|
151 | };
|
152 |
|
153 | BufferReader.prototype.readString = function (len) {
|
154 | var data = "";
|
155 | for (var i = 0; i < len; i++) {
|
156 | data += String.fromCharCode(this.readUint8());
|
157 | }
|
158 | return data;
|
159 | };
|
160 |
|
161 | BufferReader.prototype.readPCM = function (channelData, length, format) {
|
162 | var numberOfChannels = format.numberOfChannels;
|
163 | var uint8 = new Uint8Array(this.view.buffer, this.pos);
|
164 | var viewLength = length * numberOfChannels;
|
165 | var bytes = format.bitsPerSample >> 3;
|
166 | var byteOffset = Math.ceil(uint8.byteOffset / bytes) * bytes;
|
167 | var view,
|
168 | dx = 1;
|
169 |
|
170 | if (format.floatingPoint) {
|
171 | switch (format.bitsPerSample) {
|
172 | case 32:
|
173 | view = new Float32Array(uint8.buffer, byteOffset, viewLength);
|
174 | break;
|
175 | case 64:
|
176 | view = new Float64Array(uint8.buffer, byteOffset, viewLength);
|
177 | break;
|
178 | }
|
179 | } else {
|
180 | switch (format.bitsPerSample) {
|
181 | case 8:
|
182 | view = new Int8Array(uint8.buffer, byteOffset, viewLength);
|
183 | dx = 128;
|
184 | break;
|
185 | case 16:
|
186 | view = new Int16Array(uint8.buffer, byteOffset, viewLength);
|
187 | dx = 32768;
|
188 | break;
|
189 | case 24:
|
190 | view = convert24to32(uint8, uint8.byteOffset, viewLength * 3);
|
191 | dx = 8388608;
|
192 | break;
|
193 | case 32:
|
194 | view = new Int32Array(uint8.buffer, byteOffset, viewLength);
|
195 | dx = 2147483648;
|
196 | break;
|
197 | }
|
198 | }
|
199 |
|
200 | if (!view) {
|
201 | throw new Error("not suppoerted bit depth " + format.bitsPerSample);
|
202 | }
|
203 |
|
204 | for (var i = 0; i < length; i++) {
|
205 | for (var ch = 0; ch < numberOfChannels; ch++) {
|
206 | channelData[ch][i] = view[i * numberOfChannels + ch] / dx;
|
207 | }
|
208 | }
|
209 |
|
210 | this.pos += length;
|
211 | };
|
212 |
|
213 | function convert24to32(int8, byteOffset, viewLength) {
|
214 | var uint8 = new Uint8Array(int8.buffer, byteOffset, viewLength);
|
215 | var int32 = new Int32Array(uint8.length / 3);
|
216 |
|
217 | for (var i = 0, imax = int32.length; i < imax; i++) {
|
218 | var x0 = uint8[i * 3 + 0];
|
219 | var x1 = uint8[i * 3 + 1];
|
220 | var x2 = uint8[i * 3 + 2];
|
221 | var xx = x0 + (x1 << 8) + (x2 << 16);
|
222 |
|
223 | int32[i] = xx & 8388608 ? xx - 16777216 : xx;
|
224 | }
|
225 |
|
226 | return int32;
|
227 | }
|
228 | }
|
229 |
|
230 | decoder.self = decoder.util = self;
|
231 |
|
232 | module.exports = decoder; |
\ | No newline at end of file |