EDIT : we are at the Web Audio conference 2017, and it appears that the way we currently extend the ScripProcessorNode with Faust code is not the Right Way… Part of the post concerning the exposed API will probably change a bit in the near future…

Using latest developments done for the Web (the new WebAssembly backends and libfaust library compiled in WebAssembly with Emscripten), statically and dynamically Faust generated WebAudio nodes can be easily produced and deployed on the Web.

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 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 ScriptProcessor node). The name of the Faust DSP code file is used to define the final ScriptProcessorNode constructor name. So for instance if osc.dsp file is compiled, the following faust.createosc function will be generated:

/** 
* Create a 'monophonic' ScriptProcessorNode Web Audio object 
* by loading and compiling the Faust wasm file
*
* @param context - the Web Audio context
* @param buffer_size - the buffer_size in frames
* @param callback - a callback taking the created ScriptProcessorNode as parameter, 
* or null in case of error
*/
faust.createosc = function(context, buffer_size, callback)

The resulting object is an extended ScriptProcessorNode, to be used as a regular node (like connected to other WebAudio nodes), but which also adds the Faust specific API as defined in the equivalent C++ generated dsp class (see the architecture/faust/dsp/dsp.h header for more technical details), 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 to be used to control the WebAudio node section.

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 = null;

// Slider handler to change the 'noise' volume
function changeVolume(event)
{
    noise.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
    faust.createnoise(audio_context, 1024,
        function (node) {
            noise = node;
            console.log(noise.getJSON());
            // Print paths to be used with 'setParamValue'
            console.log(noise.getParams());
            // Connect it to output as a regular WebAudio node
            noise.connect(audio_context.destination);
        });
}

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

window.addEventListener("load", startnoise);

Look at the Noise and OSC online pages for the complete code.

Generating Polyphonic WebAudio nodes

Assuming that the compiled Faust DSP file is polyphonic ready, a polyphonic ready WebAudio node can be created with the -poly parameter, and will generate the following constructor for the node (where the mydsp part will be replaced by the actual DSP name):

/**
* Create a 'polyphonic' ScriptProcessorNode Web Audio object 
* by loading and compiling the Faust wasm file 
* to be use for the voice, and allocating the number of needed voices
*
* @param context - the Web Audio context
* @param buffer_size - the buffer_size in frames
* @param polyphony - the number of polyphonic voices
* @param callback - a callback taking the created ScriptProcessorNode 
* as parameter, or null in case of error
*/
faust.createmydsp_poly = function(context, buffer_size, polyphony, callback)

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 JavaScript public documentation section for the complete description.

Extended control with -comb parameter

The faust2wasm tool can be used with the -comb parameter to compile several DSP files and contatenate all JavaScript code in a unique resulting comb.js file (possibly to be used in -poly mode also).

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 osc.dsp

Assuming that the compiled Faust DSP file is polyphonic ready, the -poly parameter can be used to generate a polyphonic MIDI controlable instrument, to be used with a MIDI application or device.

The -links generates the DSP processor SVG representation, and links to the original DSP file as well as generated SVG files, so that the HTML page can possibly be deployed as a reusable Faust DSP resource.

Several pages generetd with the faust2webaudiowasm tool can be seen here.

WebAssembly module optimization

Assuming that you have Binaryen tools installed on your machine, the faust2wasm and faust2webaudiowasm scripts can take an additional -opt parameter to allow WebAssembly module optimization.

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>

Then the two following functions are 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 ScriptProcessorNode Web Audio object from a factory
*
* @param factory - the DSP factory
* @param context - the Web Audio context
* @param buffer_size - the buffer_size in frames
* @param callback - a callback taking the created ScriptProcessorNode as parameter, 
* or null in case of error
*/
faust.createDSPInstance = function (factory, context, buffer_size, callback) 
/**
* Create a 'polyphonic' 'ScriptProcessorNode Web Audio object from a factory
*
* @param factory - the DSP factory
* @param context - the Web Audio context
* @param buffer_size - the buffer_size in frames
* @param polyphony - the number of polyphonic voices
* @param callback - a callback taking the created ScriptProcessorNode as parameter, 
* or null in case of error
*/
faust.createPolyDSPInstance = function (factory, context, buffer_size, 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.createDSPInstance:

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 = "http://faust.grame.fr/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.createDSPInstance(factory, audio_context, 1024
                                                        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 page demonstrates the dynamic OSC complete code (based on the example seen before). The Dynamic Organ 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 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 -ftz 2 foo.dsp 

The same for the faust2webaudiowasm tool:

faust2webaudiowasm -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.createPolyFactory(dsp_code, ['-ftz', '2'], callback);