node-web-audio-api 0.18.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/TODOS.md +140 -12
- package/index.mjs +10 -5
- package/js/AnalyserNode.js +262 -48
- package/js/AudioBuffer.js +244 -0
- package/js/AudioBufferSourceNode.js +265 -41
- package/js/AudioContext.js +271 -28
- package/js/AudioDestinationNode.js +42 -100
- package/js/AudioListener.js +219 -0
- package/js/AudioNode.js +323 -0
- package/js/AudioParam.js +252 -39
- package/js/AudioScheduledSourceNode.js +105 -0
- package/js/BaseAudioContext.js +419 -0
- package/js/BiquadFilterNode.js +221 -29
- package/js/ChannelMergerNode.js +96 -22
- package/js/ChannelSplitterNode.js +96 -22
- package/js/ConstantSourceNode.js +92 -26
- package/js/ConvolverNode.js +161 -29
- package/js/DelayNode.js +115 -21
- package/js/DynamicsCompressorNode.js +198 -27
- package/js/GainNode.js +107 -21
- package/js/IIRFilterNode.js +139 -23
- package/js/MediaStreamAudioSourceNode.js +83 -24
- package/js/OfflineAudioContext.js +182 -34
- package/js/OscillatorNode.js +195 -32
- package/js/PannerNode.js +461 -56
- package/js/PeriodicWave.js +67 -3
- package/js/StereoPannerNode.js +107 -21
- package/js/WaveShaperNode.js +147 -29
- package/js/lib/cast.js +19 -0
- package/js/lib/errors.js +10 -55
- package/js/lib/events.js +20 -0
- package/js/lib/symbols.js +5 -0
- package/js/lib/utils.js +12 -12
- package/js/monkey-patch.js +32 -30
- package/node-web-audio-api.darwin-arm64.node +0 -0
- package/node-web-audio-api.darwin-x64.node +0 -0
- package/node-web-audio-api.linux-arm-gnueabihf.node +0 -0
- package/node-web-audio-api.linux-arm64-gnu.node +0 -0
- package/node-web-audio-api.linux-x64-gnu.node +0 -0
- package/node-web-audio-api.win32-arm64-msvc.node +0 -0
- package/node-web-audio-api.win32-x64-msvc.node +0 -0
- package/package.json +7 -4
- package/run-wpt.md +27 -0
- package/run-wpt.sh +5 -0
- package/js/AudioNode.mixin.js +0 -132
- package/js/AudioScheduledSourceNode.mixin.js +0 -67
- package/js/BaseAudioContext.mixin.js +0 -154
- package/js/EventTarget.mixin.js +0 -60
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
// -------------------------------------------------------------------------- //
|
|
2
|
+
// -------------------------------------------------------------------------- //
|
|
3
|
+
// //
|
|
4
|
+
// //
|
|
5
|
+
// //
|
|
6
|
+
// ██╗ ██╗ █████╗ ██████╗ ███╗ ██╗██╗███╗ ██╗ ██████╗ //
|
|
7
|
+
// ██║ ██║██╔══██╗██╔══██╗████╗ ██║██║████╗ ██║██╔════╝ //
|
|
8
|
+
// ██║ █╗ ██║███████║██████╔╝██╔██╗ ██║██║██╔██╗ ██║██║ ███╗ //
|
|
9
|
+
// ██║███╗██║██╔══██║██╔══██╗██║╚██╗██║██║██║╚██╗██║██║ ██║ //
|
|
10
|
+
// ╚███╔███╔╝██║ ██║██║ ██║██║ ╚████║██║██║ ╚████║╚██████╔╝ //
|
|
11
|
+
// ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝╚═╝ ╚═══╝ ╚═════╝ //
|
|
12
|
+
// //
|
|
13
|
+
// //
|
|
14
|
+
// - This file has been generated --------------------------- //
|
|
15
|
+
// //
|
|
16
|
+
// //
|
|
17
|
+
// -------------------------------------------------------------------------- //
|
|
18
|
+
// -------------------------------------------------------------------------- //
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
isFunction,
|
|
22
|
+
kEnumerableProperty,
|
|
23
|
+
kHiddenProperty,
|
|
24
|
+
} = require('./lib/utils.js');
|
|
25
|
+
const {
|
|
26
|
+
kNapiObj,
|
|
27
|
+
} = require('./lib/symbols.js');
|
|
28
|
+
|
|
29
|
+
module.exports = (jsExport, _nativeBinding) => {
|
|
30
|
+
class BaseAudioContext extends EventTarget {
|
|
31
|
+
#listener = null;
|
|
32
|
+
#destination = null;
|
|
33
|
+
|
|
34
|
+
constructor(options) {
|
|
35
|
+
// Make constructor "private"
|
|
36
|
+
if (
|
|
37
|
+
(typeof options !== 'object') ||
|
|
38
|
+
!(kNapiObj in options)
|
|
39
|
+
) {
|
|
40
|
+
throw new TypeError('Illegal constructor');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
super();
|
|
44
|
+
|
|
45
|
+
Object.defineProperty(this, kNapiObj, {
|
|
46
|
+
value: options[kNapiObj],
|
|
47
|
+
...kHiddenProperty,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
this.#listener = null; // lazily instanciated
|
|
51
|
+
this.#destination = new jsExport.AudioDestinationNode(this, {
|
|
52
|
+
[kNapiObj]: this[kNapiObj].destination,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
get listener() {
|
|
57
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
58
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (this.#listener === null) {
|
|
62
|
+
this.#listener = new jsExport.AudioListener({
|
|
63
|
+
[kNapiObj]: this[kNapiObj].listener,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return this.#listener;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
get destination() {
|
|
71
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
72
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return this.#destination;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get sampleRate() {
|
|
79
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
80
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return this[kNapiObj].sampleRate;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
get currentTime() {
|
|
87
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
88
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return this[kNapiObj].currentTime;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
get state() {
|
|
95
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
96
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return this[kNapiObj].state;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// renderQuantumSize
|
|
103
|
+
// audioWorklet
|
|
104
|
+
|
|
105
|
+
get onstatechange() {
|
|
106
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
107
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return this._statechange || null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
set onstatechange(value) {
|
|
114
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
115
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (isFunction(value) || value === null) {
|
|
119
|
+
this._statechange = value;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// This is not exactly what the spec says, but if we reject the promise
|
|
124
|
+
// when decodeErrorCallback is present the program will crash in an
|
|
125
|
+
// unexpected manner
|
|
126
|
+
// cf. https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-decodeaudiodata
|
|
127
|
+
async decodeAudioData(arrayBuffer, decodeSuccessCallback = undefined, decodeErrorCallback = undefined) {
|
|
128
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
129
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (arguments.length < 1) {
|
|
133
|
+
throw new TypeError(`Failed to execute 'decodeAudioData' on 'BaseAudioContext': 1 argument required, but only ${arguments.length} present`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (!(arrayBuffer instanceof ArrayBuffer)) {
|
|
137
|
+
throw new TypeError('Failed to execute "decodeAudioData": parameter 1 is not of type "ArrayBuffer"');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
const nativeAudioBuffer = await this[kNapiObj].decodeAudioData(arrayBuffer);
|
|
142
|
+
const audioBuffer = new jsExport.AudioBuffer({
|
|
143
|
+
[kNapiObj]: nativeAudioBuffer,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
if (isFunction(decodeSuccessCallback)) {
|
|
147
|
+
decodeSuccessCallback(audioBuffer);
|
|
148
|
+
} else {
|
|
149
|
+
return audioBuffer;
|
|
150
|
+
}
|
|
151
|
+
} catch (err) {
|
|
152
|
+
const error = new DOMException(`Failed to execute 'decodeAudioData': ${err.message}`, 'EncodingError');
|
|
153
|
+
|
|
154
|
+
if (isFunction(decodeErrorCallback)) {
|
|
155
|
+
decodeErrorCallback(error);
|
|
156
|
+
} else {
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
createBuffer(numberOfChannels, length, sampleRate) {
|
|
163
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
164
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (arguments.length < 3) {
|
|
168
|
+
throw new TypeError(`Failed to execute 'createBuffer' on 'BaseAudioContext': 3 argument required, but only ${arguments.length} present`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const options = {};
|
|
172
|
+
|
|
173
|
+
if (numberOfChannels !== undefined) {
|
|
174
|
+
options.numberOfChannels = numberOfChannels;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (length !== undefined) {
|
|
178
|
+
options.length = length;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (sampleRate !== undefined) {
|
|
182
|
+
options.sampleRate = sampleRate;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return new jsExport.AudioBuffer(options);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
createPeriodicWave(real, imag) {
|
|
189
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
190
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (arguments.length < 2) {
|
|
194
|
+
throw new TypeError(`Failed to execute 'createPeriodicWave' on 'BaseAudioContext': 2 argument required, but only ${arguments.length} present`);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const options = {};
|
|
198
|
+
|
|
199
|
+
if (real !== undefined) {
|
|
200
|
+
options.real = real;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (imag !== undefined) {
|
|
204
|
+
options.imag = imag;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return new jsExport.PeriodicWave(this, options);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// --------------------------------------------------------------------
|
|
211
|
+
// Factory Methods (use the patched AudioNodes)
|
|
212
|
+
// --------------------------------------------------------------------
|
|
213
|
+
createAnalyser() {
|
|
214
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
215
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const options = {};
|
|
219
|
+
|
|
220
|
+
return new jsExport.AnalyserNode(this, options);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
createBufferSource() {
|
|
224
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
225
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const options = {};
|
|
229
|
+
|
|
230
|
+
return new jsExport.AudioBufferSourceNode(this, options);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
createBiquadFilter() {
|
|
234
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
235
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const options = {};
|
|
239
|
+
|
|
240
|
+
return new jsExport.BiquadFilterNode(this, options);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
createChannelMerger(numberOfInputs = 6) {
|
|
244
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
245
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const options = {
|
|
249
|
+
numberOfInputs,
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
return new jsExport.ChannelMergerNode(this, options);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
createChannelSplitter(numberOfOutputs = 6) {
|
|
256
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
257
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const options = {
|
|
261
|
+
numberOfOutputs,
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
return new jsExport.ChannelSplitterNode(this, options);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
createConstantSource() {
|
|
268
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
269
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const options = {};
|
|
273
|
+
|
|
274
|
+
return new jsExport.ConstantSourceNode(this, options);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
createConvolver() {
|
|
278
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
279
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const options = {};
|
|
283
|
+
|
|
284
|
+
return new jsExport.ConvolverNode(this, options);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
createDelay(maxDelayTime = 1.0) {
|
|
288
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
289
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const options = {
|
|
293
|
+
maxDelayTime,
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
return new jsExport.DelayNode(this, options);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
createDynamicsCompressor() {
|
|
300
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
301
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const options = {};
|
|
305
|
+
|
|
306
|
+
return new jsExport.DynamicsCompressorNode(this, options);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
createGain() {
|
|
310
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
311
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const options = {};
|
|
315
|
+
|
|
316
|
+
return new jsExport.GainNode(this, options);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
createIIRFilter(feedforward, feedback) {
|
|
320
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
321
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const options = {
|
|
325
|
+
feedforward,
|
|
326
|
+
feedback,
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
return new jsExport.IIRFilterNode(this, options);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
createOscillator() {
|
|
333
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
334
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const options = {};
|
|
338
|
+
|
|
339
|
+
return new jsExport.OscillatorNode(this, options);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
createPanner() {
|
|
343
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
344
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const options = {};
|
|
348
|
+
|
|
349
|
+
return new jsExport.PannerNode(this, options);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
createStereoPanner() {
|
|
353
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
354
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const options = {};
|
|
358
|
+
|
|
359
|
+
return new jsExport.StereoPannerNode(this, options);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
createWaveShaper() {
|
|
363
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
364
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const options = {};
|
|
368
|
+
|
|
369
|
+
return new jsExport.WaveShaperNode(this, options);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
Object.defineProperties(BaseAudioContext, {
|
|
375
|
+
length: {
|
|
376
|
+
__proto__: null,
|
|
377
|
+
writable: false,
|
|
378
|
+
enumerable: false,
|
|
379
|
+
configurable: true,
|
|
380
|
+
value: 0,
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
Object.defineProperties(BaseAudioContext.prototype, {
|
|
385
|
+
[Symbol.toStringTag]: {
|
|
386
|
+
__proto__: null,
|
|
387
|
+
writable: false,
|
|
388
|
+
enumerable: false,
|
|
389
|
+
configurable: true,
|
|
390
|
+
value: 'BaseAudioContext',
|
|
391
|
+
},
|
|
392
|
+
createAnalyser: kEnumerableProperty,
|
|
393
|
+
createBufferSource: kEnumerableProperty,
|
|
394
|
+
createBiquadFilter: kEnumerableProperty,
|
|
395
|
+
createChannelMerger: kEnumerableProperty,
|
|
396
|
+
createChannelSplitter: kEnumerableProperty,
|
|
397
|
+
createConstantSource: kEnumerableProperty,
|
|
398
|
+
createConvolver: kEnumerableProperty,
|
|
399
|
+
createDelay: kEnumerableProperty,
|
|
400
|
+
createDynamicsCompressor: kEnumerableProperty,
|
|
401
|
+
createGain: kEnumerableProperty,
|
|
402
|
+
createIIRFilter: kEnumerableProperty,
|
|
403
|
+
createOscillator: kEnumerableProperty,
|
|
404
|
+
createPanner: kEnumerableProperty,
|
|
405
|
+
createStereoPanner: kEnumerableProperty,
|
|
406
|
+
createWaveShaper: kEnumerableProperty,
|
|
407
|
+
listener: kEnumerableProperty,
|
|
408
|
+
destination: kEnumerableProperty,
|
|
409
|
+
sampleRate: kEnumerableProperty,
|
|
410
|
+
currentTime: kEnumerableProperty,
|
|
411
|
+
state: kEnumerableProperty,
|
|
412
|
+
onstatechange: kEnumerableProperty,
|
|
413
|
+
decodeAudioData: kEnumerableProperty,
|
|
414
|
+
createBuffer: kEnumerableProperty,
|
|
415
|
+
createPeriodicWave: kEnumerableProperty,
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
return BaseAudioContext;
|
|
419
|
+
};
|
package/js/BiquadFilterNode.js
CHANGED
|
@@ -17,54 +17,223 @@
|
|
|
17
17
|
// -------------------------------------------------------------------------- //
|
|
18
18
|
// -------------------------------------------------------------------------- //
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
20
|
+
/* eslint-disable no-unused-vars */
|
|
21
|
+
const conversions = require('webidl-conversions');
|
|
22
|
+
const {
|
|
23
|
+
toSanitizedSequence,
|
|
24
|
+
} = require('./lib/cast.js');
|
|
25
|
+
const {
|
|
26
|
+
isFunction,
|
|
27
|
+
kEnumerableProperty,
|
|
28
|
+
} = require('./lib/utils.js');
|
|
29
|
+
const {
|
|
30
|
+
throwSanitizedError,
|
|
31
|
+
} = require('./lib/errors.js');
|
|
32
|
+
const {
|
|
33
|
+
kNapiObj,
|
|
34
|
+
kAudioBuffer,
|
|
35
|
+
} = require('./lib/symbols.js');
|
|
36
|
+
const {
|
|
37
|
+
bridgeEventTarget,
|
|
38
|
+
} = require('./lib/events.js');
|
|
39
|
+
/* eslint-enable no-unused-vars */
|
|
26
40
|
|
|
41
|
+
const AudioNode = require('./AudioNode.js');
|
|
27
42
|
|
|
28
|
-
module.exports = (
|
|
43
|
+
module.exports = (jsExport, nativeBinding) => {
|
|
44
|
+
class BiquadFilterNode extends AudioNode {
|
|
29
45
|
|
|
30
|
-
|
|
31
|
-
|
|
46
|
+
#frequency = null;
|
|
47
|
+
#detune = null;
|
|
48
|
+
#Q = null;
|
|
49
|
+
#gain = null;
|
|
32
50
|
|
|
33
|
-
class BiquadFilterNode extends AudioNode {
|
|
34
51
|
constructor(context, options) {
|
|
35
|
-
|
|
36
|
-
|
|
52
|
+
|
|
53
|
+
if (arguments.length < 1) {
|
|
54
|
+
throw new TypeError(`Failed to construct 'BiquadFilterNode': 1 argument required, but only ${arguments.length} present`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!(context instanceof jsExport.BaseAudioContext)) {
|
|
58
|
+
throw new TypeError(`Failed to construct 'BiquadFilterNode': argument 1 is not of type BaseAudioContext`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// parsed version of the option to be passed to NAPI
|
|
62
|
+
const parsedOptions = {};
|
|
63
|
+
|
|
64
|
+
if (options && typeof options !== 'object') {
|
|
65
|
+
throw new TypeError('Failed to construct \'BiquadFilterNode\': argument 2 is not of type \'BiquadFilterOptions\'');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (options && options.type !== undefined) {
|
|
69
|
+
if (!['lowpass', 'highpass', 'bandpass', 'lowshelf', 'highshelf', 'peaking', 'notch', 'allpass'].includes(options.type)) {
|
|
70
|
+
throw new TypeError(`Failed to construct 'BiquadFilterNode': Failed to read the 'type' property from BiquadFilterOptions: The provided value '${options.type}' is not a valid enum value of type BiquadFilterType`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
parsedOptions.type = conversions['DOMString'](options.type, {
|
|
74
|
+
context: `Failed to construct 'BiquadFilterNode': Failed to read the 'type' property from BiquadFilterOptions: The provided value '${options.type}'`,
|
|
75
|
+
});
|
|
76
|
+
} else {
|
|
77
|
+
parsedOptions.type = 'lowpass';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (options && options.Q !== undefined) {
|
|
81
|
+
parsedOptions.Q = conversions['float'](options.Q, {
|
|
82
|
+
context: `Failed to construct 'BiquadFilterNode': Failed to read the 'Q' property from BiquadFilterOptions: The provided value (${options.Q}})`,
|
|
83
|
+
});
|
|
84
|
+
} else {
|
|
85
|
+
parsedOptions.Q = 1;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (options && options.detune !== undefined) {
|
|
89
|
+
parsedOptions.detune = conversions['float'](options.detune, {
|
|
90
|
+
context: `Failed to construct 'BiquadFilterNode': Failed to read the 'detune' property from BiquadFilterOptions: The provided value (${options.detune}})`,
|
|
91
|
+
});
|
|
92
|
+
} else {
|
|
93
|
+
parsedOptions.detune = 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (options && options.frequency !== undefined) {
|
|
97
|
+
parsedOptions.frequency = conversions['float'](options.frequency, {
|
|
98
|
+
context: `Failed to construct 'BiquadFilterNode': Failed to read the 'frequency' property from BiquadFilterOptions: The provided value (${options.frequency}})`,
|
|
99
|
+
});
|
|
100
|
+
} else {
|
|
101
|
+
parsedOptions.frequency = 350;
|
|
37
102
|
}
|
|
38
103
|
|
|
39
|
-
|
|
104
|
+
if (options && options.gain !== undefined) {
|
|
105
|
+
parsedOptions.gain = conversions['float'](options.gain, {
|
|
106
|
+
context: `Failed to construct 'BiquadFilterNode': Failed to read the 'gain' property from BiquadFilterOptions: The provided value (${options.gain}})`,
|
|
107
|
+
});
|
|
108
|
+
} else {
|
|
109
|
+
parsedOptions.gain = 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (options && options.channelCount !== undefined) {
|
|
113
|
+
parsedOptions.channelCount = conversions['unsigned long'](options.channelCount, {
|
|
114
|
+
enforceRange: true,
|
|
115
|
+
context: `Failed to construct 'BiquadFilterNode': Failed to read the 'channelCount' property from BiquadFilterOptions: The provided value '${options.channelCount}'`,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (options && options.channelCountMode !== undefined) {
|
|
120
|
+
parsedOptions.channelCountMode = conversions['DOMString'](options.channelCountMode, {
|
|
121
|
+
context: `Failed to construct 'BiquadFilterNode': Failed to read the 'channelCount' property from BiquadFilterOptions: The provided value '${options.channelCountMode}'`,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
40
124
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
125
|
+
if (options && options.channelInterpretation !== undefined) {
|
|
126
|
+
parsedOptions.channelInterpretation = conversions['DOMString'](options.channelInterpretation, {
|
|
127
|
+
context: `Failed to construct 'BiquadFilterNode': Failed to read the 'channelInterpretation' property from BiquadFilterOptions: The provided value '${options.channelInterpretation}'`,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
let napiObj;
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
napiObj = new nativeBinding.BiquadFilterNode(context[kNapiObj], parsedOptions);
|
|
135
|
+
} catch (err) {
|
|
136
|
+
throwSanitizedError(err);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
super(context, {
|
|
140
|
+
[kNapiObj]: napiObj,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
this.#frequency = new jsExport.AudioParam({
|
|
144
|
+
[kNapiObj]: this[kNapiObj].frequency,
|
|
145
|
+
});
|
|
146
|
+
this.#detune = new jsExport.AudioParam({
|
|
147
|
+
[kNapiObj]: this[kNapiObj].detune,
|
|
148
|
+
});
|
|
149
|
+
this.#Q = new jsExport.AudioParam({
|
|
150
|
+
[kNapiObj]: this[kNapiObj].Q,
|
|
151
|
+
});
|
|
152
|
+
this.#gain = new jsExport.AudioParam({
|
|
153
|
+
[kNapiObj]: this[kNapiObj].gain,
|
|
154
|
+
});
|
|
45
155
|
}
|
|
46
156
|
|
|
47
|
-
|
|
157
|
+
get frequency() {
|
|
158
|
+
if (!(this instanceof BiquadFilterNode)) {
|
|
159
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BiquadFilterNode\'');
|
|
160
|
+
}
|
|
48
161
|
|
|
49
|
-
|
|
50
|
-
return super.type;
|
|
162
|
+
return this.#frequency;
|
|
51
163
|
}
|
|
52
164
|
|
|
53
|
-
|
|
165
|
+
get detune() {
|
|
166
|
+
if (!(this instanceof BiquadFilterNode)) {
|
|
167
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BiquadFilterNode\'');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return this.#detune;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
get Q() {
|
|
174
|
+
if (!(this instanceof BiquadFilterNode)) {
|
|
175
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BiquadFilterNode\'');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return this.#Q;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
get gain() {
|
|
182
|
+
if (!(this instanceof BiquadFilterNode)) {
|
|
183
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BiquadFilterNode\'');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return this.#gain;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
get type() {
|
|
190
|
+
if (!(this instanceof BiquadFilterNode)) {
|
|
191
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BiquadFilterNode\'');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return this[kNapiObj].type;
|
|
195
|
+
}
|
|
54
196
|
|
|
55
197
|
set type(value) {
|
|
198
|
+
if (!(this instanceof BiquadFilterNode)) {
|
|
199
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BiquadFilterNode\'');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (!['lowpass', 'highpass', 'bandpass', 'lowshelf', 'highshelf', 'peaking', 'notch', 'allpass'].includes(value)) {
|
|
203
|
+
console.warn(`Failed to set the 'type' property on 'BiquadFilterNode': Value '${value}' is not a valid 'BiquadFilterType' enum value`);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
56
207
|
try {
|
|
57
|
-
|
|
208
|
+
this[kNapiObj].type = value;
|
|
58
209
|
} catch (err) {
|
|
59
210
|
throwSanitizedError(err);
|
|
60
211
|
}
|
|
61
212
|
}
|
|
62
213
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
214
|
+
getFrequencyResponse(frequencyHz, magResponse, phaseResponse) {
|
|
215
|
+
if (!(this instanceof BiquadFilterNode)) {
|
|
216
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BiquadFilterNode\'');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (arguments.length < 3) {
|
|
220
|
+
throw new TypeError(`Failed to execute 'getFrequencyResponse' on 'BiquadFilterNode': 3 argument required, but only ${arguments.length} present`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (!(frequencyHz instanceof Float32Array)) {
|
|
224
|
+
throw new TypeError(`Failed to execute 'getFrequencyResponse' on 'BiquadFilterNode': Parameter 1 is not of type 'Float32Array'`);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (!(magResponse instanceof Float32Array)) {
|
|
228
|
+
throw new TypeError(`Failed to execute 'getFrequencyResponse' on 'BiquadFilterNode': Parameter 2 is not of type 'Float32Array'`);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (!(phaseResponse instanceof Float32Array)) {
|
|
232
|
+
throw new TypeError(`Failed to execute 'getFrequencyResponse' on 'BiquadFilterNode': Parameter 3 is not of type 'Float32Array'`);
|
|
233
|
+
}
|
|
234
|
+
|
|
66
235
|
try {
|
|
67
|
-
return
|
|
236
|
+
return this[kNapiObj].getFrequencyResponse(frequencyHz, magResponse, phaseResponse);
|
|
68
237
|
} catch (err) {
|
|
69
238
|
throwSanitizedError(err);
|
|
70
239
|
}
|
|
@@ -72,8 +241,31 @@ module.exports = (NativeBiquadFilterNode) => {
|
|
|
72
241
|
|
|
73
242
|
}
|
|
74
243
|
|
|
75
|
-
|
|
76
|
-
|
|
244
|
+
Object.defineProperties(BiquadFilterNode, {
|
|
245
|
+
length: {
|
|
246
|
+
__proto__: null,
|
|
247
|
+
writable: false,
|
|
248
|
+
enumerable: false,
|
|
249
|
+
configurable: true,
|
|
250
|
+
value: 1,
|
|
251
|
+
},
|
|
252
|
+
});
|
|
77
253
|
|
|
254
|
+
Object.defineProperties(BiquadFilterNode.prototype, {
|
|
255
|
+
[Symbol.toStringTag]: {
|
|
256
|
+
__proto__: null,
|
|
257
|
+
writable: false,
|
|
258
|
+
enumerable: false,
|
|
259
|
+
configurable: true,
|
|
260
|
+
value: 'BiquadFilterNode',
|
|
261
|
+
},
|
|
262
|
+
frequency: kEnumerableProperty,
|
|
263
|
+
detune: kEnumerableProperty,
|
|
264
|
+
Q: kEnumerableProperty,
|
|
265
|
+
gain: kEnumerableProperty,
|
|
266
|
+
type: kEnumerableProperty,
|
|
267
|
+
getFrequencyResponse: kEnumerableProperty,
|
|
268
|
+
});
|
|
78
269
|
|
|
79
|
-
|
|
270
|
+
return BiquadFilterNode;
|
|
271
|
+
};
|