@grame/faustwasm 0.15.1 → 0.15.3
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/README.md +16 -3
- package/package.json +1 -1
- package/scripts/faust2wasm.js +32 -18
- package/src/faust2wasmFiles.js +82 -0
package/README.md
CHANGED
|
@@ -73,6 +73,22 @@ node scripts/faust2wasm.js test/poly.dsp test/out -poly
|
|
|
73
73
|
```
|
|
74
74
|
will create a set of files: `index.js`, `dsp-module.wasm`, `dsp-meta.json`, `index.html` (and possibly `effect-module.wasm`, `effect-meta.json`) in the `out` folder.
|
|
75
75
|
|
|
76
|
+
##### Local include files and `-I`
|
|
77
|
+
|
|
78
|
+
The CLI runs the Faust compiler inside a WebAssembly in-memory filesystem, so it cannot read your host filesystem directly. To make local `import("something.lib")` work:
|
|
79
|
+
|
|
80
|
+
- The directory that contains the input `.dsp` is automatically copied into the in-memory FS and added to the compiler include path.
|
|
81
|
+
- Any `-I <dir>` you pass is mirrored into the in-memory FS and added to the include path.
|
|
82
|
+
- Only `.dsp` and `.lib` files are mirrored.
|
|
83
|
+
|
|
84
|
+
This means you can run the CLI from any working directory as long as your local includes live next to the DSP or are listed with `-I`.
|
|
85
|
+
|
|
86
|
+
Example:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
node scripts/faust2wasm.js -I test/includes test/something.dsp test/out
|
|
90
|
+
```
|
|
91
|
+
|
|
76
92
|
#### Creating a Progressive Web Application (PWA) of a Faust DSP
|
|
77
93
|
|
|
78
94
|
You can create a standalone Progressive Web Application using the command line:
|
|
@@ -365,6 +381,3 @@ Html pages embedding the Faust compiler must be served using https, unless using
|
|
|
365
381
|
|
|
366
382
|
----
|
|
367
383
|
<a href="http://faust.grame.fr"><img src=https://faust.grame.fr/community/logos/img/LOGO_FAUST_COMPLET_ORANGE.png width=200 /></a>
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
package/package.json
CHANGED
package/scripts/faust2wasm.js
CHANGED
|
@@ -6,7 +6,7 @@ import { copyWebStandaloneAssets, copyWebPWAAssets, copyWebTemplateAssets } from
|
|
|
6
6
|
|
|
7
7
|
const argv = process.argv.slice(2);
|
|
8
8
|
|
|
9
|
-
if (argv
|
|
9
|
+
if (argv.includes("-help") || argv.includes("-h")) {
|
|
10
10
|
console.log(`
|
|
11
11
|
faust2wasm.js <file.dsp> <outputDir> [-poly] [-standalone] [-pwa] [-no-template]
|
|
12
12
|
Generates WebAssembly and metadata JSON files of a given Faust DSP.
|
|
@@ -14,23 +14,37 @@ Generates WebAssembly and metadata JSON files of a given Faust DSP.
|
|
|
14
14
|
process.exit();
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
if (
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
17
|
+
const takeFlag = (flag) => {
|
|
18
|
+
const index = argv.indexOf(flag);
|
|
19
|
+
if (index === -1) return false;
|
|
20
|
+
argv.splice(index, 1);
|
|
21
|
+
return true;
|
|
22
|
+
};
|
|
23
|
+
const poly = takeFlag("-poly");
|
|
24
|
+
const standalone = takeFlag("-standalone");
|
|
25
|
+
const pwa = takeFlag("-pwa");
|
|
26
|
+
const noTemplate = takeFlag("-no-template");
|
|
27
|
+
|
|
28
|
+
// Allow Faust flags (like -I) anywhere while keeping the first two positionals as input/output.
|
|
29
|
+
const positionals = [];
|
|
30
|
+
const argvFaust = [];
|
|
31
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
32
|
+
const arg = argv[i];
|
|
33
|
+
if (arg === "-I" && argv[i + 1]) {
|
|
34
|
+
argvFaust.push(arg, argv[i + 1]);
|
|
35
|
+
i += 1;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (positionals.length < 2 && !arg.startsWith("-")) {
|
|
39
|
+
positionals.push(arg);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
argvFaust.push(arg);
|
|
43
|
+
}
|
|
44
|
+
const [inputFile, outputDir] = positionals;
|
|
45
|
+
if (!inputFile || !outputDir) {
|
|
46
|
+
throw new Error("Usage: faust2wasm.js <file.dsp> <outputDir> [faust args...]");
|
|
47
|
+
}
|
|
34
48
|
const fileName = inputFile.split('/').pop();
|
|
35
49
|
if (!fileName) throw new Error("No input DSP file");
|
|
36
50
|
const dspName = fileName.replace(/\.dsp$/, '');
|
package/src/faust2wasmFiles.js
CHANGED
|
@@ -38,6 +38,88 @@ const faust2wasmFiles = async (
|
|
|
38
38
|
|
|
39
39
|
const fileName = /** @type {string} */ (inputFile.split('/').pop());
|
|
40
40
|
const dspName = fileName.replace(/\.dsp$/, '');
|
|
41
|
+
// Step 1: get the compiler's in-memory FS (host FS is not visible to WASM).
|
|
42
|
+
const faustFs = compiler.fs();
|
|
43
|
+
const inputDir = path.dirname(path.resolve(inputFile));
|
|
44
|
+
// Step 2: split include flags from other args so we can remap include paths.
|
|
45
|
+
const parseIncludeArgs = (args) => {
|
|
46
|
+
const includeDirs = [];
|
|
47
|
+
const otherArgs = [];
|
|
48
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
49
|
+
const arg = args[i];
|
|
50
|
+
if (arg === '-I' && args[i + 1]) {
|
|
51
|
+
includeDirs.push(args[i + 1]);
|
|
52
|
+
i += 1;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
otherArgs.push(arg);
|
|
56
|
+
}
|
|
57
|
+
return { includeDirs, otherArgs };
|
|
58
|
+
};
|
|
59
|
+
// Step 3: helpers to build the in-memory include tree.
|
|
60
|
+
const ensureFsDir = (dir) => {
|
|
61
|
+
try {
|
|
62
|
+
faustFs.mkdirTree(dir);
|
|
63
|
+
} catch {}
|
|
64
|
+
};
|
|
65
|
+
const isFaustSource = (filePath) => {
|
|
66
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
67
|
+
return ext === '.lib' || ext === '.dsp';
|
|
68
|
+
};
|
|
69
|
+
// Step 4: mirror a host include directory into the in-memory FS.
|
|
70
|
+
const copyIncludeDirToFs = (srcDir, destDir) => {
|
|
71
|
+
if (!fs.existsSync(srcDir) || !fs.statSync(srcDir).isDirectory()) return;
|
|
72
|
+
const entries = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
73
|
+
for (const entry of entries) {
|
|
74
|
+
const srcPath = path.join(srcDir, entry.name);
|
|
75
|
+
const destPath = path.posix.join(destDir, entry.name);
|
|
76
|
+
if (entry.isDirectory()) {
|
|
77
|
+
copyIncludeDirToFs(srcPath, destPath);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (!entry.isFile() || !isFaustSource(srcPath)) continue;
|
|
81
|
+
ensureFsDir(path.posix.dirname(destPath));
|
|
82
|
+
faustFs.writeFile(destPath, fs.readFileSync(srcPath));
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
// Step 5: parse include args and build a unified include list.
|
|
86
|
+
const { includeDirs: rawIncludeDirs, otherArgs } = parseIncludeArgs(argv);
|
|
87
|
+
const includeDirs = [];
|
|
88
|
+
const seenHostDirs = new Set();
|
|
89
|
+
const seenFsDirs = new Set();
|
|
90
|
+
const addIncludeDir = (dir) => {
|
|
91
|
+
if (!dir) return;
|
|
92
|
+
const resolved = path.resolve(dir);
|
|
93
|
+
if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) {
|
|
94
|
+
if (seenHostDirs.has(resolved)) return;
|
|
95
|
+
seenHostDirs.add(resolved);
|
|
96
|
+
includeDirs.push({ type: 'host', path: resolved });
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (seenFsDirs.has(dir)) return;
|
|
100
|
+
seenFsDirs.add(dir);
|
|
101
|
+
includeDirs.push({ type: 'fs', path: dir });
|
|
102
|
+
};
|
|
103
|
+
// Step 6: always include the DSP's directory, then user-provided -I dirs.
|
|
104
|
+
addIncludeDir(inputDir);
|
|
105
|
+
rawIncludeDirs.forEach(addIncludeDir);
|
|
106
|
+
// Step 7: map host include dirs into /faust/user/incN in the in-memory FS.
|
|
107
|
+
const memfsIncludeDirs = [];
|
|
108
|
+
let includeIndex = 0;
|
|
109
|
+
for (const includeDir of includeDirs) {
|
|
110
|
+
if (includeDir.type === 'fs') {
|
|
111
|
+
memfsIncludeDirs.push(includeDir.path);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const memfsDir = path.posix.join('/faust', 'user', `inc${includeIndex}`);
|
|
115
|
+
includeIndex += 1;
|
|
116
|
+
copyIncludeDirToFs(includeDir.path, memfsDir);
|
|
117
|
+
memfsIncludeDirs.push(memfsDir);
|
|
118
|
+
}
|
|
119
|
+
// Step 8: rewrite argv to use only in-memory include paths.
|
|
120
|
+
argv.length = 0;
|
|
121
|
+
memfsIncludeDirs.forEach((dir) => argv.push('-I', dir));
|
|
122
|
+
argv.push(...otherArgs);
|
|
41
123
|
// Flush to zero to avoid costly denormalized numbers
|
|
42
124
|
argv.push('-ftz', '2');
|
|
43
125
|
const dspModulePath = path.join(outputDir, `dsp-module.wasm`);
|