provide limited momentary note on/off MIDI binding option; remove some debugging...
authorPaul Davis <paul@linuxaudiosystems.com>
Fri, 1 Jan 2010 18:14:32 +0000 (18:14 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Fri, 1 Jan 2010 18:14:32 +0000 (18:14 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@6430 d708f5d6-7413-0410-9779-e7cbd77b26cf

libs/ardour/route.cc
libs/ardour/session_state.cc
libs/pbd/controllable_descriptor.cc
libs/surfaces/control_protocol/basic_ui.cc
libs/surfaces/generic_midi/generic_midi_control_protocol.cc
libs/surfaces/generic_midi/gmcp_gui.cc
libs/surfaces/generic_midi/midicontrollable.cc
libs/surfaces/generic_midi/midicontrollable.h

index 29cc32bf24996bd44388d6878243fb67218c16f3..3ae5e90b7b546b9c75fa0d68b22156a40808faf7 100644 (file)
@@ -2852,14 +2852,30 @@ void
 Route::SoloControllable::set_value (float val)
 {
        bool bval = ((val >= 0.5f) ? true: false);
+# if 0
+       this is how it should be done 
 
+       boost::shared_ptr<RouteList> rl (new RouteList);
+       rl->push_back (route);
+
+       if (Config->get_solo_control_is_listen_control()) {
+               _session.set_listen (rl, bval);
+       } else {
+               _session.set_solo (rl, bval);
+       }
+#else
        route.set_solo (bval, this);
+#endif
 }
 
 float
 Route::SoloControllable::get_value (void) const
 {
-       return route.self_soloed() ? 1.0f : 0.0f;
+       if (Config->get_solo_control_is_listen_control()) {
+               return route.listening() ? 1.0f : 0.0f;
+       } else {
+               return route.self_soloed() ? 1.0f : 0.0f;
+       }
 }
 
 void
index 3533e489568f12b994da5521b64290e3610d0ed8..e222de2221624a1df43ea398569179f483840b9a 100644 (file)
@@ -146,10 +146,8 @@ Session::first_stage_init (string fullpath, string snapshot_name)
        }
 
        if (Glib::file_test (_path, Glib::FILE_TEST_EXISTS) && ::access (_path.c_str(), W_OK)) {
-               cerr << "Session non-writable based on " << _path << endl;
                _writable = false;
        } else {
-               cerr << "Session writable based on " << _path << endl;
                _writable = true;
        }
 
@@ -2713,6 +2711,7 @@ Session::controllable_by_descriptor (const ControllableDescriptor& desc)
        case ControllableDescriptor::Gain:
                c = r->gain_control ();
                break;
+
        case ControllableDescriptor::Solo:
                c = r->solo_control();
                break;
index 2f84d706b42cb5cb168baff4615fdec3ec16184a..86f96bb50ccfa78cca3c18b9983c1d21d40d3364 100644 (file)
@@ -49,18 +49,6 @@ ControllableDescriptor::set (const std::string& str)
                return -1;
        }
 
