One of our most popular article series was the Max 5 Guitar Processor, which built a guitar processing rig by creating a number of connected components. Since the original was published, there have been a number of changes to Max - from the look of patches, to patching 'standards', and even to the available components. We felt like it was time for an update to this series. In an earlier article, Andrew Benson and Ben Bracken went through the process of connecting a guitar to a Max-based processing system, and creating a few guitar-oriented effects patches. In this series of articles, I will be building a Max-based guitar processing "rig" and will give you the opportunity to look over my shoulder as I design and implement this system.
Designing the System
The first thing I did was to plan the processing chain that I would create. I wanted a rather typical guitar rig, with the addition of a looping delay line. I decided on the following functional graph:
I also made a few decisions about general program functionality. Since this will be a stand-alone program/patch, I wouldn't worry about limiting system resource usage; therefore I wouldn't wrap subpatches inside of poly~ objects (which could reduce their CPU load when unused). Also, I decided to take full advantage of Presentation Mode, allowing me to code in a convenient way and worry about the performance layout in a separate step.
Since I am a dyed-in-the-wool tweaker, I wanted to structure the code in such a way that I could manipulate it easily - even during live performance. This means that I needed to create an easily-to-follow patch that was well-labeled, and I needed to encapsulate at a level appropriate for on-the-fly changes. Finally, in order to build this quickly, I will beg, borrow or steal as much of the programming as possible without resorting to the use of VST plug-ins (which would reduce tweak-ability). Throughout these articles, you will see me reuse older code, appropriate from other applications and use third-party objects whenever necessary. I also take full advantage of some design patterns I've developed that allow for quick coding of more complex patch routings.
Input and Gain Handling
Since this is a stand-alone patch, we need to control our own input and output. Let's start by creating an input section. If you open the patch Max8-GtrProc-1.maxpat (link?), you will see that I have used panel objects to split the patch into segments that correspond to the functions described in my processing graph. Place the patch into Presentation Mode, and you will see that these segments jump into a "rack style" package that is easy to follow during performance.
Move back into Patching Mode, and look at the top-left section of the patch. It is labeled "Input", and includes several controls connected to an abstraction named "input_handler". Double-click on the input_handler subpatcher to see its contents.
This subpatch is pretty simple: it contains a number of adstatus objects that allow my controls to display and alter the audio settings used at runtime. The function is similar to using the DSP Status window, but exposes only those items I considered important for realtime performance. Since I don't use the adstatus object very often, I don't know the functional arguments by heart. While I could have used the documentation to learn about the object, I decided to steal the functions from the help patch instead!
By viewing the help patch, I can learn about the function of an object. However, all help patches are also functional Max patches. In this case, I was able to unlock the adstatus.maxhelp patch, copy the functional objects that I needed, then paste them into my subpatcher. By connecting a few inlets and outlets, I'd created the small and efficient input setup handler that I needed. In addition to setting the DSP Settings, I also want to control gain at this point. This is especially important with an instrument-driven patch, since different input hardware will provide vastly different levels. In my case, I will sometimes be using an IK Multimedia Stomp I/O, sometimes a mixer with an audio interface, and sometimes a direct connection into my Mac's audio input - so I want good control of my front-end gain. I did this by adding a *~ object to the input section, then routed the input into this control. By using a scale control, I'm able to change the normal range of the gain knob (0-127) into a range that goes from 0.0 (silent) to 4.0 (quadruple-volume).
The next stage of the patch is a compressor. Since compression can have a very significant effect on the sound of the instrument, I wanted detailed control over its parameters. Therefore, I am using number boxes (rather than knobs) to control the values; this gives me precise control over the compressor's operation without having to guess at a knob's output value.
Again, this section is mostly a set of controls connected to an abstraction named "comp_handler". Double-click on this subpatcher to view its contents. This is a very simple subpatch; I've just borrowed the komp subpatcher (found in examples/effects/kompressor/lib of the Max folder) without change. I set one parameter (lookahead) directly with a loadbang, but mostly expose the functions directly to the main patcher. Since this subpatcher is so simple, why didn't I just use komp directly in my main patch?
In the future, I may want to develop my own compressor system, or I may choose to implement code from someone else. I may even break down and use a VST compressor. In any of these cases, I would want to change the compressor "engine" without having to disturb my main patcher. By placing the compressor in a subpatch, I can change the underlying compression engine without ever having to change the top-level patch or the user interface. This is a "high detail" compressor, giving me the kind of control I want for a flexible rig. If you aren't that familiar with compression, though, it may seem a little intimidating. Here are a few settings that I've found useful with this processor:
Note: In each of the processing areas of the patch, you will see a small grey panel - it doesn't seem to do anything. However, when you switch into Presentation Mode, you should see these panels expand to become the background of the individual processing sections. This is an example of using the Presentation Mode's ability to change an object's geometry to keep my patching view clean, but give me an attractive user interface.
The final processing section for this tutorial controls the output. This section will be familiar to any Max/MSP programmer - it's the basis for audio output in almost any audio patch. The output of the compressor is sent to the two faders, which provide left/right volume control. The signal is then routed to an ezdac~, which is also our DSP on/off control. If you have your guitar (or other instrument) plugged into the computer, you can check the settings on the input module, turn on the DSP, turn up the volume and hear your instrument through the patch.
However, it's not always convenient to test with an actual instrument. For example, some of this patch was done while I was waiting in an airport. The last thing the airport security wants to see is for me to pull out a guitar and start wailing. What I've done is to include a "test fixture" that I can use to test the patch as it is being built. You will notice that this section (labeled "test fixture") gives me the opportunity to load and play a file, play an oscillator tone, or generate noise. This is a subpatcher, called "demosound", that is integral to many MSP help files, and has replaced a lot of different test systems I've used in the past.
For this patch, I grabbed demosound from a help file (in my case, from the comb~ help file), and copied it into my patch. The output of demosound is routed to a send~ object labeled "test". This audio is received into the audio input (that's the extra 'stuff' in the input section), inserting the audio at the same point that my patch would see an instrument. This way, I can test patches without having an instrument plugged in, helping me to be productive when my guitar isn't easily accessible.
We've created the basic I/O blocks for our performance patch. In the next article, we will expand it by adding a good-sounding distortion/overdrive system and a highly controllable filter/EQ. In the meantime, work with this basic patch, learn to understand the routing between the objects and explore the use of the compressor. Until next time - have fun!