Synth-Building with Max/MSP #2
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.
In the last article, we created a simple synthesizer using basic Max and MSP objects. The biggest problem with that synth was the last ov output control - and an annoying thump in the output whenever a new key was struck. In this, the second article, we will correct some of these problems - and open some new doors.
- Creating the Simple Synth (part 1)
- Output manipulation using Envelopes (part 2)
- Basic Filtering (part 3)
- Turning It Polyphonic #1 (part 4)
- Turning It Polyphonic #2 (part 5)
- A User-modable Oscillator (part 6)
- Making a plug-in (part 7)
- Plug-in Tweaks (part 8)
cs.synth_02, shown below, is a slightly modified version of the original cs.synth_01. If you'd like to download the patch and the supporting object, you can get it here. This synth has a few new options.
First off, the output section has been changed slightly to include a clip~ object. This allows us to prevent sending the sound card anything greater that the normal -1.0 to 1.0 range. This object will clip the audio to that range, and will keep us away from unwanted distortion and clipping at the outputs. This is a good practice, and will be expanded upon in future articles.
The second, and more important, change is the section labeled "Envelope/VCA Stuff". This section, which only contained simple divider and multiplication object. Now, we are given two controls (for attack and release time), and everything is funneled through an object called ddg.velamp. This object is an encapsulation of some more meaty logic.
Let's pop open the ddg.velamp object and see what is in there...
This object takes three inputs - the note velocity, an attack time and a release time. On the other end, it outputs an envelope signal (you can tell it's a signal by the "fuzzy" patch cable). In between the inputs and outputs, we have to determine trigger and release points, create an envelope and create a velocity-scaled output.
The pink section is a simple evaluation of attack or release events. When a velocity value is input, it is checked to determine if it is greater than zero. In Max world, velocity of 0 is a note-off (or release), while a note-on is greater than 0 (i.e., the attack event). Using a route object, a bang is triggered from the appropriate output.
The green section is the setup for the envelope. First, you will see that the incoming attack and release time values are multiplied by 20 to stretch them out. In the synth patch itself, we are using the default 0-to-127 range for the dial objects. With this multiplication, we will now have ranges from 0 to 2540 ms.
Next, the attack and release values are used to create messages to be sent to the line~ object. The format of a line~ message is "level", where time is optional (and defaults to 0 ms). On the left side of this section, we are setting up a message that will "zero out" the level, then build it up to 1.0 at the selected rate. In line~ terms, this is two separate messages. So, we create a message box that contains two messages, using the comma (,) delimiter.
The funny message that is constructed is a convoluted way of getting the message "0., 1. " to the next message box. This is made somewhat more complicated by the difficulty in creating a message with a comma in it. In order to pass a comma in the message, we need to use the "" slash in front of the comma. Also, make sure that there is a space on both sides of the "," combo - otherwise the slash will be included as part of the prior or next message.
This message "sets" a message box that will be "banged" by the next incoming note-on message. A corollary is available for the note-off, but doesn't have to set up the dual message - and is therefore much simpler. Try to carefully follow this patch to understand this routing.
The output of the line~ object is a ramping signal that runs from the last setting to the new setting. In order to use this output, as well as to still maintain velocity control, we need to scale the line~ output by the incoming velocity. To do this, we capture the note-on velocity (by ignoring note-offs), scale it using the divide (/) object, then multiply the line~ output by this value. In this way, the output of this sub-patcher will be sensitive to envelope settings as well as velocity.
The final step is the use of the rampsmooth~ object. This object is super valuable - it prevents ramped values (like amplitude envelopes) from changing too quickly. any signal (even our simple sine wave) will thump if the attack or release times occur too quickly. This object will help alleviate thumping by smoothing any change over a specified number of samples. There is still the opportunity for thumps - especially if you are driving the outputs very hard. But, in general, a simple rampsmooth~ strapped across a line~'s output will help create a more controllable envelope.
In the main patch, you can see that the velocity, attack time and release times are routed to the ddg.velamp object, and the output is sent to a multiplication (*~) object for output scaling. In this way, we maintain all of the VCA-style control of the original synth, with the new attack and decay times.
Next in the series - basic filtering.
[ddg] Darwin Grosse 7/29/2001
by Darwin Grosse on
Aug 20, 2007 1:54 PM