# HG changeset patch # User Giulio Moro # Date 1454652995 0 # Node ID 265a527f8be8908650196d16518684f1f786701f # Parent 0029562391b1a65cdaead0b43c2a2336e09bc994 Added MidiParser for channel messages diff -r 0029562391b1 -r 265a527f8be8 core/Midi.cpp --- a/core/Midi.cpp Thu Feb 04 18:41:30 2016 +0000 +++ b/core/Midi.cpp Fri Feb 05 06:16:35 2016 +0000 @@ -12,14 +12,68 @@ #define kMidiInput 0 #define kMidiOutput 1 + +midi_byte_t midiMessageStatusBytes[midiMessageStatusBytesLength]= +{ + 0x80, + 0x90, + 0xA0, + 0xB0, + 0xC0, + 0xD0, + 0xE0, + 0 +}; + +unsigned int midiMessageNumDataBytes[midiMessageStatusBytesLength]={2, 2, 2, 2, 1, 1, 2, 0}; + bool Midi::staticConstructed; AuxiliaryTask Midi::midiInputTask; AuxiliaryTask Midi::midiOutputTask; std::vector Midi::objAddrs[2]; +int MidiParser::parse(midi_byte_t* input, unsigned int length){ + unsigned int consumedBytes = 0; + for(unsigned int n = 0; n < length; n++){ + consumedBytes++; + if(waitingForStatus == true){ + int statusByte = input[n]; + MidiMessageType newType = kmmNone; + if (statusByte >= 0x80){//it actually is a status byte + for(int n = 0; n < midiMessageStatusBytesLength; n++){ //find the statusByte in the array + if(midiMessageStatusBytes[n] == (statusByte&0xf0)){ + newType = (MidiMessageType)n; + break; + } + } + elapsedDataBytes = 0; + waitingForStatus = false; + messages[writePointer].setType(newType); + messages[writePointer].setChannel((midi_byte_t)(statusByte&0xf)); + consumedBytes++; + } else { // either something went wrong or it's a system message + continue; + } + } else { + messages[writePointer].setDataByte(elapsedDataBytes, input[n]); + elapsedDataBytes++; + if(elapsedDataBytes == messages[writePointer].getNumDataBytes()){ + waitingForStatus = true; + writePointer++; + if(writePointer == messages.size()){ + writePointer = 0; + } + } + } + } + return consumedBytes; +}; + + Midi::Midi(){ outputPort = -1; inputPort = -1; + inputParser = 0; size_t inputBytesInitialSize = 1000; inputBytes.resize(inputBytesInitialSize); outputBytes.resize(inputBytesInitialSize); @@ -37,6 +91,18 @@ } Midi::~Midi(){} + +void Midi::enableParser(bool enable){ + if(enable == true){ + delete inputParser; + inputParser = new MidiParser(); + parserEnabled = true; + } else { + delete inputParser; + parserEnabled = false; + } +} + void Midi::midiInputLoop(){ printf("Midi input loop %d\n", objAddrs[kMidiInput].size()); for(unsigned int n = 0; n < objAddrs[kMidiInput].size(); n++){ @@ -66,9 +132,32 @@ if(inputBytesWritePointer == inputBytes.size()){ //wrap pointer around inputBytesWritePointer = 0; } + if(parserEnabled == true && ret > 0){ // if the parser is enabled and there is new data, send the data to it + int input; + while((input=getInput()) >= 0){ + midi_byte_t inputByte = (midi_byte_t)(input); +// printf("__0x%x\n", input); + inputParser->parse(&inputByte, 1); + } +// if(inputBytesReadPointer == inputBytesWritePointer){ + // this should never be the case, it should only happen when ret == 0 +// } else { + +// unsigned int length = (inputBytesWritePointer - inputBytesReadPointer + inputBytes.size()) +// % inputBytes.size(); +// rt_printf("inputBytes: 0x%x 0x%x 0x%x", inputBytes[inputBytesReadPointer], +// inputBytes[inputBytesReadPointer+1], inputBytes[inputBytesReadPointer+2]); +// length = inputParser->parse(&inputBytes[inputBytesReadPointer], length); +// inputBytesReadPointer += length; //if parserEnabled, the readPointer is incremented here, +// //so calls to getInput() should be avoided +// if(inputBytesReadPointer == inputBytes.size()){ +// inputBytesReadPointer = 0; +// } +// } + } if(ret < maxBytesToRead){ //no more data to retrieve at the moment usleep(1000); - } // otherwise there might be more data ready to be read, so don't sleep + } // otherwise there might be more data ready to be read (we were at the end of the buffer), so don't sleep } } @@ -85,21 +174,10 @@ int ret; ret = write(outputPort, &outputBytes[outputBytesReadPointer], sizeof(midi_byte_t)*length); if(ret < 0){ //error occurred -// if(errno != EAGAIN){ // () would return EAGAIN when no data are available to read just now -// rt_printf("Error while writing midi %d\n", errno); -// } rt_printf("error occurred while writing: %d\n", errno); usleep(10000); //wait before retrying continue; } - -// inputBytesWritePointer += ret; -// if(inputBytesWritePointer == inputBytes.size()){ //wrap pointer around -// inputBytesWritePointer = 0; -// } -// if(ret < maxBytesToRead){ //no more data to retrieve at the moment -// usleep(1000); -// } // otherwise there might be more data ready to be read, so don't sleep } } int Midi::readFrom(int port){ @@ -129,6 +207,9 @@ } int Midi::getInput(){ +// if(parserEnabled == true) { +// return -3; +// } if(inputPort < 0) return -2; if(inputBytesReadPointer == inputBytesWritePointer){ @@ -141,6 +222,13 @@ return inputMessage; } +MidiParser* Midi::getParser(){ + if(parserEnabled == false){ + return 0; + } + return inputParser; +}; + int Midi::writeOutput(midi_byte_t byte){ return writeOutput(&byte, 1); } @@ -152,3 +240,29 @@ else return 1; } + +MidiChannelMessage::MidiChannelMessage(){}; +MidiChannelMessage::MidiChannelMessage(MidiMessageType type){ + setType(type); +}; +MidiChannelMessage::~MidiChannelMessage(){}; +MidiMessageType MidiChannelMessage::getType(){ + return _type; +}; +int MidiChannelMessage::getChannel(){ + return _channel; +}; +//int MidiChannelMessage::set(midi_byte_t* input); +// +//int MidiControlChangeMessage ::getValue(); +//int MidiControlChangeMessage::set(midi_byte_t* input){ +// channel = input[0]; +// number = input[1]; +// value = input[2]; +// return 3; +//} + +//int MidiNoteMessage::getNote(); +//int MidiNoteMessage::getVelocity(); + +//midi_byte_t MidiProgramChangeMessage::getProgram(); diff -r 0029562391b1 -r 265a527f8be8 include/Midi.h --- a/include/Midi.h Thu Feb 04 18:41:30 2016 +0000 +++ b/include/Midi.h Fri Feb 05 06:16:35 2016 +0000 @@ -13,9 +13,194 @@ typedef unsigned char midi_byte_t; + +typedef enum midiMessageType{ + kmmNoteOff = 0, + kmmNoteOn, + kmmPolyphonicKeyPressure, + kmmControlChange, + kmmProgramChange, + kmmChannelPressure, + kmmPitchBend, + kmmNone, + kmmAny, +} MidiMessageType; +#define midiMessageStatusBytesLength 7+2 //2 being kmmNone and kmmAny + +extern midi_byte_t midiMessageStatusBytes[midiMessageStatusBytesLength]; +extern unsigned int midiMessageNumDataBytes[midiMessageStatusBytesLength]; + +class MidiChannelMessage{ +public: + MidiChannelMessage(); + MidiChannelMessage(MidiMessageType type); + virtual ~MidiChannelMessage(); + MidiMessageType getType(); + int getChannel(); + unsigned int getNumDataBytes(){ + return midiMessageNumDataBytes[(unsigned int)_type]; + } + void setDataByte(unsigned int dataByteIndex, midi_byte_t input){ + _dataBytes[dataByteIndex] = input; + } + void setType(MidiMessageType type){ + _type = type; + _statusByte = midiMessageStatusBytes[_type]; + } + void setChannel(midi_byte_t channel){ + _channel = channel; + } + midi_byte_t getDataByte(unsigned int index){ + return _dataBytes[index]; + } + void clear(){ + for(int n = 0; ngetType()); + rt_printf("channel: %u, ", this->getChannel()); + for(int n = 0; n < this->getNumDataBytes(); n++){ + rt_printf("data%d: %d, ", n + 1, this->getDataByte(n)); + } + rt_printf("\n"); + } + +private: + const static int maxDataBytes = 2; +protected: + midi_byte_t _statusByte; + midi_byte_t _dataBytes[maxDataBytes]; // where 2 is the maximum number of data bytes for a channel message + MidiMessageType _type; + midi_byte_t _channel; +}; +/* +class MidiControlChangeMessage : public MidiChannelMessage{ + int number; + int value; +public: + int getNumber(); + int getNumDataBytes(); + int setDataByte(midi_byte_t input, unsigned int dataByteIndex); + int getValue(); + int set(midi_byte_t* input); +}; + +class MidiNoteMessage : public MidiChannelMessage{ + int note; + int velocity; +public: + int getNote(); + int getVelocity(); + int getNumDataBytes(); + int setDataByte(midi_byte_t input, unsigned int dataByteIndex); +}; + +class MidiProgramChangeMessage : public MidiChannelMessage{ + midi_byte_t program; +public: + int getNumDataBytes(); + int setDataByte(midi_byte_t input, unsigned int dataByteIndex); + midi_byte_t getProgram(); +}; +*/ + +class MidiParser{ +private: + std::vector messages; + unsigned int writePointer; + unsigned int readPointer; + unsigned int elapsedDataBytes; + bool waitingForStatus; +public: + MidiParser(){ + waitingForStatus = true; + elapsedDataBytes= 0; + messages.resize(100); // 100 is the number of messages that can be buffered + writePointer = 0; + readPointer = 0; + } + + /** + * Parses some midi messages. + * + * @param input the array to read from + * @param length the maximum number of values available at the array + * + * @return the number of bytes parsed + */ + int parse(midi_byte_t* input, unsigned int length); + int callme(){ + return readPointer; + }; + int numAvailableMessages(){ + int num = (writePointer - readPointer + messages.size() ) % messages.size(); + if(num > 0){ + int a = a +1; + a = callme(); + } + return num; + } + /** + * Get the oldest channel message in the buffer. + * + * You should not call this function when numAvailableMessages() returns 0. + * @param type the type of the message to retrieve + * @return a copy of the oldest message of the give type in the buffer + */ + MidiChannelMessage getNextChannelMessage(/*MidiMessageType type*/){ + MidiChannelMessage message; + message = messages[readPointer]; + if(message.getType() == kmmNone){ + message.clear(); + } + messages[readPointer].setType(kmmNone); // do not use it again + readPointer++; + if(readPointer == messages.size()){ + readPointer = 0; + } + return message; + }; + +// MidiChannelMessage getNextChannelMessage(){ +// getNextChannelMessage(kmmAny); +// } +// MidiControlChangeMessage* getNextControlChangeMessage(){ +// return (MidiControlChangeMessage*)getNextChannelMessage(kmmControlChange); +// }; +// MidiProgramChangeMessage* getNextProgramChangeMessage(){ +// return (MidiProgramChangeMessage*)getNextChannelMessage(kmmProgramChange); +// }; +// MidiNoteMessage* getNextNoteOnMessage(){ +// return (MidiNoteMessage*)getNextChannelMessage(kmmNoteOn); +// }; +}; + + class Midi { public: Midi(); + + /** + * Enable the input MidiParser. + * + * If the parser is enabled, readFrom() will return an error code. + * Midi messages should instead be retrieved via, e.g.: getMidiParser()->getNextChannelMessage(); + * + * @param enable true to enable the input MidiParser, false to disable it. + */ + void enableParser(bool enable); + + /** + * Get access to the input parser in use, if any. + * + * @return a pointer to the instance of MidiParser, if currently enabled, zero otherwise. + */ + MidiParser* getParser(); + /** * Open the specified input Midi port and start reading from it. * @param port Midi port to open @@ -50,7 +235,12 @@ * @return 1 on success, -1 on error */ int writeOutput(midi_byte_t* bytes, unsigned int length); - + /** + * Gives access to the midi parser, if it has been activated. + * + * @return a pointer to the midi parser if active, zero otherwise + */ + MidiParser* getMidiParser(); virtual ~Midi(); static void midiInputLoop(); static void midiOutputLoop(); @@ -67,6 +257,8 @@ std::vector outputBytes; unsigned int outputBytesWritePointer; unsigned int outputBytesReadPointer; + MidiParser* inputParser; + bool parserEnabled; static std::vector objAddrs[2]; static AuxiliaryTask midiInputTask; static AuxiliaryTask midiOutputTask; diff -r 0029562391b1 -r 265a527f8be8 projects/basic_midi/render.cpp --- a/projects/basic_midi/render.cpp Thu Feb 04 18:41:30 2016 +0000 +++ b/projects/basic_midi/render.cpp Fri Feb 05 06:16:35 2016 +0000 @@ -25,6 +25,7 @@ { midi.readFrom(0); midi.writeTo(0); + midi.enableParser(true); if(context->analogFrames == 0) { rt_printf("Error: this example needs the matrix enabled\n"); return false; @@ -44,12 +45,14 @@ { static float f0; static float phaseIncrement = 0; - int message; static bool noteOn = 0; static int velocity = 0; static int noteNumber = 0; static int waitingFor = kNoteOn; static int playingNote = -1; +///* + int message; + if(0) // one way of getting the midi data is to parse them yourself (you should set midi.enableParser(false) above): while ((message = midi.getInput()) >= 0){ rt_printf("%d\n", message); switch(waitingFor){ @@ -88,6 +91,14 @@ break; } } +//*/ + int num; + //alternatively, you can use the built-in parser (only processes channel messages at the moment). + while((num = midi.getParser()->numAvailableMessages()) > 0){ + static MidiChannelMessage message; + message = midi.getParser()->getNextChannelMessage(); + message.prettyPrint(); + } for(unsigned int n = 0; n < context->analogFrames; n++){ static int count = 0;