You are here

Cycling '74 Max 8

Programming Environment For Audio & MIDI By Nick Rothwell

Cycling '74 Max 8

Max 8 sees Cycling '74 enhance both multi-channel audio and JavaScript support, among many other improvements.

Max 8 continues the evolution of Cycling '74's flagship media programming environment as it progresses through what must be, in software terms, a comfortable but active middle age. To strain the analogy somewhat, Max entered adulthood with version 5 (reviewed in August 2008), which delivered a new, flexible, user interface to its graphical programming features. As well as offering functional improvements, Max 6 and Max 7 each did some redecorating, resulting in a distinct visual appearance for each revision. So, what of Max 8?

On first appearance, nothing much seems to have happened: patcher windows are still, by default, pale grey with a dark grey surround, but the dark greys are noticeably darker, improving contrast and adding some visual 'punch'. This seems especially effective in text-rich areas like the console, reference and property inspector panes. I'm still not a massive fan of the default colour scheme and font, but the increased contrast helps. Of course, colours and fonts can always be customised in patchers, and Max 8 offers a handful of overall application colour 'styles', including the Max 7 look if you're particularly attached to it.

Working with Max 8 doesn't initially seem that different to Max 7: the icons in the toolbars haven't changed that much, and neither have the various pull-down and pop-up menus. There are patching improvements, but the refinements are, by and large, subtle, and nothing jumps xout in the interface. To get the most from Max 8, you'll need to skim through the release documentation — or read the rest of this article!

Multiplication Tables

With Max 8 comes first-class support for multi-channel audio processing, courtesy of a framework and set of objects that go under the collective banner MC. Of course, Max has been able to do multi-channel audio for as long as it's been able to do audio, with objects for taking in and putting out several channels at once, and for recording and playing multi-channel files, but MC addresses issues which, in retrospect, were obvious shortcomings.

Firstly, it's long been a topic of discussion in mailing lists that Max's audio patch cords are resolutely monophonic, in an era where production is at least stereo and often involves surround sound, Ambisonic mixing and so on. Monophonic patching gets you a fair way (and modular synthesizer users don't seem to complain that much), but after you've done a fair bit of routing patch cords eight or 16 at a time for a multi-speaker installation, patience starts to wear a bit thin. So at the very least, there's an argument for multi-channel cable 'snakes' to tidy things up. (Even if you're working in stereo, that's your patching effort potentially cut in half.)

Secondly, Max's support for polyphonic processing has been a little clunky, consisting only of a single 'poly~' object which loads and replicates a patcher into a multi-voice framework with the kinds of voice control you'd experience on a polyphonic synthesizer. This works surprisingly well, but requires an extra patcher file, and editing can be awkward.

Thirdly, there are lots of applications that make use of potentially large numbers of identical audio sources: anything from classic additive synthesis and granular effects to analogue-style unison mode. Modern computers can support hundreds or thousands of audio generators at once, so there's potential for some mechanism that can replicate a source at scale and configure all the copies in some unified manner.

Capable Cables

Let's start with multi-channel cables (we'll call them MC for short). The object 'mc.pack~' converts a set of single-channel cables into a single MC one, while 'mc.unpack~' does the opposite. Visually, MC cables are a little thicker than their monophonic siblings, and are tinted blue, although the colours can be customised. There are also objects for combining, splitting and interleaving MC signals in various ways.

Packing and unpacking: converting between traditional mono patch cords and multi–channel cords.Packing and unpacking: converting between traditional mono patch cords and multi–channel cords.The objects which can process MC audio have names beginning with 'mc'. In the screen I've captured, 'mc.mixdown~' is a mixer which takes an MC input of any number of channels, and delivers an MC output with (generally) a smaller number of channels, where the input signals are distributed to the outputs in some manner (options include linear or constant-power panning, or wrapping around of the inputs to the outputs). A second MC input to 'mc.mixdown~' allows panning to be controlled dynamically.

It's probably unwise to connect MC patch cords to Max objects that aren't MC aware, although it doesn't seem to cause any harm: all but the first channel are discarded. Similarly, feeding a single channel into an MC object will cause it to be duplicated into however many channels the object expects, but it's best not to mix and match the two patch cord types. In the MC world, 'thinner' MC feeds are duplicated in a cycle to attach to 'fatter' object inputs.

The screen shows some LED level meters taking MC feeds. Objects which give graphical displays of signals have been augmented to process MC inputs, and will adapt their appearance accordingly. (I was amused to notice that the old-style 'mechanical' VU meter is also MC-aware, and will sprout additional needles when it needs to show several channels. The multi-channel oscilloscope is also rather impressive.) Mouse over an MC cord, and an icon pops up specifying how many channels it carries, while the signal probe pop-up window will show all of the channels in a stack.

Some graphical controls have MC variants for multi-channel operation: 'mc.gain~' is an entire block of faders, for example, and if you want the Ableton Live look there's an 'mc.live.gain~' variant of the combined fader and level meter.

MC is compatible with Max for Live, so even if your Ableton work is primarily stereo, you can drop into multi-channel operation internally within your devices. There are even MC versions of 'plugin~' and 'plugout~' if you want to do your patching entirely in the MC world. However, you will need a full Max 8 licence to patch with MC objects; if you only own Max for Live, you'll be able to use them but not make alterations.

