A patch built in a Playground does not have to stay there. Phausto can emit a CMajor patch, a complete JUCE plugin project, or C++ for a Bela board — from the same object you have been listening to.
1. Why export at all
Pharo is an excellent place to design a sound and a poor place to ship one. Exporting closes that gap: the instrument you prototyped interactively becomes a plugin your collaborators can load, or a binary on a board that boots without a computer attached.
Everything rests on the same fact — a Phausto patch is a FAUST program. The
exporters take the FAUST that generatedCode would give you and wrap it in
whatever scaffolding the target needs.
2. Plain FAUST files
The simplest export is the FAUST source itself, which is also the most portable — anything that accepts FAUST will take it.
| Message | Writes |
|---|---|
| createFileNamed: aString | The DSP as a FAUST file |
| createFaustSynthFileNamed: aName | A synth-shaped FAUST file |
| createFaustPolySynthFileNamed: aName | A polyphonic synth file |
| createFaustSynthHeaderFileNamed: aName | The accompanying header |
| createFaustHeaderFileNamed: aName for: aPluginType | A header for a given plugin type |
| generatedCode | The source as a string, without writing anything |
| writeAsPosixText: aString to: aFileReference | Write text with POSIX line endings |
synth := SawOsc new => ADSREnv new => MoogVcf new.
dsp := synth stereo asDsp.
dsp init.
dsp generatedCode. "inspect it first"
dsp createFaustSynthFileNamed: 'MySynth'.
3. CMajor patches
CMajor is a language and runtime for audio, with its own patch format that loads in a host or standalone player. Phausto emits complete patches, including the interface.
| Message | Produces |
|---|---|
| createCmajorPatchNamed: aName | An instrument patch |
| createCmajorFXPatchNamed: aName | An effect patch — audio in, audio out |
| createCmajorMIDIPatchNamed: aName | A MIDI-driven instrument patch |
dsp createCmajorMIDIPatchNamed: 'MyInstrument'.
3.1 The CMajor classes
The export is modelled by objects rather than string templating, so it can be extended:
| Class | Role |
|---|---|
| CmajorPatch | A patch |
| CmajorFXPatch | An effect patch |
| CmajorMIDIPatch | A MIDI instrument patch |
| CmajorView | The patch's interface |
| CmajorUIElement | Superclass of the view elements |
| CmajorKnob | A knob |
| CmajorFader | A fader |
| CmajorBackground | The view background |
Your UI primitives become CMajor endpoints and view
elements automatically — a PhHSlider in the patch becomes a control in the
exported interface, with the range you declared.
3.2 Polyphonic wrapping
A Phausto patch is monophonic by construction. To make a CMajor instrument polyphonic it has to be wrapped in a voice allocator, and Phausto generates that wrapper:
| Message | Produces |
|---|---|
| asCmajorPolyWrapperFor: aName | A polyphonic voice wrapper |
| aDictionary asCmajorPolyWrapper | The wrapper from an endpoint description |
| aDictionary asCmajorEventEndpoint | An event endpoint |
| aDictionary asCmajorEventsToVoicesConnection | Event-to-voice routing |
| aDictionary asCmajorListOfUIEndpoints | The UI endpoint list |
4. JUCE plugins
The most substantial exporter generates a complete JUCE project — processor, editor, parameter tree and build files — ready to compile into a VST3 or Audio Unit.
| Message | Produces |
|---|---|
| export2JuceWithName: aString type: aPluginType | A JUCE project |
| export2JuceAPVTSWithName: aString type: aPluginType | A project with a full parameter tree |
| export2JuceAPVTSWithName: aString for: aPluginType | The same, alternative form |
dsp export2JuceAPVTSWithName: 'MyPlugin' type: aPluginType.
4.1 Plugin types
The plugin type selects which project template is used. Phausto ships three, corresponding to the three shapes a plugin can take:
| Template | Shape |
|---|---|
| Mono synth | MIDI in, audio out, one voice |
| Poly synth | MIDI in, audio out, multiple voices |
| Effects | Audio in, audio out |
The templates live in faus2JuceTemplates in the Phausto repository, as
Source_monoSynth, Source_polySynth and
Source_effects. Reading them is the quickest way to see what the exporter
fills in.
4.2 APVTS: the parameter tree
JUCE's AudioProcessorValueTreeState is how a plugin exposes automatable
parameters to a host. Phausto builds it from your patch's own controls, which is why
declaring proper ranges on your widgets pays off at export
time.
| Message | Returns |
|---|---|
| apvtsControls | The controls to expose |
| apvtsParameterControls | Just the automatable parameters |
| apvtsControlsFrom: aJsonString | Controls parsed from a UI description |
| apvtsParameterControlsFrom: aJsonString | Parameters from a UI description |
| apvtsDescriptorRowFor: aControl | One row of the parameter descriptor |
DSPParameter and DSPParameterStore described in
DSP & Parameter API §8 exists precisely so this mapping is
straightforward.
4.3 The generated files
| Message | Writes |
|---|---|
| createCppAPVTSFilesNamed:for: | Processor source with APVTS |
| createHeaderAPVTSFilesNamed:for: | Processor header |
| createCppEditorAPVTSFilesNamed:for: | Editor source |
| createHeaderEditorAPVTSFilesNamed:for: | Editor header |
| createPluginProcessorHeader: | Plugin processor header |
| createPhaustoParamsHeaderNamed: | A header of parameter definitions |
| createJucerFileNamed:for: | A Projucer project file |
| createCMakeFileNamed:for: | A CMake build file |
Setter code for the C++ side is generated from your parameter names through the
String and Array extensions —
asCppSetterDefinition, asCppSetterImplementationFor: and their
plural forms — so each Phausto parameter becomes a named C++ setter rather than a numeric
index.
5. Bela
Bela is an embedded platform for low-latency audio. Exporting to it produces a C++ file to build on the board:
| Message | Writes |
|---|---|
| createBelaCppFileNamed: aName | A Bela-ready C++ file |
Bela's analogue inputs are reached through widget metadata: declaring
belaPin: on a UI primitive binds that control
to a physical input. A potentiometer wired to the board then drives the parameter directly,
with no code in between.
"This slider becomes a hardware input on the board"
cutoff := PhHSlider new
label: 'Cutoff' values: #(800 40 12000 1);
belaPin: 0.
6. Writing a patch that travels
Some habits cost nothing while prototyping and save real trouble at export. All of them amount to the same principle: put information in the patch rather than in the Playground around it.
PhHSlider becomes a plugin parameter, a CMajor endpoint or a Bela pin.PhSec2Samp or PhSampleRate; see Math §6.midiCtrl: is honoured by the FAUST runtime, by the plugin and by the board alike.[ ... ] fork and dsp setValue:parameter: makes
sound in Pharo and silence everywhere else. Pharo is the sequencer in that arrangement, and
Pharo does not come with the plugin.
8. Troubleshooting
The exported plugin has no parameters
The patch was built with literal numbers, so there was nothing to expose. Use widgets — §6.
The plugin loads but is silent
If the sequencing lived in a forked Pharo loop, it did not come with you. Move the rhythm into the signal graph, or drive the plugin from the host — §6.
Parameter ranges in the host look wrong
They are whatever you declared on the widget. Check the values: array order —
initial value first. See UI Primitives §2.1.
The exported instrument is monophonic
Phausto patches are single-voice by construction. Use a polyphonic template for JUCE, or the CMajor poly wrapper — §3.2 and §4.1.
The generated project will not build
Export writes source, not binaries — the target's toolchain does the rest. Confirm JUCE, CMake or the Bela toolchain is installed and that the template files are present — §1, §4.1.
The exported patch sounds different from the one in Pharo
Most often a sample-rate assumption. Anything specified in samples will shift; express times in seconds — §6.