The aim of this tutorial is to cover the basics of creating a virtual instrument in Max for Live. This tutorial should be seen as a starting point that can be expanded upon.
Max for Live is an extension for Ableton Live. This allows users to load patches made in Cycling 74's Max inside of Ableton as Live devices. Max is a graphical programming language whereby patches are created by connecting various objects together. Each object performs a specific function, and multiple objects - interconnected in specific ways - can perform complex operations.
In Max for Live, three categories of devices can be created:
• Audio effects
• MIDI effects
An audio effect takes an audio signal as an input and outputs an audio signal.
A MIDI effect takes MIDI data as an input and outputs MIDI data.
An instrument takes MIDI data as an input and outputs an audio signal.
This tutorial will cover how to create a simple, monophonic sine wave-based synthesiser from start to finish.
Basics: Sine Wave Synth
In Ableton Live, start off with a new Live Set. Save your set. Go to Live Devices > Instruments and find the "Max Instrument". This represents a "blank" virtual instrument that we can use as a template to create the sine wave synth. Drag an instance of the Max Instrument device onto a new MIDI track.
You will see that the blank instrument has two objects, and some comments such as "Build your instrument here". These objects represent the MIDI data coming into our instrument, and the audio signal coming out of our instrument. To edit this blank template and begin creating our synth, click on the small double-ended block arrow (the third icon from the upper right hand side of the device). This will open up Max
Once Max is open, you are ready to edit this template! Note that the device has a vertical limit in terms of height (shown by a comment and a line) but no horizontal limit. This limit is the same with all Live devices, and is part of the Live user interface.
Increase the size of the Max patch window, so that it is easier to edit our instrument.
Move the plugout~ object down so that there is room to add objects in between the midiin object and the plugout~ object. The midiin object represents a stream of MIDI data coming into the synth. The plugout~ object represents the audio going out of the synth.
In general, signal flow in Max always moves from top to bottom. The top connections going into an object are called "inlets" and are only ever used for data or signals going into a Max object. The bottom connections coming out of an object are called "outlets" and are only ever used for data or signal coming out of a Max object.
In Max, go to File > Save As... and save the device in the Max Instrument folder. I am naming mine Seb.ExampleSineSynth.
Let's begin adding new objects to our patch. To create a new object, simply press the "n" key on the keyboard. This will create a blank object box wherever the mouse cursor is. The object we are going to create is called "midiparse". Type midiparse into the blank object box.
The midiparse object takes a serial stream of MIDI bytes and converts it into individual messages for the various types of MIDI data that is available, such as the pitch and velocity of a note, pitch bends, continuous controllers etc. Connect the outlet of the midiin object to the inlet of the midiparse object. In this way, we can receive the stream of serial MIDI bytes and split this stream up into individual messages and data types.
Each outlet of the midiparse object will send a different data type. To see more information about any inlet or outlet of any object in Max, simply hold the mouse cursor over the inlet or outlet. A pop-up message will indicate the function and data type. Try this with the midiparse object to see which outlet corresponds with which type of MIDI data.
The first outlet of the midiparse object is a list of two values, representing the pitch and velocity of a note-on or note-off event. This is the critical data that we need to create our synth; we need to know which pitch to play (i.e. the note number / MIDI pitch) and we also need to know when to play a note and when to stop playing a note (i.e. the note velocity).
Many objects in Max can take arguments. An argument is a value or message that we can add to an object when we create it to tell the object to behave in a certain way or initialise with a certain value.
Create an unpack object, with two arguments of i i (as in, the word "unpack" followed by a space, followed by the letter "i" followed by a space, followed by the letter "i").
The unpack object takes a list (such as a list of numbers) and separates that list into its parts. In our case right now, we know that the first outlet of the midiparse object outputs a list of two numbers; and we also know that these two numbers are always going to be integers (whole numbers). Hence the argument of "i i" for the unpack object.
The first number in the message that we unpack using the "unpack i i" object is the pitch, whilst the second number is the velocity. Let's deal with the pitch first.
What the unpack object is outputting via its first outlet is the MIDI pitch of any note that we send to our synth. This MIDI note is simply a value from 0 - 127, and as such does not represent a frequency directly.
As we want to make a sine wave synth, we need to somehow know the frequency of any MIDI pitches that are sent to our synth.
In order to achieve this, there is a simple Max object called "mtof" (MIDI to Frequency). This object takes a MIDI note pitch value and converts it to a frequency that is represented by that note. Create an mtof object and connect the first outlet of the unpack i i object to it.
Now that we have our note number as an actual frequency, we can create a sine wave generator that we can control with the output of the mtof object. Create a cycle~ object. The cycle~ object is a wave generator that outputs a sine wave. The tilde symbol (~) indicates that this is an audio object rather than just a data object. Any object with a tilde symbol (~) in Max will deal with audio signals in some form.
Connect the outlet of the mtof object to the first inlet of the cycle~ object. This first inlet of the cycle~ object controls the frequency of the sine wave.
We can learn more about any object in Max by option-clicking on the object. This will bring up the help reference, which usually include examples and a brief explanation of what the object does. Try option-clicking on the cycle~ object.
Here you can see the help reference for the cycle~ object. Note that you can click on the question mark (?) tab to see any related objects - this can be very useful.
Create an *~ object (i.e. asterisk tilde). This object is an audio signal multiplication object, and can be used to change the amplitude of an audio signal. Connect the outlet of the cycle~ object to the left inlet of the *~ object.
Note that the line connecting the cycle~ and *~ objects looks different that any other connection we have made so far. That is because it is an audio signal and not a data signal, and as such represents a continuous stream of samples moving through out patch.
The outlet of the cycle~ object is a continuous stream of samples representing the amplitude of the sine wave. In this case, this amplitude is always moving from -1. to +1. in a shape that represents a sine wave, similar to the graph shown below.
Basically, we can use the *~ object to change the amplitude by multiplying every sample by some number. For example, if we multiplied every value of the sine wave below by 0.5, this would effectively halve the amplitude of the sine wave. If we multiplied every value by 2, this would effective double the amplitude of the sine wave and so on.
Let's leave the *~ for now. We'll come back to it in a moment.
Now that we have dealt with the MIDI pitch of any incoming MIDI notes, we need to deal with the MIDI velocity of any incoming MIDI notes. Basically, we want to map the velocity to amplitude, so that when the velocity of an incoming MIDI note is at maximum (127), then the amplitude of our sine wave is 100%. When the velocity of an incoming MIDI note is at minimum (0) - a note off - then the amplitude of our sine should be 0% (silent).
In order to achieve this, we first need to scale our velocity value to a range that is more useful for dealing with audio signals.
Our velocity is in the range of 0 - 127. However, we want to multiply our cycle~ output by a range of 0. to 1.. To this end, create a scale object with the arguments of 0. 127. 0. 1.. Don't forget the decimal points!
The scale object simply scales any incoming numbers to fit within a certain range. These four arguments represents the minimum value we expect of any incoming data, the maximum value we expect of any incoming data, the minimum value that we want our data to be scaled to and the maximum output value that we want our data to be scaled to.
Connect the second outlet of the pack i i object (which is, in fact, the velocity of any notes that come into our synth) to the first inlet of the scale 0. 127. 0. 1. object.
The scale object is now converted all of our velocities from 0. to 127. to values between 0. and 1.. This is the perfect range that we can use to multiply our cycle~ object output with!
Simply connect the output of the scale object to the right inlet of the *~ object.
As a result, a velocity of 127 (maximum) will result in our cycle~ sine wave to be multiplied by 1.0 (i.e. 100%). A velocity of 0 (minimum), however, will result in our cycle~ sine wave to be multiplied by 0. (i.e. 0% or silence).
Finally, we need to connect the output of the *~ object to the two inlets of the plugout~ object. The plugout~ object represents the audio output of our synth. The first inlet represents the left audio channel and the right inlet represents the right audio channel.
Now it's time to clean up our patch! Turn on the grid by going to Arrange > Snap to Grid.
Re-arrange the objects so that they all fit within the vertical limit space of the Live device.
Now, save your patch and go back to Ableton Live, and try out the synth! Add MIDI notes or play your MIDI keyboard into the MIDI track. You should hear the sine synth.
Extension: Attack and Release Controls
You may have noticed that the note on / note off events are not very smooth. This is because we are simply "turning" the sine wave on and off using the *~ object - i.e. there is no smooth transition from a note on to a note off. As a result, the signal is chopped off, and we hear a click or a pop in the wave form. We can get around this issue by adding an attack and a release section to the synth.
Delete the connection from the scale object to the *~ object. Re-arrange the objects so that there is more room to work with.
The attack part of the synth sound occurs when the note on first happens. It is the length of time that the synthesiser takes to go from silence to full amplitude for that particular note.
The releae part of the synth sound occurs when the note off first happens. It is the length of time that the synthesiser takes to go from full amplitude for that particular note to silence.
To create an attack and a release, we need to differentiate between when the note starts (the note on event) and when the note finishes (the note off event). All of our note on events will have a non-zero velocity, and all of our note off events will have a velocity of zero. We can use this to to tell apart our note events.
Add a select 0. object. This object will look at any numbers coming into its left inlet, and will look for any 0.s. If it sees a zero, it will send a bang (a trigger message) out of its left outlet. Any numbers that are not 0. will be sent out of its right outlet.
As the left outlet of the select object sends a bang (trigger signal), we need to use this bang to trigger a message of 0 for turning off our amplitude and starting the release portion of our synth sound. A message in Max is simply a storage area for a number or word or list. A message can be triggered with a bang or it can be triggered by clicking on it. 2
To create a new message, simply press the "m" key on the keyboard. This will create a blank message box wherever the mouse cursor is.
Create a pack f f object. The pack object is the reverse of the unpack object, in that it takes multiple single number values and combines them to form lists of numbers. The f is short for float (i.e. a decimal point number).
Connect the outlet of the 0 message to a pack f f object. This object will - in time - store and send our release time, triggering the release portion of our synth sound.
The right outlet of the select 0. object will pass through any numbers that do not correspond with 0.. This includes all of the numbers that represent note on velocities.
Create a second pack f f object and connect the second outlet of the select 0. object to the first inlet of the new pack f f object.
Create an object called "live.numbox". Once you've typed out the object name, you will notice that the object will appear as an actual number box.
We can change the value of the number box using the mouse in Ableton Live. Furthermore, the values in the box are saved with Live sets and can be automated just like any other Live parameter!
Connect the left outlet of the live.numbox to the second inlet of our release pack f f object.
Create a second live.numbox and connect it to the second inlet of our attack pack f f object.
Try changing the values in the live.numbox objects by command (apple) clicking and dragging up and down in the box. Notice that the range of the live.numbox is only from 0 - 127. For an attack and release in milliseconds, we will need a large range of values.
Select the number box by clicking on it. Press command (apple) i. This shortcut will bring up the object inspector panel, which allows us to change many parameters about the way an object in Max looks and behaves.
There are plenty of options, even just for a simple object such as a live.numbox!
Scroll down and find the setting labelled "Range / Enum". The current values should be 0. 127.. This represents the minimum and maximum range of the live.numbox object.
Change this value from from 0. 127. to 0. 5000., giving us a maximum attack or release time of 5000 milliseconds (5 seconds). Repeat this process for both the attack and release live.numbox objects.
We need to label our attack and release controls. Add a comment to the patch by simply pressing the letter "c" on the keyboard. This will create a blank comment where the mouse cursor is. Move the comment so that the position is appropriate.
Add a line~ 0. object to the patch. The line~ 0. object generates a ramp that goes from some value to a destination value over some amount of time. The line~ object is controlled by sending a pair of values in a list to the left inlet.
This list should be made up like follows:
For example, if the current value of line~ is 0., and we send it the following list:
Then line~ will generate a control signal that will smoothly go from 0. (the current value) to 1. (the destination over a period of 500 milliseconds (the time to take to reach the destination value in milliseconds).
Connect the outlet of the first pack ff object to the line~ object. This means that every time we receive a note off event with a velocity of 0 into our synth, then a line~ transition (ramp) is generated that goes from whatever the current amplitude is to 0. over whatever length of time is determined by the release parameter live.numbox.
Connect the outlet of the second pack ff object to the line~ object. This means that every time we receive a note on event with a velocity that is not 0 into our synth, then a line~ transition (ramp) is generated that goes from 0 to whatever the velocity amplitude should be. over whatever length of time is determined by the attack parameter live.numbox.
Connect the outlet of the line~ to the right inlet of the *~ object. Thus, the signal from the cycle~ object is smoothly turning up and down in amplitude by the control signal generated by the line~ object.
Save your patch and try it out! Change the attack and release parameters. Listen to the amplitude ramping up and down for every note on / note off.
Extension: Different Waveforms
Try replacing the cycle~ object with a rect~ object. Simply highlight the word "cycle~", hit delete and type "rect~"
This will replace the sine wave with a pulse / rectangle wave. How does the sound differ?
• The help reference is actually very useful; use it accordingly!
• Create a synth that has both sine and rectangle waves
• Create a synth that allows the user to select between different waveform (hint objects: gate, umenu, +)
• Find a waveform generator that is neither cycle~ nor rect~ and use that to generate sound.