There have been a few people in the monome community who are unsure about 'taking the plunge' into Max and begin writing their own patches for the monome. Max is expensive to buy, and much like any other programming language, requires time and effort to learn and understand. Therefore some people are unsure about whether to even begin using Max in the first place.
The most common answer on the forums is 'Do the 30 day trial, and if you like it, go for it!'. But 30 days is a short amount of time, and even after reading a bunch of tutorials and learning objects, it can be hard to know where to start.
This tutorial will describe how to write a simple version of mlr for the monome (http://www.youtube.com/watch?v=t_2xgxndgSU). It will be useful for new users of Max, and could be inspiring for people who are thinking about 'taking the plunge'. Some objects and methods are explained in a lot of detail. Others (especially later on in this guide) are not, so you may need to consult the documentation. If you do not have Max yet, you can look at the documentation here: http://www.cycling74.com/docs/max5/vignettes/intro/docintro.html . Lastly, this is written for people using Max on OS X. If you are running windows, you will need to know the equivalent keyboard shortcuts, etc.
Discussion / Questions:
First make a new folder in a place on your hard disk where you will store new patches. All files you create will be stored in this folder.
Open Max, create a new patch, and save it to your new folder as 'mlrtutorial.maxpat'. This is your main patch, which you will eventually open when you want to use the patch.
Now let's think about what we are making. This is the most important part of using Max. If you are not quite sure what you are making, it will take you a long time to make anything. If a lot of detail goes into planning a patch, it will be made faster and more efficiently. We want to 'map' a sound file across 8 horizontal buttons. When the first button is pressed, the sound file plays from the beginning. When the fifth button is pressed, the sound file plays from the middle, etc. A sound file lasting 8 beats will use each button to represent one of its beats, and there will also be a stop button. This is shown in figure 1.1.
The user will load a file using the computer, play the file from one of eight positions, and stop playback using the stop button. As well as this, the 8 LEDs will reflect the current playback position, and the 'stop' button will be illuminated when the audio is playing (similar to mlr).
*note* The original mlr assigns each row to one of four groups, and the top row is used as off buttons for each group. Also, the top row is used for pattern recording. This guide does not cover these features, and is merely a simple introduction into building your own patch. If this guide did go into detail about these features, you would end up making mlr again, and although this may be useful for your understanding, you should be encouraged to make something new.
We can divide this patch up into three sections: Buttons - Button data from the monome is translated into 'instructions'. DSP - Audio is manipulated by the 'instructions'. LED - LED data is created and sent to the monome.
Start by creating three subpatchers in mlrtutorial.maxpat. There are a couple of ways of making objects in Max. I nearly always press 'N' to make an object where the mouse is, then type what I want the object to be. In figure 1.2, you will see three different 'patcher' objects with different 'arguements'. Notice that two of the 'patcher' objects have the name 'patcher', and the third has the name 'p'. This is the shorthand version of the patcher object's name (there are other max objects like this, like 'select' can also be written as 'sel'. The 'patcher' object creates a sub patch inside the patch, and it is useful for keeping your patch organized. If the patcher is locked, you can double click on it to open the sub patch (or cmd+double-click when the patcher is unlocked). Lock the patcher (click the padlock in the bottom left, or use cmd+E) and double click the 'patcher dsp' object. An empty patcher window opens, which we will now work in.
DSP is short for 'digital signal processing'. For this patch, the only dsp required is to manipulate the playback of an audio file. The dsp part of the patch will receive messages like 'play', 'stop' and 'goto', and output the audio corresponding to these messages (think of it as a bit like a CD player).
First begin inside the 'patcher dsp' object, with the patcher unlocked. Add one 'inlet' object at the top of the patcher window, and three 'outlet' objects at the bottom. The inlet will be used to receive instruction messages, and the outlets will output stereo audio, as well as messages used to generate the led data for the monome. You can now see the inlets and outlets in the main patcher window behind the one you are editing.
Highlight the inlet, and press command+i. This will pop up the inspector (also accessible via the Object menu). Here you can configure settings related to the object. Double click in the 'Comment' field, and type 'audio control messages'. It is useful to leave a comment about your inlets and outlets. When you hover over them outside of the patcher object, you can see the comment, and you know what data is associated with the inlet/outlet. Add these comments to the outlets ; 'sig: audio out left' , 'sig: audio out right' , 'data for leds' .
First you need a place in your computer's memory to load an audio file into, so the samples can be accessed quickly and easily by your computer. Add the object 'buffer~ soundfile'. 'buffer~' holds the audio file, and other objects with the same argument can access its data. 'buffer~' is a very useful object, and it is good to be familiar with its functions. Open the 'buffer~' help file to find out more about it (use cmd+shift+H). For this guide, we will use the 'replace' command to load in a .wav file (you can also use other supported audio formats). Make a message box (press 'M') and type 'replace'. Connect it to an inlet of buffer, and command-click it to send the message (cmd-click when patcher window is unlocked, normal click when patcher is locked). An open file dialogue will open. Choose an audio file which you know is longer than around 5 seconds, and click ok. The benefit of using replace is that you can ignore the size of the buffer and the number of channels it uses, because the buffer characteristics change depending on the chosen file. Using the conventional 'read' command will load the file into the environment set up by the arguments in the 'buffer~' object. Double click on the 'buffer~' file to display a graphical representation of the waveform to confirm it has been loaded correctly.
Now you need something to play the audio data stored in the buffer. Create the object 'groove~ soundfile 2'. 'groove~' plays audio data, and also has a few useful utilities for this application. The first argument links this instance of groove~ to the data in the buffer~ object. The second argument tells groove~ that you would like it to output two channels. There are two main controls for groove~; the playback speed (a signal), and a message that tells groove~ where to play from (a float). We can start by making these controls, getting some audio out, and seeing how the object behaves.
This guide is going to speed up a little. Make your patch look like this:- If you are confused about the function of an object, use the object's help file (right click, Open __ Help, or use the documentation link at the top of this guide). The 'button' object (seen as a circle) 'bangs' the float out of the 'float' object, which you can set using the 'flonum' object. This instructs groove~ where to put the 'playhead' (think of the playhead as having a similar function to the playhead in an audio editor). The two message boxes '0' and '1' are translated into a signal, and used to control the playback speed of groove~. groove~ is connected to an 'ezdac~' object (the one that looks like a speaker), which is an easy way of sending audio out of your computer.
Lock the patcher, click the ezdac~ button so it fills out as a light shade of blue. This shows that dsp is on. Click on the '1' message box that creates the signal into groove~, and cross your fingers! If you hear no audio, check your dsp status (options > dsp status). Also, try using a 'meter~' object on the groove~ output to display whether audio is present, as well as using a 'number~' object to see if msp signal data can be read coming from the outputs. Getting audio out of Max can sometimes be a bit of a pain, but once you have done some troubleshooting a couple of times, you will have very few problems in the future.
You should hear the sound file play once. If you click the button above the float box, a '0.' will be sent into groove~, and the playhead should return to the beginning of the file. If you store the float '2000.' into the float object, clicking the button will send the playhead to 2000ms (or 2 two seconds) into the file. When you click the '0' message box that is connected to the sig~ object, the sound file should stop, and when you change the signal to 1, it should start playing where it left off. If you used a value of 2, the audio data will play back twice as fast. So far so good. You now have a very simple audio file playback system. In figure 1.4, comments have been added to show each function that you have created. One initial problem is that the goto messages are in milliseconds. When we use 8 horizontally (or vertically) orientated buttons to dictate playback position, we want the user to choose the position relative to the total length of the file. The eight buttons the sample is mapped over will eventually be sending the goto messages '0. , 0.125 , 0.25 , etc). This is not currently not possible, because the user can only specify the playback position in terms of absolute time (for example, 'goto 2000' to go to two seconds into the file). A way to achieve this is to multiply the total length of the file by the 'percentage'-like value that will be generated by the buttons. So, if a file was 5000ms long, and we send the message '0.5', 0.5 x 5000 = 2500ms, which sends the playhead to exactly halfway through the audio data.
The object 'info~' can be used to extract information about a sound file loaded into the buffer~. The argument for info~ will link it to the audio file loaded into a buffer~ with the same argument. A few things have been changed here. When the audio file has finished being loaded by buffer~, a bang is sent out of its right outlet. This bang sends info regarding the audio file out of the info~ object, including the audio file size (in ms) out of the seventh outlet. This value is used as the right operand to the multiplication object. Any float between 0 and 1 will now translate as a kind of percentage of the whole audio file. Also, a new argument is set for buffer~, so it has an initial size of 10000ms, and the initial right operand of the multiplication object is set as 10000. This way, even when an audio file has not been loaded (and the info~ has not been banged to retrieve the audio file length), floats between 0 and 1 that go into the patch as goto messages will still cause groove to look for samples in the buffer~ which actually exist. This is not vital, but it is handy to consider. Problems will occur if you do not consider initialization when creating patches. This can only be learnt with time!
These little fiddley buttons and number boxes are not a fun way of performing with this audio player, and the monome is probably much more preferable. The next aim is to finish the dsp patcher so that we do not have to open it anymore. We want to send messages in, and get audio and data for leds out. Using lists and the 'route' object is a great way of using a low number of inlets and outlets between patches for a large variety of data. We could create an inlet for each function of the dsp patcher, but this can get quite hectic when patches get larger. This is the finished dsp patch. It accepts the following messages:- 'stop' - stops playback 'play' - starts playback 'goto [x]' - moves playhead to x (where x is a float between 0 and 1, 0 is the beginning of the file, 1 is the end) 'load' - opens a load dialog for opening a new audio file.
The audio out of groove~ now goes outside the patcher, where we will need to connect a dac~ object, with a less tacky dsp on/off button. The third outlet of groove~ is a 'sync' signal which represents the current position of the playhead (between 0 and 1). This is now connected to the third outlet of the dsp patcher, and will be used for LED data. Also, a 'loadbang' has been added to send the message 'loop 1' to groove~. This makes groove~ go back to the beginning of the audio data when it has reached the end.
This patch is very application specific. It is limited in functionality to make it simple to understand. I would strongly recommend that you make patches like the dsp patcher, but aimed at more unspecific applications. Try to make patches which do a certain function, and do it in a very flexible, efficient, and useful way. It may sometimes seem like a certain patch is taking too long, but if you make yourself useful building blocks early on, it can save you a lot of time in the future.
Lastly, take the patch out of the patcher, and make it into its own patch. Make sure the patcher is unlocked. Select all (cmd+A), copy (cmd+C), make a new patch (cmd+N) and paste (cmd+V). Save the patch as 'groovedsp.maxpat' in the same folder as mlrtutorial.maxpat. Now in your main patch, create a new object called 'groovedsp', and your object will magically appear. The folder where the main patch resides is automatically searched for new objects which are not in the Max object library. Check out information in the Max documentation about file preferences. There are a few limitations when transferring your patching from a patcher to new patch (!). You cannot create new objects inside them simply by double-clicking them and opening them. You have to open the original file to edit the original file. The main advantage is that you can use this object in any other patch, and when you change it once, it will change in all of the subsequent patches you have used it in. When using a patcher, this is not possible.
Now we need to get button data into Max. Make sure your monome is plugged in, and load Monomeserial. Use the prefix 'mlrtut'. Look inside the 'patcher buttons' object, and create the object 'udpreceive 8000'. This object will receive udp packets on port 8000. Connect a 'print buttondata' object to the output of the udpreceive object, and see if you are getting button data in to Max.
Button presses are prefixed with /<prefix>/press. Create a 'route /mlrtut/press' object and connect it to the output of the udpreceive. The button presses ( [column] [row] [on/off] ) will be stripped from this OSC-like prefix, and routed out of the first outlet of the route object.
At this point, it is useful to look at the diagram which was derived from the preparation stages earlier.
Button [0 0] (zero counting) will generate a 'stop' message. Buttons [0 1] to [7 1] will generate play messages, and 'goto' messages between 0 and 1. There are lots of ways to manipulate button data in Max. Sometimes it makes sense to use patterns emerging in the button data to produce your output. Other times you have no choice but to deal with each button separately. To demonstrate different ways of dealing with button data, the next two screenshots show two patches which do exactly the same job. 'buttonsA.maxpat' and 'buttonsB.maxpat' can be found in the zip file. If you are confused about how they work, then it will be good practice to go probing around with a 'print' object whilst pushing buttons, to see how the data is organized and translated into the messages. It would also help, as a test, for you to start with a blank patching window, make the updreceive object, and try and make the patch yourself without looking at buttonsA or buttonsB. Remember, these patches are very simple, and only provide functionality for one particular purpose. You will manipulate button data a lot, so if you find yourself patching the same things over and over, consider making a more flexible patch which you can use in more than one situation.
Now, in mlrtutorial.maxpat, delete the 'patcher buttons' object, and create a 'buttonsA' object (ensuring that 'buttonsA.maxpat resides in the same folder as mlrtutorial.maxpat). Shove a 'print messages' object on the output of 'buttonsA' to see what messages are triggered in response to button presses. Connect buttonsA to 'groovedsp', connect a 'load' message box to the input of groovedsp. Connect the first two outputs of groovedsp into a 'dac~ 1 2' object. This will send audio out of outputs 1 and 2, configured in DSP Status. Connect a 'toggle' box to the first inlet of dac~. This can be used to turn audio on and off. Your patch should look a bit like this.
It works! Now for the LEDs…..
'groovedsp' has a 'sync' signal out of the third outlet. We can use this to display the playback position along the row of buttons. Also, we will need to use the messages sent to 'groovedsp' to respond to play and stop messages.
'play' - The stop button LED is turned on, and the playhead position is displayed along the second row. 'stop' - The stop button LED is turned off, and the playhead position is no longer displayed long the second row.
Open 'p leds' and create two inlets, one commented as 'sig: ramp in' and the other as 'audio control messages'. Connect up 'p leds' in mlrtutorial.maxpat. Go back into 'p leds'. Connect a 'number~' box to the first inlet. 'number~' allows you to see an MSP signal in the same way as 'number' allows you to see a Max int/float message. Whilst playing an audio file, you should see the ramp rising from 0 to 1. We need to translate this signal into LED data for the monome. LED is the same as button data, so [0 0 1] will turn on the LED for button [0 0], and [0 0 0] will turn it off. Again, there are lots of different ways of creating LED data.
Firstly, the ramp is multiplied by 8, so the ramp rises from 0 to 8. The signal is converted to a stream of Max messages by using 'snapshot~ 10', which samples the signal every 10 milliseconds, and sends the sampled value out. The float is converted to an int, and a 'change' box filters out repeated messages (this is important, otherwise you can generate a lot of redundant LED data). We now have the number of the button in the row which should be illuminated. This is used to make a list using 'pack 0 0 1', which will produce the list [n 0 1] , where n is the column number generated from the ramp. We can put this into a 'maxtrixctrl' object, which has a very handy setting for this application. Open the inspector for the matrixctrl, set it to 'Autosize to Rows and Columns', set the number of rows to 1, and select 'One Non-Zero Cell Per Row'. Doing this means that we don't have to generate the LED off data ourselves, and instead maxtrixctrl knows it can only have one cell which is on, and the rest must be off. This is a very handy utility, and saves a lot of patching time. Now we can add the responses to 'play' and 'stop' messages. Here is the finished 'p leds' patcher. 'play' and 'stop' messages make [0 0 0] and [0 0 1] messages using the 'pak 0 0 0' object. This is the stop button LED. A 'play' message bangs the last int that was stored in the 'int' object by change. The 1 x 8 matrixctrl is only updated when the playhead-position LED number is changed (from 0 to 1, or from 5 to 6, etc). If you begin playback from the beginning, and press stop before the playhead-position LED number reaches 1, and then begin playback from the beginning again, the playhead-position LED number is filtered by 'change' because it is repeat of '0'. A 'play' message ensures the position is updated when playback begins. A 'stop' message clears the 1 x 8 matrixctrl.
The 'pack' and 'pak' objects (you should learn the difference between these!) send their output to 'prepend /mlrtut/led' and then into a 'udpsend localhost 8080' object. The LED data is prepended by the correct OSC prefix so that Monomeserial can identify the information as data related to the chosen prefix (mlrtut), and is LED data for the box. There are lots of other prefixes that Monomeserial will understand and subsequently send appropriate serial data to the monome. These are listed on the wiki at monome.org .
That's it. You are finished. Now you have this basic knowledge of getting button data in, manipulating audio, and creating led messages, you should have more of an idea about what you could do with Max for the monome.