new PBD::ControllableDescriptor class to encapsulate parsing of binding URIs and...
[ardour.git] / libs / surfaces / generic_midi / midicontrollable.cc
index 8b2077fb5bb813cf50a4292c5fb18387dd4b4ad9..d960140958d7d6939375f73e6acc1cb46d174aec 100644 (file)
 
 */
 
+#define __STDC_FORMAT_MACROS 1
+#include <stdint.h>
+
 #include <cstdio> /* for sprintf, sigh */
 #include <climits>
+
 #include "pbd/error.h"
+#include "pbd/controllable_descriptor.h"
 #include "pbd/xml++.h"
+
 #include "midi++/port.h"
 #include "midi++/channel.h"
+
 #include "ardour/automation_control.h"
+#include "ardour/utils.h"
 
 #include "midicontrollable.h"
 
-using namespace sigc;
+using namespace std;
 using namespace MIDI;
 using namespace PBD;
 using namespace ARDOUR;
 
-MIDIControllable::MIDIControllable (Port& p, const string& c, bool is_bistate)
-       : controllable (0), _port (p), bistate (is_bistate)
+MIDIControllable::MIDIControllable (Port& p, bool is_bistate)
+       : controllable (0)
+       , _descriptor (0)
+       , _port (p)
+       , bistate (is_bistate)
 {
-       init ();
+       _learned = false; /* from URI */
+       setting = false;
+       last_value = 0; // got a better idea ?
+       control_type = none;
+       _control_description = "MIDI Control: none";
+       control_additional = (byte) -1;
+       feedback = true; // for now
 }
 
 MIDIControllable::MIDIControllable (Port& p, Controllable& c, bool is_bistate)
-       : controllable (&c), _current_uri (c.uri()), _port (p), bistate (is_bistate)
-{
-       init ();
-}
-
-MIDIControllable::~MIDIControllable ()
-{
-       drop_external_control ();
-}
+       : controllable (&c)
+       , _descriptor (0)
+       , _port (p)
+       , bistate (is_bistate)
 
-void
-MIDIControllable::init ()
 {
+       _learned = true; /* from controllable */
        setting = false;
        last_value = 0; // got a better idea ?
        control_type = none;
        _control_description = "MIDI Control: none";
        control_additional = (byte) -1;
-       connections = 0;
        feedback = true; // for now
+}
 
-       /* use channel 0 ("1") as the initial channel */
+MIDIControllable::~MIDIControllable ()
+{
+       drop_external_control ();
+}
 
-       midi_rebind (0);
+int
+MIDIControllable::init (const std::string& s)
+{
+       _current_uri = s;
+       delete _descriptor;
+       _descriptor = new ControllableDescriptor;
+       return _descriptor->set (s);
 }
 
 void
@@ -72,23 +92,23 @@ MIDIControllable::midi_forget ()
           our existing event + type information.
        */
 
-       if (connections > 0) {
-               midi_sense_connection[0].disconnect ();
-       }
-
-       if (connections > 1) {
-               midi_sense_connection[1].disconnect ();
-       }
-
-       connections = 0;
+       midi_sense_connection[0].disconnect ();
+       midi_sense_connection[1].disconnect ();
        midi_learn_connection.disconnect ();
+}
 
+void
+MIDIControllable::drop_external_control ()
+{
+       midi_forget ();
+       control_type = none;
+       control_additional = (byte) -1;
 }
 
 void
-MIDIControllable::reacquire_controllable ()
+MIDIControllable::set_controllable (Controllable* c)
 {
-       _controllable = Controllable::controllable_by_uri (current_uri);
+       controllable = c;
 }
 
 void
@@ -105,7 +125,7 @@ void
 MIDIControllable::learn_about_external_control ()
 {
        drop_external_control ();
-       midi_learn_connection = _port.input()->any.connect (mem_fun (*this, &MIDIControllable::midi_receiver));
+       _port.input()->any.connect_same_thread (midi_learn_connection, boost::bind (&MIDIControllable::midi_receiver, this, _1, _2, _3));
 }
 
 void
@@ -114,29 +134,12 @@ MIDIControllable::stop_learning ()
        midi_learn_connection.disconnect ();
 }
 
