The FaustWasm library presents a convenient, high-level API that wraps around Faust compiler. This library's interface is primarily designed for TypeScript usage, although it also provides API descriptions and documentation for pure JavaScript. The WebAssembly version of the Faust Compiler, compatible with both Node.js and web browsers, has been compiled using Emscripten 3.1.31.
The library offers functionality for compiling Faust DSP code into WebAssembly, enabling its utilization as WebAudio nodes within a standard WebAudio node graph. Moreover, it supports offline rendering scenarios. Furthermore, supplementary tools can be employed for generating SVGs from Faust DSP programs.
Synthesizer and effect mono nodes, as well as polyphonic poly nodes can be created. MIDI support is activated as soon as MIDI metadata are used in the DSP code for mono nodes, and always in polyphonic mode.
Sensors (accelerometer and gyroscope) are supported, as well as the Progressive Web Application model.
Please use a stable version of Node.js 16+ to work with this project.
Clone and get into this project:
git clone https://github.com/grame-cncm/faustwasm.git
cd faustwasm
Install development dependencies:
npm install
Possibly:
npm update
Build the files:
npm run build
You'll have to raise the package version number in package.json
before npm run build
to properly work.
For example:
rm -rf test/out # make sure you are under the faustwasm directory.
node scripts/faust2wasm.js test/mono.dsp test/out
will create a set of files: index.js
, dsp-module.wasm
, dsp-meta.json
, index.html
in the out
folder. -no-template
option will omit index.js
and index.html
files.
Polyphonic instrument with:
rm -rf test/out # make sure you are under the faustwasm directory.
node scripts/faust2wasm.js test/poly.dsp test/out -poly
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.
You can create a standalone Progressive Web Application using the command line:
node scripts/faust2wasm.js test/rev.dsp test/rev -pwa
will create a set of files: icon.png
, service-worker.js
, manifest.json
, index.js
, dsp-module.wasm
, dsp-meta.json
, index.html
, and the faustwasm
, faust-ui
folders in the rev
folder.
The folder contains the necessary ressources to deploy the Faust application as a PWA on a server, to be installed and used offline. Note that audio files used by the soundfile
primitives in the DSP code will have to be mannually added in the folder.
A standalone polyphonic and MIDI standalone Progressive Web Application can be created with:
node scripts/faust2wasm.js test/organ1.dsp test/organ1 -poly -pwa
will create a set of files: icon.png
, service-worker.js
, manifest.json
, index.js
, dsp-module.wasm
, dsp-meta.json
, effect-module.wasm
, effect-meta.json
, index.html
, and the faustwasm
, faust-ui
folders in the organ1
folder.
You can create a standalone using the command line:
node scripts/faust2wasm.js test/rev.dsp test/rev -standalone
will create a set of files: icon.png
, service-worker.js
, manifest.json
, index.js
, dsp-module.wasm
, dsp-meta.json
, index.html
, and the faustwasm
, faust-ui
folders in the rev
folder.
The folder contains the necessary ressources to deploy the Faust application as a PWA on a server, to be installed and used offline. Note that audio files used by the soundfile
primitives in the DSP code will have to be mannually added in the folder.
A standalone polyphonic and MIDI standalone Progressive Web Application can be created with:
node scripts/faust2wasm.js test/organ1.dsp test/organ1 -poly -standalone
will create a set of files: icon.png
, service-worker.js
, manifest.json
, index.js
, dsp-module.wasm
, dsp-meta.json
, effect-module.wasm
, effect-meta.json
, index.html
, and the faustwasm
, faust-ui
folders in the organ1
folder.
For example:
rm -rf test/out # make sure you are under the faustwasm directory.
node scripts/faust2svg.js test/mono.dsp test/out
The main diagram should be in test/out/process.svg
.
For example:
rm -rf test/out # make sure you are under the faustwasm directory.
node scripts/faust2cmajor.js test/organ.dsp test/out
The Cmajor file should be in test/out/organ.cmajor
.
Options:
-
-bs <num>
to setup the rendering buffer size in frames (default: 64) -
-bd 16|24|32
to setup the output file bit-depth (default: 16) -
-c <samples>
to setup the output file length in frames, when -ct is not used (default: SR*5) -
-in <inputWav.wav>
specify an input file to process -
-sr <num>
to setup the output file sample rate (default: 44100) See this help:
node scripts/faust2sndfile.js -h
For example:
rm -rf test/out # make sure you are under the faustwasm directory.
mkdir test/out
node scripts/faust2sndfile.js test/p-dj.dsp test/out/p-dj.wav -c 192000 -sr 48000 -bd 24
Now the test/out/p-dj.wav
should be generated.
node scripts/faust2sndfile.js test/rev.dsp test/out/p-dj-rev.wav -c 192000 -sr 48000 -bd 24 -in test/out/p-dj.wav
npm i -D @grame/faustwasm
In JavaScript:
const FaustWasm = require("@grame/faustwasm");
const path = require("path");
const fs = require("fs");
const {
instantiateFaustModuleFromFile,
LibFaust,
WavEncoder,
FaustMonoDspGenerator,
FaustCompiler,
FaustSvgDiagrams
} = FaustWasm;
(async () => {
const faustModulePath = path.join(__dirname, "../node_modules/@grame/faustwasm/libfaust-wasm/libfaust-wasm.js");
// initialize the libfaust wasm
const faustModule = await instantiateFaustModuleFromFile(faustModulePath);
// Get the Faust compiler
const libFaust = new LibFaust(faustModule);
console.log(libFaust.version());
const compiler = new FaustCompiler(libFaust);
const generator = new FaustMonoDspGenerator();
const sampleRate = 48000;
const name = "Djembe"
const argv = ["-I", "libraries/"];
const code = `
import("stdfaust.lib");
process = ba.pulsen(1, 10000) : pm.djembe(60, 0.3, 0.4, 1);
`;
// Compile the DSP
await generator.compile(compiler, name, code, argv.join(" "));
const processor = await generator.createOfflineProcessor(sampleRate, 1024);
// Generate SVG diagrams.
const svgDiagrams = new FaustSvgDiagrams(compiler);
const svgs = svgDiagrams.from(name, code, argv.join(" "));
console.log(Object.keys(svgs));
const out = processor.render(null, 192000);
const wav = WavEncoder.encode(out, { sampleRate, bitDepth: 24 });
// The wav file is generated
fs.writeFileSync(`${__dirname}/out.wav`, new Uint8Array(wav));
})();
(async () => {
const {
instantiateFaustModuleFromFile,
LibFaust,
WavEncoder,
FaustMonoDspGenerator,
FaustCompiler,
FaustSvgDiagrams
} = await import("../node_modules/@grame/faustwasm/dist/esm/index.js");
// initialize the libfaust wasm
const faustModule = await instantiateFaustModuleFromFile("../node_modules/@grame/faustwasm/libfaust-wasm/libfaust-wasm.js");
// Get the Faust compiler
const libFaust = new LibFaust(faustModule);
window.libFaust = libFaust;
console.log(libFaust.version());
const compiler = new FaustCompiler(libFaust);
const generator = new FaustMonoDspGenerator();
const sampleRate = 48000;
const name = "Djembe"
const argv = ["-I", "libraries/"];
const code = `
import("stdfaust.lib");
process = ba.pulsen(1, 10000) : pm.djembe(60, 0.3, 0.4, 1);
`;
// Compile the DSP
await generator.compile(compiler, name, code, argv.join(" "));
const processor = await generator.createOfflineProcessor(sampleRate, 1024);
// Generate SVG diagrams.
const svgDiagrams = new FaustSvgDiagrams(compiler);
const svgs = svgDiagrams.from(name, code, argv.join(" "));
console.log(Object.keys(svgs));
const out = processor.render(null, 192000);
const wav = WavEncoder.encode(out, { sampleRate, bitDepth: 24 });
// The wav file is generated
const blob = new Blob([wav], { type: "audio/wav" });
const player = document.createElement("audio");
player.controls = true;
player.src = URL.createObjectURL(blob);
document.body.appendChild(player);
const svg = document.createElement("div");
svg.innerHTML = svgs["process.svg"];
document.body.appendChild(svg);
})();
Several examples can be tested by launching a local web server at the faustwasm root level, and going in test/faustlive-wasm
and libfaust-in-worklet
folders.
The package is used in the following projects:
- faust-web-component, a package providing two web components for embedding interactive Faust snippets in web pages.
- Faust Playground, a Web platform designed to enable children to learn basic audio programming in a simple and graphic way.
- Faust Editor, a simple online editor used to edit, compile and run Faust code from any recent Web Browser with WebAssembly support.
- Faust Web IDE, a more powerfull editor used to edit, compile and run Faust code from any recent Web Browser with WebAssembly support.
- Testing the embedded dynamic Faust compiler page allows to test the compiler, with this HTML source code using this JavaScript code.
The API is organised from low to high level as illustrated by the figure below.
The first level is the Faust compiler compiled as a wasm library named libfaust-wasm
.
It consists in 3 different files:
-
libfaust-wasm.wasm
: the Faust compiler provided as a WebAssembly module -
libfaust-wasm.js
: a Javascript loader of the WebAssembly module -
libfaust-wasm.data
: a virtual file system containing the Faust libraries.
The C++ code is compiled with Emscripten 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).
The Faust Compiler Javascript interface is described in FaustCompiler.ts
.
It provides classic Faust compilation services, which output is a raw WebAssembly module with an associated JSON description of the module.
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. It is described in FaustDspGenerator.ts
, FaustWasmInstantiator.ts
, FaustWebAudioDsp.ts
and FaustDspInstance.ts
files.
This level takes a Faust Wasm instance to build an audio node. AudioWorklet and ScriptProcessor nodes are supported. It is described in FaustAudioWorkletNode.ts
and FaustAudioWorkletProcessor.ts
files.
Warning: AudioWorklet is a recent technology and may not be supported by all the browsers. Check the compatibility chart.
The audio node API documentation can be accessed here.
Note that ScriptProcessor is marked as deprecated 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.
Created audio nodes have a start
and stop
methods. When started (which is done by default), they are processing audio buffers. You may have to explicitly stop them to save CPU (and start then again when needed), if for instance several nodes are created at init time before actual use.
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.
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
.
Simply include the following to get access to types and functions:
///<reference types="@grame/faustwasm"/>
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.
In the latter case, it is not necessary to include the libfaust-wasm.js
library, index.js
is sufficient to provide the required services.
This allows to generate lighter and faster-loading HTML pages.
-
FaustSvgDiagrams.ts
: provides facilities to browse Faust generated SVG diagrams -
FaustFFTAudioWorkletProcessor
: provides FFT processing
Html pages embedding the Faust compiler must be served using https, unless using http://localhost.