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.
Files changed (51) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/TODOS.md +134 -12
  3. package/index.mjs +17 -6
  4. package/js/AnalyserNode.js +259 -48
  5. package/js/AudioBuffer.js +243 -0
  6. package/js/AudioBufferSourceNode.js +259 -41
  7. package/js/AudioContext.js +294 -28
  8. package/js/AudioDestinationNode.js +42 -100
  9. package/js/AudioListener.js +219 -0
  10. package/js/AudioNode.js +323 -0
  11. package/js/AudioParam.js +252 -39
  12. package/js/AudioScheduledSourceNode.js +120 -0
  13. package/js/BaseAudioContext.js +434 -0
  14. package/js/BiquadFilterNode.js +218 -29
  15. package/js/ChannelMergerNode.js +93 -22
  16. package/js/ChannelSplitterNode.js +93 -22
  17. package/js/ConstantSourceNode.js +86 -26
  18. package/js/ConvolverNode.js +158 -29
  19. package/js/DelayNode.js +112 -21
  20. package/js/DynamicsCompressorNode.js +195 -27
  21. package/js/Events.js +84 -0
  22. package/js/GainNode.js +104 -21
  23. package/js/IIRFilterNode.js +136 -23
  24. package/js/MediaStreamAudioSourceNode.js +80 -24
  25. package/js/OfflineAudioContext.js +198 -35
  26. package/js/OscillatorNode.js +189 -32
  27. package/js/PannerNode.js +458 -56
  28. package/js/PeriodicWave.js +67 -3
  29. package/js/ScriptProcessorNode.js +179 -0
  30. package/js/StereoPannerNode.js +104 -21
  31. package/js/WaveShaperNode.js +144 -29
  32. package/js/lib/cast.js +19 -0
  33. package/js/lib/errors.js +10 -55
  34. package/js/lib/events.js +10 -0
  35. package/js/lib/symbols.js +20 -0
  36. package/js/lib/utils.js +12 -12
  37. package/js/monkey-patch.js +40 -31
  38. package/node-web-audio-api.darwin-arm64.node +0 -0
  39. package/node-web-audio-api.darwin-x64.node +0 -0
  40. package/node-web-audio-api.linux-arm-gnueabihf.node +0 -0
  41. package/node-web-audio-api.linux-arm64-gnu.node +0 -0
  42. package/node-web-audio-api.linux-x64-gnu.node +0 -0
  43. package/node-web-audio-api.win32-arm64-msvc.node +0 -0
  44. package/node-web-audio-api.win32-x64-msvc.node +0 -0
  45. package/package.json +7 -4
  46. package/run-wpt.md +27 -0
  47. package/run-wpt.sh +5 -0
  48. package/js/AudioNode.mixin.js +0 -132
  49. package/js/AudioScheduledSourceNode.mixin.js +0 -67
  50. package/js/BaseAudioContext.mixin.js +0 -154
  51. package/js/EventTarget.mixin.js +0 -60
