faust2pd: Pd Patch Generator for Faust

Version 2.16, April 11, 2018

Albert Graef <aggraef@gmail.com>

This package contains software which makes it easier to use Faust DSPs with Pd and the Pure programming language. The main component is the faust2pd script which creates Pd GUI wrappers for Faust DSPs. The package also includes a Pure script faustxml.pure for parsing Faust dsp descriptions in XML or JSON format, as well as a bunch of examples. The software is distributed under the GPL; see the COPYING file for details.

Note

This faust2pd version is written in Pure and was ported from an earlier version written in Pure’s predecessor Q. The version of the script provided here should be 100% backward-compatible to those previous versions, except for the following changes:

  • The (rarely used) -f (a.k.a. –fake-buttons) option was renamed to -b, and a new -f (a.k.a. –font-size) option was added to change the font size of the GUI elements.
  • For compatibility with pd-faust, the -s (a.k.a. –slider-nums) flag is enabled by default now. The corresponding option is still supported for backward compatibility, and for overriding the new –no-slider-nums option which can be used to disable this feature.
  • Most command line options can now also be specified using special meta data in the Faust source.

As of version 2.1, the faust2pd script is now compiled to a native executable before installation. This makes the program start up much faster, which is a big advantage because most xml and json files don’t take long to be processed.

faust2pd version 2.11 and later have full support for Faust’s new JSON-based description format, which is generated with the -json option. The JSON-based interface offers some advantages, such as accessing additional meta data in the Faust source (the XML format doesn’t offer this in the latest Faust versions any more), and the capability to read dsp descriptions straight from running dsp instances in httpd-enabled Faust applications and Grame’s new FaustLive environment. However, this format is only supported in the latest Faust versions (Faust 0.9.70 and later, or Faust 2.0.a30 and later if you are running the Faust2 development version). At the time of this writing, these versions are only available in the Faust git repository.

Copying

Copyright (c) 2009-2017 by Albert Graef.

faust2pd is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

faust2pd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.

Requirements

faust2pd is known to work on Linux, Mac OS X, and Windows, and there shouldn’t be any major roadblocks preventing it to work on other systems supported by Pure.

The faust2pd script is written in the Pure programming language and requires Pure’s XML module, so you need to install these to make it work. Install the latest pure*.tar.gz and pure-xml*.tar.gz packages and you should be set. (Pure 0.47 or later is required.) Also make sure that the LLVM base package is installed, as described in the Pure INSTALL file, some LLVM utilities are needed to make Pure’s batch compiler work.

To run the seqdemo example, you’ll also need the Pd Pure external (pd-pure*.tar.gz), also available at the Pure website.

To compile the examples, you’ll need GNU C++ and make, Pd and, of course, Faust. Make sure you get a recent version of Faust; Faust 0.9.67 or later is required. Faust 0.9.70 and later have support for the new JSON format and offer some other new and convenient capabilities, such as retrieving the JSON description directly from a running dsp instance via Faust’s httpd interface; for the latter, you also need to have the curl program installed. Note that the examples still use the “old” a.k.a. “legacy” Faust library modules, so they should work out of the box with both “old” Faust versions (up to 0.9.85) and later ones featuring the “new” Faust library (anything after 0.9.85, including current git sources).

Installation

Get the latest source from https://github.com/agraef/pure-lang/releases/download/faust2pd-2.16/faust2pd-2.16.tar.gz.

Run make and make install to compile and install the faust2pd program on your system. You can set the installation prefix by running make as make install prefix=/some/path. Default installation prefix is /usr/local, faust2pd is installed in the bin directory below that.

Optionally, you can also run make install-pd to copy the supporting Pd abstractions (faust-*.pd) to your lib/pd/extra directory, so that you can use the patches generated by faust2pd without copying these abstractions to your working directory. The Makefile tries to guess the prefix of your Pd installation, if it guesses wrong, you can specify the prefix explicitly by running make as make install-pd pdprefix=/some/path.