-       cerr << "Path: " << endl;
-       for (vector<string>::iterator x = path.begin(); x != path.end(); ++x) {
-               cerr << '[' << (*x) << "] ";
-       } 
-       cerr << endl;
-
-       cerr << "Rest: " << endl;
-       for (vector<string>::iterator x = rest.begin(); x != rest.end(); ++x) {
-               cerr << '[' << (*x) << "] ";
-       } 
-       cerr << endl;
-
        if (path[0] == "route" || path[0] == "rid") {
 
                _top_level_type = RemoteControlID;
index 8c3eb029823d18a6d7bbad71b2aaff6caff73c8c..a21da95bb1b57bb9e3d4a2a11b81653bbcb16d23 100644 (file)
@@ -296,3 +296,98 @@ BasicUI::sample_to_timecode (nframes_t sample, Timecode::Time& timecode, bool us
        session->sample_to_timecode (sample, *((Timecode::Time*)&timecode), use_offset, use_subframes);
 }
 
+#if 0
+this stuff is waiting to go in so that all UI's can offer complex solo/mute functionality
+
+void
+BasicUI::solo_release (boost::shared_ptr<Route> r)
+{
+}
+
+void
+BasicUI::solo_press (boost::shared_ptr<Route> r, bool momentary, bool global, bool exclusive, bool isolate, bool solo_group)
+{
+       if (momentary) {
+               _solo_release = new SoloMuteRelease (_route->soloed());
+       }
+       
+       if (global) {
+               
+               if (_solo_release) {
+                       _solo_release->routes = _session->get_routes ();
+               }
+               
+               if (Config->get_solo_control_is_listen_control()) {
+                       _session->set_listen (_session->get_routes(), !_route->listening(),  Session::rt_cleanup, true);
+               } else {
+                       _session->set_solo (_session->get_routes(), !_route->soloed(),  Session::rt_cleanup, true);
+               }
+               
+       } else if (exclusive) {
+               
+               if (_solo_release) {
+                       _solo_release->exclusive = true;
+                       
+                       boost::shared_ptr<RouteList> routes = _session->get_routes();
+                       
+                       for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) {
+                               if ((*i)->soloed ()) {
+                                       _solo_release->routes_on->push_back (*i);
+                               } else {
+                                       _solo_release->routes_off->push_back (*i);
+                               }
+                       }
+               }
+               
+               if (Config->get_solo_control_is_listen_control()) {
+                       /* ??? we need a just_one_listen() method */
+               } else {
+                       _session->set_just_one_solo (_route, true);
+               }
+               
+       } else if (isolate) {
+               
+               // shift-click: toggle solo isolated status
+               
+               _route->set_solo_isolated (!_route->solo_isolated(), this);
+               delete _solo_release;
+               _solo_release = 0;
+               
+       } else if (solo_group) {
+               
+               /* Primary-button1: solo mix group.
+                  NOTE: Primary-button2 is MIDI learn.
+               */
+               
+               if (_route->route_group()) {
+                       
+                       if (_solo_release) {
+                               _solo_release->routes = _route->route_group()->route_list();
+                       }
+                       
+                       if (Config->get_solo_control_is_listen_control()) {
+                               _session->set_listen (_route->route_group()->route_list(), !_route->listening(),  Session::rt_cleanup, true);
+                       } else {
+                               _session->set_solo (_route->route_group()->route_list(), !_route->soloed(),  Session::rt_cleanup, true);
+                       }
+               }
+               
+       } else {
+               
+               /* click: solo this route */
+               
+               boost::shared_ptr<RouteList> rl (new RouteList);
+               rl->push_back (route());
+               
+               if (_solo_release) {
+                       _solo_release->routes = rl;
+               }
+               
+               if (Config->get_solo_control_is_listen_control()) {
+                       _session->set_listen (rl, !_route->listening());
+               } else {
+                       _session->set_solo (rl, !_route->soloed());
+               }
+       }
+}
+#endif
index f092fabb59673474da11d4da96ca6eb76066ceb8..51c1b119eb0fa41aaddc37214cd0ec4c2be9e590 100644 (file)
@@ -315,7 +315,7 @@ GenericMidiControlProtocol::start_learning (Controllable* c)
        }
 
        if (!mc) {
-               mc = new MIDIControllable (*_port, *c);
+               mc = new MIDIControllable (*_port, *c, false);
        }
        
        {
@@ -410,7 +410,7 @@ GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos,
                MIDI::byte value = control_number;
                
                // Create a MIDIControllable
-               MIDIControllable* mc = new MIDIControllable (*_port, *control);
+               MIDIControllable* mc = new MIDIControllable (*_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
@@ -520,7 +520,7 @@ GenericMidiControlProtocol::set_state (const XMLNode& node, int version)
                                c = session->controllable_by_id (id);
                                
                                if (c) {
-                                       MIDIControllable* mc = new MIDIControllable (*_port, *c);
+                                       MIDIControllable* mc = new MIDIControllable (*_port, *c, false);
 
                                        if (mc->set_state (**niter, version) == 0) {
                                                controllables.push_back (mc);
@@ -655,6 +655,7 @@ GenericMidiControlProtocol::create_binding (const XMLNode& node)
        string uri;
        MIDI::eventType ev;
        int intval;
+       bool momentary;
 
        if ((prop = node.property (X_("ctl"))) != 0) {
                ev = MIDI::controller;
@@ -684,11 +685,17 @@ GenericMidiControlProtocol::create_binding (const XMLNode& node)
        if (channel > 0) {
                channel -= 1;
        }
+
+       if ((prop = node.property (X_("momentary"))) != 0) {
+               momentary = string_is_affirmative (prop->value());
+       } else {
+               momentary = false;
+       }
        
        prop = node.property (X_("uri"));
        uri = prop->value();
 
-       MIDIControllable* mc = new MIDIControllable (*_port, false);
+       MIDIControllable* mc = new MIDIControllable (*_port, momentary);
 
        if (mc->init (uri)) {
                delete mc;
index 38bbdbe00a0b75485e61cd284ac501a6fc475fc1..c5ffce369d0b93839e737507450890134b8ed2f9 100644 (file)
@@ -5,6 +5,8 @@
 #include <gtkmm/comboboxtext.h>
 #include <gtkmm/label.h>
 #include <gtkmm/box.h>
+#include <gtkmm/adjustment.h>
+#include <gtkmm/spinbutton.h>
 
 #include "gtkmm2ext/utils.h"
 
@@ -21,8 +23,11 @@ public:
 private:
     GenericMidiControlProtocol& cp;
     Gtk::ComboBoxText map_combo;
+    Gtk::Adjustment bank_adjustment;
+    Gtk::SpinButton bank_spinner;
 
     void binding_changed ();
+    void bank_change ();
 };
 
 using namespace PBD;
@@ -56,6 +61,8 @@ GenericMidiControlProtocol::build_gui ()
 
 GMCPGUI::GMCPGUI (GenericMidiControlProtocol& p)
        : cp (p)
+       , bank_adjustment (1, 1, 100, 1, 10)
+       , bank_spinner (bank_adjustment)
 {
        vector<string> popdowns;
        popdowns.push_back (_("Reset All"));
@@ -74,6 +81,7 @@ GMCPGUI::GMCPGUI (GenericMidiControlProtocol& p)
 
        map_combo.signal_changed().connect (sigc::mem_fun (*this, &GMCPGUI::binding_changed));
 
+       set_spacing (6);
        set_border_width (12);
 
        Label* label = manage (new Label (_("Available MIDI bindings:")));
@@ -83,17 +91,42 @@ GMCPGUI::GMCPGUI (GenericMidiControlProtocol& p)
        hpack->pack_start (*label, false, false);
        hpack->pack_start (map_combo, false, false);
 
+       map_combo.show ();
+       label->show ();
+       hpack->show ();
+       
        pack_start (*hpack, false, false);
 
-       map_combo.show ();
+
+       bank_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &GMCPGUI::bank_change));
+
+       label = manage (new Label (_("Current Bank:")));
+       hpack = manage (new HBox);
+
+       hpack->set_spacing (6);
+       hpack->pack_start (*label, false, false);
+       hpack->pack_start (bank_spinner, false, false);
+
+
+       bank_spinner.show ();
        label->show ();
        hpack->show ();
+
+       pack_start (*hpack, false, false);
+
 }
 
 GMCPGUI::~GMCPGUI ()
 {
 }
 
+void
+GMCPGUI::bank_change ()
+{
+       int new_bank = bank_adjustment.get_value() - 1;
+       cp.set_current_bank (new_bank);
+}
+
 void
 GMCPGUI::binding_changed ()
 {
index d960140958d7d6939375f73e6acc1cb46d174aec..e5173c5345d1c8175c68237b74b57b3ccd785470 100644 (file)
@@ -19,8 +19,7 @@
 
 #define __STDC_FORMAT_MACROS 1
 #include <stdint.h>
-
-#include <cstdio> /* for sprintf, sigh */
+#include <cmath>
 #include <climits>
 
 #include "pbd/error.h"
@@ -40,11 +39,11 @@ using namespace MIDI;
 using namespace PBD;
 using namespace ARDOUR;
 
-MIDIControllable::MIDIControllable (Port& p, bool is_bistate)
+MIDIControllable::MIDIControllable (Port& p, bool m)
        : controllable (0)
        , _descriptor (0)
        , _port (p)
-       , bistate (is_bistate)
+       , _momentary (m)
 {
        _learned = false; /* from URI */
        setting = false;
@@ -55,12 +54,11 @@ MIDIControllable::MIDIControllable (Port& p, bool is_bistate)
        feedback = true; // for now
 }
 
-MIDIControllable::MIDIControllable (Port& p, Controllable& c, bool is_bistate)
+MIDIControllable::MIDIControllable (Port& p, Controllable& c, bool m)
        : controllable (&c)
        , _descriptor (0)
        , _port (p)
-       , bistate (is_bistate)
-
+       , _momentary (m)
 {
        _learned = true; /* from controllable */
        setting = false;
@@ -187,23 +185,23 @@ MIDIControllable::midi_sense_note_off (Parser &p, EventTwoBytes *tb)
 }
 
 void
-MIDIControllable::midi_sense_note (Parser &, EventTwoBytes *msg, bool is_on)
+MIDIControllable::midi_sense_note (Parser &, EventTwoBytes *msg, bool /* is_on */)
 {
        if (!controllable) { 
                return;
        }
 
-       if (!bistate) {
+
+       if (!controllable->is_toggle()) {
                controllable->set_value (msg->note_number/127.0);
        } else {
 
-               /* Note: parser handles the use of zero velocity to
-                  mean note off. if we get called with is_on=true, then we
-                  got a *real* note on.
-               */
-
-               if (msg->note_number == control_additional) {
-                       controllable->set_value (is_on ? 1 : 0);
+               if (control_additional == msg->note_number) {
+                       /* Note: parser handles the use of zero velocity to
+                          mean note off. if we get called with is_on=true, then we
+                          got a *real* note on.
+                       */
+                       controllable->set_value (controllable->get_value() > 0.5f ? 0.0f : 1.0f);
                }
        }
 
@@ -222,10 +220,11 @@ MIDIControllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
        }
 
        if (control_additional == msg->controller_number) {
-               if (!bistate) {
-                       controllable->set_value (midi_to_control(msg->value));
+
+               if (!controllable->is_toggle()) {
+                       controllable->set_value (midi_to_control (msg->value));
                } else {
-                       if (msg->value > 64.0) {
+                       if (msg->value > 64.0f) {
                                controllable->set_value (1);
                        } else {
                                controllable->set_value (0);
@@ -242,12 +241,14 @@ MIDIControllable::midi_sense_program_change (Parser &, byte msg)
        if (!controllable) { 
                return;
        }
-       /* XXX program change messages make no sense for bistates */
 
-       if (!bistate) {
+       if (!controllable->is_toggle()) {
                controllable->set_value (msg/127.0);
-               last_value = (MIDI::byte) (controllable->get_value() * 127.0); // to prevent feedback fights
+       } else {
+               controllable->set_value (controllable->get_value() > 0.5f ? 0.0f : 1.0f);
        }
+
+       last_value = (MIDI::byte) (controllable->get_value() * 127.0); // to prevent feedback fights
 }
 
 void
@@ -257,11 +258,14 @@ MIDIControllable::midi_sense_pitchbend (Parser &, pitchbend_t pb)
                return;
        }
 
-       /* pitchbend messages make no sense for bistates */
 
-       /* XXX gack - get rid of assumption about typeof pitchbend_t */
+       if (!controllable->is_toggle()) {
+               /* XXX gack - get rid of assumption about typeof pitchbend_t */
+               controllable->set_value ((pb/(float) SHRT_MAX));
+       } else {
+               controllable->set_value (controllable->get_value() > 0.5f ? 0.0f : 1.0f);
+       }
 
-       controllable->set_value ((pb/(float) SHRT_MAX));
        last_value = (MIDI::byte) (controllable->get_value() * 127.0); // to prevent feedback fights
 }
 
@@ -307,11 +311,11 @@ MIDIControllable::bind_midi (channel_t chn, eventType ev, MIDI::byte additional)
        case MIDI::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,
+               /* if this is a togglee, connect to noteOn as well,
                   and we'll toggle back and forth between the two.
                */
 
-               if (bistate) {
+               if (_momentary) {
                        p.channel_note_on[chn_i].connect_same_thread (midi_sense_connection[1], boost::bind (&MIDIControllable::midi_sense_note_on, this, _1, _2));
                } 
 
@@ -320,7 +324,7 @@ MIDIControllable::bind_midi (channel_t chn, eventType ev, MIDI::byte additional)
 
        case MIDI::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) {
+               if (_momentary) {
                        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";
@@ -333,17 +337,13 @@ MIDIControllable::bind_midi (channel_t chn, eventType ev, MIDI::byte additional)
                break;
 
        case MIDI::program:
-               if (!bistate) {
-                       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";
-               }
+               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) {
-                       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";
-               }
+               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;
 
        default:
@@ -356,13 +356,18 @@ MIDIControllable::send_feedback ()
 {
        byte msg[3];
 
-       if (setting || !feedback || control_type == none) {
+       if (!_learned || setting || !feedback || control_type == none) {
                return;
        }
 
        msg[0] = (control_type & 0xF0) | (control_channel & 0xF);
        msg[1] = control_additional;
-       msg[2] = (byte) (control_to_midi(controllable->get_value()));
+
+       if (controllable->is_gain_like()) {
+               msg[2] = (byte) lrintf (gain_to_slider_position (controllable->get_value()) * 127.0f);
+       } else {
+               msg[2] = (byte) (control_to_midi(controllable->get_value()));
+       }
 
        _port.write (msg, 3, 0);
 }
@@ -372,7 +377,13 @@ MIDIControllable::write_feedback (MIDI::byte* buf, int32_t& bufsize, bool /*forc
 {
        if (control_type != none && feedback && bufsize > 2) {
 
-               MIDI::byte gm = (MIDI::byte) (control_to_midi(controllable->get_value()));
+               MIDI::byte gm;
+
+               if (controllable->is_gain_like()) {
+                       gm = (byte) lrintf (gain_to_slider_position (controllable->get_value()) * 127.0f);
+               } else {
+                       gm = (byte) (control_to_midi(controllable->get_value()));
+               }
 
                if (gm != last_value) {
                        *buf++ = (0xF0 & control_type) | (0xF & control_channel);
index 0ce28ecabd3e6b02e436391a1d71d18dd5180109..2e70e98a751e461c68a7b886accf86e21df2fd3d 100644 (file)
@@ -43,8 +43,8 @@ namespace MIDI {
 class MIDIControllable : public PBD::Stateful
 {
   public:
-       MIDIControllable (MIDI::Port&, PBD::Controllable&, bool bistate = false);
-       MIDIControllable (MIDI::Port&, bool bistate = false);
+       MIDIControllable (MIDI::Port&, PBD::Controllable&, bool momentary);
+       MIDIControllable (MIDI::Port&, bool momentary = false);
        virtual ~MIDIControllable ();
 
        int init (const std::string&);
@@ -95,7 +95,7 @@ class MIDIControllable : public PBD::Stateful
        MIDI::Port&     _port;
        bool             setting;
        MIDI::byte       last_value;
-       bool             bistate;
+       bool            _momentary;
        bool            _is_gain_controller;
        bool            _learned;
        int              midi_msg_id;      /* controller ID or note number */