diff --git a/src/MIDI.cpp b/src/MIDI.cpp index d5981f9..9dbf305 100644 --- a/src/MIDI.cpp +++ b/src/MIDI.cpp @@ -33,8 +33,7 @@ MIDI_Class MIDI; /*! \brief Default constructor for MIDI_Class. */ MIDI_Class::MIDI_Class() { - -#if USE_CALLBACKS +#if COMPILE_MIDI_IN && USE_CALLBACKS // Initialise callbacks to NULL pointer mNoteOffCallback = NULL; @@ -57,7 +56,6 @@ MIDI_Class::MIDI_Class() mSystemResetCallback = NULL; #endif - } @@ -79,20 +77,14 @@ MIDI_Class::~MIDI_Class() */ void MIDI_Class::begin(const byte inChannel) { - // Initialise the Serial port MIDI_SERIAL_PORT.begin(MIDI_BAUDRATE); - -#if COMPILE_MIDI_OUT - -#if USE_RUNNING_STATUS +#if COMPILE_MIDI_OUT && USE_RUNNING_STATUS mRunningStatus_TX = InvalidType; - -#endif // USE_RUNNING_STATUS - -#endif // COMPILE_MIDI_OUT + +#endif // COMPILE_MIDI_OUT && USE_RUNNING_STATUS #if COMPILE_MIDI_IN @@ -131,9 +123,7 @@ void MIDI_Class::begin(const byte inChannel) const byte MIDI_Class::genstatus(const kMIDIType inType, const byte inChannel) const { - return ((byte)inType | ((inChannel-1) & 0x0F)); - } @@ -153,9 +143,11 @@ void MIDI_Class::send(kMIDIType type, byte data2, byte channel) { - // Then test if channel is valid - if (channel >= MIDI_CHANNEL_OFF || channel == MIDI_CHANNEL_OMNI || type < NoteOff) { + if (channel >= MIDI_CHANNEL_OFF || + channel == MIDI_CHANNEL_OMNI || + type < NoteOff) + { #if USE_RUNNING_STATUS mRunningStatus_TX = InvalidType; @@ -164,9 +156,8 @@ void MIDI_Class::send(kMIDIType type, return; // Don't send anything } - if (type <= PitchBend) { - // Channel messages - + if (type <= PitchBend) // Channel messages + { // Protection: remove MSBs on data data1 &= 0x7F; data2 &= 0x7F; @@ -175,7 +166,8 @@ void MIDI_Class::send(kMIDIType type, #if USE_RUNNING_STATUS // Check Running Status - if (mRunningStatus_TX != statusbyte) { + if (mRunningStatus_TX != statusbyte) + { // New message, memorise and send header mRunningStatus_TX = statusbyte; MIDI_SERIAL_PORT.write(mRunningStatus_TX); @@ -187,16 +179,13 @@ void MIDI_Class::send(kMIDIType type, // Then send data MIDI_SERIAL_PORT.write(data1); - if (type != ProgramChange && type != AfterTouchChannel) { + if (type != ProgramChange && type != AfterTouchChannel) MIDI_SERIAL_PORT.write(data2); - } + return; } - if (type >= TuneRequest && type <= SystemReset) { - // System Real-time and 1 byte. - sendRealTime(type); - } - + if (type >= TuneRequest && type <= SystemReset) + sendRealTime(type); // System Real-time and 1 byte. } @@ -213,17 +202,18 @@ void MIDI_Class::sendNoteOn(byte NoteNumber, byte Velocity, byte Channel) { - send(NoteOn,NoteNumber,Velocity,Channel); - } -/*! \brief Send a Note Off message (a real Note Off, not a Note On with null velocity) +/*! \brief Send a Note Off message \param NoteNumber Pitch value in the MIDI format (0 to 127). \param Velocity Release velocity (0 to 127). \param Channel 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: http://www.phys.unsw.edu.au/jw/notes.html */ @@ -231,9 +221,7 @@ void MIDI_Class::sendNoteOff(byte NoteNumber, byte Velocity, byte Channel) { - - send(NoteOff,NoteNumber,Velocity,Channel); - + send(NoteOff,NoteNumber,Velocity,Channel); } @@ -244,9 +232,7 @@ void MIDI_Class::sendNoteOff(byte NoteNumber, void MIDI_Class::sendProgramChange(byte ProgramNumber, byte Channel) { - send(ProgramChange,ProgramNumber,0,Channel); - } @@ -262,13 +248,11 @@ void MIDI_Class::sendControlChange(byte ControlNumber, byte ControlValue, byte Channel) { - send(ControlChange,ControlNumber,ControlValue,Channel); - } -/*! \brief Send a Polyphonic AfterTouch message (applies to only one specified note) +/*! \brief Send a Polyphonic AfterTouch message (applies to a specified note) \param NoteNumber The note to apply AfterTouch to (0 to 127). \param Pressure The amount of AfterTouch to apply (0 to 127). \param Channel The channel on which the message will be sent (1 to 16). @@ -277,9 +261,7 @@ void MIDI_Class::sendPolyPressure(byte NoteNumber, byte Pressure, byte Channel) { - send(AfterTouchPoly,NoteNumber,Pressure,Channel); - } @@ -290,9 +272,7 @@ void MIDI_Class::sendPolyPressure(byte NoteNumber, void MIDI_Class::sendAfterTouch(byte Pressure, byte Channel) { - send(AfterTouchChannel,Pressure,0,Channel); - } @@ -305,11 +285,8 @@ void MIDI_Class::sendAfterTouch(byte Pressure, void MIDI_Class::sendPitchBend(int PitchValue, byte Channel) { - - unsigned int bend = PitchValue - MIDI_PITCHBEND_MIN; - + const unsigned int bend = PitchValue - MIDI_PITCHBEND_MIN; send(PitchBend,(bend & 0x7F),(bend >> 7) & 0x7F,Channel); - } @@ -322,10 +299,8 @@ void MIDI_Class::sendPitchBend(int PitchValue, void MIDI_Class::sendPitchBend(double PitchValue, byte Channel) { - - int pitchval = PitchValue * MIDI_PITCHBEND_MAX; + const int pitchval = PitchValue * MIDI_PITCHBEND_MAX; sendPitchBend(pitchval,Channel); - } @@ -342,34 +317,24 @@ void MIDI_Class::sendSysEx(int length, const byte *const array, bool ArrayContainsBoundaries) { - - if (ArrayContainsBoundaries == false) { - + if (ArrayContainsBoundaries == false) + { MIDI_SERIAL_PORT.write(0xF0); - for (int i=0;i> 7) & 0x7F); @@ -433,21 +391,18 @@ void MIDI_Class::sendSongPosition(unsigned int Beats) #if USE_RUNNING_STATUS mRunningStatus_TX = InvalidType; #endif - } /*! \brief Send a Song Select message */ void MIDI_Class::sendSongSelect(byte SongNumber) { - MIDI_SERIAL_PORT.write((byte)SongSelect); MIDI_SERIAL_PORT.write(SongNumber & 0x7F); #if USE_RUNNING_STATUS mRunningStatus_TX = InvalidType; #endif - } @@ -460,7 +415,8 @@ void MIDI_Class::sendSongSelect(byte SongNumber) */ void MIDI_Class::sendRealTime(kMIDIType Type) { - switch (Type) { + switch (Type) + { case TuneRequest: // Not really real-time, but one byte anyway. case Clock: case Start: @@ -475,12 +431,12 @@ void MIDI_Class::sendRealTime(kMIDIType Type) break; } - // 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. + // 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 USE_RUNNING_STATUS if (Type == TuneRequest) mRunningStatus_TX = InvalidType; #endif - } #endif // COMPILE_MIDI_OUT @@ -502,9 +458,7 @@ void MIDI_Class::sendRealTime(kMIDIType Type) */ bool MIDI_Class::read() { - return read(mInputChannel); - } @@ -513,12 +467,13 @@ bool MIDI_Class::read() */ bool MIDI_Class::read(const byte inChannel) { + if (inChannel >= MIDI_CHANNEL_OFF) + return false; // MIDI Input disabled. - if (inChannel >= MIDI_CHANNEL_OFF) return false; // MIDI Input disabled. - - if (parse(inChannel)) { - - if (input_filter(inChannel)) { + if (parse(inChannel)) + { + if (input_filter(inChannel)) + { #if (COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) thru_filter(inChannel); @@ -527,51 +482,247 @@ bool MIDI_Class::read(const byte inChannel) #if USE_CALLBACKS launchCallback(); #endif - return true; } - } return false; - } // Private method: MIDI parser bool MIDI_Class::parse(byte inChannel) { + const byte bytes_available = MIDI_SERIAL_PORT.available(); - const int bytes_available = MIDI_SERIAL_PORT.available(); - - if (bytes_available <= 0) { + if (bytes_available == 0) // No data available. return false; - } - // If the buffer is full -> Don't Panic! Call the Vogons to destroy it. - if (bytes_available == 128) { - MIDI_SERIAL_PORT.flush(); - } - else { + + /* 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, + until the message is assembled or the buffer is empty. + * Else, add the extracted byte to the pending message, and check validity. + When the message is done, store it. + */ + + const byte extracted = MIDI_SERIAL_PORT.read(); + + if (mPendingMessageIndex == 0) + { + // Start a new pending message + mPendingMessage[0] = extracted; - /* 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, until the message is assembled or the buffer is empty. - * Else, add the extracted byte to the pending message, and check validity. When the message is done, store it. - */ + // Check for running status first + switch (getTypeFromStatusByte(mRunningStatus_RX)) + { + // Only these types allow Running Status: + case NoteOff: + case NoteOn: + case AfterTouchPoly: + case ControlChange: + case ProgramChange: + case AfterTouchChannel: + case PitchBend: + + // If the status byte is not received, prepend it + // to the pending message + if (extracted < 0x80) + { + mPendingMessage[0] = mRunningStatus_RX; + mPendingMessage[1] = extracted; + mPendingMessageIndex = 1; + } + // Else: well, we received another status byte, + // so the running status does not apply here. + // It will be updated upon completion of this message. + + break; + + default: + // No running status + break; + } - const byte extracted = MIDI_SERIAL_PORT.read(); + switch (getTypeFromStatusByte(mPendingMessage[0])) + { + // 1 byte messages + case Start: + case Continue: + case Stop: + case Clock: + case ActiveSensing: + case SystemReset: + case TuneRequest: + // Handle the message type directly here. + mMessage.type = getTypeFromStatusByte(mPendingMessage[0]); + mMessage.channel = 0; + 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. + //reset_input_attributes(); + + // We still need to reset these + mPendingMessageIndex = 0; + mPendingMessageExpectedLenght = 0; + + return true; + break; + + // 2 bytes messages + case ProgramChange: + case AfterTouchChannel: + case TimeCodeQuarterFrame: + case SongSelect: + mPendingMessageExpectedLenght = 2; + break; + + // 3 bytes messages + case NoteOn: + case NoteOff: + case ControlChange: + case PitchBend: + case AfterTouchPoly: + case SongPosition: + mPendingMessageExpectedLenght = 3; + break; + + case SystemExclusive: + // The message can be any lenght + // between 3 and MIDI_SYSEX_ARRAY_SIZE bytes + mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE; + mRunningStatus_RX = InvalidType; + break; + + case InvalidType: + default: + // This is obviously wrong. Let's get the hell out'a here. + reset_input_attributes(); + return false; + break; + } - if (mPendingMessageIndex == 0) { // Start a new pending message - mPendingMessage[0] = extracted; + // Then update the index of the pending message. + mPendingMessageIndex++; + +#if USE_1BYTE_PARSING + // Message is not complete. + return false; +#else + // Call the parser recursively + // to parse the rest of the message. + return parse(inChannel); +#endif + + } + else + { + // First, test if this is a status byte + if (extracted >= 0x80) + { + // Reception of status bytes in the middle of an uncompleted message + // are allowed only for interleaved Real Time message or EOX + switch (extracted) + { + case Clock: + case Start: + case Continue: + case Stop: + case ActiveSensing: + case SystemReset: + + // 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, + // it will be completed on next calls. + + mMessage.type = (kMIDIType)extracted; + mMessage.data1 = 0; + mMessage.data2 = 0; + mMessage.channel = 0; + mMessage.valid = true; + return true; + + break; + + // End of Exclusive + case 0xF7: + if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) + { + // Store System Exclusive array in midimsg structure + for (byte i=0;i> 8; + mMessage.channel = 0; + mMessage.valid = true; + + reset_input_attributes(); + return true; + } + else + { + // Well well well.. error. + reset_input_attributes(); + return false; + } + + break; + default: + break; + } + } + + // Add extracted data byte to pending message + 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 + // the buffer. If this happens, try increasing MIDI_SYSEX_ARRAY_SIZE. + if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) + { + reset_input_attributes(); + return false; + } - // Check for running status first - switch (getTypeFromStatusByte(mRunningStatus_RX)) { - // Only these types allow Running Status: + mMessage.type = getTypeFromStatusByte(mPendingMessage[0]); + // Don't check if it is a Channel Message + mMessage.channel = (mPendingMessage[0] & 0x0F)+1; + + mMessage.data1 = mPendingMessage[1]; + + // Save data2 only if applicable + if (mPendingMessageExpectedLenght == 3) + mMessage.data2 = mPendingMessage[2]; + 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) + { case NoteOff: case NoteOn: case AfterTouchPoly: @@ -579,83 +730,19 @@ bool MIDI_Class::parse(byte inChannel) case ProgramChange: case AfterTouchChannel: case PitchBend: - - // If the status byte is not received, prepend it to the pending message - if (extracted < 0x80) { - mPendingMessage[0] = mRunningStatus_RX; - mPendingMessage[1] = extracted; - mPendingMessageIndex = 1; - } - // Else: well, we received another status byte, so the running status does not apply here. - // It will be updated upon completion of this message. - + // Running status enabled: store it from received message + mRunningStatus_RX = mPendingMessage[0]; break; default: // No running status - break; - } - - - switch (getTypeFromStatusByte(mPendingMessage[0])) { - - // 1 byte messages - case Start: - case Continue: - case Stop: - case Clock: - case ActiveSensing: - case SystemReset: - case TuneRequest: - // Handle the message type directly here. - mMessage.type = getTypeFromStatusByte(mPendingMessage[0]); - mMessage.channel = 0; - 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. - //reset_input_attributes(); - - // We still need to reset these - mPendingMessageIndex = 0; - mPendingMessageExpectedLenght = 0; - - return true; - break; - - // 2 bytes messages - case ProgramChange: - case AfterTouchChannel: - case TimeCodeQuarterFrame: - case SongSelect: - mPendingMessageExpectedLenght = 2; - break; - - // 3 bytes messages - case NoteOn: - case NoteOff: - case ControlChange: - case PitchBend: - case AfterTouchPoly: - case SongPosition: - mPendingMessageExpectedLenght = 3; - break; - - case SystemExclusive: - mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE; // As the message can be any lenght between 3 and MIDI_SYSEX_ARRAY_SIZE bytes mRunningStatus_RX = InvalidType; break; - - case InvalidType: - default: - // This is obviously wrong. Let's get the hell out'a here. - reset_input_attributes(); - return false; - break; } - + return true; + } + else + { // Then update the index of the pending message. mPendingMessageIndex++; @@ -667,148 +754,7 @@ bool MIDI_Class::parse(byte inChannel) // to parse the rest of the message. return parse(inChannel); #endif - } - else { - - // First, test if this is a status byte - if (extracted >= 0x80) { - - // Reception of status bytes in the middle of an uncompleted message - // are allowed only for interleaved Real Time message or EOX - switch (extracted) { - case Clock: - case Start: - case Continue: - case Stop: - case ActiveSensing: - case SystemReset: - - /* - This is tricky. 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, it will be completed on next calls. - */ - - mMessage.type = (kMIDIType)extracted; - mMessage.data1 = 0; - mMessage.data2 = 0; - mMessage.channel = 0; - mMessage.valid = true; - return true; - - break; - - // End of Exclusive - case 0xF7: - if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) { - - // Store System Exclusive array in midimsg structure - for (byte i=0;i> 8; - - mMessage.channel = 0; - mMessage.valid = true; - - reset_input_attributes(); - - return true; - } - else { - // Well well well.. error. - reset_input_attributes(); - return false; - } - - break; - default: - break; - } - - - - } - - - // Add extracted data byte to pending message - 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 the buffer. - // If this happens, try increasing MIDI_SYSEX_ARRAY_SIZE. - if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) { - reset_input_attributes(); - return false; - } - - - mMessage.type = getTypeFromStatusByte(mPendingMessage[0]); - mMessage.channel = (mPendingMessage[0] & 0x0F)+1; // Don't check if it is a Channel Message - - mMessage.data1 = mPendingMessage[1]; - - // Save data2 only if applicable - if (mPendingMessageExpectedLenght == 3) mMessage.data2 = mPendingMessage[2]; - 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) { - case NoteOff: - case NoteOn: - case AfterTouchPoly: - case ControlChange: - case ProgramChange: - case AfterTouchChannel: - case PitchBend: - // Running status enabled: store it from received message - mRunningStatus_RX = mPendingMessage[0]; - break; - - default: - // No running status - mRunningStatus_RX = InvalidType; - break; - } - return true; - } - else { - // Then update the index of the pending message. - mPendingMessageIndex++; - -#if USE_1BYTE_PARSING - // Message is not complete. - return false; -#else - // Call the parser recursively - // to parse the rest of the message. - return parse(inChannel); -#endif - - } - - } - } // What are our chances to fall here? @@ -819,45 +765,41 @@ bool MIDI_Class::parse(byte inChannel) // Private method: check if the received message is on the listened channel bool MIDI_Class::input_filter(byte inChannel) { + // This method handles recognition of channel + // (to know if the message is destinated to the Arduino) - - // This method handles recognition of channel (to know if the message is destinated to the Arduino) - - - if (mMessage.type == InvalidType) return false; - + if (mMessage.type == InvalidType) + return false; // First, check if the received message is Channel - if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) { - + if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) + { // Then we need to know if we listen to it - if ((mMessage.channel == mInputChannel) || (mInputChannel == MIDI_CHANNEL_OMNI)) { + 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; } - } // Private method: reset input attributes void MIDI_Class::reset_input_attributes() { - mPendingMessageIndex = 0; mPendingMessageExpectedLenght = 0; mRunningStatus_RX = InvalidType; - } @@ -868,9 +810,7 @@ void MIDI_Class::reset_input_attributes() */ kMIDIType MIDI_Class::getType() const { - return mMessage.type; - } @@ -881,27 +821,21 @@ kMIDIType MIDI_Class::getType() const */ byte MIDI_Class::getChannel() const { - return mMessage.channel; - } /*! \brief Get the first data byte of the last received message. */ byte MIDI_Class::getData1() const { - return mMessage.data1; - } /*! \brief Get the second data byte of the last received message. */ byte MIDI_Class::getData2() const { - return mMessage.data2; - } @@ -911,9 +845,7 @@ byte MIDI_Class::getData2() const */ const byte * MIDI_Class::getSysExArray() const { - return mMessage.sysex_array; - } /*! \brief Get the lenght of the System Exclusive array. @@ -923,20 +855,15 @@ const byte * MIDI_Class::getSysExArray() const */ unsigned int MIDI_Class::getSysExArrayLength() const { - - unsigned int coded_size = ((unsigned int)(mMessage.data2) << 8) | mMessage.data1; - - return (coded_size > MIDI_SYSEX_ARRAY_SIZE) ? MIDI_SYSEX_ARRAY_SIZE : coded_size; - + const unsigned int 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. */ bool MIDI_Class::check() const { - return mMessage.valid; - } @@ -948,9 +875,7 @@ bool MIDI_Class::check() const */ void MIDI_Class::setInputChannel(const byte Channel) { - mInputChannel = Channel; - } @@ -984,8 +909,8 @@ void MIDI_Class::setHandleSystemReset(void (*fptr)(void)) */ void MIDI_Class::disconnectCallbackFromType(kMIDIType Type) { - - switch (Type) { + switch (Type) + { case NoteOff: mNoteOffCallback = NULL; break; case NoteOn: mNoteOnCallback = NULL; break; case AfterTouchPoly: mAfterTouchPolyCallback = NULL; break; @@ -1007,17 +932,15 @@ void MIDI_Class::disconnectCallbackFromType(kMIDIType Type) default: break; } - } // Private - launch callback function based on received type. void MIDI_Class::launchCallback() { - // The order is mixed to allow frequent messages to trigger their callback faster. - - switch (mMessage.type) { + switch (mMessage.type) + { // Notes case NoteOff: if (mNoteOffCallback != NULL) mNoteOffCallback(mMessage.channel,mMessage.data1,mMessage.data2); break; case NoteOn: if (mNoteOnCallback != NULL) mNoteOnCallback(mMessage.channel,mMessage.data1,mMessage.data2); break; @@ -1049,7 +972,6 @@ void MIDI_Class::launchCallback() default: break; } - } @@ -1072,31 +994,27 @@ void MIDI_Class::launchCallback() */ void MIDI_Class::setThruFilterMode(kThruFilterMode inThruFilterMode) { - mThruFilterMode = inThruFilterMode; - if (mThruFilterMode != Off) mThruActivated = true; - else mThruActivated = false; - + if (mThruFilterMode != Off) + mThruActivated = true; + else + mThruActivated = false; } /*! \brief Setter method: turn message mirroring on. */ void MIDI_Class::turnThruOn(kThruFilterMode inThruFilterMode) { - mThruActivated = true; mThruFilterMode = inThruFilterMode; - } /*! \brief Setter method: turn message mirroring off. */ void MIDI_Class::turnThruOff() { - mThruActivated = false; mThruFilterMode = Off; - } @@ -1115,45 +1033,60 @@ void MIDI_Class::thru_filter(byte inChannel) */ // If the feature is disabled, don't do anything. - if (!mThruActivated || (mThruFilterMode == Off)) return; + 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) || (mInputChannel == MIDI_CHANNEL_OMNI)); + if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) + { + const bool filter_condition = ((mMessage.channel == mInputChannel) || + (mInputChannel == MIDI_CHANNEL_OMNI)); // Now let's pass it to the output - switch (mThruFilterMode) { + switch (mThruFilterMode) + { case Full: - send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel); + send(mMessage.type, + mMessage.data1, + mMessage.data2, + mMessage.channel); return; break; case SameChannel: - if (filter_condition) { - send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel); + if (filter_condition) + { + send(mMessage.type, + mMessage.data1, + mMessage.data2, + mMessage.channel); return; } break; case DifferentChannel: - if (!filter_condition) { - send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel); + if (!filter_condition) + { + send(mMessage.type, + mMessage.data1, + mMessage.data2, + mMessage.channel); return; } break; case Off: // Do nothing. - // Technically it's impossible to get there because the case was already tested earlier. + // Technically it's impossible to get there because + // the case was already tested earlier. break; default: break; } - } - else { - + else + { // Send the message to the output - switch (mMessage.type) { + switch (mMessage.type) + { // Real Time and 1 byte case Clock: case Start: @@ -1188,14 +1121,8 @@ void MIDI_Class::thru_filter(byte inChannel) break; default: break; - } - } - } - #endif // Thru - - diff --git a/src/MIDI.h b/src/MIDI.h index 039d15d..e17824a 100644 --- a/src/MIDI.h +++ b/src/MIDI.h @@ -15,16 +15,16 @@ /* - ############################################################### - # # - # CONFIGURATION AREA # - # # - # Here are a few settings you can change to customize # - # the library for your own project. You can for example # - # choose to compile only parts of it so you gain flash # - # space and optimise the speed of your sketch. # - # # - ############################################################### + ############################################################### + # # + # CONFIGURATION AREA # + # # + # Here are a few settings you can change to customize # + # the library for your own project. You can for example # + # choose to compile only parts of it so you gain flash # + # space and optimise the speed of your sketch. # + # # + ############################################################### */ @@ -56,6 +56,7 @@ // END OF CONFIGURATION AREA // (do not modify anything under this line unless you know what you are doing) + #define MIDI_BAUDRATE 31250 #define MIDI_CHANNEL_OMNI 0 @@ -76,7 +77,8 @@ typedef uint16_t word; /*! Enumeration of MIDI types */ -enum kMIDIType { +enum kMIDIType +{ NoteOff = 0x80, ///< Note Off NoteOn = 0x90, ///< Note On AfterTouchPoly = 0xA0, ///< Polyphonic AfterTouch @@ -98,7 +100,6 @@ enum kMIDIType { InvalidType = 0x00 ///< For notifying errors }; - /*! Enumeration of Thru filter modes */ enum kThruFilterMode { Off = 0, ///< Thru disabled (nothing passes through). @@ -108,6 +109,74 @@ enum kThruFilterMode { }; +enum eMIDICCNumber +{ + // High resolution Continuous Controllers MSB (+32 for LSB) ---------------- + BankSelect = 0, + ModulationWheel = 1, + BreathController = 2, + // CC3 undefined + FootController = 4, + PortamentoTime = 5, + DataEntry = 6, + ChannelVolume = 7, + Balance = 8, + // CC9 undefined + Pan = 10, + ExpressionController = 11, + EffectControl1 = 12, + EffectControl2 = 13, + // CC14 undefined + // CC15 undefined + GeneralPurposeController1 = 16, + GeneralPurposeController2 = 17, + GeneralPurposeController3 = 18, + GeneralPurposeController4 = 19, + + // Switches ---------------------------------------------------------------- + Sustain = 64, + Portamento = 65, + Sostenuto = 66, + SoftPedal = 67, + Legato = 68, + Hold2 = 69, + + // Low resolution continuous controllers ----------------------------------- + SoundController1 = 70, ///< Synth: Sound Variation FX: Exciter On/Off + SoundController2 = 71, ///< Synth: Harmonic Content FX: Compressor On/Off + SoundController3 = 72, ///< Synth: Release Time FX: Distortion On/Off + SoundController4 = 73, ///< Synth: Attack Time FX: EQ On/Off + SoundController5 = 74, ///< Synth: Brightness FX: Expander On/Off + SoundController6 = 75, ///< Synth: Decay Time FX: Reverb On/Off + SoundController7 = 76, ///< Synth: Vibrato Rate FX: Delay On/Off + SoundController8 = 77, ///< Synth: Vibrato Depth FX: Pitch Transpose On/Off + SoundController9 = 78, ///< Synth: Vibrato Delay FX: Flange/Chorus On/Off + SoundController10 = 79, ///< Synth: Undefined FX: Special Effects On/Off + GeneralPurposeController5 = 80, + GeneralPurposeController6 = 81, + GeneralPurposeController7 = 82, + GeneralPurposeController8 = 83, + PortamentoControl = 84, + // CC85 to CC90 undefined + Effects1 = 91, ///< Reverb send level + Effects2 = 92, ///< Tremolo depth + Effects3 = 93, ///< Chorus send level + Effects4 = 94, ///< Celeste depth + Effects5 = 95, ///< Phaser depth + + // Channel Mode messages --------------------------------------------------- + AllSoundOff = 120, + ResetAllControllers = 121, + LocalControl = 122, + AllNotesOff = 123, + OmniModeOff = 124, + OmniModeOn = 125, + MonoModeOn = 126, + PolyModeOn = 127 +}; + + + /*! The midimsg structure contains decoded data of a MIDI message read from the serial port with read() or thru(). @@ -157,7 +226,7 @@ struct midimsg See member descriptions to know how to use it, or check out the examples supplied with the library. */ -class MIDI_Class +class MIDI_Class { public: @@ -263,9 +332,9 @@ private: byte mRunningStatus_RX; byte mInputChannel; - byte mPendingMessage[MIDI_SYSEX_ARRAY_SIZE]; + byte mPendingMessage[3]; // SysEx are dumped into mMessage directly. unsigned int mPendingMessageExpectedLenght; - unsigned int mPendingMessageIndex; // Extended to unsigned int for larger sysex payloads. + unsigned int mPendingMessageIndex; // Extended to unsigned int for larger SysEx payloads. midimsg mMessage; @@ -350,7 +419,7 @@ private: void thru_filter(byte inChannel); bool mThruActivated; - kThruFilterMode mThruFilterMode; + kThruFilterMode mThruFilterMode; #endif // Thru