Skip to content

Commit b82f241

Browse files
lathoubfranky47
authored andcommitted
reworked ActiveSensing
reworked ActiveSensing using input from a variety of device MIDI Implementation manuals (Roland, KORG, Yamaha) found on the internet. Receiving ActiveSensing: 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. Sending ActiveSensing: send x ms after the last sent command
1 parent 350f6d1 commit b82f241

File tree

6 files changed

+169
-43
lines changed

6 files changed

+169
-43
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#include <MIDI.h>
2+
USING_NAMESPACE_MIDI
3+
4+
struct MyMIDISettings : public MIDI_NAMESPACE::DefaultSettings
5+
{
6+
// When setting UseReceiverActiveSensing to true, MIDI.read() *must* be called
7+
// as often as possible (1000 / SenderActiveSensingPeriodicity per second).
8+
//
9+
// setting UseReceiverActiveSensing to true, adds 174 bytes of code.
10+
//
11+
// (Taken from a Roland MIDI Implementation Owner's manual)
12+
// Once an Active Sensing message is received, the unit will begin monitoring
13+
// the interval between all subsequent messages. If there is an interval of 420 ms
14+
// or longer between messages while monitoring is active, the same processing
15+
// as when All Sound Off, All Notes Off,and Reset All Controllers messages are
16+
// received will be carried out. The unit will then stopmonitoring the message interval.
17+
18+
static const bool UseReceiverActiveSensing = true;
19+
20+
static const uint16_t ReceiverActiveSensingTimeout = 420;
21+
};
22+
23+
MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, Serial1, MIDI, MyMIDISettings);
24+
25+
void errorHandler(int8_t err)
26+
{
27+
if (bitRead(err, ErrorActiveSensingTimeout))
28+
{
29+
MIDI.sendControlChange(AllSoundOff, 0, 1);
30+
MIDI.sendControlChange(AllNotesOff, 0, 1);
31+
MIDI.sendControlChange(ResetAllControllers, 0, 1);
32+
33+
digitalWrite(LED_BUILTIN, HIGH);
34+
}
35+
else
36+
digitalWrite(LED_BUILTIN, LOW);
37+
}
38+
39+
void setup()
40+
{
41+
pinMode(LED_BUILTIN, OUTPUT);
42+
digitalWrite(LED_BUILTIN, LOW);
43+
44+
MIDI.setHandleError(errorHandler);
45+
MIDI.begin(1);
46+
}
47+
48+
void loop()
49+
{
50+
MIDI.read();
51+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#include <MIDI.h>
2+
USING_NAMESPACE_MIDI
3+
4+
struct MyMIDISettings : public MIDI_NAMESPACE::DefaultSettings
5+
{
6+
// When setting UseSenderActiveSensing to true, MIDI.read() *must* be called
7+
// as often as possible (1000 / SenderActiveSensingPeriodicity per second).
8+
//
9+
// setting UseSenderActiveSensing to true, adds 34 bytes of code.
10+
//
11+
// When using Active Sensing, call MIDI.read(); in the Arduino loop()
12+
//
13+
// from 'a' MIDI implementation manual: "Sent periodically"
14+
// In the example here, a NoteOn is send every 1000ms (1s), ActiveSensing is
15+
// send every 250ms after the last command.
16+
// Logging the command will look like this:
17+
//
18+
// ...
19+
// A.Sense FE
20+
// A.Sense FE
21+
// A.Sense FE
22+
// NoteOn 90 04 37 [E-2]
23+
// A.Sense FE
24+
// A.Sense FE
25+
// A.Sense FE
26+
// NoteOn 90 04 37 [E-2]
27+
// A.Sense FE
28+
// A.Sense FE
29+
// A.Sense FE
30+
// NoteOn 90 04 37 [E-2]
31+
// ...
32+
33+
static const bool UseSenderActiveSensing = true;
34+
35+
static const uint16_t SenderActiveSensingPeriodicity = 250;
36+
};
37+
38+
unsigned long t1 = millis();
39+
40+
MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, Serial1, MIDI, MyMIDISettings);
41+
42+
void setup()
43+
{
44+
MIDI.begin(1);
45+
}
46+
47+
void loop()
48+
{
49+
MIDI.read();
50+
51+
// send a note every second
52+
if ((millis() - t1) > 1000)
53+
{
54+
t1 = millis();
55+
56+
MIDI.sendNoteOn(random(1, 127), 55, 1);
57+
}
58+
}