@@ -0,0 +1,243 @@
1
+ const conversions = require('webidl-conversions');
2
+
3
+ const {
4
+ throwSanitizedError,
5
+ } = require('./lib/errors.js');
6
+ const {
7
+ kEnumerableProperty,
8
+ kHiddenProperty,
9
+ } = require('./lib/utils.js');
10
+ const {
11
+ kNapiObj,
12
+ } = require('./lib/symbols.js');
13
+
14
+
15
+ module.exports = (_jsExport, nativeBinding) => {
16
+ class AudioBuffer {
17
+ constructor(options) {
18
+ if (arguments.length < 1) {
19
+ throw new TypeError(`Failed to construct 'AudioBuffer': 1 argument required, but only ${arguments.length} present`);
20
+ }
21
+
22
+ if (typeof options !== 'object') {
23
+ throw new TypeError(`Failed to construct 'AudioBuffer': argument 1 is not of type 'AudioBufferOptions'`);
24
+ }
25
+
26
+ if (kNapiObj in options) {
27
+ // internal constructor for `startRendering` and `decodeAudioData` cases
28
+ Object.defineProperty(this, kNapiObj, {
29
+ value: options[kNapiObj],
30
+ ...kHiddenProperty,
31
+ });
32
+ } else {
33
+ // Regular public constructor
34
+ // dictionary AudioBufferOptions {
35
+ // unsigned long numberOfChannels = 1;
36
+ // required unsigned long length;
37
+ // required float sampleRate;
38
+ // };
39
+ const parsedOptions = {};
40
+
41
+ if (options.numberOfChannels !== undefined) {
42
+ parsedOptions.numberOfChannels = conversions['unsigned long'](options.numberOfChannels, {
43
+ enforceRange: true,
44
+ context: `Failed to construct 'AudioBuffer': Failed to read the 'numberOfChannels' property from AudioBufferOptions: numberOfCHannels`,
45
+ });
46
+ } else {
47
+ parsedOptions.numberOfChannels = 1;
48
+ }
49
+
50
+ if (options.length === undefined) {
51
+ throw new TypeError(`Failed to construct 'AudioBuffer': Failed to read the 'numberOfChannels' property from AudioBufferOptions: required member is undefined`);
52
+ }
53
+
54
+ parsedOptions.length = conversions['unsigned long'](options.length, {
55
+ enforceRange: true,
56
+ context: `Failed to construct 'AudioBuffer': Failed to read the 'length' property from AudioBufferOptions: length`,
57
+ });
58
+
59
+ if (options.sampleRate === undefined) {
60
+ throw new TypeError(`Failed to construct 'AudioBuffer': Failed to read the 'numberOfChannels' property from AudioBufferOptions: required member is undefined`);
61
+ }
62
+
63
+ parsedOptions.sampleRate = conversions['float'](options.sampleRate, {
64
+ context: `Failed to construct 'AudioBuffer': Failed to read the 'sampleRate' property from AudioBufferOptions: sampleRate`,
65
+ });
66
+
67
+ let napiObj;
68
+
69
+ try {
70
+ napiObj = new nativeBinding.AudioBuffer(parsedOptions);
71
+ } catch (err) {
72
+ throwSanitizedError(err);
73
+ }
74
+
75
+ Object.defineProperty(this, kNapiObj, {
76
+ value: napiObj,
77
+ ...kHiddenProperty,
78
+ });
79
+ }
80
+ }
81
+
82
+ get sampleRate() {
83
+ if (!(this instanceof AudioBuffer)) {
84
+ throw new TypeError(`Invalid Invocation: Value of 'this' must be of type 'AudioBuffer'`);
85
+ }
86
+
87
+ return this[kNapiObj].sampleRate;
88
+ }
89
+
90
+ get duration() {
91
+ if (!(this instanceof AudioBuffer)) {
92
+ throw new TypeError(`Invalid Invocation: Value of 'this' must be of type 'AudioBuffer'`);
93
+ }
94
+
95
+ return this[kNapiObj].duration;
96
+ }
97
+
98
+ get length() {
99
+ if (!(this instanceof AudioBuffer)) {
100
+ throw new TypeError(`Invalid Invocation: Value of 'this' must be of type 'AudioBuffer'`);
101
+ }
102
+
103
+ return this[kNapiObj].length;
104
+ }
105
+
106
+ get numberOfChannels() {
107
+ if (!(this instanceof AudioBuffer)) {
108
+ throw new TypeError(`Invalid Invocation: Value of 'this' must be of type 'AudioBuffer'`);
109
+ }
110
+
111
+ return this[kNapiObj].numberOfChannels;
112
+ }
113
+
114
+ copyFromChannel(destination, channelNumber, bufferOffset = 0) {
115
+ if (!(this instanceof AudioBuffer)) {
116
+ throw new TypeError(`Invalid Invocation: Value of 'this' must be of type 'AudioBuffer'`);
117
+ }
118
+
119
+ if (arguments.length < 2) {
120
+ throw new TypeError(`Failed to execute 'copyFromChannel' on 'AudioBuffer': 2 argument required, but only ${arguments.length} present`);
121
+ }
122
+
123
+ if (!(destination instanceof Float32Array)) {
124
+ throw new TypeError(`Failed to execute 'copyFromChannel' on 'AudioBuffer': parameter 1 is not of type 'Float32Array'`);
125
+ }
126
+
127
+ // Rust implementation uses a usize which will clamp -1 to 0, and spec requires
128
+ // an IndexSizeError rather than a TypeError, so this check must be done here.
129
+ // cf. note on AnalyzerNode::fftSize
130
+ if (channelNumber < 0) {
131
+ throw new DOMException(`Failed to execute 'copyFromChannel' on 'AudioBuffer': channelNumber must equal or greater than 0`, 'IndexSizeError');
132
+ }
133
+
134
+ channelNumber = conversions['unsigned long'](channelNumber, {
135
+ context: `Failed to execute 'copyFromChannel' on 'AudioBuffer': channelNumber`,
136
+ });
137
+
138
+ bufferOffset = conversions['unsigned long'](bufferOffset, {
139
+ context: `Failed to execute 'copyFromChannel' on 'AudioBuffer': bufferOffset`,
140
+ });
141
+
142
+ try {
143
+ this[kNapiObj].copyFromChannel(destination, channelNumber, bufferOffset);
144
+ } catch (err) {
145
+ throwSanitizedError(err);
146
+ }
147
+ }
148
+
149
+ copyToChannel(source, channelNumber, bufferOffset = 0) {
150
+ if (!(this instanceof AudioBuffer)) {
151
+ throw new TypeError(`Invalid Invocation: Value of 'this' must be of type 'AudioBuffer'`);
152
+ }
153
+
154
+ if (arguments.length < 2) {
155
+ throw new TypeError(`Failed to execute 'copyToChannel' on 'AudioBuffer': 2 argument required, but only ${arguments.length} present`);
156
+ }
157
+
158
+ if (!(source instanceof Float32Array)) {
159
+ throw new TypeError(`Failed to execute 'copyToChannel' on 'AudioBuffer': source is not of type 'Float32Array'`);
160
+ }
161
+
162
+ // Rust implementation uses a usize which will clamp -1 to 0, and spec requires
163
+ // an IndexSizeError rather than a TypeError, so this check must be done here.
164
+ // cf. note on AnalyzerNode::fftSize
165
+ if (channelNumber < 0) {
166
+ throw new DOMException(`Failed to execute 'copyToChannel' on 'AudioBuffer': channelNumber must equal or greater than 0`, 'IndexSizeError');
167
+ }
168
+
169
+ channelNumber = conversions['unsigned long'](channelNumber, {
170
+ context: `Failed to execute 'copyToChannel' on 'AudioBuffer': channelNumber`,
171
+ });
172
+
173
+ bufferOffset = conversions['unsigned long'](bufferOffset, {
174
+ context: `Failed to execute 'copyToChannel' on 'AudioBuffer': bufferOffset`,
175
+ });
176
+
177
+ try {
178
+ this[kNapiObj].copyToChannel(source, channelNumber, bufferOffset);
179
+ } catch (err) {
180
+ throwSanitizedError(err);
181
+ }
182
+ }
183
+
184
+ getChannelData(channel) {
185
+ if (!(this instanceof AudioBuffer)) {
186
+ throw new TypeError(`Invalid Invocation: Value of 'this' must be of type 'AudioBuffer'`);
187
+ }
188
+
189
+ if (arguments.length < 1) {
190
+ throw new TypeError(`Failed to execute 'getChannelData' on 'AudioBuffer': 1 argument required, but only ${arguments.length} present`);
191
+ }
192
+
193
+ // Rust implementation uses a usize which will clamp -1 to 0, and spec requires
194
+ // an IndexSizeError rather than a TypeError, so this check must be done here.
195
+ // cf. note on AnalyzerNode::fftSize
196
+ if (channel < 0) {
197
+ throw new DOMException(`Failed to execute 'getChannelData' on 'AudioBuffer': channel must equal or greater than 0`, 'IndexSizeError');
198
+ }
199
+
200
+ channel = conversions['unsigned long'](channel, {
201
+ context: `Failed to execute 'getChannelData' on 'AudioBuffer': channel`,
202
+ });
203
+
204
+ try {
205
+ return this[kNapiObj].getChannelData(channel);
206
+ } catch (err) {
207
+ throwSanitizedError(err);
208
+ }
209
+ }
210
+ }
211
+
212
+ Object.defineProperties(AudioBuffer, {
213
+ length: {
214
+ __proto__: null,
215
+ writable: false,
216
+ enumerable: false,
217
+ configurable: true,
218
+ value: 1,
219
+ },
220
+ });
221
+
222
+ Object.defineProperties(AudioBuffer.prototype, {
223
+ [Symbol.toStringTag]: {
224
+ __proto__: null,
225
+ writable: false,
226
+ enumerable: false,
227
+ configurable: true,
228
+ value: 'AudioBuffer',
229
+ },
230
+
231
+ sampleRate: kEnumerableProperty,
232
+ duration: kEnumerableProperty,
233
+ length: kEnumerableProperty,
234
+ numberOfChannels: kEnumerableProperty,
235
+ copyFromChannel: kEnumerableProperty,
236
+ copyToChannel: kEnumerableProperty,
237
+ getChannelData: kEnumerableProperty,
238
+ });
239
+
240
+ return AudioBuffer;
241
+ };
242
+
243
+
@@ -17,93 +17,311 @@
17
17
  // -------------------------------------------------------------------------- //
