COMP4350/8350#
Sound and Music Computing
Advanced Synth Design#
Dr Charles Martin
Outline#
- Filters (analogue, digital,
vcf~,bob~, filter math (light) - Subtractive Synthesis (from phasor to Moog Model-D clone))
- Audio and Pattern Effects in Strudel
- FM Synthesis (recall week 3, feedback, algorithms, operator-based design)
- Phase Vocoder + spectral synthesis.
Subtractive Synthesis#
Subtractive Synthesis#
Let’s take a complex sound and remove some content.

Popular Subtractive Synths#
Subtractive synthesis is often used in analogue synth designs, particular with those associated with Bob Moog (famous synth designer).
E.g.,:
- Minimoog Model-D (1970)
- Moog Mother 32 (2015) ~AUD1100
- Korg Volca Keys (2013) ~AUD250
- Arturia Microfreak (2014) ~AUD550
It’s good for analogue designs because you can get a lot of timbral variation out of few (2 or 3) basic oscillators.
Subtractive Synth Layout#

- Sound is produced by 1+ summed oscillators and/or noise generator, processed by filter
- Two envelope generators: output volume and to change the filter cut-off frequency
- Missing: low frequency oscillator for modulation
Minimoog in Pd#

Here’s a basic design for an analogue synthesiser with two sawtooth oscillators.
- “detune” changes the frequency of the second oscillator, try a number close to 1, e.g., 1.05 for a rich phase-y sound.
- the filter env gives the sound a nice changing timbre over a note
- for extra fun, try the
bob~object. Similar tovcf~but modelled on actual Moog filter designs.
N.B.: the synthesis part here is quite simple, but processing note information is tricky and requires lots of supporting objects.
Minimoog in Strudel#
We can create a similar synth in Strudel by using the sawtooth oscillator sound, and applying low pass filter and envelope effects.
note("<[c2 c3]*4 [f2 f3]*4>".mul("[1,1.01]"))
.gain(0.5)
.sound("sawtooth") // also try "supersaw"
.lpf(300).lpq(20)
.lpa(.5).lpr(.5)
.lpenv("4").lpf and .lpq are the cutoff frequency and resonance of the low-pass filter effect.
.lpenv, .lpa and `.lpr are envelope parameters for the filter frequency.
Remember that all the audio effects like .lpf, .lpq, lpenv can and should be sequenced to modulate them in interesting ways.
Effects in Strudel#
The previous example showed how to use some of the audio effects built into Strudel.
In Strudel’s functional design, effects are implemented by adding function calls to your pattern.
Some effects modify the sound of a pattern and others modify the sequence. It’s not always obvious which (e.g., does rev mean reverb or reverse the sequence?)
Note! I’m not going to explain the whole API here, you have to use Strudel’s documentation and the “reference” tab in the REPL to understand what is available.
Audio Effects#
We introduced Strudel’s filters already, but let’s look at the other typical effects. E.g., gain and pan:
s("hh").gain("0.4|0.5|0.6").pan("0|.5|1").fast(16)delay, delaytime and delayfeedback (N.B. shortcut in mininotation to all params)
note("c a f e").delay(0.65).delaytime(0.27).delayfeedback(0.8)
note("c a f e").delay("0.65:0.27:0.8")Reverb room (reverb level) and rsize (“room” size), also with mininotation shortcuts.
s("bd sd [bd bd] [sd bd], [hh*8]").room(.8).rsize(4)
s("bd sd [bd bd] [sd bd], [hh*8]").room(".8:4")Pattern Effects#
These effects modify your pattern of notes giving you lots of options for variation and algorithmic composition, e.g., rev plays a pattern backwards:
n("0 1 2 3 4 5 6 7").scale("C:minor").rev()
n("0 1 2 3 4 5 6 7").scale("C:minor").jux(rev) // left: forward, right: backwardThere are arithmetic effects add, sub, mul, div: (Note .add is effecting the mininotation!)
n("0 1 2 3".add("<0 2 5 3>")).scale("C:minor")Changing speed with fast or slow
n("0 1 2 3 4 5 6 7").scale("C:minor").slow("1,1.5,4.5")Signal Patterns#
Signal patterns are waveforms that you can use to modulate or generate patterns.
Use as a low frequency oscillator, e.g., tri controlling a lowpass filter cutoff frequency:
note("c3*16").s("sawtooth")
.lpf(tri.range(100,8000).slow(2))Use for note patterns, n creates a pattern of indices interpreted by scale, e.g.:
n(saw.range(0,15).segment(16)).scale("C:minor")
n(tri.range(0,15).euclid(7,16)).scale("C:scriabin")In the above, segment and euclid turn the signal into a discrete sequence.
Random Variations#
There’s (at least) two kinds of useful randomness paradigms in Strudel: random signals, and random modifiers. The signals work similarly to the regular signals:
n(irand(16).euclid(7,16)).scale("C:scriabin")
n(perlin.range(-7,24).segment(16)).scale("C:scriabin").s("kawai")Random modifiers like sometimes, almostNever, and often take an action sometimes.
note("c2".sometimesBy(.2, x=>x.add("12"))).fast(16)Note the arrow function in there. Nice.
Don’t forget: you can still have randomness in mininotation with | and ?
FM Synthesis#
Simple two-oscillator FM#

We introduced FM synthesis earlier in the course as a way to make interesting sounds with just two oscillators.
This fmsynth.pd patch has been used a lot!
$1is harmonicity (modulation frequency divided by carrier frequency)$2is the index (modulation depth divided by modulation frequency)
This allows us to create a consistent timbre for any frequency input. Can we do more with more oscillators?
Revision: Phase Modulation#

Let’s just revise how “frequency modulation” works.
- FM can be implemented by modifying the phase of an oscillator.
- In this patch, the phase is modified in between the
phasor~andcos~objects.
FM Operators#

We can take the concept of a phase-modulation oscillator and abstract to a reuseable unit: an FM operator.
- An op can serve as a carrier, or as a modulator.
- An op can self-modulate (crazy sounds).
Combining multiple oscillators allows lots of sounds to work together. Typical FM synths will have 4 or 6 operators.
In FM lingo, the wiring diagram between operators is called an algorithm.
Implementing 6-op FM#

Each operator needs:
- an envelope generator
- amplitude and envelope parameters
- some pitch-ratio control
- pitch ratio parameters
This gets complicated quickly…
Volca FM has 23 parameters per operator, and 16 global parameters, that’s 154 params for one patch!
Operator Layout#

And here’s how you could wire them together…
- note that every op gets the pitch and velocity signals
- using
randrouteto send freq scaling and envelope parameters throw~andcatch~for the feedback loop on operator 6.
This is a fixed configuration, could you design a way to control the FM algorithm with parameters?
Commercial FM Synths#

Op-based FM is very popular. The Yamaha DX7 was the first successful digital synthesiser in 1983 and Yamaha’s FM sound chips were found in computers and video game consoles throughout the late 80s and 90s.
- Yamaha DX7 (1983)
- Yamaha reface DX (2015) ~AUD600
- Korg Volca FM (2016) ~AUD250
- Elektron Digitone (2017) ~AUD1350
- dexed FM Plugin (modeled on DX7) AUD0
See dexed for free FM synth fun.
Can we do this in Strudel?#
Not terribly easily (6-op synths are complicated) and Strudel itself isn’t designed for creating new synths on the fly.
For any “new” synth, i.e., where you want to build the signal graph yourself like in Pd, the two options would be:
- Extend Strudel with a new sound by hacking web audio API
- Using Strudel’s CSound integration and write the synth in CSound instead
Option 2 would be likely to be easier but involves learning another programming language.
CSound in Strudel#
CSound is among the oldest computer music languages (first released in 1985) and it’s still used today. Although you would typically run CSound as a regular application, there’s now an implementation on the web.
Here’s a simple CSound synth in Strudel:
await loadCsound`
instr charlessynth
iduration = p3
ifreq = p4
igain = p5
kenv = linen(igain, 0.1, iduration, 0.2)
asig = vco2(kenv, ifreq, 4, .5)
out(asig,asig)
endin`
"0 3 1 2 5@4"
.scale('D minor')
.note()
.csound('charlessynth')CSound Instrument#
There’s some weird stuff about CSound. It’s original syntax looks like assembly and types of variables are determined by the first letter of their names.
instr charlessynth
iduration = p3 ; CSound has special parameter variables, p3 is duration
ifreq = p4 ; p4 is frequency
igain = p5 ; p5 is volume
kenv = linen(igain, 0.1, iduration, 0.2) ; linen is a basic envelope
asig = poscil(kenv, ifreq) ; poscil is sine tone
out(asig,asig) ; output the signal
endinThere’s two good resources for learning CSound, the reference manual and the flossmanual
Why introduce CSound? To make complex synths in Strudel.#
It might seem a bit unhinged to introduce a new complete computer music system at this stage of the course.
The point is: I want you to have the tools to explore computer music deeply.
CSound in Strudel gives you a very good balance of convenience to depth for defining complex instruments in a live coding environment.
There’s a learning curve to understanding the syntax and getting a feel for the system, but it’s not that hard now that you know the concepts.
tl;dr: to do feedback phase-modulation synthesis in Strudel, you should use CSound, and see this paper
Phase Modulation Feedback Synth in CSound#
await loadCSound`
giSine ftgen 0, 0, 8192, 10, 1 ; makes a sine tone table
instr feedback_PM
kCarFreq = p4
kFeedbackAmountEnv linseg 0, 2, 0.2, 0.1, 0.3, 0.8, 0.2, 1.5, 0
aAmpEnv expseg .001, 0.001, 1, 0.3, 0.5, 8.5, .001
aPhase phasor kCarFreq
aCarrier init 0 ; init for feedback
aCarrier tablei aPhase+(aCarrier*kFeedbackAmountEnv), giSine, 1, 0, 1
outs aCarrier*aAmpEnv, aCarrier*aAmpEnv
endin`
n(sine.range(0,15).segment(8).slow(16)).scale("C:minor")
.csound('feedback_PM')This is just one operator with feedback controlled by an envelope (example adapted from the CSound flossmanual)
String Synthesis#
Karplus-Strong string synthesis is a famous algorithm for creating a string-like sound with a noise source, a delay and a filter.
It can be considered a special case of digital waveguide synthesis, used for string, tube, and membrane sounds.
K-S synthesis is quite common in digital synthesisers (e.g., Arturia Microfreak) and it’s fun to do in Pd.
Pd String Synthesis#

Here’s a simple Karplus-Strong implementation.
- Noise source is some lightly filtered
noise~ vline~controls noise entering the delay loop- the
delread~anddelwrite~define the delay loop - the filter is a
lop~, and the loop has feedback of0.999 - changing the length of the delay loop changes the pitch, so this is adjusted by the frequency input
String Synthesis in CSound#
await loadCSound`
instr kstest
ifreq = p4
iamp = p5
ipluck = 0.6 ; pluck position (0-1)
idamp = 0.999 ; damping factor (0-1)
idelaytime = 1 / ifreq
anoise rand iamp
aexcite = anoise * ipluck
kenv linseg 1, 0.01, 0, 0.01, 0
ainitial = aexcite * kenv
afeedback init 0
adelay delay ainitial + afeedback, idelaytime
afilt = (adelay + delay1(adelay)) * 0.5 * idamp
afeedback = afilt
out afilt,afilt
endin`
n(sine.range(0,15).segment(8)).scale("C:minor")
.csound('kstest')Note the simple LPF: (adelay + delay1(adelay)) * 0.5
Physical Modelling Synthesis#
Physical modelling synthesis is an interesting area with lots of possibilities and challenges.
Have a look at Julius O Smith’s Stanford Courses (Music 420A) to learn more.
Note that you can do a lot of awesome physical modelling synthesis in CSound. Take a look at the book.
Fourier Resynthesis#

One form of synthesis that is usually limited to computers involves modifying the frequency domain of a sound and recreating new versions.
Spectral manipulation is something that Pd is quite good at doing!
We return a bit to the Fourier transform mentioned early in the course.
For the full story, see Fourier Analysis and Resynthesis in Miller Puckette’s book.
Back to the frequency domain#
Remember the Fourier transform? This allowed us to extract the amplitude each frequency component of a sound.
In practice, the FT can’t be used as it requires an infinite input (who has time for that).
We can use a similar construction called: Short-Time Discrete Fourier Transform (STDFT):
-
short-time: operates on a finite-length signal instead of an infinite signal
-
discrete: operates on sampled information
We often refer to STDFT as FFT, or “fast Fourier transform” (e.g., the fft~ object in Pd).
FFT is actually a clever algorithm for accomplishing a DSTFT quickly, so it’s ok to use the acronyms interchangeably.
Short-Time Discrete Fourier Transform#
For a signal with N samples, the STDFT equations providing the sine and cosine amplitudes at certain frequencies are:
-
Rk=i=0∑N−1xicos(2πki N)
-
Xk=−i=0∑N−1xisin(2πki N)
The STDFT also only focuses on N frequencies that we call “bins” between 0 and the sampling frequency.
- If the sampling rate is S then the frequency of the kth bin is: k∗NS
N.B.: the frequency “resolution” is limited by the length of the signal we are analysing!
FFT on a long signal#

Typically we want to apply FFT to a “chunk” of a signal rather than the whole thing.
Usually we can set the length of the chunk (window length) and an envelope function.
The usual choice for envelope function is the Hann function, it looks kind of like a Gaussian distribution.
The window length is flexible, but will determine the number of frequency bins that can be analysed! Usually the window length is required to be a power of 2 (e.g., 512 or 2048)
Inverse FFT#
The FFT procedure actually also works backwards!
- From a finite sequence of frequencies we can (re-)create a sampled signal!
The exclamation mark is doing a lot of work here! Imagine what we can do with full control over a spectrum!!
In fact, the IFFT algorithm is almost identical to the FFT algorithm
It is important when doing an FFT to cope with the sine and cosine elements (or the real and complex outputs of a frequency. These interact in a certain way to make the ouput sound “work”.)
FFT in Pd#
Pure Data can perform a STDFT with the fft~ object (yes it’s that easy).
The two outputs give you the real and imaginary part of the signal. You can also do rfft~ just to get the real output (saves CPU).
Similarly, you can do an inverse FFT with rifft~ and ifft~.
In Pd, N is the same as the “block size” (number of samples processed at once), so Pd patches often adjust block size just for the FFT patch to get the right number of FFT bins.
Basic Resynthesis#

Here’s a fun way to modify the spectrum of “noise”.

Timbre Stamp#

The “timbre stamp” algorithm modulates a signal by the spectral envelope of another sound.
see I06.timbre.stamp.pd
Phase Vocoder#

The “phase vocoder” is an algorithm for stretching or compressing the time and frequency axes of a recorded sound.
see I07.phase.vocoder.pd
Phase Vocoder in CSound#
You can do similar procedures in CSound as outlined in the manual