Merge 37ef1d48d9 into 0d605dc8a7
This commit is contained in:
commit
53e3289e3d
|
|
@ -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:
|
public:
|
||||||
inline void setHandleMessage(void (*fptr)(const MidiMessage&)) { mMessageCallback = fptr; };
|
inline void setHandleMessage(void (*fptr)(const MidiMessage&)) { mMessageCallback = fptr; };
|
||||||
inline void setHandleError(ErrorCallback fptr) { mErrorCallback = fptr; }
|
inline void setHandleError(ErrorCallback fptr) { mErrorCallback = fptr; }
|
||||||
|
inline void setHandleActiveSensingTimeout(ActiveSensingTimeoutCallback fptr) { mActiveSensingTimeoutCallback = fptr; }
|
||||||
inline void setHandleNoteOff(NoteOffCallback fptr) { mNoteOffCallback = fptr; }
|
inline void setHandleNoteOff(NoteOffCallback fptr) { mNoteOffCallback = fptr; }
|
||||||
inline void setHandleNoteOn(NoteOnCallback fptr) { mNoteOnCallback = fptr; }
|
inline void setHandleNoteOn(NoteOnCallback fptr) { mNoteOnCallback = fptr; }
|
||||||
inline void setHandleAfterTouchPoly(AfterTouchPolyCallback fptr) { mAfterTouchPolyCallback = fptr; }
|
inline void setHandleAfterTouchPoly(AfterTouchPolyCallback fptr) { mAfterTouchPolyCallback = fptr; }
|
||||||
|
|
@ -212,6 +213,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;
|
||||||
|
|
@ -282,8 +284,7 @@ private:
|
||||||
MidiMessage mMessage;
|
MidiMessage mMessage;
|
||||||
unsigned long mLastMessageSentTime;
|
unsigned long mLastMessageSentTime;
|
||||||
unsigned long mLastMessageReceivedTime;
|
unsigned long mLastMessageReceivedTime;
|
||||||
unsigned long mSenderActiveSensingPeriodicity;
|
bool mReceiverActiveSensingActive;
|
||||||
bool mReceiverActiveSensingActivated;
|
|
||||||
int8_t mLastError;
|
int8_t mLastError;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
62
src/MIDI.hpp
62
src/MIDI.hpp
|
|
@ -44,11 +44,10 @@ inline MidiInterface<Transport, Settings, Platform>::MidiInterface(Transport& in
|
||||||
, mThruFilterMode(Thru::Full)
|
, mThruFilterMode(Thru::Full)
|
||||||
, mLastMessageSentTime(0)
|
, mLastMessageSentTime(0)
|
||||||
, mLastMessageReceivedTime(0)
|
, mLastMessageReceivedTime(0)
|
||||||
, mSenderActiveSensingPeriodicity(0)
|
, mReceiverActiveSensingActive(false)
|
||||||
, mReceiverActiveSensingActivated(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 +83,8 @@ void MidiInterface<Transport, Settings, Platform>::begin(Channel inChannel)
|
||||||
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;
|
||||||
|
|
@ -712,27 +712,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)
|
|
||||||
{
|
{
|
||||||
sendActiveSensing();
|
// Send ActiveSensing <Settings::ActiveSensingPeriodicity> ms after the last command
|
||||||
mLastMessageSentTime = Platform::now();
|
if ((Platform::now() - mLastMessageSentTime) > Settings::SenderActiveSensingPeriodicity)
|
||||||
|
sendActiveSensing();
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
|
@ -744,25 +749,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.
|
mLastMessageReceivedTime = Platform::now();
|
||||||
// When a timeout occurs, an error message is send and time keeping ends.
|
|
||||||
mReceiverActiveSensingActivated = true;
|
|
||||||
|
|
||||||
// is ErrorActiveSensingTimeout bit in mLastError on
|
if (mMessage.type == ActiveSensing && !mReceiverActiveSensingActive)
|
||||||
if (mLastError & (1 << (ErrorActiveSensingTimeout - 1)))
|
|
||||||
{
|
{
|
||||||
mLastError &= ~(1UL << ErrorActiveSensingTimeout); // clear the ErrorActiveSensingTimeout bit
|
mReceiverActiveSensingActive = true;
|
||||||
if (mErrorCallback)
|
|
||||||
mErrorCallback(mLastError);
|
mActiveSensingTimeoutCallback(mReceiverActiveSensingActive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep the time of the last received message, so we can check for the timeout
|
|
||||||
if (Settings::UseReceiverActiveSensing && mReceiverActiveSensingActivated)
|
|
||||||
mLastMessageReceivedTime = Platform::now();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
handleNullVelocityNoteOnAsNoteOff();
|
handleNullVelocityNoteOnAsNoteOff();
|
||||||
|
|
@ -1384,7 +1382,7 @@ inline void MidiInterface<Transport, Settings, Platform>::turnThruOff()
|
||||||
template<class Transport, class Settings, class Platform>
|
template<class Transport, class Settings, class Platform>
|
||||||
inline void MidiInterface<Transport, Settings, Platform>::UpdateLastSentTime()
|
inline void MidiInterface<Transport, Settings, Platform>::UpdateLastSentTime()
|
||||||
{
|
{
|
||||||
if (Settings::UseSenderActiveSensing && mSenderActiveSensingPeriodicity)
|
if (Settings::UseSenderActiveSensing)
|
||||||
mLastMessageSentTime = Platform::now();
|
mLastMessageSentTime = Platform::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,10 +46,6 @@ 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
|
||||||
|
|
||||||
|
|
@ -61,13 +57,13 @@ 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);
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -121,10 +121,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);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue