How to make a simple version of mlr in Pd

0 Background

Following simioliolio's excellent guide to building a simple version of mlr in Max/MSP (http://docs.monome.org/doku.php?id=dev:max:mlr-tutorial), we present a similar guide for building a simple version of mlr in Pure Data (Pd). This goal of this tutorial is to guide the reader through five component modules of a single row version of mlr: setting up a connection to the monome, loading a sample, looping sample and cutting it in response to button presses, reading and responding to button press messages from the monome, and finally reflecting status in the monome's leds.

Note: if you want a fully-developed version of mlr for Pd, take a look at axiome by Gwen: http://docs.monome.org/doku.php?id=app:axiome.

Given that this tutorial aims to reproduce what simioliolio provided for Max/MSP, there will be many similarities in structure. I've also 'borrowed' the monome button diagram from there.

We start the tutorial with the assumption that you already have installed pd-extended on your computer, and that you also have either MonomeSerial, serial-pyio or ArduinomeSerial properly installed and ready to talk to your monome/arduinome. For this tutorial, you should set the prefix to '/box' (including the slash) in Monome/Arduinome serial.

Forum discussion thread: http://post.monome.org/comments.php?DiscussionID=6339&page=1

1 Preparation

We first of all should make a directory where we can store the files we'll create while building our simple version of mlr. Start pd-extended, create a new file, and then save it in your new directory as 'SingleRowmlr.pd'.

As simioliolio pointed out in the max tutorial, when creating a new patch, it's important to start out with a good idea of what you want to achieve. I'd add that in general, whenever learning a new programming language, be it graphical like Pd, or text-based like Java, it's extremely helpful to set yourself (at first modest) goals of what you'd like to achieve with the language, since that it what's going to drive you forward through the times that you get bogged down in learning new syntax, or when you build something with a bug that's hard to track down.

Since we're trying to build exactly the same function in Pd as simioliolio did in the max tutorial, I'll borrow the simple mlr description from there.

“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).”

In this Pd tutorial, I've divided the problem into five sections, largely because these were the five things I had to figure out in order to realize the patch. The first is setting up OSC communication between the monome and Pd. The second is how to load sample data into Pd. Then we get to the audio playback section, the button press handling and finally the LED output.

The tutorial will make use of many of the basic building blocks of Pd - objects, messages, tables, sub-patches and abstractions. We'll introduce each of these as we get to them, but for more background, the online Pd manual should be consulted. You can find that here: http://puredata.info/docs/manuals/pd. Also, Pd has an online help system for many of its objects - to access this, right-click on the object and select 'Help' from the resulting pop-up menu. Not all objects have help available, but most of those used in this tutorial do.

Figure 1.2 shows the start of our patch; it contains 5 sub-patches and an audio output object that includes a gain control slider and mute button. Sub-patches are a special type of Pd object which are useful for organizing large patches into chunks that are easier to manage and digest. We'll start a new patch and add these sub-patches now.

From Pd's file menu select 'New', and when the blank document opens up, go to file 'Save As' and save it as 'mlrtutorial.pd'. Remember to save from time to time - if you're uncertain of a change you've made but want to keep it anyway, just use 'save as' to save the patch under a different name. Click on the window holding your (currently empty) mlrtutorial.pd patch, and enter Pd's edit mode (command-e OS X/ctrl-e Windows). This will allow us to add our first new object: you can do this either by selecting 'object' from the Put menu, or more quickly by pressing command-1 (control-1 in Windows). This object will appear under your mouse cursor as a dotted outline. In this outline, type 'pd example_sample_load' and press return. The dotted outline will now solidify, and a new window appears with the title 'example_sample_load' - this window is where we'll soon put the contents of the example_sample_load sub-patch. By naming our object 'pd example_sample_load', we've told Pd that we want this object to be a sub-patch with the name 'example_sample_load'. So any time you see an object in Pd with the name 'pd <something>' you know it's a sub-patch.

