Generating Complexity in Max
Guest Contribution by Mark Durham
With this post I want to look at some strategies for generating control data in Max, or more specifically generating complex envelopes for controlling parameters. Now I expect that designing envelopes is nothing new for most Designing Sound readers; it’s been a staple technique for manipulating parameters on a timeline in most DAW’s, samplers etc for some time now. For me, learning to program the first version of Native Instrument’s Absynth was a real revelation with regard to designing complex sounds using envelopes. For anyone who has not tinkered with Absynth, it has a very well designed envelope section and it’s possible to control pretty much any parameter with an envelope. This lends itself very well to complex sequences where different parameters of the synth are being continually adjusted, with each of the parameters interacting to produce sound. Another interesting feature of Absynth is the breakpoint generation tool, which can be used to generate basic repeating envelopes with only a few controls. It’s a large influence on the device featured in this post.
So hand-crafting basic ADSR style shapes is easy enough, but drawing complex envelopes by hand can be a laborious process. This is made increasingly difficult if the breakpoints need to be especially precise (for say rhythmic material), or if some unusual timing is needed.
Here’s an example of the style of envelope I’m talking about:
A potentially useful envelope, but difficult to craft by hand.
Even more difficult is this type of thing:
The rate here decreases exponentially, making it almost impossible to draw accurately by hand. One option for achieving this is using an LFO and adjusting the rate; this is a perfectly good solution, but there’s still nothing better than seeing the actual pattern graphed in front of you before you press play.
Varun Nair’s recent posts on Designing Sound have been focused around the idea of sound as data, and the flexibility which comes with this approach. For me, it’s definitely one of the most exciting things about working with sound in environments like Max; the thought that with a bit of experimentation you could map anything to anything… the possibilities really are endless. In the spirit of that idea I’m not going to be presenting any patches here that actually make sound, but will cover generating control data using the graphical breakpoint editor in Max/msp. It’s called [function].
From here on, anything referred to in square brackets [ ] is a Max object.
If you are new to learning Max, it’s worth mentioning how useful the help system is. For any object you can right click and open a help file focused on that object. These are especially useful as they are actual Max patches, so the manual is partly interactive and actually really inspirational – check out the helpfile for [nodes] for an example of a really intuitive and flexible object.
Here’s the help file for [function]:
In the most basic sense, we can connect points together to create interesting sequences. The X axis is usually time and the Y axis is the output value. Left-click adds new points, shift-click removes them. [function] has two different modes of operation – line mode and curve mode. Line mode allows only for linear connections between points, whereas curve mode allows for connecting lines to be curved. If in curve mode, lines can be manipulated into curves by alt-click-dragging the line.
So this object was probably designed to be manipulated with the mouse, but with some inspection of the help file we can see that the object is actually more flexible than this. If you look closely at the help file, just above the [function] itself you should see the message box:
(50 0.5) add a new point
So by providing XY information we can also create messages which place points numerically. This opens up the possibility of creating a tool to generate data to create envelopes, rather than drawing them ourselves.
So how could we build a system to do this?
Before I started building this device I sketched down a few rough ideas about what it should be capable of:
- Generating rhythmic sequences
- Easy adjustment of position and height for the points
- Rhythmic variations and poly-rhythms
- Sequences which speed up and slow down
One answer which addresses the problem of creating rhythmic sequences is to generate groups of points, and then design some controls to shape all the points in the group. That’s the approach taken here, with four groups of points and separate controls for each group.
Like many other Max users I come from a creative, not a programming background, but by using Max you inadvertently learn to break down large problems into smaller pieces which are easier to solve. This is one of the key elements of programming, and the mindset to get into when designing patches in Max. So for the basics of this task we need to:
- Generate points across a given range (ie the X axis of [function])
- Calculate the distance between each point
- Scale the X axis points against a curve (for sequences which speed up or slow down)
- Re-calculate the distance between each point for X axis offset controls
- Define the height of each point
- Scale the height against a curve
- If in curve mode, adjust the curvature between each point
It’s probably now a good idea for you to have a look at the envelope generator in question. Here is a link to all the patches used in this post. Unzip the archive and follow the instructions in the readme. [disclaimer/don't sue us note: While they are hosted on this site in support of the article, download and use of these files is done at your own risk.]
Open the file: Complex Function Generator 1.1.maxpat
You will need either a full version of Max/msp 6 or the Max/msp 6 runtime. Both are available from Cycling ’74 here. The runtime version of Max allows you to run patches but not edit them.
It’s probably a good idea for you to have a play around with the device itself. You can flick through the presets to get an idea of the basic functionality, then should have a play around with the controls and the horizontal and vertical scaling (vertical scaling is not connected to the master preset system).
Looking inside the patch should give you an idea of what’s going on under the hood. I’m not going to go into extensive detail here, but the device is built around generating and manipulating lists of numbers. So each set of points is collected into a list, then the points are scaled and their values adjusted before being written into the [function]. List processing is very flexible in Max, especially using the [zl] series of objects which proved very useful here, as did one of my all time favorite objects [multislider]. It’s also worth noting that although it appears as if you are moving individual points, this is not exactly the case. Whenever a control is changed the whole sequence is actually cleared from [function] and re-drawn; this just happens so quickly it’s imperceptible.
That’s the basic system complete. The question which remains is what to do with it next, or what to map it to? Well, there’s a range of possible directions this could go. Controlling the usual parameters of a synthesizer are definitely a good option, but you could also think on a larger time scale – instead of seconds these could control parameters over minutes or even hours. It’s possible to copy data between instances of [function], or you could use [poke~] to transfer the data to a [buffer~] for signal rate experiments. Also, the X axis doesn’t need to be restricted to time, it could be frequency, panning or anything else you care to imagine.
Generating data with data with data…
Here’s an added twist. One (relatively unexplored) area of sound design is the use of generative or algorithmic processes for creating sounds. Before you think of random bleeps, bloops and variations of static (though they have their place), that’s not what I’m talking about. More, I’m thinking of allowing some controlled randomisation into the design process, at select places. So ask yourself this: when you begin designing a sound, and it’s just a mental construct, how complete is your idea of what the sound will eventually sound like? Are you more of a Hitchcock, where everything is pre-defined and you just need to arrive at the solution, or are you more Altman, starting with an idea and piecing it together as you go along, allowing the unexpected in? If you work more in the latter way, then this could be of interest…
The envelope generator we’ve been looking at has already broken down envelope creation into a numerical, data-driven system. Because of this, we can easily build another module to control these parameters and quickly generate new variations. Max is very well suited to this kind of thing, and if you’re interested it’s a good idea to search the Cycling ’74 forums for inspiration. Karlheinz Essl’s Real Time Composition Library is also a must-have set of externals for any algorithmic experiments. The next patches we’re going to look at depend on an external named [between], so to continue with patches beyond this point you will need to install the RTC library. If you’re on PC go with the Windows XP version. It works all the way up to Windows 7, in my experience at least.
So total randomization may be too chaotic for most sound design tasks, but a more controlled level of randomization of parameters could be a source of interesting variations. Take the example of designing, say… laser gun sounds for a game, where there needs to be variation between the different shots. You’ve decided that some low pass filter frequency movements are going to add to the sound, and set up a patch with the envelope generator controlling filter frequency. Now, you could either specify the exact parameters for each shot, or you could set a controlled randomization process in motion and record the output, selecting the most interesting variations.
Here is a small patch which generates a random integer between a certain range and quantization:
By connecting one of these to each of our parameters, we can randomise select parts of the patch at the touch of a button. Try loading up the patch Complex Function Generator 1.1 plus randomizer.maxpat. On the right you should see the new addition to the patch:
If it’s not already selected click the first preset. Clicking the GO! button will randomize the selected parameters. There are further options to include or exclude parameters from the randomization, two number boxes to define the range each control will be randomized over, and an option to quantize the output to a given interval. For example, if you set one of the offset controls to randomize between 0-1000, but then set the quantize to 250, the randomizer will return one of the following: 0,250,500,750 or 1000.
But perhaps you need a more gradual change for each variation? If so, then another system could be added to mutate the contents of [function] in varying steps. The patch Complex Envelope Generator 1.1 plus mutator adds this functionality to the patch on the height and offset controls. We have the choice of mutating one step at a time or triggering the mutation with a metronome. This system is in a fairly experimental stage, so there may still be a few bugs which need squashing. The possibilities are interesting though…
Here’s a short video of it in action:
On a final note, this is Licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. Please use it and experiment with it, and let me know what you create with it. I’ll be putting it to use in some future posts on my blog Sound Design with Max.
Please be sure to leave a comment thanking Mark Durham for his contribution to this month’s featured topic. If you have something you’d like to share with the community, whether part of our “monthly themes” or something “off-topic,” please be sure to contact shaun [at] designingsound [dot] org.