comments please? midi parser code and SerialPort interface for infusionsystems usbmicrodig
Hi all,
this code allows me to use my infusionsystems usbmicrodig in
standalone mode without having the icubex editor running (the
usbmicrodig is a usb serial device rather than usb midi device). It
works very well indeed on my macbook, and should work on Linux (and
Windows if SerialPort works). But I'm still a comparative newbie to
supercollider and would appreciate some constructive criticism. Advice
for optimisation would be nice too- under heavy sensor load with a 1ms
sampling rate it's taken 20% of one core of a 2gHz core2, though under
normal usage it's between 2 and 4%.
the midi parser is loosely based on libmidi++ by Paul Davis.
thanks,
Lorien
LowLevelMidiParser
{
classvar noteOffStat=0x80, noteOnStat=0x90, polytouchStat=0xA0, controlStat=0xB0;
classvar programStat=0xC0, touchStat=0xD0, bendStat=0xE0; classvar sysexStat=0xF0, qFrameStat=0xF1, songPositionStat=0xF2, songSelectStat=0xF3;
classvar tuneRequestStat=0xF6, eoxStat=0xF7;
classvar bufferSize=16;
var parserState, runnable, msgType, msgBuffer;
var <noteOn, <noteOff;
var <polytouch, <control;
var <program, <touch;
var <bend;
var <>sysex;
*new
{
var self = super.new;
self.initLLMP;
^self;
}
initLLMP
{
noteOn = Array.fill(16,{|i| nil});
noteOff = Array.fill(16,{|i| nil});
polytouch = Array.fill(16,{|i| nil});
control = Array.fill(16,{|i| Array.fill(128,{|j| nil})});
program = Array.fill(16,{|i| nil});
touch = Array.fill(16,{|i| nil});
bend = Array.fill(16,{|i| nil});
sysex = nil;
parserState = \needStatus;
runnable = true;
msgType = noteOnStat;
msgBuffer = Int16Array.new(bufferSize);
msgBuffer.add(noteOnStat);
}
emit
{
var chan = msgBuffer[0] & 0xF;
switch(msgType,
controlStat, { //bandwidth hungry (i.e. lots of them) msgs first
control[chan][msgBuffer[1]].value(msgBuffer[2]) },
bendStat, {
bend[chan].value(msgBuffer[2]<<7|msgBuffer[1]) },
polytouchStat, {
polytouch[chan].value(msgBuffer[1],msgBuffer[2]) },
touchStat, {
touch[chan].value(msgBuffer[1]) },
noteOnStat, {
noteOn[chan].value(msgBuffer[1],msgBuffer[2]) },
noteOffStat, {
noteOff[chan].value(msgBuffer[1],msgBuffer[2]) },
programStat, {
program[chan].value(msgBuffer[1]) }
);
}
channelMessage
{ arg byte;
switch((byte&0xF0),
controlStat, {
msgType = controlStat;
parserState = \needTwoBytes },
bendStat, {
msgType = bendStat;
parserState = \needTwoBytes },
polytouchStat, {
msgType = polytouchStat;
parserState = \needTwoBytes },
touchStat, {
msgType = touchStat;
parserState = \needOneByte },
noteOnStat, {
msgType = noteOnStat;
parserState = \needTwoBytes },
noteOffStat, {
msgType = noteOffStat;
parserState = \needTwoBytes },
programStat, {
msgType = programStat;
parserState = \needOneByte }
);
}
systemMessage
{ arg byte;
msgType = byte;
switch(byte,
sysexStat, {
parserState = \variableLength; },
qFrameStat, {
parserState = \needOneByte; },
songPositionStat, {
parserState = \needTwoBytes; },
songSelectStat, {
parserState = \needOneByte; },
tuneRequestStat, {
parserState = \needStatus; },
eoxStat, {
parserState = \needStatus; }
);
}
parseByte
{ arg byte;
var statusBit;
var size;
if((byte >= 0xF8).and(byte <= 0xFF)) {^nil}; //it's a realtime message, ignore it
statusBit = byte & noteOffStat;
if((statusBit != 0).and(parserState == \variableLength))
{
if(byte==eoxStat)
{
msgBuffer = msgBuffer.add(byte);
sysex.value(msgBuffer);
}
{
"Incorrectly terminated system exclusive message".postln;
};
};
if(statusBit != 0)
{
msgBuffer = Int16Array.new(bufferSize);
msgBuffer.add(byte);
if((byte&sysexStat) == sysexStat)
{
runnable = false;
this.systemMessage(byte);
}
{
runnable = true;
this.channelMessage(byte);
};
^nil;
};
//it's a data byte
msgBuffer = msgBuffer.add(byte);
if(parserState == \needTwoBytes)
{
if(msgBuffer.size<3) {^nil};
parserState = \needOneByte;
};
if(parserState == \needOneByte)
{
this.emit();
if(runnable)
{
if(msgBuffer.size==3)
{
msgBuffer.pop();
msgBuffer.pop();
}
{
msgBuffer.pop();
};
}
{
parserState = \needStatus;
};
};
}
}
UsbMicroDig : LowLevelMidiParser
{
var port;
var device;
var task;
*new
{ arg dev="/dev/tty.SLAB_USBtoUART";
var self = super.new;
self.initUsbMD(dev);
^self;
}
initUsbMD
{ arg dev;
device = dev;
UI.registerForShutdown({this.stop()});
}
play
{
if(port.isNil)
{
port = SerialPort.new(
port: device,
baudrate: 115200,
databits: 8,
stopbit: true,
parity: nil,
crtscts: true,
exclusive: true
);
task = fork { loop { this.parseByte(port.read()); } };
};
}
stop
{
if(port.notNil)
{
task.stop;
task = nil;
port.close;
port = nil;
}
}
}