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
package/js/Events.js ADDED
@@ -0,0 +1,84 @@
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 (typeof eventInitDict !== 'object' || eventInitDict === null || !('renderedBuffer' in eventInitDict)) {
10
+ throw TypeError(`Failed to construct 'OfflineAudioCompletionEvent': Failed to read the 'renderedBuffer' property from 'OfflineAudioCompletionEvent': Required member is undefined.`);
11
+ }
12
+
13
+ this.#renderedBuffer = eventInitDict.renderedBuffer;
14
+ }
15
+
16
+ get renderedBuffer() {
17
+ return this.#renderedBuffer;
18
+ }
19
+ }
20
+
21
+ Object.defineProperties(OfflineAudioCompletionEvent.prototype, {
22
+ [Symbol.toStringTag]: {
23
+ __proto__: null,
24
+ writable: false,
25
+ enumerable: false,
26
+ configurable: true,
27
+ value: 'OfflineAudioCompletionEvent',
28
+ },
29
+
30
+ renderedBuffer: kEnumerableProperty,
31
+ });
32
+
33
+ class AudioProcessingEvent extends Event {
34
+ #playbackTime = null;
35
+ #inputBuffer = null;
36
+ #outputBuffer = null;
37
+
38
+ constructor(type, eventInitDict) {
39
+ if (
40
+ typeof eventInitDict !== 'object'
41
+ || eventInitDict === null
42
+ || !('playbackTime' in eventInitDict)
43
+ || !('inputBuffer' in eventInitDict)
44
+ || !('outputBuffer' in eventInitDict)
45
+ ) {
46
+ throw TypeError(`Failed to construct 'AudioProcessingEvent': Invalid 'AudioProcessingEventInit' given`);
47
+ }
48
+
49
+ super(type);
50
+
51
+ this.#playbackTime = eventInitDict.playbackTime;
52
+ this.#inputBuffer = eventInitDict.inputBuffer;
53
+ this.#outputBuffer = eventInitDict.outputBuffer;
54
+ }
55
+
56
+ get playbackTime() {
57
+ return this.#playbackTime;
58
+ }
59
+
60
+ get inputBuffer() {
61
+ return this.#inputBuffer;
62
+ }
63
+
64
+ get outputBuffer() {
65
+ return this.#outputBuffer;
66
+ }
67
+ }
68
+
69
+ Object.defineProperties(AudioProcessingEvent.prototype, {
70
+ [Symbol.toStringTag]: {
71
+ __proto__: null,
72
+ writable: false,
73
+ enumerable: false,
74
+ configurable: true,
75
+ value: 'AudioProcessingEvent',
76
+ },
77
+
78
+ playbackTime: kEnumerableProperty,
79
+ inputBuffer: kEnumerableProperty,
80
+ outputBuffer: kEnumerableProperty,
81
+ });
82
+
83
+ module.exports.OfflineAudioCompletionEvent = OfflineAudioCompletionEvent;
84
+ module.exports.AudioProcessingEvent = AudioProcessingEvent;
package/js/GainNode.js CHANGED
@@ -17,40 +17,123 @@
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
+ /* eslint-enable no-unused-vars */
26
37
 
38
+ const AudioNode = require('./AudioNode.js');
27
39
 
