Lab Exercise 2: Connecting Python and Max
Max/MSP/Jitter has a large number of built-in capabilities for generating sounds, receiving inputs from various devices, and playing to output devices. While it has the ability to maintain state information and sequence sounds, doing so in a graphic programming environment can become tedious and confusing.
As a general purpose programming language, maintaining state and sequencing events in Python is easy. For this week's lab and project, we'll be combining the strengths of the two tools to create an interactive sound machine.
- Start up Max. Create a new patcher.
Create a new blank object (first item in the selection box after you
double-click on the patcher window. Type the following into the
mxj net.loadbang.jython.mxj.ScriptEngine 1 2 @script lab2
This should put one input and three outputs on your object. The ScriptEngine object will run python functions in the file lab2.py.
Still in Max, create two more objects. Type makenote 100 into
one of the objects and noteout into the other. This is how
we can create and play a MIDI sound.
Connect the leftmost output of the ScriptEngine object to the left input of the makenote object. Connect the middle output of the ScriptEngine object to the rightmost input of the makenote object. This will let us control the pitch and duration from a python function.
- Create two message objects. In one message object type script lab2 and in the other put playRandomNote. The first message box will reload the python script after you have made changes. The playRandomNote message box will call a function called playRandomNote in the lab2.py file.
Open lab2.py in a text editor (e.g. TextWrangler). Type in the
import random def playRandomNote(): maxObject.outlet(0, random.randint(30, 128) ) maxObject.outlet(1, random.randint(200, 2000) ) return
The function call maxObject.outlet() lets you set the value of the ScriptEngine's outputs in max. The first argument is selects the output, the second argument is the new value. The above code will play a random note in the range (30, 128) of a random duration (200 to 2000ms). Save your file.
- Go back to Max. Under the Options menu, selection File Preferences. Then add a new path to the list of directories by clicking on the + at the bottom. Then click the choose button and find the directory with your lab2.py file in it.
- Save your Max patcher, and then lock it and click on the script lab2 message box. Then click on the playRandomNote message box. What happens?
Go back to your python file. Above the functions, but after the
import statement, create two variables. One will hold a sequence of
notes, the other will hold an integer for counting. The counter
variable should have the value 0 to start with. The sequence should
be a list with a set of offsets from a base pitch. An example is
counter = 0 sequence = [0, 4, 2, 7, 9, 2, 0]
At the bottom of the file, create a new function called playNext(). It
should set the pitch (outlet 0) to a base pitch plus the current
offset in the sequence. Then it should move the counter forward to
the next offset. See the example below.
def playNext(): global sequence global counter maxObject.outlet(0, 50 + sequence[counter]) maxObject.outlet(1, random.randint( 200, 1000 ) counter = counter + 1 return
Go back to Max, create a message box with the string playNext. Then reload the python script (click the "script lab2" message) and click playNext. What happens?
We need to make one more change so that the tune will loop. That
means we need to reset the counter to 0 when we've reached the end of
the tune. There are two ways to do this. One is to use modulo
counter = counter % len( sequence )
The other is to use a conditional test that only executes if the condition is true.
if counter >= len( sequence ): counter = 0
Either one will work in the situation. Go back to Max and try the playNext button again.
- Make a new sequence and try it out.
Finally, in your lab2.py file, create a new python function called
playThisNote. Give the function one argument, as below. This argument
will come from Max.
def playThisNote( pitch ): print 'playing pitch: ', pitch maxObject.outlet( 0, pitch ) maxObject.outlet( 1, 1000 )
Note the use of the print statement. This tells us that the function is being called with the proper value.
In Max create an object and put prepend playThisNote in it. Make a keyboard input device and connect its output to the prepend object's input. Then send the prepend object's output to the ScriptEngine.
Reload your script and try using the keyboard. What does it do?
Once you are comfortable with the above material, go on to the next project.