Despite years of widespread use in academia, there has never been a proper textbook, written in English, for sound synthesis and processing in Max/MSP. With a recently published text, Maurizio Giri and Alessandro Cipriani have changed that.
Electronic Music and Sound Design: Theory and Practice with Max/MSP – Volume 1 originally written in Italian, has recently been translated to English and published. Structured for use in university courses, the book is an overview of the theory and practice of Max/MSP, with a glossary of terms and suggested tests that allow students to evaluate their progress. In addition to the in-depth text, the authors have also provided hundreds of example patches and interactive demos, along with online support and practical exercises.
Within mere weeks of Microsoft releasing their Kinect camera for the XBox, excited developers have been racing to add support for different platforms. Kick-started by a bounty from Adafruit.com, the DIY community was very quick to respond with Open Kinect drivers.
As expected, the Max community has been bitten by the Kinect bug too, and some dedicated members of the forum community have already got a working Jitter object called jit.freenect.grab (currently Mac OS X only).
To keep up with this object as it develops, watch the forum thread here: link
For background on why the Kinect is so exciting, Peter Kirn at CreateDigitalMotion.com has assembled a little roundtable of prominent artists and developers weighing in on how this technology will improve their lives: link
The Creative Applications Network has also assembled a few examples of interesting work already being done with the Kinect.
We are excited to see the new projects that result from these experiments as things develop. Combined with tools like Jean-Marc Pelletier’s cv.jit library and Mathieu Chamagne’s Max Multitouch Framework, this technology could offer some amazing opportunities for gestural interaction, live projection mapping, and tons of stuff we can’t even imagine.
I’m a sucker for well-trained musicians who push the boundaries creatively and aren’t afraid to experiment. Owen Pallet really blows me away with his talent and where he takes it. Classically trained on violin and piano he has backed the ‘indie band of the moment’ Arcade Fire and is now performing solo opening for such bands as The National with a combination of looping, live keyboard and violin. His album Heartland was included in NPR’s Best Music of 2010.
With the latest version of Max/MSP and Jitter, we are including a new set of modules called VIZZIE to help you create your own unique video programs right away. VIZZIE makes putting it together fun and gets you from start to finish in record time.
From now until January 19, 2011, we’ve knocked $100 off the price of Max/MSP/Jitter bundle and all Jitter upgrades. Visit our Shop for more details. With the new Vizzie modules and this limited time offer, it’s never been easier to get into Jitter.
San Francisco, CA • November 19, 2010 — Cycling ’74 today released Vizzie, a set of modules for quick and unique video creation in Max/MSP/Jitter. Users just grab a few simple Vizzie modules, connect them together and almost instantly have a VJ rig or interactive video work, complete with real-time effects. Since Vizzie uses Jitter under the hood, it integrates seamlessly with all existing Max/MSP/Jitter objects. Even advanced users will benefit from these modules through increased productivity.
My most recent project, the USB-Octomod, uses Processing to create an OpenSoundControl (OSC) interface between any OSC-ready software and a hardware CV device I built using a Teensy 2.0 microcontroller and two MAX5250 DAC chips.
In this article, I’m going to break down the connections between the different pieces of software and hardware used, in order to explain how the system works and to provide the basis for a future tutorial on how one might use the device.
You can read more about the Octomod here, but it essentially allows computer control over the analog control voltages commonly used in analog synthesizers. Input a number 0 – 1023, and the device will output an analog voltage from -5V to +5V.
The OSC interface presents 8 numbered output channels that can be addressed from within the Max patcher. The user sends an OSC message from Max and the interface program in Processing receives, processes, and communicates the data to the microcontroller. For the USB-Octomod, I used a Teensy 2.0, which is a programmable USB microcontroller board very similar to Arduino. Due to the similarity, any of this information should easily translate to the Arduino.
The OSC Interface
The OSC interface is simple. In Max, you need to create a message formatted as follows:
The trick here is that instead of sending an individual message whenever a channel changes, you can reduce network traffic by packaging all of the channels in one message, and updating that message at the rate of the most rapidly changing channel.
Sending OSC from Max/MSP
Here’s what it looks like in Max:
What is this code doing?
The pak object outputs all eight of its inputs as a list whenever any one of the inputs changes.
The message box below appends the /dac prefix to the list. Now the OSC message is formatted correctly.
We don’t want the message to send automatically whenever a channel updates, so we buffer it with the second message box. This is done by sending the first message to the right inlet of the second message box.
Finally, the metro object triggers the full OSC message to be sent once every 10ms.
The OSC interface application is expecting data on port 9999, and we’re going to be using the software locally, so we use the localhost address: 127.0.0.1. The Max udpsend object takes those two numbers as arguments, and transmits the OSC message.
Receiving OSC in Processing
The OSC interface program is written in Processing. OSC is easy to use in Processing as well. With a couple of lines of code, we’re ready to go:
import oscP5.*; // import the oscP5 library
import netP5.*; // the netP5 library is also required for the osc library
OscP5 oscP5;
oscP5 = new OscP5(this, 9999); // all you need to start oscp5 listening on port 9999
Now all we have to do is tell our program what to do when an OSC message is received. This is done by defining the oscEvent function.
After parsing out each of the eight input numbers, we check if a given channel needs to update its state. If so, we pass it to the writeValue() function. If not, we ignore it and don’t have to waste processor time sending the redundant data over the serial port. In my experience, this allows update rates of up to (possibly beyond) 1ms.
void oscEvent(OscMessage theOscMessage){
if(theOscMessage.checkAddrPattern("/dac")==true){
for(int i = 0; i < 8; i++){
data[i] = theOscMessage.get(i).intValue();
channelData[i] = data[i];
}
for(int i = 0; i < 8; i++){
writeValue(i, data[i]);
}
}
}
Writing Serial Data to the Teensy
Serial teensy;
teensy = new Serial(this, Serial.list()[0], 19200);
The above lines are used in Processing to initialize a Serial object, allowing both read and write operations. The Serial.list()[0] argument indicates which actual serial port we want to write to. On my system, the Teensy always shows up as port 0 – this might be different on yours. Finally, the baud rate of 19200 is specified. Baud rate is the number of distinct signal events per second, and is a measure of data transfer speed.
Below is our writeValue() function, which was referenced above. The function is called repeatedly, once for each new sample to be written. First, we have to choose which of our two DAC chips should receive the data. Channels 0 – 3 go to chip A, 4 – 7 to chip B.
The MAX5250 is expecting a two byte word, which is assembled in the next section of code.
The SPI data expected by the MAX5250 DAC is as follows:
The first two bits select which of the four-per-chip channels to use, the second two bits allow us to write data with or without updating the actual voltage outputs, the next 10 bits are the actual data to be assigned, and the last two bits are unused. So, to write a data value of 512 to channel 3 and immediately output a voltage, we would send 1011001110110100.
As you can see, it’s a bit involved, and that’s why we want to avoid running all of this code unless the data has actually changed. We end up with three bytes to send to the Teensy 2.0, a one byte digit to indicate which DAC we want to write to, and the two additional SPI bytes. These are put into a buffer (really just an array) which is only transmitted when the buffer is full. This is to circumvent some timing weirdness in the USB to Serial conversion hardware.
void writeValue(int channel, int _data){
if(channel > 3) { // assign one of two dac chips to respond
dacChip = 1;
} else {
dacChip = 0;
}
/* bit shifting and masking to assemble proper list of bits for the DAC */
_channel = _channel << 14;
updateBits = 3 << 12;
_channel = _channel | updateBits;
_data = _data << 2; spiWord = _channel | _data;
binaryString = binary(spiWord, 16); // at this point, we've assembled our proper list of 16 bits
outputData.add(byte(dacChip)); // so we'll throw them into an array, to facilitate transfer over serial
outputData.add(byte(unbinary(binaryString.substring(0, 8))));
outputData.add(byte(unbinary(binaryString.substring(8, 16))));
if(outputData.size() >= 24){
outputBytes = new byte[outputData.size()];
for(int i = 0; i < outputData.size(); i++){
outputBytes[i] = outputData.get(i);
}
teensy.write(outputBytes);
dataIndex = 0;
outputData = new ArrayList();
previousUpdate = currentTime;
}
}
Initializing SPI on the Teensy 2.0
Here’s an explanation of SPI from Wikipedia:
The SPI bus specifies four logic signals.
SCLK — Serial Clock (output from master)
MOSI/SIMO — Master Output, Slave Input (output from master)
MISO/SOMI — Master Input, Slave Output (output from slave)
SS — Slave Select (active low; output from master)
Essentially, the Master (Teensy 2.0 here) triggers the Slave chip by setting the SS pin low. Then the SCLK pin outputs a periodic clock pulse while the MOSI pin transmits the data (holding the SS pin low for the entire transfer). Here’s an image of the transmission from the MAX5250 datasheet – note that they use DIN (Data In) instead of MOSI, but it’s the same thing.
The first bit of code here is just a couple of statements to simplify our SPI communication. The DACs have a “Slave Select” pin, which allows them to either receive or ignore incoming data. This allows for easier wiring, you can connect all of the SPI lines to each chip, and just select which chip should respond at a given moment. Our DAC select byte (from above, in the writeValue() function) interfaces with the Slave Select code on the Teensy, and allows us to route data to the appropriate chip.Below, in the setup() function, we set the SS pins to output and set them both HIGH, so that no data is accidentally received by the DACs.
Finally, we call the setup_spi() function, found in Andrew Smallbone’s SPI library. These settings define how the Teensy should handle SPI, whether the DACs read the data on the rising or falling edge of the clock pulse, the SPI transmission rate as related to the Teensy clock, and a couple of other settings. You might notice that the serial interface is being initialized with a baud rate of 9600. The Teensy 2.0 actually ignores any baud rate argument and runs at full USB 2.0 speed.
The last bit of code here reads incoming serial data, and immediately sends it out to the proper DAC. The serial buffering on the Teensy is a little bit different than the Arduino, in that it receives an entire USB packet at a time. The timing of the calls to Serial.read() can then be an issue. We want to make sure that we’re reading our three bytes in the proper order, and not getting out of phase with the host app, so we check that our first byte is either a 1 or a 0. Since the SPI interface packs data into the first and last bits of our data word (the second two bytes), a byte with the value of 1 or 0 will only appear as the first byte in the series. Timing is also important here, we need to introduce some brief delays so that we’re not reading or writing data too quickly.
void loop(){
pollAndWrite();
}
void pollAndWrite(){
data = false;
while(!data){
if(Serial.available()) { // look into the receive buffering - not receiving from Max properly
firstByte = Serial.read();
delayMicroseconds(100);
if(firstByte == B00000000) {
secondByte = Serial.read();
delayMicroseconds(100);
thirdByte = Serial.read();
SELECT_DAC_ONE;
send_spi(secondByte);
send_spi(thirdByte);
delayMicroseconds(10);
DESELECT_DAC_ONE;
data = true;
}
if(firstByte == B00000001){
secondByte = Serial.read();
delayMicroseconds(100);
thirdByte = Serial.read();
SELECT_DAC_TWO;
send_spi(secondByte);
send_spi(thirdByte);
delayMicroseconds(10);
DESELECT_DAC_TWO;
data = true;
}
}
}
}
So that’s the software side of the USB-Octomod. Although it’s fairly involved, there are only a few tricky spots, and the OSC interface greatly simplifies what the end-user actually has to think about during composition or performance. Once the Processing and Teensy code is compiled and loaded, it becomes a plug-and-play device.
Greg Surges makes electronic and acoustic music. His work has been released on the Petcord, Wandering Ear, and Stasisfield labels, and his research and music has been presented at the International Computer Music Conference in Belfast, Ireland, the SPARK Festival in Minneapolis, and the 2010 SEAMUS National Conference. Greg is a member of the anarchic semi-improvisational electroacoustic duo Lazers! and MiLO – the Milwaukee Laptop Orchestra. He lives in Milwaukee, Wisconsin with his wife and cats. Information, music, and code/circuitry at www.gregsurges.com.