-
Notifications
You must be signed in to change notification settings - Fork 1
/
wave-worker.js
95 lines (81 loc) · 3.39 KB
/
wave-worker.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
self.onmessage = function (e) {
var wavPCM = new WavePCM(e['data']['config']);
wavPCM.record(e['data']['pcmArrays']);
wavPCM.requestData();
};
var WavePCM = function (config) {
this.sampleRate = config['sampleRate'] || 48000;
this.bitDepth = config['bitDepth'] || 16;
this.recordedBuffers = [];
this.bytesPerSample = this.bitDepth / 8;
};
WavePCM.prototype.record = function (buffers) {
this.numberOfChannels = this.numberOfChannels || buffers.length;
var bufferLength = buffers[0].length;
var reducedData = new Uint8Array(bufferLength * this.numberOfChannels * this.bytesPerSample);
// Interleave
for (var i = 0; i < bufferLength; i++) {
for (var channel = 0; channel < this.numberOfChannels; channel++) {
var outputIndex = (i * this.numberOfChannels + channel) * this.bytesPerSample;
var sample = buffers[channel][i];
// Check for clipping
if (sample > 1) {
sample = 1;
} else if (sample < -1) {
sample = -1;
}
// bit reduce and convert to uInt
switch (this.bytesPerSample) {
case 4:
sample = sample * 2147483648;
reducedData[outputIndex] = sample;
reducedData[outputIndex + 1] = sample >> 8;
reducedData[outputIndex + 2] = sample >> 16;
reducedData[outputIndex + 3] = sample >> 24;
break;
case 3:
sample = sample * 8388608;
reducedData[outputIndex] = sample;
reducedData[outputIndex + 1] = sample >> 8;
reducedData[outputIndex + 2] = sample >> 16;
break;
case 2:
sample = sample * 32768;
reducedData[outputIndex] = sample;
reducedData[outputIndex + 1] = sample >> 8;
break;
case 1:
reducedData[outputIndex] = (sample + 1) * 128;
break;
default:
throw 'Only 8, 16, 24 and 32 bits per sample are supported';
}
}
}
this.recordedBuffers.push(reducedData);
};
WavePCM.prototype.requestData = function () {
var bufferLength = this.recordedBuffers[0].length;
var dataLength = this.recordedBuffers.length * bufferLength;
var headerLength = 44;
var wav = new Uint8Array(headerLength + dataLength);
var view = new DataView(wav.buffer);
view.setUint32(0, 1380533830, false); // RIFF identifier 'RIFF'
view.setUint32(4, 36 + dataLength, true); // file length minus RIFF identifier length and file description length
view.setUint32(8, 1463899717, false); // RIFF type 'WAVE'
view.setUint32(12, 1718449184, false); // format chunk identifier 'fmt '
view.setUint32(16, 16, true); // format chunk length
view.setUint16(20, 1, true); // sample format (raw)
view.setUint16(22, this.numberOfChannels, true); // channel count
view.setUint32(24, this.sampleRate, true); // sample rate
view.setUint32(28, this.sampleRate * this.bytesPerSample * this.numberOfChannels, true); // byte rate (sample rate * block align)
view.setUint16(32, this.bytesPerSample * this.numberOfChannels, true); // block align (channel count * bytes per sample)
view.setUint16(34, this.bitDepth, true); // bits per sample
view.setUint32(36, 1684108385, false); // data chunk identifier 'data'
view.setUint32(40, dataLength, true); // data chunk length
for (var i = 0; i < this.recordedBuffers.length; i++) {
wav.set(this.recordedBuffers[i], i * bufferLength + headerLength);
}
self.postMessage(wav, [wav.buffer]);
self.close();
};