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
package/js/PeriodicWave.js
CHANGED
|
@@ -1,16 +1,80 @@
|
|
|
1
|
+
const conversions = require('webidl-conversions');
|
|
2
|
+
|
|
1
3
|
const { throwSanitizedError } = require('./lib/errors.js');
|
|
4
|
+
const { toSanitizedSequence } = require('./lib/cast.js');
|
|
5
|
+
const { kNapiObj } = require('./lib/symbols.js');
|
|
6
|
+
const { kHiddenProperty } = require('./lib/utils.js');
|
|
2
7
|
|
|
3
|
-
module.exports = (
|
|
4
|
-
class PeriodicWave
|
|
8
|
+
module.exports = (jsExport, nativeBinding) => {
|
|
9
|
+
class PeriodicWave {
|
|
5
10
|
constructor(context, options) {
|
|
11
|
+
if (arguments.length < 1) {
|
|
12
|
+
throw new TypeError(`Failed to construct 'PeriodicWave': 1 argument required, but only ${arguments.length} present`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (!(context instanceof jsExport.BaseAudioContext)) {
|
|
16
|
+
throw new TypeError(`Failed to construct 'PeriodicWave': argument 1 is not of type BaseAudioContext`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const parsedOptions = {};
|
|
20
|
+
|
|
21
|
+
if (options && 'real' in options) {
|
|
22
|
+
try {
|
|
23
|
+
parsedOptions.real = toSanitizedSequence(options.real, Float32Array);
|
|
24
|
+
} catch (err) {
|
|
25
|
+
throw new TypeError(`Failed to construct 'PeriodicWave': Failed to read the 'real' property from PeriodicWaveOptions: The provided value ${err.message}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (options && 'imag' in options) {
|
|
30
|
+
try {
|
|
31
|
+
parsedOptions.imag = toSanitizedSequence(options.imag, Float32Array);
|
|
32
|
+
} catch (err) {
|
|
33
|
+
throw new TypeError(`Failed to construct 'PeriodicWave': Failed to read the 'imag' property from PeriodicWaveOptions: The provided value ${err.message}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// disableNormalization = false
|
|
38
|
+
if (options && 'disableNormalization' in options) {
|
|
39
|
+
parsedOptions.disableNormalization = conversions['boolean'](options.disableNormalization, {
|
|
40
|
+
context: `Failed to construct 'PeriodicWave': Failed to read the 'imag' property from PeriodicWaveOptions: The provided value`,
|
|
41
|
+
});
|
|
42
|
+
} else {
|
|
43
|
+
parsedOptions.disableNormalization;
|
|
44
|
+
}
|
|
45
|
+
|
|
6
46
|
try {
|
|
7
|
-
|
|
47
|
+
const napiObj = new nativeBinding.PeriodicWave(context[kNapiObj], parsedOptions);
|
|
48
|
+
Object.defineProperty(this, kNapiObj, {
|
|
49
|
+
value: napiObj,
|
|
50
|
+
...kHiddenProperty,
|
|
51
|
+
});
|
|
8
52
|
} catch (err) {
|
|
9
53
|
throwSanitizedError(err);
|
|
10
54
|
}
|
|
11
55
|
}
|
|
12
56
|
}
|
|
13
57
|
|
|
58
|
+
Object.defineProperties(PeriodicWave, {
|
|
59
|
+
length: {
|
|
60
|
+
__proto__: null,
|
|
61
|
+
writable: false,
|
|
62
|
+
enumerable: false,
|
|
63
|
+
configurable: true,
|
|
64
|
+
value: 1,
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
Object.defineProperties(PeriodicWave.prototype, {
|
|
69
|
+
[Symbol.toStringTag]: {
|
|
70
|
+
__proto__: null,
|
|
71
|
+
writable: false,
|
|
72
|
+
enumerable: false,
|
|
73
|
+
configurable: true,
|
|
74
|
+
value: 'PeriodicWave',
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
14
78
|
return PeriodicWave;
|
|
15
79
|
};
|
|
16
80
|
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
const conversions = require('webidl-conversions');
|
|
3
|
+
const {
|
|
4
|
+
toSanitizedSequence,
|
|
5
|
+
} = require('./lib/cast.js');
|
|
6
|
+
const {
|
|
7
|
+
isFunction,
|
|
8
|
+
kEnumerableProperty,
|
|
9
|
+
} = require('./lib/utils.js');
|
|
10
|
+
const {
|
|
11
|
+
throwSanitizedError,
|
|
12
|
+
} = require('./lib/errors.js');
|
|
13
|
+
const {
|
|
14
|
+
kNapiObj,
|
|
15
|
+
kAudioBuffer,
|
|
16
|
+
kOnAudioProcess,
|
|
17
|
+
} = require('./lib/symbols.js');
|
|
18
|
+
const {
|
|
19
|
+
propagateEvent,
|
|
20
|
+
} = require('./lib/events.js');
|
|
21
|
+
/* eslint-enable no-unused-vars */
|
|
22
|
+
|
|
23
|
+
const AudioNode = require('./AudioNode.js');
|
|
24
|
+
|
|
25
|
+
module.exports = (jsExport, nativeBinding) => {
|
|
26
|
+
class ScriptProcessorNode extends AudioNode {
|
|
27
|
+
|
|
28
|
+
#onaudioprocess = null;
|
|
29
|
+
|
|
30
|
+
constructor(context, options) {
|
|
31
|
+
|
|
32
|
+
if (arguments.length < 1) {
|
|
33
|
+
throw new TypeError(`Failed to construct 'ScriptProcessorNode': 1 argument required, but only ${arguments.length} present`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!(context instanceof jsExport.BaseAudioContext)) {
|
|
37
|
+
throw new TypeError(`Failed to construct 'ScriptProcessorNode': argument 1 is not of type BaseAudioContext`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// parsed version of the option to be passed to NAPI
|
|
41
|
+
const parsedOptions = {};
|
|
42
|
+
|
|
43
|
+
if (options && typeof options !== 'object') {
|
|
44
|
+
throw new TypeError('Failed to construct \'ScriptProcessorNode\': argument 2 is not of type \'ScriptProcessorNodeOptions\'');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// IDL defines bufferSize default value as 0
|
|
48
|
+
// cf. https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-createscriptprocessor
|
|
49
|
+
// > If it’s not passed in, or if the value is 0, then the implementation
|
|
50
|
+
// > will choose the best buffer size for the given environment, which will
|
|
51
|
+
// > be constant power of 2 throughout the lifetime of the node.
|
|
52
|
+
if (options && options.bufferSize !== undefined && options.bufferSize !== 0) {
|
|
53
|
+
parsedOptions.bufferSize = conversions['unsigned long'](options.bufferSize, {
|
|
54
|
+
enforceRange: true,
|
|
55
|
+
context: `Failed to construct 'ScriptProcessorNode': Failed to read the 'bufferSize' property from ScriptProcessorNodeOptions: The provided value '${options.bufferSize}'`,
|
|
56
|
+
});
|
|
57
|
+
} else {
|
|
58
|
+
parsedOptions.bufferSize = 256;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (options && options.numberOfInputChannels !== undefined) {
|
|
62
|
+
parsedOptions.numberOfInputChannels = conversions['unsigned long'](options.numberOfInputChannels, {
|
|
63
|
+
enforceRange: true,
|
|
64
|
+
context: `Failed to construct 'ScriptProcessorNode': Failed to read the 'numberOfInputChannels' property from ScriptProcessorNodeOptions: The provided value '${options.numberOfInputChannels}'`,
|
|
65
|
+
});
|
|
66
|
+
} else {
|
|
67
|
+
parsedOptions.numberOfInputChannels = 2;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (options && options.numberOfOutputChannels !== undefined) {
|
|
71
|
+
parsedOptions.numberOfOutputChannels = conversions['unsigned long'](options.numberOfOutputChannels, {
|
|
72
|
+
enforceRange: true,
|
|
73
|
+
context: `Failed to construct 'ScriptProcessorNode': Failed to read the 'numberOfOutputChannels' property from ScriptProcessorNodeOptions: The provided value '${options.numberOfOutputChannels}'`,
|
|
74
|
+
});
|
|
75
|
+
} else {
|
|
76
|
+
parsedOptions.numberOfOutputChannels = 2;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (options && options.channelCount !== undefined) {
|
|
80
|
+
parsedOptions.channelCount = conversions['unsigned long'](options.channelCount, {
|
|
81
|
+
enforceRange: true,
|
|
82
|
+
context: `Failed to construct 'ScriptProcessorNode': Failed to read the 'channelCount' property from ScriptProcessorNodeOptions: The provided value '${options.channelCount}'`,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (options && options.channelCountMode !== undefined) {
|
|
87
|
+
parsedOptions.channelCountMode = conversions['DOMString'](options.channelCountMode, {
|
|
88
|
+
context: `Failed to construct 'ScriptProcessorNode': Failed to read the 'channelCount' property from ScriptProcessorNodeOptions: The provided value '${options.channelCountMode}'`,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (options && options.channelInterpretation !== undefined) {
|
|
93
|
+
parsedOptions.channelInterpretation = conversions['DOMString'](options.channelInterpretation, {
|
|
94
|
+
context: `Failed to construct 'ScriptProcessorNode': Failed to read the 'channelInterpretation' property from ScriptProcessorNodeOptions: The provided value '${options.channelInterpretation}'`,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let napiObj;
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
napiObj = new nativeBinding.ScriptProcessorNode(context[kNapiObj], parsedOptions);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
throwSanitizedError(err);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
super(context, {
|
|
107
|
+
[kNapiObj]: napiObj,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
this[kNapiObj][kOnAudioProcess] = (err, rawEvent) => {
|
|
111
|
+
if (typeof rawEvent !== 'object' && !('type' in rawEvent)) {
|
|
112
|
+
throw new TypeError('Invalid [kOnStateChange] Invocation: rawEvent should have a type property');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const audioProcessingEventInit = {
|
|
116
|
+
playbackTime: rawEvent.playbackTime,
|
|
117
|
+
inputBuffer: new jsExport.AudioBuffer({ [kNapiObj]: rawEvent.inputBuffer }),
|
|
118
|
+
outputBuffer: new jsExport.AudioBuffer({ [kNapiObj]: rawEvent.outputBuffer }),
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const event = new jsExport.AudioProcessingEvent('audioprocess', audioProcessingEventInit);
|
|
122
|
+
propagateEvent(this, event);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
this[kNapiObj].listen_to_events();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
get bufferSize() {
|
|
129
|
+
if (!(this instanceof ScriptProcessorNode)) {
|
|
130
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'ScriptProcessorNode\'');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return this[kNapiObj].bufferSize;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
get onaudioprocess() {
|
|
137
|
+
if (!(this instanceof ScriptProcessorNode)) {
|
|
138
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'ScriptProcessorNode\'');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return this.#onaudioprocess;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
set onaudioprocess(value) {
|
|
145
|
+
if (!(this instanceof ScriptProcessorNode)) {
|
|
146
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'ScriptProcessorNode\'');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (isFunction(value) || value === null) {
|
|
150
|
+
this.#onaudioprocess = value;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
Object.defineProperties(ScriptProcessorNode, {
|
|
156
|
+
length: {
|
|
157
|
+
__proto__: null,
|
|
158
|
+
writable: false,
|
|
159
|
+
enumerable: false,
|
|
160
|
+
configurable: true,
|
|
161
|
+
value: 0,
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
Object.defineProperties(ScriptProcessorNode.prototype, {
|
|
166
|
+
[Symbol.toStringTag]: {
|
|
167
|
+
__proto__: null,
|
|
168
|
+
writable: false,
|
|
169
|
+
enumerable: false,
|
|
170
|
+
configurable: true,
|
|
171
|
+
value: 'ScriptProcessorNode',
|
|
172
|
+
},
|
|
173
|
+
bufferSize: kEnumerableProperty,
|
|
174
|
+
onaudioprocess: kEnumerableProperty,
|
|
175
|
+
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
return ScriptProcessorNode;
|
|
179
|
+
};
|
package/js/StereoPannerNode.js
CHANGED
|
@@ -17,40 +17,123 @@
|
|
|
17
17
|
// -------------------------------------------------------------------------- //
|
|
18
18
|
// -------------------------------------------------------------------------- //
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
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 = (
|
|
40
|
+
module.exports = (jsExport, nativeBinding) => {
|
|
41
|
+
class StereoPannerNode extends AudioNode {
|
|
29
42
|
|
|
30
|
-
|
|
31
|
-
const AudioNode = AudioNodeMixin(EventTarget);
|
|
43
|
+
#pan = null;
|
|
32
44
|
|
|
33
|
-
class StereoPannerNode extends AudioNode {
|
|
34
45
|
constructor(context, options) {
|
|
35
|
-
|
|
36
|
-
|
|
46
|
+
|
|
47
|
+
if (arguments.length < 1) {
|
|
48
|
+
throw new TypeError(`Failed to construct 'StereoPannerNode': 1 argument required, but only ${arguments.length} present`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!(context instanceof jsExport.BaseAudioContext)) {
|
|
52
|
+
throw new TypeError(`Failed to construct 'StereoPannerNode': 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 \'StereoPannerNode\': argument 2 is not of type \'StereoPannerOptions\'');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (options && options.pan !== undefined) {
|
|
63
|
+
parsedOptions.pan = conversions['float'](options.pan, {
|
|
64
|
+
context: `Failed to construct 'StereoPannerNode': Failed to read the 'pan' property from StereoPannerOptions: The provided value (${options.pan}})`,
|
|
65
|
+
});
|
|
66
|
+
} else {
|
|
67
|
+
parsedOptions.pan = 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 'StereoPannerNode': Failed to read the 'channelCount' property from StereoPannerOptions: 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 'StereoPannerNode': Failed to read the 'channelCount' property from StereoPannerOptions: 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 'StereoPannerNode': Failed to read the 'channelInterpretation' property from StereoPannerOptions: The provided value '${options.channelInterpretation}'`,
|
|
86
|
+
});
|
|
37
87
|
}
|
|
38
88
|
|
|
39
|
-
|
|
89
|
+
let napiObj;
|
|
40
90
|
|
|
41
|
-
|
|
91
|
+
try {
|
|
92
|
+
napiObj = new nativeBinding.StereoPannerNode(context[kNapiObj], parsedOptions);
|
|
93
|
+
} catch (err) {
|
|
94
|
+
throwSanitizedError(err);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
super(context, {
|
|
98
|
+
[kNapiObj]: napiObj,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
this.#pan = new jsExport.AudioParam({
|
|
102
|
+
[kNapiObj]: this[kNapiObj].pan,
|
|
103
|
+
});
|
|
42
104
|
}
|
|
43
105
|
|
|
44
|
-
|
|
106
|
+
get pan() {
|
|
107
|
+
if (!(this instanceof StereoPannerNode)) {
|
|
108
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'StereoPannerNode\'');
|
|
109
|
+
}
|
|
45
110
|
|
|
46
|
-
|
|
111
|
+
return this.#pan;
|
|
112
|
+
}
|
|
47
113
|
|
|
48
|
-
// methods
|
|
49
|
-
|
|
50
114
|
}
|
|
51
115
|
|
|
52
|
-
|
|
53
|
-
|
|
116
|
+
Object.defineProperties(StereoPannerNode, {
|
|
117
|
+
length: {
|
|
118
|
+
__proto__: null,
|
|
119
|
+
writable: false,
|
|
120
|
+
enumerable: false,
|
|
121
|
+
configurable: true,
|
|
122
|
+
value: 1,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
54
125
|
|
|
126
|
+
Object.defineProperties(StereoPannerNode.prototype, {
|
|
127
|
+
[Symbol.toStringTag]: {
|
|
128
|
+
__proto__: null,
|
|
129
|
+
writable: false,
|
|
130
|
+
enumerable: false,
|
|
131
|
+
configurable: true,
|
|
132
|
+
value: 'StereoPannerNode',
|
|
133
|
+
},
|
|
134
|
+
pan: kEnumerableProperty,
|
|
55
135
|
|
|
56
|
-
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
return StereoPannerNode;
|
|
139
|
+
};
|
package/js/WaveShaperNode.js
CHANGED
|
@@ -17,63 +17,178 @@
|
|
|
17
17
|
// -------------------------------------------------------------------------- //
|
|
18
18
|
// -------------------------------------------------------------------------- //
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
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 */
|
|
37
|
+
|
|
38
|
+
const AudioNode = require('./AudioNode.js');
|
|
39
|
+
|
|
40
|
+
module.exports = (jsExport, nativeBinding) => {
|
|
41
|
+
class WaveShaperNode extends AudioNode {
|
|
26
42
|
|
|
43
|
+
constructor(context, options) {
|
|
27
44
|
|
|
28
|
-
|
|
45
|
+
if (arguments.length < 1) {
|
|
46
|
+
throw new TypeError(`Failed to construct 'WaveShaperNode': 1 argument required, but only ${arguments.length} present`);
|
|
47
|
+
}
|
|
29
48
|
|
|
30
|
-
|
|
31
|
-
|
|
49
|
+
if (!(context instanceof jsExport.BaseAudioContext)) {
|
|
50
|
+
throw new TypeError(`Failed to construct 'WaveShaperNode': argument 1 is not of type BaseAudioContext`);
|
|
51
|
+
}
|
|
32
52
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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 \'WaveShaperNode\': argument 2 is not of type \'WaveShaperOptions\'');
|
|
37
58
|
}
|
|
38
59
|
|
|
39
|
-
|
|
60
|
+
if (options && options.curve !== undefined) {
|
|
61
|
+
try {
|
|
62
|
+
parsedOptions.curve = toSanitizedSequence(options.curve, Float32Array);
|
|
63
|
+
} catch (err) {
|
|
64
|
+
throw new TypeError(`Failed to construct 'WaveShaperNode': Failed to read the 'curve' property from WaveShaperOptions: The provided value ${err.message}`);
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
parsedOptions.curve = null;
|
|
68
|
+
}
|
|
40
69
|
|
|
41
|
-
|
|
70
|
+
if (options && options.oversample !== undefined) {
|
|
71
|
+
if (!['none', '2x', '4x'].includes(options.oversample)) {
|
|
72
|
+
throw new TypeError(`Failed to construct 'WaveShaperNode': Failed to read the 'oversample' property from WaveShaperOptions: The provided value '${options.oversample}' is not a valid enum value of type OverSampleType`);
|
|
73
|
+
}
|
|
42
74
|
|
|
43
|
-
|
|
75
|
+
parsedOptions.oversample = conversions['DOMString'](options.oversample, {
|
|
76
|
+
context: `Failed to construct 'WaveShaperNode': Failed to read the 'oversample' property from WaveShaperOptions: The provided value '${options.oversample}'`,
|
|
77
|
+
});
|
|
78
|
+
} else {
|
|
79
|
+
parsedOptions.oversample = 'none';
|
|
80
|
+
}
|
|
44
81
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
82
|
+
if (options && options.channelCount !== undefined) {
|
|
83
|
+
parsedOptions.channelCount = conversions['unsigned long'](options.channelCount, {
|
|
84
|
+
enforceRange: true,
|
|
85
|
+
context: `Failed to construct 'WaveShaperNode': Failed to read the 'channelCount' property from WaveShaperOptions: The provided value '${options.channelCount}'`,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (options && options.channelCountMode !== undefined) {
|
|
90
|
+
parsedOptions.channelCountMode = conversions['DOMString'](options.channelCountMode, {
|
|
91
|
+
context: `Failed to construct 'WaveShaperNode': Failed to read the 'channelCount' property from WaveShaperOptions: The provided value '${options.channelCountMode}'`,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (options && options.channelInterpretation !== undefined) {
|
|
96
|
+
parsedOptions.channelInterpretation = conversions['DOMString'](options.channelInterpretation, {
|
|
97
|
+
context: `Failed to construct 'WaveShaperNode': Failed to read the 'channelInterpretation' property from WaveShaperOptions: The provided value '${options.channelInterpretation}'`,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let napiObj;
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
napiObj = new nativeBinding.WaveShaperNode(context[kNapiObj], parsedOptions);
|
|
105
|
+
} catch (err) {
|
|
106
|
+
throwSanitizedError(err);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
super(context, {
|
|
110
|
+
[kNapiObj]: napiObj,
|
|
111
|
+
});
|
|
48
112
|
|
|
49
|
-
get oversample() {
|
|
50
|
-
return super.oversample;
|
|
51
113
|
}
|
|
52
114
|
|
|
53
|
-
|
|
115
|
+
get curve() {
|
|
116
|
+
if (!(this instanceof WaveShaperNode)) {
|
|
117
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'WaveShaperNode\'');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return this[kNapiObj].curve;
|
|
121
|
+
}
|
|
54
122
|
|
|
55
123
|
set curve(value) {
|
|
124
|
+
if (!(this instanceof WaveShaperNode)) {
|
|
125
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'WaveShaperNode\'');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (value === null) {
|
|
129
|
+
console.warn('Setting the \'curve\' property on \'WaveShaperNode\' to \'null\' is not supported yet');
|
|
130
|
+
return;
|
|
131
|
+
} else if (!(value instanceof Float32Array)) {
|
|
132
|
+
throw new TypeError('Failed to set the \'curve\' property on \'WaveShaperNode\': Value is not a valid \'Float32Array\' value');
|
|
133
|
+
}
|
|
134
|
+
|
|
56
135
|
try {
|
|
57
|
-
|
|
136
|
+
this[kNapiObj].curve = value;
|
|
58
137
|
} catch (err) {
|
|
59
138
|
throwSanitizedError(err);
|
|
60
139
|
}
|
|
61
140
|
}
|
|
62
141
|
|
|
142
|
+
get oversample() {
|
|
143
|
+
if (!(this instanceof WaveShaperNode)) {
|
|
144
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'WaveShaperNode\'');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return this[kNapiObj].oversample;
|
|
148
|
+
}
|
|
149
|
+
|
|
63
150
|
set oversample(value) {
|
|
151
|
+
if (!(this instanceof WaveShaperNode)) {
|
|
152
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'WaveShaperNode\'');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (!['none', '2x', '4x'].includes(value)) {
|
|
156
|
+
console.warn(`Failed to set the 'oversample' property on 'WaveShaperNode': Value '${value}' is not a valid 'OverSampleType' enum value`);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
64
160
|
try {
|
|
65
|
-
|
|
161
|
+
this[kNapiObj].oversample = value;
|
|
66
162
|
} catch (err) {
|
|
67
163
|
throwSanitizedError(err);
|
|
68
164
|
}
|
|
69
165
|
}
|
|
70
166
|
|
|
71
|
-
// methods
|
|
72
|
-
|
|
73
167
|
}
|
|
74
168
|
|
|
169
|
+
Object.defineProperties(WaveShaperNode, {
|
|
170
|
+
length: {
|
|
171
|
+
__proto__: null,
|
|
172
|
+
writable: false,
|
|
173
|
+
enumerable: false,
|
|
174
|
+
configurable: true,
|
|
175
|
+
value: 1,
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
Object.defineProperties(WaveShaperNode.prototype, {
|
|
180
|
+
[Symbol.toStringTag]: {
|
|
181
|
+
__proto__: null,
|
|
182
|
+
writable: false,
|
|
183
|
+
enumerable: false,
|
|
184
|
+
configurable: true,
|
|
185
|
+
value: 'WaveShaperNode',
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
curve: kEnumerableProperty,
|
|
189
|
+
oversample: kEnumerableProperty,
|
|
190
|
+
|
|
191
|
+
});
|
|
192
|
+
|
|
75
193
|
return WaveShaperNode;
|
|
76
194
|
};
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
package/js/lib/cast.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
exports.toSanitizedSequence = function toSanitizedSequence(data, targetCtor) {
|
|
2
|
+
if (
|
|
3
|
+
(data.buffer && data.buffer instanceof ArrayBuffer)
|
|
4
|
+
|| Array.isArray(data)
|
|
5
|
+
) {
|
|
6
|
+
data = new targetCtor(data);
|
|
7
|
+
} else {
|
|
8
|
+
throw new TypeError(`cannot be converted to sequence of ${targetCtor}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// check it only contains finite values
|
|
12
|
+
for (let i = 0; i < data.length; i++) {
|
|
13
|
+
if (!Number.isFinite(data[i])) {
|
|
14
|
+
throw new TypeError(`should contain only finite values`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return data;
|
|
19
|
+
}
|