node-web-audio-api 0.19.0 → 0.21.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 +11 -0
- package/README.md +1 -3
- package/index.cjs +81 -83
- package/index.mjs +12 -1
- package/js/AnalyserNode.js +0 -3
- package/js/AudioBuffer.js +10 -11
- package/js/AudioBufferSourceNode.js +0 -6
- package/js/AudioContext.js +44 -13
- package/js/AudioDestinationNode.js +1 -1
- package/js/AudioListener.js +2 -2
- package/js/AudioParamMap.js +88 -0
- package/js/AudioRenderCapacity.js +117 -0
- package/js/AudioScheduledSourceNode.js +15 -0
- package/js/AudioWorklet.js +261 -0
- package/js/AudioWorkletGlobalScope.js +303 -0
- package/js/AudioWorkletNode.js +290 -0
- package/js/BaseAudioContext.js +51 -13
- package/js/BiquadFilterNode.js +0 -3
- package/js/ChannelMergerNode.js +0 -3
- package/js/ChannelSplitterNode.js +0 -3
- package/js/ConstantSourceNode.js +0 -6
- package/js/ConvolverNode.js +0 -3
- package/js/DelayNode.js +0 -3
- package/js/DynamicsCompressorNode.js +0 -3
- package/js/Events.js +230 -0
- package/js/GainNode.js +0 -3
- package/js/IIRFilterNode.js +0 -3
- package/js/MediaStreamAudioSourceNode.js +0 -3
- package/js/OfflineAudioContext.js +57 -34
- package/js/OscillatorNode.js +0 -6
- package/js/PannerNode.js +0 -3
- package/js/ScriptProcessorNode.js +179 -0
- package/js/StereoPannerNode.js +0 -3
- package/js/WaveShaperNode.js +0 -3
- package/js/lib/events.js +6 -16
- package/js/lib/symbols.js +23 -2
- package/load-native.cjs +87 -0
- 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 +3 -1
- package/TODOS.md +0 -149
- package/js/monkey-patch.js +0 -77
- package/run-wpt.md +0 -27
- package/simple-test.cjs +0 -20
- package/simple-test.mjs +0 -20
package/js/BaseAudioContext.js
CHANGED
|
@@ -24,12 +24,16 @@ const {
|
|
|
24
24
|
} = require('./lib/utils.js');
|
|
25
25
|
const {
|
|
26
26
|
kNapiObj,
|
|
27
|
+
kPrivateConstructor,
|
|
27
28
|
} = require('./lib/symbols.js');
|
|
28
29
|
|
|
30
|
+
const AudioWorklet = require('./AudioWorklet.js');
|
|
31
|
+
|
|
29
32
|
module.exports = (jsExport, _nativeBinding) => {
|
|
30
33
|
class BaseAudioContext extends EventTarget {
|
|
31
|
-
#
|
|
34
|
+
#audioWorklet = null;
|
|
32
35
|
#destination = null;
|
|
36
|
+
#listener = null;
|
|
33
37
|
|
|
34
38
|
constructor(options) {
|
|
35
39
|
// Make constructor "private"
|
|
@@ -47,24 +51,23 @@ module.exports = (jsExport, _nativeBinding) => {
|
|
|
47
51
|
...kHiddenProperty,
|
|
48
52
|
});
|
|
49
53
|
|
|
50
|
-
this.#
|
|
54
|
+
this.#audioWorklet = new AudioWorklet({
|
|
55
|
+
[kPrivateConstructor]: true,
|
|
56
|
+
workletId: this[kNapiObj].workletId,
|
|
57
|
+
sampleRate: this[kNapiObj].sampleRate,
|
|
58
|
+
});
|
|
59
|
+
|
|
51
60
|
this.#destination = new jsExport.AudioDestinationNode(this, {
|
|
52
61
|
[kNapiObj]: this[kNapiObj].destination,
|
|
53
62
|
});
|
|
54
63
|
}
|
|
55
64
|
|
|
56
|
-
get
|
|
65
|
+
get audioWorklet() {
|
|
57
66
|
if (!(this instanceof BaseAudioContext)) {
|
|
58
67
|
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
59
68
|
}
|
|
60
69
|
|
|
61
|
-
|
|
62
|
-
this.#listener = new jsExport.AudioListener({
|
|
63
|
-
[kNapiObj]: this[kNapiObj].listener,
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return this.#listener;
|
|
70
|
+
return this.#audioWorklet;
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
get destination() {
|
|
@@ -75,6 +78,20 @@ module.exports = (jsExport, _nativeBinding) => {
|
|
|
75
78
|
return this.#destination;
|
|
76
79
|
}
|
|
77
80
|
|
|
81
|
+
get listener() {
|
|
82
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
83
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (this.#listener === null) {
|
|
87
|
+
this.#listener = new jsExport.AudioListener({
|
|
88
|
+
[kNapiObj]: this[kNapiObj].listener,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return this.#listener;
|
|
93
|
+
}
|
|
94
|
+
|
|
78
95
|
get sampleRate() {
|
|
79
96
|
if (!(this instanceof BaseAudioContext)) {
|
|
80
97
|
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
@@ -91,6 +108,15 @@ module.exports = (jsExport, _nativeBinding) => {
|
|
|
91
108
|
return this[kNapiObj].currentTime;
|
|
92
109
|
}
|
|
93
110
|
|
|
111
|
+
// @todo - implement in upstream crate + pass to AudioWorkletGlobalScope
|
|
112
|
+
// get renderQuantumSize() {
|
|
113
|
+
// if (!(this instanceof BaseAudioContext)) {
|
|
114
|
+
// throw new TypeError("Invalid Invocation: Value of 'this' must be of type 'BaseAudioContext'");
|
|
115
|
+
// }
|
|
116
|
+
|
|
117
|
+
// return this[kNapiObj].renderQuantumSize;
|
|
118
|
+
// }
|
|
119
|
+
|
|
94
120
|
get state() {
|
|
95
121
|
if (!(this instanceof BaseAudioContext)) {
|
|
96
122
|
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
@@ -99,9 +125,6 @@ module.exports = (jsExport, _nativeBinding) => {
|
|
|
99
125
|
return this[kNapiObj].state;
|
|
100
126
|
}
|
|
101
127
|
|
|
102
|
-
// renderQuantumSize
|
|
103
|
-
// audioWorklet
|
|
104
|
-
|
|
105
128
|
get onstatechange() {
|
|
106
129
|
if (!(this instanceof BaseAudioContext)) {
|
|
107
130
|
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
@@ -210,6 +233,20 @@ module.exports = (jsExport, _nativeBinding) => {
|
|
|
210
233
|
// --------------------------------------------------------------------
|
|
211
234
|
// Factory Methods (use the patched AudioNodes)
|
|
212
235
|
// --------------------------------------------------------------------
|
|
236
|
+
createScriptProcessor(bufferSize = 0, numberOfInputChannels = 2, numberOfOutputChannels = 2) {
|
|
237
|
+
if (!(this instanceof BaseAudioContext)) {
|
|
238
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const options = {
|
|
242
|
+
bufferSize,
|
|
243
|
+
numberOfInputChannels,
|
|
244
|
+
numberOfOutputChannels,
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
return new jsExport.ScriptProcessorNode(this, options);
|
|
248
|
+
}
|
|
249
|
+
|
|
213
250
|
createAnalyser() {
|
|
214
251
|
if (!(this instanceof BaseAudioContext)) {
|
|
215
252
|
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'BaseAudioContext\'');
|
|
@@ -389,6 +426,7 @@ module.exports = (jsExport, _nativeBinding) => {
|
|
|
389
426
|
configurable: true,
|
|
390
427
|
value: 'BaseAudioContext',
|
|
391
428
|
},
|
|
429
|
+
createScriptProcessor: kEnumerableProperty,
|
|
392
430
|
createAnalyser: kEnumerableProperty,
|
|
393
431
|
createBufferSource: kEnumerableProperty,
|
|
394
432
|
createBiquadFilter: kEnumerableProperty,
|
package/js/BiquadFilterNode.js
CHANGED
package/js/ChannelMergerNode.js
CHANGED
package/js/ConstantSourceNode.js
CHANGED
|
@@ -33,9 +33,6 @@ const {
|
|
|
33
33
|
kNapiObj,
|
|
34
34
|
kAudioBuffer,
|
|
35
35
|
} = require('./lib/symbols.js');
|
|
36
|
-
const {
|
|
37
|
-
bridgeEventTarget,
|
|
38
|
-
} = require('./lib/events.js');
|
|
39
36
|
/* eslint-enable no-unused-vars */
|
|
40
37
|
|
|
41
38
|
const AudioScheduledSourceNode = require('./AudioScheduledSourceNode.js');
|
|
@@ -82,9 +79,6 @@ module.exports = (jsExport, nativeBinding) => {
|
|
|
82
79
|
[kNapiObj]: napiObj,
|
|
83
80
|
});
|
|
84
81
|
|
|
85
|
-
// Bridge Rust native event to Node EventTarget
|
|
86
|
-
bridgeEventTarget(this);
|
|
87
|
-
|
|
88
82
|
this.#offset = new jsExport.AudioParam({
|
|
89
83
|
[kNapiObj]: this[kNapiObj].offset,
|
|
90
84
|
});
|
package/js/ConvolverNode.js
CHANGED
package/js/DelayNode.js
CHANGED
package/js/Events.js
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
const { kEnumerableProperty } = require('./lib/utils.js');
|
|
2
|
+
|
|
3
|
+
class OfflineAudioCompletionEvent extends Event {
|
|
4
|
+
#renderedBuffer = null;
|
|
5
|
+
|
|
6
|
+
constructor(type, eventInitDict) {
|
|
7
|
+
super(type);
|
|
8
|
+
|
|
9
|
+
if (
|
|
10
|
+
typeof eventInitDict !== 'object'
|
|
11
|
+
|| eventInitDict === null
|
|
12
|
+
|| !('renderedBuffer' in eventInitDict)
|
|
13
|
+
) {
|
|
14
|
+
throw TypeError(`Failed to construct 'OfflineAudioCompletionEvent': Invalid 'OfflineAudioCompletionEventInit' dict given`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
this.#renderedBuffer = eventInitDict.renderedBuffer;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get renderedBuffer() {
|
|
21
|
+
return this.#renderedBuffer;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
Object.defineProperties(OfflineAudioCompletionEvent.prototype, {
|
|
26
|
+
[Symbol.toStringTag]: {
|
|
27
|
+
__proto__: null,
|
|
28
|
+
writable: false,
|
|
29
|
+
enumerable: false,
|
|
30
|
+
configurable: true,
|
|
31
|
+
value: 'OfflineAudioCompletionEvent',
|
|
32
|
+
},
|
|
33
|
+
renderedBuffer: kEnumerableProperty,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
class AudioProcessingEvent extends Event {
|
|
37
|
+
#playbackTime = null;
|
|
38
|
+
#inputBuffer = null;
|
|
39
|
+
#outputBuffer = null;
|
|
40
|
+
|
|
41
|
+
constructor(type, eventInitDict) {
|
|
42
|
+
if (
|
|
43
|
+
typeof eventInitDict !== 'object'
|
|
44
|
+
|| eventInitDict === null
|
|
45
|
+
|| !('playbackTime' in eventInitDict)
|
|
46
|
+
|| !('inputBuffer' in eventInitDict)
|
|
47
|
+
|| !('outputBuffer' in eventInitDict)
|
|
48
|
+
) {
|
|
49
|
+
throw TypeError(`Failed to construct 'AudioProcessingEvent': Invalid 'AudioProcessingEventInit' dict given`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
super(type);
|
|
53
|
+
|
|
54
|
+
this.#playbackTime = eventInitDict.playbackTime;
|
|
55
|
+
this.#inputBuffer = eventInitDict.inputBuffer;
|
|
56
|
+
this.#outputBuffer = eventInitDict.outputBuffer;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get playbackTime() {
|
|
60
|
+
return this.#playbackTime;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
get inputBuffer() {
|
|
64
|
+
return this.#inputBuffer;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
get outputBuffer() {
|
|
68
|
+
return this.#outputBuffer;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
Object.defineProperties(AudioProcessingEvent.prototype, {
|
|
73
|
+
[Symbol.toStringTag]: {
|
|
74
|
+
__proto__: null,
|
|
75
|
+
writable: false,
|
|
76
|
+
enumerable: false,
|
|
77
|
+
configurable: true,
|
|
78
|
+
value: 'AudioProcessingEvent',
|
|
79
|
+
},
|
|
80
|
+
playbackTime: kEnumerableProperty,
|
|
81
|
+
inputBuffer: kEnumerableProperty,
|
|
82
|
+
outputBuffer: kEnumerableProperty,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
class AudioRenderCapacityEvent extends Event {
|
|
86
|
+
#timestamp = 0;
|
|
87
|
+
#averageLoad = 0;
|
|
88
|
+
#peakLoad = 0;
|
|
89
|
+
#underrunRatio = 0;
|
|
90
|
+
|
|
91
|
+
constructor(type, eventInitDict) {
|
|
92
|
+
if (
|
|
93
|
+
typeof eventInitDict !== 'object'
|
|
94
|
+
|| eventInitDict === null
|
|
95
|
+
|| !('timestamp' in eventInitDict)
|
|
96
|
+
|| !('averageLoad' in eventInitDict)
|
|
97
|
+
|| !('peakLoad' in eventInitDict)
|
|
98
|
+
|| !('underrunRatio' in eventInitDict)
|
|
99
|
+
) {
|
|
100
|
+
throw TypeError(`Failed to construct 'AudioRenderCapacityEvent': Invalid 'AudioRenderCapacityEventInit' dict given`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
super(type);
|
|
104
|
+
|
|
105
|
+
this.#timestamp = eventInitDict.timestamp;
|
|
106
|
+
this.#averageLoad = eventInitDict.averageLoad;
|
|
107
|
+
this.#peakLoad = eventInitDict.peakLoad;
|
|
108
|
+
this.#underrunRatio = eventInitDict.underrunRatio;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
get timestamp() {
|
|
112
|
+
return this.#timestamp;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
get averageLoad() {
|
|
116
|
+
return this.#averageLoad;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
get peakLoad() {
|
|
120
|
+
return this.#peakLoad;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
get underrunRatio() {
|
|
124
|
+
return this.#underrunRatio;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
Object.defineProperties(AudioRenderCapacityEvent.prototype, {
|
|
129
|
+
[Symbol.toStringTag]: {
|
|
130
|
+
__proto__: null,
|
|
131
|
+
writable: false,
|
|
132
|
+
enumerable: false,
|
|
133
|
+
configurable: true,
|
|
134
|
+
value: 'AudioRenderCapacityEvent',
|
|
135
|
+
},
|
|
136
|
+
timestamp: kEnumerableProperty,
|
|
137
|
+
averageLoad: kEnumerableProperty,
|
|
138
|
+
peakLoad: kEnumerableProperty,
|
|
139
|
+
underrunRatio: kEnumerableProperty,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// https://html.spec.whatwg.org/multipage/webappapis.html#errorevent
|
|
143
|
+
// interface ErrorEvent : Event {
|
|
144
|
+
// constructor(DOMString type, optional ErrorEventInit eventInitDict = {});
|
|
145
|
+
|
|
146
|
+
// readonly attribute DOMString message;
|
|
147
|
+
// readonly attribute USVString filename;
|
|
148
|
+
// readonly attribute unsigned long lineno;
|
|
149
|
+
// readonly attribute unsigned long colno;
|
|
150
|
+
// readonly attribute any error;
|
|
151
|
+
// };
|
|
152
|
+
|
|
153
|
+
// dictionary ErrorEventInit : EventInit {
|
|
154
|
+
// DOMString message = "";
|
|
155
|
+
// USVString filename = "";
|
|
156
|
+
// unsigned long lineno = 0;
|
|
157
|
+
// unsigned long colno = 0;
|
|
158
|
+
// any error;
|
|
159
|
+
// };
|
|
160
|
+
class ErrorEvent extends Event {
|
|
161
|
+
#message = '';
|
|
162
|
+
#filename = '';
|
|
163
|
+
#lineno = 0;
|
|
164
|
+
#colno = 0;
|
|
165
|
+
#error = undefined;
|
|
166
|
+
|
|
167
|
+
constructor(type, eventInitDict = {}) {
|
|
168
|
+
super(type);
|
|
169
|
+
|
|
170
|
+
if (eventInitDict && typeof eventInitDict.message === 'string') {
|
|
171
|
+
this.#message = eventInitDict.message;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (eventInitDict && typeof eventInitDict.filename === 'string') {
|
|
175
|
+
this.#filename = eventInitDict.filename;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (eventInitDict && Number.isFinite(eventInitDict.lineno)) {
|
|
179
|
+
this.#lineno = eventInitDict.lineno;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (eventInitDict && Number.isFinite(eventInitDict.colno)) {
|
|
183
|
+
this.#colno = eventInitDict.colno;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (eventInitDict && eventInitDict.error instanceof Error) {
|
|
187
|
+
this.#error = eventInitDict.error;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
get message() {
|
|
192
|
+
return this.#message;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
get filename() {
|
|
196
|
+
return this.#filename;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
get lineno() {
|
|
200
|
+
return this.#lineno;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
get colno() {
|
|
204
|
+
return this.#colno;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
get error() {
|
|
208
|
+
return this.#error;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
Object.defineProperties(ErrorEvent.prototype, {
|
|
213
|
+
[Symbol.toStringTag]: {
|
|
214
|
+
__proto__: null,
|
|
215
|
+
writable: false,
|
|
216
|
+
enumerable: false,
|
|
217
|
+
configurable: true,
|
|
218
|
+
value: 'ErrorEvent',
|
|
219
|
+
},
|
|
220
|
+
message: kEnumerableProperty,
|
|
221
|
+
filename: kEnumerableProperty,
|
|
222
|
+
lineno: kEnumerableProperty,
|
|
223
|
+
colno: kEnumerableProperty,
|
|
224
|
+
error: kEnumerableProperty,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
module.exports.OfflineAudioCompletionEvent = OfflineAudioCompletionEvent;
|
|
228
|
+
module.exports.AudioProcessingEvent = AudioProcessingEvent;
|
|
229
|
+
module.exports.AudioRenderCapacityEvent = AudioRenderCapacityEvent;
|
|
230
|
+
module.exports.ErrorEvent = ErrorEvent;
|
package/js/GainNode.js
CHANGED
package/js/IIRFilterNode.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const conversions = require('webidl-conversions');
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
|
-
|
|
4
|
+
propagateEvent,
|
|
5
5
|
} = require('./lib/events.js');
|
|
6
6
|
const {
|
|
7
7
|
throwSanitizedError,
|
|
@@ -11,19 +11,17 @@ const {
|
|
|
11
11
|
kEnumerableProperty,
|
|
12
12
|
} = require('./lib/utils.js');
|
|
13
13
|
const {
|
|
14
|
-
kNapiObj
|
|
14
|
+
kNapiObj,
|
|
15
|
+
kWorkletRelease,
|
|
16
|
+
kOnStateChange,
|
|
17
|
+
kOnComplete,
|
|
18
|
+
kCheckProcessorsCreated,
|
|
15
19
|
} = require('./lib/symbols.js');
|
|
16
20
|
|
|
17
|
-
// constructor(OfflineAudioContextOptions contextOptions);
|
|
18
|
-
// constructor(unsigned long numberOfChannels, unsigned long length, float sampleRate);
|
|
19
|
-
// Promise<AudioBuffer> startRendering();
|
|
20
|
-
// Promise<undefined> resume();
|
|
21
|
-
// Promise<undefined> suspend(double suspendTime);
|
|
22
|
-
// readonly attribute unsigned long length;
|
|
23
|
-
// attribute EventHandler oncomplete;
|
|
24
|
-
|
|
25
21
|
module.exports = function patchOfflineAudioContext(jsExport, nativeBinding) {
|
|
26
22
|
class OfflineAudioContext extends jsExport.BaseAudioContext {
|
|
23
|
+
#renderedBuffer = null;
|
|
24
|
+
|
|
27
25
|
constructor(...args) {
|
|
28
26
|
if (arguments.length < 1) {
|
|
29
27
|
throw new TypeError(`Failed to construct 'OfflineAudioContext': 1 argument required, but only ${arguments.length} present`);
|
|
@@ -52,7 +50,7 @@ module.exports = function patchOfflineAudioContext(jsExport, nativeBinding) {
|
|
|
52
50
|
args = [
|
|
53
51
|
options.numberOfChannels,
|
|
54
52
|
options.length,
|
|
55
|
-
options.sampleRate
|
|
53
|
+
options.sampleRate,
|
|
56
54
|
];
|
|
57
55
|
}
|
|
58
56
|
|
|
@@ -60,16 +58,16 @@ module.exports = function patchOfflineAudioContext(jsExport, nativeBinding) {
|
|
|
60
58
|
|
|
61
59
|
numberOfChannels = conversions['unsigned long'](numberOfChannels, {
|
|
62
60
|
enforceRange: true,
|
|
63
|
-
context: `Failed to construct 'OfflineAudioContext': Failed to read the 'numberOfChannels' property from OfflineContextOptions; The provided value (${numberOfChannels})
|
|
61
|
+
context: `Failed to construct 'OfflineAudioContext': Failed to read the 'numberOfChannels' property from OfflineContextOptions; The provided value (${numberOfChannels})`,
|
|
64
62
|
});
|
|
65
63
|
|
|
66
64
|
length = conversions['unsigned long'](length, {
|
|
67
65
|
enforceRange: true,
|
|
68
|
-
context: `Failed to construct 'OfflineAudioContext': Failed to read the 'length' property from OfflineContextOptions; The provided value (${length})
|
|
66
|
+
context: `Failed to construct 'OfflineAudioContext': Failed to read the 'length' property from OfflineContextOptions; The provided value (${length})`,
|
|
69
67
|
});
|
|
70
68
|
|
|
71
69
|
sampleRate = conversions['float'](sampleRate, {
|
|
72
|
-
context: `Failed to construct 'OfflineAudioContext': Failed to read the 'sampleRate' property from OfflineContextOptions; The provided value (${sampleRate})
|
|
70
|
+
context: `Failed to construct 'OfflineAudioContext': Failed to read the 'sampleRate' property from OfflineContextOptions; The provided value (${sampleRate})`,
|
|
73
71
|
});
|
|
74
72
|
|
|
75
73
|
let napiObj;
|
|
@@ -81,11 +79,41 @@ module.exports = function patchOfflineAudioContext(jsExport, nativeBinding) {
|
|
|
81
79
|
}
|
|
82
80
|
|
|
83
81
|
super({ [kNapiObj]: napiObj });
|
|
82
|
+
|
|
83
|
+
// Add function to Napi object to bridge from Rust events to JS EventTarget
|
|
84
|
+
// They will be effectively registered on rust side when `startRendering` is called
|
|
85
|
+
this[kNapiObj][kOnStateChange] = (err, rawEvent) => {
|
|
86
|
+
if (typeof rawEvent !== 'object' && !('type' in rawEvent)) {
|
|
87
|
+
throw new TypeError('Invalid [kOnStateChange] Invocation: rawEvent should have a type property');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const event = new Event(rawEvent.type);
|
|
91
|
+
propagateEvent(this, event);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// This event is, per spec, the last trigerred one
|
|
95
|
+
this[kNapiObj][kOnComplete] = (err, rawEvent) => {
|
|
96
|
+
if (typeof rawEvent !== 'object' && !('type' in rawEvent)) {
|
|
97
|
+
throw new TypeError('Invalid [kOnComplete] Invocation: rawEvent should have a type property');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// @fixme: workaround the fact that this event seems to be triggered before
|
|
101
|
+
// startRendering fulfills and that we want to return the exact same instance
|
|
102
|
+
if (this.#renderedBuffer === null) {
|
|
103
|
+
this.#renderedBuffer = new jsExport.AudioBuffer({ [kNapiObj]: rawEvent.renderedBuffer });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const event = new jsExport.OfflineAudioCompletionEvent(rawEvent.type, {
|
|
107
|
+
renderedBuffer: this.#renderedBuffer,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
propagateEvent(this, event);
|
|
111
|
+
};
|
|
84
112
|
}
|
|
85
113
|
|
|
86
114
|
get length() {
|
|
87
115
|
if (!(this instanceof OfflineAudioContext)) {
|
|
88
|
-
throw new TypeError(
|
|
116
|
+
throw new TypeError(`Invalid Invocation: Value of 'this' must be of type 'OfflineAudioContext'`);
|
|
89
117
|
}
|
|
90
118
|
|
|
91
119
|
return this[kNapiObj].length;
|
|
@@ -93,7 +121,7 @@ module.exports = function patchOfflineAudioContext(jsExport, nativeBinding) {
|
|
|
93
121
|
|
|
94
122
|
get oncomplete() {
|
|
95
123
|
if (!(this instanceof OfflineAudioContext)) {
|
|
96
|
-
throw new TypeError(
|
|
124
|
+
throw new TypeError(`Invalid Invocation: Value of 'this' must be of type 'OfflineAudioContext'`);
|
|
97
125
|
}
|
|
98
126
|
|
|
99
127
|
return this._complete || null;
|
|
@@ -101,7 +129,7 @@ module.exports = function patchOfflineAudioContext(jsExport, nativeBinding) {
|
|
|
101
129
|
|
|
102
130
|
set oncomplete(value) {
|
|
103
131
|
if (!(this instanceof OfflineAudioContext)) {
|
|
104
|
-
throw new TypeError(
|
|
132
|
+
throw new TypeError(`Invalid Invocation: Value of 'this' must be of type 'OfflineAudioContext'`);
|
|
105
133
|
}
|
|
106
134
|
|
|
107
135
|
if (isFunction(value) || value === null) {
|
|
@@ -111,11 +139,11 @@ module.exports = function patchOfflineAudioContext(jsExport, nativeBinding) {
|
|
|
111
139
|
|
|
112
140
|
async startRendering() {
|
|
113
141
|
if (!(this instanceof OfflineAudioContext)) {
|
|
114
|
-
throw new TypeError(
|
|
142
|
+
throw new TypeError(`Invalid Invocation: Value of 'this' must be of type 'OfflineAudioContext'`);
|
|
115
143
|
}
|
|
116
144
|
|
|
117
|
-
//
|
|
118
|
-
|
|
145
|
+
// ensure all AudioWorkletProcessor have finished their instanciation
|
|
146
|
+
await this.audioWorklet[kCheckProcessorsCreated]();
|
|
119
147
|
|
|
120
148
|
let nativeAudioBuffer;
|
|
121
149
|
|
|
@@ -125,26 +153,21 @@ module.exports = function patchOfflineAudioContext(jsExport, nativeBinding) {
|
|
|
125
153
|
throwSanitizedError(err);
|
|
126
154
|
}
|
|
127
155
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
// We dispatch the complete event manually to simplify the sharing of the
|
|
131
|
-
// `AudioBuffer` instance. This also simplifies code on the rust side as
|
|
132
|
-
// we don't need to deal with the `OfflineAudioCompletionEvent` type.
|
|
133
|
-
const event = new Event('complete');
|
|
134
|
-
event.renderedBuffer = audioBuffer;
|
|
156
|
+
// release audio worklet, if any
|
|
157
|
+
await this.audioWorklet[kWorkletRelease]();
|
|
135
158
|
|
|
136
|
-
|
|
137
|
-
|
|
159
|
+
// workaround the fact that this event seems to be triggered before
|
|
160
|
+
// startRendering fulfills and that we want to return the exact same instance
|
|
161
|
+
if (this.#renderedBuffer === null) {
|
|
162
|
+
this.#renderedBuffer = new jsExport.AudioBuffer({ [kNapiObj]: nativeAudioBuffer });
|
|
138
163
|
}
|
|
139
164
|
|
|
140
|
-
this
|
|
141
|
-
|
|
142
|
-
return audioBuffer;
|
|
165
|
+
return this.#renderedBuffer;
|
|
143
166
|
}
|
|
144
167
|
|
|
145
168
|
async resume() {
|
|
146
169
|
if (!(this instanceof OfflineAudioContext)) {
|
|
147
|
-
throw new TypeError(
|
|
170
|
+
throw new TypeError(`Invalid Invocation: Value of 'this' must be of type 'OfflineAudioContext'`);
|
|
148
171
|
}
|
|
149
172
|
|
|
150
173
|
try {
|
|
@@ -156,7 +179,7 @@ module.exports = function patchOfflineAudioContext(jsExport, nativeBinding) {
|
|
|
156
179
|
|
|
157
180
|
async suspend(suspendTime) {
|
|
158
181
|
if (!(this instanceof OfflineAudioContext)) {
|
|
159
|
-
throw new TypeError(
|
|
182
|
+
throw new TypeError(`Invalid Invocation: Value of 'this' must be of type 'OfflineAudioContext'`);
|
|
160
183
|
}
|
|
161
184
|
|
|
162
185
|
if (arguments.length < 1) {
|
package/js/OscillatorNode.js
CHANGED
|
@@ -33,9 +33,6 @@ const {
|
|
|
33
33
|
kNapiObj,
|
|
34
34
|
kAudioBuffer,
|
|
35
35
|
} = require('./lib/symbols.js');
|
|
36
|
-
const {
|
|
37
|
-
bridgeEventTarget,
|
|
38
|
-
} = require('./lib/events.js');
|
|
39
36
|
/* eslint-enable no-unused-vars */
|
|
40
37
|
|
|
41
38
|
const AudioScheduledSourceNode = require('./AudioScheduledSourceNode.js');
|
|
@@ -140,9 +137,6 @@ module.exports = (jsExport, nativeBinding) => {
|
|
|
140
137
|
[kNapiObj]: napiObj,
|
|
141
138
|
});
|
|
142
139
|
|
|
143
|
-
// Bridge Rust native event to Node EventTarget
|
|
144
|
-
bridgeEventTarget(this);
|
|
145
|
-
|
|
146
140
|
this.#frequency = new jsExport.AudioParam({
|
|
147
141
|
[kNapiObj]: this[kNapiObj].frequency,
|
|
148
142
|
});
|
package/js/PannerNode.js
CHANGED