giuliomoro@181: /* giuliomoro@181: * Midi.h giuliomoro@181: * giuliomoro@181: * Created on: 15 Jan 2016 giuliomoro@181: * Author: giulio giuliomoro@181: */ giuliomoro@181: giuliomoro@181: #ifndef MIDI_H_ giuliomoro@181: #define MIDI_H_ giuliomoro@181: giuliomoro@181: #include giuliomoro@181: #include giuliomoro@181: giuliomoro@181: typedef unsigned char midi_byte_t; giuliomoro@181: giuliomoro@197: giuliomoro@197: typedef enum midiMessageType{ giuliomoro@197: kmmNoteOff = 0, giuliomoro@197: kmmNoteOn, giuliomoro@197: kmmPolyphonicKeyPressure, giuliomoro@197: kmmControlChange, giuliomoro@197: kmmProgramChange, giuliomoro@197: kmmChannelPressure, giuliomoro@197: kmmPitchBend, giuliomoro@197: kmmNone, giuliomoro@197: kmmAny, giuliomoro@197: } MidiMessageType; giuliomoro@197: #define midiMessageStatusBytesLength 7+2 //2 being kmmNone and kmmAny giuliomoro@197: giuliomoro@197: extern midi_byte_t midiMessageStatusBytes[midiMessageStatusBytesLength]; giuliomoro@197: extern unsigned int midiMessageNumDataBytes[midiMessageStatusBytesLength]; giuliomoro@197: giuliomoro@197: class MidiChannelMessage{ giuliomoro@197: public: giuliomoro@197: MidiChannelMessage(); giuliomoro@197: MidiChannelMessage(MidiMessageType type); giuliomoro@197: virtual ~MidiChannelMessage(); giuliomoro@197: MidiMessageType getType(); giuliomoro@197: int getChannel(); giuliomoro@197: unsigned int getNumDataBytes(){ giuliomoro@197: return midiMessageNumDataBytes[(unsigned int)_type]; giuliomoro@197: } giuliomoro@197: void setDataByte(unsigned int dataByteIndex, midi_byte_t input){ giuliomoro@197: _dataBytes[dataByteIndex] = input; giuliomoro@197: } giuliomoro@197: void setType(MidiMessageType type){ giuliomoro@197: _type = type; giuliomoro@197: _statusByte = midiMessageStatusBytes[_type]; giuliomoro@197: } giuliomoro@197: void setChannel(midi_byte_t channel){ giuliomoro@197: _channel = channel; giuliomoro@197: } giuliomoro@197: midi_byte_t getDataByte(unsigned int index){ giuliomoro@197: return _dataBytes[index]; giuliomoro@197: } giuliomoro@197: void clear(){ giuliomoro@197: for(int n = 0; ngetType()); giuliomoro@197: rt_printf("channel: %u, ", this->getChannel()); giuliomoro@216: for(unsigned int n = 0; n < this->getNumDataBytes(); n++){ giuliomoro@197: rt_printf("data%d: %d, ", n + 1, this->getDataByte(n)); giuliomoro@197: } giuliomoro@197: rt_printf("\n"); giuliomoro@197: } giuliomoro@197: giuliomoro@197: private: giuliomoro@197: const static int maxDataBytes = 2; giuliomoro@197: protected: giuliomoro@197: midi_byte_t _statusByte; giuliomoro@197: midi_byte_t _dataBytes[maxDataBytes]; // where 2 is the maximum number of data bytes for a channel message giuliomoro@197: MidiMessageType _type; giuliomoro@197: midi_byte_t _channel; giuliomoro@197: }; giuliomoro@197: /* giuliomoro@197: class MidiControlChangeMessage : public MidiChannelMessage{ giuliomoro@197: int number; giuliomoro@197: int value; giuliomoro@197: public: giuliomoro@197: int getNumber(); giuliomoro@197: int getNumDataBytes(); giuliomoro@197: int setDataByte(midi_byte_t input, unsigned int dataByteIndex); giuliomoro@197: int getValue(); giuliomoro@197: int set(midi_byte_t* input); giuliomoro@197: }; giuliomoro@197: giuliomoro@197: class MidiNoteMessage : public MidiChannelMessage{ giuliomoro@197: int note; giuliomoro@197: int velocity; giuliomoro@197: public: giuliomoro@197: int getNote(); giuliomoro@197: int getVelocity(); giuliomoro@197: int getNumDataBytes(); giuliomoro@197: int setDataByte(midi_byte_t input, unsigned int dataByteIndex); giuliomoro@197: }; giuliomoro@197: giuliomoro@197: class MidiProgramChangeMessage : public MidiChannelMessage{ giuliomoro@197: midi_byte_t program; giuliomoro@197: public: giuliomoro@197: int getNumDataBytes(); giuliomoro@197: int setDataByte(midi_byte_t input, unsigned int dataByteIndex); giuliomoro@197: midi_byte_t getProgram(); giuliomoro@197: }; giuliomoro@197: */ giuliomoro@197: giuliomoro@197: class MidiParser{ giuliomoro@197: private: giuliomoro@197: std::vector messages; giuliomoro@197: unsigned int writePointer; giuliomoro@197: unsigned int readPointer; giuliomoro@197: unsigned int elapsedDataBytes; giuliomoro@197: bool waitingForStatus; giuliomoro@224: void (*messageReadyCallback)(MidiChannelMessage); giuliomoro@224: bool callbackEnabled; giuliomoro@197: public: giuliomoro@197: MidiParser(){ giuliomoro@197: waitingForStatus = true; giuliomoro@197: elapsedDataBytes= 0; giuliomoro@197: messages.resize(100); // 100 is the number of messages that can be buffered giuliomoro@197: writePointer = 0; giuliomoro@197: readPointer = 0; giuliomoro@224: callbackEnabled = false; giuliomoro@224: messageReadyCallback = NULL; giuliomoro@197: } giuliomoro@197: giuliomoro@197: /** giuliomoro@197: * Parses some midi messages. giuliomoro@197: * giuliomoro@197: * @param input the array to read from giuliomoro@197: * @param length the maximum number of values available at the array giuliomoro@197: * giuliomoro@197: * @return the number of bytes parsed giuliomoro@197: */ giuliomoro@197: int parse(midi_byte_t* input, unsigned int length); giuliomoro@224: giuliomoro@225: /** giuliomoro@225: * Sets the callback to call when a new MidiChannelMessage is available giuliomoro@225: * from the input port. giuliomoro@225: * giuliomoro@225: * In order to deactivate the callback, call this method with NULL as an giuliomoro@225: * argument. giuliomoro@225: * While the callback is enabled, calling numAvailableMessages() and giuliomoro@225: * getNextChannelMessage() is still possible, but it will probably always giuliomoro@225: * return 0 as the callback is called as soon as a new message is available. giuliomoro@225: * giuliomoro@225: * @param newCallback the callback function. This function giuliomoro@225: * should takes one argument: the MidiChannelMessage . giuliomoro@225: * giuliomoro@225: */ giuliomoro@224: void setCallback(void (*newCallback)(MidiChannelMessage)){ giuliomoro@224: messageReadyCallback = newCallback; giuliomoro@224: if(newCallback != NULL){ giuliomoro@224: callbackEnabled = true; giuliomoro@224: } else { giuliomoro@224: callbackEnabled = false; giuliomoro@224: } giuliomoro@197: }; giuliomoro@224: giuliomoro@225: /** giuliomoro@225: * Checks whether there is a callback currently set to be called giuliomoro@225: * every time a new input MidiChannelMessage is available from the giuliomoro@225: * input port. giuliomoro@225: */ giuliomoro@224: bool isCallbackEnabled(){ giuliomoro@224: return callbackEnabled; giuliomoro@224: }; giuliomoro@224: giuliomoro@225: /** giuliomoro@225: * Returns the number of unread MidiChannelMessage available from the giuliomoro@225: * input port. giuliomoro@225: * giuliomoro@225: */ giuliomoro@225: giuliomoro@197: int numAvailableMessages(){ giuliomoro@197: int num = (writePointer - readPointer + messages.size() ) % messages.size(); giuliomoro@197: if(num > 0){ giuliomoro@197: int a = a +1; giuliomoro@224: a = readPointer; giuliomoro@197: } giuliomoro@197: return num; giuliomoro@197: } giuliomoro@225: giuliomoro@197: /** giuliomoro@197: * Get the oldest channel message in the buffer. giuliomoro@197: * giuliomoro@199: * If this method is called when numAvailableMessages()==0, then giuliomoro@199: * a message with all fields set to zero is returned. giuliomoro@225: * giuliomoro@197: * @param type the type of the message to retrieve giuliomoro@225: * giuliomoro@197: * @return a copy of the oldest message of the give type in the buffer giuliomoro@197: */ giuliomoro@197: MidiChannelMessage getNextChannelMessage(/*MidiMessageType type*/){ giuliomoro@197: MidiChannelMessage message; giuliomoro@197: message = messages[readPointer]; giuliomoro@197: if(message.getType() == kmmNone){ giuliomoro@197: message.clear(); giuliomoro@197: } giuliomoro@197: messages[readPointer].setType(kmmNone); // do not use it again giuliomoro@197: readPointer++; giuliomoro@197: if(readPointer == messages.size()){ giuliomoro@197: readPointer = 0; giuliomoro@197: } giuliomoro@197: return message; giuliomoro@197: }; giuliomoro@197: giuliomoro@197: // MidiChannelMessage getNextChannelMessage(){ giuliomoro@197: // getNextChannelMessage(kmmAny); giuliomoro@197: // } giuliomoro@197: // MidiControlChangeMessage* getNextControlChangeMessage(){ giuliomoro@197: // return (MidiControlChangeMessage*)getNextChannelMessage(kmmControlChange); giuliomoro@197: // }; giuliomoro@197: // MidiProgramChangeMessage* getNextProgramChangeMessage(){ giuliomoro@197: // return (MidiProgramChangeMessage*)getNextChannelMessage(kmmProgramChange); giuliomoro@197: // }; giuliomoro@197: // MidiNoteMessage* getNextNoteOnMessage(){ giuliomoro@197: // return (MidiNoteMessage*)getNextChannelMessage(kmmNoteOn); giuliomoro@197: // }; giuliomoro@197: }; giuliomoro@197: giuliomoro@197: giuliomoro@181: class Midi { giuliomoro@181: public: giuliomoro@181: Midi(); giuliomoro@197: giuliomoro@197: /** giuliomoro@197: * Enable the input MidiParser. giuliomoro@197: * giuliomoro@199: * If the parser is enabled, getInput() will return an error code. giuliomoro@197: * Midi messages should instead be retrieved via, e.g.: getMidiParser()->getNextChannelMessage(); giuliomoro@197: * giuliomoro@197: * @param enable true to enable the input MidiParser, false to disable it. giuliomoro@197: */ giuliomoro@197: void enableParser(bool enable); giuliomoro@197: giuliomoro@197: /** giuliomoro@197: * Get access to the input parser in use, if any. giuliomoro@197: * giuliomoro@197: * @return a pointer to the instance of MidiParser, if currently enabled, zero otherwise. giuliomoro@197: */ giuliomoro@197: MidiParser* getParser(); giuliomoro@197: giuliomoro@225: /** giuliomoro@225: * Sets the callback to call when a new MidiChannelMessage is available giuliomoro@225: * from the input port. giuliomoro@225: * giuliomoro@225: * Internally, it calls enableParser() and the MidiParser::setCallback(); giuliomoro@225: * giuliomoro@225: * @param newCallback the callback function. This function giuliomoro@225: * should takes one argument: the MidiChannelMessage . giuliomoro@225: */ giuliomoro@224: void setParserCallback(void (*callback)(MidiChannelMessage)){ giuliomoro@224: // if callback is not NULL, also enable the parser giuliomoro@224: enableParser(callback != NULL); //this needs to be first, as it deletes the parser(if exists) giuliomoro@224: getParser()->setCallback(callback); giuliomoro@224: } giuliomoro@224: giuliomoro@181: /** giuliomoro@181: * Open the specified input Midi port and start reading from it. giuliomoro@181: * @param port Midi port to open giuliomoro@181: * @return 1 on success, -1 on failure giuliomoro@181: */ giuliomoro@181: int readFrom(int port); giuliomoro@181: /** giuliomoro@181: * Open the specified output Midi port and prepares to write to it. giuliomoro@181: * @param port Midi port to open giuliomoro@181: * @return 1 on success, -1 on failure giuliomoro@181: */ giuliomoro@181: int writeTo(int port); giuliomoro@181: giuliomoro@181: /** giuliomoro@191: * Get received midi bytes, one at a time. giuliomoro@181: * @return -1 if no new byte is available, -2 on error, giuliomoro@181: * the oldest not yet retrieved midi byte otherwise giuliomoro@181: */ giuliomoro@181: int getInput(); giuliomoro@181: giuliomoro@181: /** giuliomoro@181: * Writes a Midi byte to the output port giuliomoro@181: * @param byte the Midi byte to write giuliomoro@181: * @return 1 on success, -1 on error giuliomoro@181: */ giuliomoro@181: int writeOutput(midi_byte_t byte); giuliomoro@181: giuliomoro@181: /** giuliomoro@181: * Writes Midi bytes to the output port giuliomoro@181: * @param bytes an array of bytes to be written giuliomoro@181: * @param length number of bytes to write giuliomoro@181: * @return 1 on success, -1 on error giuliomoro@181: */ giuliomoro@191: int writeOutput(midi_byte_t* bytes, unsigned int length); giuliomoro@197: /** giuliomoro@197: * Gives access to the midi parser, if it has been activated. giuliomoro@197: * giuliomoro@197: * @return a pointer to the midi parser if active, zero otherwise giuliomoro@197: */ giuliomoro@197: MidiParser* getMidiParser(); giuliomoro@181: virtual ~Midi(); giuliomoro@181: static void midiInputLoop(); giuliomoro@191: static void midiOutputLoop(); giuliomoro@181: static bool staticConstructed; giuliomoro@181: static void staticConstructor(); giuliomoro@181: private: giuliomoro@199: int _getInput(); giuliomoro@181: void readInputLoop(); giuliomoro@191: void writeOutputLoop(); giuliomoro@181: int outputPort; giuliomoro@181: int inputPort; giuliomoro@181: std::vector inputBytes; giuliomoro@181: unsigned int inputBytesWritePointer; giuliomoro@181: unsigned int inputBytesReadPointer; giuliomoro@181: std::vector outputBytes; giuliomoro@191: unsigned int outputBytesWritePointer; giuliomoro@191: unsigned int outputBytesReadPointer; giuliomoro@197: MidiParser* inputParser; giuliomoro@197: bool parserEnabled; giuliomoro@191: static std::vector objAddrs[2]; giuliomoro@181: static AuxiliaryTask midiInputTask; giuliomoro@181: static AuxiliaryTask midiOutputTask; giuliomoro@181: }; giuliomoro@181: giuliomoro@181: giuliomoro@181: #endif /* MIDI_H_ */