Merge branch 'avr_core' into Arduino
This commit is contained in:
commit
efd72f9df6
371
src/MIDI.cpp
371
src/MIDI.cpp
|
|
@ -33,8 +33,7 @@ MIDI_Class MIDI;
|
||||||
/*! \brief Default constructor for MIDI_Class. */
|
/*! \brief Default constructor for MIDI_Class. */
|
||||||
MIDI_Class::MIDI_Class()
|
MIDI_Class::MIDI_Class()
|
||||||
{
|
{
|
||||||
|
#if COMPILE_MIDI_IN && USE_CALLBACKS
|
||||||
#if USE_CALLBACKS
|
|
||||||
|
|
||||||
// Initialise callbacks to NULL pointer
|
// Initialise callbacks to NULL pointer
|
||||||
mNoteOffCallback = NULL;
|
mNoteOffCallback = NULL;
|
||||||
|
|
@ -57,7 +56,6 @@ MIDI_Class::MIDI_Class()
|
||||||
mSystemResetCallback = NULL;
|
mSystemResetCallback = NULL;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -79,20 +77,14 @@ MIDI_Class::~MIDI_Class()
|
||||||
*/
|
*/
|
||||||
void MIDI_Class::begin(const byte inChannel)
|
void MIDI_Class::begin(const byte inChannel)
|
||||||
{
|
{
|
||||||
|
|
||||||
// Initialise the Serial port
|
// Initialise the Serial port
|
||||||
MIDI_SERIAL_PORT.begin(MIDI_BAUDRATE);
|
MIDI_SERIAL_PORT.begin(MIDI_BAUDRATE);
|
||||||
|
|
||||||
|
#if COMPILE_MIDI_OUT && USE_RUNNING_STATUS
|
||||||
#if COMPILE_MIDI_OUT
|
|
||||||
|
|
||||||
#if USE_RUNNING_STATUS
|
|
||||||
|
|
||||||
mRunningStatus_TX = InvalidType;
|
mRunningStatus_TX = InvalidType;
|
||||||
|
|
||||||
#endif // USE_RUNNING_STATUS
|
#endif // COMPILE_MIDI_OUT && USE_RUNNING_STATUS
|
||||||
|
|
||||||
#endif // COMPILE_MIDI_OUT
|
|
||||||
|
|
||||||
|
|
||||||
#if COMPILE_MIDI_IN
|
#if COMPILE_MIDI_IN
|
||||||
|
|
@ -131,9 +123,7 @@ void MIDI_Class::begin(const byte inChannel)
|
||||||
const byte MIDI_Class::genstatus(const kMIDIType inType,
|
const byte MIDI_Class::genstatus(const kMIDIType inType,
|
||||||
const byte inChannel) const
|
const byte inChannel) const
|
||||||
{
|
{
|
||||||
|
|
||||||
return ((byte)inType | ((inChannel-1) & 0x0F));
|
return ((byte)inType | ((inChannel-1) & 0x0F));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -153,9 +143,11 @@ void MIDI_Class::send(kMIDIType type,
|
||||||
byte data2,
|
byte data2,
|
||||||
byte channel)
|
byte channel)
|
||||||
{
|
{
|
||||||
|
|
||||||
// Then test if channel is valid
|
// Then test if channel is valid
|
||||||
if (channel >= MIDI_CHANNEL_OFF || channel == MIDI_CHANNEL_OMNI || type < NoteOff) {
|
if (channel >= MIDI_CHANNEL_OFF ||
|
||||||
|
channel == MIDI_CHANNEL_OMNI ||
|
||||||
|
type < NoteOff)
|
||||||
|
{
|
||||||
|
|
||||||
#if USE_RUNNING_STATUS
|
#if USE_RUNNING_STATUS
|
||||||
mRunningStatus_TX = InvalidType;
|
mRunningStatus_TX = InvalidType;
|
||||||
|
|
@ -164,9 +156,8 @@ void MIDI_Class::send(kMIDIType type,
|
||||||
return; // Don't send anything
|
return; // Don't send anything
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type <= PitchBend) {
|
if (type <= PitchBend) // Channel messages
|
||||||
// Channel messages
|
{
|
||||||
|
|
||||||
// Protection: remove MSBs on data
|
// Protection: remove MSBs on data
|
||||||
data1 &= 0x7F;
|
data1 &= 0x7F;
|
||||||
data2 &= 0x7F;
|
data2 &= 0x7F;
|
||||||
|
|
@ -175,7 +166,8 @@ void MIDI_Class::send(kMIDIType type,
|
||||||
|
|
||||||
#if USE_RUNNING_STATUS
|
#if USE_RUNNING_STATUS
|
||||||
// Check Running Status
|
// Check Running Status
|
||||||
if (mRunningStatus_TX != statusbyte) {
|
if (mRunningStatus_TX != statusbyte)
|
||||||
|
{
|
||||||
// New message, memorise and send header
|
// New message, memorise and send header
|
||||||
mRunningStatus_TX = statusbyte;
|
mRunningStatus_TX = statusbyte;
|
||||||
MIDI_SERIAL_PORT.write(mRunningStatus_TX);
|
MIDI_SERIAL_PORT.write(mRunningStatus_TX);
|
||||||
|
|
@ -187,16 +179,13 @@ void MIDI_Class::send(kMIDIType type,
|
||||||
|
|
||||||
// Then send data
|
// Then send data
|
||||||
MIDI_SERIAL_PORT.write(data1);
|
MIDI_SERIAL_PORT.write(data1);
|
||||||
if (type != ProgramChange && type != AfterTouchChannel) {
|
if (type != ProgramChange && type != AfterTouchChannel)
|
||||||
MIDI_SERIAL_PORT.write(data2);
|
MIDI_SERIAL_PORT.write(data2);
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (type >= TuneRequest && type <= SystemReset) {
|
if (type >= TuneRequest && type <= SystemReset)
|
||||||
// System Real-time and 1 byte.
|
sendRealTime(type); // System Real-time and 1 byte.
|
||||||
sendRealTime(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -213,17 +202,18 @@ void MIDI_Class::sendNoteOn(byte NoteNumber,
|
||||||
byte Velocity,
|
byte Velocity,
|
||||||
byte Channel)
|
byte Channel)
|
||||||
{
|
{
|
||||||
|
|
||||||
send(NoteOn,NoteNumber,Velocity,Channel);
|
send(NoteOn,NoteNumber,Velocity,Channel);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! \brief Send a Note Off message (a real Note Off, not a Note On with null velocity)
|
/*! \brief Send a Note Off message
|
||||||
\param NoteNumber Pitch value in the MIDI format (0 to 127).
|
\param NoteNumber Pitch value in the MIDI format (0 to 127).
|
||||||
\param Velocity Release velocity (0 to 127).
|
\param Velocity Release velocity (0 to 127).
|
||||||
\param Channel The channel on which the message will be sent (1 to 16).
|
\param Channel The channel on which the message will be sent (1 to 16).
|
||||||
|
|
||||||
|
Note: you can send NoteOn with zero velocity to make a NoteOff, this is based
|
||||||
|
on the Running Status principle, to avoid sending status messages and thus
|
||||||
|
sending only NoteOn data. This method will always send a real NoteOff message.
|
||||||
Take a look at the values, names and frequencies of notes here:
|
Take a look at the values, names and frequencies of notes here:
|
||||||
http://www.phys.unsw.edu.au/jw/notes.html
|
http://www.phys.unsw.edu.au/jw/notes.html
|
||||||
*/
|
*/
|
||||||
|
|
@ -231,9 +221,7 @@ void MIDI_Class::sendNoteOff(byte NoteNumber,
|
||||||
byte Velocity,
|
byte Velocity,
|
||||||
byte Channel)
|
byte Channel)
|
||||||
{
|
{
|
||||||
|
|
||||||
send(NoteOff,NoteNumber,Velocity,Channel);
|
send(NoteOff,NoteNumber,Velocity,Channel);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -244,9 +232,7 @@ void MIDI_Class::sendNoteOff(byte NoteNumber,
|
||||||
void MIDI_Class::sendProgramChange(byte ProgramNumber,
|
void MIDI_Class::sendProgramChange(byte ProgramNumber,
|
||||||
byte Channel)
|
byte Channel)
|
||||||
{
|
{
|
||||||
|
|
||||||
send(ProgramChange,ProgramNumber,0,Channel);
|
send(ProgramChange,ProgramNumber,0,Channel);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -262,13 +248,11 @@ void MIDI_Class::sendControlChange(byte ControlNumber,
|
||||||
byte ControlValue,
|
byte ControlValue,
|
||||||
byte Channel)
|
byte Channel)
|
||||||
{
|
{
|
||||||
|
|
||||||
send(ControlChange,ControlNumber,ControlValue,Channel);
|
send(ControlChange,ControlNumber,ControlValue,Channel);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! \brief Send a Polyphonic AfterTouch message (applies to only one specified note)
|
/*! \brief Send a Polyphonic AfterTouch message (applies to a specified note)
|
||||||
\param NoteNumber The note to apply AfterTouch to (0 to 127).
|
\param NoteNumber The note to apply AfterTouch to (0 to 127).
|
||||||
\param Pressure The amount of AfterTouch to apply (0 to 127).
|
\param Pressure The amount of AfterTouch to apply (0 to 127).
|
||||||
\param Channel The channel on which the message will be sent (1 to 16).
|
\param Channel The channel on which the message will be sent (1 to 16).
|
||||||
|
|
@ -277,9 +261,7 @@ void MIDI_Class::sendPolyPressure(byte NoteNumber,
|
||||||
byte Pressure,
|
byte Pressure,
|
||||||
byte Channel)
|
byte Channel)
|
||||||
{
|
{
|
||||||
|
|
||||||
send(AfterTouchPoly,NoteNumber,Pressure,Channel);
|
send(AfterTouchPoly,NoteNumber,Pressure,Channel);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -290,9 +272,7 @@ void MIDI_Class::sendPolyPressure(byte NoteNumber,
|
||||||
void MIDI_Class::sendAfterTouch(byte Pressure,
|
void MIDI_Class::sendAfterTouch(byte Pressure,
|
||||||
byte Channel)
|
byte Channel)
|
||||||
{
|
{
|
||||||
|
|
||||||
send(AfterTouchChannel,Pressure,0,Channel);
|
send(AfterTouchChannel,Pressure,0,Channel);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -305,11 +285,8 @@ void MIDI_Class::sendAfterTouch(byte Pressure,
|
||||||
void MIDI_Class::sendPitchBend(int PitchValue,
|
void MIDI_Class::sendPitchBend(int PitchValue,
|
||||||
byte Channel)
|
byte Channel)
|
||||||
{
|
{
|
||||||
|
const unsigned int bend = PitchValue - MIDI_PITCHBEND_MIN;
|
||||||
unsigned int bend = PitchValue - MIDI_PITCHBEND_MIN;
|
|
||||||
|
|
||||||
send(PitchBend,(bend & 0x7F),(bend >> 7) & 0x7F,Channel);
|
send(PitchBend,(bend & 0x7F),(bend >> 7) & 0x7F,Channel);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -322,10 +299,8 @@ void MIDI_Class::sendPitchBend(int PitchValue,
|
||||||
void MIDI_Class::sendPitchBend(double PitchValue,
|
void MIDI_Class::sendPitchBend(double PitchValue,
|
||||||
byte Channel)
|
byte Channel)
|
||||||
{
|
{
|
||||||
|
const int pitchval = PitchValue * MIDI_PITCHBEND_MAX;
|
||||||
int pitchval = PitchValue * MIDI_PITCHBEND_MAX;
|
|
||||||
sendPitchBend(pitchval,Channel);
|
sendPitchBend(pitchval,Channel);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -342,34 +317,24 @@ void MIDI_Class::sendSysEx(int length,
|
||||||
const byte *const array,
|
const byte *const array,
|
||||||
bool ArrayContainsBoundaries)
|
bool ArrayContainsBoundaries)
|
||||||
{
|
{
|
||||||
|
if (ArrayContainsBoundaries == false)
|
||||||
if (ArrayContainsBoundaries == false) {
|
{
|
||||||
|
|
||||||
MIDI_SERIAL_PORT.write(0xF0);
|
MIDI_SERIAL_PORT.write(0xF0);
|
||||||
|
|
||||||
for (int i=0;i<length;++i) {
|
for (int i=0;i<length;++i)
|
||||||
|
|
||||||
MIDI_SERIAL_PORT.write(array[i]);
|
MIDI_SERIAL_PORT.write(array[i]);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
MIDI_SERIAL_PORT.write(0xF7);
|
MIDI_SERIAL_PORT.write(0xF7);
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
for (int i=0;i<length;++i) {
|
for (int i=0;i<length;++i)
|
||||||
|
|
||||||
MIDI_SERIAL_PORT.write(array[i]);
|
MIDI_SERIAL_PORT.write(array[i]);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if USE_RUNNING_STATUS
|
#if USE_RUNNING_STATUS
|
||||||
mRunningStatus_TX = InvalidType;
|
mRunningStatus_TX = InvalidType;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -380,9 +345,7 @@ void MIDI_Class::sendSysEx(int length,
|
||||||
*/
|
*/
|
||||||
void MIDI_Class::sendTuneRequest()
|
void MIDI_Class::sendTuneRequest()
|
||||||
{
|
{
|
||||||
|
|
||||||
sendRealTime(TuneRequest);
|
sendRealTime(TuneRequest);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -394,29 +357,25 @@ void MIDI_Class::sendTuneRequest()
|
||||||
*/
|
*/
|
||||||
void MIDI_Class::sendTimeCodeQuarterFrame(byte TypeNibble, byte ValuesNibble)
|
void MIDI_Class::sendTimeCodeQuarterFrame(byte TypeNibble, byte ValuesNibble)
|
||||||
{
|
{
|
||||||
|
const byte data = ( ((TypeNibble & 0x07) << 4) | (ValuesNibble & 0x0F) );
|
||||||
byte data = ( ((TypeNibble & 0x07) << 4) | (ValuesNibble & 0x0F) );
|
|
||||||
sendTimeCodeQuarterFrame(data);
|
sendTimeCodeQuarterFrame(data);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! \brief Send a MIDI Time Code Quarter Frame.
|
/*! \brief Send a MIDI Time Code Quarter Frame.
|
||||||
|
|
||||||
|
See MIDI Specification for more information.
|
||||||
\param data if you want to encode directly the nibbles in your program,
|
\param data if you want to encode directly the nibbles in your program,
|
||||||
you can send the byte here.
|
you can send the byte here.
|
||||||
See MIDI Specification for more information.
|
|
||||||
*/
|
*/
|
||||||
void MIDI_Class::sendTimeCodeQuarterFrame(byte data)
|
void MIDI_Class::sendTimeCodeQuarterFrame(byte data)
|
||||||
{
|
{
|
||||||
|
|
||||||
MIDI_SERIAL_PORT.write((byte)TimeCodeQuarterFrame);
|
MIDI_SERIAL_PORT.write((byte)TimeCodeQuarterFrame);
|
||||||
MIDI_SERIAL_PORT.write(data);
|
MIDI_SERIAL_PORT.write(data);
|
||||||
|
|
||||||
#if USE_RUNNING_STATUS
|
#if USE_RUNNING_STATUS
|
||||||
mRunningStatus_TX = InvalidType;
|
mRunningStatus_TX = InvalidType;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -425,7 +384,6 @@ void MIDI_Class::sendTimeCodeQuarterFrame(byte data)
|
||||||
*/
|
*/
|
||||||
void MIDI_Class::sendSongPosition(unsigned int Beats)
|
void MIDI_Class::sendSongPosition(unsigned int Beats)
|
||||||
{
|
{
|
||||||
|
|
||||||
MIDI_SERIAL_PORT.write((byte)SongPosition);
|
MIDI_SERIAL_PORT.write((byte)SongPosition);
|
||||||
MIDI_SERIAL_PORT.write(Beats & 0x7F);
|
MIDI_SERIAL_PORT.write(Beats & 0x7F);
|
||||||
MIDI_SERIAL_PORT.write((Beats >> 7) & 0x7F);
|
MIDI_SERIAL_PORT.write((Beats >> 7) & 0x7F);
|
||||||
|
|
@ -433,21 +391,18 @@ void MIDI_Class::sendSongPosition(unsigned int Beats)
|
||||||
#if USE_RUNNING_STATUS
|
#if USE_RUNNING_STATUS
|
||||||
mRunningStatus_TX = InvalidType;
|
mRunningStatus_TX = InvalidType;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! \brief Send a Song Select message */
|
/*! \brief Send a Song Select message */
|
||||||
void MIDI_Class::sendSongSelect(byte SongNumber)
|
void MIDI_Class::sendSongSelect(byte SongNumber)
|
||||||
{
|
{
|
||||||
|
|
||||||
MIDI_SERIAL_PORT.write((byte)SongSelect);
|
MIDI_SERIAL_PORT.write((byte)SongSelect);
|
||||||
MIDI_SERIAL_PORT.write(SongNumber & 0x7F);
|
MIDI_SERIAL_PORT.write(SongNumber & 0x7F);
|
||||||
|
|
||||||
#if USE_RUNNING_STATUS
|
#if USE_RUNNING_STATUS
|
||||||
mRunningStatus_TX = InvalidType;
|
mRunningStatus_TX = InvalidType;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -460,7 +415,8 @@ void MIDI_Class::sendSongSelect(byte SongNumber)
|
||||||
*/
|
*/
|
||||||
void MIDI_Class::sendRealTime(kMIDIType Type)
|
void MIDI_Class::sendRealTime(kMIDIType Type)
|
||||||
{
|
{
|
||||||
switch (Type) {
|
switch (Type)
|
||||||
|
{
|
||||||
case TuneRequest: // Not really real-time, but one byte anyway.
|
case TuneRequest: // Not really real-time, but one byte anyway.
|
||||||
case Clock:
|
case Clock:
|
||||||
case Start:
|
case Start:
|
||||||
|
|
@ -475,12 +431,12 @@ void MIDI_Class::sendRealTime(kMIDIType Type)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not cancel Running Status for real-time messages as they can be interleaved within any message.
|
// Do not cancel Running Status for real-time messages as they can be
|
||||||
// Though, TuneRequest can be sent here, and as it is a System Common message, it must reset Running Status.
|
// interleaved within any message. Though, TuneRequest can be sent here,
|
||||||
|
// and as it is a System Common message, it must reset Running Status.
|
||||||
#if USE_RUNNING_STATUS
|
#if USE_RUNNING_STATUS
|
||||||
if (Type == TuneRequest) mRunningStatus_TX = InvalidType;
|
if (Type == TuneRequest) mRunningStatus_TX = InvalidType;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // COMPILE_MIDI_OUT
|
#endif // COMPILE_MIDI_OUT
|
||||||
|
|
@ -502,9 +458,7 @@ void MIDI_Class::sendRealTime(kMIDIType Type)
|
||||||
*/
|
*/
|
||||||
bool MIDI_Class::read()
|
bool MIDI_Class::read()
|
||||||
{
|
{
|
||||||
|
|
||||||
return read(mInputChannel);
|
return read(mInputChannel);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -513,12 +467,13 @@ bool MIDI_Class::read()
|
||||||
*/
|
*/
|
||||||
bool MIDI_Class::read(const byte inChannel)
|
bool MIDI_Class::read(const byte inChannel)
|
||||||
{
|
{
|
||||||
|
if (inChannel >= MIDI_CHANNEL_OFF)
|
||||||
|
return false; // MIDI Input disabled.
|
||||||
|
|
||||||
if (inChannel >= MIDI_CHANNEL_OFF) return false; // MIDI Input disabled.
|
if (parse(inChannel))
|
||||||
|
{
|
||||||
if (parse(inChannel)) {
|
if (input_filter(inChannel))
|
||||||
|
{
|
||||||
if (input_filter(inChannel)) {
|
|
||||||
|
|
||||||
#if (COMPILE_MIDI_OUT && COMPILE_MIDI_THRU)
|
#if (COMPILE_MIDI_OUT && COMPILE_MIDI_THRU)
|
||||||
thru_filter(inChannel);
|
thru_filter(inChannel);
|
||||||
|
|
@ -527,50 +482,44 @@ bool MIDI_Class::read(const byte inChannel)
|
||||||
#if USE_CALLBACKS
|
#if USE_CALLBACKS
|
||||||
launchCallback();
|
launchCallback();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Private method: MIDI parser
|
// Private method: MIDI parser
|
||||||
bool MIDI_Class::parse(byte inChannel)
|
bool MIDI_Class::parse(byte inChannel)
|
||||||
{
|
{
|
||||||
|
const byte bytes_available = MIDI_SERIAL_PORT.available();
|
||||||
|
|
||||||
const int bytes_available = MIDI_SERIAL_PORT.available();
|
if (bytes_available == 0)
|
||||||
|
|
||||||
if (bytes_available <= 0) {
|
|
||||||
// No data available.
|
// No data available.
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
// If the buffer is full -> Don't Panic! Call the Vogons to destroy it.
|
|
||||||
if (bytes_available == 128) {
|
|
||||||
MIDI_SERIAL_PORT.flush();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
/* Parsing algorithm:
|
/* Parsing algorithm:
|
||||||
Get a byte from the serial buffer.
|
Get a byte from the serial buffer.
|
||||||
* If there is no pending message to be recomposed, start a new one.
|
* If there is no pending message to be recomposed, start a new one.
|
||||||
- Find type and channel (if pertinent)
|
- Find type and channel (if pertinent)
|
||||||
- Look for other bytes in buffer, call parser recursively, until the message is assembled or the buffer is empty.
|
- Look for other bytes in buffer, call parser recursively,
|
||||||
* Else, add the extracted byte to the pending message, and check validity. When the message is done, store it.
|
until the message is assembled or the buffer is empty.
|
||||||
|
* Else, add the extracted byte to the pending message, and check validity.
|
||||||
|
When the message is done, store it.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
const byte extracted = MIDI_SERIAL_PORT.read();
|
const byte extracted = MIDI_SERIAL_PORT.read();
|
||||||
|
|
||||||
if (mPendingMessageIndex == 0) { // Start a new pending message
|
if (mPendingMessageIndex == 0)
|
||||||
|
{
|
||||||
|
// Start a new pending message
|
||||||
mPendingMessage[0] = extracted;
|
mPendingMessage[0] = extracted;
|
||||||
|
|
||||||
// Check for running status first
|
// Check for running status first
|
||||||
switch (getTypeFromStatusByte(mRunningStatus_RX)) {
|
switch (getTypeFromStatusByte(mRunningStatus_RX))
|
||||||
|
{
|
||||||
// Only these types allow Running Status:
|
// Only these types allow Running Status:
|
||||||
case NoteOff:
|
case NoteOff:
|
||||||
case NoteOn:
|
case NoteOn:
|
||||||
|
|
@ -580,13 +529,16 @@ bool MIDI_Class::parse(byte inChannel)
|
||||||
case AfterTouchChannel:
|
case AfterTouchChannel:
|
||||||
case PitchBend:
|
case PitchBend:
|
||||||
|
|
||||||
// If the status byte is not received, prepend it to the pending message
|
// If the status byte is not received, prepend it
|
||||||
if (extracted < 0x80) {
|
// to the pending message
|
||||||
|
if (extracted < 0x80)
|
||||||
|
{
|
||||||
mPendingMessage[0] = mRunningStatus_RX;
|
mPendingMessage[0] = mRunningStatus_RX;
|
||||||
mPendingMessage[1] = extracted;
|
mPendingMessage[1] = extracted;
|
||||||
mPendingMessageIndex = 1;
|
mPendingMessageIndex = 1;
|
||||||
}
|
}
|
||||||
// Else: well, we received another status byte, so the running status does not apply here.
|
// Else: well, we received another status byte,
|
||||||
|
// so the running status does not apply here.
|
||||||
// It will be updated upon completion of this message.
|
// It will be updated upon completion of this message.
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
@ -597,8 +549,8 @@ bool MIDI_Class::parse(byte inChannel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
switch (getTypeFromStatusByte(mPendingMessage[0])) {
|
switch (getTypeFromStatusByte(mPendingMessage[0]))
|
||||||
|
{
|
||||||
// 1 byte messages
|
// 1 byte messages
|
||||||
case Start:
|
case Start:
|
||||||
case Continue:
|
case Continue:
|
||||||
|
|
@ -644,7 +596,9 @@ bool MIDI_Class::parse(byte inChannel)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SystemExclusive:
|
case SystemExclusive:
|
||||||
mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE; // As the message can be any lenght between 3 and MIDI_SYSEX_ARRAY_SIZE bytes
|
// The message can be any lenght
|
||||||
|
// between 3 and MIDI_SYSEX_ARRAY_SIZE bytes
|
||||||
|
mPendingMessageExpectedLenght = MIDI_SYSEX_ARRAY_SIZE;
|
||||||
mRunningStatus_RX = InvalidType;
|
mRunningStatus_RX = InvalidType;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -669,14 +623,15 @@ bool MIDI_Class::parse(byte inChannel)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
// First, test if this is a status byte
|
// First, test if this is a status byte
|
||||||
if (extracted >= 0x80) {
|
if (extracted >= 0x80)
|
||||||
|
{
|
||||||
// Reception of status bytes in the middle of an uncompleted message
|
// Reception of status bytes in the middle of an uncompleted message
|
||||||
// are allowed only for interleaved Real Time message or EOX
|
// are allowed only for interleaved Real Time message or EOX
|
||||||
switch (extracted) {
|
switch (extracted)
|
||||||
|
{
|
||||||
case Clock:
|
case Clock:
|
||||||
case Start:
|
case Start:
|
||||||
case Continue:
|
case Continue:
|
||||||
|
|
@ -684,15 +639,12 @@ bool MIDI_Class::parse(byte inChannel)
|
||||||
case ActiveSensing:
|
case ActiveSensing:
|
||||||
case SystemReset:
|
case SystemReset:
|
||||||
|
|
||||||
/*
|
// Here we will have to extract the one-byte message,
|
||||||
This is tricky. Here we will have to extract the one-byte message,
|
// pass it to the structure for being read outside
|
||||||
pass it to the structure for being read outside the MIDI class,
|
// the MIDI class, and recompose the message it was
|
||||||
and recompose the message it was interleaved into.
|
// interleaved into. Oh, and without killing the running status..
|
||||||
|
// This is done by leaving the pending message as is,
|
||||||
Oh, and without killing the running status..
|
// it will be completed on next calls.
|
||||||
|
|
||||||
This is done by leaving the pending message as is, it will be completed on next calls.
|
|
||||||
*/
|
|
||||||
|
|
||||||
mMessage.type = (kMIDIType)extracted;
|
mMessage.type = (kMIDIType)extracted;
|
||||||
mMessage.data1 = 0;
|
mMessage.data1 = 0;
|
||||||
|
|
@ -705,27 +657,25 @@ bool MIDI_Class::parse(byte inChannel)
|
||||||
|
|
||||||
// End of Exclusive
|
// End of Exclusive
|
||||||
case 0xF7:
|
case 0xF7:
|
||||||
if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) {
|
if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive)
|
||||||
|
{
|
||||||
// Store System Exclusive array in midimsg structure
|
// Store System Exclusive array in midimsg structure
|
||||||
for (byte i=0;i<MIDI_SYSEX_ARRAY_SIZE;i++) {
|
for (byte i=0;i<MIDI_SYSEX_ARRAY_SIZE;i++)
|
||||||
mMessage.sysex_array[i] = mPendingMessage[i];
|
mMessage.sysex_array[i] = mPendingMessage[i];
|
||||||
}
|
|
||||||
|
|
||||||
mMessage.type = SystemExclusive;
|
mMessage.type = SystemExclusive;
|
||||||
|
|
||||||
// Get length
|
// Get length
|
||||||
mMessage.data1 = (mPendingMessageIndex+1) & 0xFF;
|
mMessage.data1 = (mPendingMessageIndex+1) & 0xFF;
|
||||||
mMessage.data2 = (mPendingMessageIndex+1) >> 8;
|
mMessage.data2 = (mPendingMessageIndex+1) >> 8;
|
||||||
|
|
||||||
mMessage.channel = 0;
|
mMessage.channel = 0;
|
||||||
mMessage.valid = true;
|
mMessage.valid = true;
|
||||||
|
|
||||||
reset_input_attributes();
|
reset_input_attributes();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
// Well well well.. error.
|
// Well well well.. error.
|
||||||
reset_input_attributes();
|
reset_input_attributes();
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -735,36 +685,34 @@ bool MIDI_Class::parse(byte inChannel)
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Add extracted data byte to pending message
|
// Add extracted data byte to pending message
|
||||||
mPendingMessage[mPendingMessageIndex] = extracted;
|
mPendingMessage[mPendingMessageIndex] = extracted;
|
||||||
|
|
||||||
|
|
||||||
// Now we are going to check if we have reached the end of the message
|
// Now we are going to check if we have reached the end of the message
|
||||||
if (mPendingMessageIndex >= (mPendingMessageExpectedLenght-1)) {
|
if (mPendingMessageIndex >= (mPendingMessageExpectedLenght-1))
|
||||||
|
{
|
||||||
// "FML" case: fall down here with an overflown SysEx..
|
// "FML" case: fall down here with an overflown SysEx..
|
||||||
// This means we received the last possible data byte that can fit the buffer.
|
// This means we received the last possible data byte that can fit
|
||||||
// If this happens, try increasing MIDI_SYSEX_ARRAY_SIZE.
|
// the buffer. If this happens, try increasing MIDI_SYSEX_ARRAY_SIZE.
|
||||||
if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive) {
|
if (getTypeFromStatusByte(mPendingMessage[0]) == SystemExclusive)
|
||||||
|
{
|
||||||
reset_input_attributes();
|
reset_input_attributes();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
|
mMessage.type = getTypeFromStatusByte(mPendingMessage[0]);
|
||||||
mMessage.channel = (mPendingMessage[0] & 0x0F)+1; // Don't check if it is a Channel Message
|
// Don't check if it is a Channel Message
|
||||||
|
mMessage.channel = (mPendingMessage[0] & 0x0F)+1;
|
||||||
|
|
||||||
mMessage.data1 = mPendingMessage[1];
|
mMessage.data1 = mPendingMessage[1];
|
||||||
|
|
||||||
// Save data2 only if applicable
|
// Save data2 only if applicable
|
||||||
if (mPendingMessageExpectedLenght == 3) mMessage.data2 = mPendingMessage[2];
|
if (mPendingMessageExpectedLenght == 3)
|
||||||
else mMessage.data2 = 0;
|
mMessage.data2 = mPendingMessage[2];
|
||||||
|
else
|
||||||
|
mMessage.data2 = 0;
|
||||||
|
|
||||||
// Reset local variables
|
// Reset local variables
|
||||||
mPendingMessageIndex = 0;
|
mPendingMessageIndex = 0;
|
||||||
|
|
@ -773,7 +721,8 @@ bool MIDI_Class::parse(byte inChannel)
|
||||||
mMessage.valid = true;
|
mMessage.valid = true;
|
||||||
|
|
||||||
// Activate running status (if enabled for the received type)
|
// Activate running status (if enabled for the received type)
|
||||||
switch (mMessage.type) {
|
switch (mMessage.type)
|
||||||
|
{
|
||||||
case NoteOff:
|
case NoteOff:
|
||||||
case NoteOn:
|
case NoteOn:
|
||||||
case AfterTouchPoly:
|
case AfterTouchPoly:
|
||||||
|
|
@ -792,7 +741,8 @@ bool MIDI_Class::parse(byte inChannel)
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
// Then update the index of the pending message.
|
// Then update the index of the pending message.
|
||||||
mPendingMessageIndex++;
|
mPendingMessageIndex++;
|
||||||
|
|
||||||
|
|
@ -804,11 +754,7 @@ bool MIDI_Class::parse(byte inChannel)
|
||||||
// to parse the rest of the message.
|
// to parse the rest of the message.
|
||||||
return parse(inChannel);
|
return parse(inChannel);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// What are our chances to fall here?
|
// What are our chances to fall here?
|
||||||
|
|
@ -819,45 +765,41 @@ bool MIDI_Class::parse(byte inChannel)
|
||||||
// Private method: check if the received message is on the listened channel
|
// Private method: check if the received message is on the listened channel
|
||||||
bool MIDI_Class::input_filter(byte inChannel)
|
bool MIDI_Class::input_filter(byte inChannel)
|
||||||
{
|
{
|
||||||
|
// This method handles recognition of channel
|
||||||
|
// (to know if the message is destinated to the Arduino)
|
||||||
|
|
||||||
|
if (mMessage.type == InvalidType)
|
||||||
// This method handles recognition of channel (to know if the message is destinated to the Arduino)
|
return false;
|
||||||
|
|
||||||
|
|
||||||
if (mMessage.type == InvalidType) return false;
|
|
||||||
|
|
||||||
|
|
||||||
// First, check if the received message is Channel
|
// First, check if the received message is Channel
|
||||||
if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) {
|
if (mMessage.type >= NoteOff && mMessage.type <= PitchBend)
|
||||||
|
{
|
||||||
// Then we need to know if we listen to it
|
// Then we need to know if we listen to it
|
||||||
if ((mMessage.channel == mInputChannel) || (mInputChannel == MIDI_CHANNEL_OMNI)) {
|
if ((mMessage.channel == mInputChannel) ||
|
||||||
|
(mInputChannel == MIDI_CHANNEL_OMNI))
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
// We don't listen to this channel
|
// We don't listen to this channel
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
// System messages are always received
|
// System messages are always received
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Private method: reset input attributes
|
// Private method: reset input attributes
|
||||||
void MIDI_Class::reset_input_attributes()
|
void MIDI_Class::reset_input_attributes()
|
||||||
{
|
{
|
||||||
|
|
||||||
mPendingMessageIndex = 0;
|
mPendingMessageIndex = 0;
|
||||||
mPendingMessageExpectedLenght = 0;
|
mPendingMessageExpectedLenght = 0;
|
||||||
mRunningStatus_RX = InvalidType;
|
mRunningStatus_RX = InvalidType;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -868,9 +810,7 @@ void MIDI_Class::reset_input_attributes()
|
||||||
*/
|
*/
|
||||||
kMIDIType MIDI_Class::getType() const
|
kMIDIType MIDI_Class::getType() const
|
||||||
{
|
{
|
||||||
|
|
||||||
return mMessage.type;
|
return mMessage.type;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -881,27 +821,21 @@ kMIDIType MIDI_Class::getType() const
|
||||||
*/
|
*/
|
||||||
byte MIDI_Class::getChannel() const
|
byte MIDI_Class::getChannel() const
|
||||||
{
|
{
|
||||||
|
|
||||||
return mMessage.channel;
|
return mMessage.channel;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! \brief Get the first data byte of the last received message. */
|
/*! \brief Get the first data byte of the last received message. */
|
||||||
byte MIDI_Class::getData1() const
|
byte MIDI_Class::getData1() const
|
||||||
{
|
{
|
||||||
|
|
||||||
return mMessage.data1;
|
return mMessage.data1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! \brief Get the second data byte of the last received message. */
|
/*! \brief Get the second data byte of the last received message. */
|
||||||
byte MIDI_Class::getData2() const
|
byte MIDI_Class::getData2() const
|
||||||
{
|
{
|
||||||
|
|
||||||
return mMessage.data2;
|
return mMessage.data2;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -911,9 +845,7 @@ byte MIDI_Class::getData2() const
|
||||||
*/
|
*/
|
||||||
const byte * MIDI_Class::getSysExArray() const
|
const byte * MIDI_Class::getSysExArray() const
|
||||||
{
|
{
|
||||||
|
|
||||||
return mMessage.sysex_array;
|
return mMessage.sysex_array;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! \brief Get the lenght of the System Exclusive array.
|
/*! \brief Get the lenght of the System Exclusive array.
|
||||||
|
|
@ -923,20 +855,15 @@ const byte * MIDI_Class::getSysExArray() const
|
||||||
*/
|
*/
|
||||||
unsigned int MIDI_Class::getSysExArrayLength() const
|
unsigned int MIDI_Class::getSysExArrayLength() const
|
||||||
{
|
{
|
||||||
|
const unsigned int size = ((unsigned)(mMessage.data2) << 8) | mMessage.data1;
|
||||||
unsigned int coded_size = ((unsigned int)(mMessage.data2) << 8) | mMessage.data1;
|
return (size > MIDI_SYSEX_ARRAY_SIZE) ? MIDI_SYSEX_ARRAY_SIZE : size;
|
||||||
|
|
||||||
return (coded_size > MIDI_SYSEX_ARRAY_SIZE) ? MIDI_SYSEX_ARRAY_SIZE : coded_size;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! \brief Check if a valid message is stored in the structure. */
|
/*! \brief Check if a valid message is stored in the structure. */
|
||||||
bool MIDI_Class::check() const
|
bool MIDI_Class::check() const
|
||||||
{
|
{
|
||||||
|
|
||||||
return mMessage.valid;
|
return mMessage.valid;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -948,9 +875,7 @@ bool MIDI_Class::check() const
|
||||||
*/
|
*/
|
||||||
void MIDI_Class::setInputChannel(const byte Channel)
|
void MIDI_Class::setInputChannel(const byte Channel)
|
||||||
{
|
{
|
||||||
|
|
||||||
mInputChannel = Channel;
|
mInputChannel = Channel;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -984,8 +909,8 @@ void MIDI_Class::setHandleSystemReset(void (*fptr)(void))
|
||||||
*/
|
*/
|
||||||
void MIDI_Class::disconnectCallbackFromType(kMIDIType Type)
|
void MIDI_Class::disconnectCallbackFromType(kMIDIType Type)
|
||||||
{
|
{
|
||||||
|
switch (Type)
|
||||||
switch (Type) {
|
{
|
||||||
case NoteOff: mNoteOffCallback = NULL; break;
|
case NoteOff: mNoteOffCallback = NULL; break;
|
||||||
case NoteOn: mNoteOnCallback = NULL; break;
|
case NoteOn: mNoteOnCallback = NULL; break;
|
||||||
case AfterTouchPoly: mAfterTouchPolyCallback = NULL; break;
|
case AfterTouchPoly: mAfterTouchPolyCallback = NULL; break;
|
||||||
|
|
@ -1007,17 +932,15 @@ void MIDI_Class::disconnectCallbackFromType(kMIDIType Type)
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Private - launch callback function based on received type.
|
// Private - launch callback function based on received type.
|
||||||
void MIDI_Class::launchCallback()
|
void MIDI_Class::launchCallback()
|
||||||
{
|
{
|
||||||
|
|
||||||
// The order is mixed to allow frequent messages to trigger their callback faster.
|
// The order is mixed to allow frequent messages to trigger their callback faster.
|
||||||
|
switch (mMessage.type)
|
||||||
switch (mMessage.type) {
|
{
|
||||||
// Notes
|
// Notes
|
||||||
case NoteOff: if (mNoteOffCallback != NULL) mNoteOffCallback(mMessage.channel,mMessage.data1,mMessage.data2); break;
|
case NoteOff: if (mNoteOffCallback != NULL) mNoteOffCallback(mMessage.channel,mMessage.data1,mMessage.data2); break;
|
||||||
case NoteOn: if (mNoteOnCallback != NULL) mNoteOnCallback(mMessage.channel,mMessage.data1,mMessage.data2); break;
|
case NoteOn: if (mNoteOnCallback != NULL) mNoteOnCallback(mMessage.channel,mMessage.data1,mMessage.data2); break;
|
||||||
|
|
@ -1049,7 +972,6 @@ void MIDI_Class::launchCallback()
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1072,31 +994,27 @@ void MIDI_Class::launchCallback()
|
||||||
*/
|
*/
|
||||||
void MIDI_Class::setThruFilterMode(kThruFilterMode inThruFilterMode)
|
void MIDI_Class::setThruFilterMode(kThruFilterMode inThruFilterMode)
|
||||||
{
|
{
|
||||||
|
|
||||||
mThruFilterMode = inThruFilterMode;
|
mThruFilterMode = inThruFilterMode;
|
||||||
if (mThruFilterMode != Off) mThruActivated = true;
|
if (mThruFilterMode != Off)
|
||||||
else mThruActivated = false;
|
mThruActivated = true;
|
||||||
|
else
|
||||||
|
mThruActivated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! \brief Setter method: turn message mirroring on. */
|
/*! \brief Setter method: turn message mirroring on. */
|
||||||
void MIDI_Class::turnThruOn(kThruFilterMode inThruFilterMode)
|
void MIDI_Class::turnThruOn(kThruFilterMode inThruFilterMode)
|
||||||
{
|
{
|
||||||
|
|
||||||
mThruActivated = true;
|
mThruActivated = true;
|
||||||
mThruFilterMode = inThruFilterMode;
|
mThruFilterMode = inThruFilterMode;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! \brief Setter method: turn message mirroring off. */
|
/*! \brief Setter method: turn message mirroring off. */
|
||||||
void MIDI_Class::turnThruOff()
|
void MIDI_Class::turnThruOff()
|
||||||
{
|
{
|
||||||
|
|
||||||
mThruActivated = false;
|
mThruActivated = false;
|
||||||
mThruFilterMode = Off;
|
mThruFilterMode = Off;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1115,45 +1033,60 @@ void MIDI_Class::thru_filter(byte inChannel)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// If the feature is disabled, don't do anything.
|
// If the feature is disabled, don't do anything.
|
||||||
if (!mThruActivated || (mThruFilterMode == Off)) return;
|
if (!mThruActivated || (mThruFilterMode == Off))
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
// First, check if the received message is Channel
|
// First, check if the received message is Channel
|
||||||
if (mMessage.type >= NoteOff && mMessage.type <= PitchBend) {
|
if (mMessage.type >= NoteOff && mMessage.type <= PitchBend)
|
||||||
|
{
|
||||||
const bool filter_condition = ((mMessage.channel == mInputChannel) || (mInputChannel == MIDI_CHANNEL_OMNI));
|
const bool filter_condition = ((mMessage.channel == mInputChannel) ||
|
||||||
|
(mInputChannel == MIDI_CHANNEL_OMNI));
|
||||||
|
|
||||||
// Now let's pass it to the output
|
// Now let's pass it to the output
|
||||||
switch (mThruFilterMode) {
|
switch (mThruFilterMode)
|
||||||
|
{
|
||||||
case Full:
|
case Full:
|
||||||
send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel);
|
send(mMessage.type,
|
||||||
|
mMessage.data1,
|
||||||
|
mMessage.data2,
|
||||||
|
mMessage.channel);
|
||||||
return;
|
return;
|
||||||
break;
|
break;
|
||||||
case SameChannel:
|
case SameChannel:
|
||||||
if (filter_condition) {
|
if (filter_condition)
|
||||||
send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel);
|
{
|
||||||
|
send(mMessage.type,
|
||||||
|
mMessage.data1,
|
||||||
|
mMessage.data2,
|
||||||
|
mMessage.channel);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DifferentChannel:
|
case DifferentChannel:
|
||||||
if (!filter_condition) {
|
if (!filter_condition)
|
||||||
send(mMessage.type,mMessage.data1,mMessage.data2,mMessage.channel);
|
{
|
||||||
|
send(mMessage.type,
|
||||||
|
mMessage.data1,
|
||||||
|
mMessage.data2,
|
||||||
|
mMessage.channel);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Off:
|
case Off:
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
// Technically it's impossible to get there because the case was already tested earlier.
|
// Technically it's impossible to get there because
|
||||||
|
// the case was already tested earlier.
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
// Send the message to the output
|
// Send the message to the output
|
||||||
switch (mMessage.type) {
|
switch (mMessage.type)
|
||||||
|
{
|
||||||
// Real Time and 1 byte
|
// Real Time and 1 byte
|
||||||
case Clock:
|
case Clock:
|
||||||
case Start:
|
case Start:
|
||||||
|
|
@ -1188,14 +1121,8 @@ void MIDI_Class::thru_filter(byte inChannel)
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif // Thru
|
#endif // Thru
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
77
src/MIDI.h
77
src/MIDI.h
|
|
@ -56,6 +56,7 @@
|
||||||
// END OF CONFIGURATION AREA
|
// END OF CONFIGURATION AREA
|
||||||
// (do not modify anything under this line unless you know what you are doing)
|
// (do not modify anything under this line unless you know what you are doing)
|
||||||
|
|
||||||
|
|
||||||
#define MIDI_BAUDRATE 31250
|
#define MIDI_BAUDRATE 31250
|
||||||
|
|
||||||
#define MIDI_CHANNEL_OMNI 0
|
#define MIDI_CHANNEL_OMNI 0
|
||||||
|
|
@ -76,7 +77,8 @@ typedef uint16_t word;
|
||||||
|
|
||||||
|
|
||||||
/*! Enumeration of MIDI types */
|
/*! Enumeration of MIDI types */
|
||||||
enum kMIDIType {
|
enum kMIDIType
|
||||||
|
{
|
||||||
NoteOff = 0x80, ///< Note Off
|
NoteOff = 0x80, ///< Note Off
|
||||||
NoteOn = 0x90, ///< Note On
|
NoteOn = 0x90, ///< Note On
|
||||||
AfterTouchPoly = 0xA0, ///< Polyphonic AfterTouch
|
AfterTouchPoly = 0xA0, ///< Polyphonic AfterTouch
|
||||||
|
|
@ -98,7 +100,6 @@ enum kMIDIType {
|
||||||
InvalidType = 0x00 ///< For notifying errors
|
InvalidType = 0x00 ///< For notifying errors
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/*! Enumeration of Thru filter modes */
|
/*! Enumeration of Thru filter modes */
|
||||||
enum kThruFilterMode {
|
enum kThruFilterMode {
|
||||||
Off = 0, ///< Thru disabled (nothing passes through).
|
Off = 0, ///< Thru disabled (nothing passes through).
|
||||||
|
|
@ -108,6 +109,74 @@ enum kThruFilterMode {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum eMIDICCNumber
|
||||||
|
{
|
||||||
|
// High resolution Continuous Controllers MSB (+32 for LSB) ----------------
|
||||||
|
BankSelect = 0,
|
||||||
|
ModulationWheel = 1,
|
||||||
|
BreathController = 2,
|
||||||
|
// CC3 undefined
|
||||||
|
FootController = 4,
|
||||||
|
PortamentoTime = 5,
|
||||||
|
DataEntry = 6,
|
||||||
|
ChannelVolume = 7,
|
||||||
|
Balance = 8,
|
||||||
|
// CC9 undefined
|
||||||
|
Pan = 10,
|
||||||
|
ExpressionController = 11,
|
||||||
|
EffectControl1 = 12,
|
||||||
|
EffectControl2 = 13,
|
||||||
|
// CC14 undefined
|
||||||
|
// CC15 undefined
|
||||||
|
GeneralPurposeController1 = 16,
|
||||||
|
GeneralPurposeController2 = 17,
|
||||||
|
GeneralPurposeController3 = 18,
|
||||||
|
GeneralPurposeController4 = 19,
|
||||||
|
|
||||||
|
// Switches ----------------------------------------------------------------
|
||||||
|
Sustain = 64,
|
||||||
|
Portamento = 65,
|
||||||
|
Sostenuto = 66,
|
||||||
|
SoftPedal = 67,
|
||||||
|
Legato = 68,
|
||||||
|
Hold2 = 69,
|
||||||
|
|
||||||
|
// Low resolution continuous controllers -----------------------------------
|
||||||
|
SoundController1 = 70, ///< Synth: Sound Variation FX: Exciter On/Off
|
||||||
|
SoundController2 = 71, ///< Synth: Harmonic Content FX: Compressor On/Off
|
||||||
|
SoundController3 = 72, ///< Synth: Release Time FX: Distortion On/Off
|
||||||
|
SoundController4 = 73, ///< Synth: Attack Time FX: EQ On/Off
|
||||||
|
SoundController5 = 74, ///< Synth: Brightness FX: Expander On/Off
|
||||||
|
SoundController6 = 75, ///< Synth: Decay Time FX: Reverb On/Off
|
||||||
|
SoundController7 = 76, ///< Synth: Vibrato Rate FX: Delay On/Off
|
||||||
|
SoundController8 = 77, ///< Synth: Vibrato Depth FX: Pitch Transpose On/Off
|
||||||
|
SoundController9 = 78, ///< Synth: Vibrato Delay FX: Flange/Chorus On/Off
|
||||||
|
SoundController10 = 79, ///< Synth: Undefined FX: Special Effects On/Off
|
||||||
|
GeneralPurposeController5 = 80,
|
||||||
|
GeneralPurposeController6 = 81,
|
||||||
|
GeneralPurposeController7 = 82,
|
||||||
|
GeneralPurposeController8 = 83,
|
||||||
|
PortamentoControl = 84,
|
||||||
|
// CC85 to CC90 undefined
|
||||||
|
Effects1 = 91, ///< Reverb send level
|
||||||
|
Effects2 = 92, ///< Tremolo depth
|
||||||
|
Effects3 = 93, ///< Chorus send level
|
||||||
|
Effects4 = 94, ///< Celeste depth
|
||||||
|
Effects5 = 95, ///< Phaser depth
|
||||||
|
|
||||||
|
// Channel Mode messages ---------------------------------------------------
|
||||||
|
AllSoundOff = 120,
|
||||||
|
ResetAllControllers = 121,
|
||||||
|
LocalControl = 122,
|
||||||
|
AllNotesOff = 123,
|
||||||
|
OmniModeOff = 124,
|
||||||
|
OmniModeOn = 125,
|
||||||
|
MonoModeOn = 126,
|
||||||
|
PolyModeOn = 127
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*! The midimsg structure contains decoded data
|
/*! The midimsg structure contains decoded data
|
||||||
of a MIDI message read from the serial port
|
of a MIDI message read from the serial port
|
||||||
with read() or thru().
|
with read() or thru().
|
||||||
|
|
@ -263,9 +332,9 @@ private:
|
||||||
byte mRunningStatus_RX;
|
byte mRunningStatus_RX;
|
||||||
byte mInputChannel;
|
byte mInputChannel;
|
||||||
|
|
||||||
byte mPendingMessage[MIDI_SYSEX_ARRAY_SIZE];
|
byte mPendingMessage[3]; // SysEx are dumped into mMessage directly.
|
||||||
unsigned int mPendingMessageExpectedLenght;
|
unsigned int mPendingMessageExpectedLenght;
|
||||||
unsigned int mPendingMessageIndex; // Extended to unsigned int for larger sysex payloads.
|
unsigned int mPendingMessageIndex; // Extended to unsigned int for larger SysEx payloads.
|
||||||
|
|
||||||
midimsg mMessage;
|
midimsg mMessage;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue