Compare commits
14 Commits
master
...
feat/v5.1.
| Author | SHA1 | Date |
|---|---|---|
|
|
99fe61c993 | |
|
|
f6d95113f5 | |
|
|
21a4ad23df | |
|
|
12f05866cf | |
|
|
59fafdbf1a | |
|
|
0db6859802 | |
|
|
bb58efec1d | |
|
|
b82f241349 | |
|
|
350f6d1ec4 | |
|
|
f42c344375 | |
|
|
c2a838d8cc | |
|
|
4469f2aa7f | |
|
|
1fe67bec4f | |
|
|
a6c3a48571 |
|
|
@ -31,6 +31,8 @@ jobs:
|
||||||
- leonardo
|
- leonardo
|
||||||
- micro
|
- micro
|
||||||
- nanoatmega328
|
- nanoatmega328
|
||||||
|
- nano_every
|
||||||
|
- nano33ble
|
||||||
- megaatmega2560
|
- megaatmega2560
|
||||||
- teensy2
|
- teensy2
|
||||||
- teensy30
|
- teensy30
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
#include <MIDI.h>
|
||||||
|
USING_NAMESPACE_MIDI
|
||||||
|
|
||||||
|
struct MySerialSettings : public MIDI_NAMESPACE::DefaultSerialSettings
|
||||||
|
{
|
||||||
|
static const long BaudRate = 115200;
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned long t1 = millis();
|
||||||
|
|
||||||
|
MIDI_NAMESPACE::SerialMIDI<HardwareSerial, MySerialSettings> serialMIDI(Serial1);
|
||||||
|
MIDI_NAMESPACE::MidiInterface<MIDI_NAMESPACE::SerialMIDI<HardwareSerial, MySerialSettings>> MIDI((MIDI_NAMESPACE::SerialMIDI<HardwareSerial, MySerialSettings>&)serialMIDI);
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
MIDI.begin(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
MIDI.read();
|
||||||
|
|
||||||
|
// send a note every second
|
||||||
|
if ((millis() - t1) > 1000)
|
||||||
|
{
|
||||||
|
t1 = millis();
|
||||||
|
|
||||||
|
MIDI.sendNoteOn(random(1, 127), 55, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
#include <MIDI.h>
|
||||||
|
USING_NAMESPACE_MIDI
|
||||||
|
|
||||||
|
struct MyMIDISettings : public MIDI_NAMESPACE::DefaultSettings
|
||||||
|
{
|
||||||
|
// When setting UseReceiverActiveSensing to true, MIDI.read() *must* be called
|
||||||
|
// as often as possible (1000 / SenderActiveSensingPeriodicity per second).
|
||||||
|
//
|
||||||
|
// setting UseReceiverActiveSensing to true, adds 174 bytes of code.
|
||||||
|
//
|
||||||
|
// (Taken from a Roland MIDI Implementation Owner's manual)
|
||||||
|
// Once an Active Sensing message is received, the unit will begin monitoring
|
||||||
|
// the interval between all subsequent messages. If there is an interval of 420 ms
|
||||||
|
// or longer between messages while monitoring is active, the same processing
|
||||||
|
// as when All Sound Off, All Notes Off,and Reset All Controllers messages are
|
||||||
|
// received will be carried out. The unit will then stopmonitoring the message interval.
|
||||||
|
|
||||||
|
static const bool UseReceiverActiveSensing = true;
|
||||||
|
|
||||||
|
static const uint16_t ReceiverActiveSensingTimeout = 420;
|
||||||
|
};
|
||||||
|
|
||||||
|
MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, Serial1, MIDI, MyMIDISettings);
|
||||||
|
|
||||||
|
void activeSensingTimeoutExceptionHandler(bool active)
|
||||||
|
{
|
||||||
|
if (!active)
|
||||||
|
{
|
||||||
|
MIDI.sendControlChange(AllSoundOff, 0, 1);
|
||||||
|
MIDI.sendControlChange(AllNotesOff, 0, 1);
|
||||||
|
MIDI.sendControlChange(ResetAllControllers, 0, 1);
|
||||||
|
|
||||||
|
digitalWrite(LED_BUILTIN, HIGH);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
digitalWrite(LED_BUILTIN, LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
pinMode(LED_BUILTIN, OUTPUT);
|
||||||
|
digitalWrite(LED_BUILTIN, LOW);
|
||||||
|
|
||||||
|
MIDI.setHandleActiveSensingTimeout(activeSensingTimeoutExceptionHandler);
|
||||||
|
MIDI.begin(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
MIDI.read();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
#include <MIDI.h>
|
||||||
|
USING_NAMESPACE_MIDI
|
||||||
|
|
||||||
|
struct MyMIDISettings : public MIDI_NAMESPACE::DefaultSettings
|
||||||
|
{
|
||||||
|
// When setting UseSenderActiveSensing to true, MIDI.read() *must* be called
|
||||||
|
// as often as possible (1000 / SenderActiveSensingPeriodicity per second).
|
||||||
|
//
|
||||||
|
// setting UseSenderActiveSensing to true, adds 34 bytes of code.
|
||||||
|
//
|
||||||
|
// When using Active Sensing, call MIDI.read(); in the Arduino loop()
|
||||||
|
//
|
||||||
|
// from 'a' MIDI implementation manual: "Sent periodically"
|
||||||
|
// In the example here, a NoteOn is send every 1000ms (1s), ActiveSensing is
|
||||||
|
// send every 250ms after the last command.
|
||||||
|
// Logging the command will look like this:
|
||||||
|
//
|
||||||
|
// ...
|
||||||
|
// A.Sense FE
|
||||||
|
// A.Sense FE
|
||||||
|
// A.Sense FE
|
||||||
|
// NoteOn 90 04 37 [E-2]
|
||||||
|
// A.Sense FE
|
||||||
|
// A.Sense FE
|
||||||
|
// A.Sense FE
|
||||||
|
// NoteOn 90 04 37 [E-2]
|
||||||
|
// A.Sense FE
|
||||||
|
// A.Sense FE
|
||||||
|
// A.Sense FE
|
||||||
|
// NoteOn 90 04 37 [E-2]
|
||||||
|
// ...
|
||||||
|
|
||||||
|
static const bool UseSenderActiveSensing = true;
|
||||||
|
|
||||||
|
static const uint16_t SenderActiveSensingPeriodicity = 250;
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned long t1 = millis();
|
||||||
|
|
||||||
|
MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, Serial1, MIDI, MyMIDISettings);
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
MIDI.begin(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
MIDI.read();
|
||||||
|
|
||||||
|
// send a note every second
|
||||||
|
if ((millis() - t1) > 1000)
|
||||||
|
{
|
||||||
|
t1 = millis();
|
||||||
|
|
||||||
|
MIDI.sendNoteOn(random(1, 127), 55, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
#include <MIDI.h>
|
||||||
|
|
||||||
|
MIDI_CREATE_DEFAULT_INSTANCE();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This example shows how to make MIDI processors.
|
||||||
|
*
|
||||||
|
* The `filter` function defines whether to forward an incoming
|
||||||
|
* MIDI message to the output.
|
||||||
|
*
|
||||||
|
* The `map` function transforms the forwarded message before
|
||||||
|
* it is sent, allowing to change things.
|
||||||
|
*
|
||||||
|
* Here we will transform NoteOn messages into Program Change,
|
||||||
|
* allowing to use a keyboard to change patches on a MIDI device.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool filter(const MIDIMessage& message)
|
||||||
|
{
|
||||||
|
if (message.type == midi::NoteOn)
|
||||||
|
{
|
||||||
|
// Only forward NoteOn messages
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MIDIMessage map(const MIDIMessage& message)
|
||||||
|
{
|
||||||
|
// Make a copy of the message
|
||||||
|
MIDIMessage output(message);
|
||||||
|
if (message.type == midi::NoteOn)
|
||||||
|
{
|
||||||
|
output.type = midi::ProgramChange;
|
||||||
|
output.data2 = 0; // Not needed in ProgramChange
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
MIDI.begin();
|
||||||
|
MIDI.setThruFilter(filter);
|
||||||
|
MIDI.setThruMap(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
MIDI.read();
|
||||||
|
}
|
||||||
|
|
@ -55,14 +55,12 @@ getData1 KEYWORD2
|
||||||
getData2 KEYWORD2
|
getData2 KEYWORD2
|
||||||
getSysExArray KEYWORD2
|
getSysExArray KEYWORD2
|
||||||
getSysExArrayLength KEYWORD2
|
getSysExArrayLength KEYWORD2
|
||||||
getFilterMode KEYWORD2
|
|
||||||
getThruState KEYWORD2
|
getThruState KEYWORD2
|
||||||
getInputChannel KEYWORD2
|
getInputChannel KEYWORD2
|
||||||
check KEYWORD2
|
check KEYWORD2
|
||||||
setInputChannel KEYWORD2
|
setInputChannel KEYWORD2
|
||||||
turnThruOn KEYWORD2
|
turnThruOn KEYWORD2
|
||||||
turnThruOff KEYWORD2
|
turnThruOff KEYWORD2
|
||||||
setThruFilterMode KEYWORD2
|
|
||||||
disconnectCallbackFromType KEYWORD2
|
disconnectCallbackFromType KEYWORD2
|
||||||
setHandleNoteOff KEYWORD2
|
setHandleNoteOff KEYWORD2
|
||||||
setHandleNoteOn KEYWORD2
|
setHandleNoteOn KEYWORD2
|
||||||
|
|
|
||||||
30
src/MIDI.h
30
src/MIDI.h
|
|
@ -212,6 +212,7 @@ private:
|
||||||
|
|
||||||
void (*mMessageCallback)(const MidiMessage& message) = nullptr;
|
void (*mMessageCallback)(const MidiMessage& message) = nullptr;
|
||||||
ErrorCallback mErrorCallback = nullptr;
|
ErrorCallback mErrorCallback = nullptr;
|
||||||
|
ActiveSensingTimeoutCallback mActiveSensingTimeoutCallback = nullptr;
|
||||||
NoteOffCallback mNoteOffCallback = nullptr;
|
NoteOffCallback mNoteOffCallback = nullptr;
|
||||||
NoteOnCallback mNoteOnCallback = nullptr;
|
NoteOnCallback mNoteOnCallback = nullptr;
|
||||||
AfterTouchPolyCallback mAfterTouchPolyCallback = nullptr;
|
AfterTouchPolyCallback mAfterTouchPolyCallback = nullptr;
|
||||||
|
|
@ -236,15 +237,28 @@ private:
|
||||||
// MIDI Soft Thru
|
// MIDI Soft Thru
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline Thru::Mode getFilterMode() const;
|
using ThruFilterCallback = bool (*)(const MidiMessage& inMessage);
|
||||||
inline bool getThruState() const;
|
using ThruMapCallback = MidiMessage (*)(const MidiMessage& inMessage);
|
||||||
|
inline MidiInterface& turnThruOn(ThruFilterCallback fptr = thruOn);
|
||||||
inline MidiInterface& turnThruOn(Thru::Mode inThruFilterMode = Thru::Full);
|
|
||||||
inline MidiInterface& turnThruOff();
|
inline MidiInterface& turnThruOff();
|
||||||
inline MidiInterface& setThruFilterMode(Thru::Mode inThruFilterMode);
|
inline MidiInterface& setThruFilter(ThruFilterCallback fptr)
|
||||||
|
{
|
||||||
|
mThruFilterCallback = fptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
inline MidiInterface& setThruMap(ThruMapCallback fptr)
|
||||||
|
{
|
||||||
|
mThruMapCallback = fptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void thruFilter(byte inChannel);
|
void processThru();
|
||||||
|
static inline bool thruOn(const MidiMessage& inMessage) { (void)inMessage; return true; }
|
||||||
|
static inline bool thruOff(const MidiMessage& inMessage) { (void)inMessage; return false; }
|
||||||
|
static inline MidiMessage thruEcho(const MidiMessage& inMessage) { return inMessage; }
|
||||||
|
ThruFilterCallback mThruFilterCallback;
|
||||||
|
ThruMapCallback mThruMapCallback;
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// MIDI Parsing
|
// MIDI Parsing
|
||||||
|
|
@ -277,13 +291,11 @@ private:
|
||||||
unsigned mPendingMessageIndex;
|
unsigned mPendingMessageIndex;
|
||||||
unsigned mCurrentRpnNumber;
|
unsigned mCurrentRpnNumber;
|
||||||
unsigned mCurrentNrpnNumber;
|
unsigned mCurrentNrpnNumber;
|
||||||
bool mThruActivated : 1;
|
|
||||||
Thru::Mode mThruFilterMode : 7;
|
|
||||||
MidiMessage mMessage;
|
MidiMessage mMessage;
|
||||||
unsigned long mLastMessageSentTime;
|
unsigned long mLastMessageSentTime;
|
||||||
unsigned long mLastMessageReceivedTime;
|
unsigned long mLastMessageReceivedTime;
|
||||||
unsigned long mSenderActiveSensingPeriodicity;
|
unsigned long mSenderActiveSensingPeriodicity;
|
||||||
bool mReceiverActiveSensingActivated;
|
bool mReceiverActiveSensingActive;
|
||||||
int8_t mLastError;
|
int8_t mLastError;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
180
src/MIDI.hpp
180
src/MIDI.hpp
|
|
@ -40,15 +40,13 @@ inline MidiInterface<Transport, Settings, Platform>::MidiInterface(Transport& in
|
||||||
, mPendingMessageIndex(0)
|
, mPendingMessageIndex(0)
|
||||||
, mCurrentRpnNumber(0xffff)
|
, mCurrentRpnNumber(0xffff)
|
||||||
, mCurrentNrpnNumber(0xffff)
|
, mCurrentNrpnNumber(0xffff)
|
||||||
, mThruActivated(true)
|
|
||||||
, mThruFilterMode(Thru::Full)
|
|
||||||
, mLastMessageSentTime(0)
|
, mLastMessageSentTime(0)
|
||||||
, mLastMessageReceivedTime(0)
|
, mLastMessageReceivedTime(0)
|
||||||
, mSenderActiveSensingPeriodicity(0)
|
, mSenderActiveSensingPeriodicity(Settings::SenderActiveSensingPeriodicity)
|
||||||
, mReceiverActiveSensingActivated(false)
|
, mReceiverActiveSensingActive(false)
|
||||||
, mLastError(0)
|
, mLastError(0)
|
||||||
{
|
{
|
||||||
mSenderActiveSensingPeriodicity = Settings::SenderActiveSensingPeriodicity;
|
static_assert(!(Settings::UseSenderActiveSensing && Settings::UseReceiverActiveSensing), "UseSenderActiveSensing and UseReceiverActiveSensing can't be both set to true.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! \brief Destructor for MidiInterface.
|
/*! \brief Destructor for MidiInterface.
|
||||||
|
|
@ -84,7 +82,8 @@ MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings,
|
||||||
mCurrentRpnNumber = 0xffff;
|
mCurrentRpnNumber = 0xffff;
|
||||||
mCurrentNrpnNumber = 0xffff;
|
mCurrentNrpnNumber = 0xffff;
|
||||||
|
|
||||||
mLastMessageSentTime = Platform::now();
|
mLastMessageSentTime =
|
||||||
|
mLastMessageReceivedTime = Platform::now();
|
||||||
|
|
||||||
mMessage.valid = false;
|
mMessage.valid = false;
|
||||||
mMessage.type = InvalidType;
|
mMessage.type = InvalidType;
|
||||||
|
|
@ -93,9 +92,8 @@ MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings,
|
||||||
mMessage.data2 = 0;
|
mMessage.data2 = 0;
|
||||||
mMessage.length = 0;
|
mMessage.length = 0;
|
||||||
|
|
||||||
mThruFilterMode = Thru::Full;
|
mThruFilterCallback = Transport::thruActivated ? thruOn : thruOff;
|
||||||
mThruActivated = mTransport.thruActivated;
|
mThruMapCallback = thruEcho;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -385,7 +383,9 @@ MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings::UseRunningStatus)
|
if (Settings::UseRunningStatus)
|
||||||
|
{
|
||||||
mRunningStatus_TX = InvalidType;
|
mRunningStatus_TX = InvalidType;
|
||||||
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
@ -492,7 +492,9 @@ MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings::UseRunningStatus)
|
if (Settings::UseRunningStatus)
|
||||||
|
{
|
||||||
mRunningStatus_TX = InvalidType;
|
mRunningStatus_TX = InvalidType;
|
||||||
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
@ -760,27 +762,32 @@ template<class Transport, class Settings, class Platform>
|
||||||
inline bool MidiInterface<Transport, Settings, Platform>::read(Channel inChannel)
|
inline bool MidiInterface<Transport, Settings, Platform>::read(Channel inChannel)
|
||||||
{
|
{
|
||||||
#ifndef RegionActiveSending
|
#ifndef RegionActiveSending
|
||||||
|
|
||||||
// Active Sensing. This message is intended to be sent
|
// Active Sensing. This message is intended to be sent
|
||||||
// repeatedly to tell the receiver that a connection is alive. Use
|
// repeatedly to tell the receiver that a connection is alive. Use
|
||||||
// of this message is optional. When initially received, the
|
// of this message is optional.
|
||||||
// receiver will expect to receive another Active Sensing
|
if (Settings::UseSenderActiveSensing)
|
||||||
// message each 300ms (max), and if it does not then it will
|
|
||||||
// assume that the connection has been terminated. At
|
|
||||||
// termination, the receiver will turn off all voices and return to
|
|
||||||
// normal (non- active sensing) operation.
|
|
||||||
if (Settings::UseSenderActiveSensing && (mSenderActiveSensingPeriodicity > 0) && (Platform::now() - mLastMessageSentTime) > mSenderActiveSensingPeriodicity)
|
|
||||||
{
|
{
|
||||||
|
// Send ActiveSensing <Settings::ActiveSensingPeriodicity> ms after the last command
|
||||||
|
if ((Platform::now() - mLastMessageSentTime) > Settings::SenderActiveSensingPeriodicity)
|
||||||
sendActiveSensing();
|
sendActiveSensing();
|
||||||
mLastMessageSentTime = Platform::now();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings::UseReceiverActiveSensing && mReceiverActiveSensingActivated && (mLastMessageReceivedTime + ActiveSensingTimeout < Platform::now()))
|
// Once an Active Sensing message is received, the unit will begin monitoring
|
||||||
|
// the intervalbetween all subsequent messages. If there is an interval of 420 ms
|
||||||
|
// or longer betweenmessages while monitoring is active, the same processing
|
||||||
|
// as when All Sound Off, All Notes Off,and Reset All Controllers messages are
|
||||||
|
// received will be carried out. The unit will then stopmonitoring the message interval.
|
||||||
|
if (Settings::UseReceiverActiveSensing && mReceiverActiveSensingActive)
|
||||||
{
|
{
|
||||||
mReceiverActiveSensingActivated = false;
|
if ((Platform::now() - mLastMessageReceivedTime > Settings::ReceiverActiveSensingTimeout))
|
||||||
|
{
|
||||||
|
mReceiverActiveSensingActive = false;
|
||||||
|
|
||||||
mLastError |= 1UL << ErrorActiveSensingTimeout; // set the ErrorActiveSensingTimeout bit
|
// its up to the handler to send the stop processing messages
|
||||||
if (mErrorCallback)
|
// (also, no clue what the channel is on which to send them)
|
||||||
mErrorCallback(mLastError);
|
mActiveSensingTimeoutCallback(mReceiverActiveSensingActive);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -792,25 +799,18 @@ inline bool MidiInterface<Transport, Settings, Platform>::read(Channel inChannel
|
||||||
|
|
||||||
#ifndef RegionActiveSending
|
#ifndef RegionActiveSending
|
||||||
|
|
||||||
if (Settings::UseReceiverActiveSensing && mMessage.type == ActiveSensing)
|
if (Settings::UseReceiverActiveSensing)
|
||||||
{
|
{
|
||||||
// When an ActiveSensing message is received, the time keeping is activated.
|
|
||||||
// When a timeout occurs, an error message is send and time keeping ends.
|
|
||||||
mReceiverActiveSensingActivated = true;
|
|
||||||
|
|
||||||
// is ErrorActiveSensingTimeout bit in mLastError on
|
|
||||||
if (mLastError & (1 << (ErrorActiveSensingTimeout - 1)))
|
|
||||||
{
|
|
||||||
mLastError &= ~(1UL << ErrorActiveSensingTimeout); // clear the ErrorActiveSensingTimeout bit
|
|
||||||
if (mErrorCallback)
|
|
||||||
mErrorCallback(mLastError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep the time of the last received message, so we can check for the timeout
|
|
||||||
if (Settings::UseReceiverActiveSensing && mReceiverActiveSensingActivated)
|
|
||||||
mLastMessageReceivedTime = Platform::now();
|
mLastMessageReceivedTime = Platform::now();
|
||||||
|
|
||||||
|
if (mMessage.type == ActiveSensing && !mReceiverActiveSensingActive)
|
||||||
|
{
|
||||||
|
mReceiverActiveSensingActive = true;
|
||||||
|
|
||||||
|
mActiveSensingTimeoutCallback(mReceiverActiveSensingActive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
handleNullVelocityNoteOnAsNoteOff();
|
handleNullVelocityNoteOnAsNoteOff();
|
||||||
|
|
@ -819,7 +819,7 @@ inline bool MidiInterface<Transport, Settings, Platform>::read(Channel inChannel
|
||||||
if (channelMatch)
|
if (channelMatch)
|
||||||
launchCallback();
|
launchCallback();
|
||||||
|
|
||||||
thruFilter(inChannel);
|
processThru();
|
||||||
|
|
||||||
return channelMatch;
|
return channelMatch;
|
||||||
}
|
}
|
||||||
|
|
@ -1399,51 +1399,24 @@ void MidiInterface<Transport, Settings, Platform>::launchCallback()
|
||||||
@{
|
@{
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \brief Set the filter for thru mirroring
|
|
||||||
\param inThruFilterMode a filter mode
|
|
||||||
|
|
||||||
@see Thru::Mode
|
|
||||||
*/
|
|
||||||
template<class Transport, class Settings, class Platform>
|
template<class Transport, class Settings, class Platform>
|
||||||
inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::setThruFilterMode(Thru::Mode inThruFilterMode)
|
inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::turnThruOn(ThruFilterCallback fptr)
|
||||||
{
|
{
|
||||||
mThruFilterMode = inThruFilterMode;
|
mThruFilterCallback = fptr;
|
||||||
mThruActivated = mThruFilterMode != Thru::Off;
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Transport, class Settings, class Platform>
|
|
||||||
inline Thru::Mode MidiInterface<Transport, Settings, Platform>::getFilterMode() const
|
|
||||||
{
|
|
||||||
return mThruFilterMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Transport, class Settings, class Platform>
|
|
||||||
inline bool MidiInterface<Transport, Settings, Platform>::getThruState() const
|
|
||||||
{
|
|
||||||
return mThruActivated;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Transport, class Settings, class Platform>
|
|
||||||
inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::turnThruOn(Thru::Mode inThruFilterMode)
|
|
||||||
{
|
|
||||||
mThruActivated = true;
|
|
||||||
mThruFilterMode = inThruFilterMode;
|
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Transport, class Settings, class Platform>
|
template<class Transport, class Settings, class Platform>
|
||||||
inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::turnThruOff()
|
inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings, Platform>::turnThruOff()
|
||||||
{
|
{
|
||||||
mThruActivated = false;
|
mThruFilterCallback = thruOff;
|
||||||
mThruFilterMode = Thru::Off;
|
if (Settings::UseSenderActiveSensing)
|
||||||
|
{
|
||||||
|
mLastMessageSentTime = Platform::now();
|
||||||
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! @} */ // End of doc group MIDI Thru
|
/*! @} */ // End of doc group MIDI Thru
|
||||||
|
|
||||||
// This method is called upon reception of a message
|
// This method is called upon reception of a message
|
||||||
|
|
@ -1453,56 +1426,25 @@ inline MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Se
|
||||||
// - Channel messages are passed to the output whether their channel
|
// - Channel messages are passed to the output whether their channel
|
||||||
// is matching the input channel and the filter setting
|
// is matching the input channel and the filter setting
|
||||||
template<class Transport, class Settings, class Platform>
|
template<class Transport, class Settings, class Platform>
|
||||||
void MidiInterface<Transport, Settings, Platform>::thruFilter(Channel inChannel)
|
void MidiInterface<Transport, Settings, Platform>::processThru()
|
||||||
{
|
{
|
||||||
// If the feature is disabled, don't do anything.
|
if (!Transport::thruActivated || !mThruFilterCallback(mMessage))
|
||||||
if (!mThruActivated || (mThruFilterMode == Thru::Off))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
MidiMessage thruMessage = mThruMapCallback(mMessage);
|
||||||
|
|
||||||
// First, check if the received message is Channel
|
// First, check if the received message is Channel
|
||||||
if (mMessage.type >= NoteOff && mMessage.type <= PitchBend)
|
if (thruMessage.type >= NoteOff && thruMessage.type <= PitchBend)
|
||||||
{
|
{
|
||||||
const bool filter_condition = ((mMessage.channel == inChannel) ||
|
send(thruMessage.type,
|
||||||
(inChannel == MIDI_CHANNEL_OMNI));
|
thruMessage.data1,
|
||||||
|
thruMessage.data2,
|
||||||
// Now let's pass it to the output
|
thruMessage.channel);
|
||||||
switch (mThruFilterMode)
|
|
||||||
{
|
|
||||||
case Thru::Full:
|
|
||||||
send(mMessage.type,
|
|
||||||
mMessage.data1,
|
|
||||||
mMessage.data2,
|
|
||||||
mMessage.channel);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Thru::SameChannel:
|
|
||||||
if (filter_condition)
|
|
||||||
{
|
|
||||||
send(mMessage.type,
|
|
||||||
mMessage.data1,
|
|
||||||
mMessage.data2,
|
|
||||||
mMessage.channel);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Thru::DifferentChannel:
|
|
||||||
if (!filter_condition)
|
|
||||||
{
|
|
||||||
send(mMessage.type,
|
|
||||||
mMessage.data1,
|
|
||||||
mMessage.data2,
|
|
||||||
mMessage.channel);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Send the message to the output
|
// Send the message to the output
|
||||||
switch (mMessage.type)
|
switch (thruMessage.type)
|
||||||
{
|
{
|
||||||
// Real Time and 1 byte
|
// Real Time and 1 byte
|
||||||
case Clock:
|
case Clock:
|
||||||
|
|
@ -1512,24 +1454,24 @@ void MidiInterface<Transport, Settings, Platform>::thruFilter(Channel inChannel)
|
||||||
case ActiveSensing:
|
case ActiveSensing:
|
||||||
case SystemReset:
|
case SystemReset:
|
||||||
case TuneRequest:
|
case TuneRequest:
|
||||||
sendRealTime(mMessage.type);
|
sendRealTime(thruMessage.type);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SystemExclusive:
|
case SystemExclusive:
|
||||||
// Send SysEx (0xf0 and 0xf7 are included in the buffer)
|
// Send SysEx (0xf0 and 0xf7 are included in the buffer)
|
||||||
sendSysEx(getSysExArrayLength(), getSysExArray(), true);
|
sendSysEx(thruMessage.getSysExSize(), thruMessage.sysexArray, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SongSelect:
|
case SongSelect:
|
||||||
sendSongSelect(mMessage.data1);
|
sendSongSelect(thruMessage.data1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SongPosition:
|
case SongPosition:
|
||||||
sendSongPosition(mMessage.data1 | ((unsigned)mMessage.data2 << 7));
|
sendSongPosition(thruMessage.data1 | ((unsigned)thruMessage.data2 << 7));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TimeCodeQuarterFrame:
|
case TimeCodeQuarterFrame:
|
||||||
sendTimeCodeQuarterFrame(mMessage.data1,mMessage.data2);
|
sendTimeCodeQuarterFrame(thruMessage.data1,thruMessage.data2);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -46,28 +46,23 @@ BEGIN_MIDI_NAMESPACE
|
||||||
#define MIDI_PITCHBEND_MIN -8192
|
#define MIDI_PITCHBEND_MIN -8192
|
||||||
#define MIDI_PITCHBEND_MAX 8191
|
#define MIDI_PITCHBEND_MAX 8191
|
||||||
|
|
||||||
/*! Receiving Active Sensing
|
|
||||||
*/
|
|
||||||
static const uint16_t ActiveSensingTimeout = 300;
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Type definitions
|
// Type definitions
|
||||||
|
|
||||||
typedef byte StatusByte;
|
typedef byte StatusByte;
|
||||||
typedef byte DataByte;
|
typedef byte DataByte;
|
||||||
typedef byte Channel;
|
typedef byte Channel;
|
||||||
typedef byte FilterMode;
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Errors
|
// Errors
|
||||||
static const uint8_t ErrorParse = 0;
|
static const uint8_t ErrorParse = 0;
|
||||||
static const uint8_t ErrorActiveSensingTimeout = 1;
|
|
||||||
static const uint8_t WarningSplitSysEx = 2;
|
static const uint8_t WarningSplitSysEx = 2;
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Aliasing
|
// Aliasing
|
||||||
|
|
||||||
using ErrorCallback = void (*)(int8_t);
|
using ErrorCallback = void (*)(int8_t);
|
||||||
|
using ActiveSensingTimeoutCallback = void (*)(bool);
|
||||||
using NoteOffCallback = void (*)(Channel channel, byte note, byte velocity);
|
using NoteOffCallback = void (*)(Channel channel, byte note, byte velocity);
|
||||||
using NoteOnCallback = void (*)(Channel channel, byte note, byte velocity);
|
using NoteOnCallback = void (*)(Channel channel, byte note, byte velocity);
|
||||||
using AfterTouchPolyCallback = void (*)(Channel channel, byte note, byte velocity);
|
using AfterTouchPolyCallback = void (*)(Channel channel, byte note, byte velocity);
|
||||||
|
|
@ -123,20 +118,6 @@ enum MidiType: uint8_t
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
/*! Enumeration of Thru filter modes */
|
|
||||||
struct Thru
|
|
||||||
{
|
|
||||||
enum Mode
|
|
||||||
{
|
|
||||||
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.
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/*! \brief Enumeration of Control Change command numbers.
|
/*! \brief Enumeration of Control Change command numbers.
|
||||||
See the detailed controllers numbers & description here:
|
See the detailed controllers numbers & description here:
|
||||||
http://www.somascape.org/midi/tech/spec.html#ctrlnums
|
http://www.somascape.org/midi/tech/spec.html#ctrlnums
|
||||||
|
|
|
||||||
|
|
@ -72,18 +72,15 @@ struct DefaultSettings
|
||||||
*/
|
*/
|
||||||
static const unsigned SysExMaxSize = 128;
|
static const unsigned SysExMaxSize = 128;
|
||||||
|
|
||||||
/*! Global switch to turn on/off sender ActiveSensing
|
/*! Global switch to turn on/off sending and receiving ActiveSensing
|
||||||
Set to true to send ActiveSensing
|
Set to true to activate ActiveSensing
|
||||||
Set to false will not send ActiveSensing message (will also save memory)
|
Set to false will not send/receive ActiveSensing message (will also save 236 bytes of memory)
|
||||||
|
|
||||||
|
When setting UseActiveSensing to true, MIDI.read() *must* be called
|
||||||
|
as often as possible (1000 / ActiveSensingPeriodicity per second).
|
||||||
*/
|
*/
|
||||||
static const bool UseSenderActiveSensing = false;
|
static const bool UseSenderActiveSensing = false;
|
||||||
|
|
||||||
/*! Global switch to turn on/off receiver ActiveSensing
|
|
||||||
Set to true to check for message timeouts (via ErrorCallback)
|
|
||||||
Set to false will not check if chained device are still alive (if they use ActiveSensing) (will also save memory)
|
|
||||||
*/
|
|
||||||
static const bool UseReceiverActiveSensing = false;
|
|
||||||
|
|
||||||
/*! Active Sensing is intended to be sent
|
/*! Active Sensing is intended to be sent
|
||||||
repeatedly by the sender to tell the receiver that a connection is alive. Use
|
repeatedly by the sender to tell the receiver that a connection is alive. Use
|
||||||
of this message is optional. When initially received, the
|
of this message is optional. When initially received, the
|
||||||
|
|
@ -94,11 +91,20 @@ struct DefaultSettings
|
||||||
normal (non- active sensing) operation.
|
normal (non- active sensing) operation.
|
||||||
|
|
||||||
Typical value is 250 (ms) - an Active Sensing command is send every 250ms.
|
Typical value is 250 (ms) - an Active Sensing command is send every 250ms.
|
||||||
(All Roland devices send Active Sensing every 250ms)
|
(Most Roland devices send Active Sensing every 250ms)
|
||||||
|
|
||||||
Setting this field to 0 will disable sending MIDI active sensing.
|
|
||||||
*/
|
*/
|
||||||
static const uint16_t SenderActiveSensingPeriodicity = 0;
|
static const uint16_t SenderActiveSensingPeriodicity = 300;
|
||||||
|
|
||||||
|
/*! Once an Active Sensing message is received, the unit will begin monitoring
|
||||||
|
the intervalbetween all subsequent messages. If there is an interval of ActiveSensingPeriodicity ms
|
||||||
|
or longer betweenmessages while monitoring is active, the same processing
|
||||||
|
as when All Sound Off, All Notes Off,and Reset All Controllers messages are
|
||||||
|
received will be carried out. The unit will then stopmonitoring the message interval.
|
||||||
|
*/
|
||||||
|
static const bool UseReceiverActiveSensing = false;
|
||||||
|
|
||||||
|
static const uint16_t ReceiverActiveSensingTimeout = 300;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
END_MIDI_NAMESPACE
|
END_MIDI_NAMESPACE
|
||||||
|
|
|
||||||
|
|
@ -104,8 +104,11 @@ END_MIDI_NAMESPACE
|
||||||
Then call midi2.begin(), midi2.read() etc..
|
Then call midi2.begin(), midi2.read() etc..
|
||||||
*/
|
*/
|
||||||
#define MIDI_CREATE_INSTANCE(Type, SerialPort, Name) \
|
#define MIDI_CREATE_INSTANCE(Type, SerialPort, Name) \
|
||||||
MIDI_NAMESPACE::SerialMIDI<Type> serial##Name(SerialPort);\
|
using Name##SerialTransport = MIDI_NAMESPACE::SerialMIDI<Type>; \
|
||||||
MIDI_NAMESPACE::MidiInterface<MIDI_NAMESPACE::SerialMIDI<Type>> Name((MIDI_NAMESPACE::SerialMIDI<Type>&)serial##Name);
|
using Name##Interface = MIDI_NAMESPACE::MidiInterface<Name##SerialTransport>; \
|
||||||
|
using Name##Message = Name##Interface::MidiMessage; \
|
||||||
|
Name##SerialTransport serial##Name(SerialPort); \
|
||||||
|
Name##Interface Name((Name##SerialTransport&)serial##Name);
|
||||||
|
|
||||||
#if defined(ARDUINO_SAM_DUE) || defined(USBCON) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__)
|
#if defined(ARDUINO_SAM_DUE) || defined(USBCON) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MKL26Z64__)
|
||||||
// Leonardo, Due and other USB boards use Serial1 by default.
|
// Leonardo, Due and other USB boards use Serial1 by default.
|
||||||
|
|
@ -121,10 +124,11 @@ END_MIDI_NAMESPACE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*! \brief Create an instance of the library attached to a serial port with
|
/*! \brief Create an instance of the library attached to a serial port with
|
||||||
custom settings.
|
custom MIDI settings (not to be confused with modified Serial Settings, like BaudRate)
|
||||||
@see DefaultSettings
|
@see DefaultSettings
|
||||||
@see MIDI_CREATE_INSTANCE
|
@see MIDI_CREATE_INSTANCE
|
||||||
*/
|
*/
|
||||||
#define MIDI_CREATE_CUSTOM_INSTANCE(Type, SerialPort, Name, Settings) \
|
#define MIDI_CREATE_CUSTOM_INSTANCE(Type, SerialPort, Name, Settings) \
|
||||||
MIDI_NAMESPACE::SerialMIDI<Type> serial##Name(SerialPort);\
|
MIDI_NAMESPACE::SerialMIDI<Type> serial##Name(SerialPort);\
|
||||||
MIDI_NAMESPACE::MidiInterface<MIDI_NAMESPACE::SerialMIDI<Type>, Settings> Name((MIDI_NAMESPACE::SerialMIDI<Type>&)serial##Name);
|
MIDI_NAMESPACE::MidiInterface<MIDI_NAMESPACE::SerialMIDI<Type>, Settings> Name((MIDI_NAMESPACE::SerialMIDI<Type>&)serial##Name);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ add_executable(unit-tests
|
||||||
tests/unit-tests_MidiThru.cpp
|
tests/unit-tests_MidiThru.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set_source_files_properties(tests/unit-tests_MidiThru.cpp PROPERTIES COMPILE_FLAGS -Wno-shadow)
|
||||||
|
|
||||||
target_link_libraries(unit-tests
|
target_link_libraries(unit-tests
|
||||||
gtest
|
gtest
|
||||||
gmock
|
gmock
|
||||||
|
|
|
||||||
|
|
@ -547,6 +547,41 @@ TEST(MidiOutput, sendRealTime)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(MidiOutput, sendCommon)
|
||||||
|
{
|
||||||
|
SerialMock serial;
|
||||||
|
Transport transport(serial);
|
||||||
|
MidiInterface midi((Transport&)transport);
|
||||||
|
|
||||||
|
Buffer buffer;
|
||||||
|
|
||||||
|
// Test valid Common messages
|
||||||
|
{
|
||||||
|
buffer.clear();
|
||||||
|
buffer.resize(8);
|
||||||
|
|
||||||
|
midi.begin();
|
||||||
|
midi.sendCommon(midi::TimeCodeQuarterFrame, 1);
|
||||||
|
midi.sendCommon(midi::SongPosition, 0x5555);
|
||||||
|
midi.sendCommon(midi::SongSelect, 3);
|
||||||
|
midi.sendCommon(midi::TuneRequest, 4);
|
||||||
|
|
||||||
|
EXPECT_EQ(serial.mTxBuffer.getLength(), 8);
|
||||||
|
serial.mTxBuffer.read(&buffer[0], 8);
|
||||||
|
EXPECT_THAT(buffer, ElementsAreArray({
|
||||||
|
0xf1, 0x01, 0xf2, 0x55, 0x2a, 0xf3, 0x03, 0xf6
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
// Test invalid messages
|
||||||
|
{
|
||||||
|
midi.begin();
|
||||||
|
midi.sendCommon(midi::Undefined_F4, 0);
|
||||||
|
midi.sendCommon(midi::Undefined_F5, 0);
|
||||||
|
midi.sendCommon(midi::InvalidType, 0);
|
||||||
|
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST(MidiOutput, RPN)
|
TEST(MidiOutput, RPN)
|
||||||
{
|
{
|
||||||
typedef VariableSettings<true, true> Settings;
|
typedef VariableSettings<true, true> Settings;
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ typedef test_mocks::SerialMock<32> SerialMock;
|
||||||
typedef midi::SerialMIDI<SerialMock> Transport;
|
typedef midi::SerialMIDI<SerialMock> Transport;
|
||||||
typedef midi::MidiInterface<Transport> MidiInterface;
|
typedef midi::MidiInterface<Transport> MidiInterface;
|
||||||
typedef std::vector<byte> Buffer;
|
typedef std::vector<byte> Buffer;
|
||||||
|
typedef midi::Message<midi::DefaultSettings::SysExMaxSize> MidiMessage;
|
||||||
|
|
||||||
template<unsigned Size>
|
template<unsigned Size>
|
||||||
struct VariableSysExSettings : midi::DefaultSettings
|
struct VariableSysExSettings : midi::DefaultSettings
|
||||||
|
|
@ -24,75 +25,42 @@ struct VariableSysExSettings : midi::DefaultSettings
|
||||||
static const unsigned SysExMaxSize = Size;
|
static const unsigned SysExMaxSize = Size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
SerialMock serial;
|
||||||
|
Transport transport(serial);
|
||||||
|
MidiInterface midi((Transport&)transport);
|
||||||
|
|
||||||
|
bool thruFilterSameChannel(const MidiMessage& inMessage)
|
||||||
|
{
|
||||||
|
if (!midi.isChannelMessage(inMessage.type))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return MIDI_CHANNEL_OMNI == midi.getInputChannel() ||
|
||||||
|
inMessage.channel == midi.getInputChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool thruFilterDifferentChannel(const MidiMessage& inMessage)
|
||||||
|
{
|
||||||
|
if (!midi.isChannelMessage(inMessage.type))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return MIDI_CHANNEL_OMNI != midi.getInputChannel() &&
|
||||||
|
inMessage.channel != midi.getInputChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiMessage thruMapNoteOnFullVelocity(const MidiMessage& inMessage)
|
||||||
|
{
|
||||||
|
if (inMessage.type != midi::MidiType::NoteOn)
|
||||||
|
return inMessage;
|
||||||
|
|
||||||
|
MidiMessage modified = inMessage;
|
||||||
|
modified.data2 = 127;
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
TEST(MidiThru, defaultValues)
|
|
||||||
{
|
|
||||||
SerialMock serial;
|
|
||||||
Transport transport(serial);
|
|
||||||
MidiInterface midi((Transport&)transport);
|
|
||||||
|
|
||||||
EXPECT_EQ(midi.getThruState(), true);
|
|
||||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::Full);
|
|
||||||
midi.begin(); // Should not change the state
|
|
||||||
EXPECT_EQ(midi.getThruState(), true);
|
|
||||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::Full);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(MidiThru, beginEnablesThru)
|
|
||||||
{
|
|
||||||
SerialMock serial;
|
|
||||||
Transport transport(serial);
|
|
||||||
MidiInterface midi((Transport&)transport);
|
|
||||||
|
|
||||||
midi.turnThruOff();
|
|
||||||
EXPECT_EQ(midi.getThruState(), false);
|
|
||||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::Off);
|
|
||||||
midi.begin();
|
|
||||||
EXPECT_EQ(midi.getThruState(), true);
|
|
||||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::Full);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(MidiThru, setGet)
|
|
||||||
{
|
|
||||||
SerialMock serial;
|
|
||||||
Transport transport(serial);
|
|
||||||
MidiInterface midi((Transport&)transport);
|
|
||||||
|
|
||||||
midi.turnThruOff();
|
|
||||||
EXPECT_EQ(midi.getThruState(), false);
|
|
||||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::Off);
|
|
||||||
|
|
||||||
midi.turnThruOn();
|
|
||||||
EXPECT_EQ(midi.getThruState(), true);
|
|
||||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::Full);
|
|
||||||
midi.turnThruOn(midi::Thru::SameChannel);
|
|
||||||
EXPECT_EQ(midi.getThruState(), true);
|
|
||||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::SameChannel);
|
|
||||||
midi.turnThruOn(midi::Thru::DifferentChannel);
|
|
||||||
EXPECT_EQ(midi.getThruState(), true);
|
|
||||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::DifferentChannel);
|
|
||||||
|
|
||||||
midi.setThruFilterMode(midi::Thru::Full);
|
|
||||||
EXPECT_EQ(midi.getThruState(), true);
|
|
||||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::Full);
|
|
||||||
midi.setThruFilterMode(midi::Thru::SameChannel);
|
|
||||||
EXPECT_EQ(midi.getThruState(), true);
|
|
||||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::SameChannel);
|
|
||||||
midi.setThruFilterMode(midi::Thru::DifferentChannel);
|
|
||||||
EXPECT_EQ(midi.getThruState(), true);
|
|
||||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::DifferentChannel);
|
|
||||||
midi.setThruFilterMode(midi::Thru::Off);
|
|
||||||
EXPECT_EQ(midi.getThruState(), false);
|
|
||||||
EXPECT_EQ(midi.getFilterMode(), midi::Thru::Off);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(MidiThru, off)
|
TEST(MidiThru, off)
|
||||||
{
|
{
|
||||||
SerialMock serial;
|
|
||||||
Transport transport(serial);
|
|
||||||
MidiInterface midi((Transport&)transport);
|
|
||||||
|
|
||||||
midi.begin(MIDI_CHANNEL_OMNI);
|
midi.begin(MIDI_CHANNEL_OMNI);
|
||||||
midi.turnThruOff();
|
midi.turnThruOff();
|
||||||
|
|
||||||
|
|
@ -110,14 +78,9 @@ TEST(MidiThru, off)
|
||||||
|
|
||||||
TEST(MidiThru, full)
|
TEST(MidiThru, full)
|
||||||
{
|
{
|
||||||
SerialMock serial;
|
|
||||||
Transport transport(serial);
|
|
||||||
MidiInterface midi((Transport&)transport);
|
|
||||||
|
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
|
|
||||||
midi.begin(MIDI_CHANNEL_OMNI);
|
midi.begin(MIDI_CHANNEL_OMNI);
|
||||||
midi.setThruFilterMode(midi::Thru::Full);
|
|
||||||
|
|
||||||
static const unsigned rxSize = 6;
|
static const unsigned rxSize = 6;
|
||||||
static const byte rxData[rxSize] = { 0x9b, 12, 34, 0x9c, 56, 78 };
|
static const byte rxData[rxSize] = { 0x9b, 12, 34, 0x9c, 56, 78 };
|
||||||
|
|
@ -154,14 +117,10 @@ TEST(MidiThru, full)
|
||||||
|
|
||||||
TEST(MidiThru, sameChannel)
|
TEST(MidiThru, sameChannel)
|
||||||
{
|
{
|
||||||
SerialMock serial;
|
|
||||||
Transport transport(serial);
|
|
||||||
MidiInterface midi((Transport&)transport);
|
|
||||||
|
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
|
|
||||||
midi.begin(12);
|
midi.begin(12);
|
||||||
midi.setThruFilterMode(midi::Thru::SameChannel);
|
midi.setThruFilter(thruFilterSameChannel);
|
||||||
|
|
||||||
static const unsigned rxSize = 6;
|
static const unsigned rxSize = 6;
|
||||||
static const byte rxData[rxSize] = { 0x9b, 12, 34, 0x9c, 56, 78 };
|
static const byte rxData[rxSize] = { 0x9b, 12, 34, 0x9c, 56, 78 };
|
||||||
|
|
@ -185,14 +144,10 @@ TEST(MidiThru, sameChannel)
|
||||||
|
|
||||||
TEST(MidiThru, sameChannelOmni) // Acts like full
|
TEST(MidiThru, sameChannelOmni) // Acts like full
|
||||||
{
|
{
|
||||||
SerialMock serial;
|
|
||||||
Transport transport(serial);
|
|
||||||
MidiInterface midi((Transport&)transport);
|
|
||||||
|
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
|
|
||||||
midi.begin(MIDI_CHANNEL_OMNI);
|
midi.begin(MIDI_CHANNEL_OMNI);
|
||||||
midi.setThruFilterMode(midi::Thru::SameChannel);
|
midi.setThruFilter(thruFilterSameChannel);
|
||||||
|
|
||||||
static const unsigned rxSize = 6;
|
static const unsigned rxSize = 6;
|
||||||
static const byte rxData[rxSize] = { 0x9b, 12, 34, 0x9c, 56, 78 };
|
static const byte rxData[rxSize] = { 0x9b, 12, 34, 0x9c, 56, 78 };
|
||||||
|
|
@ -229,14 +184,10 @@ TEST(MidiThru, sameChannelOmni) // Acts like full
|
||||||
|
|
||||||
TEST(MidiThru, differentChannel)
|
TEST(MidiThru, differentChannel)
|
||||||
{
|
{
|
||||||
SerialMock serial;
|
|
||||||
Transport transport(serial);
|
|
||||||
MidiInterface midi((Transport&)transport);
|
|
||||||
|
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
|
|
||||||
midi.begin(12);
|
midi.begin(12);
|
||||||
midi.setThruFilterMode(midi::Thru::DifferentChannel);
|
midi.setThruFilter(thruFilterDifferentChannel);
|
||||||
|
|
||||||
static const unsigned rxSize = 6;
|
static const unsigned rxSize = 6;
|
||||||
static const byte rxData[rxSize] = { 0x9b, 12, 34, 0x9c, 56, 78 };
|
static const byte rxData[rxSize] = { 0x9b, 12, 34, 0x9c, 56, 78 };
|
||||||
|
|
@ -260,14 +211,10 @@ TEST(MidiThru, differentChannel)
|
||||||
|
|
||||||
TEST(MidiThru, differentChannelOmni) // Acts like off
|
TEST(MidiThru, differentChannelOmni) // Acts like off
|
||||||
{
|
{
|
||||||
SerialMock serial;
|
|
||||||
Transport transport(serial);
|
|
||||||
MidiInterface midi((Transport&)transport);
|
|
||||||
|
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
|
|
||||||
midi.begin(MIDI_CHANNEL_OMNI);
|
midi.begin(MIDI_CHANNEL_OMNI);
|
||||||
midi.setThruFilterMode(midi::Thru::DifferentChannel);
|
midi.setThruFilter(thruFilterDifferentChannel);
|
||||||
|
|
||||||
static const unsigned rxSize = 6;
|
static const unsigned rxSize = 6;
|
||||||
static const byte rxData[rxSize] = { 0x9b, 12, 34, 0x9c, 56, 78 };
|
static const byte rxData[rxSize] = { 0x9b, 12, 34, 0x9c, 56, 78 };
|
||||||
|
|
@ -293,14 +240,11 @@ TEST(MidiThru, multiByteThru)
|
||||||
typedef VariableSettings<false, false> MultiByteParsing;
|
typedef VariableSettings<false, false> MultiByteParsing;
|
||||||
typedef midi::MidiInterface<Transport, MultiByteParsing> MultiByteMidiInterface;
|
typedef midi::MidiInterface<Transport, MultiByteParsing> MultiByteMidiInterface;
|
||||||
|
|
||||||
SerialMock serial;
|
|
||||||
Transport transport(serial);
|
|
||||||
MultiByteMidiInterface midi((Transport&)transport);
|
MultiByteMidiInterface midi((Transport&)transport);
|
||||||
|
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
|
|
||||||
midi.begin(MIDI_CHANNEL_OMNI);
|
midi.begin(MIDI_CHANNEL_OMNI);
|
||||||
midi.setThruFilterMode(midi::Thru::Full);
|
|
||||||
|
|
||||||
static const unsigned rxSize = 6;
|
static const unsigned rxSize = 6;
|
||||||
static const byte rxData[rxSize] = { 0x9b, 12, 34, 56, 78 };
|
static const byte rxData[rxSize] = { 0x9b, 12, 34, 56, 78 };
|
||||||
|
|
@ -324,14 +268,11 @@ TEST(MidiThru, withTxRunningStatus)
|
||||||
typedef VariableSettings<true, true> Settings;
|
typedef VariableSettings<true, true> Settings;
|
||||||
typedef midi::MidiInterface<Transport, Settings> RsMidiInterface;
|
typedef midi::MidiInterface<Transport, Settings> RsMidiInterface;
|
||||||
|
|
||||||
SerialMock serial;
|
|
||||||
Transport transport(serial);
|
|
||||||
RsMidiInterface midi((Transport&)transport);
|
RsMidiInterface midi((Transport&)transport);
|
||||||
|
|
||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
|
|
||||||
midi.begin(MIDI_CHANNEL_OMNI);
|
midi.begin(MIDI_CHANNEL_OMNI);
|
||||||
midi.setThruFilterMode(midi::Thru::Full);
|
|
||||||
|
|
||||||
static const unsigned rxSize = 5;
|
static const unsigned rxSize = 5;
|
||||||
static const byte rxData[rxSize] = { 0x9b, 12, 34, 56, 78 };
|
static const byte rxData[rxSize] = { 0x9b, 12, 34, 56, 78 };
|
||||||
|
|
@ -364,26 +305,52 @@ TEST(MidiThru, withTxRunningStatus)
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(MidiThru, invalidMode)
|
TEST(MidiThru, mapNoteOnFullVelocity)
|
||||||
{
|
{
|
||||||
SerialMock serial;
|
Buffer buffer;
|
||||||
Transport transport(serial);
|
|
||||||
MidiInterface midi((Transport&)transport);
|
|
||||||
|
|
||||||
midi.begin(MIDI_CHANNEL_OMNI);
|
midi.begin(MIDI_CHANNEL_OMNI);
|
||||||
midi.setThruFilterMode(midi::Thru::Mode(42));
|
midi.setThruMap(thruMapNoteOnFullVelocity);
|
||||||
|
|
||||||
static const unsigned rxSize = 6;
|
static const unsigned rxSize = 6;
|
||||||
static const byte rxData[rxSize] = { 0x9b, 12, 34, 0x9c, 56, 78 };
|
static const byte rxData[rxSize] = { 0x9b, 12, 34, 0x9c, 56, 78 };
|
||||||
serial.mRxBuffer.write(rxData, rxSize);
|
serial.mRxBuffer.write(rxData, rxSize);
|
||||||
|
|
||||||
EXPECT_EQ(midi.read(), false);
|
EXPECT_EQ(midi.read(), false);
|
||||||
|
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||||
EXPECT_EQ(midi.read(), false);
|
EXPECT_EQ(midi.read(), false);
|
||||||
EXPECT_EQ(midi.read(), true);
|
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||||
EXPECT_EQ(midi.read(), false);
|
|
||||||
EXPECT_EQ(midi.read(), false);
|
|
||||||
EXPECT_EQ(midi.read(), true);
|
EXPECT_EQ(midi.read(), true);
|
||||||
|
|
||||||
|
buffer.clear();
|
||||||
|
buffer.resize(3);
|
||||||
|
EXPECT_EQ(serial.mTxBuffer.getLength(), 3);
|
||||||
|
serial.mTxBuffer.read(&buffer[0], 3);
|
||||||
|
EXPECT_THAT(buffer, ElementsAreArray({
|
||||||
|
0x9b, 12, 127 // thru message full velocity
|
||||||
|
}));
|
||||||
|
EXPECT_EQ(midi.getType(), midi::NoteOn);
|
||||||
|
EXPECT_EQ(midi.getChannel(), 12);
|
||||||
|
EXPECT_EQ(midi.getData1(), 12);
|
||||||
|
EXPECT_EQ(midi.getData2(), 34); // mMessage velocity unchanged
|
||||||
|
|
||||||
|
EXPECT_EQ(midi.read(), false);
|
||||||
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||||
|
EXPECT_EQ(midi.read(), false);
|
||||||
|
EXPECT_EQ(serial.mTxBuffer.getLength(), 0);
|
||||||
|
EXPECT_EQ(midi.read(), true);
|
||||||
|
|
||||||
|
buffer.clear();
|
||||||
|
buffer.resize(3);
|
||||||
|
EXPECT_EQ(serial.mTxBuffer.getLength(), 3);
|
||||||
|
serial.mTxBuffer.read(&buffer[0], 3);
|
||||||
|
EXPECT_THAT(buffer, ElementsAreArray({
|
||||||
|
0x9c, 56, 127 // thru message full velocity
|
||||||
|
}));
|
||||||
|
EXPECT_EQ(midi.getType(), midi::NoteOn);
|
||||||
|
EXPECT_EQ(midi.getChannel(), 13);
|
||||||
|
EXPECT_EQ(midi.getData1(), 56);
|
||||||
|
EXPECT_EQ(midi.getData2(), 78); // mMessage velocity unchanged
|
||||||
}
|
}
|
||||||
|
|
||||||
END_UNNAMED_NAMESPACE
|
END_UNNAMED_NAMESPACE
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue