Add motorised attribute to DeviceInfo for generic MIDI maps so that

we can specify if a surface is motorised, and as such will keep its
phyiscal controls in sync with Ardour's controllables at all times.
If this is not the case, we enable the code to avoid jumps when controls and
controllables are out of sync.  Mark the BCF2000 as motorised.



git-svn-id: svn://localhost/ardour2/branches/3.0@11611 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Carl Hetherington 2012-03-07 01:11:22 +00:00
parent 208703da53
commit fb6895ba86
6 changed files with 57 additions and 27 deletions

View file

@ -55,9 +55,9 @@ using namespace std;
GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s) GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
: ControlProtocol (s, _("Generic MIDI"), midi_ui_context()) : ControlProtocol (s, _("Generic MIDI"), midi_ui_context())
, _motorised (false)
, gui (0) , gui (0)
{ {
_input_port = MIDI::Manager::instance()->midi_input_port (); _input_port = MIDI::Manager::instance()->midi_input_port ();
_output_port = MIDI::Manager::instance()->midi_output_port (); _output_port = MIDI::Manager::instance()->midi_output_port ();
@ -320,7 +320,7 @@ GenericMidiControlProtocol::start_learning (Controllable* c)
} }
if (!mc) { if (!mc) {
mc = new MIDIControllable (*_input_port, *c, false); mc = new MIDIControllable (this, *_input_port, *c, false);
} }
{ {
@ -417,7 +417,7 @@ GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos,
MIDI::byte value = control_number; MIDI::byte value = control_number;
// Create a MIDIControllable // Create a MIDIControllable
MIDIControllable* mc = new MIDIControllable (*_input_port, *control, false); MIDIControllable* mc = new MIDIControllable (this, *_input_port, *control, false);
// Remove any old binding for this midi channel/type/value pair // Remove any old binding for this midi channel/type/value pair
// Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information // Note: can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
@ -533,7 +533,7 @@ GenericMidiControlProtocol::set_state (const XMLNode& node, int version)
cerr << "\tresult = " << c << endl; cerr << "\tresult = " << c << endl;
if (c) { if (c) {
MIDIControllable* mc = new MIDIControllable (*_input_port, *c, false); MIDIControllable* mc = new MIDIControllable (this, *_input_port, *c, false);
if (mc->set_state (**niter, version) == 0) { if (mc->set_state (**niter, version) == 0) {
controllables.push_back (mc); controllables.push_back (mc);
@ -622,6 +622,12 @@ GenericMidiControlProtocol::load_bindings (const string& xmlpath)
_bank_size = atoi (prop->value()); _bank_size = atoi (prop->value());
_current_bank = 0; _current_bank = 0;
} }
if ((prop = (*citer)->property ("motorised")) != 0) {
_motorised = string_is_affirmative (prop->value ());
} else {
_motorised = false;
}
} }
if ((*citer)->name() == "Binding") { if ((*citer)->name() == "Binding") {
@ -714,7 +720,7 @@ GenericMidiControlProtocol::create_binding (const XMLNode& node)
prop = node.property (X_("uri")); prop = node.property (X_("uri"));
uri = prop->value(); uri = prop->value();
MIDIControllable* mc = new MIDIControllable (*_input_port, momentary); MIDIControllable* mc = new MIDIControllable (this, *_input_port, momentary);
if (mc->init (uri)) { if (mc->init (uri)) {
delete mc; delete mc;

View file

@ -81,6 +81,10 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
void next_bank (); void next_bank ();
void prev_bank (); void prev_bank ();
bool motorised () const {
return _motorised;
}
private: private:
MIDI::Port* _input_port; MIDI::Port* _input_port;
MIDI::Port* _output_port; MIDI::Port* _output_port;
@ -124,6 +128,12 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
std::string _current_binding; std::string _current_binding;
uint32_t _bank_size; uint32_t _bank_size;
uint32_t _current_bank; uint32_t _current_bank;
/** true if this surface is motorised. If it is, we assume
that the surface's controls are never out of sync with
Ardour's state, so we don't have to take steps to avoid
values jumping around when things are not in sync.
*/
bool _motorised;
mutable void *gui; mutable void *gui;
void build_gui (); void build_gui ();

View file

@ -33,14 +33,16 @@
#include "ardour/utils.h" #include "ardour/utils.h"
#include "midicontrollable.h" #include "midicontrollable.h"
#include "generic_midi_control_protocol.h"
using namespace std; using namespace std;
using namespace MIDI; using namespace MIDI;
using namespace PBD; using namespace PBD;
using namespace ARDOUR; using namespace ARDOUR;
MIDIControllable::MIDIControllable (Port& p, bool m) MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, Port& p, bool m)
: controllable (0) : _surface (s)
, controllable (0)
, _descriptor (0) , _descriptor (0)
, _port (p) , _port (p)
, _momentary (m) , _momentary (m)
@ -55,8 +57,9 @@ MIDIControllable::MIDIControllable (Port& p, bool m)
feedback = true; // for now feedback = true; // for now
} }
MIDIControllable::MIDIControllable (Port& p, Controllable& c, bool m) MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, Port& p, Controllable& c, bool m)
: controllable (&c) : _surface (s)
, controllable (&c)
, _descriptor (0) , _descriptor (0)
, _port (p) , _port (p)
, _momentary (m) , _momentary (m)
@ -134,7 +137,7 @@ MIDIControllable::stop_learning ()
midi_learn_connection.disconnect (); midi_learn_connection.disconnect ();
} }
float int
MIDIControllable::control_to_midi (float val) MIDIControllable::control_to_midi (float val)
{ {
if (controllable->is_gain_like()) { if (controllable->is_gain_like()) {
@ -149,24 +152,24 @@ MIDIControllable::control_to_midi (float val)
} }
float float
MIDIControllable::midi_to_control (float val) MIDIControllable::midi_to_control (int val)
{ {
/* fiddle with MIDI value so that we get an odd number of integer steps /* fiddle with MIDI value so that we get an odd number of integer steps
and can thus represent "middle" precisely as 0.5. this maps to and can thus represent "middle" precisely as 0.5. this maps to
the range 0..+1.0 the range 0..+1.0
*/ */
val = (val == 0.0f ? 0.0f : (val-1.0f) / (max_value_for_type() - 1)); float fv = (val == 0 ? 0 : float (val - 1) / (max_value_for_type() - 1));
if (controllable->is_gain_like()) { if (controllable->is_gain_like()) {
return slider_position_to_gain (val); return slider_position_to_gain (fv);
} }
float control_min = controllable->lower (); float control_min = controllable->lower ();
float control_max = controllable->upper (); float control_max = controllable->upper ();
const float control_range = control_max - control_min; const float control_range = control_max - control_min;
return (val * control_range) + control_min; return (fv * control_range) + control_min;
} }
void void
@ -219,11 +222,19 @@ MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
float range = max_value - min_value; float range = max_value - min_value;
float threshold = 10; float threshold = 10;
// prevent jumps when MIDI controller and controllable are "out of sync" bool const in_sync = (
if (range < threshold && range < threshold &&
controllable->get_value() <= midi_to_control(max_value) && controllable->get_value() <= midi_to_control(max_value) &&
controllable->get_value() >= midi_to_control(min_value)) { controllable->get_value() >= midi_to_control(min_value)
controllable->set_value (midi_to_control (new_value) ); );
/* If the surface is not motorised, we try to prevent jumps when
the MIDI controller and controllable are out of sync.
There might be a better way of doing this.
*/
if (in_sync || _surface->motorised ()) {
controllable->set_value (midi_to_control (new_value));
} }
last_controllable_value = new_value; last_controllable_value = new_value;
@ -360,7 +371,7 @@ MIDIControllable::write_feedback (MIDI::byte* buf, int32_t& bufsize, bool /*forc
return buf; return buf;
} }
float const gm = control_to_midi (controllable->get_value()); int const gm = control_to_midi (controllable->get_value());
if (gm == last_value) { if (gm == last_value) {
return buf; return buf;

View file

@ -40,11 +40,13 @@ namespace MIDI {
class Parser; class Parser;
} }
class GenericMidiControlProtocol;
class MIDIControllable : public PBD::Stateful class MIDIControllable : public PBD::Stateful
{ {
public: public:
MIDIControllable (MIDI::Port&, PBD::Controllable&, bool momentary); MIDIControllable (GenericMidiControlProtocol *, MIDI::Port&, PBD::Controllable&, bool momentary);
MIDIControllable (MIDI::Port&, bool momentary = false); MIDIControllable (GenericMidiControlProtocol *, MIDI::Port&, bool momentary = false);
virtual ~MIDIControllable (); virtual ~MIDIControllable ();
int init (const std::string&); int init (const std::string&);
@ -65,8 +67,8 @@ class MIDIControllable : public PBD::Stateful
bool get_midi_feedback () { return feedback; } bool get_midi_feedback () { return feedback; }
void set_midi_feedback (bool val) { feedback = val; } void set_midi_feedback (bool val) { feedback = val; }
float control_to_midi(float val); int control_to_midi(float val);
float midi_to_control(float val); float midi_to_control(int val);
bool learned() const { return _learned; } bool learned() const { return _learned; }
@ -91,6 +93,7 @@ class MIDIControllable : public PBD::Stateful
int max_value_for_type () const; int max_value_for_type () const;
GenericMidiControlProtocol* _surface;
PBD::Controllable* controllable; PBD::Controllable* controllable;
PBD::ControllableDescriptor* _descriptor; PBD::ControllableDescriptor* _descriptor;
std::string _current_uri; std::string _current_uri;

View file

@ -5,7 +5,7 @@
<!-- Set the BCF2000 to factory preset number 2, and this will bind --> <!-- Set the BCF2000 to factory preset number 2, and this will bind -->
<!-- the controllers intuitively to the DAW controllers. --> <!-- the controllers intuitively to the DAW controllers. -->
<!-- --> <!-- -->
<DeviceInfo bank-size="8"/> <DeviceInfo bank-size="8" motorised="yes"/>
<!-- Channel controls: --> <!-- Channel controls: -->
<!-- - the rotary encoder, when pushed, will --> <!-- - the rotary encoder, when pushed, will -->

View file

@ -4,7 +4,7 @@
<!-- Adapted by Carl Hetherington --> <!-- Adapted by Carl Hetherington -->
<!-- Map for the Behringer BCF2000 in Mackie Control emulation mode --> <!-- Map for the Behringer BCF2000 in Mackie Control emulation mode -->
<DeviceInfo bank-size="8"/> <DeviceInfo bank-size="8" motorised="yes"/>
<!-- Channel controls: --> <!-- Channel controls: -->
<!-- - the rotary encoder, when pushed, will --> <!-- - the rotary encoder, when pushed, will -->