node-web-audio-api 0.16.0 → 0.18.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 +9 -0
- package/TODOS.md +21 -0
- package/index.mjs +3 -2
- package/js/AnalyserNode.js +4 -0
- package/js/AudioBufferSourceNode.js +4 -0
- package/js/AudioDestinationNode.js +111 -0
- package/js/AudioNode.mixin.js +11 -0
- package/js/BaseAudioContext.mixin.js +14 -0
- package/js/BiquadFilterNode.js +4 -0
- package/js/ChannelMergerNode.js +4 -0
- package/js/ChannelSplitterNode.js +4 -0
- package/js/ConstantSourceNode.js +4 -0
- package/js/ConvolverNode.js +4 -0
- package/js/DelayNode.js +4 -0
- package/js/DynamicsCompressorNode.js +4 -0
- package/js/EventTarget.mixin.js +1 -0
- package/js/GainNode.js +4 -0
- package/js/IIRFilterNode.js +4 -0
- package/js/MediaStreamAudioSourceNode.js +59 -0
- package/js/OfflineAudioContext.js +23 -11
- package/js/OscillatorNode.js +4 -0
- package/js/PannerNode.js +4 -0
- package/js/PeriodicWave.js +16 -0
- package/js/StereoPannerNode.js +4 -0
- package/js/WaveShaperNode.js +4 -0
- package/js/lib/errors.js +53 -32
- package/js/monkey-patch.js +8 -0
- 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 +5 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
## v0.18.0 (13/03/2024)
|
|
2
|
+
|
|
3
|
+
- Fix `MediaStreamAudioSourceNode`
|
|
4
|
+
|
|
5
|
+
## v0.17.0 (08/03/2024)
|
|
6
|
+
|
|
7
|
+
- Update upstream crate to [1.0.0-rc.2](https://github.com/orottier/web-audio-api-rs/blob/main/CHANGELOG.md#version-100-rc2-2024-03-07)
|
|
8
|
+
- Improve compliance and error handling
|
|
9
|
+
|
|
1
10
|
## v0.16.0 (09/02/2024)
|
|
2
11
|
|
|
3
12
|
- Update upstream create to [v0.42.0](https://github.com/orottier/web-audio-api-rs/blob/main/CHANGELOG.md#version-0420-2024-02-05)
|
package/TODOS.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# TODO
|
|
2
|
+
|
|
3
|
+
- [ ] Review AudioBuffer
|
|
4
|
+
- [ ] Ended event in AudioScheduledSourceNode for offline audio context
|
|
5
|
+
- [ ] `MediaStreamAudioSourceNode`
|
|
6
|
+
+ [x] properly handle `mediaStream`
|
|
7
|
+
+ [ ] do not accept OfflineAudioContext (see if this can be delegated to upstream)
|
|
8
|
+
|
|
9
|
+
- [ ] wpt bot
|
|
10
|
+
|
|
11
|
+
- [ ] OfflineAudioContext should not lock the process w/ event listeners if startRendering has not been called
|
|
12
|
+
|
|
13
|
+
- [ ] wrap EventTarget::dispatchEvent in setTimeout(callback , 0); so that events are dispatched in the next microtask ?
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## AudioBuffer notes
|
|
17
|
+
|
|
18
|
+
- [x] `AudioBuffer` has to be a facade because `startRendering`, `decodeAudioData`
|
|
19
|
+
- [x] need to adapt `AudioBufferSourceNode` and `ConvolverNode` so
|
|
20
|
+
that `set buffer(value)` retrieve the wrapped value
|
|
21
|
+
- [ ] No fucking sound when buffer comes from `decodeAudioData`..., but ok when `createBuffer`
|
package/index.mjs
CHANGED
|
@@ -27,10 +27,10 @@ const nativeModule = require('./index.cjs');
|
|
|
27
27
|
export const {
|
|
28
28
|
AudioContext,
|
|
29
29
|
OfflineAudioContext,
|
|
30
|
+
AudioParam,
|
|
31
|
+
AudioDestinationNode,
|
|
30
32
|
AudioBuffer,
|
|
31
33
|
PeriodicWave,
|
|
32
|
-
// manually written nodes
|
|
33
|
-
MediaStreamAudioSourceNode,
|
|
34
34
|
// generated supported nodes
|
|
35
35
|
AnalyserNode,
|
|
36
36
|
AudioBufferSourceNode,
|
|
@@ -43,6 +43,7 @@ export const {
|
|
|
43
43
|
DynamicsCompressorNode,
|
|
44
44
|
GainNode,
|
|
45
45
|
IIRFilterNode,
|
|
46
|
+
MediaStreamAudioSourceNode,
|
|
46
47
|
OscillatorNode,
|
|
47
48
|
PannerNode,
|
|
48
49
|
StereoPannerNode,
|
package/js/AnalyserNode.js
CHANGED
|
@@ -32,6 +32,10 @@ module.exports = (NativeAnalyserNode) => {
|
|
|
32
32
|
|
|
33
33
|
class AnalyserNode extends AudioNode {
|
|
34
34
|
constructor(context, options) {
|
|
35
|
+
if (options !== undefined && typeof options !== 'object') {
|
|
36
|
+
throw new TypeError("Failed to construct 'AnalyserNode': argument 2 is not of type 'AnalyserOptions'")
|
|
37
|
+
}
|
|
38
|
+
|
|
35
39
|
super(context, options);
|
|
36
40
|
|
|
37
41
|
}
|
|
@@ -33,6 +33,10 @@ module.exports = (NativeAudioBufferSourceNode) => {
|
|
|
33
33
|
|
|
34
34
|
class AudioBufferSourceNode extends AudioScheduledSourceNode {
|
|
35
35
|
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'")
|
|
38
|
+
}
|
|
39
|
+
|
|
36
40
|
super(context, options);
|
|
37
41
|
// EventTargetMixin has been called so EventTargetMixin[kDispatchEvent] is
|
|
38
42
|
// bound to this, then we can safely finalize event target initialization
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// @note - This should be reviewed (but how...?)
|
|
2
|
+
//
|
|
3
|
+
// We can't really use the AudioNode mixin because we need to wrap
|
|
4
|
+
// the native destination instance.
|
|
5
|
+
|
|
6
|
+
const { throwSanitizedError } = require('./lib/errors.js');
|
|
7
|
+
const { AudioParam, kNativeAudioParam } = require('./AudioParam.js');
|
|
8
|
+
const kNativeAudioDestinationNode = Symbol('node-web-audio-api:audio-destination-node');
|
|
9
|
+
|
|
10
|
+
class AudioDestinationNode {
|
|
11
|
+
constructor(nativeAudioDestinationNode) {
|
|
12
|
+
this[kNativeAudioDestinationNode] = nativeAudioDestinationNode;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// AudioNode interface
|
|
16
|
+
get context() {
|
|
17
|
+
return this[kNativeAudioDestinationNode].context;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get numberOfInputs() {
|
|
21
|
+
return this[kNativeAudioDestinationNode].numberOfInputs;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get numberOfOutputs() {
|
|
25
|
+
return this[kNativeAudioDestinationNode].numberOfOutputs;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get channelCount() {
|
|
29
|
+
return this[kNativeAudioDestinationNode].channelCount;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get channelCountMode() {
|
|
33
|
+
return this[kNativeAudioDestinationNode].channelCountMode;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get channelInterpretation() {
|
|
37
|
+
return this[kNativeAudioDestinationNode].channelInterpretation;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// setters
|
|
41
|
+
|
|
42
|
+
set channelCount(value) {
|
|
43
|
+
try {
|
|
44
|
+
this[kNativeAudioDestinationNode].channelCount = value;
|
|
45
|
+
} catch (err) {
|
|
46
|
+
throwSanitizedError(err);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
set channelCountMode(value) {
|
|
51
|
+
try {
|
|
52
|
+
this[kNativeAudioDestinationNode].channelCountMode = value;
|
|
53
|
+
} catch (err) {
|
|
54
|
+
throwSanitizedError(err);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
set channelInterpretation(value) {
|
|
59
|
+
try {
|
|
60
|
+
this[kNativeAudioDestinationNode].channelInterpretation = value;
|
|
61
|
+
} catch (err) {
|
|
62
|
+
throwSanitizedError(err);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// methods - connect / disconnect
|
|
67
|
+
|
|
68
|
+
connect(...args) {
|
|
69
|
+
// unwrap raw audio params from facade
|
|
70
|
+
if (args[0] instanceof AudioParam) {
|
|
71
|
+
args[0] = args[0][kNativeAudioParam];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// unwrap raw audio destination from facade
|
|
75
|
+
if (args[0] instanceof AudioDestinationNode) {
|
|
76
|
+
args[0] = args[0][kNativeAudioDestinationNode];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
return this[kNativeAudioDestinationNode].connect(...args);
|
|
81
|
+
} catch (err) {
|
|
82
|
+
throwSanitizedError(err);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
disconnect(...args) {
|
|
87
|
+
// unwrap raw audio params from facade
|
|
88
|
+
if (args[0] instanceof AudioParam) {
|
|
89
|
+
args[0] = args[0][kNativeAudioParam];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// unwrap raw audio destination from facade
|
|
93
|
+
if (args[0] instanceof AudioDestinationNode) {
|
|
94
|
+
args[0] = args[0][kNativeAudioDestinationNode];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
return this[kNativeAudioDestinationNode].disconnect(...args);
|
|
99
|
+
} catch (err) {
|
|
100
|
+
throwSanitizedError(err);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
get maxChannelCount() {
|
|
105
|
+
return this[kNativeAudioDestinationNode].maxChannelCount;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
module.exports.kNativeAudioDestinationNode = kNativeAudioDestinationNode;
|
|
110
|
+
module.exports.AudioDestinationNode = AudioDestinationNode;
|
|
111
|
+
|
package/js/AudioNode.mixin.js
CHANGED
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
const { throwSanitizedError } = require('./lib/errors.js');
|
|
21
21
|
|
|
22
22
|
const { AudioParam, kNativeAudioParam } = require('./AudioParam.js');
|
|
23
|
+
const { AudioDestinationNode, kNativeAudioDestinationNode } = require('./AudioDestinationNode.js');
|
|
23
24
|
|
|
24
25
|
module.exports = (superclass) => {
|
|
25
26
|
class AudioNode extends superclass {
|
|
@@ -93,6 +94,11 @@ module.exports = (superclass) => {
|
|
|
93
94
|
args[0] = args[0][kNativeAudioParam];
|
|
94
95
|
}
|
|
95
96
|
|
|
97
|
+
// unwrap raw audio destination from facade
|
|
98
|
+
if (args[0] instanceof AudioDestinationNode) {
|
|
99
|
+
args[0] = args[0][kNativeAudioDestinationNode];
|
|
100
|
+
}
|
|
101
|
+
|
|
96
102
|
try {
|
|
97
103
|
return super.connect(...args);
|
|
98
104
|
} catch (err) {
|
|
@@ -106,6 +112,11 @@ module.exports = (superclass) => {
|
|
|
106
112
|
args[0] = args[0][kNativeAudioParam];
|
|
107
113
|
}
|
|
108
114
|
|
|
115
|
+
// unwrap raw audio destination from facade
|
|
116
|
+
if (args[0] instanceof AudioDestinationNode) {
|
|
117
|
+
args[0] = args[0][kNativeAudioDestinationNode];
|
|
118
|
+
}
|
|
119
|
+
|
|
109
120
|
try {
|
|
110
121
|
return super.disconnect(...args);
|
|
111
122
|
} catch (err) {
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
// -------------------------------------------------------------------------- //
|
|
18
18
|
// -------------------------------------------------------------------------- //
|
|
19
19
|
|
|
20
|
+
const { AudioDestinationNode } = require('./AudioDestinationNode.js');
|
|
20
21
|
const { isFunction } = require('./lib/utils.js');
|
|
21
22
|
|
|
22
23
|
module.exports = (superclass, bindings) => {
|
|
@@ -32,13 +33,21 @@ module.exports = (superclass, bindings) => {
|
|
|
32
33
|
DynamicsCompressorNode,
|
|
33
34
|
GainNode,
|
|
34
35
|
IIRFilterNode,
|
|
36
|
+
MediaStreamAudioSourceNode,
|
|
35
37
|
OscillatorNode,
|
|
36
38
|
PannerNode,
|
|
37
39
|
StereoPannerNode,
|
|
38
40
|
WaveShaperNode,
|
|
41
|
+
PeriodicWave,
|
|
39
42
|
} = bindings;
|
|
40
43
|
|
|
41
44
|
class BaseAudioContext extends superclass {
|
|
45
|
+
constructor(...args) {
|
|
46
|
+
super(...args);
|
|
47
|
+
|
|
48
|
+
this.destination = new AudioDestinationNode(this.destination);
|
|
49
|
+
}
|
|
50
|
+
|
|
42
51
|
// This is not exactly what the spec says, but if we reject the promise
|
|
43
52
|
// when decodeErrorCallback is present the program will crash in an
|
|
44
53
|
// unexpected manner
|
|
@@ -65,6 +74,10 @@ module.exports = (superclass, bindings) => {
|
|
|
65
74
|
}
|
|
66
75
|
}
|
|
67
76
|
|
|
77
|
+
createPeriodicWave(real, imag) {
|
|
78
|
+
return new PeriodicWave(this, { real, imag });
|
|
79
|
+
}
|
|
80
|
+
|
|
68
81
|
// --------------------------------------------------------------------
|
|
69
82
|
// Factory Methods (use the patched AudioNodes)
|
|
70
83
|
// --------------------------------------------------------------------
|
|
@@ -116,6 +129,7 @@ module.exports = (superclass, bindings) => {
|
|
|
116
129
|
return new IIRFilterNode(this, options);
|
|
117
130
|
}
|
|
118
131
|
|
|
132
|
+
|
|
119
133
|
createOscillator() {
|
|
120
134
|
return new OscillatorNode(this);
|
|
121
135
|
}
|
package/js/BiquadFilterNode.js
CHANGED
|
@@ -32,6 +32,10 @@ module.exports = (NativeBiquadFilterNode) => {
|
|
|
32
32
|
|
|
33
33
|
class BiquadFilterNode extends AudioNode {
|
|
34
34
|
constructor(context, options) {
|
|
35
|
+
if (options !== undefined && typeof options !== 'object') {
|
|
36
|
+
throw new TypeError("Failed to construct 'BiquadFilterNode': argument 2 is not of type 'BiquadFilterOptions'")
|
|
37
|
+
}
|
|
38
|
+
|
|
35
39
|
super(context, options);
|
|
36
40
|
|
|
37
41
|
this.frequency = new AudioParam(this.frequency);
|
package/js/ChannelMergerNode.js
CHANGED
|
@@ -32,6 +32,10 @@ module.exports = (NativeChannelMergerNode) => {
|
|
|
32
32
|
|
|
33
33
|
class ChannelMergerNode extends AudioNode {
|
|
34
34
|
constructor(context, options) {
|
|
35
|
+
if (options !== undefined && typeof options !== 'object') {
|
|
36
|
+
throw new TypeError("Failed to construct 'ChannelMergerNode': argument 2 is not of type 'ChannelMergerOptions'")
|
|
37
|
+
}
|
|
38
|
+
|
|
35
39
|
super(context, options);
|
|
36
40
|
|
|
37
41
|
}
|
|
@@ -32,6 +32,10 @@ module.exports = (NativeChannelSplitterNode) => {
|
|
|
32
32
|
|
|
33
33
|
class ChannelSplitterNode extends AudioNode {
|
|
34
34
|
constructor(context, options) {
|
|
35
|
+
if (options !== undefined && typeof options !== 'object') {
|
|
36
|
+
throw new TypeError("Failed to construct 'ChannelSplitterNode': argument 2 is not of type 'ChannelSplitterOptions'")
|
|
37
|
+
}
|
|
38
|
+
|
|
35
39
|
super(context, options);
|
|
36
40
|
|
|
37
41
|
}
|
package/js/ConstantSourceNode.js
CHANGED
|
@@ -33,6 +33,10 @@ module.exports = (NativeConstantSourceNode) => {
|
|
|
33
33
|
|
|
34
34
|
class ConstantSourceNode extends AudioScheduledSourceNode {
|
|
35
35
|
constructor(context, options) {
|
|
36
|
+
if (options !== undefined && typeof options !== 'object') {
|
|
37
|
+
throw new TypeError("Failed to construct 'ConstantSourceNode': argument 2 is not of type 'ConstantSourceOptions'")
|
|
38
|
+
}
|
|
39
|
+
|
|
36
40
|
super(context, options);
|
|
37
41
|
// EventTargetMixin has been called so EventTargetMixin[kDispatchEvent] is
|
|
38
42
|
// bound to this, then we can safely finalize event target initialization
|
package/js/ConvolverNode.js
CHANGED
|
@@ -32,6 +32,10 @@ module.exports = (NativeConvolverNode) => {
|
|
|
32
32
|
|
|
33
33
|
class ConvolverNode extends AudioNode {
|
|
34
34
|
constructor(context, options) {
|
|
35
|
+
if (options !== undefined && typeof options !== 'object') {
|
|
36
|
+
throw new TypeError("Failed to construct 'ConvolverNode': argument 2 is not of type 'ConvolverOptions'")
|
|
37
|
+
}
|
|
38
|
+
|
|
35
39
|
super(context, options);
|
|
36
40
|
|
|
37
41
|
}
|
package/js/DelayNode.js
CHANGED
|
@@ -32,6 +32,10 @@ module.exports = (NativeDelayNode) => {
|
|
|
32
32
|
|
|
33
33
|
class DelayNode extends AudioNode {
|
|
34
34
|
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'")
|
|
37
|
+
}
|
|
38
|
+
|
|
35
39
|
super(context, options);
|
|
36
40
|
|
|
37
41
|
this.delayTime = new AudioParam(this.delayTime);
|
|
@@ -32,6 +32,10 @@ module.exports = (NativeDynamicsCompressorNode) => {
|
|
|
32
32
|
|
|
33
33
|
class DynamicsCompressorNode extends AudioNode {
|
|
34
34
|
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'")
|
|
37
|
+
}
|
|
38
|
+
|
|
35
39
|
super(context, options);
|
|
36
40
|
|
|
37
41
|
this.threshold = new AudioParam(this.threshold);
|
package/js/EventTarget.mixin.js
CHANGED
package/js/GainNode.js
CHANGED
|
@@ -32,6 +32,10 @@ module.exports = (NativeGainNode) => {
|
|
|
32
32
|
|
|
33
33
|
class GainNode extends AudioNode {
|
|
34
34
|
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'")
|
|
37
|
+
}
|
|
38
|
+
|
|
35
39
|
super(context, options);
|
|
36
40
|
|
|
37
41
|
this.gain = new AudioParam(this.gain);
|
package/js/IIRFilterNode.js
CHANGED
|
@@ -32,6 +32,10 @@ module.exports = (NativeIIRFilterNode) => {
|
|
|
32
32
|
|
|
33
33
|
class IIRFilterNode extends AudioNode {
|
|
34
34
|
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'")
|
|
37
|
+
}
|
|
38
|
+
|
|
35
39
|
super(context, options);
|
|
36
40
|
|
|
37
41
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// -------------------------------------------------------------------------- //
|
|
2
|
+
// -------------------------------------------------------------------------- //
|
|
3
|
+
// //
|
|
4
|
+
// //
|
|
5
|
+
// //
|
|
6
|
+
// ██╗ ██╗ █████╗ ██████╗ ███╗ ██╗██╗███╗ ██╗ ██████╗ //
|
|
7
|
+
// ██║ ██║██╔══██╗██╔══██╗████╗ ██║██║████╗ ██║██╔════╝ //
|
|
8
|
+
// ██║ █╗ ██║███████║██████╔╝██╔██╗ ██║██║██╔██╗ ██║██║ ███╗ //
|
|
9
|
+
// ██║███╗██║██╔══██║██╔══██╗██║╚██╗██║██║██║╚██╗██║██║ ██║ //
|
|
10
|
+
// ╚███╔███╔╝██║ ██║██║ ██║██║ ╚████║██║██║ ╚████║╚██████╔╝ //
|
|
11
|
+
// ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝╚═╝ ╚═══╝ ╚═════╝ //
|
|
12
|
+
// //
|
|
13
|
+
// //
|
|
14
|
+
// - This file has been generated --------------------------- //
|
|
15
|
+
// //
|
|
16
|
+
// //
|
|
17
|
+
// -------------------------------------------------------------------------- //
|
|
18
|
+
// -------------------------------------------------------------------------- //
|
|
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
|
+
|
|
27
|
+
|
|
28
|
+
module.exports = (NativeMediaStreamAudioSourceNode) => {
|
|
29
|
+
|
|
30
|
+
const EventTarget = EventTargetMixin(NativeMediaStreamAudioSourceNode);
|
|
31
|
+
const AudioNode = AudioNodeMixin(EventTarget);
|
|
32
|
+
|
|
33
|
+
class MediaStreamAudioSourceNode extends AudioNode {
|
|
34
|
+
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'")
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
super(context, options);
|
|
40
|
+
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// getters
|
|
44
|
+
|
|
45
|
+
get mediaStream() {
|
|
46
|
+
return super.mediaStream;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// setters
|
|
50
|
+
|
|
51
|
+
// methods
|
|
52
|
+
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return MediaStreamAudioSourceNode;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { nameCodeMap, DOMException } = require('./lib/errors.js');
|
|
2
2
|
const { isPlainObject, isPositiveInt, isPositiveNumber } = require('./lib/utils.js');
|
|
3
3
|
|
|
4
4
|
module.exports = function patchOfflineAudioContext(bindings) {
|
|
@@ -13,31 +13,43 @@ module.exports = function patchOfflineAudioContext(bindings) {
|
|
|
13
13
|
constructor(...args) {
|
|
14
14
|
// handle initialisation with either an options object or a sequence of parameters
|
|
15
15
|
// https://webaudio.github.io/web-audio-api/#dom-offlineaudiocontext-constructor-contextoptions-contextoptions
|
|
16
|
-
if (isPlainObject(args[0])
|
|
17
|
-
&& 'numberOfChannels' in args[0] && 'length' in args[0] && 'sampleRate' in args[0]
|
|
16
|
+
if (isPlainObject(args[0]) && 'length' in args[0] && 'sampleRate' in args[0]
|
|
18
17
|
) {
|
|
19
|
-
|
|
18
|
+
let { numberOfChannels, length, sampleRate } = args[0];
|
|
19
|
+
if (numberOfChannels === undefined) {
|
|
20
|
+
numberOfChannels = 1;
|
|
21
|
+
}
|
|
20
22
|
args = [numberOfChannels, length, sampleRate];
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
const [numberOfChannels, length, sampleRate] = args;
|
|
24
26
|
|
|
25
27
|
if (!isPositiveInt(numberOfChannels)) {
|
|
26
|
-
throw new
|
|
28
|
+
throw new TypeError(`Invalid value for numberOfChannels: ${numberOfChannels}`);
|
|
27
29
|
} else if (!isPositiveInt(length)) {
|
|
28
|
-
throw new
|
|
30
|
+
throw new TypeError(`Invalid value for length: ${length}`);
|
|
29
31
|
} else if (!isPositiveNumber(sampleRate)) {
|
|
30
|
-
throw new
|
|
32
|
+
throw new TypeError(`Invalid value for sampleRate: ${sampleRate}`);
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
super(numberOfChannels, length, sampleRate);
|
|
34
36
|
|
|
35
|
-
// @todo - do not init the event target, no way to clean the thread safe
|
|
36
|
-
// functions for now
|
|
37
|
-
|
|
38
37
|
// EventTargetMixin has been called so EventTargetMixin[kDispatchEvent] is
|
|
39
38
|
// bound to this, then we can safely finalize event target initialization
|
|
40
|
-
|
|
39
|
+
super.__initEventTarget__();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async startRendering() {
|
|
43
|
+
const renderedBuffer = await super.startRendering();
|
|
44
|
+
|
|
45
|
+
// We do this here, so that we can just share the same audioBuffer instance.
|
|
46
|
+
// This also simplifies code on the rust side as we don't need to deal
|
|
47
|
+
// with the OfflineAudioCompletionEvent.
|
|
48
|
+
const event = new Event('complete');
|
|
49
|
+
event.renderedBuffer = renderedBuffer;
|
|
50
|
+
this.dispatchEvent(event)
|
|
51
|
+
|
|
52
|
+
return renderedBuffer;
|
|
41
53
|
}
|
|
42
54
|
}
|
|
43
55
|
|
package/js/OscillatorNode.js
CHANGED
|
@@ -33,6 +33,10 @@ module.exports = (NativeOscillatorNode) => {
|
|
|
33
33
|
|
|
34
34
|
class OscillatorNode extends AudioScheduledSourceNode {
|
|
35
35
|
constructor(context, options) {
|
|
36
|
+
if (options !== undefined && typeof options !== 'object') {
|
|
37
|
+
throw new TypeError("Failed to construct 'OscillatorNode': argument 2 is not of type 'OscillatorOptions'")
|
|
38
|
+
}
|
|
39
|
+
|
|
36
40
|
super(context, options);
|
|
37
41
|
// EventTargetMixin has been called so EventTargetMixin[kDispatchEvent] is
|
|
38
42
|
// bound to this, then we can safely finalize event target initialization
|
package/js/PannerNode.js
CHANGED
|
@@ -32,6 +32,10 @@ module.exports = (NativePannerNode) => {
|
|
|
32
32
|
|
|
33
33
|
class PannerNode extends AudioNode {
|
|
34
34
|
constructor(context, options) {
|
|
35
|
+
if (options !== undefined && typeof options !== 'object') {
|
|
36
|
+
throw new TypeError("Failed to construct 'PannerNode': argument 2 is not of type 'PannerOptions'")
|
|
37
|
+
}
|
|
38
|
+
|
|
35
39
|
super(context, options);
|
|
36
40
|
|
|
37
41
|
this.positionX = new AudioParam(this.positionX);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const { throwSanitizedError } = require('./lib/errors.js');
|
|
2
|
+
|
|
3
|
+
module.exports = (NativePeriodicWave) => {
|
|
4
|
+
class PeriodicWave extends NativePeriodicWave {
|
|
5
|
+
constructor(context, options) {
|
|
6
|
+
try {
|
|
7
|
+
super(context, options);
|
|
8
|
+
} catch (err) {
|
|
9
|
+
throwSanitizedError(err);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return PeriodicWave;
|
|
15
|
+
};
|
|
16
|
+
|
package/js/StereoPannerNode.js
CHANGED
|
@@ -32,6 +32,10 @@ module.exports = (NativeStereoPannerNode) => {
|
|
|
32
32
|
|
|
33
33
|
class StereoPannerNode extends AudioNode {
|
|
34
34
|
constructor(context, options) {
|
|
35
|
+
if (options !== undefined && typeof options !== 'object') {
|
|
36
|
+
throw new TypeError("Failed to construct 'StereoPannerNode': argument 2 is not of type 'StereoPannerOptions'")
|
|
37
|
+
}
|
|
38
|
+
|
|
35
39
|
super(context, options);
|
|
36
40
|
|
|
37
41
|
this.pan = new AudioParam(this.pan);
|
package/js/WaveShaperNode.js
CHANGED
|
@@ -32,6 +32,10 @@ module.exports = (NativeWaveShaperNode) => {
|
|
|
32
32
|
|
|
33
33
|
class WaveShaperNode extends AudioNode {
|
|
34
34
|
constructor(context, options) {
|
|
35
|
+
if (options !== undefined && typeof options !== 'object') {
|
|
36
|
+
throw new TypeError("Failed to construct 'WaveShaperNode': argument 2 is not of type 'WaveShaperOptions'")
|
|
37
|
+
}
|
|
38
|
+
|
|
35
39
|
super(context, options);
|
|
36
40
|
|
|
37
41
|
}
|
package/js/lib/errors.js
CHANGED
|
@@ -4,37 +4,57 @@ const path = require('path');
|
|
|
4
4
|
const internalPath = path.join('node-web-audio-api', 'js');
|
|
5
5
|
const internalRe = new RegExp(internalPath);
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
// from wpt/resources/tesharness.js (line 2226)
|
|
8
|
+
const nameCodeMap = {
|
|
9
|
+
IndexSizeError: 1,
|
|
10
|
+
HierarchyRequestError: 3,
|
|
11
|
+
WrongDocumentError: 4,
|
|
12
|
+
InvalidCharacterError: 5,
|
|
13
|
+
NoModificationAllowedError: 7,
|
|
14
|
+
NotFoundError: 8,
|
|
15
|
+
NotSupportedError: 9,
|
|
16
|
+
InUseAttributeError: 10,
|
|
17
|
+
InvalidStateError: 11,
|
|
18
|
+
SyntaxError: 12,
|
|
19
|
+
InvalidModificationError: 13,
|
|
20
|
+
NamespaceError: 14,
|
|
21
|
+
InvalidAccessError: 15,
|
|
22
|
+
TypeMismatchError: 17,
|
|
23
|
+
SecurityError: 18,
|
|
24
|
+
NetworkError: 19,
|
|
25
|
+
AbortError: 20,
|
|
26
|
+
URLMismatchError: 21,
|
|
27
|
+
QuotaExceededError: 22,
|
|
28
|
+
TimeoutError: 23,
|
|
29
|
+
InvalidNodeTypeError: 24,
|
|
30
|
+
DataCloneError: 25,
|
|
31
|
+
|
|
32
|
+
EncodingError: 0,
|
|
33
|
+
NotReadableError: 0,
|
|
34
|
+
UnknownError: 0,
|
|
35
|
+
ConstraintError: 0,
|
|
36
|
+
DataError: 0,
|
|
37
|
+
TransactionInactiveError: 0,
|
|
38
|
+
ReadOnlyError: 0,
|
|
39
|
+
VersionError: 0,
|
|
40
|
+
OperationError: 0,
|
|
41
|
+
NotAllowedError: 0,
|
|
42
|
+
OptOutError: 0
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
exports.nameCodeMap = nameCodeMap;
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class DOMException extends Error {
|
|
49
|
+
constructor(message, name) {
|
|
9
50
|
super(message);
|
|
10
|
-
this.name = 'NotSupportedError';
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
class InvalidStateError extends Error {
|
|
15
|
-
constructor(message) {
|
|
16
|
-
super(message);
|
|
17
|
-
this.name = 'InvalidStateError';
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
51
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
super(message);
|
|
24
|
-
this.name = 'IndexSizeError';
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
class InvalidAccessError extends Error {
|
|
29
|
-
constructor(message) {
|
|
30
|
-
super(message);
|
|
31
|
-
this.name = 'InvalidAccessError';
|
|
52
|
+
this.name = name;
|
|
53
|
+
this.code = nameCodeMap[this.name];
|
|
32
54
|
}
|
|
33
55
|
}
|
|
34
56
|
|
|
35
|
-
exports.
|
|
36
|
-
exports.InvalidStateError = InvalidStateError;
|
|
37
|
-
exports.IndexSizeError = IndexSizeError;
|
|
57
|
+
exports.DOMException = DOMException;
|
|
38
58
|
|
|
39
59
|
function overrideStack(originalError, newError) {
|
|
40
60
|
// override previous error message
|
|
@@ -60,6 +80,7 @@ exports.throwSanitizedError = function throwSanitizedError(err) {
|
|
|
60
80
|
// right: Max
|
|
61
81
|
let originalMessage = err.message;
|
|
62
82
|
originalMessage = originalMessage.replace('assertion `left != right` failed: ', '');
|
|
83
|
+
originalMessage = originalMessage.replace('assertion `left == right` failed: ', '');
|
|
63
84
|
originalMessage = originalMessage.split(EOL)[0]; // keep only first line
|
|
64
85
|
|
|
65
86
|
// "Native Errors"
|
|
@@ -76,33 +97,33 @@ exports.throwSanitizedError = function throwSanitizedError(err) {
|
|
|
76
97
|
throw error;
|
|
77
98
|
}
|
|
78
99
|
|
|
79
|
-
//
|
|
100
|
+
// DOM Exceptions
|
|
80
101
|
if (originalMessage.startsWith('NotSupportedError')) {
|
|
81
102
|
const msg = originalMessage.replace(/^NotSupportedError - /, '');
|
|
82
|
-
const error = new
|
|
103
|
+
const error = new DOMException(msg, 'NotSupportedError');
|
|
83
104
|
overrideStack(err, error);
|
|
84
105
|
|
|
85
106
|
throw error;
|
|
86
107
|
} else if (originalMessage.startsWith('InvalidStateError')) {
|
|
87
108
|
const msg = originalMessage.replace(/^InvalidStateError - /, '');
|
|
88
|
-
const error = new
|
|
109
|
+
const error = new DOMException(msg, 'InvalidStateError');
|
|
89
110
|
overrideStack(err, error);
|
|
90
111
|
|
|
91
112
|
throw error;
|
|
92
113
|
} if (originalMessage.startsWith('IndexSizeError')) {
|
|
93
114
|
const msg = originalMessage.replace(/^IndexSizeError - /, '');
|
|
94
|
-
const error = new
|
|
115
|
+
const error = new DOMException(msg, 'IndexSizeError');
|
|
95
116
|
overrideStack(err, error);
|
|
96
117
|
|
|
97
118
|
throw error;
|
|
98
119
|
} if (originalMessage.startsWith('InvalidAccessError')) {
|
|
99
120
|
const msg = originalMessage.replace(/^InvalidAccessError - /, '');
|
|
100
|
-
const error = new
|
|
121
|
+
const error = new DOMException(msg, 'InvalidAccessError');
|
|
101
122
|
overrideStack(err, error);
|
|
102
123
|
|
|
103
124
|
throw error;
|
|
104
125
|
}
|
|
105
126
|
|
|
106
|
-
console.warn('[lib/errors.js]
|
|
127
|
+
console.warn('[lib/errors.js] Possibly unhandled error type', err.message);
|
|
107
128
|
throw err;
|
|
108
129
|
}
|
package/js/monkey-patch.js
CHANGED
|
@@ -33,14 +33,22 @@ module.exports = function monkeyPatch(nativeBinding) {
|
|
|
33
33
|
nativeBinding.DynamicsCompressorNode = require('./DynamicsCompressorNode.js')(nativeBinding.DynamicsCompressorNode);
|
|
34
34
|
nativeBinding.GainNode = require('./GainNode.js')(nativeBinding.GainNode);
|
|
35
35
|
nativeBinding.IIRFilterNode = require('./IIRFilterNode.js')(nativeBinding.IIRFilterNode);
|
|
36
|
+
nativeBinding.MediaStreamAudioSourceNode = require('./MediaStreamAudioSourceNode.js')(nativeBinding.MediaStreamAudioSourceNode);
|
|
36
37
|
nativeBinding.OscillatorNode = require('./OscillatorNode.js')(nativeBinding.OscillatorNode);
|
|
37
38
|
nativeBinding.PannerNode = require('./PannerNode.js')(nativeBinding.PannerNode);
|
|
38
39
|
nativeBinding.StereoPannerNode = require('./StereoPannerNode.js')(nativeBinding.StereoPannerNode);
|
|
39
40
|
nativeBinding.WaveShaperNode = require('./WaveShaperNode.js')(nativeBinding.WaveShaperNode);
|
|
40
41
|
|
|
42
|
+
// @todo - wrap AudioBuffer interface as well
|
|
43
|
+
nativeBinding.PeriodicWave = require('./PeriodicWave.js')(nativeBinding.PeriodicWave);
|
|
44
|
+
|
|
41
45
|
nativeBinding.AudioContext = require('./AudioContext.js')(nativeBinding);
|
|
42
46
|
nativeBinding.OfflineAudioContext = require('./OfflineAudioContext.js')(nativeBinding);
|
|
43
47
|
|
|
48
|
+
// find a way to make the constructor private
|
|
49
|
+
nativeBinding.AudioParam = require('./AudioParam.js').AudioParam;
|
|
50
|
+
nativeBinding.AudioDestinationNode = require('./AudioDestinationNode.js').AudioDestinationNode;
|
|
51
|
+
|
|
44
52
|
// --------------------------------------------------------------------------
|
|
45
53
|
// Promisify MediaDevices API
|
|
46
54
|
// --------------------------------------------------------------------------
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-web-audio-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"author": "Benjamin Matuszewski",
|
|
5
5
|
"description": "Node.js bindings for web-audio-api-rs using napi-rs",
|
|
6
6
|
"exports": {
|
|
7
7
|
"import": "./index.mjs",
|
|
8
8
|
"require": "./index.cjs"
|
|
9
9
|
},
|
|
10
|
-
"repository":
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/ircam-ismm/node-web-audio-api.git"
|
|
13
|
+
},
|
|
11
14
|
"license": "BSD-3-Clause",
|
|
12
15
|
"keywords": [
|
|
13
16
|
"audio",
|