node-web-audio-api 0.21.5 → 1.0.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 +5 -0
- package/js/AudioRenderCapacity.js +3 -2
- package/js/AudioWorklet.js +15 -21
- package/js/AudioWorkletGlobalScope.js +5 -9
- package/js/AudioWorkletNode.js +1 -4
- package/js/OfflineAudioContext.js +4 -2
- package/load-native.cjs +18 -4
- 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 +19 -24
- package/.eslintrc +0 -3
- package/all-checks.sh +0 -18
- package/run-wpt.sh +0 -5
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,7 @@ const {
|
|
|
5
5
|
kOnUpdate,
|
|
6
6
|
} = require('./lib/symbols.js');
|
|
7
7
|
const {
|
|
8
|
+
isFunction,
|
|
8
9
|
kEnumerableProperty,
|
|
9
10
|
} = require('./lib/utils.js');
|
|
10
11
|
const {
|
|
@@ -70,7 +71,7 @@ class AudioRenderCapacity extends EventTarget {
|
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
targetOptions.updateInterval = conversions['double'](options.updateInterval, {
|
|
73
|
-
context: `Failed to execute 'start' on 'AudioRenderCapacity': Failed to read the 'updateInterval' property on 'AudioRenderCapacityOptions': The provided value ()
|
|
74
|
+
context: `Failed to execute 'start' on 'AudioRenderCapacity': Failed to read the 'updateInterval' property on 'AudioRenderCapacityOptions': The provided value ()`,
|
|
74
75
|
});
|
|
75
76
|
} else {
|
|
76
77
|
targetOptions.updateInterval = 1;
|
|
@@ -108,7 +109,7 @@ Object.defineProperties(AudioRenderCapacity.prototype, {
|
|
|
108
109
|
},
|
|
109
110
|
|
|
110
111
|
onupdate: kEnumerableProperty,
|
|
111
|
-
|
|
112
|
+
start: kEnumerableProperty,
|
|
112
113
|
stop: kEnumerableProperty,
|
|
113
114
|
});
|
|
114
115
|
|
package/js/AudioWorklet.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
const {
|
|
2
|
-
resolveObjectURL
|
|
2
|
+
resolveObjectURL,
|
|
3
3
|
} = require('node:buffer');
|
|
4
|
-
const
|
|
5
|
-
|
|
4
|
+
const {
|
|
5
|
+
existsSync,
|
|
6
|
+
} = require('node:fs');
|
|
6
7
|
const path = require('node:path');
|
|
7
8
|
const {
|
|
8
9
|
Worker,
|
|
@@ -23,7 +24,7 @@ const {
|
|
|
23
24
|
|
|
24
25
|
const caller = require('caller');
|
|
25
26
|
// cf. https://www.npmjs.com/package/node-fetch#commonjs
|
|
26
|
-
const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args));
|
|
27
|
+
const fetch = (...args) => import('node-fetch').then(({ default: fetch }) => fetch(...args));
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* Retrieve code with different module resolution strategies
|
|
@@ -47,11 +48,11 @@ const resolveModule = async (moduleUrl) => {
|
|
|
47
48
|
}
|
|
48
49
|
} else if (moduleUrl.startsWith('http')) {
|
|
49
50
|
try {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
const res = await fetch(moduleUrl);
|
|
52
|
+
code = await res.text();
|
|
53
|
+
} catch (err) {
|
|
54
|
+
throw new DOMException(`Failed to execute 'addModule' on 'AudioWorklet': ${err.message}`, 'AbortError');
|
|
55
|
+
}
|
|
55
56
|
} else if (moduleUrl.startsWith('blob:')) {
|
|
56
57
|
try {
|
|
57
58
|
const blob = resolveObjectURL(moduleUrl);
|
|
@@ -63,16 +64,8 @@ const resolveModule = async (moduleUrl) => {
|
|
|
63
64
|
const callerSite = caller(2);
|
|
64
65
|
|
|
65
66
|
if (callerSite.startsWith('http')) { // this branch exists for wpt where caller site is an url
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (moduleUrl.startsWith('/')) {
|
|
69
|
-
const origin = new URL(baseUrl).origin;
|
|
70
|
-
url = origin + moduleUrl;
|
|
71
|
-
} else {
|
|
72
|
-
// we know separators are '/'
|
|
73
|
-
const baseUrl = callerSite.substr(0, callerSite.lastIndexOf('/'));
|
|
74
|
-
url = baseUrl + '/' + moduleUrl;
|
|
75
|
-
}
|
|
67
|
+
const baseUrl = callerSite.substring(0, callerSite.lastIndexOf('/'));
|
|
68
|
+
const url = baseUrl + '/' + moduleUrl;
|
|
76
69
|
|
|
77
70
|
try {
|
|
78
71
|
const res = await fetch(url);
|
|
@@ -92,7 +85,7 @@ const resolveModule = async (moduleUrl) => {
|
|
|
92
85
|
try {
|
|
93
86
|
// try resolve according to process.cwd()
|
|
94
87
|
absPathname = require.resolve(moduleUrl, { paths: [process.cwd()] });
|
|
95
|
-
} catch
|
|
88
|
+
} catch {
|
|
96
89
|
throw new DOMException(`Failed to execute 'addModule' on 'AudioWorklet': Cannot resolve module ${moduleUrl}`, 'AbortError');
|
|
97
90
|
}
|
|
98
91
|
}
|
|
@@ -100,7 +93,7 @@ const resolveModule = async (moduleUrl) => {
|
|
|
100
93
|
}
|
|
101
94
|
|
|
102
95
|
return { absPathname, code };
|
|
103
|
-
}
|
|
96
|
+
};
|
|
104
97
|
|
|
105
98
|
class AudioWorklet {
|
|
106
99
|
#workletId = null;
|
|
@@ -200,6 +193,7 @@ class AudioWorklet {
|
|
|
200
193
|
// For OfflineAudioContext only, check that all processors have been properly
|
|
201
194
|
// created before actual `startRendering`
|
|
202
195
|
async [kCheckProcessorsCreated]() {
|
|
196
|
+
// eslint-disable-next-line no-async-promise-executor
|
|
203
197
|
return new Promise(async resolve => {
|
|
204
198
|
while (this.#pendingCreateProcessors.size !== 0) {
|
|
205
199
|
// we need a microtask to ensure message can be received
|
|
@@ -87,7 +87,7 @@ globalThis[kWorkletRecycleBuffer1] = buffer => pool1.recycle(buffer);
|
|
|
87
87
|
globalThis[kWorkletMarkAsUntransferable] = obj => {
|
|
88
88
|
markAsUntransferable(obj);
|
|
89
89
|
return obj;
|
|
90
|
-
}
|
|
90
|
+
};
|
|
91
91
|
|
|
92
92
|
function isIterable(obj) {
|
|
93
93
|
// checks for null and undefined
|
|
@@ -101,7 +101,7 @@ function isIterable(obj) {
|
|
|
101
101
|
function isConstructor(f) {
|
|
102
102
|
try {
|
|
103
103
|
Reflect.construct(String, [], f);
|
|
104
|
-
} catch
|
|
104
|
+
} catch {
|
|
105
105
|
return false;
|
|
106
106
|
}
|
|
107
107
|
return true;
|
|
@@ -114,7 +114,7 @@ function runLoop() {
|
|
|
114
114
|
runLoopImmediateId = setImmediate(runLoop);
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
globalThis.currentTime = 0
|
|
117
|
+
globalThis.currentTime = 0;
|
|
118
118
|
globalThis.currentFrame = 0;
|
|
119
119
|
globalThis.sampleRate = sampleRate;
|
|
120
120
|
// @todo - implement in upstream crate
|
|
@@ -153,7 +153,7 @@ globalThis.AudioWorkletProcessor = class AudioWorkletProcessor {
|
|
|
153
153
|
this[kWorkletParamsCache][desc.name] = [
|
|
154
154
|
pool128.get(), // should be globalThis.renderQuantumSize
|
|
155
155
|
pool1.get(),
|
|
156
|
-
]
|
|
156
|
+
];
|
|
157
157
|
});
|
|
158
158
|
|
|
159
159
|
this.#port = port;
|
|
@@ -170,7 +170,7 @@ globalThis.AudioWorkletProcessor = class AudioWorkletProcessor {
|
|
|
170
170
|
[kWorkletQueueTask](cmd, err) {
|
|
171
171
|
this.#port.postMessage({ cmd, err });
|
|
172
172
|
}
|
|
173
|
-
}
|
|
173
|
+
};
|
|
174
174
|
|
|
175
175
|
// follow algorithm from:
|
|
176
176
|
// https://webaudio.github.io/web-audio-api/#dom-audioworkletglobalscope-registerprocessor
|
|
@@ -300,10 +300,6 @@ globalThis.registerProcessor = function registerProcessor(name, processorCtor) {
|
|
|
300
300
|
|
|
301
301
|
parentPort.on('message', async event => {
|
|
302
302
|
switch (event.cmd) {
|
|
303
|
-
case 'node-web-audio-api:worklet:init': {
|
|
304
|
-
const { workletId, processors, promiseId } = event;
|
|
305
|
-
break;
|
|
306
|
-
}
|
|
307
303
|
case 'node-web-audio-api:worklet:exit': {
|
|
308
304
|
clearImmediate(runLoopImmediateId);
|
|
309
305
|
// properly exit audio worklet on rust side
|
package/js/AudioWorkletNode.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable no-unused-vars */
|
|
2
1
|
const conversions = require('webidl-conversions');
|
|
3
2
|
const {
|
|
4
3
|
toSanitizedSequence,
|
|
@@ -23,8 +22,6 @@ const {
|
|
|
23
22
|
ErrorEvent,
|
|
24
23
|
} = require('./Events.js');
|
|
25
24
|
|
|
26
|
-
/* eslint-enable no-unused-vars */
|
|
27
|
-
|
|
28
25
|
const AudioNode = require('./AudioNode.js');
|
|
29
26
|
const AudioParamMap = require('./AudioParamMap.js');
|
|
30
27
|
const IMPLEMENTATION_MAX_NUMBER_OF_CHANNELS = 32;
|
|
@@ -156,7 +153,7 @@ module.exports = (jsExport, nativeBinding) => {
|
|
|
156
153
|
// if we delegate this check to Rust, this can poison a Mutex
|
|
157
154
|
// (probably the `audio_param_descriptor_channel` one)
|
|
158
155
|
if (parsedOptions.channelCount <= 0 || parsedOptions.channelCount > IMPLEMENTATION_MAX_NUMBER_OF_CHANNELS) {
|
|
159
|
-
throw new DOMException(`Failed to construct 'AudioWorkletNode': Invalid 'channelCount' property: Number of channels: ${parsedOptions.channelCount} is outside range [1, 32]`, 'NotSupportedError')
|
|
156
|
+
throw new DOMException(`Failed to construct 'AudioWorkletNode': Invalid 'channelCount' property: Number of channels: ${parsedOptions.channelCount} is outside range [1, 32]`, 'NotSupportedError');
|
|
160
157
|
}
|
|
161
158
|
}
|
|
162
159
|
|
|
@@ -138,10 +138,12 @@ module.exports = function patchOfflineAudioContext(jsExport, nativeBinding) {
|
|
|
138
138
|
// ensure all AudioWorkletProcessor have finished their instanciation
|
|
139
139
|
await this.audioWorklet[kCheckProcessorsCreated]();
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
// keep this to highlight the workaround w/ the oncomplete event
|
|
142
|
+
let _nativeAudioBuffer;
|
|
142
143
|
|
|
143
144
|
try {
|
|
144
|
-
|
|
145
|
+
// eslint-disable-next-line no-unused-vars
|
|
146
|
+
_nativeAudioBuffer = await this[kNapiObj].startRendering();
|
|
145
147
|
} catch (err) {
|
|
146
148
|
throwSanitizedError(err);
|
|
147
149
|
}
|
package/load-native.cjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
1
2
|
const { platform, arch } = process;
|
|
2
3
|
|
|
3
4
|
let nativeBinding = null;
|
|
@@ -21,7 +22,7 @@ switch (platform) {
|
|
|
21
22
|
}
|
|
22
23
|
break;
|
|
23
24
|
default:
|
|
24
|
-
|
|
25
|
+
loadError = new Error(`Unsupported architecture on Windows: ${arch}`);
|
|
25
26
|
}
|
|
26
27
|
break;
|
|
27
28
|
case 'darwin':
|
|
@@ -41,11 +42,15 @@ switch (platform) {
|
|
|
41
42
|
}
|
|
42
43
|
break;
|
|
43
44
|
default:
|
|
44
|
-
|
|
45
|
+
loadError = new Error(`Unsupported architecture on macOS: ${arch}`);
|
|
45
46
|
}
|
|
46
47
|
break;
|
|
48
|
+
// case 'freebsd': x64 only
|
|
47
49
|
case 'linux':
|
|
48
50
|
switch (arch) {
|
|
51
|
+
// @todo
|
|
52
|
+
// - support riscv64 arch
|
|
53
|
+
// - support musl C lib
|
|
49
54
|
case 'x64':
|
|
50
55
|
try {
|
|
51
56
|
nativeBinding = require('./node-web-audio-api.linux-x64-gnu.node');
|
|
@@ -68,11 +73,20 @@ switch (platform) {
|
|
|
68
73
|
}
|
|
69
74
|
break;
|
|
70
75
|
default:
|
|
71
|
-
|
|
76
|
+
loadError = new Error(`Unsupported architecture on Linux: ${arch}`);
|
|
72
77
|
}
|
|
73
78
|
break;
|
|
74
79
|
default:
|
|
75
|
-
|
|
80
|
+
loadError = new Error(`Unsupported OS: ${platform}, architecture: ${arch}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// use local build if exists
|
|
84
|
+
if (fs.existsSync('node-web-audio-api.build-release.node')) {
|
|
85
|
+
nativeBinding = require('./node-web-audio-api.build-release.node');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (fs.existsSync('node-web-audio-api.build-debug.node')) {
|
|
89
|
+
nativeBinding = require('./node-web-audio-api.build-debug.node');
|
|
76
90
|
}
|
|
77
91
|
|
|
78
92
|
if (!nativeBinding) {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-web-audio-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"author": "Benjamin Matuszewski",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Web Audio API implementation for Node.js",
|
|
6
6
|
"exports": {
|
|
7
7
|
"import": "./index.mjs",
|
|
8
8
|
"require": "./index.cjs",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"music",
|
|
22
22
|
"dsp",
|
|
23
23
|
"rust",
|
|
24
|
-
"
|
|
24
|
+
"node-api"
|
|
25
25
|
],
|
|
26
26
|
"engines": {
|
|
27
27
|
"node": ">= 14"
|
|
@@ -35,46 +35,41 @@
|
|
|
35
35
|
"access": "public"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
|
-
"
|
|
39
|
-
"build": "npm run generate &&
|
|
40
|
-
"build:
|
|
41
|
-
"build:
|
|
42
|
-
"build:only": "napi build --platform --release",
|
|
38
|
+
"build": "npm run generate && cargo build --release && node ./.scripts/move-artifact.mjs --release",
|
|
39
|
+
"build:jack": "npm run generate && cargo build --features jack --release && node ./.scripts/move-artifact.mjs --release",
|
|
40
|
+
"build:debug": "npm run generate && cargo build && node ./.scripts/move-artifact.mjs",
|
|
41
|
+
"build:only": "cargo build --release && node ./.scripts/move-artifact.mjs --release",
|
|
43
42
|
"check": "cargo fmt && cargo clippy",
|
|
44
43
|
"generate": "node generator/index.mjs && cargo fmt",
|
|
45
|
-
"lint": "npx eslint index.cjs index.mjs && npx eslint js/*.js && npx eslint examples/*.
|
|
46
|
-
"preversion": "
|
|
44
|
+
"lint": "npx eslint index.cjs index.mjs && npx eslint js/*.js && npx eslint examples/*.js",
|
|
45
|
+
"preversion": "npm install && npm run generate",
|
|
47
46
|
"postversion": "cargo bump $npm_package_version && git commit -am \"v$npm_package_version\" && node .scripts/check-changelog.mjs",
|
|
48
47
|
"test": "mocha tests/*.spec.mjs",
|
|
48
|
+
"test:ci": "mocha tests/*.spec.mjs -- --ci",
|
|
49
49
|
"test:only": "mocha",
|
|
50
50
|
"wpt": "npm run build && node ./.scripts/wpt-harness.mjs",
|
|
51
51
|
"wpt:only": "node ./.scripts/wpt-harness.mjs"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@ircam/eslint-config": "^
|
|
55
|
-
"@ircam/sc-
|
|
56
|
-
"@ircam/sc-
|
|
57
|
-
"@ircam/sc-utils": "^1.3.3",
|
|
54
|
+
"@ircam/eslint-config": "^2.0.0",
|
|
55
|
+
"@ircam/sc-scheduling": "^1.0.0",
|
|
56
|
+
"@ircam/sc-utils": "^1.9.0",
|
|
58
57
|
"@sindresorhus/slugify": "^2.1.1",
|
|
59
|
-
"camelcase": "^
|
|
60
|
-
"chai": "^
|
|
58
|
+
"camelcase": "^8.0.0",
|
|
59
|
+
"chai": "^5.1.2",
|
|
61
60
|
"chalk": "^5.3.0",
|
|
62
61
|
"cli-table": "^0.3.11",
|
|
63
|
-
"commander": "^
|
|
62
|
+
"commander": "^13.0.0",
|
|
64
63
|
"dotenv": "^16.0.3",
|
|
65
|
-
"eslint": "^
|
|
64
|
+
"eslint": "^9.18.0",
|
|
66
65
|
"js-beautify": "^1.15.1",
|
|
67
|
-
"mocha": "^
|
|
68
|
-
"
|
|
69
|
-
"octokit": "^2.0.11",
|
|
70
|
-
"ping": "^0.4.2",
|
|
66
|
+
"mocha": "^11.0.1",
|
|
67
|
+
"octokit": "^4.1.0",
|
|
71
68
|
"template-literal": "^1.0.4",
|
|
72
69
|
"webidl2": "^24.2.0",
|
|
73
70
|
"wpt-runner": "^5.0.0"
|
|
74
71
|
},
|
|
75
72
|
"dependencies": {
|
|
76
|
-
"@napi-rs/cli": "^2.14.3",
|
|
77
|
-
"@node-rs/helper": "^1.3.3",
|
|
78
73
|
"caller": "^1.1.0",
|
|
79
74
|
"node-fetch": "^3.3.2",
|
|
80
75
|
"webidl-conversions": "^7.0.0"
|
package/.eslintrc
DELETED
package/all-checks.sh
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
echo "-----------------------------------------------"
|
|
5
|
-
echo "> cargo fmt -- --check --color always"
|
|
6
|
-
echo "-----------------------------------------------"
|
|
7
|
-
cargo fmt -- --check --color always
|
|
8
|
-
|
|
9
|
-
echo "-----------------------------------------------"
|
|
10
|
-
echo "> cargo clippy --all-targets --features cpal -- -D warnings"
|
|
11
|
-
echo "-----------------------------------------------"
|
|
12
|
-
cargo clippy --all-targets --features cpal -- -D warnings
|
|
13
|
-
|
|
14
|
-
echo "-----------------------------------------------"
|
|
15
|
-
echo "> Run js tests"
|
|
16
|
-
echo "-----------------------------------------------"
|
|
17
|
-
npm run test
|
|
18
|
-
|