@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.
Files changed (111) hide show
  1. package/README.md +181 -0
  2. package/assets/standalone/faust-ui/index.css +414 -0
  3. package/assets/standalone/faust-ui/index.css.map +1 -0
  4. package/assets/standalone/faust-ui/index.d.ts +1 -0
  5. package/assets/standalone/faust-ui/index.js +3478 -0
  6. package/assets/standalone/faust-ui/index.js.map +1 -0
  7. package/assets/standalone/faustwasm/index.d.ts +1252 -0
  8. package/assets/standalone/faustwasm/index.js +3772 -0
  9. package/assets/standalone/faustwasm/index.js.map +7 -0
  10. package/assets/standalone/index.html +73 -0
  11. package/assets/standalone/index.js +198 -0
  12. package/assets/standalone/types.d.ts +9 -0
  13. package/dist/cjs/index.d.ts +1252 -0
  14. package/dist/cjs/index.js +3752 -0
  15. package/dist/cjs/index.js.map +7 -0
  16. package/dist/cjs-bundle/index.d.ts +1259 -0
  17. package/dist/cjs-bundle/index.js +11507 -0
  18. package/dist/cjs-bundle/index.js.map +7 -0
  19. package/dist/esm/index.d.ts +1252 -0
  20. package/dist/esm/index.js +3772 -0
  21. package/dist/esm/index.js.map +7 -0
  22. package/dist/esm-bundle/index.d.ts +1259 -0
  23. package/dist/esm-bundle/index.js +11495 -0
  24. package/dist/esm-bundle/index.js.map +7 -0
  25. package/fileutils.js +40 -0
  26. package/libfaust-wasm/libfaust-wasm.d.cts +3 -0
  27. package/libfaust-wasm/libfaust-wasm.d.ts +3 -0
  28. package/libfaust-wasm/libfaust-wasm.data +0 -0
  29. package/libfaust-wasm/libfaust-wasm.data.d.ts +2 -0
  30. package/libfaust-wasm/libfaust-wasm.js +21 -0
  31. package/libfaust-wasm/libfaust-wasm.wasm +0 -0
  32. package/libfaust-wasm/libfaust-wasm.wasm.d.ts +2 -0
  33. package/library.md +91 -0
  34. package/package.json +43 -0
  35. package/postbuild-bundled.js +11 -0
  36. package/postbuild.js +63 -0
  37. package/prebuild-bundled.js +11 -0
  38. package/rsrc/overview.png +0 -0
  39. package/rsrc/overview.xlsx +0 -0
  40. package/scripts/faust2sndfile.js +43 -0
  41. package/scripts/faust2svg.js +17 -0
  42. package/scripts/faust2wasm.js +29 -0
  43. package/src/FaustAudioWorkletNode.ts +264 -0
  44. package/src/FaustAudioWorkletProcessor.ts +273 -0
  45. package/src/FaustCompiler.ts +252 -0
  46. package/src/FaustDspGenerator.ts +498 -0
  47. package/src/FaustDspInstance.ts +141 -0
  48. package/src/FaustFFTAudioWorkletProcessor.ts +554 -0
  49. package/src/FaustOfflineProcessor.ts +141 -0
  50. package/src/FaustScriptProcessorNode.ts +80 -0
  51. package/src/FaustSvgDiagrams.ts +37 -0
  52. package/src/FaustWasmInstantiator.ts +202 -0
  53. package/src/FaustWebAudioDsp.ts +1060 -0
  54. package/src/LibFaust.ts +57 -0
  55. package/src/WavDecoder.ts +207 -0
  56. package/src/WavEncoder.ts +204 -0
  57. package/src/copyWebStandaloneAssets.d.ts +3 -0
  58. package/src/copyWebStandaloneAssets.js +20 -0
  59. package/src/exports-bundle.ts +5 -0
  60. package/src/exports.ts +24 -0
  61. package/src/faust2svgFiles.d.ts +3 -0
  62. package/src/faust2svgFiles.js +43 -0
  63. package/src/faust2wasmFiles.d.ts +5 -0
  64. package/src/faust2wasmFiles.js +93 -0
  65. package/src/faust2wavFiles.d.ts +3 -0
  66. package/src/faust2wavFiles.js +58 -0
  67. package/src/index-bundle-iife.ts +7 -0
  68. package/src/index-bundle.ts +5 -0
  69. package/src/index.ts +5 -0
  70. package/src/instantiateFaustModule.ts +32 -0
  71. package/src/instantiateFaustModuleFromFile.ts +57 -0
  72. package/src/types.ts +234 -0
  73. package/test/clarinet.dsp +1 -0
  74. package/test/faustlive-wasm/faust-ui/index.css +414 -0
  75. package/test/faustlive-wasm/faust-ui/index.css.map +1 -0
  76. package/test/faustlive-wasm/faust-ui/index.d.ts +1 -0
  77. package/test/faustlive-wasm/faust-ui/index.js +3478 -0
  78. package/test/faustlive-wasm/faust-ui/index.js.map +1 -0
  79. package/test/faustlive-wasm/faustwasm/index.d.ts +1252 -0
  80. package/test/faustlive-wasm/faustwasm/index.js +3772 -0
  81. package/test/faustlive-wasm/faustwasm/index.js.map +7 -0
  82. package/test/faustlive-wasm/index.css +97 -0
  83. package/test/faustlive-wasm/index.html +130 -0
  84. package/test/faustlive-wasm/index.js +638 -0
  85. package/test/guitar.dsp +1 -0
  86. package/test/guitar1.dsp +2 -0
  87. package/test/libfaust-in-worklet/index.html +13 -0
  88. package/test/libfaust-in-worklet/index.js +40 -0
  89. package/test/mono.dsp +8 -0
  90. package/test/node/test.js +17 -0
  91. package/test/noise.dsp +3 -0
  92. package/test/organ.dsp +5 -0
  93. package/test/organ1.dsp +6 -0
  94. package/test/osc.dsp +4 -0
  95. package/test/osc2.dsp +5 -0
  96. package/test/osc3.dsp +4 -0
  97. package/test/p-dj.dsp +4 -0
  98. package/test/poly.dsp +13 -0
  99. package/test/rev.dsp +2 -0
  100. package/test/t1.dsp +1 -0
  101. package/test/web/fft.html +87 -0
  102. package/test/web/fft1.html +88 -0
  103. package/test/web/fft2.html +114 -0
  104. package/test/web/fftw.js +1907 -0
  105. package/test/web/index.html +12 -0
  106. package/test/web/index.js +235 -0
  107. package/test/web/kissfft.js +904 -0
  108. package/test/web/mono.html +46 -0
  109. package/test/web/poly-key.html +62 -0
  110. package/test/web/poly.html +59 -0
  111. package/tsconfig.json +22 -0
Binary file
@@ -0,0 +1,2 @@
1
+ declare const data: Uint8Array;
2
+ export default data;
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
+ }