This repository has been archived by the owner on Sep 22, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.js
122 lines (89 loc) · 3.42 KB
/
index.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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/**
* @module audio-pcm-format
*/
var inherits = require('inherits');
var Transform = require('stream').Transform;
var PassThrough = require('stream').PassThrough;
var extend = require('xtend/mutable');
var util = require('pcm-util');
/** @constructor */
function PCMFormat (input, output) {
if (!(this instanceof PCMFormat)) return new PCMFormat(input, output);
Transform.call(this);
//save input format
this.input = extend({}, PCMFormat.default, input);
this.output = extend({}, PCMFormat.default, output);
//if input format === output format - return passthrough stream
var eq = true;
for (var key in this.input) {
if (this.input[key] !== this.output[key]) {
eq = false;
break;
}
}
if (eq) return new PassThrough();
//precalc formats
this.input = util.normalizeFormat(this.input);
this.output = util.normalizeFormat(this.output);
//convert channels to arrays, if mapping is required
this.input.channels = PCMFormat.getChannelsMap(this.input.channels);
this.output.channels = PCMFormat.getChannelsMap(this.output.channels);
//normalized data for output channels
this.data = this.output.channels.map(function () {return []});
}
inherits(PCMFormat, Transform);
PCMFormat.prototype._transform = function (inputChunk, enc, cb) {
var self = this;
var input = self.input, output = self.output, data = self.data;
var value, channel, offset, channels;
var inputFrameLength = inputChunk.length / input.sampleSize / input.channels.length;
var outputFrameLength = output.samplesPerFrame || inputFrameLength;
var sampleRateRatio = input.sampleRate / output.sampleRate;
//bring value to normalized form
channels = input.channels;
for (var i = 0; i < inputFrameLength; i++) {
for (var cIdx = 0; cIdx < channels.length; cIdx++) {
channel = channels[cIdx];
offset = (input.interleaved ? channel + i * channels.length : channel * inputFrameLength + i) * input.sampleSize;
value = inputChunk[input.readMethodName](offset);
//put recalculated value to the proper channel
data[channel].push(util.convertSample(value, input, output));
}
}
//if there is enough data - send chunk
channels = output.channels;
if (!output.samplesPerFrame || (output.samplesPerFrame && self.data[channel].length > output.samplesPerFrame)) {
var outputChunkSize = outputFrameLength * output.sampleSize * channels.length;
var outputChunk = new Buffer(outputChunkSize);
//write sample
for (var i = 0; i < outputFrameLength; i++) {
for (var cIdx = 0; cIdx < channels.length; cIdx++) {
channel = channels[cIdx];
//pick resampled value
value = data[channel][Math.round(i * sampleRateRatio)];
//write value to proper position
offset = (output.interleaved ? channel + i * channels.length : channel * outputFrameLength + i) * output.sampleSize;
outputChunk[output.writeMethodName](value, offset);
}
}
//shorten inner data on the input frame size, inc sample rate
self.data = self.data.map(function (data) {
return data.slice(Math.round(outputFrameLength * sampleRateRatio));
});
cb(null, outputChunk);
}
};
/** Generate channels array from number of channels */
PCMFormat.getChannelsMap = function (n) {
if (!Array.isArray(n)) {
var result = [];
for (var i = 0; i < n; i++) {
result[i] = i;
}
}
return result;
};
/** Default PCM settings. Technically redefinable. */
PCMFormat.default = extend({}, util.defaultFormat);
PCMFormat.default.samplesPerFrame = null;
module.exports = PCMFormat;