18
18
  // -------------------------------------------------------------------------- //
19
19
 
20
- // eslint-disable-next-line no-unused-vars
21
- const { throwSanitizedError } = require('./lib/errors.js');
22
- // eslint-disable-next-line no-unused-vars
23
- const { AudioParam } = require('./AudioParam.js');
24
- const EventTargetMixin = require('./EventTarget.mixin.js');
25
- const AudioNodeMixin = require('./AudioNode.mixin.js');
26
- const AudioScheduledSourceNodeMixin = require('./AudioScheduledSourceNode.mixin.js');
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
- module.exports = (NativeAudioBufferSourceNode) => {
29
-
30
- const EventTarget = EventTargetMixin(NativeAudioBufferSourceNode, ['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 AudioBufferSourceNode extends AudioScheduledSourceNode {
42
+
43
+ #playbackRate = null;
44
+ #detune = null;
45
+
35
46
  constructor(context, options) {
36
- if (options !== undefined && typeof options !== 'object') {
37
- throw new TypeError("Failed to construct 'AudioBufferSourceNode': argument 2 is not of type 'AudioBufferSourceOptions'")
47
+
48
+ if (arguments.length < 1) {
49
+ throw new TypeError(`Failed to construct 'AudioBufferSourceNode': 1 argument required, but only ${arguments.length} present`);
38
50
  }
39
51
 
40
- super(context, options);
41
- // EventTargetMixin has been called so EventTargetMixin[kDispatchEvent] is
42
- // bound to this, then we can safely finalize event target initialization
43
- super.__initEventTarget__();
52
+ if (!(context instanceof jsExport.BaseAudioContext)) {
53
+ throw new TypeError(`Failed to construct 'AudioBufferSourceNode': argument 1 is not of type BaseAudioContext`);
54
+ }
44
55
 
45
- this.playbackRate = new AudioParam(this.playbackRate);
46
- this.detune = new AudioParam(this.detune);
47
- }
56
+ // parsed version of the option to be passed to NAPI
57
+ const parsedOptions = {};
48
58
 
49
- // getters
59
+ if (options && typeof options !== 'object') {
60
+ throw new TypeError('Failed to construct \'AudioBufferSourceNode\': argument 2 is not of type \'AudioBufferSourceOptions\'');
61
+ }
50
62
 
51
- get buffer() {
52
- return super.buffer;
53
- }
63
+ if (options && options.buffer !== undefined) {
64
+ if (options.buffer !== null) {
65
+ if (!(options.buffer instanceof jsExport.AudioBuffer)) {
66
+ throw new TypeError('Failed to construct \'AudioBufferSourceNode\': Failed to read the \'buffer\' property from AudioBufferSourceOptions: The provided value cannot be converted to \'AudioBuffer\'');
67
+ }
54
68
 
55
- get loop() {
56
- return super.loop;
69
+ // unwrap napi audio buffer
70
+ parsedOptions.buffer = options.buffer[kNapiObj];
71
+ } else {
72
+ parsedOptions.buffer = null;
73
+ }
74
+ } else {
75
+ parsedOptions.buffer = null;
76
+ }
77
+
78
+ if (options && options.detune !== undefined) {
79
+ parsedOptions.detune = conversions['float'](options.detune, {
80
+ context: `Failed to construct 'AudioBufferSourceNode': Failed to read the 'detune' property from AudioBufferSourceOptions: The provided value (${options.detune}})`,
81
+ });
82
+ } else {
83
+ parsedOptions.detune = 0;
84
+ }
85
+
86
+ if (options && options.loop !== undefined) {
87
+ parsedOptions.loop = conversions['boolean'](options.loop, {
88
+ context: `Failed to construct 'AudioBufferSourceNode': Failed to read the 'loop' property from AudioBufferSourceOptions: The provided value (${options.loop}})`,
89
+ });
90
+ } else {
91
+ parsedOptions.loop = false;
92
+ }
93
+
94
+ if (options && options.loopEnd !== undefined) {
95
+ parsedOptions.loopEnd = conversions['double'](options.loopEnd, {
96
+ context: `Failed to construct 'AudioBufferSourceNode': Failed to read the 'loopEnd' property from AudioBufferSourceOptions: The provided value (${options.loopEnd}})`,
97
+ });
98
+ } else {
99
+ parsedOptions.loopEnd = 0;
100
+ }
101
+
102
+ if (options && options.loopStart !== undefined) {
103
+ parsedOptions.loopStart = conversions['double'](options.loopStart, {
104
+ context: `Failed to construct 'AudioBufferSourceNode': Failed to read the 'loopStart' property from AudioBufferSourceOptions: The provided value (${options.loopStart}})`,
105
+ });
106
+ } else {
107
+ parsedOptions.loopStart = 0;
108
+ }
109
+
110
+ if (options && options.playbackRate !== undefined) {
111
+ parsedOptions.playbackRate = conversions['float'](options.playbackRate, {
112
+ context: `Failed to construct 'AudioBufferSourceNode': Failed to read the 'playbackRate' property from AudioBufferSourceOptions: The provided value (${options.playbackRate}})`,
113
+ });
114
+ } else {
115
+ parsedOptions.playbackRate = 1;
116
+ }
117
+
118
+ let napiObj;
119
+
120
+ try {
121
+ napiObj = new nativeBinding.AudioBufferSourceNode(context[kNapiObj], parsedOptions);
122
+ } catch (err) {
123
+ throwSanitizedError(err);
124
+ }
125
+
126
+ super(context, {
127
+ [kNapiObj]: napiObj,
128
+ });
129
+
130
+ // keep the wrapped AudioBuffer around
131
+ Object.defineProperty(this, kAudioBuffer, {
132
+ __proto__: null,
133
+ enumerable: false,
134
+ writable: true,
135
+ value: null,
136
+ });
137
+
138
+ if (options && options.buffer !== undefined) {
139
+ this[kAudioBuffer] = options.buffer;
140
+ }
141
+
142
+ this.#playbackRate = new jsExport.AudioParam({
143
+ [kNapiObj]: this[kNapiObj].playbackRate,
144
+ });
145
+ this.#detune = new jsExport.AudioParam({
146
+ [kNapiObj]: this[kNapiObj].detune,
147
+ });
57
148
  }