-void
-MIDIControllable::drop_external_control ()
-{
-       if (connections > 0) {
-               midi_sense_connection[0].disconnect ();
-       }
-       if (connections > 1) {
-               midi_sense_connection[1].disconnect ();
-       }
-
-       connections = 0;
-       midi_learn_connection.disconnect ();
-
-       control_type = none;
-       control_additional = (byte) -1;
-}
-
 float
 MIDIControllable::control_to_midi(float val)
 {
        float control_min = 0.0f;
        float control_max = 1.0f;
-       ARDOUR::AutomationControl* ac = dynamic_cast<ARDOUR::AutomationControl*>(&controllable);
+       ARDOUR::AutomationControl* ac = dynamic_cast<ARDOUR::AutomationControl*>(controllable);
        if (ac) {
                control_min = ac->parameter().min();
                control_max = ac->parameter().max();
@@ -153,16 +156,22 @@ MIDIControllable::midi_to_control(float val)
 {
        float control_min = 0.0f;
        float control_max = 1.0f;
-       ARDOUR::AutomationControl* ac = dynamic_cast<ARDOUR::AutomationControl*>(&controllable);
+       ARDOUR::AutomationControl* ac = dynamic_cast<ARDOUR::AutomationControl*>(controllable);
+
+       const float midi_range = 127.0f; // TODO: NRPN etc.
+       
        if (ac) {
+
+               if (ac->is_gain_like()) {
+                       return slider_position_to_gain (val/midi_range);
+               }
+               
                control_min = ac->parameter().min();
                control_max = ac->parameter().max();
        }
 
        const float control_range = control_max - control_min;
-       const float midi_range    = 127.0f; // TODO: NRPN etc.
-
-       return val / midi_range * control_range + control_min;
+       return  val / midi_range * control_range + control_min;
 }
 
 void
@@ -194,7 +203,7 @@ MIDIControllable::midi_sense_note (Parser &, EventTwoBytes *msg, bool is_on)
                */
 
                if (msg->note_number == control_additional) {
-                       controllable.set_value (is_on ? 1 : 0);
+                       controllable->set_value (is_on ? 1 : 0);
                }
        }
 
@@ -296,59 +305,43 @@ MIDIControllable::bind_midi (channel_t chn, eventType ev, MIDI::byte additional)
        int chn_i = chn;
        switch (ev) {
        case MIDI::off:
-               midi_sense_connection[0] = p.channel_note_off[chn_i].connect
-                       (mem_fun (*this, &MIDIControllable::midi_sense_note_off));
+               p.channel_note_off[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_note_off, this, _1, _2));
 
                /* if this is a bistate, connect to noteOn as well,
                   and we'll toggle back and forth between the two.
                */
 
                if (bistate) {
-                       midi_sense_connection[1] = p.channel_note_on[chn_i].connect
-                               (mem_fun (*this, &MIDIControllable::midi_sense_note_on));
-                       connections = 2;
-               } else {
-                       connections = 1;
-               }
+                       p.channel_note_on[chn_i].connect_same_thread (midi_sense_connection[1], boost::bind (&MIDIControllable::midi_sense_note_on, this, _1, _2));
+               } 
+
                _control_description = "MIDI control: NoteOff";
                break;
 
        case MIDI::on:
-               midi_sense_connection[0] = p.channel_note_on[chn_i].connect
-                       (mem_fun (*this, &MIDIControllable::midi_sense_note_on));
+               p.channel_note_on[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_note_on, this, _1, _2));
                if (bistate) {
-                       midi_sense_connection[1] = p.channel_note_off[chn_i].connect
-                               (mem_fun (*this, &MIDIControllable::midi_sense_note_off));
-                       connections = 2;
-               } else {
-                       connections = 1;
+                       p.channel_note_off[chn_i].connect_same_thread (midi_sense_connection[1], boost::bind (&MIDIControllable::midi_sense_note_off, this, _1, _2));
                }
                _control_description = "MIDI control: NoteOn";
                break;
-
+               
        case MIDI::controller:
-               midi_sense_connection[0] = p.channel_controller[chn_i].connect
-                       (mem_fun (*this, &MIDIControllable::midi_sense_controller));
-               connections = 1;
+               p.channel_controller[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_controller, this, _1, _2));
                snprintf (buf, sizeof (buf), "MIDI control: Controller %d", control_additional);
                _control_description = buf;
                break;
 
        case MIDI::program:
                if (!bistate) {
-                       midi_sense_connection[0] = p.channel_program_change[chn_i].connect
-                               (mem_fun (*this,
-                                      &MIDIControllable::midi_sense_program_change));
-                       connections = 1;
+                       p.channel_program_change[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_program_change, this, _1, _2));
                        _control_description = "MIDI control: ProgramChange";
                }
                break;
 
        case MIDI::pitchbend:
                if (!bistate) {
-                       midi_sense_connection[0] = p.channel_pitchbend[chn_i].connect
-                               (mem_fun (*this, &MIDIControllable::midi_sense_pitchbend));
-                       connections = 1;
+                       p.channel_pitchbend[chn_i].connect_same_thread (midi_sense_connection[0], boost::bind (&MIDIControllable::midi_sense_pitchbend, this, _1, _2));
                        _control_description = "MIDI control: Pitchbend";
                }
                break;
@@ -434,21 +427,24 @@ MIDIControllable::set_state (const XMLNode& node, int /*version*/)
 XMLNode&
 MIDIControllable::get_state ()
 {
-       if (!controllable) {
-               return XXX !what!;
-       }
-
        char buf[32];
-       XMLNode& node (controllable->get_state ());
 
-       snprintf (buf, sizeof(buf), "0x%x", (int) control_type);
-       node.add_property ("event", buf);
-       snprintf (buf, sizeof(buf), "%d", (int) control_channel);
-       node.add_property ("channel", buf);
-       snprintf (buf, sizeof(buf), "0x%x", (int) control_additional);
-       node.add_property ("additional", buf);
-       node.add_property ("feedback", (feedback ? "yes" : "no"));
+       XMLNode* node = new XMLNode ("MIDIControllable");
+
+       if (!_current_uri.empty()) {
+               node->add_property ("uri", _current_uri);
+       }
+
+       if (controllable) {
+               snprintf (buf, sizeof(buf), "0x%x", (int) control_type);
+               node->add_property ("event", buf);
+               snprintf (buf, sizeof(buf), "%d", (int) control_channel);
+               node->add_property ("channel", buf);
+               snprintf (buf, sizeof(buf), "0x%x", (int) control_additional);
+               node->add_property ("additional", buf);
+               node->add_property ("feedback", (feedback ? "yes" : "no"));
+       }
 
-       return node;
+       return *node;
 }