The included faustxml.pure script provides access to Faust-generated dsp descriptions in XML or JSON files to Pure scripts. This module is described in its own appendix below. It may have uses beyond faust2pd, but isn’t normally installed. If you want to use this module, you can just copy it to your Pure library directory.

Quickstart

Run make examples to compile the Faust examples included in this package to corresponding Pd plugins. After that you should be able to run the patches in the various subdirectories of the examples directory. Everything is set up so that you can try the examples “in-place”, without installing anything except the required software as noted in Requirements above. You can also run make realclean before make to regenerate everything from scratch (this requires faust2pd, so this will only work if you already installed the Pure interpreter).

Faust Pd plugins work in much the same way as the well-known plugin~ object (which interfaces to LADSPA plugins), except that each Faust DSP is compiled to its own Pd external. Under Linux, the basic compilation process is as follows (taking the freeverb module from the Faust distribution as an example):

# compile the Faust source to a C++ module using the "puredata" architecture
faust -a puredata.cpp freeverb.dsp -o freeverb.cpp
# compile the C++ module to a Pd plugin
g++ -shared -Dmydsp=freeverb freeverb.cpp -o freeverb~.pd_linux

By these means, a Faust DSP named xyz with n audio inputs and m audio outputs becomes a Pd object xyz~ with n+1 inlets and m+1 outlets. The leftmost inlet/outlet pair is for control messages only. This allows you to inspect and change the controls the unit provides, as detailed below. The remaining inlets and outlets are the audio inputs and outputs of the unit, respectively. For instance, freeverb.dsp becomes the Pd object freeverb~ which, in addition to the control inlet/outlet pair, has 2 audio inputs and outputs.

When creating a Faust object it is also possible to specify, as optional creation parameters, an extra unit name (this is explained in the following section) and a sample rate. If no sample rate is specified explicitly, it defaults to the sample rate at which Pd is executing. (Usually it is not necessary or even desirable to override the default choice, but this might occasionally be useful for debugging purposes.)

As already mentioned, the main ingredient of this package is a Pure script named “faust2pd” which allows you to create Pd abstractions as “wrappers” around Faust units. The wrappers generated by faust2pd can be used in Pd patches just like any other Pd objects. They are much easier to operate than the “naked” Faust plugins themselves, as they also provide “graph-on-parent” GUI elements to inspect and change the control values.

The process to compile a plugin and build a wrapper patch is very similar to what we’ve seen above. You only have to add the -xml option when invoking the Faust compiler and run faust2pd on the resulting XML file:

# compile the Faust source and generate the xml file
faust -a puredata.cpp -xml freeverb.dsp -o freeverb.cpp
# compile the C++ module to a Pd plugin
g++ -shared -Dmydsp=freeverb freeverb.cpp -o freeverb~.pd_linux
# generate the Pd patch from the xml file
faust2pd freeverb.dsp.xml

As of version 2.11, faust2pd can also process dsp descriptions in Faust’s new JSON format (-json option). Please see Wrapping DSPs with faust2pd below for further details.

Note that, just as with other Pd externals and abstractions, the compiled .pd_linux modules and wrapper patches must be put somewhere where Pd can find them. To these ends you can either move the files into the directory with the patches that use the plugin, or you can put them into the lib/pd/extra directory or some other directory on Pd’s library path for system-wide use.

Also, faust2pd-generated wrappers use a number of supporting abstractions (the faust-*.pd files in the faust2pd sources), so you have to put these into the directory of the main patch or install them under lib/pd/extra as well. (The make pd-install step does the latter, see Installation above.)

Control Interface

The control inlet of a Faust plugin understands messages in one of the following forms:

  • bang, which reports all available controls of the unit on the control outlet, in the form: type name val init min max step, where type is the type of the control as specified in the Faust source (checkbox, nentry, etc.), name its (fully qualified) name, val the current value, and init, min, max, step the initial value, minimum, maximum and stepsize of the control, respectively.
  • foo 0.99, which sets the control foo to the value 0.99, and outputs nothing.
  • Just foo, which outputs the (fully qualified) name and current value of the foo control on the control outlet.

