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.
Files changed (49) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/TODOS.md +140 -12
  3. package/index.mjs +10 -5
  4. package/js/AnalyserNode.js +262 -48
  5. package/js/AudioBuffer.js +244 -0
  6. package/js/AudioBufferSourceNode.js +265 -41
  7. package/js/AudioContext.js +271 -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 +105 -0
  13. package/js/BaseAudioContext.js +419 -0
  14. package/js/BiquadFilterNode.js +221 -29
  15. package/js/ChannelMergerNode.js +96 -22
  16. package/js/ChannelSplitterNode.js +96 -22
  17. package/js/ConstantSourceNode.js +92 -26
  18. package/js/ConvolverNode.js +161 -29
  19. package/js/DelayNode.js +115 -21
  20. package/js/DynamicsCompressorNode.js +198 -27
  21. package/js/GainNode.js +107 -21
  22. package/js/IIRFilterNode.js +139 -23
  23. package/js/MediaStreamAudioSourceNode.js +83 -24
  24. package/js/OfflineAudioContext.js +182 -34
  25. package/js/OscillatorNode.js +195 -32
  26. package/js/PannerNode.js +461 -56
  27. package/js/PeriodicWave.js +67 -3
  28. package/js/StereoPannerNode.js +107 -21
  29. package/js/WaveShaperNode.js +147 -29
  30. package/js/lib/cast.js +19 -0
  31. package/js/lib/errors.js +10 -55
  32. package/js/lib/events.js +20 -0
  33. package/js/lib/symbols.js +5 -0
  34. package/js/lib/utils.js +12 -12
  35. package/js/monkey-patch.js +32 -30
  36. package/node-web-audio-api.darwin-arm64.node +0 -0
  37. package/node-web-audio-api.darwin-x64.node +0 -0
  38. package/node-web-audio-api.linux-arm-gnueabihf.node +0 -0
  39. package/node-web-audio-api.linux-arm64-gnu.node +0 -0
  40. package/node-web-audio-api.linux-x64-gnu.node +0 -0
  41. package/node-web-audio-api.win32-arm64-msvc.node +0 -0
  42. package/node-web-audio-api.win32-x64-msvc.node +0 -0
  43. package/package.json +7 -4
  44. package/run-wpt.md +27 -0
  45. package/run-wpt.sh +5 -0
  46. package/js/AudioNode.mixin.js +0 -132
  47. package/js/AudioScheduledSourceNode.mixin.js +0 -67
  48. package/js/BaseAudioContext.mixin.js +0 -154
  49. package/js/EventTarget.mixin.js +0 -60
