Unit Generators are the atoms of synthesis in Phausto. There are over five hundred of them and they all behave the same way — this page is about what they have in common.
1. What a Unit Generator is
A Unit Generator — UGen — is an object that describes a signal. Oscillators, noise sources,
envelopes, filters, effects, physical models and even arithmetic operators are all UGens.
They are all subclasses of UnitGenerator, and there are 506 of them in the
current package.
A UGen is a description, not a sound. Sending it messages builds a bigger
description; sending it asDsp hands that description to the FAUST engine, which
compiles it to native code. Nothing is heard until then. This is why you can freely build,
inspect and recombine UGens without any audio running.
"White noise is the simplest signal source there is"
noise := Noise new.
dsp := noise stereo asDsp.
dsp init.
dsp start.
dsp displayUI. "one knob: uLevel"
dsp stop.
1.1 Where the idea comes from
The Unit Generator was introduced by Max Mathews at Bell Labs in the 1950s, in the MUSIC family of languages. Nearly every audio environment since — Csound, SuperCollider, Max, Pd, ChucK, FAUST — has kept the concept and mostly kept the name. If you have used any of them, Phausto's UGens will feel familiar; what is different is that here they are ordinary Smalltalk objects, inspectable and debuggable in a live image.
2. What every UGen has
2.1 uLevel, the universal control
Every UGen carries a uLevel — a linear gain applied to its output. It is the
one parameter you can count on being present, whatever the UGen is:
sine := SineOsc new uLevel: 0.3.
uLevel is a straight multiplication. Human loudness perception is closer to
logarithmic, so a change from 0.1 to 0.2 sounds far larger than one from 0.8 to 0.9. When
you want decibels, convert with PhDb2Linear or the db2linear
message — see Math & Conversion Tools.
Because uLevel: accepts a number, a UGen or a UI primitive, it doubles as an
amplitude modulation input:
"Tremolo: amplitude driven by a slow oscillator"
sine := SineOsc new uLevel: (LFOTri new freq: 5).
2.2 Labels and parameter names
Each UGen has a label, which defaults to its class name. The label is what
prefixes every parameter name the UGen contributes to the finished DSP — which is why a
PulseOsc gives you PulseOscFreq and PulseOscDuty.
Change the label and you change the parameter names:
"Two oscillators that would otherwise collide"
a := SineOsc new label: 'Left'.
b := SineOsc new label: 'Right'.
"now addressable as LeftFreq and RightFreq"
playNote:prefix:dur: find the frequency and gate
of an arbitrary instrument — see MIDI & Playing Notes.
2.3 Inputs and outputs
Every UGen knows its channel count, through numberOfInputs and
numberOfOutputs. This is what makes connection errors catchable: a UGen with
two outputs cannot be fed into one expecting one input without an explicit conversion.
stereo and mono are the two conversions you will use constantly.
| Message | Meaning |
|---|---|
| uLevel: | Linear output gain — accepts a number, UGen or UI primitive |
| label: | Rename the UGen, and with it all its parameter names |
| stereo | Duplicate the signal to two channels |
| mono | Collapse to a single channel |
| numberOfInputs | How many channels this UGen consumes |
| numberOfOutputs | How many channels it produces |
| asBox | The underlying FAUST box — see Raw FAUST & the Box API |
| asDsp | Compile into a playable DSP |
3. The families of UGen
UGens are organised into families by package tag. The counts below are the current contents of the package — use them to gauge where the depth is.
| Family | Size | Examples | Guide |
|---|---|---|---|
| Filters | 60 | MoogLadder, PhSvfLp, Lowpass | Filters |
| Effects | 47 | GreyHole, AutoWah, PhEcho | Effects |
| Math | 46 | PhLog2, PhSignum, PhSampleRate | Math |
| Oscillators | 42 | SineOsc, SawOsc, CZpulse | Synthesis |
| Physical models | 87 | Djembe, ElecGuitar, ClarinetModel | Physical Modelling |
| Compressors | 30 | PhCompressorStereo, PhLimiter1176Mono | Dynamics |
| Toolkit | 23 | PhSequencer, PhSelectN, PhLooper | Sequencing |
| Counters & time | 18 | Pulse, PhTempo, PhBeat | Sequencing |
| Conversion tools | 17 | PhMidiKey2Hz, PhDb2Linear | Conversions |
| TurboPhausto | 17 | TpSampler, Acid, TpKick99 | TurboPhausto |
| Basics | 16 | PhLatch, PhBitCrusher, PhLine | UGen Library |
| Antialiased | 14 | AASine, ArcTan2, Cosine1 | UGen Library |
| Envelopes | 13 | ADSREnv, AREnv, AHDSREnvBias | Envelopes |
| Synths | 12 | Kick, Clap, Fm4Op | Synthesis |
| Signals | 11 | PhSmooth, PhBus, PhRev | UGen Library |
| Operations | 10 | PhAdder, PhMultiplicator | Connecting |
| UI primitives | 9 | PhHSlider, PhButton, PhNumEntry | UI Primitives |
| Dx7 | 5 | Dx7Op, Dx7Algo, Dx7Env | FM Synthesis |
| Noises | 4 | Noise, PinkNoise, SparseNoise | UGen Library |
The full catalogue, class by class, is in the UGen Library reference.
4. Where the setters come from
Browse SineOsc in the System Browser and you will find exactly one method:
initialize. Yet SineOsc new freq: 200; uLevel: 0.5 works. This
surprises people, and it is worth understanding early.
The fluid setter API is supplied by traits. freq: comes from
PhFrequencySetter, period: from PhPeriodSetter,
trigger: and gate: from PhTriggerSetter, and a broad
catch-all of thirty-five parameter setters from PhParamsSetter. Twenty-two such
traits are composed into UGens across the hierarchy.
asDsp, init and traceAllParams, and read the answer
off the Transcript. The complete list of traits and their selectors is in
Setter Traits.
Every setter accepts the same three kinds of argument, which is what makes modulation so easy to write:
"a number — fixed at compile time, not changeable at runtime"
osc := SineOsc new freq: 440.
"a UGen — modulation"
osc := SineOsc new freq: (LFOTri new freq: 3) * 20 + 440.
"a symbol — creates a named, runtime-controllable parameter"
osc := SineOsc new freq: #myFreq.
5. UGens, numbers and boxes
Underneath, every UGen becomes a FAUST box. Phausto extends Number,
String, Symbol and Array so that ordinary Pharo values
can enter a signal graph wherever a UGen is expected:
| Expression | Result |
|---|---|
| 200 asBox | A constant signal, usable in arithmetic with UGens |
| 60 midiNNToFreq | MIDI note number converted to hertz |
| #gate asPhButton | A named button UI primitive |
| #cutoff asPhHSlider | A named horizontal slider |
| anArray asSumOfUGen | All elements summed into one signal |
| anArray asChainOfUGen | All elements chained in series |
Which is what lets a pitch envelope be written as plain arithmetic:
"An ADSR scaled to 200 Hz of sweep, offset from a 100 Hz base"
pitchEnv := 200 asBox * ADSREnv new.
osc := TriOsc new freq: (100 asBox + pitchEnv).
The operators themselves are covered in Connecting Unit Generators; the box layer beneath them in Raw FAUST & the Box API.
7. Troubleshooting
The System Browser shows almost no methods on the UGen I want to use
That is expected — the setters arrive by trait. See §4, and Setter Traits.
Two copies of the same UGen fight over the same knob
They share a label, so they share parameter names. Give at least one of them its own
label: — §2.2.
doesNotUnderstand on a setter I am sure exists
The trait providing it is not composed into that particular class. Check the selector against
Setter Traits; several near-synonyms exist —
freq:, frequency:, fr:, cutoff: — and
which one applies depends on the family.
Setting uLevel: to 2 distorts instead of getting louder
uLevel is a raw multiplier with no limiting. Values above 1 will clip. Add a
limiter from Dynamics, or scale down elsewhere in the chain.