Skip to content

Commit

Permalink
fix: use "real" module import for worklet processors
Browse files Browse the repository at this point in the history
  • Loading branch information
b-ma committed Dec 21, 2024
1 parent b17d96c commit 9f60cf0
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 26 deletions.
35 changes: 14 additions & 21 deletions js/AudioWorklet.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,19 @@ const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch
/**
* Retrieve code with different module resolution strategies
* - file - absolute or relative to cwd path
* - URL
* - Blob
*
* - URL - do not support import within module
* - Blob - do not support import within module
* - fallback: relative to caller site
* + in fs
* + in fs - support import within module
* + caller site is url - required for wpt, probably no other use case
*/
const resolveModule = async (moduleUrl) => {
let code;
let code = null;
let absPathname = null;

if (existsSync(moduleUrl)) {
const pathname = moduleUrl;

try {
const buffer = await fs.readFile(pathname);
code = buffer.toString();
} catch (err) {
throw new Error(`Failed to execute 'addModule' on 'AudioWorklet': ${err.message}`);
}
absPathname = path.join(process.cwd(), moduleUrl);
} else if (moduleUrl.startsWith('http')) {
try {
const res = await fetch(moduleUrl);
Expand Down Expand Up @@ -88,19 +83,14 @@ const resolveModule = async (moduleUrl) => {
const pathname = path.join(absDirname, moduleUrl);

if (existsSync(pathname)) {
try {
const buffer = await fs.readFile(pathname);
code = buffer.toString();
} catch (err) {
throw new Error(`Failed to execute 'addModule' on 'AudioWorklet': ${err.message}`);
}
absPathname = pathname;
} else {
throw new Error(`Failed to execute 'addModule' on 'AudioWorklet': Cannot resolve module ${moduleUrl}`);
}
}
}

return code;
return { absPathname, code };
}

class AudioWorklet {
Expand Down Expand Up @@ -161,7 +151,9 @@ class AudioWorklet {
}

async addModule(moduleUrl) {
const code = await resolveModule(moduleUrl);
// @important - `resolveModule` must be called because it uses `caller`
// which will return `null` if it is not the in the first line...
const resolved = await resolveModule(moduleUrl);

// launch Worker if not exists
if (!this.#port) {
Expand All @@ -187,7 +179,8 @@ class AudioWorklet {

this.#port.postMessage({
cmd: 'node-web-audio-api:worklet:add-module',
code,
moduleUrl: resolved.absPathname,
code: resolved.code,
promiseId,
});
});
Expand Down
19 changes: 14 additions & 5 deletions js/AudioWorkletGlobalScope.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ globalThis.registerProcessor = function registerProcessor(name, processorCtor) {
// process.stdout.write('closing worklet');
// });

parentPort.on('message', event => {
parentPort.on('message', async event => {
switch (event.cmd) {
case 'node-web-audio-api:worklet:init': {
const { workletId, processors, promiseId } = event;
Expand All @@ -313,11 +313,19 @@ parentPort.on('message', event => {
break;
}
case 'node-web-audio-api:worklet:add-module': {
const { code, promiseId } = event;
const func = new Function('AudioWorkletProcessor', 'registerProcessor', code);
const { moduleUrl, code, promiseId } = event;

try {
func(AudioWorkletProcessor, registerProcessor);
// 1. If given module is a "real" file, we can import it as is,
// 2. If module is a blob or loaded from an URL, we use the raw text as
// input. In this case, if the module uses `import` it will crash
if (moduleUrl !== null) {
await import(moduleUrl);
} else {
await import(`data:text/javascript;base64,${btoa(unescape(encodeURIComponent(code)))}`);
}
// func(AudioWorkletProcessor, registerProcessor);
// await import(`data:text/javascript;base64,${btoa(code)}`);
// send registered param descriptors on main thread and resolve Promise
parentPort.postMessage({
cmd: 'node-web-audio-api:worklet:module-added',
Expand All @@ -338,7 +346,7 @@ parentPort.on('message', event => {
const { name, id, options, port } = event;
const ctor = nameProcessorCtorMap.get(name);

// rewrap options of interest for the AudioWorkletNodeBaseClass
// re-wrap options of interest for the AudioWorkletNodeBaseClass
pendingProcessorConstructionData = {
port,
numberOfInputs: options.numberOfInputs,
Expand All @@ -352,6 +360,7 @@ parentPort.on('message', event => {
instance = new ctor(options);
} catch (err) {
port.postMessage({ cmd: 'node-web-audio-api:worklet:ctor-error', err });
return;
}

pendingProcessorConstructionData = null;
Expand Down

0 comments on commit 9f60cf0

Please sign in to comment.