diff --git a/src/MIDI.cpp b/src/MIDI.cpp index dab4704..cafc55a 100644 --- a/src/MIDI.cpp +++ b/src/MIDI.cpp @@ -2,7 +2,7 @@ * @file MIDI.cpp * Project Arduino MIDI Library * @brief MIDI Library for the Arduino - * @version 3.2 + * @version 4.0 * @author Francois Best * @date 24/02/11 * license GPL Forty Seven Effects - 2011 @@ -12,6 +12,7 @@ #include #include "Arduino.h" // If using an old (pre-1.0) version of Arduino, // use WConstants.h instead of Arduino.h + #if MIDI_USE_SOFTWARE_SERIAL // Note: Make sure the following relative path is correct. @@ -26,14 +27,17 @@ SoftwareSerial softSerialClass(MIDI_SOFTSERIAL_RX_PIN, #endif // MIDI_USE_SOFTWARE_SERIAL -/*! \brief Main instance (the class comes pre-instantiated). */ -MIDI_Class MIDI; +#if MIDI_AUTO_INSTANCIATE +midi::MidiInterface MIDI; +#endif -/*! \brief Default constructor for MIDI_Class. */ -MIDI_Class::MIDI_Class() +BEGIN_MIDI_NAMESPACE + +/*! \brief Default constructor for MidiInterface. */ +MidiInterface::MidiInterface() { -#if COMPILE_MIDI_IN && USE_CALLBACKS +#if MIDI_BUILD_INPUT && USE_CALLBACKS // Initialise callbacks to NULL pointer mNoteOffCallback = NULL; @@ -59,11 +63,11 @@ MIDI_Class::MIDI_Class() } -/*! \brief Default destructor for MIDI_Class. +/*! \brief Default destructor for MidiInterface. This is not really useful for the Arduino, as it is never called... */ -MIDI_Class::~MIDI_Class() +MidiInterface::~MidiInterface() { } @@ -75,19 +79,19 @@ MIDI_Class::~MIDI_Class() - Input channel set to 1 if no value is specified - Full thru mirroring */ -void MIDI_Class::begin(const byte inChannel) +void MidiInterface::begin(const byte inChannel) { // Initialise the Serial port MIDI_SERIAL_PORT.begin(MIDI_BAUDRATE); -#if COMPILE_MIDI_OUT && USE_RUNNING_STATUS +#if MIDI_BUILD_OUTPUT && MIDI_USE_RUNNING_STATUS mRunningStatus_TX = InvalidType; -#endif // COMPILE_MIDI_OUT && USE_RUNNING_STATUS +#endif // MIDI_BUILD_OUTPUT && MIDI_USE_RUNNING_STATUS -#if COMPILE_MIDI_IN +#if MIDI_BUILD_INPUT mInputChannel = inChannel; mRunningStatus_RX = InvalidType; @@ -100,10 +104,10 @@ void MIDI_Class::begin(const byte inChannel) mMessage.data1 = 0; mMessage.data2 = 0; -#endif // COMPILE_MIDI_IN +#endif // MIDI_BUILD_INPUT -#if (COMPILE_MIDI_IN && COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) // Thru +#if (MIDI_BUILD_INPUT && MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU) // Thru mThruFilterMode = Full; mThruActivated = true; @@ -117,14 +121,7 @@ void MIDI_Class::begin(const byte inChannel) // MIDI Output // ============================================================================= -#if COMPILE_MIDI_OUT - -// Private method for generating a status byte from channel and type -const byte MIDI_Class::genstatus(const kMIDIType inType, - const byte inChannel) const -{ - return ((byte)inType | ((inChannel-1) & 0x0F)); -} +#if MIDI_BUILD_OUTPUT /*! \brief Generate and send a MIDI message from the values given. @@ -138,7 +135,7 @@ const byte MIDI_Class::genstatus(const kMIDIType inType, This is an internal method, use it only if you need to send raw data from your code, at your own risks. */ -void MIDI_Class::send(kMIDIType type, +void MidiInterface::send(MidiType type, byte data1, byte data2, byte channel) @@ -149,7 +146,7 @@ void MIDI_Class::send(kMIDIType type, type < NoteOff) { -#if USE_RUNNING_STATUS +#if MIDI_USE_RUNNING_STATUS mRunningStatus_TX = InvalidType; #endif @@ -164,7 +161,7 @@ void MIDI_Class::send(kMIDIType type, byte statusbyte = genstatus(type,channel); -#if USE_RUNNING_STATUS +#if MIDI_USE_RUNNING_STATUS // Check Running Status if (mRunningStatus_TX != statusbyte) { @@ -198,7 +195,7 @@ void MIDI_Class::send(kMIDIType type, Take a look at the values, names and frequencies of notes here: http://www.phys.unsw.edu.au/jw/notes.html */ -void MIDI_Class::sendNoteOn(byte NoteNumber, +void MidiInterface::sendNoteOn(byte NoteNumber, byte Velocity, byte Channel) { @@ -217,7 +214,7 @@ void MIDI_Class::sendNoteOn(byte NoteNumber, Take a look at the values, names and frequencies of notes here: http://www.phys.unsw.edu.au/jw/notes.html */ -void MIDI_Class::sendNoteOff(byte NoteNumber, +void MidiInterface::sendNoteOff(byte NoteNumber, byte Velocity, byte Channel) { @@ -229,7 +226,7 @@ void MIDI_Class::sendNoteOff(byte NoteNumber, \param ProgramNumber The Program to select (0 to 127). \param Channel The channel on which the message will be sent (1 to 16). */ -void MIDI_Class::sendProgramChange(byte ProgramNumber, +void MidiInterface::sendProgramChange(byte ProgramNumber, byte Channel) { send(ProgramChange,ProgramNumber,0,Channel); @@ -244,7 +241,7 @@ void MIDI_Class::sendProgramChange(byte ProgramNumber, See the detailed controllers numbers & description here: http://www.somascape.org/midi/tech/spec.html#ctrlnums */ -void MIDI_Class::sendControlChange(byte ControlNumber, +void MidiInterface::sendControlChange(byte ControlNumber, byte ControlValue, byte Channel) { @@ -257,7 +254,7 @@ void MIDI_Class::sendControlChange(byte ControlNumber, \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). */ -void MIDI_Class::sendPolyPressure(byte NoteNumber, +void MidiInterface::sendPolyPressure(byte NoteNumber, byte Pressure, byte Channel) { @@ -269,7 +266,7 @@ void MIDI_Class::sendPolyPressure(byte NoteNumber, \param Pressure The amount of AfterTouch to apply to all notes. \param Channel The channel on which the message will be sent (1 to 16). */ -void MIDI_Class::sendAfterTouch(byte Pressure, +void MidiInterface::sendAfterTouch(byte Pressure, byte Channel) { send(AfterTouchChannel,Pressure,0,Channel); @@ -282,7 +279,7 @@ void MIDI_Class::sendAfterTouch(byte Pressure, center value is 0. \param Channel The channel on which the message will be sent (1 to 16). */ -void MIDI_Class::sendPitchBend(int PitchValue, +void MidiInterface::sendPitchBend(int PitchValue, byte Channel) { const unsigned int bend = PitchValue - MIDI_PITCHBEND_MIN; @@ -296,7 +293,7 @@ void MIDI_Class::sendPitchBend(int PitchValue, and +1.0f (max upwards bend), center value is 0.0f. \param Channel The channel on which the message will be sent (1 to 16). */ -void MIDI_Class::sendPitchBend(double PitchValue, +void MidiInterface::sendPitchBend(double PitchValue, byte Channel) { const int pitchval = PitchValue * MIDI_PITCHBEND_MAX; @@ -313,7 +310,7 @@ void MIDI_Class::sendPitchBend(double PitchValue, default value for ArrayContainsBoundaries is set to 'false' for compatibility with previous versions of the library. */ -void MIDI_Class::sendSysEx(int length, +void MidiInterface::sendSysEx(int length, const byte *const array, bool ArrayContainsBoundaries) { @@ -332,7 +329,7 @@ void MIDI_Class::sendSysEx(int length, MIDI_SERIAL_PORT.write(array[i]); } -#if USE_RUNNING_STATUS +#if MIDI_USE_RUNNING_STATUS mRunningStatus_TX = InvalidType; #endif } @@ -343,7 +340,7 @@ void MIDI_Class::sendSysEx(int length, When a MIDI unit receives this message, it should tune its oscillators (if equipped with any). */ -void MIDI_Class::sendTuneRequest() +void MidiInterface::sendTuneRequest() { sendRealTime(TuneRequest); } @@ -355,7 +352,7 @@ void MIDI_Class::sendTuneRequest() \param ValuesNibble MTC data See MIDI Specification for more information. */ -void MIDI_Class::sendTimeCodeQuarterFrame(byte TypeNibble, byte ValuesNibble) +void MidiInterface::sendTimeCodeQuarterFrame(byte TypeNibble, byte ValuesNibble) { const byte data = ( ((TypeNibble & 0x07) << 4) | (ValuesNibble & 0x0F) ); sendTimeCodeQuarterFrame(data); @@ -368,12 +365,12 @@ void MIDI_Class::sendTimeCodeQuarterFrame(byte TypeNibble, byte ValuesNibble) \param data if you want to encode directly the nibbles in your program, you can send the byte here. */ -void MIDI_Class::sendTimeCodeQuarterFrame(byte data) +void MidiInterface::sendTimeCodeQuarterFrame(byte data) { MIDI_SERIAL_PORT.write((byte)TimeCodeQuarterFrame); MIDI_SERIAL_PORT.write(data); -#if USE_RUNNING_STATUS +#if MIDI_USE_RUNNING_STATUS mRunningStatus_TX = InvalidType; #endif } @@ -382,25 +379,25 @@ void MIDI_Class::sendTimeCodeQuarterFrame(byte data) /*! \brief Send a Song Position Pointer message. \param Beats The number of beats since the start of the song. */ -void MIDI_Class::sendSongPosition(unsigned int Beats) +void MidiInterface::sendSongPosition(unsigned int Beats) { MIDI_SERIAL_PORT.write((byte)SongPosition); MIDI_SERIAL_PORT.write(Beats & 0x7F); MIDI_SERIAL_PORT.write((Beats >> 7) & 0x7F); -#if USE_RUNNING_STATUS +#if MIDI_USE_RUNNING_STATUS mRunningStatus_TX = InvalidType; #endif } /*! \brief Send a Song Select message */ -void MIDI_Class::sendSongSelect(byte SongNumber) +void MidiInterface::sendSongSelect(byte SongNumber) { MIDI_SERIAL_PORT.write((byte)SongSelect); MIDI_SERIAL_PORT.write(SongNumber & 0x7F); -#if USE_RUNNING_STATUS +#if MIDI_USE_RUNNING_STATUS mRunningStatus_TX = InvalidType; #endif } @@ -411,9 +408,9 @@ void MIDI_Class::sendSongSelect(byte SongNumber) \param Type The available Real Time types are: Start, Stop, Continue, Clock, ActiveSensing and SystemReset. You can also send a Tune Request with this method. - @see kMIDIType + @see MidiType */ -void MIDI_Class::sendRealTime(kMIDIType Type) +void MidiInterface::sendRealTime(MidiType Type) { switch (Type) { @@ -434,19 +431,19 @@ void MIDI_Class::sendRealTime(kMIDIType Type) // 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 MIDI_USE_RUNNING_STATUS if (Type == TuneRequest) mRunningStatus_TX = InvalidType; #endif } -#endif // COMPILE_MIDI_OUT +#endif // MIDI_BUILD_OUTPUT // ============================================================================= // MIDI Input // ============================================================================= -#if COMPILE_MIDI_IN +#if MIDI_BUILD_INPUT /*! \brief Read a MIDI message from the serial port using the main input channel (see setInputChannel() for reference). @@ -456,7 +453,7 @@ void MIDI_Class::sendRealTime(kMIDIType Type) If the Thru is enabled and the messages matches the filter, it is sent back on the MIDI output. */ -bool MIDI_Class::read() +bool MidiInterface::read() { return read(mInputChannel); } @@ -465,7 +462,7 @@ bool MIDI_Class::read() /*! \brief Reading/thru-ing method, the same as read() with a given input channel to read on. */ -bool MIDI_Class::read(const byte inChannel) +bool MidiInterface::read(const byte inChannel) { if (inChannel >= MIDI_CHANNEL_OFF) return false; // MIDI Input disabled. @@ -475,7 +472,7 @@ bool MIDI_Class::read(const byte inChannel) if (input_filter(inChannel)) { -#if (COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) +#if (MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU) thru_filter(inChannel); #endif @@ -491,7 +488,7 @@ bool MIDI_Class::read(const byte inChannel) // Private method: MIDI parser -bool MIDI_Class::parse(byte inChannel) +bool MidiInterface::parse(byte inChannel) { const byte bytes_available = MIDI_SERIAL_PORT.available(); @@ -662,7 +659,7 @@ bool MIDI_Class::parse(byte inChannel) // This is done by leaving the pending message as is, // it will be completed on next calls. - mMessage.type = (kMIDIType)extracted; + mMessage.type = (MidiType)extracted; mMessage.data1 = 0; mMessage.data2 = 0; mMessage.channel = 0; @@ -779,7 +776,7 @@ 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) +bool MidiInterface::input_filter(byte inChannel) { // This method handles recognition of channel // (to know if the message is destinated to the Arduino) @@ -811,7 +808,7 @@ bool MIDI_Class::input_filter(byte inChannel) // Private method: reset input attributes -void MIDI_Class::reset_input_attributes() +void MidiInterface::reset_input_attributes() { mPendingMessageIndex = 0; mPendingMessageExpectedLenght = 0; @@ -819,102 +816,30 @@ void MIDI_Class::reset_input_attributes() } -// Getters -/*! \brief Get the last received message's type - - Returns an enumerated type. @see kMIDIType - */ -kMIDIType MIDI_Class::getType() const -{ - return mMessage.type; -} -/*! \brief Get the channel of the message stored in the structure. - - \return Channel range is 1 to 16. - For non-channel messages, this will return 0. - */ -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; -} - - -/*! \brief Get the System Exclusive byte array. - - @see getSysExArrayLength to get the array's length in bytes. - */ -const byte * MIDI_Class::getSysExArray() const -{ - return mMessage.sysex_array; -} - -/*! \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. - */ -unsigned int MIDI_Class::getSysExArrayLength() const -{ - 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; -} - - -// Setters -/*! \brief Set the value for the input MIDI channel - \param Channel 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 MIDI input. - */ -void MIDI_Class::setInputChannel(const byte Channel) -{ - mInputChannel = Channel; -} #if USE_CALLBACKS -void MIDI_Class::setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity)) { mNoteOffCallback = fptr; } -void MIDI_Class::setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity)) { mNoteOnCallback = fptr; } -void MIDI_Class::setHandleAfterTouchPoly(void (*fptr)(byte channel, byte note, byte pressure)) { mAfterTouchPolyCallback = fptr; } -void MIDI_Class::setHandleControlChange(void (*fptr)(byte channel, byte number, byte value)) { mControlChangeCallback = fptr; } -void MIDI_Class::setHandleProgramChange(void (*fptr)(byte channel, byte number)) { mProgramChangeCallback = fptr; } -void MIDI_Class::setHandleAfterTouchChannel(void (*fptr)(byte channel, byte pressure)) { mAfterTouchChannelCallback = fptr; } -void MIDI_Class::setHandlePitchBend(void (*fptr)(byte channel, int bend)) { mPitchBendCallback = fptr; } -void MIDI_Class::setHandleSystemExclusive(void (*fptr)(byte * array, byte size)) { mSystemExclusiveCallback = fptr; } -void MIDI_Class::setHandleTimeCodeQuarterFrame(void (*fptr)(byte data)) { mTimeCodeQuarterFrameCallback = fptr; } -void MIDI_Class::setHandleSongPosition(void (*fptr)(unsigned int beats)) { mSongPositionCallback = fptr; } -void MIDI_Class::setHandleSongSelect(void (*fptr)(byte songnumber)) { mSongSelectCallback = fptr; } -void MIDI_Class::setHandleTuneRequest(void (*fptr)(void)) { mTuneRequestCallback = fptr; } -void MIDI_Class::setHandleClock(void (*fptr)(void)) { mClockCallback = fptr; } -void MIDI_Class::setHandleStart(void (*fptr)(void)) { mStartCallback = fptr; } -void MIDI_Class::setHandleContinue(void (*fptr)(void)) { mContinueCallback = fptr; } -void MIDI_Class::setHandleStop(void (*fptr)(void)) { mStopCallback = fptr; } -void MIDI_Class::setHandleActiveSensing(void (*fptr)(void)) { mActiveSensingCallback = fptr; } -void MIDI_Class::setHandleSystemReset(void (*fptr)(void)) { mSystemResetCallback = fptr; } +void MidiInterface::setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity)) { mNoteOffCallback = fptr; } +void MidiInterface::setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity)) { mNoteOnCallback = fptr; } +void MidiInterface::setHandleAfterTouchPoly(void (*fptr)(byte channel, byte note, byte pressure)) { mAfterTouchPolyCallback = fptr; } +void MidiInterface::setHandleControlChange(void (*fptr)(byte channel, byte number, byte value)) { mControlChangeCallback = fptr; } +void MidiInterface::setHandleProgramChange(void (*fptr)(byte channel, byte number)) { mProgramChangeCallback = fptr; } +void MidiInterface::setHandleAfterTouchChannel(void (*fptr)(byte channel, byte pressure)) { mAfterTouchChannelCallback = fptr; } +void MidiInterface::setHandlePitchBend(void (*fptr)(byte channel, int bend)) { mPitchBendCallback = fptr; } +void MidiInterface::setHandleSystemExclusive(void (*fptr)(byte * array, byte size)) { mSystemExclusiveCallback = fptr; } +void MidiInterface::setHandleTimeCodeQuarterFrame(void (*fptr)(byte data)) { mTimeCodeQuarterFrameCallback = fptr; } +void MidiInterface::setHandleSongPosition(void (*fptr)(unsigned int beats)) { mSongPositionCallback = fptr; } +void MidiInterface::setHandleSongSelect(void (*fptr)(byte songnumber)) { mSongSelectCallback = fptr; } +void MidiInterface::setHandleTuneRequest(void (*fptr)(void)) { mTuneRequestCallback = fptr; } +void MidiInterface::setHandleClock(void (*fptr)(void)) { mClockCallback = fptr; } +void MidiInterface::setHandleStart(void (*fptr)(void)) { mStartCallback = fptr; } +void MidiInterface::setHandleContinue(void (*fptr)(void)) { mContinueCallback = fptr; } +void MidiInterface::setHandleStop(void (*fptr)(void)) { mStopCallback = fptr; } +void MidiInterface::setHandleActiveSensing(void (*fptr)(void)) { mActiveSensingCallback = fptr; } +void MidiInterface::setHandleSystemReset(void (*fptr)(void)) { mSystemResetCallback = fptr; } /*! \brief Detach an external function from the given type. @@ -923,7 +848,7 @@ void MIDI_Class::setHandleSystemReset(void (*fptr)(void)) \param Type The type of message to unbind. When a message of this type is received, no function will be called. */ -void MIDI_Class::disconnectCallbackFromType(kMIDIType Type) +void MidiInterface::disconnectCallbackFromType(MidiType Type) { switch (Type) { @@ -952,7 +877,7 @@ void MIDI_Class::disconnectCallbackFromType(kMIDIType Type) // Private - launch callback function based on received type. -void MIDI_Class::launchCallback() +void MidiInterface::launchCallback() { // The order is mixed to allow frequent messages to trigger their callback faster. switch (mMessage.type) @@ -994,49 +919,18 @@ void MIDI_Class::launchCallback() #endif // USE_CALLBACKS -#endif // COMPILE_MIDI_IN +#endif // MIDI_BUILD_INPUT // ============================================================================= // MIDI Soft Thru // ============================================================================= -#if (COMPILE_MIDI_IN && COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) - -/*! \brief Set the filter for thru mirroring - \param inThruFilterMode a filter mode - - @see kThruFilterMode - */ -void MIDI_Class::setThruFilterMode(kThruFilterMode inThruFilterMode) -{ - mThruFilterMode = inThruFilterMode; - 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; -} - +#if (MIDI_BUILD_INPUT && MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU) // This method is called upon reception of a message // and takes care of Thru filtering and sending. -void MIDI_Class::thru_filter(byte inChannel) +void MidiInterface::thru_filter(byte inChannel) { /* @@ -1142,3 +1036,5 @@ void MIDI_Class::thru_filter(byte inChannel) } #endif // Thru + +END_MIDI_NAMESPACE diff --git a/src/MIDI.h b/src/MIDI.h index 684c2a7..9b344c0 100644 --- a/src/MIDI.h +++ b/src/MIDI.h @@ -2,7 +2,7 @@ * @file MIDI.h * Project Arduino MIDI Library * @brief MIDI Library for the Arduino - * @version 3.2 + * @version 4.0 * @author Francois Best * @date 24/02/11 * license GPL Forty Seven Effects - 2011 @@ -15,313 +15,101 @@ #include "midi_Settings.h" #include "midi_Defs.h" -/* - ############################################################### - # # - # 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. # - # # - ############################################################### +BEGIN_MIDI_NAMESPACE + +/*! \brief The main class for MIDI handling. */ - - -#define COMPILE_MIDI_IN 1 // Set this setting to 1 to use the MIDI input. -#define COMPILE_MIDI_OUT 1 // Set this setting to 1 to use the MIDI output. -#define COMPILE_MIDI_THRU 1 // Set this setting to 1 to use the MIDI Soft Thru feature - // Please note that the Thru will work only when both COMPILE_MIDI_IN and COMPILE_MIDI_OUT set to 1. - - -#define MIDI_SERIAL_PORT Serial // Change the number (to Serial1 for example) if you want - // to use a different serial port for MIDI I/O. - -#define MIDI_USE_SOFTWARE_SERIAL 0 // Set to 1 to use SoftwareSerial instead of native serial ports. -#define MIDI_SOFTSERIAL_RX_PIN 1 // This pin number will be used for MIDI Input -#define MIDI_SOFTSERIAL_TX_PIN 2 // This pin number will be used for MIDI Output. - - -#define USE_RUNNING_STATUS 1 // Running status enables short messages when sending multiple values - // of the same type and channel. - // Set to 0 if you have troubles controlling your hardware. - - -#define USE_CALLBACKS 1 // Set this to 1 if you want to use callback handlers (to bind your functions to the library). - // To use the callbacks, you need to have COMPILE_MIDI_IN set to 1 - -#define USE_1BYTE_PARSING 1 // Each call to MIDI.read will only parse one byte (might be faster). - - -// 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 -#define MIDI_CHANNEL_OFF 17 // and over - -#define MIDI_SYSEX_ARRAY_SIZE 255 // Maximum size is 65535 bytes. - -#define MIDI_PITCHBEND_MIN -8192 -#define MIDI_PITCHBEND_MAX 8191 - - - -/*! Type definition for practical use - (because "unsigned char" is a bit long to write.. ) - */ -typedef uint8_t byte; -typedef uint16_t word; - - -/*! Enumeration of MIDI types */ -enum kMIDIType -{ - NoteOff = 0x80, ///< Note Off - NoteOn = 0x90, ///< Note On - AfterTouchPoly = 0xA0, ///< Polyphonic AfterTouch - ControlChange = 0xB0, ///< Control Change / Channel Mode - ProgramChange = 0xC0, ///< Program Change - AfterTouchChannel = 0xD0, ///< Channel (monophonic) AfterTouch - PitchBend = 0xE0, ///< Pitch Bend - SystemExclusive = 0xF0, ///< System Exclusive - TimeCodeQuarterFrame = 0xF1, ///< System Common - MIDI Time Code Quarter Frame - SongPosition = 0xF2, ///< System Common - Song Position Pointer - SongSelect = 0xF3, ///< System Common - Song Select - TuneRequest = 0xF6, ///< System Common - Tune Request - Clock = 0xF8, ///< System Real Time - Timing Clock - Start = 0xFA, ///< System Real Time - Start - Continue = 0xFB, ///< System Real Time - Continue - Stop = 0xFC, ///< System Real Time - Stop - ActiveSensing = 0xFE, ///< System Real Time - Active Sensing - SystemReset = 0xFF, ///< System Real Time - System Reset - InvalidType = 0x00 ///< For notifying errors -}; - -/*! Enumeration of Thru filter modes */ -enum kThruFilterMode { - Off = 0, ///< Thru disabled (nothing passes through). - Full = 1, ///< Fully enabled Thru (every incoming message is sent back). - SameChannel = 2, ///< Only the messages on the Input Channel will be sent back. - DifferentChannel = 3 ///< All the messages but the ones on the Input Channel will be sent back. -}; - - -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(). - */ -struct midimsg -{ - - /*! The MIDI channel on which the message was recieved. - \n Value goes from 1 to 16. - */ - byte channel; - - /*! The type of the message - (see the define section for types reference) - */ - kMIDIType type; - - /*! The first data byte. - \n Value goes from 0 to 127. - */ - byte data1; - - /*! The second data byte. - If the message is only 2 bytes long, this one is null. - \n Value goes from 0 to 127. - */ - byte data2; - - /*! System Exclusive dedicated byte array. - \n Array length is stocked on 16 bits, - in data1 (LSB) and data2 (MSB) - */ - byte sysex_array[MIDI_SYSEX_ARRAY_SIZE]; - - /*! This boolean indicates if the message is valid or not. - There is no channel consideration here, - validity means the message respects the MIDI norm. - */ - bool valid; - -}; - - - - -/*! \brief The main class for MIDI handling.\n - See member descriptions to know how to use it, - or check out the examples supplied with the library. - */ -class MIDI_Class +//template +class MidiInterface { +public: + MidiInterface(); + ~MidiInterface(); public: - - // ========================================================================= - // Constructor and Destructor - - MIDI_Class(); - ~MIDI_Class(); - - void begin(const byte inChannel = 1); + void begin(byte inChannel = 1); - // ========================================================================= + // ------------------------------------------------------------------------- // MIDI Output -#if COMPILE_MIDI_OUT // Start compilation block - -public: - - void sendNoteOn(byte NoteNumber,byte Velocity,byte Channel); - void sendNoteOff(byte NoteNumber,byte Velocity,byte Channel); - void sendProgramChange(byte ProgramNumber,byte Channel); - void sendControlChange(byte ControlNumber, byte ControlValue,byte Channel); - void sendPitchBend(int PitchValue,byte Channel); - void sendPitchBend(double PitchValue,byte Channel); - void sendPolyPressure(byte NoteNumber,byte Pressure,byte Channel); - void sendAfterTouch(byte Pressure,byte Channel); - void sendSysEx(int length, const byte *const array,bool ArrayContainsBoundaries = false); - void sendTimeCodeQuarterFrame(byte TypeNibble, byte ValuesNibble); - void sendTimeCodeQuarterFrame(byte data); - void sendSongPosition(unsigned int Beats); - void sendSongSelect(byte SongNumber); - void sendTuneRequest(); - void sendRealTime(kMIDIType Type); - - void send(kMIDIType type, byte param1, byte param2, byte channel); - -private: - - const byte genstatus(const kMIDIType inType,const byte inChannel) const; - - - // Attributes -#if USE_RUNNING_STATUS - byte mRunningStatus_TX; -#endif // USE_RUNNING_STATUS - -#endif // COMPILE_MIDI_OUT - - - - // ========================================================================= - // MIDI Input - -#if COMPILE_MIDI_IN // Start compilation block +#if MIDI_BUILD_OUTPUT public: + void sendNoteOn(byte inNoteNumber, + byte inVelocity, + Channel inChannel); + void sendNoteOff(byte inNoteNumber, + byte inVelocity, + Channel inChannel); + + void sendProgramChange(byte inProgramNumber, + Channel inChannel); + + void sendControlChange(byte inControlNumber, + byte inControlValue, + Channel inChannel); + + void sendPitchBend(int inPitchValue, Channel inChannel); + void sendPitchBend(double inPitchValue, Channel inChannel); + + void sendPolyPressure(byte inNoteNumber, + byte inPressure, + Channel inChannel); + + void sendAfterTouch(byte inPressure, + Channel inChannel); + + void sendSysEx(unsigned int inLength, + const byte* inArray, + bool inArrayContainsBoundaries = false); + + void sendTimeCodeQuarterFrame(byte inTypeNibble, + byte inValuesNibble); + void sendTimeCodeQuarterFrame(byte inData); + + void sendSongPosition(unsigned int inBeats); + void sendSongSelect(byte inSongNumber); + void sendTuneRequest(); + void sendRealTime(MidiType inType); + + 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(const byte Channel); + bool read(Channel inChannel); - // Getters - kMIDIType getType() const; - byte getChannel() const; - byte getData1() const; - byte getData2() const; - const byte * getSysExArray() const; - unsigned int getSysExArrayLength() const; - bool check() const; +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 bool check() const; - byte getInputChannel() const - { - return mInputChannel; - } - - // Setters - void setInputChannel(const byte Channel); - - /*! \brief Extract an enumerated MIDI type from a status byte. - - This is a utility static method, used internally, made public so you can handle kMIDITypes more easily. - */ - static inline const kMIDIType getTypeFromStatusByte(const byte inStatus) - { - if ((inStatus < 0x80) || - (inStatus == 0xF4) || - (inStatus == 0xF5) || - (inStatus == 0xF9) || - (inStatus == 0xFD)) return InvalidType; // data bytes and undefined. - if (inStatus < 0xF0) return (kMIDIType)(inStatus & 0xF0); // Channel message, remove channel nibble. - else return (kMIDIType)inStatus; - } +public: + inline Channel getInputChannel() const; + inline void setInputChannel(Channel inChannel); +public: + static inline MidiType getTypeFromStatusByte(const byte inStatus); private: @@ -366,7 +154,7 @@ public: void setHandleActiveSensing(void (*fptr)(void)); void setHandleSystemReset(void (*fptr)(void)); - void disconnectCallbackFromType(kMIDIType Type); + void disconnectCallbackFromType(MidiType Type); private: @@ -393,39 +181,46 @@ private: #endif // USE_CALLBACKS -#endif // COMPILE_MIDI_IN +#endif // MIDI_BUILD_INPUT // ========================================================================= // MIDI Soft Thru -#if (COMPILE_MIDI_IN && COMPILE_MIDI_OUT && COMPILE_MIDI_THRU) +#if (MIDI_BUILD_INPUT && MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU) public: + inline MidiFilterMode getFilterMode() const; + inline bool getThruState() const; - // Getters - kThruFilterMode getFilterMode() const { return mThruFilterMode; } - bool getThruState() const { return mThruActivated; } - - - // Setters - void turnThruOn(kThruFilterMode inThruFilterMode = Full); - void turnThruOff(); - - void setThruFilterMode(const kThruFilterMode inThruFilterMode); + inline void turnThruOn(MidiFilterMode inThruFilterMode = Full); + inline void turnThruOff(); + inline void setThruFilterMode(MidiFilterMode inThruFilterMode); private: void thru_filter(byte inChannel); - bool mThruActivated; - kThruFilterMode mThruFilterMode; + bool mThruActivated : 1; + MidiFilterMode mThruFilterMode : 7; #endif // Thru + + + // Attributes +#if MIDI_USE_RUNNING_STATUS + StatusByte mRunningStatus_TX; +#endif // MIDI_USE_RUNNING_STATUS + + }; -extern MIDI_Class MIDI; +END_MIDI_NAMESPACE -#endif // LIB_MIDI_H_ +#if MIDI_AUTO_INSTANCIATE +extern midi::MidiInterface MIDI; +#endif + +#include "midi_Inline.hpp" diff --git a/src/midi_Defs.h b/src/midi_Defs.h index f2836b7..ca3c027 100644 --- a/src/midi_Defs.h +++ b/src/midi_Defs.h @@ -2,7 +2,7 @@ * @file midi_Defs.h * Project Arduino MIDI Library * @brief MIDI Library for the Arduino - Definitions - * @version 3.5 + * @version 4.0 * @author Francois Best * @date 24/02/11 * license GPL Forty Seven Effects - 2011 @@ -15,23 +15,31 @@ BEGIN_MIDI_NAMESPACE +// ----------------------------------------------------------------------------- + #define MIDI_CHANNEL_OMNI 0 -#define MIDI_CHANNEL_OFF 17 // and over +#define MIDI_CHANNEL_OFF 17 // and over #define MIDI_PITCHBEND_MIN -8192 #define MIDI_PITCHBEND_MAX 8191 +// ----------------------------------------------------------------------------- +// Type definitions -/*! Type definition for practical use - (because "unsigned char" is a bit long to write.. ) - */ typedef uint8_t byte; typedef uint16_t word; +typedef byte StatusByte; +typedef byte DataByte; +typedef byte Channel; +typedef byte FilterMode; + +// ----------------------------------------------------------------------------- /*! Enumeration of MIDI types */ -enum kMIDIType +enum MidiType { + InvalidType = 0x00, ///< For notifying errors NoteOff = 0x80, ///< Note Off NoteOn = 0x90, ///< Note On AfterTouchPoly = 0xA0, ///< Polyphonic AfterTouch @@ -50,19 +58,22 @@ enum kMIDIType Stop = 0xFC, ///< System Real Time - Stop ActiveSensing = 0xFE, ///< System Real Time - Active Sensing SystemReset = 0xFF, ///< System Real Time - System Reset - InvalidType = 0x00 ///< For notifying errors }; +// ----------------------------------------------------------------------------- + /*! Enumeration of Thru filter modes */ -enum kThruFilterMode { +enum MidiFilterMode +{ Off = 0, ///< Thru disabled (nothing passes through). Full = 1, ///< Fully enabled Thru (every incoming message is sent back). SameChannel = 2, ///< Only the messages on the Input Channel will be sent back. - DifferentChannel = 3 ///< All the messages but the ones on the Input Channel will be sent back. + DifferentChannel = 3, ///< All the messages but the ones on the Input Channel will be sent back. }; +// ----------------------------------------------------------------------------- -enum eMIDICCNumber +enum MidiControlChangeNumber { // High resolution Continuous Controllers MSB (+32 for LSB) ---------------- BankSelect = 0, @@ -92,7 +103,7 @@ enum eMIDICCNumber Sostenuto = 66, SoftPedal = 67, Legato = 68, - Hold2 = 69, + Hold = 69, // Low resolution continuous controllers ----------------------------------- SoundController1 = 70, ///< Synth: Sound Variation FX: Exciter On/Off @@ -129,40 +140,41 @@ enum eMIDICCNumber }; +// ----------------------------------------------------------------------------- /*! The midimsg structure contains decoded data of a MIDI message read from the serial port with read() or thru(). */ -struct midimsg +struct Message { /*! The MIDI channel on which the message was recieved. \n Value goes from 1 to 16. */ - byte channel; + Channel channel; /*! The type of the message - (see the define section for types reference) + (see the MidiType enum for types reference) */ - kMIDIType type; + MidiType type; /*! The first data byte. \n Value goes from 0 to 127. */ - byte data1; + DataByte data1; /*! The second data byte. If the message is only 2 bytes long, this one is null. \n Value goes from 0 to 127. */ - byte data2; + DataByte data2; /*! System Exclusive dedicated byte array. \n Array length is stocked on 16 bits, in data1 (LSB) and data2 (MSB) */ - byte sysex_array[MIDI_SYSEX_ARRAY_SIZE]; + DataByte sysex_array[MIDI_SYSEX_ARRAY_SIZE]; /*! This boolean indicates if the message is valid or not. There is no channel consideration here, @@ -172,5 +184,4 @@ struct midimsg }; - END_MIDI_NAMESPACE diff --git a/src/midi_Inline.hpp b/src/midi_Inline.hpp new file mode 100644 index 0000000..ddd2716 --- /dev/null +++ b/src/midi_Inline.hpp @@ -0,0 +1,167 @@ +/*! + * @file midi_Inline.hpp + * Project Arduino MIDI Library + * @brief MIDI Library for the Arduino - Inline implementations + * @version 4.0 + * @author Francois Best + * @date 24/02/11 + * license GPL Forty Seven Effects - 2011 + */ + +#pragma once + +BEGIN_MIDI_NAMESPACE + +#if MIDI_BUILD_OUTPUT + +StatusByte MidiInterface::getStatus(kMIDIType inType, + Channel inChannel) const +{ + return ((byte)inType | ((inChannel - 1) & 0x0F)); +} + +#endif // MIDI_BUILD_OUTPUT + +// ----------------------------------------------------------------------------- + +#if MIDI_BUILD_INPUT + +/*! \brief Get the last received message's type + + Returns an enumerated type. @see MidiType + */ +MidiType MidiInterface::getType() const +{ + return mMessage.type; +} + +/*! \brief Get the channel of the message stored in the structure. + + \return Channel range is 1 to 16. + For non-channel messages, this will return 0. + */ +Channel MidiInterface::getChannel() const +{ + return mMessage.channel; +} + +/*! \brief Get the first data byte of the last received message. */ +DataByte MidiInterface::getData1() const +{ + return mMessage.data1; +} + +/*! \brief Get the second data byte of the last received message. */ +DataByte MidiInterface::getData2() const +{ + return mMessage.data2; +} + +/*! \brief Get the System Exclusive byte array. + + @see getSysExArrayLength to get the array's length in bytes. + */ +const byte* MidiInterface::getSysExArray() const +{ + return mMessage.sysex_array; +} + +/*! \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. + */ +unsigned int MidiInterface::getSysExArrayLength() const +{ + 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 MidiInterface::check() const +{ + return mMessage.valid; +} + +// ----------------------------------------------------------------------------- + +Channel MidiInterface::getInputChannel() const +{ + return mInputChannel; +} + +/*! \brief Set the value for the input MIDI channel + \param Channel 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. +*/ +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, + made public so you can handle MidiTypes more easily. + */ +MidiType MidiInterface::getTypeFromStatusByte(const byte inStatus) +{ + if ((inStatus < 0x80) || + (inStatus == 0xF4) || + (inStatus == 0xF5) || + (inStatus == 0xF9) || + (inStatus == 0xFD)) return InvalidType; // data bytes and undefined. + if (inStatus < 0xF0) return (MidiType)(inStatus & 0xF0); // Channel message, remove channel nibble. + else return (MidiType)inStatus; +} + +#endif // MIDI_BUILD_INPUT + +// ----------------------------------------------------------------------------- + +#if (MIDI_BUILD_INPUT && MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU) + +MidiFilterMode MidiInterface::getFilterMode() const +{ + return mThruFilterMode; +} + +bool MidiInterface::getThruState() const +{ + return mThruActivated; +} + +/*! \brief Setter method: turn message mirroring on. */ +void MidiInterface::turnThruOn(MidiFilterMode inThruFilterMode) +{ + mThruActivated = true; + mThruFilterMode = inThruFilterMode; +} + + +/*! \brief Setter method: turn message mirroring off. */ +void MidiInterface::turnThruOff() +{ + mThruActivated = false; + mThruFilterMode = Off; +} + +/*! \brief Set the filter for thru mirroring + \param inThruFilterMode a filter mode + + @see MidiFilterMode + */ +void MidiInterface::setThruFilterMode(MidiFilterMode inThruFilterMode) +{ + mThruFilterMode = inThruFilterMode; + if (mThruFilterMode != Off) + mThruActivated = true; + else + mThruActivated = false; +} + +#endif // MIDI_BUILD_THRU + +END_MIDI_NAMESPACE diff --git a/src/midi_Namespace.h b/src/midi_Namespace.h index 4ea3e3f..0867ab7 100644 --- a/src/midi_Namespace.h +++ b/src/midi_Namespace.h @@ -2,7 +2,7 @@ * @file midi_Namespace.h * Project Arduino MIDI Library * @brief MIDI Library for the Arduino - Namespace declaration - * @version 3.5 + * @version 4.0 * @author Francois Best * @date 24/02/11 * license GPL Forty Seven Effects - 2011 diff --git a/src/midi_Settings.h b/src/midi_Settings.h index fd51315..f835c2a 100644 --- a/src/midi_Settings.h +++ b/src/midi_Settings.h @@ -27,23 +27,25 @@ BEGIN_MIDI_NAMESPACE // (MIDI in, out, thru), or to 0 to disable the feature and save space. // Note that the Thru can only work if in and out are enabled. -#define MIDI_BUILD_INPUT 1 -#define MIDI_BUILD_OUTPUT 1 -#define MIDI_BUILD_THRU 1 +#define MIDI_BUILD_INPUT 1 +#define MIDI_BUILD_OUTPUT 1 +#define MIDI_BUILD_THRU 1 + +// Create a MIDI object automatically on the port defined with MIDI_SERIAL_PORT. +#define MIDI_AUTO_INSTANCIATE 1 // ----------------------------------------------------------------------------- // Serial port configuration -// Change the number (to Serial1 for example) -// if you want to use a different serial port for MIDI I/O. -#define MIDI_SERIAL_PORT Serial +// Set the default port to use for MIDI. +#define MIDI_SERIAL_PORT Serial // Software serial options -#define MIDI_USE_SOFTWARE_SERIAL 0 +#define MIDI_USE_SOFTWARE_SERIAL 0 #if MIDI_USE_SOFTWARE_SERIAL - #define MIDI_SOFTSERIAL_RX_PIN 1 // Pin number to use for MIDI Input - #define MIDI_SOFTSERIAL_TX_PIN 2 // Pin number to use for MIDI Output. + #define MIDI_SOFTSERIAL_RX_PIN 1 // Pin number to use for MIDI Input + #define MIDI_SOFTSERIAL_TX_PIN 2 // Pin number to use for MIDI Output #endif // ----------------------------------------------------------------------------- @@ -52,11 +54,10 @@ BEGIN_MIDI_NAMESPACE // Running status enables short messages when sending multiple values // of the same type and channel. // Set to 0 if you have troubles controlling your hardware. -#define MIDI_USE_RUNNING_STATUS 1 -#define MIDI_USE_1BYTE_PARSING 1 +#define MIDI_USE_RUNNING_STATUS 1 +#define MIDI_USE_1BYTE_PARSING 1 - -#define MIDI_BAUDRATE 31250 -#define MIDI_SYSEX_ARRAY_SIZE 255 // Maximum size is 65535 bytes. +#define MIDI_BAUDRATE 31250 +#define MIDI_SYSEX_ARRAY_SIZE 255 // Maximum size is 65535 bytes. END_MIDI_NAMESPACE