Control names can be specified in their fully qualified form, like e.g. /gnu/bar/foo which indicates the control foo in the subgroup bar of the topmost group gnu, following the hierarchical group layout defined in the Faust source. This lets you distinguish between different controls with the same name which are located in different groups. To find out about all the controls of a unit and their fully qualified names, you can bang the control inlet of the unit as described above, and connect its control outlet to a print object, which will cause the descriptions of all controls to be printed in Pd’s main window. (The same information can also be used, e.g., to initialize GUI elements with the proper values. Patches generated with faust2pd rely on this.)

You can also specify just a part of the control path (like bar/foo or just foo in the example above) which means that the message applies to all controls which have the given pathname as the final portion of their fully qualified name. Thus, if there is more than one foo control in different groups of the Faust unit then sending the message foo to the control inlet will report the fully qualified name and value for each of them. Likewise, sending foo 0.99 will set the value of all controls named foo at once.

Concerning the naming of Faust controls in Pd you should also note the following:

  • A unit name can be specified at object creation time, in which case the given symbol is used as a prefix for all control names of the unit. E.g., the control /gnu/bar/foo of an object baz~ created with baz~ baz1 has the fully qualified name /baz1/gnu/bar/foo. This lets you distinguish different instances of an object such as, e.g., different voices of a polyphonic synth unit.
  • Pd’s input syntax for symbols is rather restrictive. Therefore group and control names in the Faust source are mangled into a form which only contains alphanumeric characters and hyphens, so that the control names are always legal Pd symbols. For instance, a Faust control name like meter #1 (dB) will become meter-1-dB which can be input directly as a symbol in Pd without any problems.
  • “Anonymous” groups and controls (groups and controls which have empty labels in the Faust source) are omitted from the path specification. E.g., if foo is a control located in a main group with an empty name then the fully qualified name of the control is just /foo rather than //foo. Likewise, an anonymous control in the group /foo/bar is named just /foo/bar instead of /foo/bar/.

Last but not least, there is also a special control named active which is generated automatically for your convenience. The default behaviour of this control is as follows:

  • When active is nonzero (the default), the unit works as usual.
  • When active is zero, and the unit’s number of audio inputs and outputs match, then the audio input is simply passed through.
  • When active is zero, but the unit’s number of audio inputs and outputs do not match, then the unit generates silence.

The active control frequently alleviates the need for special “bypass” or “mute” controls in the Faust source. However, if the default behaviour of the generated control is not appropriate you can also define your own custom version of active explicitly in the Faust program; in this case the custom version will override the default one.

Examples

In the examples subdirectory you’ll find a bunch of sample Faust DSPs and Pd patches illustrating how Faust units are used in Pd.

  • The examples/basic/test.pd patch demonstrates the basics of operating “bare” Faust plugins in Pd. You’ll rarely have to do this when using the wrappers generated with the faust2pd program, but it is a useful starting point to take a look behind the scenes anyway.
  • The examples/faust directory contains all the examples from (an earlier version of) the Faust distribution, along with corresponding Pd wrappers generated with faust2pd. Have a look at examples/faust/faustdemo.pd to see some of the DSPs in action. Note that not all examples from the Faust distribution are working out of the box because of name clashes with Pd builtins, so we renamed those. We also edited some of the .dsp sources (e.g., turning buttons into checkboxes or sliders into nentries) where this seemed necessary to make it easier to operate the Pd patches.
  • The examples/synth directory contains various plugins and patches showing how to implement polyphonic synthesizers using Faust units. Take a look at examples/synth/synth.pd for an example. If you have properly configured your interfaces then you should be able to play the synthesizer via Pd’s MIDI input.
  • The examples/seqdemo/seqdemo.pd patch demonstrates how to operate a multitimbral synth, built with Faust units, in an automatic fashion using a pattern sequencer programmed in Pure. This example requires the Pure interpreter as well as the pd-pure plugin available from https://agraef.github.io/pure-lang/.

Wrapping DSPs with faust2pd

The faust2pd script generates Pd patches from the dsp.xml files created by Faust when run with the -xml option. Most of the sample patches were actually created that way. After installation you can run the script as follows:

faust2pd [-hVbs] [-f size] [-o output-file] [-n #voices]
  [-r max] [-X patterns] [-x width] [-y height] input-file

The default output filename is input-file with new extension .pd. Thus, faust2pd filename.dsp.xml creates a Pd patch named filename.pd from the Faust XML description in filename.dsp.xml.

JSON Support

As of version 2.11, faust2pd also fully supports Faust’s new JSON-based description format, which is generated with the -json option. In this case the filename extension is .dsp.json, so faust2pd is invoked as faust2pd filename.dsp.json instead. Please note that this format is only supported in the latest Faust versions (Faust 0.9.70 and later).

Instead of generating a JSON file with the Faust compiler, you can also read the JSON description straight from a running dsp instance via the httpd interface. This works with both stand-alone Faust applications which have the httpd interface enabled (e.g., created with faust2jaqt -httpd mydsp.dsp) and instances running in Grame’s FaustLive application. To these ends, you just specify the URL of the running dsp instance instead of the JSON filename. (To make this work, you need to have curl installed.)

For instance, assuming that there’s a Faust dsp running locally at port 5510, you can run faust2pd as follows to create a GUI patch for it: faust2pd http://localhost:5510. You can find out about the port a Faust dsp runs on by inspecting the terminal output of a stand-alone Faust application. In FaustLive, use the Window / View QRcode menu option to retrieve the IP under which the httpd interface of a dsp window can be accessed. If FaustLive is running locally on its default port (7777), then you can also retrieve a list of dsp instances currently running in FaustLive using faust2pd http://localhost:7777/availableInterfaces. This prints the dsp names along with their URLs on stdout.

faust2pd Options

The faust2pd program understands a number of options which affect the layout of the GUI elements and the contents of the generated patch. Here is a brief list of the available options:

-h, --help display a short help message and exit
-V, --version display the version number and exit
-b, --fake-buttons
 replace buttons (bangs) with checkboxes (toggles)
-f, --font-size
 font size for GUI elements (10 by default)
-n, --nvoices create a synth patch with the given number of voices
-o, --output-file
 output file name (.pd file)
-r, --radio-sliders
 radio controls for sliders
-s, --slider-nums
 sliders with additional number control
-X, --exclude exclude controls matching the given glob patterns
-x, --width maximum width of the GUI area
-y, --height maximum height of the GUI area

Just like the Faust plugin itself, the generated patch has a control input/output as the leftmost inlet/outlet pair, and the remaining plugs are signal inlets and outlets for each audio input/output of the Faust unit. However, the control inlet/outlet pair works slightly different from that of the Faust plugin. Instead of being used for control replies, the control outlet of the patch simply passes through its control input (after processing messages which are understood by the wrapped plugin). By these means control messages can flow along with the audio signal through an entire chain of Faust units. (You can find an example of this in examples/faust/faustdemo.pd.) Moreover, when generating a polyphonic synth patch using the -n option then there will actually be two control inlets, one for note messages and one for ordinary control messages; this is illustrated in the examples/synth/synth.pd example.

The generated patch also includes the necessary GUI elements to see and change all (active and passive) controls of the Faust unit. Faust control elements are mapped to Pd GUI elements in an obvious fashion, following the horizontal and vertical layout specified in the Faust source. The script also adds special buttons for resetting all controls to their defaults and to operate the special active control.

This generally works very well, but the control GUIs generated by faust2pd are somewhat hampered by the limited range of GUI elements available in a vanilla Pd installation. As a remedy, faust2pd provides various options to change the content of the generated wrapper and work around these limitations.

  • There are no real button widgets as required by the Faust specification, so bangs are used instead. There is a global delay time for switching the control from 1 back to 0, which can be changed by sending a value in milliseconds to the faust-delay receiver. If you need interactive control over the switching time then it is better to use checkboxes instead, or you can have faust2pd automatically substitute checkboxes for all buttons in a patch by invoking it with the -b a.k.a. –fake-buttons option.
  • Sliders in Pd do not display their value in numeric form so it may be hard to figure out what the current value is. Therefore faust2pd has an option -s a.k.a. –slider-nums which causes it to add a number box to each slider control. (This flag also applies to Faust’s passive bargraph controls, as these are implemented using sliders, see below. As of faust2pd 2.12, this option is actually the default now, but you can still disable it with the –no-slider-nums option.)
  • Pd’s sliders also have no provision for specifying a stepsize, so they are an awkward way to input integral values from a small range. OTOH, Faust doesn’t support the “radio” control elements which Pd provides for that purpose. As a remedy, faust2pd allows you to specify the option -r max (a.k.a. –radio-sliders=max) to indicate that sliders with integral values from the range 0..max-1 are to be mapped to corresponding Pd radio controls.
  • Faust’s bargraphs are emulated using sliders. Note that these are passive controls which just display a value computed by the Faust unit. A different background color is used for these widgets so that you can distinguish them from the ordinary (active) slider controls. The values shown in passive controls are sampled every 40 ms by default. You can change this value by sending an appropriate message to the global faust-timer receiver.
  • Since Pd has no “tabbed” (notebook-like) GUI element, Faust’s tgroups are mapped to hgroups instead. It may be difficult to present large and complicated control interfaces without tabbed dialogs, though. As a remedy, you can control the amount of horizontal or vertical space available for the GUI area with the -x and -y (a.k.a. –width and –height) options and faust2pd will then try to break rows and columns in the layout to make everything fit within that area. (This feature has only been tested with simple layouts so far, so beware.)
  • You can also exclude certain controls from appearing in the GUI using the -X option. This option takes a comma-separated list of shell glob patterns indicating either just the names or the fully qualified paths of Faust controls which are to be excluded from the GUI. For instance, the option -X 'volume,meter*,faust/resonator?/*' will exclude all volume controls, all controls whose names start with meter, and all controls in groups matching faust/resonator?. (Note that the argument to -X has to be quoted if it contains any wildcards such as * and ?, so that the shell doesn’t try to expand the patterns beforehand. Also note that only one -X option is recognized, so you have to specify all controls to be excluded as a single option.)
  • Faust group labels are not shown at all, since I haven’t found an easy way to draw some kind of labelled frame in Pd yet.

Despite these limitations, faust2pd appears to work rather well, at least for the kind of DSPs found in the Faust distribution. (Still, for more complicated control surfaces and interfaces to be used on stage you’ll probably have to edit the generated GUI layouts by hand.)

For convenience, all the content-related command line options mentioned above can also be specified in the Faust source, as special meta data in the label of the toplevel group of the dsp. These take the form [pd:option] or [pd:option=value] where option is any of the layout options understood by faust2pd. For instance:

process = vgroup("mysynth [pd:nvoices=8] [pd:no-slider-nums]", ...);

Alternatively, as of faust2pd 2.11, these options can also be specified as a single chunk of global meta data, using the faust2pd key. So the above can also be written as:

declare faust2pd "--nvoices=8 --no-slider-nums";

Note that source options carrying arguments, like --nvoices in the above example, can still be overridden with corresponding command line options. Also note that all this works with the JSON format only, as meta data in the Faust source isn’t recorded in the XML format produced by recent Faust versions any more.

Conclusion

Creating Faust plugins for use with Pd has never been easier before, so I hope that you’ll soon have much joy trying your Faust programs in Pd. Add Pd-Pure to this, and you can program all your specialized audio and control objects using two modern-style functional languages which are much more fun than C/C++. Of course there’s an initial learning curve to be mastered, but IMHO it is well worth the effort. The bottomline is that Pd+Faust+Pure really makes an excellent combo which provides you with a powerful, programmable interactive environment for creating advanced computer music and multimedia applications with ease.

Acknowledgements

Thanks are due to Yann Orlarey for his wonderful Faust, which makes developing DSP algorithms so easy and fun.

Appendix: faustxml

The faustxml module is provided along with faust2pd to retrieve the description of a Faust DSP from its XML or JSON file as a data structure which is ready to be processed by Pure programs. It may also be useful in other Pure applications which need to inspect descriptions of Faust DSPs.

The main entry point is the info function which takes the name of a Faust-generated XML or JSON file as argument and returns a tuple (name, descr, version, in, out, controls, options) with the name, description, version, number of inputs and outputs, control descriptions and faust2pd options (from the global meta data of the dsp module). A couple of other convenience functions are provided to deal with the control descriptions.

Usage

Use the following declaration to import this module in your programs:

using faustxml;

For convenience, you can also use the following to get access to the module’s namespace:

using namespace faustxml;

Data Structure

The following constructors are used to represent the UI controls of Faust DSPs:

constructor faustxml::button label
constructor faustxml::checkbox label

A button or checkbox with the given label.

constructor faustxml::nentry (label,init,min,max,step)
constructor faustxml::vslider (label,init,min,max,step)
constructor faustxml::hslider (label,init,min,max,step)

A numeric input control with the given label, initial value, range and stepwidth.

constructor faustxml::vbargraph (label,min,max)
constructor faustxml::hbargraph (label,min,max)

A numeric output control with the given label and range.

constructor faustxml::vgroup (label,controls)
constructor faustxml::hgroup (label,controls)
constructor faustxml::tgroup (label,controls)

A group with the given label and list of controls in the group.

Operations

faustxml::controlp x

Check for control description values.

faustxml::control_type x
faustxml::control_label x
faustxml::control_args x

Access functions for the various components of a control description.

faustxml::controls x

This function returns a flat representation of a control group x as a list of basic control descriptions, which provides a quick way to access all the control values of a Faust DSP. The grouping controls themselves are omitted. You can pass the last component of the return value of the info function to this function.

faustxml::pcontrols x

Works like the controls function above, but also replaces the label of each basic control with a fully qualified path consisting of all control labels leading up to the given control. Thus, e.g., the label of a slider "gain" inside a group "voice#0" inside the main "faust" group will be denoted by the label "faust/voice#0/gain".

faustxml::info fname

Extract the description of a Faust DSP from its XML or JSON file. This is the main entry point. Returns a tuple with the name, description and version of the DSP, as well as the number of inputs and outputs, the toplevel group with all the control descriptions, and additional faust2pd-specific options specified in the global meta data. Raises an exception if the XML/JSON file doesn’t exist or contains invalid contents.

Example:

> using faustxml;
> let name,descr,version,in,out,group,opts =
>   faustxml::info "examples/basic/freeverb.dsp.xml";
> name,descr,version,in,out;
"freeverb","freeverb -- a Schroeder reverb","1.0",2,2
> using system;
> do (puts.str) $ faustxml::pcontrols group;
faustxml::hslider ("freeverb/damp",0.5,0.0,1.0,0.025)
faustxml::hslider ("freeverb/roomsize",0.5,0.0,1.0,0.025)
faustxml::hslider ("freeverb/wet",0.3333,0.0,1.0,0.025)

Note: As of faust2pd 2.11, the info function can also process descriptions in JSON format (as obtained with faust -json in recent Faust versions). Moreover, instead of a JSON file you may also specify the URL of a running Faust dsp instance (typically something like http://localhost:5510). This works with stand-alone Faust applications which have httpd support enabled (created with, e.g., faust2jaqt -httpd), as well as dsp instances running in Grame’s FaustLive application. You also need to have the curl program installed to make this work.

The latter currently has some minor limitations. Specifically, the httpd/JSON interface only provides the name of a running dsp; the description, version and other global meta data is not available. In the current implementation, we therefore set the description to the name of the dsp, and the version and auxiliary faust2pd options to empty strings in this case.

Furthermore, the info function can also be invoked with a special URL of the form http://localhost:7777/availableInterfaces to retrieve the list of dsp instances running in a (local or remote) FaustLive instance. (Replace localhost with the hostname or IP address and 7777 with the actual port number as necessary. FaustLive’s default port is usually 7777, but you should check the actual IP address with FaustLive’s Window / View QRcode option.) The result is a list of hash pairs of names and URLs of dsp instances which can be queried for their JSON data.