58
149
 
59
- get loopStart() {
60
- return super.loopStart;
150
+ get playbackRate() {
151
+ if (!(this instanceof AudioBufferSourceNode)) {
152
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'AudioBufferSourceNode\'');
153
+ }
154
+
155
+ return this.#playbackRate;
61
156
  }
62
157
 
63
- get loopEnd() {
64
- return super.loopEnd;
158
+ get detune() {
159
+ if (!(this instanceof AudioBufferSourceNode)) {
160
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'AudioBufferSourceNode\'');
161
+ }
162
+
163
+ return this.#detune;
65
164
  }
66
165
 
67
- // setters
166
+ get buffer() {
167
+ if (!(this instanceof AudioBufferSourceNode)) {
168
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'AudioBufferSourceNode\'');
169
+ }
170
+
171
+ return this[kAudioBuffer];
172
+ }
68
173
 
69
174
  set buffer(value) {
175
+ if (!(this instanceof AudioBufferSourceNode)) {
176
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'AudioBufferSourceNode\'');
177
+ }
178
+
179
+ if (value === null) {
180
+ console.warn('Setting the \'buffer\' property on \'AudioBufferSourceNode\' to \'null\' is not supported yet');
181
+ return;
182
+ } else if (!(kNapiObj in value)) {
183
+ throw new TypeError('Failed to set the \'buffer\' property on \'AudioBufferSourceNode\': Failed to convert value to \'AudioBuffer\'');
184
+ }
185
+
70
186
  try {
71
- super.buffer = value;
187
+ this[kNapiObj].buffer = value[kNapiObj];
72
188
  } catch (err) {
73
189
  throwSanitizedError(err);
74
190
  }
191
+
192
+ this[kAudioBuffer] = value;
193
+ }
194
+
195
+ get loop() {
196
+ if (!(this instanceof AudioBufferSourceNode)) {
197
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'AudioBufferSourceNode\'');
198
+ }
199
+
200
+ return this[kNapiObj].loop;
75
201
  }
76
202
 
77
203
  set loop(value) {
204
+ if (!(this instanceof AudioBufferSourceNode)) {
205
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'AudioBufferSourceNode\'');
206
+ }
207
+
208
+ value = conversions['boolean'](value, {
209
+ context: `Failed to set the 'loop' property on 'AudioBufferSourceNode': Value`,
210
+ });
211
+
78
212
  try {
79
- super.loop = value;
213
+ this[kNapiObj].loop = value;
80
214
  } catch (err) {
81
215
  throwSanitizedError(err);
82
216
  }
83
217
  }
84
218
 
219
+ get loopStart() {
220
+ if (!(this instanceof AudioBufferSourceNode)) {
221
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'AudioBufferSourceNode\'');
222
+ }
223
+
224
+ return this[kNapiObj].loopStart;
225
+ }
226
+
85
227
  set loopStart(value) {
228
+ if (!(this instanceof AudioBufferSourceNode)) {
229
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'AudioBufferSourceNode\'');
230
+ }
231
+
232
+ value = conversions['double'](value, {
233
+ context: `Failed to set the 'loopStart' property on 'AudioBufferSourceNode': Value`,
234
+ });
235
+
86
236
  try {
87
- super.loopStart = value;
237
+ this[kNapiObj].loopStart = value;
88
238
  } catch (err) {
89
239
  throwSanitizedError(err);
90
240
  }
