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.
- package/CHANGELOG.md +7 -0
- package/TODOS.md +140 -12
- package/index.mjs +10 -5
- package/js/AnalyserNode.js +262 -48
- package/js/AudioBuffer.js +244 -0
- package/js/AudioBufferSourceNode.js +265 -41
- package/js/AudioContext.js +271 -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 +105 -0
- package/js/BaseAudioContext.js +419 -0
- package/js/BiquadFilterNode.js +221 -29
- package/js/ChannelMergerNode.js +96 -22
- package/js/ChannelSplitterNode.js +96 -22
- package/js/ConstantSourceNode.js +92 -26
- package/js/ConvolverNode.js +161 -29
- package/js/DelayNode.js +115 -21
- package/js/DynamicsCompressorNode.js +198 -27
- package/js/GainNode.js +107 -21
- package/js/IIRFilterNode.js +139 -23
- package/js/MediaStreamAudioSourceNode.js +83 -24
- package/js/OfflineAudioContext.js +182 -34
- package/js/OscillatorNode.js +195 -32
- package/js/PannerNode.js +461 -56
- package/js/PeriodicWave.js +67 -3
- package/js/StereoPannerNode.js +107 -21
- package/js/WaveShaperNode.js +147 -29
- package/js/lib/cast.js +19 -0
- package/js/lib/errors.js +10 -55
- package/js/lib/events.js +20 -0
- package/js/lib/symbols.js +5 -0
- package/js/lib/utils.js +12 -12
- package/js/monkey-patch.js +32 -30
- 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/IIRFilterNode.js
CHANGED
|
@@ -17,38 +17,135 @@
|
|
|
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
|
+
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 = (
|
|
29
|
-
|
|
30
|
-
const EventTarget = EventTargetMixin(NativeIIRFilterNode);
|
|
31
|
-
const AudioNode = AudioNodeMixin(EventTarget);
|
|
32
|
-
|
|
43
|
+
module.exports = (jsExport, nativeBinding) => {
|
|
33
44
|
class IIRFilterNode extends AudioNode {
|
|
45
|
+
|
|
34
46
|
constructor(context, options) {
|
|
35
|
-
|
|
36
|
-
|
|
47
|
+
|
|
48
|
+
if (arguments.length < 2) {
|
|
49
|
+
throw new TypeError(`Failed to construct 'IIRFilterNode': 2 argument required, but only ${arguments.length} present`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!(context instanceof jsExport.BaseAudioContext)) {
|
|
53
|
+
throw new TypeError(`Failed to construct 'IIRFilterNode': argument 1 is not of type BaseAudioContext`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// parsed version of the option to be passed to NAPI
|
|
57
|
+
const parsedOptions = {};
|
|
58
|
+
|
|
59
|
+
if (options && typeof options !== 'object') {
|
|
60
|
+
throw new TypeError('Failed to construct \'IIRFilterNode\': argument 2 is not of type \'IIRFilterOptions\'');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// required options
|
|
64
|
+
if (typeof options !== 'object' || (options && options.feedforward === undefined)) {
|
|
65
|
+
throw new TypeError('Failed to construct \'IIRFilterNode\': Failed to read the \'feedforward\' property from IIRFilterOptions: Required member is undefined');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (options && options.feedforward !== undefined) {
|
|
69
|
+
try {
|
|
70
|
+
parsedOptions.feedforward = toSanitizedSequence(options.feedforward, Float64Array);
|
|
71
|
+
} catch (err) {
|
|
72
|
+
throw new TypeError(`Failed to construct 'IIRFilterNode': Failed to read the 'feedforward' property from IIRFilterOptions: The provided value ${err.message}`);
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
parsedOptions.feedforward = null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// required options
|
|
79
|
+
if (typeof options !== 'object' || (options && options.feedback === undefined)) {
|
|
80
|
+
throw new TypeError('Failed to construct \'IIRFilterNode\': Failed to read the \'feedback\' property from IIRFilterOptions: Required member is undefined');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (options && options.feedback !== undefined) {
|
|
84
|
+
try {
|
|
85
|
+
parsedOptions.feedback = toSanitizedSequence(options.feedback, Float64Array);
|
|
86
|
+
} catch (err) {
|
|
87
|
+
throw new TypeError(`Failed to construct 'IIRFilterNode': Failed to read the 'feedback' property from IIRFilterOptions: The provided value ${err.message}`);
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
parsedOptions.feedback = null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (options && options.channelCount !== undefined) {
|
|
94
|
+
parsedOptions.channelCount = conversions['unsigned long'](options.channelCount, {
|
|
95
|
+
enforceRange: true,
|
|
96
|
+
context: `Failed to construct 'IIRFilterNode': Failed to read the 'channelCount' property from IIRFilterOptions: The provided value '${options.channelCount}'`,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (options && options.channelCountMode !== undefined) {
|
|
101
|
+
parsedOptions.channelCountMode = conversions['DOMString'](options.channelCountMode, {
|
|
102
|
+
context: `Failed to construct 'IIRFilterNode': Failed to read the 'channelCount' property from IIRFilterOptions: The provided value '${options.channelCountMode}'`,
|
|
103
|
+
});
|
|
37
104
|
}
|
|
38
105
|
|
|
39
|
-
|
|
106
|
+
if (options && options.channelInterpretation !== undefined) {
|
|
107
|
+
parsedOptions.channelInterpretation = conversions['DOMString'](options.channelInterpretation, {
|
|
108
|
+
context: `Failed to construct 'IIRFilterNode': Failed to read the 'channelInterpretation' property from IIRFilterOptions: The provided value '${options.channelInterpretation}'`,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let napiObj;
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
napiObj = new nativeBinding.IIRFilterNode(context[kNapiObj], parsedOptions);
|
|
116
|
+
} catch (err) {
|
|
117
|
+
throwSanitizedError(err);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
super(context, {
|
|
121
|
+
[kNapiObj]: napiObj,
|
|
122
|
+
});
|
|
40
123
|
|
|
41
124
|
}
|
|
42
125
|
|
|
43
|
-
|
|
126
|
+
getFrequencyResponse(frequencyHz, magResponse, phaseResponse) {
|
|
127
|
+
if (!(this instanceof IIRFilterNode)) {
|
|
128
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'IIRFilterNode\'');
|
|
129
|
+
}
|
|
44
130
|
|
|
45
|
-
|
|
131
|
+
if (arguments.length < 3) {
|
|
132
|
+
throw new TypeError(`Failed to execute 'getFrequencyResponse' on 'IIRFilterNode': 3 argument required, but only ${arguments.length} present`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!(frequencyHz instanceof Float32Array)) {
|
|
136
|
+
throw new TypeError(`Failed to execute 'getFrequencyResponse' on 'IIRFilterNode': Parameter 1 is not of type 'Float32Array'`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!(magResponse instanceof Float32Array)) {
|
|
140
|
+
throw new TypeError(`Failed to execute 'getFrequencyResponse' on 'IIRFilterNode': Parameter 2 is not of type 'Float32Array'`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!(phaseResponse instanceof Float32Array)) {
|
|
144
|
+
throw new TypeError(`Failed to execute 'getFrequencyResponse' on 'IIRFilterNode': Parameter 3 is not of type 'Float32Array'`);
|
|
145
|
+
}
|
|
46
146
|
|
|
47
|
-
// methods
|
|
48
|
-
|
|
49
|
-
getFrequencyResponse(...args) {
|
|
50
147
|
try {
|
|
51
|
-
return
|
|
148
|
+
return this[kNapiObj].getFrequencyResponse(frequencyHz, magResponse, phaseResponse);
|
|
52
149
|
} catch (err) {
|
|
53
150
|
throwSanitizedError(err);
|
|
54
151
|
}
|
|
@@ -56,8 +153,27 @@ module.exports = (NativeIIRFilterNode) => {
|
|
|
56
153
|
|
|
57
154
|
}
|
|
58
155
|
|
|
59
|
-
|
|
60
|
-
|
|
156
|
+
Object.defineProperties(IIRFilterNode, {
|
|
157
|
+
length: {
|
|
158
|
+
__proto__: null,
|
|
159
|
+
writable: false,
|
|
160
|
+
enumerable: false,
|
|
161
|
+
configurable: true,
|
|
162
|
+
value: 2,
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
Object.defineProperties(IIRFilterNode.prototype, {
|
|
167
|
+
[Symbol.toStringTag]: {
|
|
168
|
+
__proto__: null,
|
|
169
|
+
writable: false,
|
|
170
|
+
enumerable: false,
|
|
171
|
+
configurable: true,
|
|
172
|
+
value: 'IIRFilterNode',
|
|
173
|
+
},
|
|
61
174
|
|
|
175
|
+
getFrequencyResponse: kEnumerableProperty,
|
|
176
|
+
});
|
|
62
177
|
|
|
63
|
-
|
|
178
|
+
return IIRFilterNode;
|
|
179
|
+
};
|
|
@@ -17,43 +17,102 @@
|
|
|
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
|
+
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 = (
|
|
29
|
-
|
|
30
|
-
const EventTarget = EventTargetMixin(NativeMediaStreamAudioSourceNode);
|
|
31
|
-
const AudioNode = AudioNodeMixin(EventTarget);
|
|
32
|
-
|
|
43
|
+
module.exports = (jsExport, nativeBinding) => {
|
|
33
44
|
class MediaStreamAudioSourceNode extends AudioNode {
|
|
45
|
+
|
|
34
46
|
constructor(context, options) {
|
|
35
|
-
|
|
36
|
-
|
|
47
|
+
|
|
48
|
+
if (arguments.length < 2) {
|
|
49
|
+
throw new TypeError(`Failed to construct 'MediaStreamAudioSourceNode': 2 argument required, but only ${arguments.length} present`);
|
|
37
50
|
}
|
|
38
51
|
|
|
39
|
-
|
|
52
|
+
if (!(context instanceof jsExport.AudioContext)) {
|
|
53
|
+
throw new TypeError(`Failed to construct 'MediaStreamAudioSourceNode': argument 1 is not of type AudioContext`);
|
|
54
|
+
}
|
|
40
55
|
|
|
41
|
-
|
|
56
|
+
// parsed version of the option to be passed to NAPI
|
|
57
|
+
const parsedOptions = {};
|
|
42
58
|
|
|
43
|
-
|
|
59
|
+
if (options && typeof options !== 'object') {
|
|
60
|
+
throw new TypeError('Failed to construct \'MediaStreamAudioSourceNode\': argument 2 is not of type \'MediaStreamAudioSourceOptions\'');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// required options
|
|
64
|
+
if (typeof options !== 'object' || (options && options.mediaStream === undefined)) {
|
|
65
|
+
throw new TypeError('Failed to construct \'MediaStreamAudioSourceNode\': Failed to read the \'mediaStream\' property from MediaStreamAudioSourceOptions: Required member is undefined');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
parsedOptions.mediaStream = options.mediaStream;
|
|
69
|
+
|
|
70
|
+
let napiObj;
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
napiObj = new nativeBinding.MediaStreamAudioSourceNode(context[kNapiObj], parsedOptions);
|
|
74
|
+
} catch (err) {
|
|
75
|
+
throwSanitizedError(err);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
super(context, {
|
|
79
|
+
[kNapiObj]: napiObj,
|
|
80
|
+
});
|
|
44
81
|
|
|
45
|
-
get mediaStream() {
|
|
46
|
-
return super.mediaStream;
|
|
47
82
|
}
|
|
48
83
|
|
|
49
|
-
|
|
84
|
+
get mediaStream() {
|
|
85
|
+
if (!(this instanceof MediaStreamAudioSourceNode)) {
|
|
86
|
+
throw new TypeError('Invalid Invocation: Value of \'this\' must be of type \'MediaStreamAudioSourceNode\'');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return this[kNapiObj].mediaStream;
|
|
90
|
+
}
|
|
50
91
|
|
|
51
|
-
// methods
|
|
52
|
-
|
|
53
92
|
}
|
|
54
93
|
|
|
55
|
-
|
|
56
|
-
|
|
94
|
+
Object.defineProperties(MediaStreamAudioSourceNode, {
|
|
95
|
+
length: {
|
|
96
|
+
__proto__: null,
|
|
97
|
+
writable: false,
|
|
98
|
+
enumerable: false,
|
|
99
|
+
configurable: true,
|
|
100
|
+
value: 2,
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
Object.defineProperties(MediaStreamAudioSourceNode.prototype, {
|
|
105
|
+
[Symbol.toStringTag]: {
|
|
106
|
+
__proto__: null,
|
|
107
|
+
writable: false,
|
|
108
|
+
enumerable: false,
|
|
109
|
+
configurable: true,
|
|
110
|
+
value: 'MediaStreamAudioSourceNode',
|
|
111
|
+
},
|
|
57
112
|
|
|
113
|
+
mediaStream: kEnumerableProperty,
|
|
58
114
|
|
|
59
|
-
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
return MediaStreamAudioSourceNode;
|
|
118
|
+
};
|
|
@@ -1,57 +1,205 @@
|
|
|
1
|
-
const
|
|
2
|
-
const { isPlainObject, isPositiveInt, isPositiveNumber } = require('./lib/utils.js');
|
|
1
|
+
const conversions = require('webidl-conversions');
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
const {
|
|
4
|
+
bridgeEventTarget,
|
|
5
|
+
} = require('./lib/events.js');
|
|
6
|
+
const {
|
|
7
|
+
throwSanitizedError,
|
|
8
|
+
} = require('./lib/errors.js');
|
|
9
|
+
const {
|
|
10
|
+
isFunction,
|
|
11
|
+
kEnumerableProperty,
|
|
12
|
+
} = require('./lib/utils.js');
|
|
13
|
+
const {
|
|
14
|
+
kNapiObj
|
|
15
|
+
} = require('./lib/symbols.js');
|
|
8
16
|
|
|
9
|
-
|
|
10
|
-
|
|
17
|
+
// constructor(OfflineAudioContextOptions contextOptions);
|
|
18
|
+
// constructor(unsigned long numberOfChannels, unsigned long length, float sampleRate);
|
|
19
|
+
// Promise<AudioBuffer> startRendering();
|
|
20
|
+
// Promise<undefined> resume();
|
|
21
|
+
// Promise<undefined> suspend(double suspendTime);
|
|
22
|
+
// readonly attribute unsigned long length;
|
|
23
|
+
// attribute EventHandler oncomplete;
|
|
11
24
|
|
|
12
|
-
|
|
25
|
+
module.exports = function patchOfflineAudioContext(jsExport, nativeBinding) {
|
|
26
|
+
class OfflineAudioContext extends jsExport.BaseAudioContext {
|
|
13
27
|
constructor(...args) {
|
|
14
|
-
|
|
28
|
+
if (arguments.length < 1) {
|
|
29
|
+
throw new TypeError(`Failed to construct 'OfflineAudioContext': 1 argument required, but only ${arguments.length} present`);
|
|
30
|
+
}
|
|
31
|
+
|
|
15
32
|
// https://webaudio.github.io/web-audio-api/#dom-offlineaudiocontext-constructor-contextoptions-contextoptions
|
|
16
|
-
if (
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
if (
|
|
20
|
-
|
|
33
|
+
if (arguments.length === 1) {
|
|
34
|
+
const options = args[0];
|
|
35
|
+
|
|
36
|
+
if (typeof options !== 'object') {
|
|
37
|
+
throw new TypeError(`Failed to construct 'OfflineAudioContext': argument 1 is not of type 'OfflineAudioContextOptions'`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (options.length === undefined) {
|
|
41
|
+
throw new TypeError(`Failed to construct 'OfflineAudioContext': Failed to read the 'length' property from 'OfflineAudioContextOptions': Required member is undefined.`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (options.sampleRate === undefined) {
|
|
45
|
+
throw new TypeError(`Failed to construct 'OfflineAudioContext': Failed to read the 'sampleRate' property from 'OfflineAudioContextOptions': Required member is undefined.`);
|
|
21
46
|
}
|
|
22
|
-
|
|
47
|
+
|
|
48
|
+
if (options.numberOfChannels === undefined) {
|
|
49
|
+
options.numberOfChannels = 1;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
args = [
|
|
53
|
+
options.numberOfChannels,
|
|
54
|
+
options.length,
|
|
55
|
+
options.sampleRate
|
|
56
|
+
];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let [numberOfChannels, length, sampleRate] = args;
|
|
60
|
+
|
|
61
|
+
numberOfChannels = conversions['unsigned long'](numberOfChannels, {
|
|
62
|
+
enforceRange: true,
|
|
63
|
+
context: `Failed to construct 'OfflineAudioContext': Failed to read the 'numberOfChannels' property from OfflineContextOptions; The provided value (${numberOfChannels})`
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
length = conversions['unsigned long'](length, {
|
|
67
|
+
enforceRange: true,
|
|
68
|
+
context: `Failed to construct 'OfflineAudioContext': Failed to read the 'length' property from OfflineContextOptions; The provided value (${length})`
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
sampleRate = conversions['float'](sampleRate, {
|
|
72
|
+
context: `Failed to construct 'OfflineAudioContext': Failed to read the 'sampleRate' property from OfflineContextOptions; The provided value (${sampleRate})`
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
let napiObj;
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
napiObj = new nativeBinding.OfflineAudioContext(numberOfChannels, length, sampleRate);
|
|
79
|
+
} catch (err) {
|
|
80
|
+
throwSanitizedError(err);
|
|
23
81
|
}
|
|
24
82
|
|
|
25
|
-
|
|
83
|
+
super({ [kNapiObj]: napiObj });
|
|
84
|
+
}
|
|
26
85
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
throw new TypeError(`Invalid value for length: ${length}`);
|
|
31
|
-
} else if (!isPositiveNumber(sampleRate)) {
|
|
32
|
-
throw new TypeError(`Invalid value for sampleRate: ${sampleRate}`);
|
|
86
|
+
get length() {
|
|
87
|
+
if (!(this instanceof OfflineAudioContext)) {
|
|
88
|
+
throw new TypeError("Invalid Invocation: Value of 'this' must be of type 'OfflineAudioContext'");
|
|
33
89
|
}
|
|
34
90
|
|
|
35
|
-
|
|
91
|
+
return this[kNapiObj].length;
|
|
92
|
+
}
|
|
36
93
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
94
|
+
get oncomplete() {
|
|
95
|
+
if (!(this instanceof OfflineAudioContext)) {
|
|
96
|
+
throw new TypeError("Invalid Invocation: Value of 'this' must be of type 'OfflineAudioContext'");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return this._complete || null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
set oncomplete(value) {
|
|
103
|
+
if (!(this instanceof OfflineAudioContext)) {
|
|
104
|
+
throw new TypeError("Invalid Invocation: Value of 'this' must be of type 'OfflineAudioContext'");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (isFunction(value) || value === null) {
|
|
108
|
+
this._complete = value;
|
|
109
|
+
}
|
|
40
110
|
}
|
|
41
111
|
|
|
42
112
|
async startRendering() {
|
|
43
|
-
|
|
113
|
+
if (!(this instanceof OfflineAudioContext)) {
|
|
114
|
+
throw new TypeError("Invalid Invocation: Value of 'this' must be of type 'OfflineAudioContext'");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Lazily register event callback on rust side
|
|
118
|
+
bridgeEventTarget(this);
|
|
44
119
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
120
|
+
let nativeAudioBuffer;
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
nativeAudioBuffer = await this[kNapiObj].startRendering();
|
|
124
|
+
} catch (err) {
|
|
125
|
+
throwSanitizedError(err);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const audioBuffer = new jsExport.AudioBuffer({ [kNapiObj]: nativeAudioBuffer });
|
|
129
|
+
|
|
130
|
+
// We dispatch the complete event manually to simplify the sharing of the
|
|
131
|
+
// `AudioBuffer` instance. This also simplifies code on the rust side as
|
|
132
|
+
// we don't need to deal with the `OfflineAudioCompletionEvent` type.
|
|
48
133
|
const event = new Event('complete');
|
|
49
|
-
event.renderedBuffer =
|
|
50
|
-
this.dispatchEvent(event)
|
|
134
|
+
event.renderedBuffer = audioBuffer;
|
|
51
135
|
|
|
52
|
-
|
|
136
|
+
if (isFunction(this[`oncomplete`])) {
|
|
137
|
+
this[`oncomplete`](event);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
this.dispatchEvent(event);
|
|
141
|
+
|
|
142
|
+
return audioBuffer;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async resume() {
|
|
146
|
+
if (!(this instanceof OfflineAudioContext)) {
|
|
147
|
+
throw new TypeError("Invalid Invocation: Value of 'this' must be of type 'OfflineAudioContext'");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
await this[kNapiObj].resume();
|
|
152
|
+
} catch (err) {
|
|
153
|
+
throwSanitizedError(err);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async suspend(suspendTime) {
|
|
158
|
+
if (!(this instanceof OfflineAudioContext)) {
|
|
159
|
+
throw new TypeError("Invalid Invocation: Value of 'this' must be of type 'OfflineAudioContext'");
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (arguments.length < 1) {
|
|
163
|
+
throw new TypeError(`Failed to execute 'suspend' on 'OfflineAudioContext': 1 argument required, but only ${arguments.length} present`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
suspendTime = conversions['double'](suspendTime, {
|
|
167
|
+
context: `Failed to execute 'suspend' on 'OfflineAudioContext': argument 1`,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
await this[kNapiObj].suspend(suspendTime);
|
|
172
|
+
} catch (err) {
|
|
173
|
+
throwSanitizedError(err);
|
|
174
|
+
}
|
|
53
175
|
}
|
|
54
176
|
}
|
|
55
177
|
|
|
178
|
+
Object.defineProperties(OfflineAudioContext, {
|
|
179
|
+
length: {
|
|
180
|
+
__proto__: null,
|
|
181
|
+
writable: false,
|
|
182
|
+
enumerable: false,
|
|
183
|
+
configurable: true,
|
|
184
|
+
value: 1,
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
Object.defineProperties(OfflineAudioContext.prototype, {
|
|
189
|
+
[Symbol.toStringTag]: {
|
|
190
|
+
__proto__: null,
|
|
191
|
+
writable: false,
|
|
192
|
+
enumerable: false,
|
|
193
|
+
configurable: true,
|
|
194
|
+
value: 'OfflineAudioContext',
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
length: kEnumerableProperty,
|
|
198
|
+
oncomplete: kEnumerableProperty,
|
|
199
|
+
startRendering: kEnumerableProperty,
|
|
200
|
+
resume: kEnumerableProperty,
|
|
201
|
+
suspend: kEnumerableProperty,
|
|
202
|
+
});
|
|
203
|
+
|
|
56
204
|
return OfflineAudioContext;
|
|
57
205
|
};
|