# Deploying Faust DSP on the Web

[**WARNING**] 03/31/2024: this code using the Faust 2.70.3 is now obsolete. Please use the [faustwasm](https://github.com/grame-cncm/faustwasm) project for new developments [**WARNING**]. This folder will be cleanup at the next release.

## Deploying statically compiled Faust WebAudio nodes

From a **foo.dsp** source file, JavaScript and the associated WebAssembly files can be produced with the following script:

    faust2wasm -worklet foo.dsp 

This will generate a **foo.wasm** file with the WebAssembly module as binary code, as well as a **foo.js** wrapper file containing the code needed to turn the Faust DSP in a fully working WebAudio node (using an extended **AudioWorkletNode** node). The name of the Faust DSP code file is used to define the final AudioWorkletNode constructor name. So for instance if **osc.dsp** file is compiled, the **Faustosc** class will be generated and can be used the following way:

```
// Create the Faust generated node
var pluginURL = ".";
var plugin = new Faustosc(audio_context, pluginURL);
plugin.load().then(node => {....});
```

The resulting node is an extended AudioWorkletNode, to be used as a regular node (like connected to other WebAudio nodes), as well as JavaScript more specific functions. For instance the following ones will have to be used to control the node:

    var paths = node.getParams(); // get the set of paths to read/write input control parameters

Then knowing the path for a given parameter, the following function is used to change the parameter value:

    node.setParamValue("/Oscillator/freq", 0.6);

A full JSON description of the node with the complete UI, can be retrieved with:  

    var json = node.getJSON(); 

The complete usable API is fully documented in the generated JavaScript file in the *Public API* section. **To properly deallocate ressources at the end of its life, be sure to call the *destroy* function on the node.**. Use `faust2wasm -h` to see all available options.

### A simple example Web page

A simple Web page using the files generated by **faust2wasm** on the **noise.dsp** file can be defined with the following parts:

```
<!-- Load 'faust2wasm' script generated .js file -->
<script src="noise.js"></script>
```

A slider to control the noise volume parameter is defined with:

```
<P> Noise volume:
<input type="range" oninput="changeVolume(event) "min="0" max="1" value="0.5" step="0.01"/>
```

The WebAudio context is created and the noise slide hander is defined with:

```
var isWebKitAudio = (typeof (webkitAudioContext) !== "undefined");
var audio_context = (isWebKitAudio) ? new webkitAudioContext() : new AudioContext();
var noise_dsp = null;

// Slider handler to change the 'noise' volume
function changeVolume(event)
{
    noise_dsp.setParamValue("/Noise/Volume", parseFloat(event.target.value));
}
```

A **startnoise** function which creates the Faust WebAudio node is defined with:

```
function startnoise()
{
    // Create the Faust generated node
    var pluginURL = ".";
    var plugin = new Faustnoise(audio_context, pluginURL);
    plugin.load().then(node => {
        noise_dsp = node;
        console.log(noise_dsp.getJSON());
        // Print path to be used with 'setParamValue'
        console.log(noise_dsp.getParams());
        // Connect it to output as a regular WebAudio node
        noise_dsp.connect(audio_context.destination);
    });
}
```

An finally the load handler is defined to activate the code:

    window.addEventListener("load", startnoise);

Look at the [Noise](noise-wasm.html) and [OSC](osc-wasm.html) pages for the complete code. 

Note that pages loading an additional .wasm file cannot directly be loaded in Chrome. You'll have to start a local server (using the *python -m SimpleHTTPServer* command for instance) and access them with their *http://* based URL.  


### Generating Polyphonic WebAudio nodes

Assuming that the compiled Faust DSP file is [polyphonic ready](https://faust.grame.fr/doc/manual/index.html#midi-polyphony-support), a polyphonic ready WebAudio node can be created by adding the *-poly* option, and will generate the following class for the node, to be used like: 


```
// Create the Faust generated node with 16 voices
var pluginURL = ".";
var plugin = new FaustorganPoly(audio_context, 16, pluginURL);
plugin.load().then(node => {....});
```

Polyphonic nodes have an extended API to be controled with MIDI messages: 

```
/**
* Instantiates a new polyphonic voice. 
*
* @param channel - the MIDI channel (0..15, not used for now)
* @param pitch - the MIDI pitch (0..127)
* @param velocity - the MIDI velocity (0..127)
*/
keyOn = function (channel, pitch, velocity) 
```

```
/**
* De-instantiates a polyphonic voice. 
*
* @param channel - the MIDI channel (0..15, not used for now)
* @param pitch - the MIDI pitch (0..127)
* @param velocity - the MIDI velocity (0..127)
*/
keyOff = function (channel, pitch, velocity)
```

```
/**
* Gently terminates all the active voices.
*/
allNotesOff = function ()
````

Look at the [Organ](organ-wasm.html) page for the complete code. Look at the JavaScript public documentation section for the complete description. 


## Deploying WebAudio Plugins (WAP)

The [WebAudio Plugins](https://github.com/micbuffa/WebAudioPlugins) is a plugin format currently developed by a group of researchers and developers. Faust DSP code can be directly compiled for it using the *-wap* parameter, like this: 

    faust2wasm -wap osc.dsp 

In this case a generic **main.js** file is created, a **main.json** file to describe the plugin, and an **index.html** file to test it.

## Generating fully working self-contained HTML pages

The **faust2webaudiowasm** script can be used to generate a fully working self-contained HTML page, with a SVG/CSS a Graphical User Interface. From the **osc.dsp** Faust DSP source file, it will generate an **osc.html** file:

    faust2webaudiowasm -worklet osc.dsp

Assuming that the compiled Faust DSP file is polyphonic ready, the *-poly* parameter can be used to generate a polyphonic MIDI controllable instrument, to be used with a MIDI application or device. Use `faust2webaudiowasm -h` to see all available options.


## Deploying dynamically compiled Faust WebAudio nodes

Since the **libfaust** library is available for the Web, it becomes possible to **embed the complete dynamic compilation chain in a Web page**, from the Faust DSP source to the executable WebAudio node. First the following resources (located on the Faust GitHub in architecture/webaudio folder) have to be loaded in the page:

```
<!-- Load 'libfaust' library and wrapper code -->
<script src="libfaust-wasm.js"></script>
<script src="webaudio-wasm-wrapper.js"></script>
```

The **faust_module** global is defined in webaudio-wasm-wrapper.js file, `onRuntimeInitialized` will be called when the code is ready. So something like the following line has to be written:

```
faust_module['onRuntimeInitialized'] = init;
```

The two following functions are then used to generate factories, creating later on *monophonic* or *polyphonic* instances (this is necessary because of the way internal WebAssembly memory is managed): 

```
/**
* Create a DSP factory from source code as a string to be used to create monophonic DSP 
*
* @param code - the source code as a string
* @param argv - an array of parameters to be given to the Faust compiler
* @param callback - a callback taking the created DSP factory as parameter, or null in case of error
*/
faust.createDSPFactory = function (code, argv, callback) 
```

```
/**
* Create a DSP factory from source code as a string to be used to create polyphonic DSP 
*
* @param code - the source code as a string
* @param argv - an array of parameters to be given to the Faust compiler
* @param callback - a callback taking the created DSP factory as parameter, or null in case of error
*/
faust.createPolyDSPFactory = function (code, argv, callback) 
```

The two following functions are used to generate *monophonic* or *polyphonic* Faust WebAudio nodes:

```
/**
* Create a AudioWorklet Web Audio object from a factory
*
* @param factory - the DSP factory
* @param context - the Web Audio context
* @param callback - a callback taking the created AudioWorklet as parameter, or null in case of error
*/
faust.createDSPWorkletInstance = function (factory, context, callback) 
```

```
/**
* Create a 'polyphonic' AudioWorklet Web Audio object from a factory
*
* @param factory - the DSP factory
* @param context - the Web Audio context
* @param polyphony - the number of polyphonic voices
* @param callback - a callback taking the created AudioWorklet as parameter, or null in case of error
*/
faust.createPolyDSPWorkletInstance = function (factory, context, polyphony, callback) 
```

The resulting nodes have the same API as statically compiled nodes described in the first section, so can be controlled the same way, including the polyphonic ones. Here is a code example using **faust.createDSPFactory** and **faust.createDSPWorkletInstance**:

```
var isWebKitAudio = (typeof (webkitAudioContext) !== "undefined");
var audio_context = (isWebKitAudio) ? new webkitAudioContext() : new AudioContext();
var dsp_code = "import(\"stdfaust.lib\"); vol = hslider(\"volume [unit:dB]\", 0, -96, 0, 0.1) : ba.db2linear : si.smoo; freq = hslider(\"freq [unit:Hz]\", 1000, 20, 24000, 1); process = vgroup(\"Oscillator\", os.osc(freq) * vol);";
var osc = null;
var libraries_url = "/modules/libraries/";

function startosc()
{
    // Prepare argv list
    var argv = [];
    argv.push("-ftz");
    argv.push("2");
    argv.push("-I");
    argv.push(libraries_url);

    // Dynamically create the Faust generated node from explicit DSP source in 'dsp_code'
    faust.createDSPFactory(dsp_code,
                            argv,
                            function (factory) {
                            faust.createDSPWorkletInstance(factory, audio_context
                                                            function (node) {
                                                                osc = node;
                                                                console.log(osc.getJSON());
                                                                // Print paths to be used with 'setParamValue'
                                                                console.log(osc.getParams());
                                                                // Connect it to output as a regular WebAudio node
                                                                osc.connect(audio_context.destination);
                                                            })});
}
```

The [Dynamic OSC](dynamic-osc-worklet-wasm.html) page demonstrates the dynamic OSC complete code (based on the example seen before). The [Dynamic Organ](dynamic-organ-worklet-wasm.html) page demonstrates a polyphonic organ instrument, which loads a DSP from an url, and ready to be controlled with a MIDI device or application. Look at the [Dynamic Faust compiler](faustlive-wasm.html) page for a more complete use-case of the dynamic compiler.

## Float denormal handling

A specific problem occurs when audio computation produces denormal float values, which is quite common with recursive filters, and can be extremely costly to compute on some processors like the Intel family for instance. A Flush To Zero (FTZ) mode for denormals can usually be set at hardware level, but it not yet available in the WebAssembly MVP version, which strictly conform to the IEEE 754 norm 8. 

Thus an automatic software strategy which consists in adding FTZ code in all recursive loops has been implemented in the Faust compiler. To activate it, the *-ftz* compilation parameter must be used at compilation time. 

The *-ftz 1* mode adds a test in each recursive loop which uses the *fabs* function and a threshold to detect subnormal samples (slower). The *-ftz 2* mode adds a test in each recursive loop which uses a mask to detect subnormal samples (faster). 

Use for example the following line to active software denormal handing when using **faust2wasm** tool:

    faust2wasm -worklet -ftz 2 foo.dsp 

The same for the **faust2webaudiowasm** tool:

    faust2webaudiowasm -worklet -ftz 2 foo.dsp 

For dynamic compilation, the *-ftz v* flag will have to be added in the *argv* parameter in **faust.createDSPFactory** or **faust.createPolyDSPFactory**, like for instance:

    faust.createPolyDSPFactory(dsp_code, ['-ftz', '2'], callback);