28
- module.exports = (NativeGainNode) => {
40
+ module.exports = (jsExport, nativeBinding) => {
41
+ class GainNode extends AudioNode {
29
42
 
30
- const EventTarget = EventTargetMixin(NativeGainNode);
31
- const AudioNode = AudioNodeMixin(EventTarget);
43
+ #gain = null;
32
44
 
33
- class GainNode extends AudioNode {
34
45
  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'")
46
+
47
+ if (arguments.length < 1) {
48
+ throw new TypeError(`Failed to construct 'GainNode': 1 argument required, but only ${arguments.length} present`);
49
+ }
50
+
51
+ if (!(context instanceof jsExport.BaseAudioContext)) {
52
+ throw new TypeError(`Failed to construct 'GainNode': argument 1 is not of type BaseAudioContext`);
53
+ }
54
+
55
+ // parsed version of the option to be passed to NAPI
56
+ const parsedOptions = {};
57
+
58
+ if (options && typeof options !== 'object') {
59
+ throw new TypeError('Failed to construct \'GainNode\': argument 2 is not of type \'GainOptions\'');
60
+ }
61
+
62
+ if (options && options.gain !== undefined) {
63
+ parsedOptions.gain = conversions['float'](options.gain, {
64
+ context: `Failed to construct 'GainNode': Failed to read the 'gain' property from GainOptions: The provided value (${options.gain}})`,
65
+ });
66
+ } else {
67
+ parsedOptions.gain = 1.0;
68
+ }
69
+
70
+ if (options && options.channelCount !== undefined) {
71
+ parsedOptions.channelCount = conversions['unsigned long'](options.channelCount, {
72
+ enforceRange: true,
73
+ context: `Failed to construct 'GainNode': Failed to read the 'channelCount' property from GainOptions: The provided value '${options.channelCount}'`,
74
+ });
75
+ }
76
+
77
+ if (options && options.channelCountMode !== undefined) {
78
+ parsedOptions.channelCountMode = conversions['DOMString'](options.channelCountMode, {
79
+ context: `Failed to construct 'GainNode': Failed to read the 'channelCount' property from GainOptions: The provided value '${options.channelCountMode}'`,
80
+ });
81
+ }
82
+
83
+ if (options && options.channelInterpretation !== undefined) {
84
+ parsedOptions.channelInterpretation = conversions['DOMString'](options.channelInterpretation, {
85
+ context: `Failed to construct 'GainNode': Failed to read the 'channelInterpretation' property from GainOptions: The provided value '${options.channelInterpretation}'`,
86
+ });
37
87
  }
38
88
 
39
- super(context, options);
89
+ let napiObj;
40
90
 
41
- this.gain = new AudioParam(this.gain);
91
+ try {
92
+ napiObj = new nativeBinding.GainNode(context[kNapiObj], parsedOptions);
93
+ } catch (err) {
94
+ throwSanitizedError(err);
95
+ }
96
+
97
+ super(context, {
98
+ [kNapiObj]: napiObj,
99
+ });
100
+
101
+ this.#gain = new jsExport.AudioParam({
102
+ [kNapiObj]: this[kNapiObj].gain,
103
+ });
42
104
  }
43
105
 
44
- // getters
106
+ get gain() {
107
+ if (!(this instanceof GainNode)) {
108
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'GainNode\'');
109
+ }
45
110
 
46
- // setters
111
+ return this.#gain;
112
+ }
47
113
 
48
- // methods
49
-
50
114
  }
51
115
 
52
- return GainNode;
53
- };
116
+ Object.defineProperties(GainNode, {
117
+ length: {
118
+ __proto__: null,
119
+ writable: false,
120
+ enumerable: false,
121
+ configurable: true,
122
+ value: 1,
123
+ },
124
+ });
54
125
 
126
+ Object.defineProperties(GainNode.prototype, {
127
+ [Symbol.toStringTag]: {
128
+ __proto__: null,
129
+ writable: false,
130
+ enumerable: false,
131
+ configurable: true,
132
+ value: 'GainNode',
133
+ },
134
+ gain: kEnumerableProperty,
55
135
 
56
-
136
+ });
137
+
138
+ return GainNode;
139
+ };
@@ -17,38 +17,132 @@
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
+ /* eslint-enable no-unused-vars */
26
37
 
38
+ const AudioNode = require('./AudioNode.js');
27
39
 
28
- module.exports = (NativeIIRFilterNode) => {
29
-
30
- const EventTarget = EventTargetMixin(NativeIIRFilterNode);
31
- const AudioNode = AudioNodeMixin(EventTarget);
32
-
40
+ module.exports = (jsExport, nativeBinding) => {
33
41
  class IIRFilterNode extends AudioNode {
42
+
34
43
  constructor(context, options) {
35
- if (options !== undefined && typeof options !== 'object') {
36
- throw new TypeError("Failed to construct 'IIRFilterNode': argument 2 is not of type 'IIRFilterOptions'")
44
+
45
+ if (arguments.length < 2) {
46
+ throw new TypeError(`Failed to construct 'IIRFilterNode': 2 argument required, but only ${arguments.length} present`);
47
+ }
48
+
49
+ if (!(context instanceof jsExport.BaseAudioContext)) {
50
+ throw new TypeError(`Failed to construct 'IIRFilterNode': argument 1 is not of type BaseAudioContext`);
51
+ }
52
+
53
+ // parsed version of the option to be passed to NAPI
54
+ const parsedOptions = {};
55
+
56
+ if (options && typeof options !== 'object') {
57
+ throw new TypeError('Failed to construct \'IIRFilterNode\': argument 2 is not of type \'IIRFilterOptions\'');
58
+ }
59
+
60
+ // required options
61
+ if (typeof options !== 'object' || (options && options.feedforward === undefined)) {
62
+ throw new TypeError('Failed to construct \'IIRFilterNode\': Failed to read the \'feedforward\' property from IIRFilterOptions: Required member is undefined');
63
+ }
64
+
65
+ if (options && options.feedforward !== undefined) {
66
+ try {
67
+ parsedOptions.feedforward = toSanitizedSequence(options.feedforward, Float64Array);
68
+ } catch (err) {
69
+ throw new TypeError(`Failed to construct 'IIRFilterNode': Failed to read the 'feedforward' property from IIRFilterOptions: The provided value ${err.message}`);
70
+ }
71
+ } else {
72
+ parsedOptions.feedforward = null;
73
+ }
74
+
75
+ // required options
76
+ if (typeof options !== 'object' || (options && options.feedback === undefined)) {
77
+ throw new TypeError('Failed to construct \'IIRFilterNode\': Failed to read the \'feedback\' property from IIRFilterOptions: Required member is undefined');
78
+ }
79
+
80
+ if (options && options.feedback !== undefined) {
81
+ try {
82
+ parsedOptions.feedback = toSanitizedSequence(options.feedback, Float64Array);
83
+ } catch (err) {
84
+ throw new TypeError(`Failed to construct 'IIRFilterNode': Failed to read the 'feedback' property from IIRFilterOptions: The provided value ${err.message}`);
85
+ }
86
+ } else {
87
+ parsedOptions.feedback = null;
88
+ }
89
+
90
+ if (options && options.channelCount !== undefined) {
91
+ parsedOptions.channelCount = conversions['unsigned long'](options.channelCount, {
92
+ enforceRange: true,
93
+ context: `Failed to construct 'IIRFilterNode': Failed to read the 'channelCount' property from IIRFilterOptions: The provided value '${options.channelCount}'`,
94
+ });
95
+ }
96
+
97
+ if (options && options.channelCountMode !== undefined) {
98
+ parsedOptions.channelCountMode = conversions['DOMString'](options.channelCountMode, {
99
+ context: `Failed to construct 'IIRFilterNode': Failed to read the 'channelCount' property from IIRFilterOptions: The provided value '${options.channelCountMode}'`,
100
+ });
37
101
  }
38
102
 
39
- super(context, options);
103
+ if (options && options.channelInterpretation !== undefined) {
104
+ parsedOptions.channelInterpretation = conversions['DOMString'](options.channelInterpretation, {
105
+ context: `Failed to construct 'IIRFilterNode': Failed to read the 'channelInterpretation' property from IIRFilterOptions: The provided value '${options.channelInterpretation}'`,
106
+ });
107
+ }
108
+
109
+ let napiObj;
110
+
111
+ try {
112
+ napiObj = new nativeBinding.IIRFilterNode(context[kNapiObj], parsedOptions);
113
+ } catch (err) {
114
+ throwSanitizedError(err);
115
+ }
116
+
117
+ super(context, {
118
+ [kNapiObj]: napiObj,
119
+ });
40
120
 
41
121
  }
42
122
 
43
- // getters
123
+ getFrequencyResponse(frequencyHz, magResponse, phaseResponse) {
124
+ if (!(this instanceof IIRFilterNode)) {
125
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'IIRFilterNode\'');
126
+ }
44
127
 
45
- // setters
128
+ if (arguments.length < 3) {
129
+ throw new TypeError(`Failed to execute 'getFrequencyResponse' on 'IIRFilterNode': 3 argument required, but only ${arguments.length} present`);
130
+ }
131
+
132
+ if (!(frequencyHz instanceof Float32Array)) {
133
+ throw new TypeError(`Failed to execute 'getFrequencyResponse' on 'IIRFilterNode': Parameter 1 is not of type 'Float32Array'`);
134
+ }
135
+
136
+ if (!(magResponse instanceof Float32Array)) {
137
+ throw new TypeError(`Failed to execute 'getFrequencyResponse' on 'IIRFilterNode': Parameter 2 is not of type 'Float32Array'`);
138
+ }
139
+
140
+ if (!(phaseResponse instanceof Float32Array)) {
141
+ throw new TypeError(`Failed to execute 'getFrequencyResponse' on 'IIRFilterNode': Parameter 3 is not of type 'Float32Array'`);
142
+ }
46
143
 
47
- // methods
48
-
49
- getFrequencyResponse(...args) {
50
144
  try {
51
- return super.getFrequencyResponse(...args);
145
+ return this[kNapiObj].getFrequencyResponse(frequencyHz, magResponse, phaseResponse);
52
146
  } catch (err) {
53
147
  throwSanitizedError(err);
54
148
  }
@@ -56,8 +150,27 @@ module.exports = (NativeIIRFilterNode) => {
56
150
 
57
151
  }
58
152
 
59
- return IIRFilterNode;
60
- };
153
+ Object.defineProperties(IIRFilterNode, {
154
+ length: {
155
+ __proto__: null,
156
+ writable: false,
157
+ enumerable: false,
158
+ configurable: true,
159
+ value: 2,
160
+ },
161
+ });
162
+
163
+ Object.defineProperties(IIRFilterNode.prototype, {
164
+ [Symbol.toStringTag]: {
165
+ __proto__: null,
166
+ writable: false,
167
+ enumerable: false,
168
+ configurable: true,
169
+ value: 'IIRFilterNode',
170
+ },
61
171
 
172
+ getFrequencyResponse: kEnumerableProperty,
173
+ });
62
174
 
63
-
175
+ return IIRFilterNode;
176
+ };
@@ -17,43 +17,99 @@
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
+ /* eslint-enable no-unused-vars */
26
37
 
38
+ const AudioNode = require('./AudioNode.js');
27
39
 
28
- module.exports = (NativeMediaStreamAudioSourceNode) => {
29
-
30
- const EventTarget = EventTargetMixin(NativeMediaStreamAudioSourceNode);
31
- const AudioNode = AudioNodeMixin(EventTarget);
32
-
40
+ module.exports = (jsExport, nativeBinding) => {
33
41
  class MediaStreamAudioSourceNode extends AudioNode {
42
+
34
43
  constructor(context, options) {
35
- if (options !== undefined && typeof options !== 'object') {
36
- throw new TypeError("Failed to construct 'MediaStreamAudioSourceNode': argument 2 is not of type 'MediaStreamAudioSourceOptions'")
44
+
45
+ if (arguments.length < 2) {
46
+ throw new TypeError(`Failed to construct 'MediaStreamAudioSourceNode': 2 argument required, but only ${arguments.length} present`);
37
47
  }
38
48
 
39
- super(context, options);
49
+ if (!(context instanceof jsExport.AudioContext)) {
50
+ throw new TypeError(`Failed to construct 'MediaStreamAudioSourceNode': argument 1 is not of type AudioContext`);
51
+ }
40
52
 
41
- }
53
+ // parsed version of the option to be passed to NAPI
54
+ const parsedOptions = {};
42
55
 
43
- // getters
56
+ if (options && typeof options !== 'object') {
57
+ throw new TypeError('Failed to construct \'MediaStreamAudioSourceNode\': argument 2 is not of type \'MediaStreamAudioSourceOptions\'');
58
+ }
59
+
60
+ // required options
61
+ if (typeof options !== 'object' || (options && options.mediaStream === undefined)) {
62
+ throw new TypeError('Failed to construct \'MediaStreamAudioSourceNode\': Failed to read the \'mediaStream\' property from MediaStreamAudioSourceOptions: Required member is undefined');
63
+ }
64
+
65
+ parsedOptions.mediaStream = options.mediaStream;
66
+
67
+ let napiObj;
68
+
69
+ try {
70
+ napiObj = new nativeBinding.MediaStreamAudioSourceNode(context[kNapiObj], parsedOptions);
71
+ } catch (err) {
72
+ throwSanitizedError(err);
73
+ }
74
+
75
+ super(context, {
76
+ [kNapiObj]: napiObj,
77
+ });
44
78
 
45
- get mediaStream() {
46
- return super.mediaStream;
47
79
  }
48
80
 
49
- // setters
81
+ get mediaStream() {
82
+ if (!(this instanceof MediaStreamAudioSourceNode)) {
83
+ throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'MediaStreamAudioSourceNode\'');
84
+ }
85
+
86
+ return this[kNapiObj].mediaStream;
87
+ }
50
88
 
51
- // methods
52
-
53
89
  }
54
90
 
55
- return MediaStreamAudioSourceNode;
56
- };
91
+ Object.defineProperties(MediaStreamAudioSourceNode, {
92
+ length: {
93
+ __proto__: null,
94
+ writable: false,
95
+ enumerable: false,
96
+ configurable: true,
97
+ value: 2,
98
+ },
99
+ });
100
+
101
+ Object.defineProperties(MediaStreamAudioSourceNode.prototype, {
102
+ [Symbol.toStringTag]: {
103
+ __proto__: null,
104
+ writable: false,
105
+ enumerable: false,
106
+ configurable: true,
107
+ value: 'MediaStreamAudioSourceNode',
108
+ },
57
109
 
110
+ mediaStream: kEnumerableProperty,
58
111
 
59
-
112
+ });
113
+
114
+ return MediaStreamAudioSourceNode;
115
+ };