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:
parent
208703da53
commit
fb6895ba86
@ -55,9 +55,9 @@ using namespace std;
|
||||
|
||||
GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
|
||||
: ControlProtocol (s, _("Generic MIDI"), midi_ui_context())
|
||||
, _motorised (false)
|
||||
, gui (0)
|
||||
{
|
||||
|
||||
_input_port = MIDI::Manager::instance()->midi_input_port ();
|
||||
_output_port = MIDI::Manager::instance()->midi_output_port ();
|
||||
|
||||
@ -320,7 +320,7 @@ GenericMidiControlProtocol::start_learning (Controllable* c)
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// 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
|
||||
// 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;
|
||||
|
||||
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) {
|
||||
controllables.push_back (mc);
|
||||
@ -622,6 +622,12 @@ GenericMidiControlProtocol::load_bindings (const string& xmlpath)
|
||||
_bank_size = atoi (prop->value());
|
||||
_current_bank = 0;
|
||||
}
|
||||
|
||||
if ((prop = (*citer)->property ("motorised")) != 0) {
|
||||
_motorised = string_is_affirmative (prop->value ());
|
||||
} else {
|
||||
_motorised = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((*citer)->name() == "Binding") {
|
||||
@ -714,7 +720,7 @@ GenericMidiControlProtocol::create_binding (const XMLNode& node)
|
||||
prop = node.property (X_("uri"));
|
||||
uri = prop->value();
|
||||
|
||||
MIDIControllable* mc = new MIDIControllable (*_input_port, momentary);
|
||||
MIDIControllable* mc = new MIDIControllable (this, *_input_port, momentary);
|
||||
|
||||
if (mc->init (uri)) {
|
||||
delete mc;
|
||||
|
@ -81,6 +81,10 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
|
||||
void next_bank ();
|
||||
void prev_bank ();
|
||||
|
||||
bool motorised () const {
|
||||
return _motorised;
|
||||
}
|
||||
|
||||
private:
|
||||
MIDI::Port* _input_port;
|
||||
MIDI::Port* _output_port;
|
||||
@ -124,6 +128,12 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
|
||||
std::string _current_binding;
|
||||
uint32_t _bank_size;
|
||||
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;
|
||||
void build_gui ();
|
||||
|
@ -33,14 +33,16 @@
|
||||
#include "ardour/utils.h"
|
||||
|
||||
#include "midicontrollable.h"
|
||||
#include "generic_midi_control_protocol.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace MIDI;
|
||||
using namespace PBD;
|
||||
using namespace ARDOUR;
|
||||
|
||||
MIDIControllable::MIDIControllable (Port& p, bool m)
|
||||
: controllable (0)
|
||||
MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, Port& p, bool m)
|
||||
: _surface (s)
|
||||
, controllable (0)
|
||||
, _descriptor (0)
|
||||
, _port (p)
|
||||
, _momentary (m)
|
||||
@ -55,8 +57,9 @@ MIDIControllable::MIDIControllable (Port& p, bool m)
|
||||
feedback = true; // for now
|
||||
}
|
||||
|
||||
MIDIControllable::MIDIControllable (Port& p, Controllable& c, bool m)
|
||||
: controllable (&c)
|
||||
MIDIControllable::MIDIControllable (GenericMidiControlProtocol* s, Port& p, Controllable& c, bool m)
|
||||
: _surface (s)
|
||||
, controllable (&c)
|
||||
, _descriptor (0)
|
||||
, _port (p)
|
||||
, _momentary (m)
|
||||
@ -134,7 +137,7 @@ MIDIControllable::stop_learning ()
|
||||
midi_learn_connection.disconnect ();
|
||||
}
|
||||
|
||||
float
|
||||
int
|
||||
MIDIControllable::control_to_midi (float val)
|
||||
{
|
||||
if (controllable->is_gain_like()) {
|
||||
@ -149,24 +152,24 @@ MIDIControllable::control_to_midi (float val)
|
||||
}
|
||||
|
||||
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
|
||||
and can thus represent "middle" precisely as 0.5. this maps to
|
||||
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()) {
|
||||
return slider_position_to_gain (val);
|
||||
return slider_position_to_gain (fv);
|
||||
}
|
||||
|
||||
float control_min = controllable->lower ();
|
||||
float control_max = controllable->upper ();
|
||||
const float control_range = control_max - control_min;
|
||||
|
||||
return (val * control_range) + control_min;
|
||||
return (fv * control_range) + control_min;
|
||||
}
|
||||
|
||||
void
|
||||
@ -219,11 +222,19 @@ MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
|
||||
float range = max_value - min_value;
|
||||
float threshold = 10;
|
||||
|
||||
// prevent jumps when MIDI controller and controllable are "out of sync"
|
||||
if (range < threshold &&
|
||||
controllable->get_value() <= midi_to_control(max_value) &&
|
||||
controllable->get_value() >= midi_to_control(min_value)) {
|
||||
controllable->set_value (midi_to_control (new_value) );
|
||||
bool const in_sync = (
|
||||
range < threshold &&
|
||||
controllable->get_value() <= midi_to_control(max_value) &&
|
||||
controllable->get_value() >= midi_to_control(min_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;
|
||||
@ -360,7 +371,7 @@ MIDIControllable::write_feedback (MIDI::byte* buf, int32_t& bufsize, bool /*forc
|
||||
return buf;
|
||||
}
|
||||
|
||||
float const gm = control_to_midi (controllable->get_value());
|
||||
int const gm = control_to_midi (controllable->get_value());
|
||||
|
||||
if (gm == last_value) {
|
||||
return buf;
|
||||
|
@ -40,11 +40,13 @@ namespace MIDI {
|
||||
class Parser;
|
||||
}
|
||||
|
||||
class GenericMidiControlProtocol;
|
||||
|
||||
class MIDIControllable : public PBD::Stateful
|
||||
{
|
||||
public:
|
||||
MIDIControllable (MIDI::Port&, PBD::Controllable&, bool momentary);
|
||||
MIDIControllable (MIDI::Port&, bool momentary = false);
|
||||
MIDIControllable (GenericMidiControlProtocol *, MIDI::Port&, PBD::Controllable&, bool momentary);
|
||||
MIDIControllable (GenericMidiControlProtocol *, MIDI::Port&, bool momentary = false);
|
||||
virtual ~MIDIControllable ();
|
||||
|
||||
int init (const std::string&);
|
||||
@ -65,8 +67,8 @@ class MIDIControllable : public PBD::Stateful
|
||||
bool get_midi_feedback () { return feedback; }
|
||||
void set_midi_feedback (bool val) { feedback = val; }
|
||||
|
||||
float control_to_midi(float val);
|
||||
float midi_to_control(float val);
|
||||
int control_to_midi(float val);
|
||||
float midi_to_control(int val);
|
||||
|
||||
bool learned() const { return _learned; }
|
||||
|
||||
@ -91,6 +93,7 @@ class MIDIControllable : public PBD::Stateful
|
||||
|
||||
int max_value_for_type () const;
|
||||
|
||||
GenericMidiControlProtocol* _surface;
|
||||
PBD::Controllable* controllable;
|
||||
PBD::ControllableDescriptor* _descriptor;
|
||||
std::string _current_uri;
|
||||
|
@ -5,7 +5,7 @@
|
||||
<!-- Set the BCF2000 to factory preset number 2, and this will bind -->
|
||||
<!-- the controllers intuitively to the DAW controllers. -->
|
||||
<!-- -->
|
||||
<DeviceInfo bank-size="8"/>
|
||||
<DeviceInfo bank-size="8" motorised="yes"/>
|
||||
|
||||
<!-- Channel controls: -->
|
||||
<!-- - the rotary encoder, when pushed, will -->
|
||||
|
@ -4,7 +4,7 @@
|
||||
<!-- Adapted by Carl Hetherington -->
|
||||
|
||||
<!-- Map for the Behringer BCF2000 in Mackie Control emulation mode -->
|
||||
<DeviceInfo bank-size="8"/>
|
||||
<DeviceInfo bank-size="8" motorised="yes"/>
|
||||
|
||||
<!-- Channel controls: -->
|
||||
<!-- - the rotary encoder, when pushed, will -->
|
||||
|
Loading…
Reference in New Issue
Block a user