Synth-Building with Max/MSP #6
This series of tutorials first appeared on my CreativeSynth.com website between 2001 and 2002. Due to their popularity (especially amongst new Max users), I have moved them to the Cycling74.com website. Read the introduction.
Several of the responses I've gotten back from the last tutorials requested some techniques to provide some additional waveforms. Over the next few tutorials, we will expand our little polysynth to include some waveform - but do it in a way that is a little more fun than just switching between static waves.
Tutorial Series
We are going to be changing the oscillator section of our existing synth to have a varying waveform that can slide from ramp, to triangle, to sawtooth waveform:
And we will wire this to the Mod Wheel of our controller to make it easily accessible. In this way, rather than a simple waveform selection (which anyone can do...), we will have a great dynamic waveshaper as an oscillator.
To begin our experimentation, we will make some changes to the "voice" patch. Here is the new contents:
First, we added a new in object for a fifth parameter (which will be our waveshape). Then, we replace the original mtof/phasor~ combo with a subpatcher. Subpatchers are the equivalent of subroutines for computer programmers - they provide a level of compartmentalization that makes patches easier to manage and read.
To look at the contents of a subpatcher, you double-click on it while the patcher is locked. This will bring up a new window containing a specially-formatted patch:
The special formatting required has to do with the creation of inlets and outlets. In our new subpatcher (which we've called "osc_stuff"), we've created two inlets and one outlet with the inlet and outlet objects (found on the object palette).
This oscillator is obviously different from our original, but believe me - it will be worth the effort. The main object is the triangle~ object, which can provide ramp, triangle or sawtooth waves (or any in-between). However, triangle~ is different from phasor~ - it doesn't cycle under its own power! In order to send audio out of the triangle~ object, you need to drive it with a signal that moves from 0.0 to 1.0 at the frequency of your desired pitch.
Which is, in fact, what phasor~ already does for us.
So, we drive phasor~ with mtof to get us our "driver" signal, and pipe it into the triangle~ object. We've initialized the triangle~ object with the 1. value, which makes the output conform to a sawtooth wave. The output of the triangle~ object is the output audio of the oscillator.
So, what's that stuff on the right-hand side? It's a way to alter that argument (1.) that we used to set up the triangle~ object. Since settings from 0.0 to 1.0 will change triangle~'s waveform, we need to get some input that ranges in that area, and feed it to this object.
A couple of interim objects are used to accomplish this. First, we use the clip object to prevent inappropriate values. Next, we append a 50 to that value in preparation for the line object. The line object will take our incoming list (say, for example, 0.5 50), and ramp from whatever the current value is to the new value (0.5 in this example) over 50 ms. This prevents clicky changes in the waveshape, and makes the whole oscillator more stable sounding.
One point about line - that first argument is critically important. The argument provides the initial value, but is also the key for line to know whether to output int or float values. Since we have to feed floats to triangle~, we need to make sure that the first argument is a float.
Closing the subpatcher, then saving and closing the voice patch almost completes our work. However, we have a final bit of business - making the user-interface elements to support this new functionality. Here's our new "main" patch:
You'll now see that our poly~ object has five (not four) inlets - since we added that in 5 object in the voice patch. We've also added a new knob (called waveshape) to the interface - with the default 0-127 range. In order to coerce the value to a 0.0-1.0 range, we divide the output by 127. (Note the period at the end of the number - that's a hint to the / object that we want the output to be a float).
Finally, we want to optionally drive this knob with the Mod Wheel. We take the third output of midiparse (which will give us lists of controller/value combinations) and send it to the route object. Using route, we limit the output to lists beginning with the number 1 (the controller ID for the Mod Wheel), and send that to the waveshape knob. Now we have a fully operational UI for our updated voice module - one that gives a tasty wave alteration (similar, but different, to pulse width modulation).
Next time we will add some more waveforms to the oscillator. In the meantime, play around with this patch, add features of your own and have a ball!
Download this patch set at this link: cs.05.zip
[ddg]
Darwin Grosse
5/5/2002
by Darwin Grosse on August 20, 2007