In this review, Nick Rothwell explains Max for Live in terms of what the addition of Max offers to Live users. He also shares his perspective on what Live offers to Max users, something that is less commonly discussed.
Since a lot of people are interested in what the process of porting a Max patch for use in Max for Live looks like, I thought I’d take this tutorial as an opportunity to go over the steps I used to take my waveplayah patch and to convert it to a Max for Live device waveplayah.amxd.
In my last LFO tutorial, I took the basic LFO module I’ve been working with in the previous tutorials, added some new extensions, and created a nice little patch called the waveplayah that used a summed set of the LFO modules to drive the playback of the contents of a buffer~. A good deal of my thinking about and creating it overlapped with the arrival of Max for Live – it’s where I got the idea for the different modulation styles I talked about in our last tutorial, after all.
Although every instance of porting a Max patch for use as a Max for Live device varies a little bit, here’s what the basic procedure looks like:
Decide what kind of Live device your patch will become (Instrument, Audio effect, etc.) and make the necessary I/O substitutions to work in the Live environment.
Replace any UI objects that represent parameters with their Max for Live counterparts (dial > live.dial, etc.) or set anything else that doesn’t have a counterpart up to function in the Live environment.
Enable all device parameters and set their initial states.
Go through and set up the modulation types you wish to use for each UI object so you can use clip automation.
Use Max’s Presentation Mode to set up your user interface within the standard rack space area, and save your patch so that it opens in Presentation mode.
If you’re planning to share your device with anyone, you’ll need to check to see if there are any Max objects or abstractions of files that you’ve used in your device that other users may not have. If there are, you’ll have to include them in your device, freeze your device to collect everything together, and save the result.
Moving day
The first part of the patch to device conversion is simple copying and pasting. You’ll need to think of what kind of Max for Live device the patch you’re porting is going to be – an audio device, a MIDI instrument, or a MIDI effect device. Since our patch is going to become a Max for Live audio device, we start as we always do when creating Max for Live audio devices – by clicking on the Max Audio Effect icon in the Live browser and dragging it into the Effects rack on a Live track to create the familiar and popular starter patch, then clicking on the edit button in the device’s toolbar to launch the Max editor. We really don’t need anything else in the audio device starter patch except for the plugout~ object, so select and remove everything else, then move the lonely plugout~ object to the side for now.
Next, we’ll open the waveplayah.maxpat file from inside the Max editor as well. Choose Select All from the Edit menu, and then choose Copy to copy the contents of the patch to the clipboard. Go to the audio device starter patch and choose Paste from the Edit menu to copy the contents of the Max patch into the Live audio device patch. Before you forget, name and save your patch as waveplayah. Now, let’s get down to business.
First things first
The first and most obvious thing about converting a Max patch for use with Max for Live has to do with making the changes that will help the Max patch function most easily in the Live environment. That’s really composed of three different things:
A Max for Live device handles its input and output in ways that are specific to working in the Live environment; audio input and output are handled using the plugin~ and plugout~ objects.
Since the waveplayah patch loads a waveform into a buffer, we don’t really care about the audio input. This patch doesn’t need a plugin~ object at all, but we do need a way to load an audio file into our buffer. In this case, I’m going to keep it really simple and use a technique in the original patch that uses a button object to load an audio patch.
For output, we’ll use the plugout~ object from the audio device starter patch in place of the ezdac~ object in the original patch. And since we’re working in Max for Live, we also don’t need the transport object any more. When we use Max objects that support ITM (such as the phasor~ objects in our patch), the Live transport automatically becomes the transport that the patch uses. So we can remove the transport object, too.
The namespace in Max is global. That means that any time I have a named object such as a table or a buffer~ or a coll object, any reference I make to that named object anywhere will refer to an object with that name.
In the Max world, that’s a nice advantage and not that big of a deal, but when we deal with Max for Live, things can get complicated. In the case of the waveplayah patch, the wave~ object plays back the file loaded into the buffer named source (since the wave~ object includes the name of the buffer as an argument). That means that any wave~ object in any Max for Live audio effect device that makes reference to a buffer named source will refer to that buffer. That could get awkward when you use the waveplayah device in more than one audio track in a Live session.
Happily, there’s a very simple change I can make to sort this out. In Max, whenever the name of a buffer~ or table or coll object begins with three dashes (—), the named object will be used only within that specific device. So in my waveplayah patch, all I really need to do is to change the name of the buffer I’m using from source to —source, and then make sure that the wave~ object references that new buffer name as an argument.
In order to make the best use of the things like creating presets and automating parameters in the Live environment, we should make use of the new Max for Live UI objects, which are already MIDI-friendly and can set and save initial states (which the Live application needs).
That’s really the part of the patch porting that takes most of the time, and I’ll describe that process in detail below.
Cleaning and sorting and organizing
The next task involves going through our patch and replacing every single Max user interface object with the Max for Live object that is its equivalent – every slider should be replaced by a live.slider object, and so on. We’re doing this because the Max for Live user interface objects have some special attributes that make assigning MIDI controllers and automating parameters much easier.
When converting large Max patches to Max for Live devices, I like to do this by adding a single Max for Live object to my patch, copying it to the clipboard, selecting every object I want to replace with that one and then choosing Paste Replace from the Edit menu (and then going through and filling in menus or tabs as needed). Your mileage may vary!
Technical detail: Although we aren’t doing it in this patch, you should keep in mind that the live.numbox object can be used to replace both floating point and integer number boxes in Max for Live. Since the Live application only works with integer values in the range 0 – 255, you should always replace integer number boxes with live.numbox objects whose Type attribute is set to Float and whose Unit Style attribute is set to Int.
There are a few rslider objects in my patch, and those objects don’t have a Max for Live equivalent. Since the range slider values can be set using the two number box objects to the left of each range slider, I decided that I didn’t need to worry about them and could leave them as is. Similarly, I really don’t care about the button objects I’m using to load audio or reset center and range values, either. But I did decide to replace the *~/number box stuff I was using to control output volume with a live.gain~ object.
Now that I’ve got all of my user interface objects swapped over to Max for Live objects, it’s time to do some housekeeping that’ll make our experience using the new device easier. Here’s what we need to do:
Unlike regular Max objects, Max for Live user interface objects can set and save an initial state (Yes, that’s right – the number of loadbang object objects in your patch just plummeted). We’ll need to add that to everything.
When you unlocked the original waveplayah Max patch, you probably noticed that a pair of objects – a loadbang object and a trigger (t) object – and a bunch of previously invisible patch cords suddenly appeared (If you’re wondering how that trick is done, you can Option-click (Macintosh) or Alt-click (Macintosh) and drag to select the objects and patch cords you want to hide or reveal and choose Hide on Lock from the Object menu. Any selected patch cords or objects will now be invisible when the Patcher window is locked. In the days before Max 5, that was how we initialized values in a patch). You can select the loadbang and trigger objects and just delete them. We’ve got a much nicer way to handle that now.
We’ll want to set up our UI objects so that they’ll work with Live’s parameter automation. That’ll mean setting up recognizable parameter names for our UI object and enabling modulation modes.
These tasks can be a little time-consuming in a patch that includes a lot of user interface objects, but Max for Live includes a new feature that makes the whole process a lot simpler – the Parameters window. The Parameters window provides you with a listing of all the relevant data about all the UI objects in your device in one place where you can view and edit them.
To view the Parameters window, choose Parameters from the View menu.
You’ll see a nice long list that includes every single Max for Live UI object in your patch. It will look a little scary at first, since every UI object is listed as something like live.slider[4] or live.numbox[14], but we’ll fix that in a minute.
The smart thing to do is to go through and give all the UI objects easily identifiable names so we can identify them if we want to automate a device parameter. But how do we know what’s what? Simple – click on the blue dot in the leftmost column in the Parameters window and watch your user patch – you’ll see the UI object turn a nice lime green.
To give the UI object a name (e.g. phase_1), double-click in the Name column in the Parameters window and type in a name. Then click in the Link checkbox to the left of the Name column to link this name to the Long Name attribute of the object (which is what Max for Live uses to identify parameter names for automation). Using the Parameters window, you can just go down the list and rename everything all at once.
You’ll also want to set each entry in the Parameters window to enable saving an initial value by clicking in the checkbox in the parameters column labeled “I” (initial value), and then double-click in the Initial Value column and type in a value (for menu items, use an integer and remember that the first menu item is 0). When you save and close your patch, all of your UI objects will be set to the initial values you type in.
Finally, you’ll want to set a modulation Range value for each parameter (the modulation ranges were what I described here, in case you’d like to refresh your memory). The Bipolar mode works well for nearly everything. In situations where you’re adding modes for things like toggle objects, you should choose Absolute.
Again, the great thing about using the Parameters window is that you do all the housekeeping in one place. Now that things are named and enabled, the only thing left to do is to arrange your interface.
Good interface design 101 – learning by doing
The next thing we’ll need to do is to create the user interface for our device – to take all the parameters in the Max patch and arrange them within the Live audio rack space. Max 5 comes equipped with the tools to make this process easy – the Presentation Mode. When using the Presentation Mode, you select only those portions of your Max patch you make up the user interface and arrange them as you want without affecting the layout of the original patch.
To create the audio effect’s UI, all we have to do is to select those parts of the patch we want to have visible – number boxes, sliders, menus and labels – and choose Add to Presentation from the Object menu. When we do that, every item we’ve selected will display a pink Presentation mode halo. When we click on the Presentation mode icon in the Max patcher window toolbar, everything but the objects we’ve added to the Presentation layer will vanish, letting us arrange the UI objects for our effect in a way that is pleasing and makes sense. This is often the step in creating any Max patch that takes the most time and fine-tuning, but hey – it’s your interface, so take a little time to get it looking just the way you want.
While working on porting the waveplayah patch, I decided that it would be nice to have some kind of visual indication of what the waveform driving the wave~ object in my device looked like, so I made one small addition to the original waveplayah patch– a bit of patching that samples the waveform output and uses a multislider object with its Slider Style attribute set to Reverse Point Scroll to draw a nice moving graph.
Once the design is formalized, we’ll need to set the patch so that the Presentation layer is displayed by default. To do that, choose Patcher Inspector from the View menu and click in the checkbox marked Open in Presentation. When you save and close the patch, you’ll see your layout displayed in the effects rack (If you re-open your patch to edit it, you’ll need to click on the Presentation layer icon in the toolbar in order to display the rest of your patch).
The big freeze
If you want to share any Max for Live device you create with anyone else, you’ll want to freeze your device. Freezing is a way of preparing a device for distribution to other people by including all the files your device needs to work. That includes things like third-party Max external objects, subpatchers, audio files, image files, and Javascript code.
Note: If your device uses third-party objects that only run on Windows or Apple platforms, you can only share devices on that platform.
Max will analyze your patch to locate any files it needs – these files are called dependencies – and then combine these files with your device when you freeze it. Whenever you open a Max for Live device that’s frozen, the files inside that device will be used even if someone else using the device happens to have files with the same name on their system.
In the case of the waveplayah patch, this part of the job is simple – there aren’t any third party Max externals in my patch, I’m not using any abstractions that I’ve created for use elsewhere else, and I’m not using messages to add files (such as loading sound files or wavetables using the loadbang object) in my patch.
That’s not always the case, however – you might have forgotten what you put into your own patch at some point in the past, or you might be working with a patch that someone else made. So I still make it a practice to always to check for things I might need to include in my device as a matter of course.
With your patch open for editing, choose Dependencies from the View menu to display the dependencies window. In the case of the waveplayah patch, the window is reassuringly blank.
But suppose that my patch contained an abstraction I’d made previously (for those of you who are beginners, an abstraction is a Max patch that you can create and save and use in the same way you’d use a Max object in another patch. For more on this, see Max Tutorial 15). No one else but me would have that abstraction, and any audio device I made that used it would be broken for anyone I gave it to.
For example, let’s suppose that I have an abstraction called gt_bundle in my patch. If my patch includes that abstraction, here’s what we’d see in the dependencies window:
You’ll see that it’s listed as a patcher file. This means that when I freeze my audio device, the patcher file will be included. In any case where the Dependencies window shows me a file and lists its type as Missing, the file or objects would need to be manually included in the dependencies list (see the Sharing Devices vignette for more on this).
But the waveplayah file doesn’t have any problems at all, so we’re ready to freeze the device. To freeze the device, click on the Freeze icon in the Patcher Window toolbar.
That’s it. The next time you save your devices, any dependencies you have in your file will be included.
My work here is done
That’s the basic procedure for converting a Max patch to a Max for Live device. For your enjoyment, I’ve included an already-ported version of the waveplayah patch for your edification. But don’t be lazy – it would still be a good idea to try converting the patch and creating your own user interface yourself for practice. After that, there are a whole lot more cool Max patches floating around than there are Max for Live devices, so you know enough to start finding things that interest you in the Max forum and on the web and converting them for your own use.
In the next tutorial, we’ll discuss life in the “tweak loop” – that cycle of living with and improving your devices with things like presets, drag-and-drop interfaces, and the judicious application of color.
A while back, I wrote a series of four tutorials based around the idea of how you could generate and organize variety in Max patches. I wrote them first and foremost because that idea of generating and organizing variety by some means other than random numbers or noise sources has been an interest of mine for a long time. I thought it might be useful to show some examples of how one could make use of these basic techniques not just for generating raw material, but also for ways to transfer explicit control of a Max patch in live performance from the operator to some automated process (think of it as outsourcing) in ways that were less jarring than I often hear in live performance.
I guess people liked the tutorials, which is always flattering. The email that followed was a mix of praise (which I always enjoy), responses that ran the gamut from requests about what to write about next to full-on patch grovels, and even some complaints that I was “doing obvious stuff” (I sure hope that these ideas appear to be obvious, in retrospect – that’s how I know an idea is a good one). In the final tutorial, I’d made the specific decision not to provide any kind of prejudicial example of specific implementation, figuring that no one would be in a better position to imagine what might be done next than readers themselves.
For this tutorial, I’ve tried to address a variety of suggestions and feedback and to extend the ideas covered in parts one, two, three, and four of my humble set of LFO tutorials. This tutorial contains three parts:
I’ve taken the basic LFO module I’ve used in the tutorials and extended it in some new and useful directions.
Based on my experiences working on development for Max for Live, I’ve added some interesting variants on how to transfer control from a user-defined parameter to an LFO-driven process based on the Live application’s “Modulation Modes.”
I’ve combined these changes and demonstrated one possible, if humble, future – creating a set of summed LFOs that drive the playback of the content of a buffer~.
Workin’ the waveform
Throughout the previous tutorials, I’ve been working with a simple single LFO module that included the ability to slave the LFO output to the Max transport, provided a number of different output waveforms, and also allowed for scaling the output waveform within the -1.0 – 1.0 waveform output range. That waveform is then sampled using the snapshot~ object and the output was used to control other parameters. Here’s what it looked like:
This tutorial uses a new variant of that basic single oscillator LFO with some added features. For the sake of functionality and readability, I’ve created subpatches that contain those new changes. Let’s look at those new features:
In our original LFO patch, the speed of the phasor~ object that generated waveforms was set using ITM-based note value settings to determine the rate. While that works just fine, I thought that I’d rather have a choice about whether or not to derive the speed of the phasor~ using Max 5 note values or to specify the phasor~ rate as a floating-point number that describes a frequency. The obvious reason for that is simple – I might someday want to use another LFO (or summed network of LFOs) to modulate the phasor~ rate. I realized that I could also consider the values stored in the umenu as representing a kind of synchronized sequence of frequencies that I could step through using some process (a counter? The contents of a coll object?). So it seemed like a good idea to build some flexibility into the LFO module here. I also realized in the course of patching that it’d be useful to know how the current tempo values represented by the ITM note values mapped to frequency in order to switch between time-synced to free phasor~ rates. So my new subpatch also includes some logic that will automatically set the frequency of the “free” oscillator to match the current value of the note value and the tempo I’m working with.
Let’s look at the inside of the subpatch.
The bulk of the patch is exactly what you’d expect: some logic to select whether you want note values or numerical frequencies using the selector~ object as a switch (and that second argument to the object sets the default behavior). But I’ve added another useful feature in the center of the patch – the translate object that automatically converts the current note value into an equivalent frequency value in Hertz. There’s some trigger-based logic added to the inlets to make sure that the calculation will be triggered whenever a new note value is chosen, and also to make sure that the frequency value is sent to the second phasor~ object before switching to it (think of the zl reg object as the symbol equivalent of a Max int or float object that stores a value and will output the most recently stored value when a bang is received in the left inlet), but the translate object is right in the heart of things. If you spend much time working with the new musical time features of Max 5, this object will become one of your best friends. It takes two arguments – one to specify the kind of value you want to translate and a second argument to specify what kind of values you want to end up with. In this case, we’re translating note values (notevalues) to frequency (hz).
Choosing between manual frequency values or note values is certainly useful, but there’s another reason for wanting to derive the frequency value that corresponds to a given note value. Although I’ve not done it in this patch, you could use that value to derive a subdivision of frequency for generating tuplets well outside of the triplets and dotted notes and intervals described by standard Max 5 note values. If you’re interested in doing it, I’ve already done most of the heavy lifting and will leave the rest as an entertaining and potentially useful “exercise for the reader.”
Here’s your question: what would be the easiest way to specify phasor~ outputs that would give me tuplets that weren’t duples or triples? It’s not rocket science, but I’ve got other fish to fry, at any um… rate.
Although the wave_select subpatcher might look similar to the one we used in our last LFO tutorial, I’ve added a few things – things I use in my own practice, and some bits and pieces that began their lives as reader requests. Here’s the inside of the new and improved subpatcher.
The first change is something simple that I can’t believe I didn’t add in the first place: the ability to set the phase of the output waveform. This isn’t at all difficult – we’re using a rate~ object already to provide a time multiplier to work LFO frequencies that are longer than a dotted whole note, it’s a simple matter to send a floating point value in the range 0.0 – 1.0 to the left inlet of the rate~ object. This lets us sum multiple versions of the same waveform that differ only in their phases, and create interesting effects by being able to place the start point of an oscillator’s output. It’s something I use all the time.
The second addition was a reader request for a negative-going ramp. To be honest, I didn’t add it because I tend to simply invert a positive going ramp waveform when I need one (Extra credit: is there a situation you can think of where this might be an important feature to have?). So I thought I’d add the new waveform as a way to point out a lovely feature of the triangle~ oscillator – its ability to use a signal input to change phase offset of the peak value. In effect, this lets us derive a standard ramp signal (the kind of output we see from the phasor~ object), a triangular waveform, and a reverse ramp signal merely by modifying the phase offset.
It may have occurred to you at this point that you could modify this patch so that there’s only one triangle~ oscillator object that takes an input value between zero and one to produce a variety of waveforms along the “positive-ramp to triangle to negative ramp” continuum. A fair number of savvy Max programmers use this feature to create oscillators with morphing waveforms, in fact. I’ll leave this as yet another interesting project for your edification. For this subpatch I’ve instantiated the triangle~ object three times, each with an argument that gives me a phasor~-style positive-going ramp (0.), a negative-going ramp (1.0) and a triangle wave output (.5).
Using square wave output in an LFO provides some interesting effects (it allows you to create ostinati, and the sudden jumps in mixed waveforms can be really useful), I thought it’d be useful to have a square wave output with a variable duty cycle that would allow me to relative amount of time at which the square wave output was high or low.
Finally, After I had the variable duty-cycle square-wave working properly, I realized that there was something else I would like to have. As nice as the square wave output is, the “jump” when it’s summed with existing waveforms is always a fixed amount. I was thinking that it would be nice to be able to use a sample and hold technique to provide random jumps at ITM rates rather than the set vertical offset, so I added a noise~ source and the Max sah~ object to sample the noise output to provide that non-deterministic vertical offset.
There’s more to modal modulation than Miles Davis
In the fourth tutorial, I spent a lot of time talking about what the relationship between a modulating LFO and a parameter would be, and tried to suggest some interesting ways in which you could using value scaling and value ranges to gradually turn control of a give parameter over to the LFO in a way that seemed smooth.
As you might imagine, a number of us here at Cycling ’74 have been pretty intimately involved with working with the new version of Ableton Live as we prepare for the release of Max for Live. One side effect of this frenzied activity for me personally has been encountering the ways in which the Live application thinks about modulating a parameter. Although they certainly have one method for modulating parameters that matches my personal approach, Live 8 recognizes a number of what they refer to as “Modulation Modes.” In brief, here’s what they are and how they work:
Unipolar: the parameter is modulated between the minimum range value and its current value
Bipolar: the full modulation range of a parameter is equal to twice the distance between the current value and nearest boundary of the parameter’s range (upper or lower). If the parameter is exactly halfway between the lower and upper range boundaries, the modulation range is equal to the total parameter range.
Additive: The modulation range from the current value is equal to plus or minus ½ of the total range of the parameter with values being truncated if they fall outside of the parameter range.
Absolute: The absolute mode requires that you specify a modulation range value, and uses the current value as either the upper or lower bound of the modulation range. If the current value is less than half of the full parameter range, the modulation assumes a lower range of the current value minus the modulation range. If the current value is greater than half of the full parameter range, the modulation assumes the upper range is current value and the lower range is equal to the current value minus the modulation range value.
Working with these modes in Live suggested that I generally use what Live calls “bipolar” modulation (it’s what the previous tutorial did when controlling the amount of modulation added to an LFO, in fact), but that there are some interesting features to doing that in other ways. So I sat down and decided to try to create a Max patch that would implement these modes of modulation. In part, I thought it would be useful to actually see all four modes operating next to each other at the same time to get a better sense of how they worked with various parameter changes. After some quality Max time, I emerged from my studio weary and singed, but holding aloft the modulation modes patch you see here.
It uses the same value and range controls you saw in the previous LFO tutorials, but applies those inputs to four subpatches that implement each of the four modulation modes (feel free to open the subpatches up and poke around in them, of course), and then displays the new output ranges using 0-127 range slider (rslider) objects and applies the output range to a simple sampled sine wave for demonstration purposes.
This demonstration patch requires audio input to help you visualize the changes associated with each mode, and I initially kept the 0-127 range I used in the last tutorials. But when it came to adding the modulation modes to my patch, I realized that I could modify the patch just a little bit and make things easier to use. First, I really didn’t need to watch the output modes, so there wasn’t really any reason to be working with audio inputs, so I could remove that logic from the subpatch. After a little thought, I realized that I already had the patch functionality necessarily to scale the waveform output – the numerical outputs for the wavescale subpatch from a couple of tutorials back. All I needed to do was to modify the output of my modulation mode patch to output numbers in the range -1.0 – 1.0 instead of 0-127, and then connect the outlet of the subpatch that calculated modulation modes and I was all set. A nice bonus here was that my output was still in the signal domain – none of the waveform sampling we worked with in the last tutorial. So I sat down and removed some outlets, added a couple of scale objects to transform data ranges from 0-127 to -1.0 – 1.0, and I was ready to go.
Here’s the handy new LFO module with all the new added features and the modulation modes built right in, ready for use as a waveform or ready to be sampled for some other means:
The careful reader will notice that I’d added some subpatches here that allow me to reset range slider (rslider) objects with the click of a button. I’m very partial to this technique – particularly for the ability to return to initial state in live situations. In I generally like to add the UI elements to the Presentation Mode and then make the buttons really tiny and position them at the position associated with a variable or locate them near to the UI I want to reset.
Putting the new LFO to work
You’ll notice that I used the phrase “generating and organizing variety” at the very beginning of this tutorial to describe my interest as a Max programmer and performer. The phrase has two parts, and I’ve arguably only dealt with half the question. I purposely left that the business organizing variety (i.e. making noise) as an exercise for my readers last time out. This time, I thought I’d finish off this tutorial by taking the LFO module we’ve just made and put it to work doing something interesting, or at least audible.
The humble Max wave~ object lets you use the contents of a Max buffer~ object with which it is associated with by name as a wavetable. All you need to do is to use a phasor~ object to drive the rate at which the wavetable is read.
At low speeds, you can get something like regular buffer playback, and higher speeds will yield waveforms (that usually don’t have anything to do with the pitch or the original audio sample, of course. In these cases, the contents of the buffer translate to timbre rather than the sample at normal playback rates).
But we’re talking about Max patches here – the wave~ object accepts a signal input that is uses to read through the wavetable. There’s no particular reason that the signal input needs to be a single phasor~ object, or even that the input needs to proceed in a stately manner from 0. to 1.0 as the phasor~ object does. Consider the little LFO object I’ve just put together as an input source for a wave~ object.
There might be a couple of things to consider when using the LFO to drive the wave~ object for playback:
The LFO outputs signal values in the range -1.0 – 1.0, whereas the phasor~ object produces output in a narrower range of 0. – 1.0. What happens when the wave~ object gets a negative value between -1.0 and 0, as it would for one-half of the period of a sine wave? (Feel free to try it and see what happens).
If the phasor~ object’s output doesn’t start at 0. and end at 1.0, will we get complete playback of the sample? What if the sample goes from 1.0 to 0. or changes direction?
Square wave output won’t be particularly useful, since that kind of output won’t do much interesting in terms of audio playback – the horizontal portion of the square wave output will just output a steady value associated with a particular portion of the waveform. However, what do you think might happen if we were to mix square wave output with another kind of waveform (a ramp or triangle, for example)?
These questions suggest some interesting things we can take advantage of. We can modify or constrain the range the phasor~ object whose input the wave~ object uses for playback to produce interesting playback patterns – patterns that are locked to the current tempo of the Max transport, in fact. And we can combine the output of several synced LFOs to produce all kinds of interesting nonlinear playback.
The waveplayah patch is an attempt to do just that. Simply put, it takes a trio of the LFO modules we created earlier in the tutorial, sums their output, adds some playback logic that we can use to add a little interest to the proceedings, and lets us produce various kinds of nonlinear playback.
The amount of logic we need to add to create the waveplayah patch beyond simply duplicating the LFO module three times over is pretty simple. One thing we do need to keep in mind is that since we’re summing waveforms, the summed output range of the waveform will now potentially be in the range -3.0 – 3.0. We can fix that easily by multiplying the output waveform by .33 using the *~ object.
And since our summed output range doesn’t match the expected 0. – 1.0 range of a phasor~, we have some interesting opportunities to modify the way that our summed and synchronized waveform drives the wave~ object. The waveplayah patch has a subpatch called playback_mode that lets us map our LFO output in one of several ways. Here’s an example of how the playback_mode subpatch works.
The first example uses signal multiplication and signal scaling to move the entire waveform so that it falls into the 0. – 1.0 range. The Max clip~ object is used for the same thing we did when we needed to create square wave output – arguments to the clip~ object set the low and high ranges for the output; anything that falls above or below that output range is simply clipped at the lower or upper range (We’ll leave it to you to imagine or discover what that sounds like). Finally, we’re using the Max pong~ object in the subpatch to provide us with two new playback options – folding and wrapping. The pong~ object can do both things – its first argument sets the mode of signal treatment (0 for wrapping, 1 for folding) and the second and third arguments set the lower and upper ranges below or above which the signal folding or wrapping will happen. The playback_mode subpatch provides all those options by using a gate~ object together with a menu object whose index number (with 1 added to that number so that we don’t have a 0 value – that would turn the gate off) sets which mode to use in playback.
Technical note: If you take a look at the inside of the playback_mode subpatch, you’ll notice, too that the first choice (scale) also includes a gate to turn the output of the *~ and +~ objects off. We’re doing that because the output of these two objects is always “on” and will be added to the other output waveforms if we don’t.
I’m kind of stunned by the range of kinds of output this little patch will produce, and hope you enjoy playing with it. There are some obvious improvements you might want to make, such as adding objects to the Presentation Layer to create your own UI for the playback unit, or pattrizing your patch to allow for quick preset loading. Or you just might want to spend a few days getting acquainted.
Next time out, I’ll show you how to turn the waveplayah patch into a Max for Live audio effect device (and, more generally, how to port a Max patch for use in Max for Live).