Every Phausto patch, from a bare sine wave to a forty-voice physical model, follows the same five-step lifecycle. Learn it once here and every later page is a variation on it.
1. The shortest program
A SineOsc with no arguments oscillates at 440 Hz — concert A. Open a Playground
and evaluate these lines one at a time, with Cmd-D (macOS) or
Ctrl-D, so you can hear each step take effect:
"Create a sine oscillator — 440 Hz by default"
sine := SineOsc new.
"Wrap it in a DSP: the object that can actually make sound"
dsp := sine asDsp.
"Compile the FAUST code to native audio and allocate buffers"
dsp init.
"Start the audio thread — the tone begins here"
dsp start.
"Silence"
dsp stop.
Note what is not here: no sample rate, no buffer size, no output device, no build step, no file. A UGen becomes a DSP, and a DSP makes sound.
SineOsc new makes no noise on its own. It is a description of a signal. Only
asDsp hands that description to the FAUST engine, and only start
connects the result to your speakers.
2. The DSP lifecycle
The same five messages govern every patch in this manual. Learning where each one belongs is most of what "knowing Phausto" means at the start.
asDsp.dsp init compiles the patch to machine code and allocates buffers.dsp start hands the DSP to the audio thread. Sound begins.dsp stop halts output. The DSP stays alive and can be started again.dsp destroy frees the native resources for good.2.1 The five messages
| Message | Effect |
|---|---|
| asDsp | Wrap the UGen chain in a DSP object |
| asDspWithName: | Same, with a name you choose — useful when several DSPs run at once |
| init | Compile and allocate. Required before start |
| start | Begin audio output on the audio thread |
| stop | Halt output; the DSP remains initialised |
| destroy | Release native memory and unregister the DSP |
| play | Convenience: init and start together |
| playFor: | Start, wait for a duration, then stop |
stop and start can be alternated freely — that is the basis of
live performance. init only needs to happen once.
2.2 Why destroy matters
A DSP holds memory allocated outside the Pharo heap, in the FAUST engine. Stopping a DSP does not release it. If you are working iteratively — evaluating a patch, changing it, evaluating again — destroy the old DSP before building the next one, or you will accumulate live DSPs that continue to consume CPU:
dsp stop.
dsp destroy.
stop then destroy. Several lessons in
MasterLu finish exactly this way, and for this reason.
3. Mono and stereo
asDsp on its own produces a mono DSP. Send stereo to the UGen
first to duplicate the signal across two channels:
"A sawtooth, this time in stereo"
saw := SawOsc new.
"note 'stereo' before 'asDsp'"
dsp := saw stereo asDsp.
dsp init.
dsp start.
dsp stop.
The counterpart mono collapses a stereo signal back down to one channel. Both
are ordinary messages on the UGen, so they compose with everything else — you will usually
see stereo as the last thing before asDsp.
GreyHole, FreeverbMono's stereo
siblings, ZitaRevStereo — expect two channels of input. Building in stereo from
the start saves rewiring later. See Effects, Reverbs & Dynamics.
4. Opening the interface
FAUST derives a user interface from the sliders, buttons and numeric entries declared inside each module, and Phausto can render it as a Pharo window. One message:
dsp displayUI.
This is the fastest way to understand an unfamiliar UGen. Rather than reading its class comment, start it and look at what knobs appear. For UGens that need to be triggered — envelopes, drums, plucked strings — the UI is also the only way to make a sound before you have learned programmatic triggering.
You can also open a single control in its own window, which is convenient when you want one slider to hand during a performance:
slider := dsp sliderFor: 'SineOscFreq'.
slider openInWindow.
button := dsp buttonFor: 'DjembeTrigger'.
button openInWindow.
5. Finding and changing parameters
Every controllable value in a running DSP has a string name. Those names are generated from
the UGen's label and the parameter's own label, so a PulseOsc contributes
PulseOscFreq, PulseOscDuty and so on.
5.1 Discovering names with traceAllParams
You rarely need to guess. After init, ask the DSP to print everything it
exposes to the Transcript:
dsp traceAllParams.
SineOsc defines only
initialize. Browsing a single class will therefore not show you its API, but
traceAllParams always will.
5.2 Setting a value while it plays
PulseOsc is a square wave with a variable duty cycle — a good UGen to hear
parameter changes on, because both of its controls have an obvious audible effect:
pulse := PulseOsc new.
dsp := pulse asDsp.
dsp init.
dsp start.
"Narrow the pulse — the timbre thins and gains harmonics"
dsp setValue: 0.2 parameter: 'PulseOscDuty'.
"Drop the pitch two octaves and a bit"
dsp setValue: 120 parameter: 'PulseOscFreq'.
"List everything this DSP exposes"
dsp traceAllParams.
dsp stop.
aNumber is the new value, in the parameter's own units — hertz, seconds,
decibels or a normalised 0–1 range depending on the control. aString must
match the parameter name exactly.
SineOsc new freq: 200 — becomes a
constant in the compiled DSP and cannot be changed afterwards. To change something at
runtime, leave it at its default or give it a
UI primitive. This trips up almost everyone once.
6. Something with a body
Oscillators are the "hello world" of synthesis, but Phausto's physical models are where the
FAUST standard library earns its reputation. Djembe models the West African
goblet drum:
perc := Djembe new.
dsp := perc asDsp.
dsp init.
dsp start.
"Open the UI and press DjembeTrigger to strike the drum"
dsp displayUI.
dsp stop.
Then substitute Marimba for Djembe and run it again. The code is
identical; only the model changes. That interchangeability — every UGen accepting the same
lifecycle — is the point of the design. See
Physical Modelling for the full family.
8. Troubleshooting
I evaluated the whole snippet at once and nothing happened
Evaluate line by line. Evaluating the block in one go runs start and
stop within microseconds of each other, so the tone exists for no audible time.
dsp start complains that the DSP is not initialised
init has to happen first, and only once per DSP. If you rebuilt the UGen chain
you also built a new DSP, and the new one needs its own init — see §2.1.
Sound keeps playing after I stopped it
You are probably hearing an earlier DSP that was never stopped. Each
asDsp creates a new object; the variable dsp pointing somewhere
else does not silence the old one. Keep the habit in §2.2.
setValue:parameter: does nothing
Either the name is wrong — run traceAllParams and copy it exactly, including
capitalisation — or the value was fixed at construction time and is now a compiled constant.
See §5.2.
A physical model or envelope makes no sound at all
It is waiting to be triggered. Open dsp displayUI and press the trigger button,
or trigger it from code with dsp trig: 'DjembeTrigger'. See
Parameters & Control.
The image gets slower every time I evaluate my patch
Undestroyed DSPs are stacking up. Send destroy before rebuilding — §2.2.