node-web-audio-api 0.18.0 → 0.20.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 +14 -0
- package/TODOS.md +134 -12
- package/index.mjs +17 -6
- package/js/AnalyserNode.js +259 -48
- package/js/AudioBuffer.js +243 -0
- package/js/AudioBufferSourceNode.js +259 -41
- package/js/AudioContext.js +294 -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 +120 -0
- package/js/BaseAudioContext.js +434 -0
- package/js/BiquadFilterNode.js +218 -29
- package/js/ChannelMergerNode.js +93 -22
- package/js/ChannelSplitterNode.js +93 -22
- package/js/ConstantSourceNode.js +86 -26
- package/js/ConvolverNode.js +158 -29
- package/js/DelayNode.js +112 -21
- package/js/DynamicsCompressorNode.js +195 -27
- package/js/Events.js +84 -0
- package/js/GainNode.js +104 -21
- package/js/IIRFilterNode.js +136 -23
- package/js/MediaStreamAudioSourceNode.js +80 -24
- package/js/OfflineAudioContext.js +198 -35
- package/js/OscillatorNode.js +189 -32
- package/js/PannerNode.js +458 -56
- package/js/PeriodicWave.js +67 -3
- package/js/ScriptProcessorNode.js +179 -0
- package/js/StereoPannerNode.js +104 -21
- package/js/WaveShaperNode.js +144 -29
- package/js/lib/cast.js +19 -0
- package/js/lib/errors.js +10 -55
- package/js/lib/events.js +10 -0
- package/js/lib/symbols.js +20 -0
- package/js/lib/utils.js +12 -12
- package/js/monkey-patch.js +40 -31
- 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
|
@@ -1,57 +1,220 @@
|
|
|
1
|
-
const
|
|
2
|
-
const { isPlainObject, isPositiveInt, isPositiveNumber } = require('./lib/utils.js');
|
|
1
|
+
const conversions = require('webidl-conversions');
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
const {
|
|
4
|
+
propagateEvent,
|
|
5
|
+
} = require('./lib/events.js');
|
|
6
|
+
const {
|
|
7
|
+
throwSanitizedError,
|
|
8
|
+
} = require('./lib/errors.js');
|
|
9
|
+
const {
|
|
10
|
+
isFunction,
|
|
11
|
+
kEnumerableProperty,
|
|
12
|
+
} = require('./lib/utils.js');
|
|
13
|
+
const {
|
|
14
|
+
kNapiObj,
|
|
15
|
+
kOnStateChange,
|
|
16
|
+
kOnComplete,
|
|
17
|
+
} = require('./lib/symbols.js');
|
|
8
18
|
|
|
9
|
-
|
|
10
|
-
|
|
19
|
+
module.exports = function patchOfflineAudioContext(jsExport, nativeBinding) {
|
|
20
|
+
class OfflineAudioContext extends jsExport.BaseAudioContext {
|
|
21
|
+
#renderedBuffer = null;
|
|
11
22
|
|
|
12
|
-
class OfflineAudioContext extends BaseAudioContext {
|
|
13
23
|
constructor(...args) {
|
|
14
|
-
|
|
24
|
+
if (arguments.length < 1) {
|
|
25
|
+
throw new TypeError(`Failed to construct 'OfflineAudioContext': 1 argument required, but only ${arguments.length} present`);
|
|
26
|
+
}
|
|
27
|
+
|
|
15
28
|
// https://webaudio.github.io/web-audio-api/#dom-offlineaudiocontext-constructor-contextoptions-contextoptions
|
|
16
|
-
if (
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (
|
|
20
|
-
|
|
29
|
+
if (arguments.length === 1) {
|
|
30
|
+
const options = args[0];
|
|
31
|
+
|
|
32
|
+
if (typeof options !== 'object') {
|
|
33
|
+
throw new TypeError(`Failed to construct 'OfflineAudioContext': argument 1 is not of type 'OfflineAudioContextOptions'`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (options.length === undefined) {
|
|
37
|
+
throw new TypeError(`Failed to construct 'OfflineAudioContext': Failed to read the 'length' property from 'OfflineAudioContextOptions': Required member is undefined.`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (options.sampleRate === undefined) {
|
|
41
|
+
throw new TypeError(`Failed to construct 'OfflineAudioContext': Failed to read the 'sampleRate' property from 'OfflineAudioContextOptions': Required member is undefined.`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (options.numberOfChannels === undefined) {
|
|
45
|
+
options.numberOfChannels = 1;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
args = [
|
|
49
|
+
options.numberOfChannels,
|
|
50
|
+
options.length,
|
|
51
|
+
options.sampleRate,
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let [numberOfChannels, length, sampleRate] = args;
|
|
56
|
+
|
|
57
|
+
numberOfChannels = conversions['unsigned long'](numberOfChannels, {
|
|
58
|
+
enforceRange: true,
|
|
59
|
+
context: `Failed to construct 'OfflineAudioContext': Failed to read the 'numberOfChannels' property from OfflineContextOptions; The provided value (${numberOfChannels})`,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
length = conversions['unsigned long'](length, {
|
|
63
|
+
enforceRange: true,
|
|
64
|
+
context: `Failed to construct 'OfflineAudioContext': Failed to read the 'length' property from OfflineContextOptions; The provided value (${length})`,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
sampleRate = conversions['float'](sampleRate, {
|
|
68
|
+
context: `Failed to construct 'OfflineAudioContext': Failed to read the 'sampleRate' property from OfflineContextOptions; The provided value (${sampleRate})`,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
let napiObj;
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
napiObj = new nativeBinding.OfflineAudioContext(numberOfChannels, length, sampleRate);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
throwSanitizedError(err);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
super({ [kNapiObj]: napiObj });
|
|
80
|
+
|
|
81
|
+
// Add function to Napi object to bridge from Rust events to JS EventTarget
|
|
82
|
+
// They will be effectively registered on rust side when `startRendering` is called
|
|
83
|
+
this[kNapiObj][kOnStateChange] = (err, rawEvent) => {
|
|
84
|
+
if (typeof rawEvent !== 'object' && !('type' in rawEvent)) {
|
|
85
|
+
throw new TypeError('Invalid [kOnStateChange] Invocation: rawEvent should have a type property');
|
|
21
86
|
}
|
|
22
|
-
|
|
87
|
+
|
|
88
|
+
const event = new Event(rawEvent.type);
|
|
89
|
+
propagateEvent(this, event);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// This event is, per spec, the last trigerred one
|
|
93
|
+
this[kNapiObj][kOnComplete] = (err, rawEvent) => {
|
|
94
|
+
if (typeof rawEvent !== 'object' && !('type' in rawEvent)) {
|
|
95
|
+
throw new TypeError('Invalid [kOnComplete] Invocation: rawEvent should have a type property');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// @fixme: workaround the fact that this event seems to be triggered before
|
|
99
|
+
// startRendering fulfills and that we want to return the exact same instance
|
|
100
|
+
if (this.#renderedBuffer === null) {
|
|
101
|
+
this.#renderedBuffer = new jsExport.AudioBuffer({ [kNapiObj]: rawEvent.renderedBuffer });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const event = new jsExport.OfflineAudioCompletionEvent(rawEvent.type, {
|
|
105
|
+
renderedBuffer: this.#renderedBuffer,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
propagateEvent(this, event);
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
get length() {
|
|
113
|
+
if (!(this instanceof OfflineAudioContext)) {
|
|
114
|
+
throw new TypeError(`Invalid Invocation: Value of 'this' must be of type 'OfflineAudioContext'`);
|
|
23
115
|
}
|
|
24
116
|
|
|
25
|
-
|
|
117
|
+
return this[kNapiObj].length;
|
|
118
|
+
}
|
|
26
119
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
throw new TypeError(`Invalid value for length: ${length}`);
|
|
31
|
-
} else if (!isPositiveNumber(sampleRate)) {
|
|
32
|
-
throw new TypeError(`Invalid value for sampleRate: ${sampleRate}`);
|
|
120
|
+
get oncomplete() {
|
|
121
|
+
if (!(this instanceof OfflineAudioContext)) {
|
|
122
|
+
throw new TypeError(`Invalid Invocation: Value of 'this' must be of type 'OfflineAudioContext'`);
|
|
33
123
|
}
|
|
34
124
|
|
|
35
|
-
|
|
125
|
+
return this._complete || null;
|
|
126
|
+
}
|
|
36
127
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
128
|
+
set oncomplete(value) {
|
|
129
|
+
if (!(this instanceof OfflineAudioContext)) {
|
|
130
|
+
throw new TypeError(`Invalid Invocation: Value of 'this' must be of type 'OfflineAudioContext'`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (isFunction(value) || value === null) {
|
|
134
|
+
this._complete = value;
|
|
135
|
+
}
|
|
40
136
|
}
|
|
41
137
|
|
|
42
138
|
async startRendering() {
|
|
43
|
-
|
|
139
|
+
if (!(this instanceof OfflineAudioContext)) {
|
|
140
|
+
throw new TypeError(`Invalid Invocation: Value of 'this' must be of type 'OfflineAudioContext'`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
let nativeAudioBuffer;
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
nativeAudioBuffer = await this[kNapiObj].startRendering();
|
|
147
|
+
} catch (err) {
|
|
148
|
+
throwSanitizedError(err);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// @fixme: workaround the fact that this event seems to be triggered before
|
|
152
|
+
// startRendering fulfills and that we want to return the exact same instance
|
|
153
|
+
if (this.#renderedBuffer === null) {
|
|
154
|
+
this.#renderedBuffer = new jsExport.AudioBuffer({ [kNapiObj]: nativeAudioBuffer });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return this.#renderedBuffer;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async resume() {
|
|
161
|
+
if (!(this instanceof OfflineAudioContext)) {
|
|
162
|
+
throw new TypeError(`Invalid Invocation: Value of 'this' must be of type 'OfflineAudioContext'`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
await this[kNapiObj].resume();
|
|
167
|
+
} catch (err) {
|
|
168
|
+
throwSanitizedError(err);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async suspend(suspendTime) {
|
|
173
|
+
if (!(this instanceof OfflineAudioContext)) {
|
|
174
|
+
throw new TypeError(`Invalid Invocation: Value of 'this' must be of type 'OfflineAudioContext'`);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (arguments.length < 1) {
|
|
178
|
+
throw new TypeError(`Failed to execute 'suspend' on 'OfflineAudioContext': 1 argument required, but only ${arguments.length} present`);
|
|
179
|
+
}
|
|
44
180
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const event = new Event('complete');
|
|
49
|
-
event.renderedBuffer = renderedBuffer;
|
|
50
|
-
this.dispatchEvent(event)
|
|
181
|
+
suspendTime = conversions['double'](suspendTime, {
|
|
182
|
+
context: `Failed to execute 'suspend' on 'OfflineAudioContext': argument 1`,
|
|
183
|
+
});
|
|
51
184
|
|
|
52
|
-
|
|
185
|
+
try {
|
|
186
|
+
await this[kNapiObj].suspend(suspendTime);
|
|
187
|
+
} catch (err) {
|
|
188
|
+
throwSanitizedError(err);
|
|
189
|
+
}
|
|
53
190
|
}
|
|
54
191
|
}
|
|
55
192
|
|
|
193
|
+
Object.defineProperties(OfflineAudioContext, {
|
|
194
|
+
length: {
|
|
195
|
+
__proto__: null,
|
|
196
|
+
writable: false,
|
|
197
|
+
enumerable: false,
|
|
198
|
+
configurable: true,
|
|
199
|
+
value: 1,
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
Object.defineProperties(OfflineAudioContext.prototype, {
|
|
204
|
+
[Symbol.toStringTag]: {
|
|
205
|
+
__proto__: null,
|
|
206
|
+
writable: false,
|
|
207
|
+
enumerable: false,
|
|
208
|
+
configurable: true,
|
|
209
|
+
value: 'OfflineAudioContext',
|
|
210
|
+
},
|
|
211
|
+
|
|
212
|
+
length: kEnumerableProperty,
|
|
213
|
+
oncomplete: kEnumerableProperty,
|
|
214
|
+
startRendering: kEnumerableProperty,
|
|
215
|
+
resume: kEnumerableProperty,
|
|
216
|
+
suspend: kEnumerableProperty,
|
|
217
|
+
});
|
|
218
|
+
|
|
56
219
|
return OfflineAudioContext;
|
|
57
220
|
};
|
package/js/OscillatorNode.js
CHANGED
|
@@ -17,56 +17,192 @@
|
|
|
17
17
|
// -------------------------------------------------------------------------- //
|
|
18
18
|
// -------------------------------------------------------------------------- //
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
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
|
+
/* eslint-enable no-unused-vars */
|
|
27
37
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const EventTarget = EventTargetMixin(NativeOscillatorNode, ['ended']);
|
|
31
|
-
const AudioNode = AudioNodeMixin(EventTarget);
|
|
32
|
-
const AudioScheduledSourceNode = AudioScheduledSourceNodeMixin(AudioNode);
|
|
38
|
+
const AudioScheduledSourceNode = require('./AudioScheduledSourceNode.js');
|
|
33
39
|
|
|
40
|
+
module.exports = (jsExport, nativeBinding) => {
|
|
34
41
|
class OscillatorNode extends AudioScheduledSourceNode {
|
|
42
|
+
|
|
43
|
+
#frequency = null;
|
|
44
|
+
#detune = null;
|
|
45
|
+
|
|
35
46
|
constructor(context, options) {
|
|
36
|
-
|
|
37
|
-
|
|
47
|
+
|
|
48
|
+
if (arguments.length < 1) {
|
|
49
|
+
throw new TypeError(`Failed to construct 'OscillatorNode': 1 argument required, but only ${arguments.length} present`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!(context instanceof jsExport.BaseAudioContext)) {
|
|
53
|
+
throw new TypeError(`Failed to construct 'OscillatorNode': argument 1 is not of type BaseAudioContext`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// parsed version of the option to be passed to NAPI
|
|
57
|
+
const parsedOptions = {};
|
|
58
|
+
|
|
59
|
+
if (options && typeof options !== 'object') {
|
|
60
|
+
throw new TypeError('Failed to construct \'OscillatorNode\': argument 2 is not of type \'OscillatorOptions\'');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (options && options.type !== undefined) {
|
|
64
|
+
if (!['sine', 'square', 'sawtooth', 'triangle', 'custom'].includes(options.type)) {
|
|
65
|
+
throw new TypeError(`Failed to construct 'OscillatorNode': Failed to read the 'type' property from OscillatorOptions: The provided value '${options.type}' is not a valid enum value of type OscillatorType`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
parsedOptions.type = conversions['DOMString'](options.type, {
|
|
69
|
+
context: `Failed to construct 'OscillatorNode': Failed to read the 'type' property from OscillatorOptions: The provided value '${options.type}'`,
|
|
70
|
+
});
|
|
71
|
+
} else {
|
|
72
|
+
parsedOptions.type = 'sine';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (options && options.frequency !== undefined) {
|
|
76
|
+
parsedOptions.frequency = conversions['float'](options.frequency, {
|
|
77
|
+
context: `Failed to construct 'OscillatorNode': Failed to read the 'frequency' property from OscillatorOptions: The provided value (${options.frequency}})`,
|
|
78
|
+
});
|
|
79
|
+
} else {
|
|
80
|
+
parsedOptions.frequency = 440;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (options && options.detune !== undefined) {
|
|
84
|
+
parsedOptions.detune = conversions['float'](options.detune, {
|
|
85
|
+
context: `Failed to construct 'OscillatorNode': Failed to read the 'detune' property from OscillatorOptions: The provided value (${options.detune}})`,
|
|
86
|
+
});
|
|
87
|
+
} else {
|
|
88
|
+
parsedOptions.detune = 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (options && options.periodicWave !== undefined) {
|
|
92
|
+
if (!(options.periodicWave instanceof jsExport.PeriodicWave)) {
|
|
93
|
+
throw new TypeError(`Failed to construct 'OscillatorNode': Failed to read the 'periodicWave' property from OscillatorOptions: The provided value '${options.periodicWave}' is not an instance of PeriodicWave`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
parsedOptions.periodicWave = options.periodicWave[kNapiObj];
|
|
97
|
+
} else {
|
|
98
|
+
parsedOptions.periodicWave = null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (parsedOptions.type === 'custom' && parsedOptions.periodicWave === null) {
|
|
102
|
+
throw new DOMException('Failed to construct \'OscillatorNode\': A PeriodicWave must be specified if the type is set to \'custom\'', 'InvalidStateError');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (parsedOptions.periodicWave !== null) {
|
|
106
|
+
parsedOptions.type = 'custom';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (options && options.channelCount !== undefined) {
|
|
110
|
+
parsedOptions.channelCount = conversions['unsigned long'](options.channelCount, {
|
|
111
|
+
enforceRange: true,
|
|
112
|
+
context: `Failed to construct 'OscillatorNode': Failed to read the 'channelCount' property from OscillatorOptions: The provided value '${options.channelCount}'`,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (options && options.channelCountMode !== undefined) {
|
|
117
|
+
parsedOptions.channelCountMode = conversions['DOMString'](options.channelCountMode, {
|
|
118
|
+
context: `Failed to construct 'OscillatorNode': Failed to read the 'channelCount' property from OscillatorOptions: The provided value '${options.channelCountMode}'`,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (options && options.channelInterpretation !== undefined) {
|
|
123
|
+
parsedOptions.channelInterpretation = conversions['DOMString'](options.channelInterpretation, {
|
|
124
|
+
context: `Failed to construct 'OscillatorNode': Failed to read the 'channelInterpretation' property from OscillatorOptions: The provided value '${options.channelInterpretation}'`,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let napiObj;
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
napiObj = new nativeBinding.OscillatorNode(context[kNapiObj], parsedOptions);
|
|
132
|
+
} catch (err) {
|
|
133
|
+
throwSanitizedError(err);
|
|
38
134
|
}
|
|
39
135
|
|
|
40
|
-
super(context,
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
super.__initEventTarget__();
|
|
136
|
+
super(context, {
|
|
137
|
+
[kNapiObj]: napiObj,
|
|
138
|
+
});
|
|
44
139
|
|
|
45
|
-
this
|
|
46
|
-
|
|
140
|
+
this.#frequency = new jsExport.AudioParam({
|
|
141
|
+
[kNapiObj]: this[kNapiObj].frequency,
|
|
142
|
+
});
|
|
143
|
+
this.#detune = new jsExport.AudioParam({
|
|
144
|
+
[kNapiObj]: this[kNapiObj].detune,
|
|
145
|
+
});
|
|
47
146
|
}
|
|
48
147
|
|
|
49
|
-
|
|
148
|
+
get frequency() {
|
|
149
|
+
if (!(this instanceof OscillatorNode)) {
|
|
150
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'OscillatorNode\'');
|
|
151
|
+
}
|
|
50
152
|
|
|
51
|
-
|
|
52
|
-
return super.type;
|
|
153
|
+
return this.#frequency;
|
|
53
154
|
}
|
|
54
155
|
|
|
55
|
-
|
|
156
|
+
get detune() {
|
|
157
|
+
if (!(this instanceof OscillatorNode)) {
|
|
158
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'OscillatorNode\'');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return this.#detune;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
get type() {
|
|
165
|
+
if (!(this instanceof OscillatorNode)) {
|
|
166
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'OscillatorNode\'');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return this[kNapiObj].type;
|
|
170
|
+
}
|
|
56
171
|
|
|
57
172
|
set type(value) {
|
|
173
|
+
if (!(this instanceof OscillatorNode)) {
|
|
174
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'OscillatorNode\'');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (!['sine', 'square', 'sawtooth', 'triangle', 'custom'].includes(value)) {
|
|
178
|
+
console.warn(`Failed to set the 'type' property on 'OscillatorNode': Value '${value}' is not a valid 'OscillatorType' enum value`);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
58
182
|
try {
|
|
59
|
-
|
|
183
|
+
this[kNapiObj].type = value;
|
|
60
184
|
} catch (err) {
|
|
61
185
|
throwSanitizedError(err);
|
|
62
186
|
}
|
|
63
187
|
}
|
|
64
188
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
189
|
+
setPeriodicWave(periodicWave) {
|
|
190
|
+
if (!(this instanceof OscillatorNode)) {
|
|
191
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'OscillatorNode\'');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (arguments.length < 1) {
|
|
195
|
+
throw new TypeError(`Failed to execute 'setPeriodicWave' on 'OscillatorNode': 1 argument required, but only ${arguments.length} present`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (!(periodicWave instanceof jsExport.PeriodicWave)) {
|
|
199
|
+
throw new TypeError(`Failed to execute 'setPeriodicWave' on 'OscillatorNode': Parameter 1 is not of type 'PeriodicWave'`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
periodicWave = periodicWave[kNapiObj];
|
|
203
|
+
|
|
68
204
|
try {
|
|
69
|
-
return
|
|
205
|
+
return this[kNapiObj].setPeriodicWave(periodicWave);
|
|
70
206
|
} catch (err) {
|
|
71
207
|
throwSanitizedError(err);
|
|
72
208
|
}
|
|
@@ -74,8 +210,29 @@ module.exports = (NativeOscillatorNode) => {
|
|
|
74
210
|
|
|
75
211
|
}
|
|
76
212
|
|
|
77
|
-
|
|
78
|
-
|
|
213
|
+
Object.defineProperties(OscillatorNode, {
|
|
214
|
+
length: {
|
|
215
|
+
__proto__: null,
|
|
216
|
+
writable: false,
|
|
217
|
+
enumerable: false,
|
|
218
|
+
configurable: true,
|
|
219
|
+
value: 1,
|
|
220
|
+
},
|
|
221
|
+
});
|
|
79
222
|
|
|
223
|
+
Object.defineProperties(OscillatorNode.prototype, {
|
|
224
|
+
[Symbol.toStringTag]: {
|
|
225
|
+
__proto__: null,
|
|
226
|
+
writable: false,
|
|
227
|
+
enumerable: false,
|
|
228
|
+
configurable: true,
|
|
229
|
+
value: 'OscillatorNode',
|
|
230
|
+
},
|
|
231
|
+
frequency: kEnumerableProperty,
|
|
232
|
+
detune: kEnumerableProperty,
|
|
233
|
+
type: kEnumerableProperty,
|
|
234
|
+
setPeriodicWave: kEnumerableProperty,
|
|
235
|
+
});
|
|
80
236
|
|
|
81
|
-
|
|
237
|
+
return OscillatorNode;
|
|
238
|
+
};
|