The Center for Concrete and Abstract Machines (CCAM) has invited me to be on their Synthesis Team for the audio software development of three DIY synths that will be showcased in a workshop called FUTURHYTM MACHINES: Chicago House Music. Each synth will have their own name and sonic output:
Each synth circuit uses a custom PCB circuit with potentiometers and buttons for ease of control of parameters. It will also have:
A major goal is for ease of interaction between these synths to create a wonderful sonic experience.
The heart of this synth is the Electrosmith Daisy Seed. The Kickstarter for this board started in 2020 and has already become a popular choice for audio programmers that want to make their programs come to life by taking a more physical and portable form. This is a perfect board for sound artists wanting to make sound installations, and for performers/musicians wanting to make custom synths for their Eurorack setups. Before this board, the Teensy was a popular choice.
There are several ways to write an audio program to flash into the Daisy, for example you can use Arduino or Pure Data, but I will use the Max/MSP gen~ environment, which uses lower-level objects allowing the ability to compile a patch into code that the Daisy can understand. The Max package for flashing the board is called Oopsy.
For more options and info about how to get started check out this wiki page: https://github.com/electro-smith/DaisyWiki/wiki
Graham Wakefield and Gregory Taylor’s book “Generating Sound and Organizing Time” is a great resource for learning gen~. I will be pulling some useful images from there to demonstrate some of the concepts. I will refer it as the GO book from now on.
Learn more about it here: https://cycling74.com/books/go
Description:
Button Control:
Potentiometer Control:
Audio Synthesis:
Inspired by Erica’s Synth Desktop Bassline.
Description:
Potentiometer Control:
The “Hello World” for any sequencer construction is to have a single event triggered repeatedly by a clock.
The way to make a clock in Max gen~ is by using a
[phasor]
object. The number following phasor is the
frequency parameter.
Creating a [phasor 1]
will create a ramp going at a rate
of 1Hz, going from a value of 0.0 to 1.0 in one second.
What we want is for this ramp to trigger an event when it resets its cycle. In the analog synthesis world, a trigger is a very short pulse (1-2ms) that is used to activate something, in Max/MSP gen~ a click is the closest equivalent. A click is simply an output of 1.0 for one sample.
In the GO book, this figure is provided to demonstrate what it should look like, describing the clicks as ticks.
Conveniently, the GO book comes with an abstraction called
go.ramp2trig
that creates a trigger (click) when the ramp
resets.
The get a trigger out of a phasor we have to send a 1 when we detect a big jump/change in value between the current and last sample of the phasor output.
A quick and dirty way of doing this is by using the [delta]
operator
[delta]
Returns the difference between the current and previous input.
The difference in change could be incrementing or decrementing, so we
should use an abs
operator to take both cases into account.
Lastly, we can use [< 0.5]
to see if the change is
greater than 0.5. If condition is true, it will output a value of 1,
which is what we need.
The next step is to have that trigger activate a sound event. As a simple example we can trigger an envelope on a running oscillator.
We pass the [cycle]
through a [*]
operator
because the right inlet will be used for the envelope signal. This
signal will go from 0.0 to 1.0 in a certain amount of time, starting
when it receives a trigger.
A very simple way is by using a combination of [counter]
and [%]
.
In this case, a trigger is used to repeatedly turn on the envelope, AND turn off.
The [counter]
operator does exactly what it is called,
it counts how many triggers it receives until is is told to reset. In
reality, it is adding the value on the left most inlet every sample.
Whenever it is 0, it adds 0, and when it is 1, it adds 1.
[counter]:
Accumulates and outputs a stored count, similarly to Max’s counter object, but triggered at sample-rate. The amount to accumulate per sample is set by the first input (incr). The count can be reset by a non-zero value in the second input (reset). The third inlet (max) sets a maximum value; the counter will wrap if it reaches this value. However if the maximum value is set to 0 (the default), the counter will assume no limit and count indefinitely. The first outlet outputs the current count, the second outlet outputs 1 when the count wraps at the maximum and zero otherwise, and the third outlet outputs the number of wraps (the carry count).
We can start to connect the envelope now to hear the result, but there is a problem. The immediate change from 0.0 to 1.0 creates clicks which is not ideal.
A nice way to ease the transition is by using
[slide]
.
Use the slide operator for envelope following and lowpass filtering. Related to the MSP slide~ object.
The [slide]
operator takes two parameters.
The result in a much cleaner sound. This completes the barebones of a sequencer. By adding more sounds triggered at different times we can create interesting additive results.
For the sake of getting right to the chase of making a drum
sequencer, I will be using the provided GO
examples that
synthesize a snare drum, kick, and hihat. These abstractions take a
trigger to activate the sound.
To add BPM, we can create a [param]
called BPM, and
divide it by 60.
A BPM of 60 will give a frequency of 1Hz, a BPM of 120 will give a frequency of 2Hz, etc.
To create steps from a ramp, we can multiply a ramp by the number of
steps we want, and then using the [floor]
operator.
Here is the plot with the multiplied phasor:
Note that the count of the orange plot starts at 0 and goes up to 7. To quantize them to be between 0 and 1, we can just divide by 8 again.
This module is a topographic drum sequencer, meaning that it derives its drum sequences based on a map.