Turning a DSP into an instrument you can play: note numbers instead of frequencies, MIDI-enabled DSPs, on-screen keyboards, and mapping hardware controllers to parameters.
1. Notes, not frequencies
A DSP thinks in hertz; music is usually written in notes. MIDI note numbers bridge the two: an integer from 0 to 127, where 60 is middle C and each step is a semitone. Phausto converts between the two representations in several places so you rarely have to.
1.1 Converting by hand
| Expression | Converts |
|---|---|
| 60 midiNNToFreq | MIDI note number to hertz, in Pharo |
| aSignal midikey2hz | MIDI note number to hertz, inside the signal graph |
| PhMidiKey2Hz | The same conversion as a UGen |
| PhHz2MidiKey | Hertz back to a MIDI note number |
| PhPianoKey2Hz | Piano key number to hertz |
| PhHz2PianoKey | Hertz to piano key number |
| PhSemi2Ratio | Semitones to a frequency ratio — for transposition |
| PhCent2Ratio | Cents to a frequency ratio — for fine tuning |
The distinction matters: midiNNToFreq is a Pharo message that computes a
number before the DSP is built, while midikey2hz and
PhMidiKey2Hz operate on a signal at audio rate. Use the first for a fixed
note, the second when the note number is itself modulated.
"A fixed note, converted once at build time"
osc := SineOsc new freq: 69 midiNNToFreq. "A4, 440 Hz"
"A note number that changes — converted in the graph"
osc := SineOsc new freq: (noteSignal midikey2hz).
2. Playing a note
This one message does everything a note-on/note-off pair does: it converts the note number to a frequency, writes it to the instrument's frequency parameter, opens the gate, waits, and closes the gate again.
dsp playNote: 60 prefix: 'ElecGuitar' dur: 0.25.
2.1 Preparing an instrument
The prefix: is how the method finds the two parameters it needs. The
instrument must expose a frequency and a gate whose names begin with that prefix, which
you arrange by passing symbols to the setters:
"Symbols name the parameters rather than fixing their values"
synth := ElecGuitar new
freq: #ElecGuitarFreq;
trigger: #ElecGuitarTrigger.
dsp := synth asDsp.
dsp init.
dsp start.
playNote:prefix:dur: will play it — including a
TpSampler, which is how sample-based melodic playback works. See
Sequencing §4.3.
2.2 Patterns
With that in place, a melody is an ordinary Pharo loop. Fork it so the image stays responsive:
"A random line, 128 notes, one every 125 ms"
pattern := [
dsp playNote: (Random new nextIntegerBetween: 28 and: 76)
prefix: 'ElecGuitar'
dur: 0.11.
(Delay forMilliseconds: 125) wait
].
[128 timesRepeat: pattern] fork.
Because it is just Pharo, a scale is a collection, a chord is three sends, and a
probabilistic sequence is an ifTrue:. This is the point at which Phausto stops
being a synthesiser and starts being a composition environment.
"A D minor pentatonic scale, ascending"
scale := #(62 65 67 69 72).
[ scale do: [ :n |
dsp playNote: n prefix: 'ElecGuitar' dur: 0.2.
220 milliSeconds wait ] ] fork.
3. MIDI-enabled DSPs
A DSP can be compiled with MIDI support built in, so that the FAUST runtime interprets MIDI messages itself rather than requiring you to translate them:
| Message | Effect |
|---|---|
| asDspMIDI | Compile the UGen chain as a MIDI-enabled DSP |
| asDspMIDIWithName: | The same, with a name you choose |
| isMIDI | Whether a DSP was built with MIDI support |
| isMIDI: | Set the MIDI flag |
synth := SawOsc new => ADSREnv new => MoogVcf new.
dsp := synth asDspMIDI.
dsp init.
dsp start.
playNote:prefix:dur: uses MIDI note numbers but is not MIDI — it is
Pharo writing to parameters. asDspMIDI builds a DSP that understands actual MIDI
messages. You can use either, or both.
4. On-screen keyboards
For trying an instrument out, Phausto can open a piano keyboard bound to it:
| Message | Effect |
|---|---|
| keyBoardFor: | A keyboard presenter bound to a named instrument |
| requirePianoKeyboard | Ensure the keyboard component is available |
kb := dsp keyBoardFor: 'ElecGuitar'.
kb openInWindow.
The keyboard sends the same note events playNote:prefix:dur: does, so an
instrument prepared as in §2.1 works with it immediately.
5. Mapping controllers
FAUST supports metadata declarations that bind a UI element to a MIDI control, and Phausto
exposes them directly on UIPrimitive. The binding is declared on the widget when
you build the patch, and the runtime does the rest.
| Message | Binds to |
|---|---|
| midiCtrl: aNumber | A MIDI continuous controller number, any channel |
| midiCtrl: aNumber channel: aChannel | A controller on a specific channel |
| midiKeyon: aPitch | Note-on for a given pitch |
| midiPitchwheel | The pitch bend wheel |
| declare: aKey value: aValue | Any FAUST metadata declaration |
| belaPin: aBelaPin | An analogue pin on a Bela board |
"Bind the filter cutoff to the modulation wheel (CC 1)"
cutoff := PhHSlider new
label: 'Cutoff' values: #(800 40 12000 1);
midiCtrl: 1.
voice := SawOsc new => (MoogVcf new cutoff: cutoff smoo).
dsp := voice stereo asDspMIDI.
smoo — see
Envelopes §5.
The same belaPin: declaration is what lets a patch exported to a Bela board read
its analogue inputs — see Exporting.
6. The MIDI instrument classes
Several physical models ship in a MIDI-ready variant, already wired so that note number and gate arrive where they should. When you want to play an instrument rather than design one, start with these:
| Class | Instrument |
|---|---|
| DjembeMIDI | Goblet drum |
| MarimbaMIDI | Tuned bars |
| ElecGuitarMIDI | Electric guitar |
| GuitarMIDI | Acoustic guitar |
| NylonGuitarMIDI | Nylon-string guitar |
| ViolinMIDI | Bowed violin |
| KarplusStrongMIDI | Plucked string |
| ClarinetMIDI | Clarinet |
| FluteMIDI | Flute |
| BrassMIDI | Brass |
See Physical Modelling for what each one models.
7. External MIDI hardware
Phausto generates sound; it does not itself open MIDI ports. For sending and receiving MIDI to and from external hardware inside Pharo, the companion package is Pharo-Sound, which wraps PortMidi over uFFI. Coypu builds on both and adds live-coding sequencing, including MIDI output to hardware synthesisers.
| Package | Role |
|---|---|
| Phausto | Synthesis and DSP; consumes note numbers and controller values |
| Pharo-Sound | MIDI send and receive via PortMidi |
| Coypu | Live-coding sequencer; drives Phausto, MIDI, SuperDirt, Kyma and Pd |
9. Troubleshooting
playNote:prefix:dur: produces no sound
The instrument's frequency and gate are not labelled with the prefix. Set them with symbols
and confirm the resulting names with traceAllParams — §2.1.
Notes sound but always at the same pitch
The frequency was fixed as a number at construction time, so the note number has nowhere to go. Pass a symbol instead — §2.1.
Notes never release, and the sound piles up
The gate parameter is not being found, so the note-off half of the cycle does nothing. Check the trigger label matches the prefix — §2.1.
My MIDI controller does nothing
Check three things in order: the DSP was built with asDspMIDI rather than
asDsp (§3); the widget carries a midiCtrl: declaration (§5); and
the controller number and channel match what the hardware sends.
A mapped control moves in audible steps
MIDI is 7-bit. Add smoo — §5.
I want to send MIDI out to a hardware synth
That is Pharo-Sound or Coypu, not Phausto — §7.