MC channels are allocated statically: there's no ability to change the 'width' of an MC cord while audio is running. If you send a 'chans' message to increase the output channel count for an object, nothing will happen until you turn audio processing off and on again. Objects that take MC inputs (including VU meters and gain sliders) will adapt to the 'width' of their incoming audio when audio processing is turned on.

Cycle Of Fifths

The power of MC comes from its ability to replicate audio objects: so, for example, while 'cycle~' gives you a single sine-wave oscillator, 'mc.cycle~' creates a wrapper object containing multiple 'cycle~' instances, feeding to an MC output. However, it's not terribly useful to create an entire array of audio objects unless their individual behaviours differ in an interesting way. If you need a relatively small number of instances with specific parameter values, you can just type them in when you create the MC wrapper. For more complex setups, you can do this programmatically. The messages 'deviate', 'increment' and 'spread' sent with arguments to an MC wrapper vary the individual instance parameters in various ways: 'deviate' randomises a parameter, which is ideal for unison or detuning effects if that parameter is oscillator frequency, while 'increment' and 'spread' generate a linear range, perhaps for additive spectral effects.

A hint of additive synthesis: a graphical envelope configures a  bank of oscillators using 'mc.gradient~'.A hint of additive synthesis: a graphical envelope configures a bank of oscillators using 'mc.gradient~'.

For more sophisticated multi-channel synthesis, signal-generating objects like 'mc.gradient~' and 'mc.evolve~' can be programmed using a graphical breakpoint editor to produce complex spreads of multi-channel signal values with an overall range indexed by a control input from 0 to 1. The exact behaviour of 'mc.gradient~' and 'mc.evolve~' is subtle, but it perhaps helps to think of them rather like banks of additive oscillators to which you can apply multi-point envelopes — except that they output DC signals, not audio. The screen shows an 'mc.gradient~' object generating a bank of control signals (illustrated by the signal probe pop-up) according to the envelope values from a graphical range panel; the signals then attenuate a bank of triangle-wave oscillators. And finally, if you really need to, you can dynamically address individual objects in an MC wrapper, muting or configuring them as required.

MC can be applied in a variety of different audio processing contexts: 'mc.tapin~' and 'mc.tapout~' extend the classic multi-tap delay objects to provide independent programmable delays across the channels of an MC patch cord, while 'mc.gen~' runs multiple copies of low-level native code patchers which can be edited as a group. You can load multiple copies of a VST plug-in with 'mc.vst~', and even build your own MPE (MIDI Polyphonic Expression) instruments with the help of 'mc.noteallocator~'.

Since many objects are 'wrapped', Alt-clicking on them opens the original help patcher for the non-MC variant, which is a little frustrating: you sometimes need to dig around in the documentation to find MC-specific support. Hopefully the MC documentation will get deeper integration in future.

Out Of The Box

While Max's graphical programming is immensely powerful, there has always been a place for more traditional, text-based coding alongside the boxes-and-cords paradigm. Sometimes, a task benefits from the particular strengths of 'real' languages: they can express algorithms succinctly (allowing direct iterative or recursive approaches), support complex data structures, can manipulate text, and are generally modular, allowing tasks to be broken up into separate parts in ways that a graphical paradigm doesn't easily support. Max is also a minority language in the world of software, so if you're looking for example code to perform a particular task, you have a much better chance of finding it in a mainstream language.

Max was never intended to be a general-purpose programming language, so it lacks some facilities which other languages consider essential: free access to a computer's file system, for example, or the ability to make network connections or talk to databases. While Max might offer piecemeal support here and there (for example, OSC networking), text languages come with comprehensive libraries for talking to the outside world. Think of graphical and text programming as complementary: Max is suited to processing audio and video, building graphical interfaces, working with timing and general creative discovery, while text languages tackle complex algorithms and real-world interfacing.

It's always been possible to implement new Max objects by writing code in C or C++ — and that's still a popular route for performance reasons — but the process is intricate and error-prone. In 2004, Max 4.5 shipped with support for Java, a sophisticated high-level, portable and network-aware language developed by Sun Microsystems. Java-literate Max users got to work straight away building hybrid Java+Max applications, but the language's complexity prevented it from gaining mass appeal, and it was beset by packaging issues: for years, Apple shipped their own Java system, and Oracle's acquisition of Sun Microsystems muddied the waters in terms of software licensing and installation.

Java is still supported in Max 8, albeit with minimal documentation, and is perfectly usable if you're willing to take the time to set up your environment. And I'm actually rather a fan of the language, especially since its runtime engine supports other languages like Python and Ruby, all of which can be wheeled into a Max application. But the world has moved on, and everyone and his dog now programs in JavaScript. JavaScript couldn't exactly be described as an improved Java (the names are similar only for historical reasons, and the languages resemble each other only superficially), but it's easy to pick up and is wildly popular, since it's the native language of web browsers. There's lots of example code online, and a cornucopia of open-source libraries.

Max has had some JavaScript support for a while: the 'js' and 'jsui' objects support JavaScript snippets for scripting Max's user interface and doing interactive graphics. But these objects are limited in scope and use a rather old dialect of the language. So, enter Node for Max, which revitalises Max's JavaScript and boosts its powers.

Published November 2019