diff --git a/src/MIDI.cpp b/src/MIDI.cpp index 22860aa..a01beb4 100644 --- a/src/MIDI.cpp +++ b/src/MIDI.cpp @@ -3,7 +3,7 @@ * Project Arduino MIDI Library * @brief MIDI Library for the Arduino * @version 4.0 - * @author Francois Best + * @author Francois Best * @date 24/02/11 * license GPL Forty Seven Effects - 2011 */ @@ -37,7 +37,7 @@ BEGIN_MIDI_NAMESPACE /*! \brief Encode System Exclusive messages. SysEx messages are encoded to guarantee transmission of data bytes higher than - 127 without breaking the MIDI protocol. Use this static method to convert the + 127 without breaking the MIDI protocol. Use this static method to convert the data you want to send. \param inData The data to encode. \param outSysEx The output buffer where to store the encoded message. @@ -59,7 +59,7 @@ unsigned encodeSysEx(const byte* inData, byte* outSysEx, unsigned inLength) outSysEx[0] |= (msb << count); outSysEx[1 + count] = body; - + if (count++ == 6) { outSysEx += 8; @@ -73,7 +73,7 @@ unsigned encodeSysEx(const byte* inData, byte* outSysEx, unsigned inLength) /*! \brief Decode System Exclusive messages. SysEx messages are encoded to guarantee transmission of data bytes higher than - 127 without breaking the MIDI protocol. Use this static method to reassemble + 127 without breaking the MIDI protocol. Use this static method to reassemble your received message. \param inSysEx The SysEx data received from MIDI in. \param outData The output buffer where to store the decrypted message. @@ -85,13 +85,13 @@ unsigned decodeSysEx(const byte* inSysEx, byte* outData, unsigned inLength) { unsigned count = 0; byte msbStorage = 0; - + for (unsigned i = 0; i < inLength; ++i) { if ((i % 8) == 0) { msbStorage = inSysEx[i]; - } + } else { outData[count++] = inSysEx[i] | ((msbStorage & 1) << 7); diff --git a/src/MIDI.h b/src/MIDI.h index 68a3dfd..9ba6296 100644 --- a/src/MIDI.h +++ b/src/MIDI.h @@ -3,7 +3,7 @@ * Project Arduino MIDI Library * @brief MIDI Library for the Arduino * @version 4.0 - * @author Francois Best + * @author Francois Best * @date 24/02/11 * license GPL Forty Seven Effects - 2011 */ @@ -33,113 +33,113 @@ class MidiInterface public: MidiInterface(SerialPort& inSerial); ~MidiInterface(); - + public: void begin(Channel inChannel = 1); - + // ------------------------------------------------------------------------- // MIDI Output - + #if MIDI_BUILD_OUTPUT - + public: inline void sendNoteOn(DataByte inNoteNumber, DataByte inVelocity, Channel inChannel); - + inline void sendNoteOff(DataByte inNoteNumber, DataByte inVelocity, Channel inChannel); - + inline void sendProgramChange(DataByte inProgramNumber, Channel inChannel); - + inline void sendControlChange(DataByte inControlNumber, - DataByte inControlValue, + DataByte inControlValue, Channel inChannel); - + inline void sendPitchBend(int inPitchValue, Channel inChannel); inline void sendPitchBend(double inPitchValue, Channel inChannel); - + inline void sendPolyPressure(DataByte inNoteNumber, DataByte inPressure, Channel inChannel); - + inline void sendAfterTouch(DataByte inPressure, Channel inChannel); - - inline void sendSysEx(unsigned int inLength, + + inline void sendSysEx(unsigned inLength, const byte* inArray, - bool inArrayContainsBoundaries = false); - - inline void sendTimeCodeQuarterFrame(DataByte inTypeNibble, + bool inArrayContainsBoundaries = false); + + inline void sendTimeCodeQuarterFrame(DataByte inTypeNibble, DataByte inValuesNibble); inline void sendTimeCodeQuarterFrame(DataByte inData); - - inline void sendSongPosition(unsigned int inBeats); + + inline void sendSongPosition(unsigned inBeats); inline void sendSongSelect(DataByte inSongNumber); inline void sendTuneRequest(); inline void sendRealTime(MidiType inType); - + public: void send(MidiType inType, DataByte inData1, DataByte inData2, Channel inChannel); - + private: inline StatusByte getStatus(MidiType inType, Channel inChannel) const; - + #endif // MIDI_BUILD_OUTPUT - - + + // ------------------------------------------------------------------------- // MIDI Input - + #if MIDI_BUILD_INPUT - + public: bool read(); bool read(Channel inChannel); - + public: inline MidiType getType() const; inline Channel getChannel() const; inline DataByte getData1() const; inline DataByte getData2() const; inline const byte* getSysExArray() const; - inline unsigned int getSysExArrayLength() const; + inline unsigned getSysExArrayLength() const; inline bool check() const; - + public: inline Channel getInputChannel() const; inline void setInputChannel(Channel inChannel); - + public: static inline MidiType getTypeFromStatusByte(byte inStatus); static inline bool isChannelMessage(MidiType inType); - + private: bool inputFilter(Channel inChannel); bool parse(); void resetInput(); - + private: StatusByte mRunningStatus_RX; Channel mInputChannel; - + byte mPendingMessage[3]; // SysEx are dumped into mMessage directly. - unsigned int mPendingMessageExpectedLenght; - unsigned int mPendingMessageIndex; // Extended to unsigned int for larger SysEx payloads. + unsigned mPendingMessageExpectedLenght; + unsigned mPendingMessageIndex; // Extended to unsigned for larger SysEx payloads. Message mMessage; - - + + // ------------------------------------------------------------------------- // Input Callbacks - + #if MIDI_USE_CALLBACKS - + public: inline void setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity)); inline void setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity)); @@ -150,7 +150,7 @@ public: inline void setHandlePitchBend(void (*fptr)(byte channel, int bend)); inline void setHandleSystemExclusive(void (*fptr)(byte * array, byte size)); inline void setHandleTimeCodeQuarterFrame(void (*fptr)(byte data)); - inline void setHandleSongPosition(void (*fptr)(unsigned int beats)); + inline void setHandleSongPosition(void (*fptr)(unsigned beats)); inline void setHandleSongSelect(void (*fptr)(byte songnumber)); inline void setHandleTuneRequest(void (*fptr)(void)); inline void setHandleClock(void (*fptr)(void)); @@ -159,13 +159,13 @@ public: inline void setHandleStop(void (*fptr)(void)); inline void setHandleActiveSensing(void (*fptr)(void)); inline void setHandleSystemReset(void (*fptr)(void)); - + inline void disconnectCallbackFromType(MidiType inType); - + private: - + void launchCallback(); - + void (*mNoteOffCallback)(byte channel, byte note, byte velocity); void (*mNoteOnCallback)(byte channel, byte note, byte velocity); void (*mAfterTouchPolyCallback)(byte channel, byte note, byte velocity); @@ -175,7 +175,7 @@ private: void (*mPitchBendCallback)(byte channel, int); void (*mSystemExclusiveCallback)(byte * array, byte size); void (*mTimeCodeQuarterFrameCallback)(byte data); - void (*mSongPositionCallback)(unsigned int beats); + void (*mSongPositionCallback)(unsigned beats); void (*mSongSelectCallback)(byte songnumber); void (*mTuneRequestCallback)(void); void (*mClockCallback)(void); @@ -184,43 +184,43 @@ private: void (*mStopCallback)(void); void (*mActiveSensingCallback)(void); void (*mSystemResetCallback)(void); - -#endif // MIDI_USE_CALLBACKS - + +#endif // MIDI_USE_CALLBACKS + #endif // MIDI_BUILD_INPUT - - + + // ------------------------------------------------------------------------- // MIDI Soft Thru - + #if MIDI_BUILD_THRU - + public: inline MidiFilterMode getFilterMode() const; inline bool getThruState() const; - + inline void turnThruOn(MidiFilterMode inThruFilterMode = Full); inline void turnThruOff(); inline void setThruFilterMode(MidiFilterMode inThruFilterMode); - - + + private: void thruFilter(byte inChannel); - + private: bool mThruActivated : 1; MidiFilterMode mThruFilterMode : 7; - + #endif // MIDI_BUILD_THRU - + #if MIDI_USE_RUNNING_STATUS - + private: StatusByte mRunningStatus_TX; - + #endif // MIDI_USE_RUNNING_STATUS - + private: SerialPort& mSerial; }; diff --git a/src/MIDI.hpp b/src/MIDI.hpp index f6b02b4..52e2ac4 100644 --- a/src/MIDI.hpp +++ b/src/MIDI.hpp @@ -3,7 +3,7 @@ * Project Arduino MIDI Library * @brief MIDI Library for the Arduino - Inline implementations * @version 4.0 - * @author Francois Best + * @author Francois Best * @date 24/02/11 * license GPL Forty Seven Effects - 2011 */ @@ -40,7 +40,7 @@ MidiInterface::MidiInterface(SerialPort& inSerial) } /*! \brief Destructor for MidiInterface. - + This is not really useful for the Arduino, as it is never called... */ template @@ -51,7 +51,7 @@ MidiInterface::~MidiInterface() // ----------------------------------------------------------------------------- /*! \brief Call the begin method in the setup() function of the Arduino. - + All parameters are set to their default values: - Input channel set to 1 if no value is specified - Full thru mirroring @@ -65,37 +65,37 @@ void MidiInterface::begin(Channel inChannel) #elif defined(FSE_AVR) mSerial. template open(); #endif - + #if MIDI_BUILD_OUTPUT && MIDI_USE_RUNNING_STATUS - + mRunningStatus_TX = InvalidType; - + #endif // MIDI_BUILD_OUTPUT && MIDI_USE_RUNNING_STATUS - - + + #if MIDI_BUILD_INPUT - + mInputChannel = inChannel; mRunningStatus_RX = InvalidType; mPendingMessageIndex = 0; mPendingMessageExpectedLenght = 0; - + mMessage.valid = false; mMessage.type = InvalidType; mMessage.channel = 0; mMessage.data1 = 0; mMessage.data2 = 0; - + #endif // MIDI_BUILD_INPUT - - + + #if (MIDI_BUILD_INPUT && MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU) // Thru - + mThruFilterMode = Full; mThruActivated = true; - + #endif // Thru - + } @@ -114,9 +114,9 @@ void MidiInterface::begin(Channel inChannel) \param inData1 The first data byte. \param inData2 The second data byte (if the message contains only 1 data byte, set this one to 0). - \param inChannel The output channel on which the message will be sent + \param inChannel The output channel on which the message will be sent (values from 1 to 16). Note: you cannot send to OMNI. - + This is an internal method, use it only if you need to send raw data from your code, at your own risks. */ @@ -127,26 +127,26 @@ void MidiInterface::send(MidiType inType, Channel inChannel) { // Then test if channel is valid - if (inChannel >= MIDI_CHANNEL_OFF || - inChannel == MIDI_CHANNEL_OMNI || + if (inChannel >= MIDI_CHANNEL_OFF || + inChannel == MIDI_CHANNEL_OMNI || inType < NoteOff) { - -#if MIDI_USE_RUNNING_STATUS + +#if MIDI_USE_RUNNING_STATUS mRunningStatus_TX = InvalidType; -#endif - +#endif + return; // Don't send anything } - + if (inType <= PitchBend) // Channel messages { // Protection: remove MSBs on data inData1 &= 0x7F; inData2 &= 0x7F; - + const StatusByte status = getStatus(inType, inChannel); - + #if MIDI_USE_RUNNING_STATUS // Check Running Status if (mRunningStatus_TX != status) @@ -159,12 +159,12 @@ void MidiInterface::send(MidiType inType, // Don't care about running status, send the status byte. mSerial.write(status); #endif - + // Then send data mSerial.write(inData1); if (inType != ProgramChange && inType != AfterTouchChannel) mSerial.write(inData2); - + return; } else if (inType >= TuneRequest && inType <= SystemReset) @@ -173,32 +173,32 @@ void MidiInterface::send(MidiType inType, // ----------------------------------------------------------------------------- -/*! \brief Send a Note On message - \param inNoteNumber Pitch value in the MIDI format (0 to 127). - \param inVelocity Note attack velocity (0 to 127). A NoteOn with 0 velocity +/*! \brief Send a Note On message + \param inNoteNumber Pitch value in the MIDI format (0 to 127). + \param inVelocity Note attack velocity (0 to 127). A NoteOn with 0 velocity is considered as a NoteOff. - \param inChannel The channel on which the message will be sent (1 to 16). - - Take a look at the values, names and frequencies of notes here: + \param inChannel The channel on which the message will be sent (1 to 16). + + Take a look at the values, names and frequencies of notes here: http://www.phys.unsw.edu.au/jw/notes.html */ template void MidiInterface::sendNoteOn(DataByte inNoteNumber, DataByte inVelocity, Channel inChannel) -{ +{ send(NoteOn, inNoteNumber, inVelocity, inChannel); } /*! \brief Send a Note Off message - \param inNoteNumber Pitch value in the MIDI format (0 to 127). + \param inNoteNumber Pitch value in the MIDI format (0 to 127). \param inVelocity Release velocity (0 to 127). \param inChannel The channel on which the message will be sent (1 to 16). - + Note: you can send NoteOn with zero velocity to make a NoteOff, this is based on the Running Status principle, to avoid sending status messages and thus sending only NoteOn data. This method will always send a real NoteOff message. - Take a look at the values, names and frequencies of notes here: + Take a look at the values, names and frequencies of notes here: http://www.phys.unsw.edu.au/jw/notes.html */ template @@ -209,7 +209,7 @@ void MidiInterface::sendNoteOff(DataByte inNoteNumber, send(NoteOff, inNoteNumber, inVelocity, inChannel); } -/*! \brief Send a Program Change message +/*! \brief Send a Program Change message \param inProgramNumber The Program to select (0 to 127). \param inChannel The channel on which the message will be sent (1 to 16). */ @@ -220,10 +220,10 @@ void MidiInterface::sendProgramChange(DataByte inProgramNumber, send(ProgramChange, inProgramNumber, 0, inChannel); } -/*! \brief Send a Control Change message - \param inControlNumber The controller number (0 to 127). +/*! \brief Send a Control Change message + \param inControlNumber The controller number (0 to 127). \param inControlValue The value for the specified controller (0 to 127). - \param inChannel The channel on which the message will be sent (1 to 16). + \param inChannel The channel on which the message will be sent (1 to 16). @see MidiControlChangeNumber */ template @@ -237,7 +237,7 @@ void MidiInterface::sendControlChange(DataByte inControlNumber, /*! \brief Send a Polyphonic AfterTouch message (applies to a specified note) \param inNoteNumber The note to apply AfterTouch to (0 to 127). \param inPressure The amount of AfterTouch to apply (0 to 127). - \param inChannel The channel on which the message will be sent (1 to 16). + \param inChannel The channel on which the message will be sent (1 to 16). */ template void MidiInterface::sendPolyPressure(DataByte inNoteNumber, @@ -249,7 +249,7 @@ void MidiInterface::sendPolyPressure(DataByte inNoteNumber, /*! \brief Send a MonoPhonic AfterTouch message (applies to all notes) \param inPressure The amount of AfterTouch to apply to all notes. - \param inChannel The channel on which the message will be sent (1 to 16). + \param inChannel The channel on which the message will be sent (1 to 16). */ template void MidiInterface::sendAfterTouch(DataByte inPressure, @@ -259,8 +259,8 @@ void MidiInterface::sendAfterTouch(DataByte inPressure, } /*! \brief Send a Pitch Bend message using a signed integer value. - \param inPitchValue The amount of bend to send (in a signed integer format), - between MIDI_PITCHBEND_MIN and MIDI_PITCHBEND_MAX, + \param inPitchValue The amount of bend to send (in a signed integer format), + between MIDI_PITCHBEND_MIN and MIDI_PITCHBEND_MAX, center value is 0. \param inChannel The channel on which the message will be sent (1 to 16). */ @@ -268,14 +268,14 @@ template void MidiInterface::sendPitchBend(int inPitchValue, Channel inChannel) { - const unsigned int bend = inPitchValue - MIDI_PITCHBEND_MIN; + const unsigned bend = inPitchValue - MIDI_PITCHBEND_MIN; send(PitchBend, (bend & 0x7F), (bend >> 7) & 0x7F, inChannel); } /*! \brief Send a Pitch Bend message using a floating point value. - \param inPitchValue The amount of bend to send (in a floating point format), - between -1.0f (maximum downwards bend) + \param inPitchValue The amount of bend to send (in a floating point format), + between -1.0f (maximum downwards bend) and +1.0f (max upwards bend), center value is 0.0f. \param inChannel The channel on which the message will be sent (1 to 16). */ @@ -297,32 +297,32 @@ void MidiInterface::sendPitchBend(double inPitchValue, with previous versions of the library. */ template -void MidiInterface::sendSysEx(unsigned int inLength, +void MidiInterface::sendSysEx(unsigned inLength, const byte* inArray, bool inArrayContainsBoundaries) { if (inArrayContainsBoundaries == false) { mSerial.write(0xF0); - - for (unsigned int i = 0; i < inLength; ++i) + + for (unsigned i = 0; i < inLength; ++i) mSerial.write(inArray[i]); - + mSerial.write(0xF7); } else { - for (unsigned int i = 0; i < inLength; ++i) + for (unsigned i = 0; i < inLength; ++i) mSerial.write(inArray[i]); } - + #if MIDI_USE_RUNNING_STATUS mRunningStatus_TX = InvalidType; #endif } -/*! \brief Send a Tune Request message. - +/*! \brief Send a Tune Request message. + When a MIDI unit receives this message, it should tune its oscillators (if equipped with any). */ @@ -332,24 +332,24 @@ void MidiInterface::sendTuneRequest() sendRealTime(TuneRequest); } -/*! \brief Send a MIDI Time Code Quarter Frame. - +/*! \brief Send a MIDI Time Code Quarter Frame. + \param inTypeNibble MTC type \param inValuesNibble MTC data See MIDI Specification for more information. */ template -void MidiInterface::sendTimeCodeQuarterFrame(DataByte inTypeNibble, +void MidiInterface::sendTimeCodeQuarterFrame(DataByte inTypeNibble, DataByte inValuesNibble) { const byte data = ( ((inTypeNibble & 0x07) << 4) | (inValuesNibble & 0x0F) ); sendTimeCodeQuarterFrame(data); } -/*! \brief Send a MIDI Time Code Quarter Frame. - +/*! \brief Send a MIDI Time Code Quarter Frame. + See MIDI Specification for more information. - \param inData if you want to encode directly the nibbles in your program, + \param inData if you want to encode directly the nibbles in your program, you can send the byte here. */ template @@ -357,7 +357,7 @@ void MidiInterface::sendTimeCodeQuarterFrame(DataByte inData) { mSerial.write((byte)TimeCodeQuarterFrame); mSerial.write(inData); - + #if MIDI_USE_RUNNING_STATUS mRunningStatus_TX = InvalidType; #endif @@ -367,12 +367,12 @@ void MidiInterface::sendTimeCodeQuarterFrame(DataByte inData) \param inBeats The number of beats since the start of the song. */ template -void MidiInterface::sendSongPosition(unsigned int inBeats) +void MidiInterface::sendSongPosition(unsigned inBeats) { mSerial.write((byte)SongPosition); mSerial.write(inBeats & 0x7F); mSerial.write((inBeats >> 7) & 0x7F); - + #if MIDI_USE_RUNNING_STATUS mRunningStatus_TX = InvalidType; #endif @@ -384,15 +384,15 @@ void MidiInterface::sendSongSelect(DataByte inSongNumber) { mSerial.write((byte)SongSelect); mSerial.write(inSongNumber & 0x7F); - + #if MIDI_USE_RUNNING_STATUS mRunningStatus_TX = InvalidType; #endif } -/*! \brief Send a Real Time (one byte) message. - - \param inType The available Real Time types are: +/*! \brief Send a Real Time (one byte) message. + + \param inType The available Real Time types are: Start, Stop, Continue, Clock, ActiveSensing and SystemReset. You can also send a Tune Request with this method. @see MidiType @@ -400,12 +400,12 @@ void MidiInterface::sendSongSelect(DataByte inSongNumber) template void MidiInterface::sendRealTime(MidiType inType) { - switch (inType) + switch (inType) { case TuneRequest: // Not really real-time, but one byte anyway. case Clock: case Start: - case Stop: + case Stop: case Continue: case ActiveSensing: case SystemReset: @@ -415,9 +415,9 @@ void MidiInterface::sendRealTime(MidiType inType) // Invalid Real Time marker break; } - - // Do not cancel Running Status for real-time messages as they can be - // interleaved within any message. Though, TuneRequest can be sent here, + + // Do not cancel Running Status for real-time messages as they can be + // interleaved within any message. Though, TuneRequest can be sent here, // and as it is a System Common message, it must reset Running Status. #if MIDI_USE_RUNNING_STATUS if (inType == TuneRequest) mRunningStatus_TX = InvalidType; @@ -449,7 +449,7 @@ StatusByte MidiInterface::getStatus(MidiType inType, */ /*! \brief Read messages from the serial port using the main input channel. - + \return True if a valid message has been stored in the structure, false if not. A valid message is a message that matches the input channel. \n\n If the Thru is enabled and the message matches the filter, @@ -469,23 +469,23 @@ bool MidiInterface::read(Channel inChannel) { if (inChannel >= MIDI_CHANNEL_OFF) return false; // MIDI Input disabled. - + if (parse()) { if (inputFilter(inChannel)) { - + #if (MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU) thruFilter(inChannel); #endif - + #if MIDI_USE_CALLBACKS launchCallback(); #endif return true; } } - + return false; } @@ -496,33 +496,33 @@ template bool MidiInterface::parse() { const byte bytes_available = mSerial.available(); - + if (bytes_available == 0) // No data available. return false; - + // Parsing algorithm: // Get a byte from the serial buffer. // If there is no pending message to be recomposed, start a new one. // - Find type and channel (if pertinent) - // - Look for other bytes in buffer, call parser recursively, + // - Look for other bytes in buffer, call parser recursively, // until the message is assembled or the buffer is empty. - // Else, add the extracted byte to the pending message, and check validity. + // Else, add the extracted byte to the pending message, and check validity. // When the message is done, store it. - + const byte extracted = mSerial.read(); - - if (mPendingMessageIndex == 0) - { + + if (mPendingMessageIndex == 0) + { // Start a new pending message mPendingMessage[0] = extracted; - + // Check for running status first if (isChannelMessage(getTypeFromStatusByte(mRunningStatus_RX))) { // Only these types allow Running Status - - // If the status byte is not received, prepend it + + // If the status byte is not received, prepend it // to the pending message if (extracted < 0x80) { @@ -533,26 +533,26 @@ bool MidiInterface::parse() // Else: well, we received another status byte, // so the running status does not apply here. // It will be updated upon completion of this message. - + if (mPendingMessageIndex >= (mPendingMessageExpectedLenght-1)) { mMessage.type = getTypeFromStatusByte(mPendingMessage[0]); mMessage.channel = (mPendingMessage[0] & 0x0F)+1; mMessage.data1 = mPendingMessage[1]; - + // Save data2 only if applicable if (mPendingMessageExpectedLenght == 3) mMessage.data2 = mPendingMessage[2]; - else + else mMessage.data2 = 0; - + mPendingMessageIndex = 0; mPendingMessageExpectedLenght = 0; mMessage.valid = true; return true; } } - + switch (getTypeFromStatusByte(mPendingMessage[0])) { // 1 byte messages @@ -569,18 +569,18 @@ bool MidiInterface::parse() mMessage.data1 = 0; mMessage.data2 = 0; mMessage.valid = true; - + // \fix Running Status broken when receiving Clock messages. // Do not reset all input attributes, Running Status must remain unchanged. - //resetInput(); - + //resetInput(); + // We still need to reset these mPendingMessageIndex = 0; mPendingMessageExpectedLenght = 0; - + return true; break; - + // 2 bytes messages case ProgramChange: case AfterTouchChannel: @@ -588,7 +588,7 @@ bool MidiInterface::parse() case SongSelect: mPendingMessageExpectedLenght = 2; break; - + // 3 bytes messages case NoteOn: case NoteOff: @@ -598,15 +598,15 @@ bool MidiInterface::parse() case SongPosition: mPendingMessageExpectedLenght = 3; break; - + case SystemExclusive: - // The message can be any lenght + // The message can be any lenght // between 3 and MIDI_SYSEX_ARRAY_SIZE bytes - mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE; + mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE; mRunningStatus_RX = InvalidType; mMessage.sysexArray[0] = SystemExclusive; break; - + case InvalidType: default: // This is obviously wrong. Let's get the hell out'a here. @@ -614,10 +614,10 @@ bool MidiInterface::parse() return false; break; } - + // Then update the index of the pending message. mPendingMessageIndex++; - + #if USE_1BYTE_PARSING // Message is not complete. return false; @@ -626,10 +626,10 @@ bool MidiInterface::parse() // to parse the rest of the message. return parse(); #endif - + } else - { + { // First, test if this is a status byte if (extracted >= 0x80) { @@ -643,38 +643,38 @@ bool MidiInterface::parse() case Stop: case ActiveSensing: case SystemReset: - - // Here we will have to extract the one-byte message, + + // Here we will have to extract the one-byte message, // pass it to the structure for being read outside - // the MIDI class, and recompose the message it was - // interleaved into. Oh, and without killing the running status.. - // This is done by leaving the pending message as is, + // the MIDI class, and recompose the message it was + // interleaved into. Oh, and without killing the running status.. + // This is done by leaving the pending message as is, // it will be completed on next calls. - + mMessage.type = (MidiType)extracted; mMessage.data1 = 0; mMessage.data2 = 0; mMessage.channel = 0; mMessage.valid = true; return true; - + break; - + // End of Exclusive case 0xF7: if (mMessage.sysexArray[0] == SystemExclusive) { // Store the last byte (EOX) mMessage.sysexArray[mPendingMessageIndex++] = 0xF7; - + mMessage.type = SystemExclusive; - + // Get length - mMessage.data1 = mPendingMessageIndex & 0xFF; + mMessage.data1 = mPendingMessageIndex & 0xFF; mMessage.data2 = mPendingMessageIndex >> 8; mMessage.channel = 0; mMessage.valid = true; - + resetInput(); return true; } @@ -684,52 +684,52 @@ bool MidiInterface::parse() resetInput(); return false; } - + break; default: break; } } - + // Add extracted data byte to pending message if (mPendingMessage[0] == SystemExclusive) mMessage.sysexArray[mPendingMessageIndex] = extracted; else mPendingMessage[mPendingMessageIndex] = extracted; - + // Now we are going to check if we have reached the end of the message if (mPendingMessageIndex >= (mPendingMessageExpectedLenght-1)) { // "FML" case: fall down here with an overflown SysEx.. - // This means we received the last possible data byte that can fit + // This means we received the last possible data byte that can fit // the buffer. If this happens, try increasing MIDI_SYSEX_ARRAY_SIZE. if (mPendingMessage[0] == SystemExclusive) { resetInput(); return false; } - + mMessage.type = getTypeFromStatusByte(mPendingMessage[0]); - + if (isChannelMessage(mMessage.type)) mMessage.channel = (mPendingMessage[0] & 0x0F)+1; else mMessage.channel = 0; - + mMessage.data1 = mPendingMessage[1]; - + // Save data2 only if applicable if (mPendingMessageExpectedLenght == 3) mMessage.data2 = mPendingMessage[2]; - else + else mMessage.data2 = 0; - + // Reset local variables mPendingMessageIndex = 0; mPendingMessageExpectedLenght = 0; - + mMessage.valid = true; - + // Activate running status (if enabled for the received type) switch (mMessage.type) { @@ -739,11 +739,11 @@ bool MidiInterface::parse() case ControlChange: case ProgramChange: case AfterTouchChannel: - case PitchBend: + case PitchBend: // Running status enabled: store it from received message mRunningStatus_RX = mPendingMessage[0]; break; - + default: // No running status mRunningStatus_RX = InvalidType; @@ -755,7 +755,7 @@ bool MidiInterface::parse() { // Then update the index of the pending message. mPendingMessageIndex++; - + #if USE_1BYTE_PARSING // Message is not complete. return false; @@ -766,7 +766,7 @@ bool MidiInterface::parse() #endif } } - + // What are our chances to fall here? return false; } @@ -775,28 +775,28 @@ bool MidiInterface::parse() template bool MidiInterface::inputFilter(Channel inChannel) { - // This method handles recognition of channel + // This method handles recognition of channel // (to know if the message is destinated to the Arduino) - + if (mMessage.type == InvalidType) return false; - + // First, check if the received message is Channel if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) { // Then we need to know if we listen to it - if ((mMessage.channel == mInputChannel) || + if ((mMessage.channel == mInputChannel) || (mInputChannel == MIDI_CHANNEL_OMNI)) { return true; } - else + else { // We don't listen to this channel return false; } } - else + else { // System messages are always received return true; @@ -815,7 +815,7 @@ void MidiInterface::resetInput() // ----------------------------------------------------------------------------- /*! \brief Get the last received message's type - + Returns an enumerated type. @see MidiType */ template @@ -825,8 +825,8 @@ MidiType MidiInterface::getType() const } /*! \brief Get the channel of the message stored in the structure. - - \return Channel range is 1 to 16. + + \return Channel range is 1 to 16. For non-channel messages, this will return 0. */ template @@ -845,66 +845,66 @@ DataByte MidiInterface::getData1() const /*! \brief Get the second data byte of the last received message. */ template DataByte MidiInterface::getData2() const -{ +{ return mMessage.data2; } -/*! \brief Get the System Exclusive byte array. - +/*! \brief Get the System Exclusive byte array. + @see getSysExArrayLength to get the array's length in bytes. */ template const byte* MidiInterface::getSysExArray() const -{ +{ return mMessage.sysexArray; } /*! \brief Get the lenght of the System Exclusive array. - + It is coded using data1 as LSB and data2 as MSB. \return The array's length, in bytes. */ template -unsigned int MidiInterface::getSysExArrayLength() const +unsigned MidiInterface::getSysExArrayLength() const { - const unsigned int size = ((unsigned)(mMessage.data2) << 8) | mMessage.data1; + const unsigned size = ((unsigned)(mMessage.data2) << 8) | mMessage.data1; return (size > MIDI_SYSEX_ARRAY_SIZE) ? MIDI_SYSEX_ARRAY_SIZE : size; } /*! \brief Check if a valid message is stored in the structure. */ template bool MidiInterface::check() const -{ +{ return mMessage.valid; } // ----------------------------------------------------------------------------- template -Channel MidiInterface::getInputChannel() const +Channel MidiInterface::getInputChannel() const { return mInputChannel; } -/*! \brief Set the value for the input MIDI channel - \param inChannel the channel value. Valid values are 1 to 16, MIDI_CHANNEL_OMNI +/*! \brief Set the value for the input MIDI channel + \param inChannel the channel value. Valid values are 1 to 16, MIDI_CHANNEL_OMNI if you want to listen to all channels, and MIDI_CHANNEL_OFF to disable input. */ template void MidiInterface::setInputChannel(Channel inChannel) -{ +{ mInputChannel = inChannel; } // ----------------------------------------------------------------------------- /*! \brief Extract an enumerated MIDI type from a status byte. - - This is a utility static method, used internally, + + This is a utility static method, used internally, made public so you can handle MidiTypes more easily. */ template -MidiType MidiInterface::getTypeFromStatusByte(byte inStatus) +MidiType MidiInterface::getTypeFromStatusByte(byte inStatus) { if ((inStatus < 0x80) || (inStatus == 0xF4) || @@ -944,7 +944,7 @@ template void MidiInterface::setHandleAfterTouchCh template void MidiInterface::setHandlePitchBend(void (*fptr)(byte channel, int bend)) { mPitchBendCallback = fptr; } template void MidiInterface::setHandleSystemExclusive(void (*fptr)(byte* array, byte size)) { mSystemExclusiveCallback = fptr; } template void MidiInterface::setHandleTimeCodeQuarterFrame(void (*fptr)(byte data)) { mTimeCodeQuarterFrameCallback = fptr; } -template void MidiInterface::setHandleSongPosition(void (*fptr)(unsigned int beats)) { mSongPositionCallback = fptr; } +template void MidiInterface::setHandleSongPosition(void (*fptr)(unsigned beats)) { mSongPositionCallback = fptr; } template void MidiInterface::setHandleSongSelect(void (*fptr)(byte songnumber)) { mSongSelectCallback = fptr; } template void MidiInterface::setHandleTuneRequest(void (*fptr)(void)) { mTuneRequestCallback = fptr; } template void MidiInterface::setHandleClock(void (*fptr)(void)) { mClockCallback = fptr; } @@ -955,12 +955,12 @@ template void MidiInterface::setHandleActiveSensin template void MidiInterface::setHandleSystemReset(void (*fptr)(void)) { mSystemResetCallback = fptr; } /*! \brief Detach an external function from the given type. - + Use this method to cancel the effects of setHandle********. - \param inType The type of message to unbind. + \param inType The type of message to unbind. When a message of this type is received, no function will be called. */ -template +template void MidiInterface::disconnectCallbackFromType(MidiType inType) { switch (inType) @@ -1000,29 +1000,29 @@ void MidiInterface::launchCallback() // Notes case NoteOff: if (mNoteOffCallback != 0) mNoteOffCallback(mMessage.channel,mMessage.data1,mMessage.data2); break; case NoteOn: if (mNoteOnCallback != 0) mNoteOnCallback(mMessage.channel,mMessage.data1,mMessage.data2); break; - + // Real-time messages - case Clock: if (mClockCallback != 0) mClockCallback(); break; + case Clock: if (mClockCallback != 0) mClockCallback(); break; case Start: if (mStartCallback != 0) mStartCallback(); break; case Continue: if (mContinueCallback != 0) mContinueCallback(); break; case Stop: if (mStopCallback != 0) mStopCallback(); break; case ActiveSensing: if (mActiveSensingCallback != 0) mActiveSensingCallback(); break; - + // Continuous controllers case ControlChange: if (mControlChangeCallback != 0) mControlChangeCallback(mMessage.channel,mMessage.data1,mMessage.data2); break; case PitchBend: if (mPitchBendCallback != 0) mPitchBendCallback(mMessage.channel,(int)((mMessage.data1 & 0x7F) | ((mMessage.data2 & 0x7F)<< 7)) + MIDI_PITCHBEND_MIN); break; // TODO: check this case AfterTouchPoly: if (mAfterTouchPolyCallback != 0) mAfterTouchPolyCallback(mMessage.channel,mMessage.data1,mMessage.data2); break; case AfterTouchChannel: if (mAfterTouchChannelCallback != 0) mAfterTouchChannelCallback(mMessage.channel,mMessage.data1); break; - + case ProgramChange: if (mProgramChangeCallback != 0) mProgramChangeCallback(mMessage.channel,mMessage.data1); break; case SystemExclusive: if (mSystemExclusiveCallback != 0) mSystemExclusiveCallback(mMessage.sysexArray,mMessage.data1); break; - + // Occasional messages case TimeCodeQuarterFrame: if (mTimeCodeQuarterFrameCallback != 0) mTimeCodeQuarterFrameCallback(mMessage.data1); break; case SongPosition: if (mSongPositionCallback != 0) mSongPositionCallback((mMessage.data1 & 0x7F) | ((mMessage.data2 & 0x7F)<< 7)); break; case SongSelect: if (mSongSelectCallback != 0) mSongSelectCallback(mMessage.data1); break; case TuneRequest: if (mTuneRequestCallback != 0) mTuneRequestCallback(); break; - + case SystemReset: if (mSystemResetCallback != 0) mSystemResetCallback(); break; case InvalidType: default: @@ -1049,73 +1049,73 @@ void MidiInterface::launchCallback() /*! \brief Set the filter for thru mirroring \param inThruFilterMode a filter mode - + @see MidiFilterMode */ -template +template void MidiInterface::setThruFilterMode(MidiFilterMode inThruFilterMode) -{ +{ mThruFilterMode = inThruFilterMode; - if (mThruFilterMode != Off) + if (mThruFilterMode != Off) mThruActivated = true; - else + else mThruActivated = false; } -template +template MidiFilterMode MidiInterface::getFilterMode() const { return mThruFilterMode; } -template +template bool MidiInterface::getThruState() const { return mThruActivated; } -template +template void MidiInterface::turnThruOn(MidiFilterMode inThruFilterMode) -{ +{ mThruActivated = true; mThruFilterMode = inThruFilterMode; } -template +template void MidiInterface::turnThruOff() { - mThruActivated = false; + mThruActivated = false; mThruFilterMode = Off; } /*! @} */ // End of doc group MIDI Thru -// This method is called upon reception of a message +// This method is called upon reception of a message // and takes care of Thru filtering and sending. template void MidiInterface::thruFilter(Channel inChannel) { - + /* This method handles Soft-Thru filtering. - + Soft-Thru filtering: - All system messages (System Exclusive, Common and Real Time) are passed to output unless filter is set to Off - Channel messages are passed to the output whether their channel is matching the input channel and the filter setting - + */ - + // If the feature is disabled, don't do anything. if (!mThruActivated || (mThruFilterMode == Off)) return; - - + + // First, check if the received message is Channel if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) { - const bool filter_condition = ((mMessage.channel == mInputChannel) || + const bool filter_condition = ((mMessage.channel == mInputChannel) || (mInputChannel == MIDI_CHANNEL_OMNI)); - + // Now let's pass it to the output switch (mThruFilterMode) { @@ -1147,8 +1147,8 @@ void MidiInterface::thruFilter(Channel inChannel) } break; case Off: - // Do nothing. - // Technically it's impossible to get there because + // Do nothing. + // Technically it's impossible to get there because // the case was already tested earlier. break; default: @@ -1167,27 +1167,27 @@ void MidiInterface::thruFilter(Channel inChannel) case Continue: case ActiveSensing: case SystemReset: - case TuneRequest: + case TuneRequest: sendRealTime(mMessage.type); return; break; - + case SystemExclusive: // Send SysEx (0xF0 and 0xF7 are included in the buffer) - sendSysEx(mMessage.data1,mMessage.sysexArray,true); + sendSysEx(mMessage.data1,mMessage.sysexArray,true); return; break; - + case SongSelect: sendSongSelect(mMessage.data1); return; break; - + case SongPosition: sendSongPosition(mMessage.data1 | ((unsigned)mMessage.data2<<7)); return; break; - + case TimeCodeQuarterFrame: sendTimeCodeQuarterFrame(mMessage.data1,mMessage.data2); return;