Open
Description
So far as I can tell the existing code does break out CC, RPN, and NRPN messages, which is kind of a big deal. It just has a general-purpose CC handler.
I'm using the following code to parse these messages. It's not well tested and I've just cleaned it up here so I could post it (thus possibly introducing bugs), and as I'm not a C++ coder I thought a pull request wasn't appropriate, but maybe it might be useful to you to incorporate. Enjoy.
///// CC/RPN/NRPN PARSER
/////
///// Copyright 2016 Sean Luke
///// Licensed under Apache 2.0
///// INTRODUCTION TO THE CC/RPN/NRPN PARSER
///// The parser is located in parseControlChange(...), which
///// can be set up to be the handler for CC messages by the MIDI library.
/////
///// CC messages take one of a great many forms, which we handle in the parser
/////
///// 7-bit CC messages:
///// 1. number >=64 and < 96 or >= 102 and < 120, with value
///// -> handleControlChange(channel, number, value, VALUE_7_BIT_ONLY)
/////
///// Potentially 7-bit CC messages:
///// 1. number >= 0 and < 32, other than 6, with value
///// -> handleControlChange(channel, number, value, VALUE_MSB_ONLY)
/////
///// 14-bit CC messages:
///// 1. number >= 0 and < 32, other than 6, with MSB
///// 2. same number + 32, with LSB
///// -> handleControlChange(channel, number, value, VALUE)
///// NOTE: this means that a 14-bit CC message will have TWO handleControlChange calls.
///// There's not much we can do about this, as we simply don't know if the LSB will arrive.
/////
///// Continuing 14-bit CC messages:
///// 1. same number again + 32, with LSB
///// -> handleControlChange(channel, number, revised value, VALUE)
///// It's not clear if this is valid but I think it is
/////
///// Potentially 7-bit NRPN messages:
///// 1. number == 99, with MSB of NRPN parameter
///// 2. number == 98, with LSB of NRPN parameter
///// 3. number == 6, with value
///// -> handleNRPN(channel, parameter, value, VALUE_MSB_ONLY)
/////
///// 14-bit NRPN messages:
///// 1. number == 99, with MSB of NRPN parameter
///// 2. number == 98, with LSB of NRPN parameter
///// 3. number == 6, with MSB of value
///// 4. number == 38, with LSB of value
///// -> handleNRPN(channel, parameter, value, VALUE)
///// NOTE: this means that a 14-bit NRPN message will have TWO handleNRPN calls.
///// There's not much we can do about this, as we simply don't know if the LSB will arrive.
/////
///// Continuing 7-bit NRPN messages:
///// 4. number == 38 again, with value
///// -> handleNRPN(channel, number, revised value, VALUE_MSB_ONLY)
/////
///// Continuing 14-bit NRPN messages A:
///// 3. number == 6 again, with MSB of value
///// -> handleNRPN(channel, number, revised value, VALUE)
/////
///// Continuing 14-bit NRPN messages C:
///// 3. number == 6 again, with MSB of value
///// 4. number == 38 again, with LSB of value
///// -> handleNRPN(channel, number, revised value, VALUE)
///// NOTE: this means that a continuing 14-bit NRPN message will have TWO handleNRPN calls.
///// There's not much we can do about this, as we simply don't know if the LSB will arrive.
/////
///// Incrementing NRPN messages:
///// 1. number == 99, with MSB of NRPN parameter
///// 2. number == 98, with LSB of NRPN parameter
///// 3. number == 96, with value
///// If value == 0
///// -> handleNRPN(channel, parameter, 1, INCREMENT)
///// Else
///// -> handleNRPN(channel, parameter, value, INCREMENT)
/////
///// Decrementing NRPN messages:
///// 1. number == 99, with MSB of NRPN parameter
///// 2. number == 98, with LSB of NRPN parameter
///// 3. number == 97, with value
///// If value == 0
///// -> handleNRPN(channel, parameter, 1, DECREMENT)
///// Else
///// -> handleNRPN(channel, parameter, value, DECREMENT)
/////
///// Potentially 7-bit RPN messages:
///// 1. number == 101, with MSB of RPN parameter other than 127
///// 2. number == 100, with LSB of RPN parameter other than 127
///// 3. number == 6, with value
///// -> handleNRPN(channel, parameter, value, VALUE_MSB_ONLY)
/////
///// 14-bit RPN messages:
///// 1. number == 101, with MSB of RPN parameter other than 127
///// 2. number == 100, with LSB of RPN parameter other than 127
///// 3. number == 6, with MSB of value
///// 4. number == 38, with LSB of value
///// -> handleNRPN(channel, parameter, value, VALUE)
///// NOTE: this means that a 14-bit NRPN message will have TWO handleNRPN calls.
///// There's not much we can do about this, as we simply don't know if the LSB will arrive.
/////
///// Continuing 7-bit RPN messages A:
///// 1. number == 6 again, with value
///// -> handleRPN(channel, number, revised value, VALUE_MSB_ONLY)
/////
///// Continuing 14-bit RPN messages A:
///// 1. number == 38 again, with new LSB of value
///// -> handleRPN(channel, number, revised value, VALUE)
/////
///// Continuing 14-bit RPN messages B:
///// 1. number == 6 again, with MSB of value
///// 2. number == 38 again, with LSB of value
///// -> handleRPN(channel, number, revised value, VALUE)
///// NOTE: this means that a continuing 14-bit RPN message will have TWO handleRPN calls.
///// There's not much we can do about this, as we simply don't know if the LSB will arrive.
/////
///// Incrementing RPN messages:
///// 1. number == 101, with MSB of RPN parameter other than 127
///// 2. number == 100, with LSB of RPN parameter other than 127
///// 3. number == 96, with value
///// If value == 0
///// -> handleNRPN(channel, parameter, 1, INCREMENT)
///// Else
///// -> handleNRPN(channel, parameter, value, INCREMENT)
/////
///// Decrementing NRPN messages:
///// 1. number == 101, with MSB of RPN parameter other than 127
///// 2. number == 100, with LSB of RPN parameter other than 127
///// 3. number == 97, with value
///// If value == 0
///// -> handleNRPN(channel, parameter, 1, DECREMENT)
///// Else
///// -> handleNRPN(channel, parameter, value, DECREMENT)
/////
///// NULL messages:
///// 1. number == 101, value = 127
///// 2. number == 100, value = 127
///// [nothing happens, but parser resets]
/////
/////
///// The big problem we have is that the MIDI spec got the MSB and LSB backwards for their data
///// entry values, so we don't now if the LSB is coming and have to either ignore it when it comes
///// in or send two messages, one MSB-only and one MSB+LSB. This happens for CC, RPN, and NRPN.
/////
/////
///// Our parser maintains four bytes in a struct called _controlParser:
/////
///// 0. status. This is one of:
///// INVALID: the struct holds junk. CC: the struct is building a CC.
///// RPN_START, RPN_END, RPN_MSB, RPN_INCREMENT_DECREMENT: the struct is building an RPN.
///// NRPN_START, NRPN_END, NRPN_MSB, NRPN_INCREMENT_DECREMENT: the struct is building an NRPN.
///// 1. controllerNumberMSB. In the low 7 bits.
///// 2. controllerNumberLSB. In the low 7 bits.
///// 3. controllerValueMSB. In the low 7 bits. This holds the previous MSB for potential "continuing" messages.
// Parser status values
#define INVALID 0
#define CC 1
#define NRPN_START 2
#define NRPN_END 3
#define NRPN_MSB 4
#define NRPN_INCREMENT_DECREMENT 5
#define RPN_START 6
#define RPN_END 7
#define RPN_MSB 8
#define RPN_INCREMENT_DECREMENT 9
// Data types
#define VALUE 0 // 14-bit data (MSB + LSB)
#define VALUE_MSB_ONLY 1 // 7-bit data which could be either the MSB, or the whole thing, we don't know
#define INCREMENT 2 // An increment request (by a 7-bit delta)
#define DECREMENT 3 // A decrement request (by a 7-bit delta)
#define VALUE_7_BIT_ONLY 4 // Known 7-bit only data (notably for CC directives which are 7-bit only)
struct _controlParser
{
uint8_t status = INVALID;
// The high bit of the controllerNumberMSB is either
// NEITHER_RPN_NOR_NRPN or it is RPN_OR_NRPN.
uint8_t controllerNumberMSB;
// The high bit of the controllerNumberLSB is either
// RPN or it is NRPN
uint8_t controllerNumberLSB;
// The controllerValueMSB is either a valid MSB or it is
// NO_MSB (128).
uint8_t controllerValueMSB;
};
// We make 16 parsers here, but if you only care about specific data perhaps you could
// reduct this.
struct _controlparser midiParser[16];
// You need to implement the following functions (or modify the code to pass them in as pointers)
// type can be VALUE, VALUE_MSB_ONLY, or VALUE_7_BIT_ONLY
void handleControlChange(uint8_t channel, uint8_t number, uint16_t value, uint8_t type);
// type can be VALUE, VALUE_MSB_ONLY, INCREMENT, or DECREMENT
void handleNRPN(uint8_t channel, uint8_tcontrollerNumber, uint16_t value, uint8_t type);
void handleRPN(uint8_t channel, uint8_tcontrollerNumber, uint16_t value, uint8_t type);
// also implement this one as a hook. It can be empty.
void handleControlChangesInGeneral(uint8_t channel, uint8_t number, uint8_t value);
// Register this function as the handler for your CC data
void parseControlChange(byte channel, byte number, byte value)
{
handleControlChangesInGeneral(channel, number, value);
_controlParser* parser = &midiParser[channel];
// BEGIN PARSER
// potentially 14-bit CC messages
if (number < 6 ||
(number > 6 && number < 32))
{
parser->status = CC;
parser->controllerValueMSB = value;
handleControlChange(channel, number, value, VALUE_MSB_ONLY);
}
// LSB for 14-bit CC messages, including continuation
else if (number >= 32 && number < 64 && number != 38)
{
if (parser->status == CC)
{
handleControlChange(channel, number, (((uint16_t)parser->controllerValueMSB) << 7) | value, VALUE);
}
else parser->status = INVALID;
}
// 7-bit only CC messages
else if ((number >= 64 && number < 96) ||
(number >= 102 && number < 120))
{
parser->status = INVALID;
handleControlChange(channel, number, value, VALUE_7_BIT_ONLY);
}
// Start of NRPN
else if (number == 99)
{
parser->status = NRPN_START;
parser->controllerNumberMSB = value;
}
// End of NRPN
else if (number == 98)
{
if (parser->status == NRPN_START)
{
parser->status = NRPN_END;
parser->controllerNumberLSB = value;
}
else parser->status = INVALID;
}
// Start of RPN or NULL
else if (number == 101)
{
if (value == 127) // this is the NULL termination tradition, see for example http://www.philrees.co.uk/nrpnq.htm
{
parser->status = INVALID;
}
else
{
parser->status = RPN_START;
parser->controllerNumberMSB = value;
}
}
// End of RPN or NULL
else if (number == 100)
{
if (value == 127) // this is the NULL termination tradition, see for example http://www.philrees.co.uk/nrpnq.htm
{
parser->status = INVALID;
}
else if (parser->status == RPN_START)
{
parser->status = RPN_END;
parser->controllerNumberLSB = value;
}
}
// Data Entry MSB for NRPN and RPN
else
{
uint16_t controllerNumber = (((uint16_t) parser->controllerNumberMSB) << 7) | parser->controllerNumberLSB ;
if (number == 6)
{
if (parser->status == NRPN_END || parser->status == NRPN_MSB || parser->status == NRPN_INCREMENT_DECREMENT)
{
parser->controllerValueMSB = value;
handleNRPN(channel, controllerNumber, ((uint16_t)value) << 7, VALUE_MSB_ONLY);
parser->status = NRPN_MSB;
}
else if (parser->status == RPN_END || parser->status == RPN_MSB || parser->status == RPN_INCREMENT_DECREMENT)
{
parser->controllerValueMSB = value;
handleRPN(channel, controllerNumber , ((uint16_t)value) << 7, VALUE_MSB_ONLY);
parser->status = RPN_MSB;
}
else parser->status = INVALID;
}
// Data Entry LSB for RPN, NRPN
else if (number == 38)
{
if (parser->status == NRPN_MSB)
{
handleNRPN(channel, controllerNumber, (((uint16_t)parser->controllerValueMSB) << 7) | value, VALUE);
}
else if (parser->status == RPN_MSB)
{
handleRPN(channel, controllerNumber, (((uint16_t)parser->controllerValueMSB) << 7) | value, VALUE);
}
else parser->status = INVALID;
}
// Data Increment for RPN, NRPN
else if (number == 96)
{
if (parser->status == NRPN_END || parser->status == NRPN_MSB || parser->status == NRPN_INCREMENT_DECREMENT)
{
handleNRPN(channel, controllerNumber , (value ? value : 1), INCREMENT);
parser->status = NRPN_INCREMENT_DECREMENT;
}
else if (parser->status == RPN_END || parser->status == RPN_MSB || parser->status == RPN_INCREMENT_DECREMENT)
{
handleRPN(channel, controllerNumber , (value ? value : 1), INCREMENT);
parser->status = RPN_INCREMENT_DECREMENT;
}
else parser->status = INVALID;
}
// Data Decrement for RPN, NRPN
else if (number == 97)
{
if (parser->status == NRPN_END || parser->status == NRPN_MSB || parser->status == NRPN_INCREMENT_DECREMENT)
{
handleNRPN(channel, controllerNumber , (value ? value : 1), DECREMENT);
parser->status = NRPN_INCREMENT_DECREMENT;
}
else if (parser->status == RPN_END || parser->status == RPN_MSB || parser->status == RPN_INCREMENT_DECREMENT)
{
handleRPN(channel, controllerNumber , (value ? value : 1), DECREMENT);
parser->status = RPN_INCREMENT_DECREMENT;
}
else parser->status = INVALID;
}
// Can only be 120...127, which should never appear here
else parser->status = INVALID;
}
}