src/MIDI.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ class MidiInterface
293293
MidiMessage mMessage;
294294
unsigned long mLastMessageSentTime;
295295
unsigned long mLastMessageReceivedTime;
296-
bool mReceiverActiveSensingActivated;
296+
bool mReceiverActiveSensingActive;
297297
int8_t mLastError;
298298

299299
private:

src/MIDI.hpp

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,11 @@ inline MidiInterface<Transport, Settings, Platform>::MidiInterface(Transport& in
4242
, mCurrentNrpnNumber(0xffff)
4343
, mLastMessageSentTime(0)
4444
, mLastMessageReceivedTime(0)
45-
, mReceiverActiveSensingActivated(false)
45+
, mReceiverActiveSensingActive(false)
4646
, mLastError(0)
4747
, mSenderActiveSensingPeriodicity(Settings::SenderActiveSensingPeriodicity)
4848
{
49+
static_assert(!(Settings::UseSenderActiveSensing && Settings::UseReceiverActiveSensing), "UseSenderActiveSensing and UseReceiverActiveSensing can't be both set to true.");
4950
}
5051

5152
/*! \brief Destructor for MidiInterface.
@@ -81,7 +82,8 @@ MidiInterface<Transport, Settings, Platform>& MidiInterface<Transport, Settings,
8182
mCurrentRpnNumber = 0xffff;
8283
mCurrentNrpnNumber = 0xffff;
8384

84-
mLastMessageSentTime = Platform::now();
85+
mLastMessageSentTime =
86+
mLastMessageReceivedTime = Platform::now();
8587

8688
mMessage.valid = false;
8789
mMessage.type = InvalidType;
@@ -756,27 +758,36 @@ template<class Transport, class Settings, class Platform>
756758
inline bool MidiInterface<Transport, Settings, Platform>::read(Channel inChannel)
757759
{
758760
#ifndef RegionActiveSending
761+
759762
// Active Sensing. This message is intended to be sent
760763
// repeatedly to tell the receiver that a connection is alive. Use
761-
// of this message is optional. When initially received, the
762-
// receiver will expect to receive another Active Sensing
763-
// message each 300ms (max), and if it does not then it will
764-
// assume that the connection has been terminated. At
765-
// termination, the receiver will turn off all voices and return to
766-
// normal (non- active sensing) operation.
767-
if (Settings::UseSenderActiveSensing && (Platform::now() - mLastMessageSentTime) > Settings::SenderActiveSensingPeriodicity)
764+
// of this message is optional.
765+
if (Settings::UseSenderActiveSensing)
768766
{
769-
sendActiveSensing();
770-
mLastMessageSentTime = Platform::now();
767+
// Send ActiveSensing <Settings::ActiveSensingPeriodicity> ms after the last command
768+
if ((Platform::now() - mLastMessageSentTime) > Settings::SenderActiveSensingPeriodicity)
769+
sendActiveSensing();
771770
}
772771

773-
if (Settings::UseReceiverActiveSensing && mReceiverActiveSensingActivated && (mLastMessageReceivedTime + ActiveSensingTimeout < Platform::now()))
772+
if (Settings::UseReceiverActiveSensing && mReceiverActiveSensingActive)
774773
{
775-
mReceiverActiveSensingActivated = false;
776-
777-
mLastError |= 1UL << ErrorActiveSensingTimeout; // set the ErrorActiveSensingTimeout bit
778-
if (mErrorCallback)
779-
mErrorCallback(mLastError);
774+
if ((Platform::now() - mLastMessageReceivedTime > Settings::ReceiverActiveSensingTimeout))
775+
{
776+
// Once an Active Sensing message is received, the unit will begin monitoring
777+
// the intervalbetween all subsequent messages. If there is an interval of 420 ms
778+
// or longer betweenmessages while monitoring is active, the same processing
779+
// as when All Sound Off, All Notes Off,and Reset All Controllers messages are
780+
// received will be carried out. The unit will then stopmonitoring the message interval.
781+
mReceiverActiveSensingActive = false;
782+
783+
// its up to the error handler to send the stop processing messages
784+
// (also, no clue what the channel is on which to send them)
785+
786+
// no need to check if bit is already set, it is not (due to the mActiveSensingActive switch)
787+
mLastError |= 1UL << ErrorActiveSensingTimeout; // set the ErrorActiveSensingTimeout bit
788+
if (mErrorCallback)
789+
mErrorCallback(mLastError);
790+
}
780791
}
781792
#endif
782793

@@ -788,25 +799,26 @@ inline bool MidiInterface<Transport, Settings, Platform>::read(Channel inChannel
788799

789800
#ifndef RegionActiveSending
790801

791-
if (Settings::UseReceiverActiveSensing && mMessage.type == ActiveSensing)
802+
if (Settings::UseReceiverActiveSensing)
792803
{
793-
// When an ActiveSensing message is received, the time keeping is activated.
794-
// When a timeout occurs, an error message is send and time keeping ends.
795-
mReceiverActiveSensingActivated = true;
804+
mLastMessageReceivedTime = Platform::now();
796805

797-
// is ErrorActiveSensingTimeout bit in mLastError on
798-
if (mLastError & (1 << (ErrorActiveSensingTimeout - 1)))
806+
if (mMessage.type == ActiveSensing && !mReceiverActiveSensingActive)
799807
{
800-
mLastError &= ~(1UL << ErrorActiveSensingTimeout); // clear the ErrorActiveSensingTimeout bit
808+
// Once an Active Sensing message is received, the unit will begin monitoring
809+
// the intervalbetween all subsequent messages. If there is an interval of 420 ms
810+
// or longer betweenmessages while monitoring is active, the same processing
811+
// as when All Sound Off, All Notes Off,and Reset All Controllers messages are
812+
// received will be carried out. The unit will then stopmonitoring the message interval.
813+
mReceiverActiveSensingActive = true;
814+
815+
// Clear the ErrorActiveSensingTimeout bit
816+
mLastError &= ~(1UL << ErrorActiveSensingTimeout);
801817
if (mErrorCallback)
802818
mErrorCallback(mLastError);
803819
}
804820
}
805821

806-
// Keep the time of the last received message, so we can check for the timeout
807-
if (Settings::UseReceiverActiveSensing && mReceiverActiveSensingActivated)
808-
mLastMessageReceivedTime = Platform::now();
809-
810822
#endif
811823

812824
handleNullVelocityNoteOnAsNoteOff();

src/midi_Defs.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,6 @@ BEGIN_MIDI_NAMESPACE
4646
#define MIDI_PITCHBEND_MIN -8192
4747
#define MIDI_PITCHBEND_MAX 8191
4848

49-
/*! Receiving Active Sensing
50-
*/
51-
static const uint16_t ActiveSensingTimeout = 300;
52-
5349
// -----------------------------------------------------------------------------
5450
// Type definitions
5551

src/midi_Settings.h

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,16 +74,14 @@ struct DefaultSettings
7474

7575
/*! Global switch to turn on/off sender ActiveSensing
7676
Set to true to send ActiveSensing
77-
Set to false will not send ActiveSensing message (will also save memory)
78-
as often as possible (1000 / SenderActiveSensingPeriodicity per second).
79-
*/
80-
static const bool UseSenderActiveSensing = false;
77+
/*! Global switch to turn on/off sending and receiving ActiveSensing
78+
Set to true to activate ActiveSensing
79+
Set to false will not send/receive ActiveSensing message (will also save 236 bytes of memory)
8180
82-
/*! Global switch to turn on/off receiver ActiveSensing
83-
Set to true to check for message timeouts (via ErrorCallback)
84-
Set to false will not check if chained device are still alive (if they use ActiveSensing) (will also save memory)
81+
When setting UseActiveSensing to true, MIDI.read() *must* be called
82+
as often as possible (1000 / ActiveSensingPeriodicity per second).
8583
*/
86-
static const bool UseReceiverActiveSensing = false;
84+
static const bool UseSenderActiveSensing = false;
8785

8886
/*! Active Sensing is intended to be sent
8987
repeatedly by the sender to tell the receiver that a connection is alive. Use
@@ -95,9 +93,20 @@ struct DefaultSettings
9593
normal (non- active sensing) operation.
9694
9795
Typical value is 250 (ms) - an Active Sensing command is send every 250ms.
98-
(All Roland devices send Active Sensing every 250ms)
96+
(Most Roland devices send Active Sensing every 250ms)
9997
*/
100-
static const uint16_t SenderActiveSensingPeriodicity = 250;
98+
static const uint16_t SenderActiveSensingPeriodicity = 300;
99+
100+
/*! Once an Active Sensing message is received, the unit will begin monitoring
101+
the intervalbetween all subsequent messages. If there is an interval of ActiveSensingPeriodicity ms
102+
or longer betweenmessages while monitoring is active, the same processing
103+
as when All Sound Off, All Notes Off,and Reset All Controllers messages are
104+
received will be carried out. The unit will then stopmonitoring the message interval.
105+
*/
106+
static const bool UseReceiverActiveSensing = false;
107+
108+
static const uint16_t ReceiverActiveSensingTimeout = 300;
109+
101110
};
102111

103112
END_MIDI_NAMESPACE

0 commit comments

Comments
 (0)