Compare commits
6 Commits
master
...
ActiveSens
| Author | SHA1 | Date |
|---|---|---|
|
|
37ef1d48d9 | |
|
|
ca3371a7f2 | |
|
|
1640504544 | |
|
|
e83ea63979 | |
|
|
5972ae5e24 | |
|
|
2685bb458b |
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -185,6 +185,7 @@ public:
|
|||
public:
|
||||
inline void setHandleMessage(void (*fptr)(const MidiMessage&)) { mMessageCallback = fptr; };
|
||||
inline void setHandleError(ErrorCallback fptr) { mErrorCallback = fptr; }
|
||||
inline void setHandleActiveSensingTimeout(ActiveSensingTimeoutCallback fptr) { mActiveSensingTimeoutCallback = fptr; }
|
||||
inline void setHandleNoteOff(NoteOffCallback fptr) { mNoteOffCallback = fptr; }
|
||||
inline void setHandleNoteOn(NoteOnCallback fptr) { mNoteOnCallback = fptr; }
|
||||
inline void setHandleAfterTouchPoly(AfterTouchPolyCallback fptr) { mAfterTouchPolyCallback = fptr; }
|
||||
|
|
@ -212,6 +213,7 @@ private:
|
|||
|
||||
void (*mMessageCallback)(const MidiMessage& message) = nullptr;
|
||||
ErrorCallback mErrorCallback = nullptr;
|
||||
ActiveSensingTimeoutCallback mActiveSensingTimeoutCallback = nullptr;
|
||||
NoteOffCallback mNoteOffCallback = nullptr;
|
||||
NoteOnCallback mNoteOnCallback = nullptr;
|
||||
AfterTouchPolyCallback mAfterTouchPolyCallback = nullptr;
|
||||
|
|
@ -282,8 +284,7 @@ private:
|
|||
MidiMessage mMessage;
|
||||
unsigned long mLastMessageSentTime;
|
||||
unsigned long mLastMessageReceivedTime;
|
||||
unsigned long mSenderActiveSensingPeriodicity;
|
||||
bool mReceiverActiveSensingActivated;
|
||||
bool mReceiverActiveSensingActive;
|
||||
int8_t mLastError;
|
||||
|
||||
private:
|
||||
|
|
|
|||
66
src/MIDI.hpp
66
src/MIDI.hpp
|
|
@ -44,11 +44,10 @@ inline MidiInterface<Transport, Settings, Platform>::MidiInterface(Transport& in
|
|||
, mThruFilterMode(Thru::Full)
|
||||
, mLastMessageSentTime(0)
|
||||
, mLastMessageReceivedTime(0)
|
||||
, mSenderActiveSensingPeriodicity(0)
|
||||
, mReceiverActiveSensingActivated(false)
|
||||
, mReceiverActiveSensingActive(false)
|
||||
, 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.
|
||||
|
|
@ -84,7 +83,8 @@ void MidiInterface<Transport, Settings, Platform>::begin(Channel inChannel)
|
|||
mCurrentRpnNumber = 0xffff;
|
||||
mCurrentNrpnNumber = 0xffff;
|
||||
|
||||
mLastMessageSentTime = Platform::now();
|
||||
mLastMessageSentTime =
|
||||
mLastMessageReceivedTime = Platform::now();
|
||||
|
||||
mMessage.valid = false;
|
||||
mMessage.type = InvalidType;
|
||||
|
|
@ -712,27 +712,32 @@ template<class Transport, class Settings, class Platform>
|
|||
inline bool MidiInterface<Transport, Settings, Platform>::read(Channel inChannel)
|
||||
{
|
||||
#ifndef RegionActiveSending
|
||||
|
||||
// Active Sensing. This message is intended to be sent
|
||||
// repeatedly to tell the receiver that a connection is alive. Use
|
||||
// of this message is optional. When initially received, the
|
||||
// receiver will expect to receive another Active Sensing
|
||||
// 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)
|
||||
// of this message is optional.
|
||||
if (Settings::UseSenderActiveSensing)
|
||||
{
|
||||
// Send ActiveSensing <Settings::ActiveSensingPeriodicity> ms after the last command
|
||||
if ((Platform::now() - mLastMessageSentTime) > Settings::SenderActiveSensingPeriodicity)
|
||||
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
|
||||
if (mErrorCallback)
|
||||
mErrorCallback(mLastError);
|
||||
// its up to the handler to send the stop processing messages
|
||||
// (also, no clue what the channel is on which to send them)
|
||||
mActiveSensingTimeoutCallback(mReceiverActiveSensingActive);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -744,25 +749,18 @@ inline bool MidiInterface<Transport, Settings, Platform>::read(Channel inChannel
|
|||
|
||||
#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();
|
||||
|
||||
if (mMessage.type == ActiveSensing && !mReceiverActiveSensingActive)
|
||||
{
|
||||
mReceiverActiveSensingActive = true;
|
||||
|
||||
mActiveSensingTimeoutCallback(mReceiverActiveSensingActive);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
handleNullVelocityNoteOnAsNoteOff();
|
||||
|
|
@ -1384,7 +1382,7 @@ inline void MidiInterface<Transport, Settings, Platform>::turnThruOff()
|
|||
template<class Transport, class Settings, class Platform>
|
||||
inline void MidiInterface<Transport, Settings, Platform>::UpdateLastSentTime()
|
||||
{
|
||||
if (Settings::UseSenderActiveSensing && mSenderActiveSensingPeriodicity)
|
||||
if (Settings::UseSenderActiveSensing)
|
||||
mLastMessageSentTime = Platform::now();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,10 +46,6 @@ BEGIN_MIDI_NAMESPACE
|
|||
#define MIDI_PITCHBEND_MIN -8192
|
||||
#define MIDI_PITCHBEND_MAX 8191
|
||||
|
||||
/*! Receiving Active Sensing
|
||||
*/
|
||||
static const uint16_t ActiveSensingTimeout = 300;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Type definitions
|
||||
|
||||
|
|
@ -61,13 +57,13 @@ typedef byte FilterMode;
|
|||
// -----------------------------------------------------------------------------
|
||||
// Errors
|
||||
static const uint8_t ErrorParse = 0;
|
||||
static const uint8_t ErrorActiveSensingTimeout = 1;
|
||||
static const uint8_t WarningSplitSysEx = 2;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Aliasing
|
||||
|
||||
using ErrorCallback = void (*)(int8_t);
|
||||
using ActiveSensingTimeoutCallback = void (*)(bool);
|
||||
using NoteOffCallback = 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);
|
||||
|
|
|
|||
|
|
@ -72,18 +72,15 @@ struct DefaultSettings
|
|||
*/
|
||||
static const unsigned SysExMaxSize = 128;
|
||||
|
||||
/*! Global switch to turn on/off sender ActiveSensing
|
||||
Set to true to send ActiveSensing
|
||||
Set to false will not send ActiveSensing message (will also save memory)
|
||||
/*! Global switch to turn on/off sending and receiving ActiveSensing
|
||||
Set to true to activate ActiveSensing
|
||||
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;
|
||||
|
||||
/*! 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
|
||||
repeatedly by the sender to tell the receiver that a connection is alive. Use
|
||||
of this message is optional. When initially received, the
|
||||
|
|
@ -94,11 +91,20 @@ struct DefaultSettings
|
|||
normal (non- active sensing) operation.
|
||||
|
||||
Typical value is 250 (ms) - an Active Sensing command is send every 250ms.
|
||||
(All Roland devices send Active Sensing every 250ms)
|
||||
|
||||
Setting this field to 0 will disable sending MIDI active sensing.
|
||||
(Most Roland devices send Active Sensing every 250ms)
|
||||
*/
|
||||
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
|
||||
|
|
|
|||
|
|
@ -116,10 +116,11 @@ END_MIDI_NAMESPACE
|
|||
#endif
|
||||
|
||||
/*! \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 MIDI_CREATE_INSTANCE
|
||||
*/
|
||||
#define MIDI_CREATE_CUSTOM_INSTANCE(Type, SerialPort, Name, Settings) \
|
||||
MIDI_NAMESPACE::SerialMIDI<Type> serial##Name(SerialPort);\
|
||||
MIDI_NAMESPACE::MidiInterface<MIDI_NAMESPACE::SerialMIDI<Type>, Settings> Name((MIDI_NAMESPACE::SerialMIDI<Type>&)serial##Name);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue