diff --git a/res/Examples/MIDI_Basic_IO/MIDI_Basic_IO.ino b/res/Examples/MIDI_Basic_IO/MIDI_Basic_IO.ino index c40a666..f6efee2 100644 --- a/res/Examples/MIDI_Basic_IO/MIDI_Basic_IO.ino +++ b/res/Examples/MIDI_Basic_IO/MIDI_Basic_IO.ino @@ -1,9 +1,11 @@ #include // Simple tutorial on how to receive and send MIDI messages. -// Here, when receiving any message on channel 4, the Arduino +// Here, when receiving any message on channel 4, the Arduino // will blink a led and play back a note for 1 second. +MIDI_CREATE_DEFAULT_INSTANCE(); + #define LED 13 // LED pin on Arduino Uno void setup() diff --git a/res/Examples/MIDI_Callbacks/MIDI_Callbacks.ino b/res/Examples/MIDI_Callbacks/MIDI_Callbacks.ino index b8043e7..642223b 100644 --- a/res/Examples/MIDI_Callbacks/MIDI_Callbacks.ino +++ b/res/Examples/MIDI_Callbacks/MIDI_Callbacks.ino @@ -1,33 +1,39 @@ #include +MIDI_CREATE_DEFAULT_INSTANCE(); + +// ----------------------------------------------------------------------------- + // This function will be automatically called when a NoteOn is received. // It must be a void-returning function with the correct parameters, // see documentation here: // http://arduinomidilib.fortyseveneffects.com/a00022.html -void HandleNoteOn(byte channel, byte pitch, byte velocity) +void handleNoteOn(byte channel, byte pitch, byte velocity) { - // Do whatever you want when you receive a Note On. - - if (velocity == 0) - { - // This acts like a NoteOff. You can ask the library to call the NoteOff - // callback when receiving null-velocity NoteOn messages. - // See MIDI_HANDLE_NULL_VELOCITY_NOTE_ON_AS_NOTE_OFF in midi_Settings.h - } + // Do whatever you want when a note is pressed. // Try to keep your callbacks short (no delays ect) // otherwise it would slow down the loop() and have a bad impact // on real-time performance. } +void handleNoteOff(byte channel, byte pitch, byte velocity) +{ + // Do something when the note is released. + // Note that NoteOn messages with 0 velocity are interpreted as NoteOffs. +} + // ----------------------------------------------------------------------------- void setup() { - // Connect the HandleNoteOn function to the library, + // Connect the handleNoteOn function to the library, // so it is called upon reception of a NoteOn. - MIDI.setHandleNoteOn(HandleNoteOn); // Put only the name of the function + MIDI.setHandleNoteOn(handleNoteOn); // Put only the name of the function + + // Do the same for NoteOffs + MIDI.setHandleNoteOff(handleNoteOff); // Initiate MIDI communications, listen to all channels MIDI.begin(MIDI_CHANNEL_OMNI); diff --git a/res/Examples/MIDI_Input/MIDI_Input.ino b/res/Examples/MIDI_Input/MIDI_Input.ino index e1bf0a9..4bdcb02 100644 --- a/res/Examples/MIDI_Input/MIDI_Input.ino +++ b/res/Examples/MIDI_Input/MIDI_Input.ino @@ -1,5 +1,9 @@ #include +MIDI_CREATE_DEFAULT_INSTANCE(); + +// ----------------------------------------------------------------------------- + // This example shows the old way of checking for input messages. // It's simpler to use the callbacks now, check out the dedicated example. @@ -8,7 +12,7 @@ // ----------------------------------------------------------------------------- void BlinkLed(byte num) // Basic blink function -{ +{ for (byte i=0;i MidiMessage; + private: StatusByte mRunningStatus_RX; + StatusByte mRunningStatus_TX; Channel mInputChannel; byte mPendingMessage[3]; unsigned mPendingMessageExpectedLenght; unsigned mPendingMessageIndex; - Message mMessage; + MidiMessage mMessage; private: inline StatusByte getStatus(MidiType inType, Channel inChannel) const; - - -#if MIDI_USE_RUNNING_STATUS -private: - StatusByte mRunningStatus_TX; -#endif - private: SerialPort& mSerial; }; diff --git a/src/MIDI.hpp b/src/MIDI.hpp index 2ffd715..b73abcc 100644 --- a/src/MIDI.hpp +++ b/src/MIDI.hpp @@ -79,18 +79,10 @@ void MidiInterface::begin(Channel inChannel) mSerial.begin(Settings::BaudRate); #endif -#if MIDI_BUILD_OUTPUT - if (Settings::UseRunningStatus) - { - mRunningStatus_TX = InvalidType; - } -#endif // MIDI_BUILD_OUTPUT - - -#if MIDI_BUILD_INPUT - mInputChannel = inChannel; + mRunningStatus_TX = InvalidType; mRunningStatus_RX = InvalidType; + mPendingMessageIndex = 0; mPendingMessageExpectedLenght = 0; @@ -100,25 +92,14 @@ void MidiInterface::begin(Channel inChannel) 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 - } - // ----------------------------------------------------------------------------- // Output // ----------------------------------------------------------------------------- -#if MIDI_BUILD_OUTPUT - /*! \addtogroup output @{ */ @@ -459,15 +440,10 @@ StatusByte MidiInterface::getStatus(MidiType inType, return ((byte)inType | ((inChannel - 1) & 0x0f)); } -#endif // MIDI_BUILD_OUTPUT - - // ----------------------------------------------------------------------------- // Input // ----------------------------------------------------------------------------- -#if MIDI_BUILD_INPUT - /*! \addtogroup input @{ */ @@ -505,9 +481,7 @@ inline bool MidiInterface::read(Channel inChannel) launchCallback(); } -#if MIDI_BUILD_THRU thruFilter(inChannel); -#endif return channelMatch; } @@ -604,8 +578,8 @@ bool MidiInterface::parse() case SystemExclusive: // The message can be any lenght - // between 3 and MIDI_SYSEX_ARRAY_SIZE bytes - mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE; + // between 3 and MidiMessage::sSysExMaxSize bytes + mPendingMessageExpectedLenght = MidiMessage::sSysExMaxSize; mRunningStatus_RX = InvalidType; mMessage.sysexArray[0] = SystemExclusive; break; @@ -693,8 +667,8 @@ bool MidiInterface::parse() mMessage.type = SystemExclusive; // Get length - mMessage.data1 = mPendingMessageIndex & 0xff; - mMessage.data2 = mPendingMessageIndex >> 8; + mMessage.data1 = mPendingMessageIndex & 0xff; // LSB + mMessage.data2 = mPendingMessageIndex >> 8; // MSB mMessage.channel = 0; mMessage.valid = true; @@ -725,7 +699,7 @@ bool MidiInterface::parse() { // "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. + // the buffer. If this happens, try increasing MidiMessage::sSysExMaxSize. if (mPendingMessage[0] == SystemExclusive) { resetInput(); @@ -852,7 +826,7 @@ inline void MidiInterface::resetInput() Returns an enumerated type. @see MidiType */ template -MidiType MidiInterface::getType() const +inline MidiType MidiInterface::getType() const { return mMessage.type; } @@ -863,21 +837,21 @@ MidiType MidiInterface::getType() const For non-channel messages, this will return 0. */ template -Channel MidiInterface::getChannel() const +inline Channel MidiInterface::getChannel() const { return mMessage.channel; } /*! \brief Get the first data byte of the last received message. */ template -DataByte MidiInterface::getData1() const +inline DataByte MidiInterface::getData1() const { return mMessage.data1; } /*! \brief Get the second data byte of the last received message. */ template -DataByte MidiInterface::getData2() const +inline DataByte MidiInterface::getData2() const { return mMessage.data2; } @@ -887,7 +861,7 @@ DataByte MidiInterface::getData2() const @see getSysExArrayLength to get the array's length in bytes. */ template -const byte* MidiInterface::getSysExArray() const +inline const byte* MidiInterface::getSysExArray() const { return mMessage.sysexArray; } @@ -898,16 +872,14 @@ const byte* MidiInterface::getSysExArray() const \return The array's length, in bytes. */ template -unsigned MidiInterface::getSysExArrayLength() const +inline unsigned MidiInterface::getSysExArrayLength() const { - static const unsigned maxSize = Settings::SysExArraySize; - const unsigned size = (unsigned(mMessage.data2) << 8) | mMessage.data1; - return (size > maxSize) ? maxSize : size; + return mMessage.getSysExSize(); } /*! \brief Check if a valid message is stored in the structure. */ template -bool MidiInterface::check() const +inline bool MidiInterface::check() const { return mMessage.valid; } @@ -915,7 +887,7 @@ bool MidiInterface::check() const // ----------------------------------------------------------------------------- template -Channel MidiInterface::getInputChannel() const +inline Channel MidiInterface::getInputChannel() const { return mInputChannel; } @@ -925,7 +897,7 @@ Channel MidiInterface::getInputChannel() const if you want to listen to all channels, and MIDI_CHANNEL_OFF to disable input. */ template -void MidiInterface::setInputChannel(Channel inChannel) +inline void MidiInterface::setInputChannel(Channel inChannel) { mInputChannel = inChannel; } @@ -980,8 +952,6 @@ bool MidiInterface::isChannelMessage(MidiType inType) // ----------------------------------------------------------------------------- -#if MIDI_USE_CALLBACKS - /*! \addtogroup callbacks @{ */ @@ -993,7 +963,7 @@ template void MidiInterface void MidiInterface::setHandleProgramChange(void (*fptr)(byte channel, byte number)) { mProgramChangeCallback = fptr; } template void MidiInterface::setHandleAfterTouchChannel(void (*fptr)(byte channel, byte pressure)) { mAfterTouchChannelCallback = fptr; } 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::setHandleSystemExclusive(void (*fptr)(byte* array, unsigned size)) { mSystemExclusiveCallback = fptr; } template void MidiInterface::setHandleTimeCodeQuarterFrame(void (*fptr)(byte data)) { mTimeCodeQuarterFrameCallback = fptr; } template void MidiInterface::setHandleSongPosition(void (*fptr)(unsigned beats)) { mSongPositionCallback = fptr; } template void MidiInterface::setHandleSongSelect(void (*fptr)(byte songnumber)) { mSongSelectCallback = fptr; } @@ -1066,7 +1036,7 @@ void MidiInterface::launchCallback() 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; + case SystemExclusive: if (mSystemExclusiveCallback != 0) mSystemExclusiveCallback(mMessage.sysexArray, mMessage.getSysExSize()); break; // Occasional messages case TimeCodeQuarterFrame: if (mTimeCodeQuarterFrameCallback != 0) mTimeCodeQuarterFrameCallback(mMessage.data1); break; @@ -1081,19 +1051,12 @@ void MidiInterface::launchCallback() } } -#endif // MIDI_USE_CALLBACKS - /*! @} */ // End of doc group MIDI Input -#endif // MIDI_BUILD_INPUT - - // ----------------------------------------------------------------------------- // Thru // ----------------------------------------------------------------------------- -#if (MIDI_BUILD_INPUT && MIDI_BUILD_OUTPUT && MIDI_BUILD_THRU) - /*! \addtogroup thru @{ */ @@ -1238,8 +1201,6 @@ void MidiInterface::thruFilter(Channel inChannel) } } -#endif // MIDI_BUILD_THRU - // ----------------------------------------------------------------------------- END_MIDI_NAMESPACE diff --git a/src/midi_Defs.h b/src/midi_Defs.h index 84315be..d9eba5a 100644 --- a/src/midi_Defs.h +++ b/src/midi_Defs.h @@ -24,8 +24,13 @@ #pragma once #include "midi_Namespace.h" -#include "midi_Settings.h" + +#if ARDUINO +#include +#else #include +typedef uint8_t byte; +#endif BEGIN_MIDI_NAMESPACE @@ -40,7 +45,6 @@ BEGIN_MIDI_NAMESPACE // ----------------------------------------------------------------------------- // Type definitions -typedef uint8_t byte; typedef byte StatusByte; typedef byte DataByte; typedef byte Channel; @@ -155,48 +159,6 @@ enum MidiControlChangeNumber PolyModeOn = 127 }; - -// ----------------------------------------------------------------------------- - -/*! The midimsg structure contains decoded data of a MIDI message - read from the serial port with read() - */ -struct Message -{ - /*! The MIDI channel on which the message was recieved. - \n Value goes from 1 to 16. - */ - Channel channel; - - /*! The type of the message - (see the MidiType enum for types reference) - */ - MidiType type; - - /*! The first data byte. - \n Value goes from 0 to 127. - */ - 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. - */ - DataByte data2; - - /*! System Exclusive dedicated byte array. - \n Array length is stocked on 16 bits, - in data1 (LSB) and data2 (MSB) - */ - DataByte sysexArray[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 Create an instance of the library attached to a serial port. @@ -207,6 +169,19 @@ struct Message #define MIDI_CREATE_INSTANCE(Type, SerialPort, Name) \ midi::MidiInterface Name((Type&)SerialPort); +#if defined(ARDUINO_SAM_DUE) || defined(USBCON) + // Leonardo, Due and other USB boards use Serial1 by default. + #define MIDI_CREATE_DEFAULT_INSTANCE() \ + MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI); +#else + /*! \brief Create an instance of the library with default name, serial port + and settings, for compatibility with sketches written with pre-v4.2 MIDI Lib, + or if you don't bother using custom names, serial port or settings. + */ + #define MIDI_CREATE_DEFAULT_INSTANCE() \ + MIDI_CREATE_INSTANCE(HardwareSerial, Serial, MIDI); +#endif + /*! \brief Create an instance of the library attached to a serial port with custom settings. @see DefaultSettings diff --git a/src/midi_Message.h b/src/midi_Message.h new file mode 100644 index 0000000..d0820d8 --- /dev/null +++ b/src/midi_Message.h @@ -0,0 +1,81 @@ +/*! + * @file midi_Message.h + * Project Arduino MIDI Library + * @brief MIDI Library for the Arduino - Message struct definition + * @version 4.1 + * @author Francois Best + * @date 11/06/14 + * @license GPL v3.0 - Copyright Forty Seven Effects 2014 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include "midi_Namespace.h" +#include "midi_Defs.h" + +BEGIN_MIDI_NAMESPACE + +/*! The Message structure contains decoded data of a MIDI message + read from the serial port with read() + */ +template +struct Message +{ + /*! The maximum size for the System Exclusive array. + */ + static const unsigned sSysExMaxSize = SysExMaxSize; + + /*! The MIDI channel on which the message was recieved. + \n Value goes from 1 to 16. + */ + Channel channel; + + /*! The type of the message + (see the MidiType enum for types reference) + */ + MidiType type; + + /*! The first data byte. + \n Value goes from 0 to 127. + */ + 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. + */ + DataByte data2; + + /*! System Exclusive dedicated byte array. + \n Array length is stocked on 16 bits, + in data1 (LSB) and data2 (MSB) + */ + DataByte sysexArray[sSysExMaxSize]; + + /*! 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; + + inline unsigned getSysExSize() const + { + const unsigned size = unsigned(data2) << 8 | data1; + return size > sSysExMaxSize ? sSysExMaxSize : size; + } +}; + +END_MIDI_NAMESPACE diff --git a/src/midi_Settings.h b/src/midi_Settings.h index 85270e8..971c1e0 100644 --- a/src/midi_Settings.h +++ b/src/midi_Settings.h @@ -23,47 +23,16 @@ #pragma once -#include "midi_Namespace.h" - -// ----------------------------------------------------------------------------- - -// 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. - -// ----------------------------------------------------------------------------- +#include "midi_Defs.h" // Compilation flags. Set them to 1 to build the associated feature // (MIDI in, out, thru), or to 0 to disable the feature and save space. // Note that thru can work only if input and output are enabled. -#define MIDI_BUILD_INPUT 1 -#define MIDI_BUILD_OUTPUT 1 -#define MIDI_BUILD_THRU 1 - -#define MIDI_USE_CALLBACKS 1 - -// ----------------------------------------------------------------------------- -// Misc. options - -// 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 - -// NoteOn with 0 velocity should be handled as NoteOf. -// Set to 1 to get NoteOff events when receiving null-velocity NoteOn messages. -// Set to 0 to get NoteOn events when receiving null-velocity NoteOn messages. -#define MIDI_HANDLE_NULL_VELOCITY_NOTE_ON_AS_NOTE_OFF 1 - -// Setting this to 1 will make MIDI.read parse only one byte of data for each -// call when data is available. This can speed up your application if receiving -// a lot of traffic, but might induce MIDI Thru and treatment latency. -#define MIDI_USE_1BYTE_PARSING 1 - -#define MIDI_BAUDRATE 31250 -#define MIDI_SYSEX_ARRAY_SIZE 255 // Maximum size is 65535 bytes. +#define MIDI_BUILD_INPUT 1 +#define MIDI_BUILD_OUTPUT 1 +#define MIDI_BUILD_THRU 1 +#define MIDI_USE_CALLBACKS 1 // ----------------------------------------------------------------------------- @@ -82,11 +51,33 @@ BEGIN_MIDI_NAMESPACE */ struct DefaultSettings { - static const bool UseRunningStatus = true; - static const bool HandleNullVelocityNoteOnAsNoteOff = true; - static const bool Use1ByteParsing = true; - static const unsigned BaudRate = 31250; - static const unsigned SysExArraySize = 128; + /*! Running status enables short messages when sending multiple values + of the same type and channel.\n + Set to 0 if you have troubles controlling your hardware. + */ + static const bool UseRunningStatus = true; + + /* NoteOn with 0 velocity should be handled as NoteOf.\n + Set to 1 to get NoteOff events when receiving null-velocity NoteOn messages.\n + Set to 0 to get NoteOn events when receiving null-velocity NoteOn messages. + */ + static const bool HandleNullVelocityNoteOnAsNoteOff = true; + + // Setting this to 1 will make MIDI.read parse only one byte of data for each + // call when data is available. This can speed up your application if receiving + // a lot of traffic, but might induce MIDI Thru and treatment latency. + static const bool Use1ByteParsing = true; + + /*! Override the default MIDI baudrate to transmit over USB serial, to + a decoding program such as Hairless MIDI (set baudrate to 115200)\n + http://projectgus.github.io/hairless-midiserial/ + */ + static const long BaudRate = 31250; + + /*! Maximum size of SysEx receivable. Decrease to save RAM if you don't expect + to receive SysEx, or adjust accordingly. + */ + static const unsigned SysExMaxSize = 128; }; END_MIDI_NAMESPACE