« Return to Thread: comments please? midi parser code and SerialPort interface for infusionsystems usbmicrodig

comments please? midi parser code and SerialPort interface for infusionsystems usbmicrodig

by loriend :: Rate this Message:

Reply to Author | View in Thread

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;
                }
        }
                               
}

 « Return to Thread: comments please? midi parser code and SerialPort interface for infusionsystems usbmicrodig