@grame/faustwasm 0.0.25
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 +181 -0
- package/assets/standalone/faust-ui/index.css +414 -0
- package/assets/standalone/faust-ui/index.css.map +1 -0
- package/assets/standalone/faust-ui/index.d.ts +1 -0
- package/assets/standalone/faust-ui/index.js +3478 -0
- package/assets/standalone/faust-ui/index.js.map +1 -0
- package/assets/standalone/faustwasm/index.d.ts +1252 -0
- package/assets/standalone/faustwasm/index.js +3772 -0
- package/assets/standalone/faustwasm/index.js.map +7 -0
- package/assets/standalone/index.html +73 -0
- package/assets/standalone/index.js +198 -0
- package/assets/standalone/types.d.ts +9 -0
- package/dist/cjs/index.d.ts +1252 -0
- package/dist/cjs/index.js +3752 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs-bundle/index.d.ts +1259 -0
- package/dist/cjs-bundle/index.js +11507 -0
- package/dist/cjs-bundle/index.js.map +7 -0
- package/dist/esm/index.d.ts +1252 -0
- package/dist/esm/index.js +3772 -0
- package/dist/esm/index.js.map +7 -0
- package/dist/esm-bundle/index.d.ts +1259 -0
- package/dist/esm-bundle/index.js +11495 -0
- package/dist/esm-bundle/index.js.map +7 -0
- package/fileutils.js +40 -0
- package/libfaust-wasm/libfaust-wasm.d.cts +3 -0
- package/libfaust-wasm/libfaust-wasm.d.ts +3 -0
- package/libfaust-wasm/libfaust-wasm.data +0 -0
- package/libfaust-wasm/libfaust-wasm.data.d.ts +2 -0
- package/libfaust-wasm/libfaust-wasm.js +21 -0
- package/libfaust-wasm/libfaust-wasm.wasm +0 -0
- package/libfaust-wasm/libfaust-wasm.wasm.d.ts +2 -0
- package/library.md +91 -0
- package/package.json +43 -0
- package/postbuild-bundled.js +11 -0
- package/postbuild.js +63 -0
- package/prebuild-bundled.js +11 -0
- package/rsrc/overview.png +0 -0
- package/rsrc/overview.xlsx +0 -0
- package/scripts/faust2sndfile.js +43 -0
- package/scripts/faust2svg.js +17 -0
- package/scripts/faust2wasm.js +29 -0
- package/src/FaustAudioWorkletNode.ts +264 -0
- package/src/FaustAudioWorkletProcessor.ts +273 -0
- package/src/FaustCompiler.ts +252 -0
- package/src/FaustDspGenerator.ts +498 -0
- package/src/FaustDspInstance.ts +141 -0
- package/src/FaustFFTAudioWorkletProcessor.ts +554 -0
- package/src/FaustOfflineProcessor.ts +141 -0
- package/src/FaustScriptProcessorNode.ts +80 -0
- package/src/FaustSvgDiagrams.ts +37 -0
- package/src/FaustWasmInstantiator.ts +202 -0
- package/src/FaustWebAudioDsp.ts +1060 -0
- package/src/LibFaust.ts +57 -0
- package/src/WavDecoder.ts +207 -0
- package/src/WavEncoder.ts +204 -0
- package/src/copyWebStandaloneAssets.d.ts +3 -0
- package/src/copyWebStandaloneAssets.js +20 -0
- package/src/exports-bundle.ts +5 -0
- package/src/exports.ts +24 -0
- package/src/faust2svgFiles.d.ts +3 -0
- package/src/faust2svgFiles.js +43 -0
- package/src/faust2wasmFiles.d.ts +5 -0
- package/src/faust2wasmFiles.js +93 -0
- package/src/faust2wavFiles.d.ts +3 -0
- package/src/faust2wavFiles.js +58 -0
- package/src/index-bundle-iife.ts +7 -0
- package/src/index-bundle.ts +5 -0
- package/src/index.ts +5 -0
- package/src/instantiateFaustModule.ts +32 -0
- package/src/instantiateFaustModuleFromFile.ts +57 -0
- package/src/types.ts +234 -0
- package/test/clarinet.dsp +1 -0
- package/test/faustlive-wasm/faust-ui/index.css +414 -0
- package/test/faustlive-wasm/faust-ui/index.css.map +1 -0
- package/test/faustlive-wasm/faust-ui/index.d.ts +1 -0
- package/test/faustlive-wasm/faust-ui/index.js +3478 -0
- package/test/faustlive-wasm/faust-ui/index.js.map +1 -0
- package/test/faustlive-wasm/faustwasm/index.d.ts +1252 -0
- package/test/faustlive-wasm/faustwasm/index.js +3772 -0
- package/test/faustlive-wasm/faustwasm/index.js.map +7 -0
- package/test/faustlive-wasm/index.css +97 -0
- package/test/faustlive-wasm/index.html +130 -0
- package/test/faustlive-wasm/index.js +638 -0
- package/test/guitar.dsp +1 -0
- package/test/guitar1.dsp +2 -0
- package/test/libfaust-in-worklet/index.html +13 -0
- package/test/libfaust-in-worklet/index.js +40 -0
- package/test/mono.dsp +8 -0
- package/test/node/test.js +17 -0
- package/test/noise.dsp +3 -0
- package/test/organ.dsp +5 -0
- package/test/organ1.dsp +6 -0
- package/test/osc.dsp +4 -0
- package/test/osc2.dsp +5 -0
- package/test/osc3.dsp +4 -0
- package/test/p-dj.dsp +4 -0
- package/test/poly.dsp +13 -0
- package/test/rev.dsp +2 -0
- package/test/t1.dsp +1 -0
- package/test/web/fft.html +87 -0
- package/test/web/fft1.html +88 -0
- package/test/web/fft2.html +114 -0
- package/test/web/fftw.js +1907 -0
- package/test/web/index.html +12 -0
- package/test/web/index.js +235 -0
- package/test/web/kissfft.js +904 -0
- package/test/web/mono.html +46 -0
- package/test/web/poly-key.html +62 -0
- package/test/web/poly.html +59 -0
- package/tsconfig.json +22 -0
|
Binary file
|
package/library.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Faust Web Audio Library
|
|
2
|
+
|
|
3
|
+
The Faust Web Audio library provides a high level Javascript API over the [Faust](https://faust.grame.fr) compiler. The interface is designed to be used with [TypeScript](https://www.typescriptlang.org/), but describes and documents the API for pure Javascript as well. This document provides an overview of the differents levels of the API. Examples of use are also provided in the `test` folder.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Table of Content
|
|
7
|
+
|
|
8
|
+
- [Organisation of the API](#org)
|
|
9
|
+
|
|
10
|
+
- [Faust Compiler WebAssembly module](#module)
|
|
11
|
+
- [Faust Compiler Javascript Interface](#compiler)
|
|
12
|
+
- [Faust Wasm Instance](#wasm)
|
|
13
|
+
- [Faust Audio Nodes Instances and Offline Processor ](#audio)
|
|
14
|
+
- [High-level API](#high)
|
|
15
|
+
- [How to use with typescript](#tsuse)
|
|
16
|
+
- [Dynamic and Static Instances](#ds)
|
|
17
|
+
- [Misc. services](#misc)
|
|
18
|
+
- [Important note](#note)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## Organisation of the API <a name="org"></a>
|
|
22
|
+
|
|
23
|
+
The API is organised from low to high level as illustrated by the figure below.
|
|
24
|
+
|
|
25
|
+
<img src="rsrc/overview.png" class="mx-auto d-block" width="60%">
|
|
26
|
+
|
|
27
|
+
### Faust Compiler WebAssembly module <a name="module"></a>
|
|
28
|
+
|
|
29
|
+
The first level is the Faust compiler compiled as a wasm library named `libfaust-wasm`.
|
|
30
|
+
It consists in 3 different files:
|
|
31
|
+
|
|
32
|
+
- `libfaust-wasm.wasm` : the Faust compiler provided as a WebAssembly module
|
|
33
|
+
- `libfaust-wasm.js` : a Javascript loader of the WebAssembly module
|
|
34
|
+
- `libfaust-wasm.data` : a virtual file system containing the Faust libraries.
|
|
35
|
+
|
|
36
|
+
The C++ code is compiled with [Emscripten](https://emscripten.org) and interfaced in `LibFaust.ts` and `types.ts`files. The loader will take care of providing an instance of the Faust WebAssembly module and of the associated virtual file system (libfaust-wasm.data).
|
|
37
|
+
|
|
38
|
+
### Faust Compiler Javascript Interface <a name="compiler"></a>
|
|
39
|
+
|
|
40
|
+
The Faust Compiler Javascript interface is described in `FaustCompiler.ts`.
|
|
41
|
+
It provides *classic* Faust compilation services, which output is a raw WebAssembly module with an associated JSON description of the module.
|
|
42
|
+
|
|
43
|
+
### Faust Wasm Instance <a name="wasm"></a>
|
|
44
|
+
|
|
45
|
+
This level takes a WebAssembly module produced by the Faust compiler or a precompiled module loaded from a file, and builds an instance of this module with the proper Wasm memory layout, ready to run, but not yet connected to any audio node. It is described in `FaustDspGenerator.ts`, `FaustWasmInstantiator.ts`, `FaustWebAudioDsp.ts` and `FaustDspInstance.ts` files.
|
|
46
|
+
|
|
47
|
+
### Faust Audio Nodes Instances and Offline Processor <a name="audio"></a>
|
|
48
|
+
|
|
49
|
+
This level takes a Faust Wasm instance to build an audio node. [AudioWorklet](https://developer.mozilla.org/fr/docs/Web/API/AudioWorklet) and [ScriptProcessor](https://developer.mozilla.org/en-US/docs/Web/API/ScriptProcessorNode) nodes are supported. It is described in `FaustAudioWorkletNode.ts` and `FaustAudioWorkletProcessor.ts` files.
|
|
50
|
+
|
|
51
|
+
**Warning**: AudioWorklet is a recent technology and may not be supported by all the browsers. Check the [compatibility](https://developer.mozilla.org/fr/docs/Web/API/AudioWorklet) chart.
|
|
52
|
+
|
|
53
|
+
Note that ScriptProcessor is marked as [deprecated](https://developer.mozilla.org/en-US/docs/Web/API/ScriptProcessorNode) but it's the only audio architecture available in older Safari versions. Both monophonic (generators, effects...) or polyphonic (instruments) nodes can be created. It is described in `FaustScriptProcessorNode.ts` file.
|
|
54
|
+
|
|
55
|
+
By default, and to save CPU, created audio nodes are not processing audio buffers. They have to be explicitely started with the `start` method (and possibly stopped if needed using the `stop`method).
|
|
56
|
+
|
|
57
|
+
An offline processor to render a DSP in a non real-time context and get the computed frames is available. It is described in `FaustOfflineProcessor.ts`. It will automatically use the `start` and `stop` methods internally to activate actual rendering in its `plot` method.
|
|
58
|
+
|
|
59
|
+
### High-level API <a name="high"></a>
|
|
60
|
+
|
|
61
|
+
A high-level API is available to compile a DSP program and create the audio node, either monophonic or polyphonic using `createNode`. Offline processing monophonic or polyphonic nodes can be created using `createOfflineProcessor`. FFT processing nodes can be created using `createFFTNode`. It is described in `FaustDspGenerator.ts`.
|
|
62
|
+
|
|
63
|
+
### How to use with typescript <a name="tsuse"></a>
|
|
64
|
+
|
|
65
|
+
Simply include the following to get access to types and functions:
|
|
66
|
+
~~~~~~~~~~~~~~~
|
|
67
|
+
///<reference types="@grame/libfaust"/>
|
|
68
|
+
~~~~~~~~~~~~~~~
|
|
69
|
+
|
|
70
|
+
## Dynamic and Static Instances <a name="ds"></a>
|
|
71
|
+
|
|
72
|
+
The Faust Wasm and Audio Node levels make it possible to generate instances from Faust dsp code as well as from pre-compiled WebAssembly modules.
|
|
73
|
+
In the latter case, it is not necessary to include the `libfaust-wasm.js` library, `index.js` is sufficient to provide the required services.
|
|
74
|
+
This allows to generate lighter and faster-loading HTML pages.
|
|
75
|
+
|
|
76
|
+
## Misc. services <a name="misc"></a>
|
|
77
|
+
|
|
78
|
+
- `FaustSvgDiagrams.ts`: provides facilities to browse Faust generated SVG diagrams
|
|
79
|
+
- `FaustFFTAudioWorkletProcessor`: provides FFT processing
|
|
80
|
+
|
|
81
|
+
## Important note <a name="note"></a>
|
|
82
|
+
|
|
83
|
+
Html pages embedding the Faust compiler must be served using https, unless using http://localhost.
|
|
84
|
+
|
|
85
|
+
----
|
|
86
|
+
<a href="http://faust.grame.fr"><img src=https://faust.grame.fr/community/logos/img/LOGO_FAUST_COMPLET_ORANGE.png width=200 /></a>
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@grame/faustwasm",
|
|
3
|
+
"version": "0.0.25",
|
|
4
|
+
"description": "WebAssembly version of Faust Compiler",
|
|
5
|
+
"main": "dist/cjs/index.js",
|
|
6
|
+
"types": "dist/esm/index.d.ts",
|
|
7
|
+
"module": "dist/esm/index.js",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "npm run build-cjs && npm run build-cjs-bundle && npm run build-esm && npm run build-esm-bundle && node postbuild-bundled.js & npm run build-types & npm run build-types-bundle",
|
|
11
|
+
"build-cjs": "esbuild src/index.ts --target=es2019 --bundle --sourcemap --outdir=dist/cjs --format=iife --external:fs --external:url",
|
|
12
|
+
"build-cjs-bundle": "node prebuild-bundled.js && esbuild src/index-bundle-iife.ts --target=es2019 --bundle --sourcemap --loader:.wasm=binary --loader:.data=binary --outfile=dist/cjs-bundle/index.js --format=iife --external:fs --external:url --external:path && node postbuild-bundled.js",
|
|
13
|
+
"build-esm": "esbuild src/index.ts --target=es2019 --bundle --sourcemap --outdir=dist/esm --format=esm --external:fs --external:url",
|
|
14
|
+
"build-esm-bundle": "node prebuild-bundled.js && esbuild src/index-bundle.ts --target=es2019 --bundle --sourcemap --loader:.wasm=binary --loader:.data=binary --outfile=dist/esm-bundle/index.js --format=esm --external:fs --external:url --external:path && node postbuild-bundled.js",
|
|
15
|
+
"build-types": "dts-bundle-generator -o dist/cjs/index.d.ts src/index.ts --external-imports",
|
|
16
|
+
"build-types-bundle": "dts-bundle-generator -o dist/cjs-bundle/index.d.ts src/index-bundle.ts --external-imports",
|
|
17
|
+
"postbuild": "node postbuild.js"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/grame-cncm/faustwasm.git"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"faust",
|
|
25
|
+
"webassembly"
|
|
26
|
+
],
|
|
27
|
+
"author": "Grame-CNCM",
|
|
28
|
+
"license": "LGPL-3.0",
|
|
29
|
+
"bugs": {
|
|
30
|
+
"url": "https://github.com/grame-cncm/faustwasm/issues"
|
|
31
|
+
},
|
|
32
|
+
"homepage": "https://github.com/grame-cncm/faustwasm#readme",
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@aws-crypto/sha256-js": "^2.0.1",
|
|
35
|
+
"@shren/faust-ui": "^1.1.2",
|
|
36
|
+
"@types/emscripten": "^1.39.5",
|
|
37
|
+
"@types/node": "^16.11.7",
|
|
38
|
+
"@types/webmidi": "^2.0.6",
|
|
39
|
+
"dts-bundle-generator": "^6.13.0",
|
|
40
|
+
"esbuild": "^0.14.27",
|
|
41
|
+
"typescript": "^4.4.4"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
import { rmSync } from "./fileutils.js"
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
|
|
6
|
+
// @ts-ignore
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
|
|
11
|
+
rmSync(path.join(__dirname, "./libfaust-wasm/libfaust-wasm.cjs"));
|
package/postbuild.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
import { cpSync, rmSync } from "./fileutils.js";
|
|
3
|
+
import * as fs from "fs";
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
|
|
7
|
+
// @ts-ignore
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
// @ts-ignore
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
|
|
12
|
+
const faustUiDistPath = path.join(__dirname, "./node_modules/@shren/faust-ui/dist/esm");
|
|
13
|
+
const faustUiDistDest = path.join(__dirname, "./assets/standalone/faust-ui");
|
|
14
|
+
const faustUiDistDest2 = path.join(__dirname, "./test/faustlive-wasm/faust-ui");
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
rmSync(faustUiDistDest);
|
|
18
|
+
rmSync(faustUiDistDest2);
|
|
19
|
+
} catch (e) {
|
|
20
|
+
console.warn(e);
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
fs.mkdirSync(faustUiDistDest);
|
|
24
|
+
fs.mkdirSync(faustUiDistDest2);
|
|
25
|
+
} catch (e) {
|
|
26
|
+
console.warn(e);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
cpSync(faustUiDistPath, faustUiDistDest);
|
|
30
|
+
cpSync(faustUiDistPath, faustUiDistDest2);
|
|
31
|
+
const faustUiDts = `export * from "@shren/faust-ui";\n`;
|
|
32
|
+
fs.writeFileSync(path.join(faustUiDistDest, "index.d.ts"), faustUiDts);
|
|
33
|
+
fs.writeFileSync(path.join(faustUiDistDest2, "index.d.ts"), faustUiDts);
|
|
34
|
+
|
|
35
|
+
console.log("FaustUI files copied.")
|
|
36
|
+
|
|
37
|
+
const faustWasmDistPath = path.join(__dirname, "./dist/cjs");
|
|
38
|
+
const faustWasmDistEsmPath = path.join(__dirname, "./dist/esm");
|
|
39
|
+
const faustWasmDistBundlePath = path.join(__dirname, "./dist/cjs-bundle");
|
|
40
|
+
const faustWasmDistEsmBundlePath = path.join(__dirname, "./dist/esm-bundle");
|
|
41
|
+
const faustWasmDistDest = path.join(__dirname, "./assets/standalone/faustwasm");
|
|
42
|
+
const faustWasmDistDest2 = path.join(__dirname, "./test/faustlive-wasm/faustwasm");
|
|
43
|
+
|
|
44
|
+
fs.copyFileSync(path.join(faustWasmDistPath, "index.d.ts"), path.join(faustWasmDistEsmPath, "index.d.ts"));
|
|
45
|
+
fs.copyFileSync(path.join(faustWasmDistBundlePath, "index.d.ts"), path.join(faustWasmDistEsmBundlePath, "index.d.ts"));
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
rmSync(faustWasmDistDest);
|
|
49
|
+
rmSync(faustWasmDistDest2);
|
|
50
|
+
} catch (e) {
|
|
51
|
+
console.warn(e);
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
fs.mkdirSync(faustWasmDistDest);
|
|
55
|
+
fs.mkdirSync(faustWasmDistDest2);
|
|
56
|
+
} catch (e) {
|
|
57
|
+
console.warn(e);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
cpSync(faustWasmDistEsmPath, faustWasmDistDest);
|
|
61
|
+
cpSync(faustWasmDistEsmPath, faustWasmDistDest2);
|
|
62
|
+
|
|
63
|
+
console.log("FaustWasm files copied.")
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
import { cpSync } from "./fileutils.js"
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
|
|
6
|
+
// @ts-ignore
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
|
|
11
|
+
cpSync(path.join(__dirname, "./libfaust-wasm/libfaust-wasm.js"), path.join(__dirname, "./libfaust-wasm/libfaust-wasm.cjs"));
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
import * as process from "process";
|
|
3
|
+
import faust2wavFiles from "../src/faust2wavFiles.js";
|
|
4
|
+
|
|
5
|
+
const argv = process.argv.slice(2);
|
|
6
|
+
|
|
7
|
+
if (argv[0] === "-help" || argv[0] === "-h") {
|
|
8
|
+
console.log(`
|
|
9
|
+
faust2sndfile.js <file.dsp> <outputWav.wav>
|
|
10
|
+
Generates audio file from a Faust DSP.
|
|
11
|
+
\t -bs\t <num> to setup the rendering buffer size in frames (default: 64)
|
|
12
|
+
\t -bd\t 16|24|32 to setup the output file bit-depth (default: 16)
|
|
13
|
+
\t -c \t <samples> to setup the output file length in frames, when -ct is not used (default: SR*5)
|
|
14
|
+
\t -in\t <inputWav.wav> specify an input file to process
|
|
15
|
+
\t -sr\t <num> to setup the output file sample rate (default: 44100)
|
|
16
|
+
`);
|
|
17
|
+
process.exit();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const $in = argv.indexOf("-in");
|
|
21
|
+
/** @type {string} */let inputWav;
|
|
22
|
+
if ($in !== -1) [, inputWav] = argv.splice($in, 2);
|
|
23
|
+
|
|
24
|
+
const $bs = argv.indexOf("-bs");
|
|
25
|
+
/** @type {number} */let bufferSize;
|
|
26
|
+
if ($bs !== -1) bufferSize = +argv.splice($bs, 2)[1];
|
|
27
|
+
|
|
28
|
+
const $bd = argv.indexOf("-bd");
|
|
29
|
+
/** @type {number} */let bitDepth;
|
|
30
|
+
if ($bd !== -1) bitDepth = +argv.splice($bd, 2)[1];
|
|
31
|
+
|
|
32
|
+
const $c = argv.indexOf("-c");
|
|
33
|
+
/** @type {number} */let samples;
|
|
34
|
+
if ($c !== -1) samples = +argv.splice($c, 2)[1];
|
|
35
|
+
|
|
36
|
+
const $sr = argv.indexOf("-sr");
|
|
37
|
+
/** @type {number} */let sampleRate;
|
|
38
|
+
if ($sr !== -1) sampleRate = +argv.splice($sr, 2)[1];
|
|
39
|
+
|
|
40
|
+
const [inputFile, outputWav, ...argvFaust] = argv;
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
faust2wavFiles(inputFile, inputWav, outputWav, bufferSize, sampleRate, samples, bitDepth, argvFaust);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
import * as process from "process";
|
|
3
|
+
import faust2svgFiles from "../src/faust2svgFiles.js";
|
|
4
|
+
|
|
5
|
+
const argv = process.argv.slice(2);
|
|
6
|
+
|
|
7
|
+
if (argv[0] === "-help" || argv[0] === "-h") {
|
|
8
|
+
console.log(`
|
|
9
|
+
faust2svg.js <file.dsp> <outputDir>
|
|
10
|
+
Generates Diagram SVGs of a given Faust DSP.
|
|
11
|
+
`);
|
|
12
|
+
process.exit();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const [inputFile, outputDir, ...argvFaust] = argv;
|
|
16
|
+
|
|
17
|
+
faust2svgFiles(inputFile, outputDir, argvFaust);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//@ts-check
|
|
2
|
+
import * as process from "process";
|
|
3
|
+
import faust2wasmFiles from "../src/faust2wasmFiles.js";
|
|
4
|
+
import copyWebStandaloneAssets from "../src/copyWebStandaloneAssets.js";
|
|
5
|
+
|
|
6
|
+
const argv = process.argv.slice(2);
|
|
7
|
+
|
|
8
|
+
if (argv[0] === "-help" || argv[0] === "-h") {
|
|
9
|
+
console.log(`
|
|
10
|
+
faust2wasm.js <file.dsp> <outputDir> [-poly] [-standalone]
|
|
11
|
+
Generates WebAssembly and metadata JSON files of a given Faust DSP.
|
|
12
|
+
`);
|
|
13
|
+
process.exit();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const $poly = argv.indexOf("-poly");
|
|
17
|
+
const poly = $poly !== -1;
|
|
18
|
+
if (poly) argv.splice($poly, 1);
|
|
19
|
+
|
|
20
|
+
const $standalone = argv.indexOf("-standalone");
|
|
21
|
+
const standalone = $standalone !== -1;
|
|
22
|
+
if (standalone) argv.splice($standalone, 1);
|
|
23
|
+
|
|
24
|
+
const [inputFile, outputDir, ...argvFaust] = argv;
|
|
25
|
+
|
|
26
|
+
(async () => {
|
|
27
|
+
await faust2wasmFiles(inputFile, outputDir, argvFaust, poly);
|
|
28
|
+
if (standalone) copyWebStandaloneAssets(outputDir);
|
|
29
|
+
})();
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { OutputParamHandler, ComputeHandler, PlotHandler, UIHandler, MetadataHandler, FaustBaseWebAudioDsp, IFaustMonoWebAudioDsp, IFaustPolyWebAudioDsp } from "./FaustWebAudioDsp";
|
|
2
|
+
import type { FaustAudioWorkletNodeOptions } from "./FaustAudioWorkletProcessor";
|
|
3
|
+
import type { LooseFaustDspFactory, FaustDspMeta, FaustUIInputItem, FaustUIItem } from "./types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Base class for Monophonic and Polyphonic AudioWorkletNode
|
|
7
|
+
*/
|
|
8
|
+
export class FaustAudioWorkletNode<Poly extends boolean = false> extends (globalThis.AudioWorkletNode || null) {
|
|
9
|
+
|
|
10
|
+
protected fJSONDsp: FaustDspMeta;
|
|
11
|
+
protected fJSON: string;
|
|
12
|
+
protected fInputsItems: string[];
|
|
13
|
+
protected fOutputHandler: OutputParamHandler | null;
|
|
14
|
+
protected fComputeHandler: ComputeHandler | null;
|
|
15
|
+
protected fPlotHandler: PlotHandler | null;
|
|
16
|
+
protected fUICallback: UIHandler;
|
|
17
|
+
protected fDescriptor: FaustUIInputItem[];
|
|
18
|
+
|
|
19
|
+
constructor(context: BaseAudioContext, name: string, factory: LooseFaustDspFactory, options: FaustAudioWorkletNodeOptions<Poly>["processorOptions"], nodeOptions: Partial<FaustAudioWorkletNodeOptions> = {}) {
|
|
20
|
+
|
|
21
|
+
// Create JSON object
|
|
22
|
+
const JSONObj: FaustDspMeta = JSON.parse(factory.json);
|
|
23
|
+
|
|
24
|
+
// Create proxy FaustAudioWorkletProcessor
|
|
25
|
+
super(context, name, {
|
|
26
|
+
numberOfInputs: JSONObj.inputs > 0 ? 1 : 0,
|
|
27
|
+
numberOfOutputs: JSONObj.outputs > 0 ? 1 : 0,
|
|
28
|
+
channelCount: Math.max(1, JSONObj.inputs),
|
|
29
|
+
outputChannelCount: [JSONObj.outputs],
|
|
30
|
+
channelCountMode: "explicit",
|
|
31
|
+
channelInterpretation: "speakers",
|
|
32
|
+
processorOptions: options,
|
|
33
|
+
...nodeOptions
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
this.fJSONDsp = JSONObj;
|
|
37
|
+
this.fJSON = factory.json;
|
|
38
|
+
this.fOutputHandler = null;
|
|
39
|
+
this.fComputeHandler = null;
|
|
40
|
+
this.fPlotHandler = null;
|
|
41
|
+
this.fDescriptor = [];
|
|
42
|
+
|
|
43
|
+
// Parse UI
|
|
44
|
+
this.fInputsItems = [];
|
|
45
|
+
this.fUICallback = (item: FaustUIItem) => {
|
|
46
|
+
if (item.type === "vslider" || item.type === "hslider" || item.type === "button" || item.type === "checkbox" || item.type === "nentry") {
|
|
47
|
+
// Keep inputs adresses
|
|
48
|
+
this.fInputsItems.push(item.address);
|
|
49
|
+
this.fDescriptor.push(item);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
FaustBaseWebAudioDsp.parseUI(this.fJSONDsp.ui, this.fUICallback);
|
|
53
|
+
|
|
54
|
+
// Patch it with additional functions
|
|
55
|
+
this.port.onmessage = (e: MessageEvent) => {
|
|
56
|
+
if (e.data.type === "param" && this.fOutputHandler) {
|
|
57
|
+
this.fOutputHandler(e.data.path, e.data.value);
|
|
58
|
+
} else if (e.data.type === "plot" && this.fPlotHandler) {
|
|
59
|
+
this.fPlotHandler(e.data.value, e.data.index, e.data.events);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Public API
|
|
65
|
+
setOutputParamHandler(handler: OutputParamHandler | null) {
|
|
66
|
+
this.fOutputHandler = handler;
|
|
67
|
+
}
|
|
68
|
+
getOutputParamHandler() {
|
|
69
|
+
return this.fOutputHandler;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
setComputeHandler(handler: ComputeHandler | null) {
|
|
73
|
+
this.fComputeHandler = handler;
|
|
74
|
+
}
|
|
75
|
+
getComputeHandler(): ComputeHandler | null {
|
|
76
|
+
return this.fComputeHandler;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
setPlotHandler(handler: PlotHandler | null) {
|
|
80
|
+
this.fPlotHandler = handler;
|
|
81
|
+
// Set PlotHandler on processor side
|
|
82
|
+
if (this.fPlotHandler) {
|
|
83
|
+
this.port.postMessage({ type: "setPlotHandler", data: true });
|
|
84
|
+
} else {
|
|
85
|
+
this.port.postMessage({ type: "setPlotHandler", data: false });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
getPlotHandler(): PlotHandler | null {
|
|
89
|
+
return this.fPlotHandler;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
getNumInputs() {
|
|
93
|
+
return this.fJSONDsp.inputs;
|
|
94
|
+
}
|
|
95
|
+
getNumOutputs() {
|
|
96
|
+
return this.fJSONDsp.outputs;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Implemented in subclasses
|
|
100
|
+
compute(inputs: Float32Array[], outputs: Float32Array[]) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
metadata(handler: MetadataHandler) {
|
|
105
|
+
if (this.fJSONDsp.meta) {
|
|
106
|
+
this.fJSONDsp.meta.forEach(meta => handler(Object.keys(meta)[0], meta[Object.keys(meta)[0]]));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
midiMessage(data: number[] | Uint8Array): void {
|
|
111
|
+
const cmd = data[0] >> 4;
|
|
112
|
+
const channel = data[0] & 0xf;
|
|
113
|
+
const data1 = data[1];
|
|
114
|
+
const data2 = data[2];
|
|
115
|
+
if (cmd === 11) this.ctrlChange(channel, data1, data2);
|
|
116
|
+
else if (cmd === 14) this.pitchWheel(channel, data2 * 128.0 + data1);
|
|
117
|
+
else this.port.postMessage({ type: "midi", data: data });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
ctrlChange(channel: number, ctrl: number, value: number) {
|
|
121
|
+
const e = { type: "ctrlChange", data: [channel, ctrl, value] };
|
|
122
|
+
this.port.postMessage(e);
|
|
123
|
+
}
|
|
124
|
+
pitchWheel(channel: number, wheel: number) {
|
|
125
|
+
const e = { type: "pitchWheel", data: [channel, wheel] };
|
|
126
|
+
this.port.postMessage(e);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
setParamValue(path: string, value: number) {
|
|
130
|
+
const e = { type: "param", data: { path, value } };
|
|
131
|
+
this.port.postMessage(e);
|
|
132
|
+
// Set value on AudioParam (but this is not used on Processor side for now)
|
|
133
|
+
const param = this.parameters.get(path);
|
|
134
|
+
if (param) param.setValueAtTime(value, this.context.currentTime);
|
|
135
|
+
|
|
136
|
+
}
|
|
137
|
+
getParamValue(path: string) {
|
|
138
|
+
// Get value of AudioParam
|
|
139
|
+
const param = this.parameters.get(path);
|
|
140
|
+
return (param) ? param.value : 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
getParams() { return this.fInputsItems; }
|
|
144
|
+
getMeta() { return this.fJSONDsp; }
|
|
145
|
+
getJSON() { return JSON.stringify(this.getMeta()); }
|
|
146
|
+
getUI() { return this.fJSONDsp.ui; }
|
|
147
|
+
getDescriptors() { return this.fDescriptor; }
|
|
148
|
+
|
|
149
|
+
start() {
|
|
150
|
+
this.port.postMessage({ type: "start" });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
stop() {
|
|
154
|
+
this.port.postMessage({ type: "stop" });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
destroy() {
|
|
158
|
+
this.port.postMessage({ type: "destroy" });
|
|
159
|
+
this.port.close();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Monophonic AudioWorkletNode
|
|
165
|
+
*/
|
|
166
|
+
export class FaustMonoAudioWorkletNode extends FaustAudioWorkletNode<false> implements IFaustMonoWebAudioDsp {
|
|
167
|
+
|
|
168
|
+
onprocessorerror = (e: Event) => {
|
|
169
|
+
// console.error("Error from " + this.fJSONDsp.name + " FaustMonoAudioWorkletNode");
|
|
170
|
+
throw e;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
constructor(context: BaseAudioContext, name: string, factory: LooseFaustDspFactory, sampleSize: number, nodeOptions: Partial<FaustAudioWorkletNodeOptions> = {}) {
|
|
174
|
+
super(context, name, factory, { name, factory, sampleSize }, nodeOptions);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Polyphonic AudioWorkletNode
|
|
180
|
+
*/
|
|
181
|
+
export class FaustPolyAudioWorkletNode extends FaustAudioWorkletNode<true> implements IFaustPolyWebAudioDsp {
|
|
182
|
+
|
|
183
|
+
private fJSONEffect: FaustDspMeta | null;
|
|
184
|
+
|
|
185
|
+
onprocessorerror = (e: Event) => {
|
|
186
|
+
// console.error("Error from " + this.fJSONDsp.name + " FaustPolyAudioWorkletNode");
|
|
187
|
+
throw e;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
constructor(context: BaseAudioContext,
|
|
191
|
+
name: string,
|
|
192
|
+
voiceFactory: LooseFaustDspFactory,
|
|
193
|
+
mixerModule: WebAssembly.Module,
|
|
194
|
+
voices: number,
|
|
195
|
+
sampleSize: number,
|
|
196
|
+
effectFactory?: LooseFaustDspFactory, nodeOptions: Partial<FaustAudioWorkletNodeOptions> = {}) {
|
|
197
|
+
|
|
198
|
+
super(
|
|
199
|
+
context,
|
|
200
|
+
name,
|
|
201
|
+
voiceFactory,
|
|
202
|
+
{
|
|
203
|
+
name,
|
|
204
|
+
voiceFactory,
|
|
205
|
+
mixerModule,
|
|
206
|
+
voices,
|
|
207
|
+
sampleSize,
|
|
208
|
+
effectFactory
|
|
209
|
+
},
|
|
210
|
+
nodeOptions
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
this.fJSONEffect = effectFactory ? JSON.parse(effectFactory.json) : null;
|
|
214
|
+
|
|
215
|
+
if (this.fJSONEffect) {
|
|
216
|
+
FaustBaseWebAudioDsp.parseUI(this.fJSONEffect.ui, this.fUICallback);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Public API
|
|
221
|
+
keyOn(channel: number, pitch: number, velocity: number) {
|
|
222
|
+
const e = { type: "keyOn", data: [channel, pitch, velocity] };
|
|
223
|
+
this.port.postMessage(e);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
keyOff(channel: number, pitch: number, velocity: number) {
|
|
227
|
+
const e = { type: "keyOff", data: [channel, pitch, velocity] };
|
|
228
|
+
this.port.postMessage(e);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
allNotesOff(hard: boolean) {
|
|
232
|
+
const e = { type: "ctrlChange", data: [0, 123, 0] };
|
|
233
|
+
this.port.postMessage(e);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
getMeta() {
|
|
237
|
+
const o = this.fJSONDsp;
|
|
238
|
+
const e = this.fJSONEffect;
|
|
239
|
+
const r = { ...o };
|
|
240
|
+
if (e) {
|
|
241
|
+
r.ui = [{
|
|
242
|
+
type: "tgroup", label: "Sequencer", items: [
|
|
243
|
+
{ type: "vgroup", label: "Instrument", items: o.ui },
|
|
244
|
+
{ type: "vgroup", label: "Effect", items: e.ui }
|
|
245
|
+
]
|
|
246
|
+
}];
|
|
247
|
+
} else {
|
|
248
|
+
r.ui = [{
|
|
249
|
+
type: "tgroup", label: "Polyphonic", items: [
|
|
250
|
+
{ type: "vgroup", label: "Voices", items: o.ui }
|
|
251
|
+
]
|
|
252
|
+
}];
|
|
253
|
+
}
|
|
254
|
+
return r as FaustDspMeta;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
getJSON() {
|
|
258
|
+
return JSON.stringify(this.getMeta());
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
getUI() {
|
|
262
|
+
return this.getMeta().ui;
|
|
263
|
+
}
|
|
264
|
+
}
|