Click on the mlrtutorial.pd window to reselect the main patch window, and add a second object. Name this one 'pd OSC_setup'. As before, a new empty window should appear with the title 'OSC_setup'. Click to re-select the mlrtutorial window and add sub-patches 'dsp', 'btn_messages' and 'led_messages'.

Finally we can add the audio output object. Once again, 'Put' a new object and name it output~. Once you've positioned it and clicked on the window background, this will change from a small rectangle to the miniature GUI with the gain slider etc.

2 example_sample_load

The first of our sub-patches is used to load the sample data that we'll be playing in our single row version of mlr. Figure 2.1 shows what this sub-patch will look like when you've finished.

This sub-patch contains Pd objects (the rectangular boxes), Pd messages (the flag-shaped objects), a Pd array (the graphic labeled table_sample) and a number box (the gray rectangle with the top right-hand corner lopped off).

First of all, let's add the array that will hold our sample data. Click on the example_sample_load sub-patch window, and enter edit mode. If you've closed the example_sample_load window, simply exit edit mode (Edit menu, de-select Edit Mode, or just type command-e or ctrl-e), and then click on the sub-patch 'pd example_sample_load' in your main mlrtutorial patch, and the sub-patch window will open again.

We put a new array object into this window by selecting array from the Put menu. A dialog box entitled 'array' will pop up. In the name section of this dialog, type 'table_sample', leave the other elements of the dialog box alone, and click 'OK' to close the dialog. A new graphic will appear on the example_sample_load window, with the name 'sample_table' above it.

Now that we have an array to hold our sample data, we need to load that data into it. Still in edit mode, we can now add the structures on the left of figure 2.1. The uppermost element is a 'bang' message. To add a new message, select 'message' from the Put menu, or hit command-2 (ctrl-2). The new message will appear as a blue outline - type 'bang' in this message. In Pd, a message will send its contents to its outlet when the mouse is clicked on it (as long as we are not in edit mode), or if a signal is received at its inlet on the top. Next add a new object, and name it openpanel. Join the inlet at the top of the openpanel to the outlet of our bang message; when the bang message is clicked, it sends a 'bang' to the openpanel object, which then opens a file selection dialog.

Now add the next message 'read -resize $1 table_sample', the soundfiler object and then a number (to add the number use Put menu→Number, or command-3/ctrl-3), and finally the object 's sample_length'. Connect the outlets and inlets of these objects as shown in figure 2.1. Exit edit mode, and click on the 'bang' message at the top of the example_sample_load sub-patch. A file open dialog box should appear, and you can navigate to your sample of choice, select it and press OK.

What happens at this point is that the openpanel object sends the name of the file that you selected to the inlet of the message box 'read -resize $1 table_sample'. When this message box receives the sample file name, it substitutes this name for the $1 part of the 'read …' message. The message box then sends this new 'read -resize <your selected file name> table_sample' message to the inlet soundfiler object. This message instructs the soundfiler object to load the sample data from your chosen file into the array table_sample. At this point, the graphic for the array 'table_sample' will show the waveform of your loaded sample.

When the soundfiler object loads the sample data, it passes the length of the file, in samples, to its outlet, where it is received by the 's sample_length' object. In Pd, 's' objects (alternatively, 'send' objects) can be used to transmit variables from one part of a patch to another; the argument to the s object (in this case 'sample_length') is the name of the message that can be received elsewhere in our patch. As we'll see when we look into the dsp sub-patch, we can receive this message using an 'r' (or 'recv') object.

3 OSC_setup sub-patch

This sub-patch sets up the machinery to allow our mlrtutorial patch to receive messages from our monome or arduinome, and to send LED control commands back to it. As the comment notes, I took these components from the Pd OSC how-to patch by Matthew Rizzuto, who based his patch on that of David Brynjar Franzson. You can download that example patch from http://docs.monome.org/doku.php?id=pure_data_howto.

Figure 3.1 shows the OSC_setup sub-patch. There are three distinct components to this sub-patch. On the left we have the component which is responsible for sending OSC messages out from our mlrtutorial patch, via the SendOSC object. The most interesting new concept in this structure is probably the 'loadbang' object. This object sends a 'bang' from its outlet whenever the patch loads; in turn, this bang sends the command message 'connect localhost 8080' to the SendOSC object. If you have monomeserial/arduinomeserial/serial-pyio set up on a remote machine, or using a port other than 8080 to receive data, you should amend this message to reflect your specific settings.

You will also note that the SendOSC object receives data from the object 'r send_osc' - this is a receive object that receives data from any send object 's send_osc'.

In the center of the patch, we have part of the OSC setup that receives data on port 8000 - again, if you have your OSC router set up to send out data on a port other than 8000, change the argument of the DumpOSC object to reflect your particular settings. All messages received on port 8000 appear at the outlet of the DumpOSC outlet, from where they are connected to the inlet of a send object 's dump8000' that sends data to other parts of the patch that can be received by 'r dump8000' objects. This part of the sub-patch also allows us to print all incoming OSC messages to the main Pd console window by checking the gray toggle box that is next to the comment 'print input?'. You can add this toggle box using the Put menu 'Toggle' or by pressing shift-command-t (OS X) or shift-ctrl-t (Windows).

On the rightmost part of the sub-patch we have the mechanism to filter out press messages from incoming OSC data. As you can see, we obtain the incoming OSC messages by receiving from 'dump8000' using the 'r dump8000' receive object. Two OSCroute objects then filter incoming messages based upon first the pre-fix and then the /press message. The remaining parts of the press message (the column, row and press value (0 or 1)) are then send out via the send object 's grid_in'. Note that the prefix here is '/box' - please set the prefix in MonomeSerial/ArduinomeSerial or serial-pyio to /box (with the forward slash) to properly filter the incoming press messages.

As we'll see below, our btn_messages sub-patch will receive from grid_in and process button presses, and our led_messages sub-patch will form the LED display and transmit to 'send_osc'.

4 dsp sub-patch

The figure above shows our DSP block. This is the module that's responsible for actually playing back our sample and cutting from one part of the sample to another in response to button presses on the monome.

The two critical components of the sample playback system are the phasor~ and tabread4~ objects. The phasor~ object is a simple sawtooth oscillator, continuously ramping from 0 to 1, then returning to 0 and doing it all again at a frequency set by the figure at its left inlet. The tabread4~ object, shown here with a creation argument 'table_sample' allows us to read data from a table. You'll hopefully remember 'table_sample' from above - it's the table where we loaded our sample data.

To play back and loop our sample, we'd like to run through every element of the table from start to end, jump back to the start and do it all over again. The output of the phasor does almost this - it runs from 0 to 1 and jumps back to 0 and starts again. By multiplying the 0→1 output of the phasor by the length of the sample, we now have a signal that repeatedly runs from the start to the end of our sample data table and repeats.

The phasor has a second input, on its top right corner. Any number between 0 and 1 that is sent to this input makes the phasor 'jump' to that position. When we do sample-cutting in mlr, we're exactly doing this kind of jumping around the sample, and that's what the whole mess of connections that converge on the phasor position input are doing. The dsp sub-patch receives messages from button presses (we'll come to those soon), through the 'r step_press' receive object. The route object then sends button presses to its various outlets (the leftmost outlet 0 being the leftmost button of a row, with 7 being the rightmost button). At each of the outlets we have a constant: for outlet 0 the constant is 0, and for outlet 7, it's 7/8ths (0.875). These constant objects send their value to their outlets when a message is received at their leftmost inlet. So, pressing button 3 on row 1 of your monome leads to a step_press message being received, which is then routed to the 3/8ths (0.375) constant, which then sends the value 0.375 to the phasor, and sample playback skips to that position within the sample.

The other message received by the dsp block is on the receive object 'r sample_mute' this is the message from the 0,0 button on the monome, which simply stops the playback of the sample. This is most easily achieved by sending a value of 0 to the frequency input of the phasor~ object - that's what's achieved by the object chain that runs from the 'r sample_mute' object down to the left inlet of the phasor~ object. The 'loadbang' object here is responsible for making sure that when the patch loads, the phasor~ frequency is 0, and we do not play the sample, until a button is pressed.

5 button messages

The button message sub-patch looks fairly simple from the outset - a 'row_select' object and and a 'button_select' object that are hooked directly to send objects step_press and sample_mute respectively. You'll recall from the dsp sub-patch that step_press is used to cut around the sample in response to button presses, and sample_mute is used to stop sample playback.

The row_select and button_select objects are not from the standard set of objects that come with pd_extended. They are new objects I made for this patch (and are included in the .zip file at the bottom of this page). In pd parlance, these are 'abstractions'. An abstraction is somewhat similar to a sub-patch in that it can be used to better organize your patch, but, unlike a sub-patch, it can take creation arguments that are used to set some aspect of its behaviour (e.g. the '1' in row_select is a creation argument, and the '0 0' for button select are too), and we can also use these in other patches that we build, because they are stored in separate .pd files. The process of making these abstractions is pretty straightforward, but beyond the scope of this tutorial. Check out the pd online reference manual for the full story.

For the purposes of this tutorial, we just need to now what row_select and button_select do, not how they do it (although it you're interested in the how, I did include help files for these two objects, which you can get by selecting help from the context menu that appears if you right-click either of the objects). What row_select does is to listen for messages from the 's grid_in' send object in our OSC_setup sub-patch, and simply passes along any button press messages whose row value matches that of the creation argument. In our single row of mlr, remember we want the button presses on row 1 of the monome to control sample playback; row_select 1 passes only those press messages from row 1 on to the step_press send object.

button_press is even more selective than row_select. Whereas row_select allows presses from any button on the row given in its creation argument, button_select allows only presses from the single button whose row and column values match those of the creation arguments. Since we want the top left button of the monome (row 0, column 0) to mute the sample playback on row 1, we use button_select to let only button presses from button 0,0 through to the sample_mute send object.

6 led messages

This sub-patch is responsible for sending messages back to the monome so that it can light LEDs and tell us what our patch is doing. I'll explain the basic method for forming LED messages by looking at what happens when we press the sample_mute button. The famous MLR 'scanning LED' pattern that shows the current sample playback position is handled by the two sub-patches 'pd phasor_to_led_number' and 'pd led_number_to_led_row_message' (the former figures out which LED should be on, given the current sample playback position, while the latter makes the actual led_row message that is sent to the monome). The whole business of responding to the first button press when the sample is muted is handled by the small arrangement on the bottom right-hand side of the patch (this simply switches on the LED that corresponds to the pressed button, and also activates the LED under button 0,0 to give visible feedback that the sample is playing).

To return to how we send messages back to the monome. When the top left button of the monome is pressed, to mute the sample playing on row 1, we need to switch off the LED under the top left button, for our visual feedback that the sample is muted, and also switch off all the LEDs on row 1, since those LEDs should also be off when the sample isn't playing. At the top of this sub-patch, you can see the 'r sample_mute' receive object that receives messages whenever the 0,0 button is pressed. The output of this feeds into two of Pd's message objects - we met these at the beginning of the tutorial when building the sample loader. In that case, we used a mouse click to prompt the message to be sent to its outlet. Here, we use the arrival of messages from the sample_mute receive object to trigger the message being sent to the send_osc send object.

The message boxes hold our monome commands: /box/led 0 0 0 switches the state of LED 0,0 to 0 (i.e. off) and /box/led_row 1 0 sets all of the LEDs on row 1 to 0. The complete messages are 'send /box/led/ 0 0 0' and 'send /box/led_row 1 0' - the 'send' part is included so that Pd's OSC system knows that we are sending these messages.

The patch

This .zip file contains the mlrtutorial.pd patch, and the row_select and button_select abstractions, so if you're experiencing any troubles getting your own patch to work, you can refer to this for some tips, or just use it as is.

mlr_tutorial.zip