add (N)RPN handling to libmidi++
This commit is contained in:
parent
203bc9b87e
commit
14fe7a0ae8
@ -27,9 +27,14 @@ using namespace MIDI;
|
||||
|
||||
Channel::Channel (MIDI::byte channelnum, Port &p)
|
||||
: _port (p)
|
||||
, _channel_number (channelnum)
|
||||
, _rpn_msb (0)
|
||||
, _rpn_lsb (0)
|
||||
, _nrpn_msb (0)
|
||||
, _nrpn_lsb (0)
|
||||
, _rpn_state (RPNState (0))
|
||||
, _nrpn_state (RPNState (0))
|
||||
{
|
||||
_channel_number = channelnum;
|
||||
|
||||
reset (0, 1, false);
|
||||
}
|
||||
|
||||
@ -75,10 +80,8 @@ Channel::reset (timestamp_t timestamp, framecnt_t /*nframes*/, bool notes_off)
|
||||
_controller_14bit[n] = false;
|
||||
}
|
||||
|
||||
_rpn_msb = 0;
|
||||
_rpn_lsb = 0;
|
||||
_nrpn_msb = 0;
|
||||
_nrpn_lsb = 0;
|
||||
rpn_reset ();
|
||||
nrpn_reset ();
|
||||
|
||||
_omni = true;
|
||||
_poly = false;
|
||||
@ -86,6 +89,26 @@ Channel::reset (timestamp_t timestamp, framecnt_t /*nframes*/, bool notes_off)
|
||||
_notes_on = 0;
|
||||
}
|
||||
|
||||
void
|
||||
Channel::rpn_reset ()
|
||||
{
|
||||
_rpn_msb = 0;
|
||||
_rpn_lsb = 0;
|
||||
_rpn_val_msb = 0;
|
||||
_rpn_val_lsb = 0;
|
||||
_rpn_state = RPNState (0);
|
||||
}
|
||||
|
||||
void
|
||||
Channel::nrpn_reset ()
|
||||
{
|
||||
_nrpn_msb = 0;
|
||||
_nrpn_lsb = 0;
|
||||
_nrpn_val_msb = 0;
|
||||
_nrpn_val_lsb = 0;
|
||||
_nrpn_state = RPNState (0);
|
||||
}
|
||||
|
||||
void
|
||||
Channel::process_note_off (Parser & /*parser*/, EventTwoBytes *tb)
|
||||
{
|
||||
@ -105,8 +128,130 @@ Channel::process_note_on (Parser & /*parser*/, EventTwoBytes *tb)
|
||||
_notes_on++;
|
||||
}
|
||||
|
||||
const Channel::RPNState Channel::RPN_READY_FOR_VALUE = RPNState (HaveLSB|HaveMSB);
|
||||
const Channel::RPNState Channel::RPN_VALUE_READY = RPNState (HaveLSB|HaveMSB|HaveValue);
|
||||
|
||||
bool
|
||||
Channel::maybe_process_rpns (Parser& parser, EventTwoBytes *tb)
|
||||
{
|
||||
switch (tb->controller_number) {
|
||||
case 0x62:
|
||||
_rpn_state = RPNState (_rpn_state|HaveMSB);
|
||||
_rpn_lsb = tb->value;
|
||||
if (_rpn_msb == 0x7f && _rpn_lsb == 0x7f) {
|
||||
rpn_reset ();
|
||||
}
|
||||
return true;
|
||||
case 0x63:
|
||||
_rpn_state = RPNState (_rpn_state|HaveLSB);
|
||||
_rpn_msb = tb->value;
|
||||
if (_rpn_msb == 0x7f && _rpn_lsb == 0x7f) {
|
||||
rpn_reset ();
|
||||
}
|
||||
return true;
|
||||
|
||||
case 0x64:
|
||||
_nrpn_state = RPNState (_rpn_state|HaveMSB);
|
||||
_rpn_lsb = tb->value;
|
||||
if (_nrpn_msb == 0x7f && _nrpn_lsb == 0x7f) {
|
||||
nrpn_reset ();
|
||||
}
|
||||
return true;
|
||||
case 0x65:
|
||||
_nrpn_state = RPNState (_rpn_state|HaveLSB);
|
||||
_rpn_msb = tb->value;
|
||||
if (_rpn_msb == 0x7f && _rpn_lsb == 0x7f) {
|
||||
nrpn_reset ();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((_nrpn_state & RPN_READY_FOR_VALUE) == RPN_READY_FOR_VALUE) {
|
||||
|
||||
uint16_t rpn_id = (_rpn_msb << 7)|_rpn_lsb;
|
||||
|
||||
switch (tb->controller_number) {
|
||||
case 0x60:
|
||||
/* data increment */
|
||||
_nrpn_state = RPNState (_nrpn_state|HaveValue);
|
||||
parser.channel_nrpn_increment[_channel_number] (parser, rpn_id); /* EMIT SIGNAL */
|
||||
return true;
|
||||
case 0x61:
|
||||
/* data decrement */
|
||||
_nrpn_state = RPNState (_nrpn_state|HaveValue);
|
||||
parser.channel_nrpn_decrement[_channel_number] (parser, rpn_id); /* EMIT SIGNAL */
|
||||
return true;
|
||||
case 0x06:
|
||||
/* data entry MSB */
|
||||
_nrpn_state = RPNState (_nrpn_state|HaveValue);
|
||||
_nrpn_val_msb = tb->value;
|
||||
break;
|
||||
case 0x26:
|
||||
/* data entry LSB */
|
||||
_nrpn_state = RPNState (_nrpn_state|HaveValue);
|
||||
_nrpn_val_lsb = tb->value;
|
||||
}
|
||||
|
||||
if (_nrpn_state == RPN_VALUE_READY) {
|
||||
|
||||
float rpn_val = ((_rpn_val_msb << 7)|_rpn_val_lsb)/16384.0;
|
||||
|
||||
std::pair<RPNList::iterator,bool> result = nrpns.insert (std::make_pair (rpn_id, rpn_val));
|
||||
|
||||
if (!result.second) {
|
||||
result.first->second = rpn_val;
|
||||
}
|
||||
|
||||
parser.channel_nrpn[_channel_number] (parser, rpn_id); /* EMIT SIGNAL */
|
||||
return true;
|
||||
}
|
||||
|
||||
} else if ((_rpn_state & RPN_READY_FOR_VALUE) == RPN_READY_FOR_VALUE) {
|
||||
|
||||
uint16_t rpn_id = (_rpn_msb << 7)|_rpn_lsb;
|
||||
|
||||
switch (tb->controller_number) {
|
||||
case 0x60:
|
||||
/* data increment */
|
||||
_rpn_state = RPNState (_rpn_state|HaveValue);
|
||||
parser.channel_rpn_increment[_channel_number] (parser, rpn_id); /* EMIT SIGNAL */
|
||||
return true;
|
||||
case 0x61:
|
||||
/* data decrement */
|
||||
_rpn_state = RPNState (_rpn_state|HaveValue);
|
||||
parser.channel_rpn_decrement[_channel_number] (parser, rpn_id); /* EMIT SIGNAL */
|
||||
return true;
|
||||
case 0x06:
|
||||
/* data entry MSB */
|
||||
_rpn_state = RPNState (_rpn_state|HaveValue);
|
||||
_rpn_val_msb = tb->value;
|
||||
break;
|
||||
case 0x26:
|
||||
/* data entry LSB */
|
||||
_rpn_state = RPNState (_rpn_state|HaveValue);
|
||||
_rpn_val_lsb = tb->value;
|
||||
}
|
||||
|
||||
if (_rpn_state == RPN_VALUE_READY) {
|
||||
|
||||
float rpn_val = ((_rpn_val_msb << 7)|_rpn_val_lsb)/16384.0;
|
||||
|
||||
std::pair<RPNList::iterator,bool> result = rpns.insert (std::make_pair (rpn_id, rpn_val));
|
||||
|
||||
if (!result.second) {
|
||||
result.first->second = rpn_val;
|
||||
}
|
||||
|
||||
parser.channel_rpn[_channel_number] (parser, rpn_id); /* EMIT SIGNAL */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
Channel::process_controller (Parser & /*parser*/, EventTwoBytes *tb)
|
||||
Channel::process_controller (Parser & parser, EventTwoBytes *tb)
|
||||
{
|
||||
unsigned short cv;
|
||||
|
||||
@ -115,6 +260,16 @@ Channel::process_controller (Parser & /*parser*/, EventTwoBytes *tb)
|
||||
all changes *are* atomic.
|
||||
*/
|
||||
|
||||
if (maybe_process_rpns (parser, tb)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Note: if RPN data controllers (0x60, 0x61, 0x6, 0x26) are received
|
||||
* without a previous RPN parameter ID message, or after the RPN ID
|
||||
* has been reset, they will be treated like ordinary CC messages.
|
||||
*/
|
||||
|
||||
|
||||
if (tb->controller_number < 32) { /* unsigned: no test for >= 0 */
|
||||
|
||||
/* if this controller is already known to use 14 bits,
|
||||
@ -272,3 +427,35 @@ Channel::channel_msg (MIDI::byte id, MIDI::byte val1, MIDI::byte val2, timestamp
|
||||
|
||||
return _port.midimsg (msg, len, timestamp);
|
||||
}
|
||||
|
||||
float
|
||||
Channel::rpn_value (uint16_t rpn) const
|
||||
{
|
||||
return rpn_value_absolute (rpn) / 16384.0f;
|
||||
}
|
||||
|
||||
float
|
||||
Channel::rpn_value_absolute (uint16_t rpn) const
|
||||
{
|
||||
RPNList::const_iterator r = rpns.find (rpn);
|
||||
if (r == rpns.end()) {
|
||||
return 0.0;
|
||||
}
|
||||
return r->second;
|
||||
}
|
||||
|
||||
float
|
||||
Channel::nrpn_value (uint16_t nrpn) const
|
||||
{
|
||||
return nrpn_value_absolute (nrpn) / 16384.0f;
|
||||
}
|
||||
|
||||
float
|
||||
Channel::nrpn_value_absolute (uint16_t nrpn) const
|
||||
{
|
||||
RPNList::const_iterator r = nrpns.find (nrpn);
|
||||
if (r == nrpns.end()) {
|
||||
return 0.0;
|
||||
}
|
||||
return r->second;
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
#define __midichannel_h__
|
||||
|
||||
#include <queue>
|
||||
#include <map>
|
||||
|
||||
#include "pbd/signals.h"
|
||||
#include "midi++/parser.h"
|
||||
@ -75,6 +76,9 @@ class LIBMIDIPP_API Channel : public PBD::ScopedConnectionList {
|
||||
_controller_val[n%128] = val;
|
||||
}
|
||||
|
||||
controller_value_t rpn_value (uint16_t rpn_id);
|
||||
controller_value_t nrpn_value (uint16_t rpn_id);
|
||||
|
||||
bool channel_msg (byte id, byte val1, byte val2, timestamp_t timestamp);
|
||||
bool all_notes_off (timestamp_t timestamp) {
|
||||
return channel_msg (MIDI::controller, 123, 0, timestamp);
|
||||
@ -108,6 +112,11 @@ class LIBMIDIPP_API Channel : public PBD::ScopedConnectionList {
|
||||
return channel_msg (MIDI::pitchbend, lsb, msb, timestamp);
|
||||
}
|
||||
|
||||
float rpn_value (uint16_t rpn) const;
|
||||
float nrpn_value (uint16_t nrpn) const;
|
||||
float rpn_value_absolute (uint16_t rpn) const;
|
||||
float nrpn_value_absolute (uint16_t nrpn) const;
|
||||
|
||||
protected:
|
||||
friend class Port;
|
||||
void connect_signals ();
|
||||
@ -115,14 +124,26 @@ class LIBMIDIPP_API Channel : public PBD::ScopedConnectionList {
|
||||
private:
|
||||
Port& _port;
|
||||
|
||||
enum RPNState {
|
||||
HaveLSB = 0x1,
|
||||
HaveMSB = 0x2,
|
||||
HaveValue = 0x4
|
||||
};
|
||||
|
||||
/* Current channel values */
|
||||
byte _channel_number;
|
||||
unsigned short _bank_number;
|
||||
byte _program_number;
|
||||
byte _rpn_msb;
|
||||
byte _rpn_lsb;
|
||||
byte _rpn_val_msb;
|
||||
byte _rpn_val_lsb;
|
||||
byte _nrpn_msb;
|
||||
byte _nrpn_lsb;
|
||||
byte _nrpn_val_lsb;
|
||||
byte _nrpn_val_msb;
|
||||
RPNState _rpn_state;
|
||||
RPNState _nrpn_state;
|
||||
byte _chanpress;
|
||||
byte _polypress[128];
|
||||
bool _controller_14bit[128];
|
||||
@ -139,6 +160,11 @@ class LIBMIDIPP_API Channel : public PBD::ScopedConnectionList {
|
||||
bool _mono;
|
||||
size_t _notes_on;
|
||||
|
||||
typedef std::map<uint16_t,float> RPNList;
|
||||
|
||||
RPNList rpns;
|
||||
RPNList nrpns;
|
||||
|
||||
void reset (timestamp_t timestamp, framecnt_t nframes, bool notes_off = true);
|
||||
|
||||
void process_note_off (Parser &, EventTwoBytes *);
|
||||
@ -149,6 +175,14 @@ class LIBMIDIPP_API Channel : public PBD::ScopedConnectionList {
|
||||
void process_chanpress (Parser &, byte);
|
||||
void process_pitchbend (Parser &, pitchbend_t);
|
||||
void process_reset (Parser &);
|
||||
bool maybe_process_rpns (Parser&, EventTwoBytes *);
|
||||
|
||||
void rpn_reset ();
|
||||
void nrpn_reset ();
|
||||
|
||||
static const RPNState RPN_READY_FOR_VALUE;
|
||||
static const RPNState RPN_VALUE_READY;
|
||||
|
||||
};
|
||||
|
||||
} // namespace MIDI
|
||||
|
@ -39,6 +39,7 @@ typedef PBD::Signal2<void,Parser&,framecnt_t> TimestampedSignal;
|
||||
typedef PBD::Signal2<void,Parser&, byte> OneByteSignal;
|
||||
typedef PBD::Signal2<void,Parser &, EventTwoBytes *> TwoByteSignal;
|
||||
typedef PBD::Signal2<void,Parser &, pitchbend_t> PitchBendSignal;
|
||||
typedef PBD::Signal2<void,Parser &, uint16_t> RPNSignal;
|
||||
typedef PBD::Signal3<void,Parser &, byte *, size_t> Signal;
|
||||
|
||||
class LIBMIDIPP_API Parser {
|
||||
@ -75,6 +76,12 @@ class LIBMIDIPP_API Parser {
|
||||
TwoByteSignal channel_controller[16];
|
||||
ZeroByteSignal channel_active_preparse[16];
|
||||
ZeroByteSignal channel_active_postparse[16];
|
||||
RPNSignal channel_rpn[16];
|
||||
RPNSignal channel_nrpn[16];
|
||||
RPNSignal channel_rpn_increment[16];
|
||||
RPNSignal channel_rpn_decrement[16];
|
||||
RPNSignal channel_nrpn_increment[16];
|
||||
RPNSignal channel_nrpn_decrement[16];
|
||||
|
||||
OneByteSignal mtc_quarter_frame; /* see below for more useful signals */
|
||||
Signal mtc;
|
||||
|
Loading…
Reference in New Issue
Block a user