add Ctl_Dial to get better behaviour when binding a MIDI controller KNOB/DIAL to a toggled controllable.

No intent to change Ctl_Momentary or Ctl_Toggle behaviour, plus I tried to document what they are intended to support
This commit is contained in:
Paul Davis 2018-06-21 22:28:11 -04:00
parent 870625973e
commit 8725ed5bd1
3 changed files with 42 additions and 26 deletions

View file

@ -836,6 +836,9 @@ GenericMidiControlProtocol::create_binding (const XMLNode& node)
} else if ((prop = node.property (X_("ctl-toggle"))) !=0) { } else if ((prop = node.property (X_("ctl-toggle"))) !=0) {
ctltype = MIDIControllable::Ctl_Toggle; ctltype = MIDIControllable::Ctl_Toggle;
ev = MIDI::controller; ev = MIDI::controller;
} else if ((prop = node.property (X_("ctl-dial"))) !=0) {
ctltype = MIDIControllable::Ctl_Dial;
ev = MIDI::controller;
} else if ((prop = node.property (X_("note"))) != 0) { } else if ((prop = node.property (X_("note"))) != 0) {
ev = MIDI::on; ev = MIDI::on;
} else if ((prop = node.property (X_("pgm"))) != 0) { } else if ((prop = node.property (X_("pgm"))) != 0) {

View file

@ -56,6 +56,7 @@ MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, MIDI::Parser&
_encoder = No_enc; _encoder = No_enc;
setting = false; setting = false;
last_value = 0; // got a better idea ? last_value = 0; // got a better idea ?
last_incoming = 256; // any out of band value
last_controllable_value = 0.0f; last_controllable_value = 0.0f;
control_type = none; control_type = none;
control_rpn = -1; control_rpn = -1;
@ -135,6 +136,8 @@ MIDIControllable::set_controllable (Controllable* c)
last_controllable_value = 0.0f; // is there a better value? last_controllable_value = 0.0f; // is there a better value?
} }
last_incoming = 256;
if (controllable) { if (controllable) {
controllable->Destroyed.connect (controllable_death_connection, MISSING_INVALIDATOR, controllable->Destroyed.connect (controllable_death_connection, MISSING_INVALIDATOR,
boost::bind (&MIDIControllable::drop_controllable, this, _1), boost::bind (&MIDIControllable::drop_controllable, this, _1),
@ -372,32 +375,40 @@ MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
} }
} else { } else {
/* toggle control: make the toggle flip only if the switch (get_ctltype()) {
* incoming control value exceeds 0.5 (0x40), so that case Ctl_Dial:
* the typical button which sends "CC N=0x7f" on press /* toggle value whenever direction of knob motion changes */
* and "CC N=0x0" on release can be used to drive if (last_incoming > 127) {
* toggles on press. /* relax ... first incoming message */
* } else {
* No other arrangement really makes sense for a toggle if (msg->value > last_incoming) {
* controllable. Acting on the press+release makes the controllable->set_value (1.0, Controllable::UseGroup);
* action momentary, which is almost never } else {
* desirable. If the physical button only sends a controllable->set_value (0.0, Controllable::UseGroup);
* message on press (or release), then it will be }
* expected to send a controller value >= 0.5 DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("dial Midi CC %1 value 1 %2\n", (int) msg->controller_number, current_uri()));
* (0x40). It is hard to imagine why anyone would make }
* a MIDI controller button that sent 0x0 when pressed. last_incoming = msg->value;
*/ break;
if (msg->value >= 0x40) { case Ctl_Momentary:
controllable->set_value (controllable->get_value() >= 0.5 ? 0.0 : 1.0, Controllable::UseGroup); /* toggle it if over 64, otherwise leave it alone. This behaviour that works with buttons which send a value > 64 each
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Midi CC %1 value 1 %2\n", (int) msg->controller_number, current_uri())); * time they are pressed.
} else { */
switch (get_ctltype()) { if (msg->value >= 0x40) {
case Ctl_Momentary: controllable->set_value (controllable->get_value() >= 0.5 ? 0.0 : 1.0, Controllable::UseGroup);
break; DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("toggle Midi CC %1 value 1 %2\n", (int) msg->controller_number, current_uri()));
case Ctl_Toggle: }
controllable->set_value (0.0, Controllable::NoGroup); break;
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Midi CC %1 value 0 %2\n", (int) msg->controller_number, current_uri())); case Ctl_Toggle:
break; /* toggle if value is over 64, otherwise turn it off. This is behaviour designed for buttons which send a value > 64 when pressed,
maintain state (i.e. they know they were pressed) and then send zero the next time.
*/
if (msg->value >= 0x40) {
controllable->set_value (controllable->get_value() >= 0.5 ? 0.0 : 1.0, Controllable::UseGroup);
} else {
controllable->set_value (0.0, Controllable::NoGroup);
DEBUG_TRACE (DEBUG::GenericMidi, string_compose ("Midi CC %1 value 0 %2\n", (int) msg->controller_number, current_uri()));
break;
} }
} }
} }

View file

@ -58,6 +58,7 @@ class MIDIControllable : public PBD::Stateful
enum CtlType { enum CtlType {
Ctl_Momentary, Ctl_Momentary,
Ctl_Toggle, Ctl_Toggle,
Ctl_Dial,
}; };
enum Encoder { enum Encoder {
@ -119,6 +120,7 @@ class MIDIControllable : public PBD::Stateful
MIDI::Parser& _parser; MIDI::Parser& _parser;
bool setting; bool setting;
int last_value; int last_value;
int last_incoming;
float last_controllable_value; float last_controllable_value;
bool _momentary; bool _momentary;
bool _is_gain_controller; bool _is_gain_controller;