package/js/DelayNode.js CHANGED
@@ -17,40 +17,134 @@
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');
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 = (NativeDelayNode) => {
43
+ module.exports = (jsExport, nativeBinding) => {
44
+ class DelayNode extends AudioNode {
29
45
 
30
- const EventTarget = EventTargetMixin(NativeDelayNode);
31
- const AudioNode = AudioNodeMixin(EventTarget);
46
+ #delayTime = null;
32
47
 
33
- class DelayNode extends AudioNode {
34
48
  constructor(context, options) {
35
- if (options !== undefined && typeof options !== 'object') {
36
- throw new TypeError("Failed to construct 'DelayNode': argument 2 is not of type 'DelayOptions'")
49
+
50
+ if (arguments.length < 1) {
51
+ throw new TypeError(`Failed to construct 'DelayNode': 1 argument required, but only ${arguments.length} present`);
52
+ }
53
+
54
+ if (!(context instanceof jsExport.BaseAudioContext)) {
55
+ throw new TypeError(`Failed to construct 'DelayNode': argument 1 is not of type BaseAudioContext`);
56
+ }
57
+
58
+ // parsed version of the option to be passed to NAPI
59
+ const parsedOptions = {};
60
+
61
+ if (options && typeof options !== 'object') {
62
+ throw new TypeError('Failed to construct \'DelayNode\': argument 2 is not of type \'DelayOptions\'');
63
+ }
64
+
65
+ if (options && options.maxDelayTime !== undefined) {
66
+ parsedOptions.maxDelayTime = conversions['double'](options.maxDelayTime, {
67
+ context: `Failed to construct 'DelayNode': Failed to read the 'maxDelayTime' property from DelayOptions: The provided value (${options.maxDelayTime}})`,
68
+ });
69
+ } else {
70
+ parsedOptions.maxDelayTime = 1;
71
+ }
72
+
73
+ if (options && options.delayTime !== undefined) {
74
+ parsedOptions.delayTime = conversions['double'](options.delayTime, {
75
+ context: `Failed to construct 'DelayNode': Failed to read the 'delayTime' property from DelayOptions: The provided value (${options.delayTime}})`,
76
+ });
77
+ } else {
78
+ parsedOptions.delayTime = 0;
79
+ }
80
+
81
+ if (options && options.channelCount !== undefined) {
82
+ parsedOptions.channelCount = conversions['unsigned long'](options.channelCount, {
83
+ enforceRange: true,
84
+ context: `Failed to construct 'DelayNode': Failed to read the 'channelCount' property from DelayOptions: The provided value '${options.channelCount}'`,
85
+ });
37
86
  }
38
87
 
39
- super(context, options);
88
+ if (options && options.channelCountMode !== undefined) {
89
+ parsedOptions.channelCountMode = conversions['DOMString'](options.channelCountMode, {
90
+ context: `Failed to construct 'DelayNode': Failed to read the 'channelCount' property from DelayOptions: The provided value '${options.channelCountMode}'`,
91
+ });
92
+ }
93
+
94
+ if (options && options.channelInterpretation !== undefined) {
95
+ parsedOptions.channelInterpretation = conversions['DOMString'](options.channelInterpretation, {
96
+ context: `Failed to construct 'DelayNode': Failed to read the 'channelInterpretation' property from DelayOptions: The provided value '${options.channelInterpretation}'`,
97
+ });
98
+ }
99
+
100
+ let napiObj;
101
+
102
+ try {
103
+ napiObj = new nativeBinding.DelayNode(context[kNapiObj], parsedOptions);
104
+ } catch (err) {
105
+ throwSanitizedError(err);
106
+ }
107
+
108
+ super(context, {
109
+ [kNapiObj]: napiObj,
110
+ });
40
111
 
41
- this.delayTime = new AudioParam(this.delayTime);
112
+ this.#delayTime = new jsExport.AudioParam({
113
+ [kNapiObj]: this[kNapiObj].delayTime,
114
+ });
42
115
  }
43
116
 
44
- // getters
117
+ get delayTime() {
118
+ if (!(this instanceof DelayNode)) {
119
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'DelayNode\'');
120
+ }
45
121
 
46
- // setters
122
+ return this.#delayTime;
123
+ }
47
124
 
48
- // methods
49
-
50
125
  }
51
126
 
52
- return DelayNode;
53
- };
127
+ Object.defineProperties(DelayNode, {
128
+ length: {
129
+ __proto__: null,
130
+ writable: false,
131
+ enumerable: false,
132
+ configurable: true,
133
+ value: 1,
134
+ },
135
+ });
54
136
 
137
+ Object.defineProperties(DelayNode.prototype, {
138
+ [Symbol.toStringTag]: {
139
+ __proto__: null,
140
+ writable: false,
141
+ enumerable: false,
142
+ configurable: true,
143
+ value: 'DelayNode',
144
+ },
145
+ delayTime: kEnumerableProperty,
55
146
 
56
-
147
+ });
148
+
149
+ return DelayNode;
150
+ };
@@ -17,48 +17,219 @@
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');
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 = (NativeDynamicsCompressorNode) => {
43
+ module.exports = (jsExport, nativeBinding) => {
44
+ class DynamicsCompressorNode extends AudioNode {
29
45
 
30
- const EventTarget = EventTargetMixin(NativeDynamicsCompressorNode);
31
- const AudioNode = AudioNodeMixin(EventTarget);
46
+ #threshold = null;
47
+ #knee = null;
48
+ #ratio = null;
49
+ #attack = null;
50
+ #release = null;
32
51
 
33
- class DynamicsCompressorNode extends AudioNode {
34
52
  constructor(context, options) {
35
- if (options !== undefined && typeof options !== 'object') {
36
- throw new TypeError("Failed to construct 'DynamicsCompressorNode': argument 2 is not of type 'DynamicsCompressorOptions'")
53
+
54
+ if (arguments.length < 1) {
55
+ throw new TypeError(`Failed to construct 'DynamicsCompressorNode': 1 argument required, but only ${arguments.length} present`);
56
+ }
57
+
58
+ if (!(context instanceof jsExport.BaseAudioContext)) {
59
+ throw new TypeError(`Failed to construct 'DynamicsCompressorNode': argument 1 is not of type BaseAudioContext`);
60
+ }
61
+
62
+ // parsed version of the option to be passed to NAPI
63
+ const parsedOptions = {};
64
+
65
+ if (options && typeof options !== 'object') {
66
+ throw new TypeError('Failed to construct \'DynamicsCompressorNode\': argument 2 is not of type \'DynamicsCompressorOptions\'');
67
+ }
68
+
69
+ if (options && options.attack !== undefined) {
70
+ parsedOptions.attack = conversions['float'](options.attack, {
71
+ context: `Failed to construct 'DynamicsCompressorNode': Failed to read the 'attack' property from DynamicsCompressorOptions: The provided value (${options.attack}})`,
72
+ });
73
+ } else {
74
+ parsedOptions.attack = 0.003;
75
+ }
76
+
77
+ if (options && options.knee !== undefined) {
78
+ parsedOptions.knee = conversions['float'](options.knee, {
79
+ context: `Failed to construct 'DynamicsCompressorNode': Failed to read the 'knee' property from DynamicsCompressorOptions: The provided value (${options.knee}})`,
80
+ });
81
+ } else {
82
+ parsedOptions.knee = 30;
83
+ }
84
+
85
+ if (options && options.ratio !== undefined) {
86
+ parsedOptions.ratio = conversions['float'](options.ratio, {
87
+ context: `Failed to construct 'DynamicsCompressorNode': Failed to read the 'ratio' property from DynamicsCompressorOptions: The provided value (${options.ratio}})`,
88
+ });
89
+ } else {
90
+ parsedOptions.ratio = 12;
91
+ }
92
+
93
+ if (options && options.release !== undefined) {
94
+ parsedOptions.release = conversions['float'](options.release, {
95
+ context: `Failed to construct 'DynamicsCompressorNode': Failed to read the 'release' property from DynamicsCompressorOptions: The provided value (${options.release}})`,
96
+ });
97
+ } else {
98
+ parsedOptions.release = 0.25;
99
+ }
100
+
101
+ if (options && options.threshold !== undefined) {
102
+ parsedOptions.threshold = conversions['float'](options.threshold, {
103
+ context: `Failed to construct 'DynamicsCompressorNode': Failed to read the 'threshold' property from DynamicsCompressorOptions: The provided value (${options.threshold}})`,
104
+ });
105
+ } else {
106
+ parsedOptions.threshold = -24;
107
+ }
108
+
109
+ if (options && options.channelCount !== undefined) {
110
+ parsedOptions.channelCount = conversions['unsigned long'](options.channelCount, {
111
+ enforceRange: true,
112
+ context: `Failed to construct 'DynamicsCompressorNode': Failed to read the 'channelCount' property from DynamicsCompressorOptions: 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 'DynamicsCompressorNode': Failed to read the 'channelCount' property from DynamicsCompressorOptions: 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 'DynamicsCompressorNode': Failed to read the 'channelInterpretation' property from DynamicsCompressorOptions: The provided value '${options.channelInterpretation}'`,
125
+ });
126
+ }
127
+
128
+ let napiObj;
129
+
130
+ try {
131
+ napiObj = new nativeBinding.DynamicsCompressorNode(context[kNapiObj], parsedOptions);
132
+ } catch (err) {
133
+ throwSanitizedError(err);
37
134
  }
38
135
 
39
- super(context, options);
136
+ super(context, {
137
+ [kNapiObj]: napiObj,
138
+ });
40
139
 
41
- this.threshold = new AudioParam(this.threshold);
42
- this.knee = new AudioParam(this.knee);
43
- this.ratio = new AudioParam(this.ratio);
44
- this.attack = new AudioParam(this.attack);
45
- this.release = new AudioParam(this.release);
140
+ this.#threshold = new jsExport.AudioParam({
141
+ [kNapiObj]: this[kNapiObj].threshold,
142
+ });
143
+ this.#knee = new jsExport.AudioParam({
144
+ [kNapiObj]: this[kNapiObj].knee,
145
+ });
146
+ this.#ratio = new jsExport.AudioParam({
147
+ [kNapiObj]: this[kNapiObj].ratio,
148
+ });
149
+ this.#attack = new jsExport.AudioParam({
150
+ [kNapiObj]: this[kNapiObj].attack,
151
+ });
152
+ this.#release = new jsExport.AudioParam({
153
+ [kNapiObj]: this[kNapiObj].release,
154
+ });
46
155
  }
47
156
 
48
- // getters
157
+ get threshold() {
158
+ if (!(this instanceof DynamicsCompressorNode)) {
159
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'DynamicsCompressorNode\'');
160
+ }
49
161
 
50
- get reduction() {
51
- return super.reduction;
162
+ return this.#threshold;
163
+ }
164
+
165
+ get knee() {
166
+ if (!(this instanceof DynamicsCompressorNode)) {
167
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'DynamicsCompressorNode\'');
168
+ }
169
+
170
+ return this.#knee;
171
+ }
172
+
173
+ get ratio() {
174
+ if (!(this instanceof DynamicsCompressorNode)) {
175
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'DynamicsCompressorNode\'');
176
+ }
177
+
178
+ return this.#ratio;
52
179
  }
53
180
 
54
- // setters
181
+ get attack() {
182
+ if (!(this instanceof DynamicsCompressorNode)) {
183
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'DynamicsCompressorNode\'');
184
+ }
185
+
186
+ return this.#attack;
187
+ }
188
+
189
+ get release() {
190
+ if (!(this instanceof DynamicsCompressorNode)) {
191
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'DynamicsCompressorNode\'');
192
+ }
193
+
194
+ return this.#release;
195
+ }
196
+
197
+ get reduction() {
198
+ if (!(this instanceof DynamicsCompressorNode)) {
199
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'DynamicsCompressorNode\'');
200
+ }
201
+
202
+ return this[kNapiObj].reduction;
203
+ }
55
204
 
56
- // methods
57
-
58
205
  }
59
206
 
60
- return DynamicsCompressorNode;
61
- };
207
+ Object.defineProperties(DynamicsCompressorNode, {
208
+ length: {
209
+ __proto__: null,
210
+ writable: false,
211
+ enumerable: false,
212
+ configurable: true,
213
+ value: 1,
214
+ },
215
+ });
62
216
 
217
+ Object.defineProperties(DynamicsCompressorNode.prototype, {
218
+ [Symbol.toStringTag]: {
219
+ __proto__: null,
220
+ writable: false,
221
+ enumerable: false,
222
+ configurable: true,
223
+ value: 'DynamicsCompressorNode',
224
+ },
225
+ threshold: kEnumerableProperty,
226
+ knee: kEnumerableProperty,
227
+ ratio: kEnumerableProperty,
228
+ attack: kEnumerableProperty,
229
+ release: kEnumerableProperty,
230
+ reduction: kEnumerableProperty,
63
231
 
64
-
232
+ });
233
+
234
+ return DynamicsCompressorNode;
235
+ };
package/js/GainNode.js CHANGED
@@ -17,40 +17,126 @@
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');
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 = (NativeGainNode) => {
43
+ module.exports = (jsExport, nativeBinding) => {
44
+ class GainNode extends AudioNode {
29
45
 
30
- const EventTarget = EventTargetMixin(NativeGainNode);
31
- const AudioNode = AudioNodeMixin(EventTarget);
46
+ #gain = null;
32
47
 
33
- class GainNode extends AudioNode {
34
48
  constructor(context, options) {
35
- if (options !== undefined && typeof options !== 'object') {
36
- throw new TypeError("Failed to construct 'GainNode': argument 2 is not of type 'GainOptions'")
49
+
50
+ if (arguments.length < 1) {
51
+ throw new TypeError(`Failed to construct 'GainNode': 1 argument required, but only ${arguments.length} present`);
52
+ }
53
+
54
+ if (!(context instanceof jsExport.BaseAudioContext)) {
55
+ throw new TypeError(`Failed to construct 'GainNode': argument 1 is not of type BaseAudioContext`);
56
+ }
57
+
58
+ // parsed version of the option to be passed to NAPI
59
+ const parsedOptions = {};
60
+
61
+ if (options && typeof options !== 'object') {
62
+ throw new TypeError('Failed to construct \'GainNode\': argument 2 is not of type \'GainOptions\'');
63
+ }
64
+
65
+ if (options && options.gain !== undefined) {
66
+ parsedOptions.gain = conversions['float'](options.gain, {
67
+ context: `Failed to construct 'GainNode': Failed to read the 'gain' property from GainOptions: The provided value (${options.gain}})`,
68
+ });
69
+ } else {
70
+ parsedOptions.gain = 1.0;
71
+ }
72
+
73
+ if (options && options.channelCount !== undefined) {
74
+ parsedOptions.channelCount = conversions['unsigned long'](options.channelCount, {
75
+ enforceRange: true,
76
+ context: `Failed to construct 'GainNode': Failed to read the 'channelCount' property from GainOptions: The provided value '${options.channelCount}'`,
77
+ });
78
+ }
79
+
80
+ if (options && options.channelCountMode !== undefined) {
81
+ parsedOptions.channelCountMode = conversions['DOMString'](options.channelCountMode, {
82
+ context: `Failed to construct 'GainNode': Failed to read the 'channelCount' property from GainOptions: The provided value '${options.channelCountMode}'`,
83
+ });
84
+ }
85
+
86
+ if (options && options.channelInterpretation !== undefined) {
87
+ parsedOptions.channelInterpretation = conversions['DOMString'](options.channelInterpretation, {
88
+ context: `Failed to construct 'GainNode': Failed to read the 'channelInterpretation' property from GainOptions: The provided value '${options.channelInterpretation}'`,
89
+ });
37
90
  }
38
91
 
39
- super(context, options);
92
+ let napiObj;
40
93
 
41
- this.gain = new AudioParam(this.gain);
94
+ try {
95
+ napiObj = new nativeBinding.GainNode(context[kNapiObj], parsedOptions);
96
+ } catch (err) {
97
+ throwSanitizedError(err);
98
+ }
99
+
100
+ super(context, {
101
+ [kNapiObj]: napiObj,
102
+ });
103
+
104
+ this.#gain = new jsExport.AudioParam({
105
+ [kNapiObj]: this[kNapiObj].gain,
106
+ });
42
107
  }
43
108
 
44
- // getters
109
+ get gain() {
110
+ if (!(this instanceof GainNode)) {
111
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'GainNode\'');
112
+ }
45
113
 
46
- // setters
114
+ return this.#gain;
115
+ }
47
116
 
48
- // methods
49
-
50
117
  }
51
118
 
52
- return GainNode;
53
- };
119
+ Object.defineProperties(GainNode, {
120
+ length: {
121
+ __proto__: null,
122
+ writable: false,
123
+ enumerable: false,
124
+ configurable: true,
125
+ value: 1,
126
+ },
127
+ });
54
128
 
129
+ Object.defineProperties(GainNode.prototype, {
130
+ [Symbol.toStringTag]: {
131
+ __proto__: null,
132
+ writable: false,
133
+ enumerable: false,
134
+ configurable: true,
135
+ value: 'GainNode',
136
+ },
137
+ gain: kEnumerableProperty,
55
138
 
56
-
139
+ });
140
+
141
+ return GainNode;
142
+ };