Micro-Moog
Assign: Thursday, September 23
Due: Thursday, September 30
Assignment Policies
All assignments are partner assignments. Please make sure to review the policies and guidelines about discussing assignments with those other than your partner. Assignments are usually, but not always, a combination of paper and coded exercises. Please make two submissions (one for paper exercises and one for coded exercises) to Gradescope. Paper exercises are marked with HW to indicate a handwritten question and C to indicate a coded question. Paper submissions can receive an extra 2% bonus if they are typed. Please visit the tools section of the website for more information.
Part 1: SynthDef Practice with Saw Waves C
Starter Code
This part has no starter code. Create a new file called synthdef_saw.scd
and place your name and your partner’s name at the top in a comment.
Description
The goal of this part is to get more practice writing SynthDefs in SuperCollider. Here we will create some definitions using sawtooth waves in different ways.
-
Write a SynthDef called
\saw
that simply plays back a sawtooth wave with the following default arguments:outBus = 0, freq = 440, amp = 0.1
. Your code should use the UGen Saw and should playback the sawtooth wave in stereo. Write an additional line to play the synth and then another to free it. -
Write a SynthDef called
\sawOctaves
that plays back a sawtooth wave and a sawtooth wave one octave above. The SynthDef should use the UGenSaw
and should accept argumentsoutBus = 0, freq = 440, amp = 0.1
. The frequency argument specifies the frequency of the lower note in the octave. The amplitude of each sawtooth wave should be half the providedamp
. Write an additional line to play the synth with a frequency of 300Hz. Then write a line to set the frequency to 200Hz and then a line to free the synth. You should produce a stereo wave. -
Write a SynthDef called
\varSaw
that creates a variable sawtooth wave. A variable sawtooth wave is a waveform that can be modulated from a sawtooth wave to a triangle wave. We will not concern ourselves with the details of such an implementation. Instead we will simply use a UGen called VarSaw that can provide such a waveform. Examine the Help Documents onVarSaw
to understand how exactly to use its inputs. Notice thatVarSaw
has a parameter forwidth
. This controls the shape of the waveform. To add some interest to the sound, we will change the width of the waveform over time using a sine wave. In essence, this will shape the strength of the partials, creating an interesting sweeping effect.Some additional requirements:
- The SynthDef should accept the following default arguments:
outBus = 0, freq = 440, amp = 0.1
. Note here that theamp
andfreq
refer to the amplitude and frequency of the variable sawtooth wave and not the amplitude and frequency of the modulating sine wave. - Again, you will need to create a sine wave and use it to
control the width of the variable sawtooth wave. The sine
wave should have frequency 0.5 and should span the range from
0 to 1. Note that a sine wave is -1 to 1 by default so you
will need to consider how to change the arguments to the
sine wave to accomplish that goal. Note that the
add
argument can shift a waveform vertically. - Create a stereo signal.
- Write a line to create the synth and then free it.
- If you like, try opening up a scope to watch the waveform change in
realtime using
s.scope
.
- The SynthDef should accept the following default arguments:
-
Write a SynthDef called
\sawChorus
that creates a chorus effect. When singers sing the same note, each singer’s pitch is slightly different from the other singer. It’s why the sound of a choir is so rich. The same principle holds for a string section of an orchestra. To create a choir effect using sawtooth waves, we simply create several copies of the sawtooth wave, each with a pitch that shifts around the target frequency. For the purposes of this SynthDef, use six sawtooth waves.Some additional requirements:
- You must use a for loop, do loop or while loop to add the six sawtooth waves together.
- The SynthDef should accept the following default arguments:
outBus = 0, freq = 440, amp = 0.1
- To generate the deviation around the center pitch use the UGen
LFNoise1.kr
which will generate a changing random number -1 and 1. You can use
the range method on
LFNoise1
or any UGen for that matter and it will scale the output to a different range. For example, the codeLFNoise1.kr(0.5).range(1, 3)
will create a changing random number every two seconds between 1 and 3. Here, the idea is to useLFNoise1.kr
to create a changing frequency. If each sawtooth wave has a random value close to the desired frequency, you will achieve the chorus effect. Ensure that the chorus effect works equally well for low sounds and high sounds. Think ratios! I would also haveLFNoise1
change random numbers at a low rate, somewhere between every half and two seconds. - Each sawtooth wave’s amplitude should be divided by the number of sawtooth waves used (i.e., six).
- Create a stereo signal. Here ensure that a different
LFNoise1
controls the left channel and the right channel for each saw wave. That will create a more interesting stereo audio effect. You should be a bit careful here. If you do something likeLFNoise1.kr ! 2
that will duplicate the exact same unit generator. Here we want two different copies ofLFNoise1
. Instead you should use{LFNoise1.kr} ! 2
which evaluates the function twice and places those contents in an array. This will give you two different UGens. - Create a line to play the Synth and a line to free it.
To give you a sense of what it sounds like, here is an audio file where I set the frequency to different values. It’s a pretty good synthesizer sound!
Part 2: Micro-Moog C
The main part of this assignment involves creating your own synthesizer. We will model our synthesizer after the classic synthesizer developed by Robert Moog in the 1960s. The Moog synthesizer has been used in all genres of music including classic rock groups like the Rolling Stones and the Beatles, rap artists like Dr. Dre, and many other groups that incorporate electronic music.
The Moog synthesizer is an example of subtractive synthesis. The basic idea is we take a sound source like a sawtooth or square wave, pass that signal through a filter which attentuates (i.e., subtracts) the harmonics in the sound source, shape the amplitude of the sound using an amplitude envelope, and finally pass it through a reverberation unit to give the sound a sense of space. Your goal for this assignment is to create SynthDefs for each of these four phases of the sound generation and then connect the signal path using busses and groups.
The diagram below summarizes the signal path. The first step in the chain is the oscillator (i.e., a sawtooth, sine, triangle or pulse wave). That signal is routed to a Voltage Controlled Filter (VCF) which attenuates the harmonics. The term VCF comes from electronics where audio signals are represented as changes in voltages. The third step in the chain is a Voltage Controlled Amplifier (VCA) which applies an amplitude envelope to the sound. The final step is a reverberation unit. Here we will borrow a builtin reverb that comes with SuperCollider. Soon though we will learn how to make our own!
When you are done, I have created a GUI which you can launch to play your synthesizer using your computer keyboard.
Starter Code
Download the starter code to get started. Take a look over the top
half. You can ignore the code below that creates the GUI under the header
“Starter Code - DO NOT MODIFY”. You should see the four SynthDefs requiring
your implementation. The functions ~start
and ~free
are needed to
connect the Synths together using busses.
IMPORTANT: Do not change the arguments to any of the SynthDefs and do not modify their initial values. That will be important for the GUI to work properly.
Step 1: Create the SynthDef for the Sound Source
Your first step should be to create the SynthDef \oscillator
. This will be
the sound source. \oscillator
will be able to choose between several different
waveforms like sawtooth or square and output a mono signal. The following is a
description of the arguments to \oscillator
.
outBus
: the bus to where audio is routed.\oscillator
should output a mono signal.select
: select is an argument that can take one of four values: 0, 1, 2 or 3. If the argument is 0,\oscillator
should output a sine wave usingSinOsc.ar
. If the argument is 1,\oscillator
should output a triangle wave usingLFTri.ar
. 2 should be a sawtooth wave usingSaw.ar
. 3 should be a pulse wave usingPulse.ar
. See below for implementation details.freq
: the frequency of the oscillatorduty
: the duty cycle from 0 to 1. Note that the duty cycle only applies to the pulse wave.noiseLevel
: the amount of white noise mixed into the signal on a scale of 0 to 1. If 0, there is no noise. If 1, the signal is all noise. The UGen WhiteNoise.ar to create white noise.lagTime
: many synthesizers have a glide effect that allows notes to shift smoothly from one frequency to the next over a defined amount of time.lagTime
ranges from 0 to 1 where 0 is no glide and 1 is one second of time for the synthesizer to change from one frequency to the next.octave
: the octave of the sound source. By default this number is 4. When the octave is 4, there is no change to the frequency. If the octave is 5, the frequency should be twice thefreq
passed as an argument. If the octave is 3, the frequency should be half thefreq
passed as an argument.
Some implementation details and hints.
\oscillator
should generate all four waves but only select one of them to play. Consider using the UGenSelect
to help.- Many oscillators give the option to incorporate noise into the signal.
This is particularly useful for percussive effects. As noted above,
noiseLevel
represents the percentage of the noise mixed into the signal. You should crossfade between the oscillator and the noise. IfnoiseLevel
is 0, the mix should be purely the oscillator. If thenoiseLevel
is 1, the mix should be purely the noise. If thenoiseLevel
is 0.5, the mix should be an equal balance of noise and oscillator. ConsiderXFade2
where the argumentpan
controls the balance between the signals. - The glide effect is easy to create using the UGen
Lag2.kr
. Simply pass in the frequency as input toin
and the outputted frequency will reflect the change.
You should thoroughly test that all arguments work by creating a synth
and using the .set
method to change arguments. Use the space under
the header “Testing” to try things out. I will ignore everything under this
header when grading your work.
Step 2: Complete
Go ahead and complete the rest of the SynthDefs. Below is a description
for the arguments to each one and some implementation hints. Fortunately,
the remaining SynthDefs involve less explanation than \oscillator
.
The Voltage Controlled Filter: \vcf
outBus
: the bus where the audio is outputted.\vcf
should output a mono signal.inBus
: the bus where\vcf
reads in audio. This audio bus should be a mono signal.freq
: the cutoff frequency of the filter. The cutoff frequency is the point at which the frequencies of the signal begin to be attenuated. Everything above the cutoff frequency will be lessened. Use the UGenMoogFF.ar
in your implementation.
The Voltage Controlled Amplifier: \vca
The voltage controlled amplifier generates an ADSR amplitude envelope to shape the sound. The envelope can be triggered using a gate and shutoff by closing the gate (i.e., setting it to zero).
outBus
: the bus where the audio is outputted.\vca
should output a mono signal.inBus
: the bus where\vca
reads in audio. This audio bus should be a mono signal.aTime
: the attack timedTime
: the decay timesLevel
: the level of the sustain portion relative to the peak amplitude reached by the attack.rTime
: the release timepLevel
: controls the overall amplitude of the signal. By default this value is 1.gate
: the gate to trigger and release the envelope. 1 triggers the start of the envelope and 0 releases the envelope.
Reverberation: \reverb
The \reverb
SynthDef should add space to the sound. Reverb creates
the reflections that give the sound a sense of distance and ambiance.
You should use the UGen FreeVerb.ar
to create the reverb.
outBus
: the bus where the audio is outputted.\reverb
should output a stereo signal. Note that the other SynthDefs have produced a mono sound but here we should produce a stereo sound as this is the final part of our signal path before being sent to our speakers.inBus
: the bus where\reverb
reads in audio. This audio bus should be a mono signal.mix
: the amount of reverb in the sound.room
: the length of the reflections that determines how our ears perceive the size of the space.
Step 3: Connect the SynthDefs
The function ~start
should create audio busses to connect
the whole signal path together. It should also create instances
of each synth. Place all four synths in the same group so
they are easier to free.
Each Synth needs to be assigned to a specific environment variable. You must name
each of your synths to these respective variables in order
for the GUI to work: ~osc
, ~vcf
, ~vca
, and ~verb
.
The function ~free
should free all audio busses and the group
containing all the synths. You should free using the group you
create in ~start
.
Running the GUI
When you are done and believe everything to be working, try out the keyboard GUI by executing the starter code at the bottom. The video below shows how changing each element of the GUI effects the sound. You should make sure your implementation works as I have shown.
Grading and Style
My solution took roughly 70 lines of code to write. However, the solution to this
assignment can be a little tricky, particularly routing
each Synth to each other. It is difficult because SuperCollider will simply
make no sound if things are not working correctly. If you are having trouble,
consider using the .poll
method, the audio version of a print statement, on
any UGen to see what your sound source looks like and if you are getting non-zero
values (i.e., sound).
For style, you should have a few comments here and there, particularly in
the \oscillator
SynthDef explaining your signal flow and what each part is doing.
Do not try and write your SynthDef all on one line. You should make use of
variables to name different parts of your SynthDef and keep each line short, at most
two UGens per line. Readability is important!
You will be grading on the following:
- Successful implementation of each SynthDef
- Successful implementation of creating audio busses and nodes (i.e., Synths and
Groups) to connect each SynthDef together within the
~start
function. - Successful implementation of the
~free
function to free all groups and audio busses. - Commenting and style (see above)
Submission
Feedback
When you are finished with the problem set, please fill out the form linked here to provide feedback on how long the problem set took you and how difficult you found it. Note that you must fill out this form to receive credit for the problem set.
Submission Guidelines
All assignments in this course are submitted through Gradescope. There are two kinds of assignments in this course: paper assignments and code assignments. Both are submitted to Gradescope. Most assignments are a mix of code and paper assignments. Each question or question part will be marked either “code” or “paper” to indicate which kind of question it is. For paper assignments, note that typed answers will receive a small additional bonus of 2%.