91
241
  }
92
242
 
243
+ get loopEnd() {
244
+ if (!(this instanceof AudioBufferSourceNode)) {
245
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'AudioBufferSourceNode\'');
246
+ }
247
+
248
+ return this[kNapiObj].loopEnd;
249
+ }
250
+
93
251
  set loopEnd(value) {
252
+ if (!(this instanceof AudioBufferSourceNode)) {
253
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'AudioBufferSourceNode\'');
254
+ }
255
+
256
+ value = conversions['double'](value, {
257
+ context: `Failed to set the 'loopEnd' property on 'AudioBufferSourceNode': Value`,
258
+ });
259
+
260
+ try {
261
+ this[kNapiObj].loopEnd = value;
262
+ } catch (err) {
263
+ throwSanitizedError(err);
264
+ }
265
+ }
266
+
267
+ start(when = 0, offset = null, duration = null) {
268
+ if (!(this instanceof AudioBufferSourceNode)) {
269
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'AudioBufferSourceNode\'');
270
+ }
271
+
272
+ if (when !== 0) {
273
+ when = conversions['double'](when, {
274
+ context: `Failed to execute 'start' on 'AudioBufferSourceNode': Parameter 1`,
275
+ });
276
+ }
277
+
278
+ if (offset !== null) {
279
+ offset = conversions['double'](offset, {
280
+ context: `Failed to execute 'start' on 'AudioBufferSourceNode': Parameter 2`,
281
+ });
282
+ }
283
+
284
+ if (duration !== null) {
285
+ duration = conversions['double'](duration, {
286
+ context: `Failed to execute 'start' on 'AudioBufferSourceNode': Parameter 3`,
287
+ });
288
+ }
289
+
94
290
  try {
95
- super.loopEnd = value;
291
+ return this[kNapiObj].start(when, offset, duration);
96
292
  } catch (err) {
97
293
  throwSanitizedError(err);
98
294
  }
99
295
  }
100
296
 
101
- // methods
102
-
103
297
  }
104
298
 
105
- return AudioBufferSourceNode;
106
- };
299
+ Object.defineProperties(AudioBufferSourceNode, {
300
+ length: {
301
+ __proto__: null,
302
+ writable: false,
303
+ enumerable: false,
304
+ configurable: true,
305
+ value: 1,
306
+ },
307
+ });
107
308
 
309
+ Object.defineProperties(AudioBufferSourceNode.prototype, {
310
+ [Symbol.toStringTag]: {
311
+ __proto__: null,
312
+ writable: false,
313
+ enumerable: false,
314
+ configurable: true,
315
+ value: 'AudioBufferSourceNode',
316
+ },
317
+ playbackRate: kEnumerableProperty,
318
+ detune: kEnumerableProperty,
319
+ buffer: kEnumerableProperty,
320
+ loop: kEnumerableProperty,
321
+ loopStart: kEnumerableProperty,
322
+ loopEnd: kEnumerableProperty,
323
+ start: kEnumerableProperty,
324
+ });
108
325
 
109
-
326
+ return AudioBufferSourceNode;
327
+ };