MIDI/Controllables for monitor section, and related fixes
authorPaul Davis <paul@linuxaudiosystems.com>
Tue, 6 Apr 2010 16:57:35 +0000 (16:57 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Tue, 6 Apr 2010 16:57:35 +0000 (16:57 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@6863 d708f5d6-7413-0410-9779-e7cbd77b26cf

18 files changed:
gtk2_ardour/ardour_ui2.cc
gtk2_ardour/gui_thread.h
gtk2_ardour/monitor_section.cc
gtk2_ardour/monitor_section.h
gtk2_ardour/utils.cc
gtk2_ardour/utils.h
libs/ardour/ardour/automation_control.h
libs/ardour/ardour/monitor_processor.h
libs/ardour/monitor_processor.cc
libs/ardour/session.cc
libs/gtkmm2ext/bindable_button.cc
libs/gtkmm2ext/gtkmm2ext/bindable_button.h
libs/gtkmm2ext/gtkmm2ext/gui_thread.h [new file with mode: 0644]
libs/gtkmm2ext/gtkmm2ext/motionfeedback.h
libs/gtkmm2ext/motionfeedback.cc
libs/gtkmm2ext/wscript
libs/pbd/pbd/controllable.h
libs/surfaces/generic_midi/midicontrollable.cc

index 9747685d8a37311cd68f52f4d9589aa0b3e7132c..05b6b5d5bde49c85fa04bc9c4d37a7007c60b2b2 100644 (file)
@@ -468,6 +468,8 @@ ARDOUR_UI::reattach_tearoff (Box* b, Widget* w, int32_t n)
 void
 ARDOUR_UI::soloing_changed (bool onoff)
 {
+        cerr << "solo change, " << onoff << endl;
+
        if (solo_alert_button.get_active() != onoff) {
                solo_alert_button.set_active (onoff);
        }
index 16dc0600a15b3bb1e545ec0f4a2ad542495f729e..cd453c996a35e0badd13223f25527755df1974ba 100644 (file)
@@ -1,40 +1 @@
-/*
-    Copyright (C) 2000-2007 Paul Davis
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef __ardour_gtk_gui_thread_h__
-#define __ardour_gtk_gui_thread_h__
-
-#include <cstdlib>
-#include <gtkmm2ext/gtk_ui.h>
-#include <boost/bind.hpp>
-#include <boost/bind/protect.hpp>
-
-namespace sigc {
-        class trackable;
-}
-
-#define ENSURE_GUI_THREAD(obj,method, ...) if (!Gtkmm2ext::UI::instance()->caller_is_self()) { abort (); } 
-
-#define gui_context() Gtkmm2ext::UI::instance() /* a UICallback-derived object that specifies the event loop for GUI signal handling */
-#define ui_bind(f, ...) boost::protect (boost::bind (f, __VA_ARGS__))
-
-extern PBD::EventLoop::InvalidationRecord* __invalidator (sigc::trackable& trackable, const char*, int);
-#define invalidator(x) __invalidator ((x), __FILE__, __LINE__)
-
-#endif /* __ardour_gtk_gui_thread_h__ */
+#include "gtkmm2ext/gui_thread.h"
index f455ee515c09e8bfade2f9bf8c95de2a45263507..b9e4c1b8a592f136d854b5784ab47c4e21b2ad4d 100644 (file)
@@ -38,11 +38,11 @@ MonitorSection::MonitorSection (Session* s)
         , _tearoff (0)
         , gain_adjustment (0.781787, 0.0, 1.0, 0.01, 0.1) // initial value is unity gain
         , gain_control (0)
-        , dim_adjustment (0.2, 0.0, 1.0, 0.01, 0.1) 
+        , dim_adjustment (0.2, 0.0, 1.0, 0.01, 0.1) // upper+lower will be reset to match model
         , dim_control (0)
-        , solo_boost_adjustment (1.0, 1.0, 3.0, 0.01, 0.1) 
+        , solo_boost_adjustment (1.0, 1.0, 3.0, 0.01, 0.1)  // upper and lower will be reset to match model
         , solo_boost_control (0)
-        , solo_cut_adjustment (0.0, 0.0, 1.0, 0.01, 0.1)
+        , solo_cut_adjustment (0.0, 0.0, 1.0, 0.01, 0.1) // upper and lower will be reset to match model
         , solo_cut_control (0)
         , solo_in_place_button (solo_model_group, _("SiP"))
         , afl_button (solo_model_group, _("AFL"))
@@ -64,14 +64,13 @@ MonitorSection::MonitorSection (Session* s)
         }
         
         set_session (s);
-        
+
         VBox* spin_packer;
         Label* spin_label;
 
         /* Dim */
 
         dim_control = new VolumeController (little_knob_pixbuf, &dim_adjustment, false, 30, 30);
-        dim_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &MonitorSection::dim_level_changed));
 
         HBox* dim_packer = manage (new HBox);
         dim_packer->show ();
@@ -125,7 +124,6 @@ MonitorSection::MonitorSection (Session* s)
         /* Solo Boost */
 
         solo_boost_control = new VolumeController (little_knob_pixbuf, &solo_boost_adjustment, false, 30, 30);
-        solo_boost_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &MonitorSection::solo_boost_changed));
 
         HBox* solo_packer = manage (new HBox);
         solo_packer->set_spacing (12);
@@ -143,7 +141,6 @@ MonitorSection::MonitorSection (Session* s)
         /* Solo (SiP) cut */
 
         solo_cut_control = new VolumeController (little_knob_pixbuf, &solo_cut_adjustment, false, 30, 30);
-        solo_cut_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &MonitorSection::solo_cut_changed));
 
         spin_label = manage (new Label (_("SiP Cut")));
         spin_packer = manage (new VBox);
@@ -197,7 +194,6 @@ MonitorSection::MonitorSection (Session* s)
         /* Gain */
 
         gain_control = new VolumeController (big_knob_pixbuf, &gain_adjustment, false, 80, 80);
-        gain_adjustment.signal_value_changed().connect (sigc::mem_fun (*this, &MonitorSection::gain_value_changed));
 
         spin_label = manage (new Label (_("Gain")));
         spin_packer = manage (new VBox);
@@ -231,6 +227,7 @@ MonitorSection::MonitorSection (Session* s)
 
         populate_buttons ();
         map_state ();
+        assign_controllables ();
 
         _tearoff = new TearOff (hpacker);
 
@@ -270,16 +267,19 @@ MonitorSection::set_session (Session* s)
                 if (_route) {
                         /* session with control outs */
                         _monitor = _route->monitor_control ();
+                        assign_controllables ();
                 } else { 
                         /* session with no control outs */
                         _monitor.reset ();
                         _route.reset ();
                 }
+                
                         
         } else {
                 /* no session */
                 _monitor.reset ();
                 _route.reset ();
+                control_connections.drop_connections ();
         }
 
         /* both might be null */
@@ -669,30 +669,6 @@ MonitorSection::setup_knob_images ()
         }
 }
 
-void
-MonitorSection::gain_value_changed ()
-{
-        if (_route) {
-                _route->set_gain (slider_position_to_gain (gain_adjustment.get_value()), this);
-        }
-}
-
-void
-MonitorSection::dim_level_changed ()
-{
-        if (_monitor) {
-                _monitor->set_dim_level (dim_adjustment.get_value());
-        }
-}
-
-void
-MonitorSection::solo_boost_changed ()
-{
-        if (_monitor) {
-                _monitor->set_solo_boost_level (solo_boost_adjustment.get_value());
-        }
-}
-
 bool
 MonitorSection::nonlinear_gain_printer (SpinButton* button)
 {
@@ -880,3 +856,53 @@ MonitorSection::parameter_changed (std::string name)
                 solo_cut_adjustment.set_value (gain_to_slider_position (Config->get_solo_mute_gain()));
         }
 }
+
+void
+MonitorSection::assign_controllables ()
+{
+        boost::shared_ptr<Controllable> none;
+
+        if (!gain_control) {
+                /* too early - GUI controls not set up yet */
+                return;
+        }
+
+        if (_route) {
+                gain_control->set_controllable (_route->gain_control());
+                control_link (control_connections, _route->gain_control(), gain_adjustment);
+        } else {
+                gain_control->set_controllable (none);
+        }
+
+        if (_monitor) {
+
+                cut_all_button.set_controllable (_monitor->cut_control());
+                cut_all_button.watch ();
+                dim_all_button.set_controllable (_monitor->dim_control());
+                dim_all_button.watch ();
+                mono_button.set_controllable (_monitor->mono_control());
+                mono_button.watch ();
+
+                boost::shared_ptr<Controllable> c (_monitor->dim_level_control ());
+
+                dim_control->set_controllable (c);
+                dim_adjustment.set_lower (c->lower());
+                dim_adjustment.set_upper (c->upper());
+                control_link (control_connections, c, dim_adjustment);
+                
+                c = _monitor->solo_boost_control ();
+                solo_boost_control->set_controllable (c);
+                solo_boost_adjustment.set_lower (c->lower());
+                solo_boost_adjustment.set_upper (c->upper());
+                control_link (control_connections, c, solo_boost_adjustment);
+
+        } else {
+
+                cut_all_button.set_controllable (none);
+                dim_all_button.set_controllable (none);
+                mono_button.set_controllable (none);
+
+                dim_control->set_controllable (none);
+                solo_boost_control->set_controllable (none);
+        }
+}
index 65d460d84d3181bb55c6c60372ce3c158606cb68..da20857aead13c7506ac05492e0d441e1ee4def5 100644 (file)
@@ -121,4 +121,7 @@ class MonitorSection : public RouteUI
         void parameter_changed (std::string);
 
         PBD::ScopedConnection config_connection;
+        PBD::ScopedConnectionList control_connections;
+        
+        void assign_controllables ();
 };
index 0bf7c5657f6a5be3781b29902ea048b935fb9e8e..aa57a3b773cb87a60f3ab79fd6a9542958477e93 100644 (file)
@@ -47,6 +47,7 @@
 #include "i18n.h"
 #include "rgb_macros.h"
 #include "canvas_impl.h"
+#include "gui_thread.h"
 
 using namespace std;
 using namespace Gtk;
@@ -1040,3 +1041,41 @@ escape_underscores (string const & s)
 
        return o;
 }
+
+static void
+adjustment_to_controllable (Gtk::Adjustment* adj, boost::weak_ptr<Controllable> wcont)
+{
+        boost::shared_ptr<Controllable> cont = wcont.lock();
+
+        if (cont) {
+                double val = adj->get_value();
+                if (val != cont->get_value()) {
+                        cont->set_value (val);
+                }
+        }
+}
+
+static void
+controllable_to_adjustment (Gtk::Adjustment* adj, boost::weak_ptr<Controllable> wcont)
+{
+        boost::shared_ptr<Controllable> cont = wcont.lock();
+
+        if (cont) {
+                float val = cont->get_value();
+                
+                if (val != adj->get_value()) {
+                        adj->set_value (val);
+                }
+        }
+}
+
+void
+control_link (ScopedConnectionList& scl, boost::shared_ptr<Controllable> c, Gtk::Adjustment& a)
+{
+        boost::weak_ptr<Controllable> wc (c);
+
+        a.signal_value_changed().connect (sigc::bind (sigc::ptr_fun (adjustment_to_controllable), &a, wc));
+        c->Changed.connect (scl, MISSING_INVALIDATOR, boost::bind (controllable_to_adjustment, &a, wc),
+                            gui_context());
+}
+                                           
index ca1d751539d7fb3eb66d6db20a2df04d7e939ab6..bfde7807d5f38fbac33a0c80c639befb5f53aa25 100644 (file)
 
 #include "canvas.h"
 
+namespace PBD {
+        class Controllable;
+        class ScopedConnectionList;
+}
+
 namespace Gtk {
        class Window;
        class ComboBoxText;
        class Paned;
+        class Adjustment;
 }
 
 Glib::ustring fit_to_pixels (const Glib::ustring&, int pixel_width, Pango::FontDescription& font, int& actual_width, bool with_ellipses = false);
@@ -98,4 +104,6 @@ void resize_window_to_proportion_of_monitor (Gtk::Window*, int, int);
 
 std::string escape_underscores (std::string const &);
 
+void control_link (PBD::ScopedConnectionList& scl, boost::shared_ptr<PBD::Controllable> c, Gtk::Adjustment& a);
+
 #endif /* __ardour_gtk_utils_h__ */
index 15bac5fef863c2d40f182389241af4b95c30674f..3c832ae704cd4b016b61dfcaccdccc7392b29b26 100644 (file)
@@ -83,6 +83,9 @@ public:
         */
        float get_value() const;
 
+        float lower() const { return parameter().min(); }
+        float upper() const { return parameter().max(); }
+
 protected:
        ARDOUR::Session& _session;
 };
index 18c50e51043293af9ff045a2bf081dec2c2544dc..8b8e90573df3040cb23516d926204cf0a7caac95 100644 (file)
 #ifndef __ardour_monitor_processor_h__
 #define __ardour_monitor_processor_h__
 
+#include <iostream>
 #include <vector>
 
 #include "pbd/signals.h"
+#include "pbd/compose.h"
+#include "pbd/controllable.h"
 
 #include "ardour/types.h"
 #include "ardour/processor.h"
@@ -33,10 +36,77 @@ namespace ARDOUR {
 
 class Session;
 
+template<typename T> class MPControl : public PBD::Controllable {
+  public:
+        MPControl (T initial, const std::string& name, PBD::Controllable::Flag flag,
+                   float lower = 0.0f, float upper = 1.0f)
+                : PBD::Controllable (name, flag)
+                , _value (initial) 
+                , _lower (lower)
+                , _upper (upper) 
+        {}
+        
+        /* Controllable API */
+        
+        void set_value (float v) { 
+                T newval = (T) v;
+                if (newval != _value) {
+                        _value = newval;
+                        Changed(); /* EMIT SIGNAL */
+                }
+        }
+        
+        float get_value () const { 
+                return (float) _value;
+        }
+        
+        float lower () const { return _lower; }
+        float upper () const { return _upper; }
+
+        /* "access as T" API */
+        
+        MPControl& operator=(const T& v) { 
+                if (v != _value) {
+                        _value = v;
+                        Changed (); /* EMIT SIGNAL */
+                }
+                return *this;
+        }
+        
+        bool operator==(const T& v) const {
+                return _value == v;
+        }
+        
+        bool operator<(const T& v) const {
+                return _value < v;
+        }
+        
+        bool operator<=(const T& v) const {
+                return _value <= v;
+        }
+        
+        bool operator>(const T& v) const {
+                return _value > v;
+        }
+        
+        bool operator>=(const T& v) const {
+                return _value >= v;
+        }
+        
+        operator T() const { return _value; }
+        T val() const { return _value; }
+        
+  protected:
+        T _value;
+        T _lower;
+        T _upper;
+};
+
 class MonitorProcessor : public Processor
 {
   public:
         MonitorProcessor (Session&);
+        ~MonitorProcessor ();
 
         bool display_to_user() const;
 
@@ -71,27 +141,89 @@ class MonitorProcessor : public Processor
         bool mono () const;
 
         PBD::Signal0<void> Changed;
+
+        boost::shared_ptr<PBD::Controllable> channel_cut_control (uint32_t) const;
+        boost::shared_ptr<PBD::Controllable> channel_dim_control (uint32_t) const;
+        boost::shared_ptr<PBD::Controllable> channel_polarity_control (uint32_t) const;
+        boost::shared_ptr<PBD::Controllable> channel_solo_control (uint32_t) const;
         
+        boost::shared_ptr<PBD::Controllable> dim_control () const { return _dim_all_control; }
+        boost::shared_ptr<PBD::Controllable> cut_control () const { return _cut_all_control; }
+        boost::shared_ptr<PBD::Controllable> mono_control () const { return _mono_control; }
+        boost::shared_ptr<PBD::Controllable> dim_level_control () const { return _dim_level_control; }
+        boost::shared_ptr<PBD::Controllable> solo_boost_control () const { return _solo_boost_level_control; }
+
   private:
         struct ChannelRecord { 
             gain_t current_gain;
-            gain_t cut;
-            bool   dim;
-            gain_t polarity;
-            bool   soloed;
 
-            ChannelRecord () 
-            : current_gain(1.0), cut(1.0), dim(false), polarity(1.0), soloed (false) {}
+            /* pointers - created first, but managed by boost::shared_ptr<> */
+
+            MPControl<gain_t>* cut_ptr;
+            MPControl<bool>*   dim_ptr;
+            MPControl<gain_t>* polarity_ptr;
+            MPControl<bool>*   soloed_ptr;
+
+            /* shared ptr access and lifetime management, for external users */
+
+            boost::shared_ptr<PBD::Controllable> cut_control;
+            boost::shared_ptr<PBD::Controllable> dim_control;
+            boost::shared_ptr<PBD::Controllable> polarity_control;
+            boost::shared_ptr<PBD::Controllable> soloed_control;
+
+            /* typed controllables for internal use */
+
+            MPControl<gain_t>& cut;
+            MPControl<bool>&   dim;
+            MPControl<gain_t>& polarity;
+            MPControl<bool>&   soloed;
+            
+            ChannelRecord (uint32_t chn) : current_gain(1.0)
+            , cut_ptr (new MPControl<gain_t> (1.0, string_compose (_("cut control %1"), chn), PBD::Controllable::GainLike))
+                    , dim_ptr (new MPControl<bool> (false, string_compose (_("dim control"), chn), PBD::Controllable::Toggle))
+                    , polarity_ptr (new MPControl<gain_t> (1.0, string_compose (_("polarity control"), chn), PBD::Controllable::Toggle))
+                    , soloed_ptr (new MPControl<bool> (false, string_compose (_("solo control"), chn), PBD::Controllable::Toggle))
+
+                    , cut_control (cut_ptr)
+                    , dim_control (dim_ptr)
+                    , polarity_control (polarity_ptr)
+                    , soloed_control (soloed_ptr)
+
+                    , cut (*cut_ptr)
+                    , dim (*dim_ptr)
+                    , polarity (*polarity_ptr)
+                    , soloed (*soloed_ptr)
+                    
+            {}
         };
-
-        std::vector<ChannelRecord> _channels;
+        
+        std::vector<ChannelRecord*> _channels;
 
         uint32_t             solo_cnt;
-        bool                _dim_all;
-        bool                _cut_all;
-        bool                _mono;
-        volatile gain_t     _dim_level;
-        volatile gain_t     _solo_boost_level;
+
+        /* pointers - created first, but managed by boost::shared_ptr<> */
+
+        MPControl<bool>*            _dim_all_ptr;
+        MPControl<bool>*            _cut_all_ptr;
+        MPControl<bool>*            _mono_ptr;
+        MPControl<volatile gain_t>* _dim_level_ptr;
+        MPControl<volatile gain_t>* _solo_boost_level_ptr;
+
+        /* shared ptr access and lifetime management, for external users */
+
+        boost::shared_ptr<PBD::Controllable> _dim_all_control;
+        boost::shared_ptr<PBD::Controllable> _cut_all_control;
+        boost::shared_ptr<PBD::Controllable> _mono_control;
+        boost::shared_ptr<PBD::Controllable> _dim_level_control;
+        boost::shared_ptr<PBD::Controllable> _solo_boost_level_control;
+
+        /* typed controllables for internal use */
+
+        MPControl<bool>&            _dim_all;
+        MPControl<bool>&            _cut_all;
+        MPControl<bool>&            _mono;
+        MPControl<volatile gain_t>& _dim_level;
+        MPControl<volatile gain_t>& _solo_boost_level;
 
         void allocate_channels (uint32_t);
 };
index 51bd30a389bff38807f8ca6ea33ccc634fd8ba3f..80ed2b8e5da445aaceb03280d30a12829fe81b06 100644 (file)
@@ -15,30 +15,68 @@ using namespace ARDOUR;
 using namespace PBD;
 using namespace std;
 
+/* specialize for bool because of set_value() semantics */
+
+namespace ARDOUR {
+        template<> void MPControl<bool>::set_value (float v) {
+                bool newval = fabs (v) >= 0.5;
+                if (newval != _value) {
+                        _value = newval;
+                        Changed(); /* EMIT SIGNAL */
+                }
+        }
+}
+
 MonitorProcessor::MonitorProcessor (Session& s)
         : Processor (s, X_("MonitorOut"))
+        , solo_cnt (0)
+
+        , _dim_all_ptr (new MPControl<bool> (false, _("monitor dim"), Controllable::Toggle))
+        , _cut_all_ptr (new MPControl<bool> (false, _("monitor cut"), Controllable::Toggle))
+        , _mono_ptr (new MPControl<bool> (false, _("monitor mono"), Controllable::Toggle))
+        , _dim_level_ptr (new MPControl<volatile gain_t> 
+                          (0.2, _("monitor mono"), Controllable::Flag (0), 0.0f, 1.0f))
+        , _solo_boost_level_ptr (new MPControl<volatile gain_t> 
+                                 (1.0, _("monitor mono"), Controllable::Flag (0), 1.0f, 3.0f))
+          
+        , _dim_all_control (_dim_all_ptr)
+        , _cut_all_control (_cut_all_ptr)
+        , _mono_control (_mono_ptr)
+        , _dim_level_control (_dim_level_ptr)
+        , _solo_boost_level_control (_solo_boost_level_ptr)
+
+        , _dim_all (*_dim_all_ptr)
+        , _cut_all (*_cut_all_ptr)
+        , _mono (*_mono_ptr)
+        , _dim_level (*_dim_level_ptr)
+        , _solo_boost_level (*_solo_boost_level_ptr)
+
 {
-        solo_cnt = 0;
-        _cut_all = false;
-        _dim_all = false;
-        _dim_level = 0.2;
-        _solo_boost_level = 1.0;
+}
+
+MonitorProcessor::~MonitorProcessor ()
+{
+        allocate_channels (0);
 }
 
 void
 MonitorProcessor::allocate_channels (uint32_t size)
 {
         while (_channels.size() > size) {
-                if (_channels.back().soloed) {
+                if (_channels.back()->soloed) {
                         if (solo_cnt > 0) {
                                 --solo_cnt;
                         }
                 }
+                ChannelRecord* cr = _channels.back();
                 _channels.pop_back();
+                delete cr;
         }
 
+        uint32_t n = _channels.size() + 1;
+
         while (_channels.size() < size) {
-                _channels.push_back (ChannelRecord());
+                _channels.push_back (new ChannelRecord (n));
         }
 }
 
@@ -74,12 +112,12 @@ MonitorProcessor::set_state (const XMLNode& node, int version)
         allocate_channels (atoi (prop->value()));
 
         if ((prop = node.property (X_("dim-level"))) != 0) {
-                double val = atof (prop->value());
+                gain_t val = atof (prop->value());
                 _dim_level = val;
         }
 
         if ((prop = node.property (X_("solo-boost-level"))) != 0) {
-                double val = atof (prop->value());
+                gain_t val = atof (prop->value());
                 _solo_boost_level = val;
         }
 
@@ -118,7 +156,7 @@ MonitorProcessor::set_state (const XMLNode& node, int version)
                                       << endmsg;
                                 return -1;
                         }
-                        ChannelRecord& cr (_channels[chn]);
+                        ChannelRecord& cr (*_channels[chn]);
 
                         if ((prop = (*i)->property ("cut")) != 0) {
                                 if (string_is_affirmative (prop->value())){
@@ -152,8 +190,8 @@ MonitorProcessor::set_state (const XMLNode& node, int version)
 
         solo_cnt = 0;
 
-        for (vector<ChannelRecord>::const_iterator x = _channels.begin(); x != _channels.end(); ++x) {
-                if (x->soloed) {
+        for (vector<ChannelRecord*>::const_iterator x = _channels.begin(); x != _channels.end(); ++x) {
+                if ((*x)->soloed) {
                         solo_cnt++;
                 }
         }
@@ -171,10 +209,10 @@ MonitorProcessor::state (bool full)
 
        node.add_property (X_("type"), X_("monitor"));
         
-        snprintf (buf, sizeof(buf), "%.12g", _dim_level);
+        snprintf (buf, sizeof(buf), "%.12g", _dim_level.val());
         node.add_property (X_("dim-level"), buf);
 
-        snprintf (buf, sizeof(buf), "%.12g", _solo_boost_level);
+        snprintf (buf, sizeof(buf), "%.12g", _solo_boost_level.val());
         node.add_property (X_("solo-boost-level"), buf);
 
         node.add_property (X_("cut-all"), (_cut_all ? "yes" : "no"));
@@ -189,16 +227,16 @@ MonitorProcessor::state (bool full)
         XMLNode* chn_node;
         uint32_t chn = 0;
 
-        for (vector<ChannelRecord>::const_iterator x = _channels.begin(); x != _channels.end(); ++x, ++chn) {
+        for (vector<ChannelRecord*>::const_iterator x = _channels.begin(); x != _channels.end(); ++x, ++chn) {
                 chn_node = new XMLNode (X_("Channel"));
 
                 snprintf (buf, sizeof (buf), "%u", chn);
                 chn_node->add_property ("id", buf);
-
-                chn_node->add_property (X_("cut"), x->cut == 1.0 ? "no" : "yes");
-                chn_node->add_property (X_("invert"), x->polarity == 1.0 ? "no" : "yes");
-                chn_node->add_property (X_("dim"), x->dim ? "yes" : "no");
-                chn_node->add_property (X_("solo"), x->soloed ? "yes" : "no");
+                
+                chn_node->add_property (X_("cut"), (*x)->cut == 1.0f ? "no" : "yes");
+                chn_node->add_property (X_("invert"), (*x)->polarity == 1.0f ? "no" : "yes");
+                chn_node->add_property (X_("dim"), (*x)->dim ? "yes" : "no");
+                chn_node->add_property (X_("solo"), (*x)->soloed ? "yes" : "no");
                 
                 node.add_child_nocopy (*chn_node);
         }
@@ -226,36 +264,38 @@ MonitorProcessor::run (BufferSet& bufs, sframes_t /*start_frame*/, sframes_t /*e
 
                 /* don't double-scale by both track dim and global dim coefficients */
 
-                gain_t dim_level = (global_dim == 1.0 ? (_channels[chn].dim ? dim_level_this_time : 1.0) : 1.0);
+                gain_t dim_level = (global_dim == 1.0 ? (_channels[chn]->dim ? dim_level_this_time : 1.0) : 1.0);
 
-                if (_channels[chn].soloed) {
-                        target_gain = _channels[chn].polarity * _channels[chn].cut * dim_level * global_cut * global_dim * solo_boost;
+                if (_channels[chn]->soloed) {
+                        target_gain = _channels[chn]->polarity * _channels[chn]->cut * dim_level * global_cut * global_dim * solo_boost;
                 } else {
                         if (solo_cnt == 0) {
-                                target_gain = _channels[chn].polarity * _channels[chn].cut * dim_level * global_cut * global_dim * solo_boost;
+                                target_gain = _channels[chn]->polarity * _channels[chn]->cut * dim_level * global_cut * global_dim * solo_boost;
                         } else {
                                 target_gain = 0.0;
                         }
                 }
 
                 DEBUG_TRACE (DEBUG::Monitor, 
-                             string_compose("channel %1 sb %2 gc %3 gd %4 cd %5 dl %6 cp %7 cc %8 cs %9 sc %10 TG %11\n", 
+                             string_compose("channel %1 SB %12 sb %2 gc %3 gd %4 cd %5 dl %6 cp %7 cc %8 cs %9 sc %10 TG %11\n", 
                                             chn, 
                                             solo_boost,
                                             global_cut,
                                             global_dim,
-                                            _channels[chn].dim,
+                                            _channels[chn]->dim,
                                             dim_level,
-                                            _channels[chn].polarity,
-                                            _channels[chn].cut,
-                                            _channels[chn].soloed,
+                                            _channels[chn]->polarity,
+                                            _channels[chn]->cut,
+                                            _channels[chn]->soloed,
                                             solo_cnt,
-                                            target_gain));
+                                            target_gain, 
+                                            (float) _solo_boost_level.val()
+                                     ));
                 
-                if (target_gain != _channels[chn].current_gain || target_gain != 1.0f) {
+                if (target_gain != _channels[chn]->current_gain || target_gain != 1.0f) {
 
-                        Amp::apply_gain (*b, nframes, _channels[chn].current_gain, target_gain);
-                        _channels[chn].current_gain = target_gain;
+                        Amp::apply_gain (*b, nframes, _channels[chn]->current_gain, target_gain);
+                        _channels[chn]->current_gain = target_gain;
                 }
 
                 ++chn;
@@ -317,33 +357,33 @@ void
 MonitorProcessor::set_polarity (uint32_t chn, bool invert)
 {
         if (invert) {
-                _channels[chn].polarity = -1.0f;
+                _channels[chn]->polarity = -1.0f;
         } else {
-                _channels[chn].polarity = 1.0f;
+                _channels[chn]->polarity = 1.0f;
         }
 }       
 
 void
 MonitorProcessor::set_dim (uint32_t chn, bool yn)
 {
-        _channels[chn].dim = yn;
+        _channels[chn]->dim = yn;
 }
 
 void
 MonitorProcessor::set_cut (uint32_t chn, bool yn)
 {
         if (yn) {
-                _channels[chn].cut = 0.0f;
+                _channels[chn]->cut = 0.0f;
         } else {
-                _channels[chn].cut = 1.0f;
+                _channels[chn]->cut = 1.0f;
         }
 }
 
 void
 MonitorProcessor::set_solo (uint32_t chn, bool solo)
 {
-        if (solo != _channels[chn].soloed) {
-                _channels[chn].soloed = solo;
+        if (solo != _channels[chn]->soloed) {
+                _channels[chn]->soloed = solo;
                 
                 if (solo) {
                         solo_cnt++;
@@ -394,27 +434,27 @@ MonitorProcessor::set_solo_boost_level (gain_t val)
 bool 
 MonitorProcessor::soloed (uint32_t chn) const
 {
-        return _channels[chn].soloed;
+        return _channels[chn]->soloed;
 }
 
 
 bool 
 MonitorProcessor::inverted (uint32_t chn) const
 {
-        return _channels[chn].polarity < 0.0f;
+        return _channels[chn]->polarity < 0.0f;
 }
 
 
 bool 
 MonitorProcessor::cut (uint32_t chn) const
 {
-        return _channels[chn].cut == 0.0f;
+        return _channels[chn]->cut == 0.0f;
 }
 
 bool 
 MonitorProcessor::dimmed (uint32_t chn) const
 {
-        return _channels[chn].dim;
+        return _channels[chn]->dim;
 }
 
 bool
@@ -434,3 +474,39 @@ MonitorProcessor::cut_all () const
 {
         return _cut_all;
 }
+
+boost::shared_ptr<Controllable>
+MonitorProcessor::channel_cut_control (uint32_t chn) const
+{
+        if (chn < _channels.size()) {
+                return _channels[chn]->cut_control;
+        }
+        return boost::shared_ptr<Controllable>();
+}
+
+boost::shared_ptr<Controllable>
+MonitorProcessor::channel_dim_control (uint32_t chn) const
+{
+        if (chn < _channels.size()) {
+                return _channels[chn]->dim_control;
+        }
+        return boost::shared_ptr<Controllable>();
+}
+
+boost::shared_ptr<Controllable>
+MonitorProcessor::channel_polarity_control (uint32_t chn) const
+{
+        if (chn < _channels.size()) {
+                return _channels[chn]->polarity_control;
+        }
+        return boost::shared_ptr<Controllable>();
+}
+
+boost::shared_ptr<Controllable>
+MonitorProcessor::channel_solo_control (uint32_t chn) const
+{
+        if (chn < _channels.size()) {
+                return _channels[chn]->soloed_control;
+        }
+        return boost::shared_ptr<Controllable>();
+}
index ad04552820b840527aaee503137002ae5bff8732..22d2fc8037e7da1cb844ebe0ceb56e6772f181a4 100644 (file)
@@ -2245,7 +2245,7 @@ Session::route_solo_changed (void* /*src*/, boost::weak_ptr<Route> wpr)
                delta = -1;
        }
 
-       /* now mod the solo level of all other routes except master & control outs
+       /* now mod the solo level of all other routes except master/control outs/auditioner
           so that they will be silent if appropriate.
        */
 
@@ -2269,12 +2269,18 @@ Session::route_solo_changed (void* /*src*/, boost::weak_ptr<Route> wpr)
                _master_out->mod_solo_by_others (1);
        }
  
-       /* ditto for control outs make sure master is never muted by solo */
+       /* ditto for control outs make sure it is never muted by solo */
 
        if (_monitor_out && route != _monitor_out && _monitor_out && _monitor_out->soloed_by_others() == 0) {
                _monitor_out->mod_solo_by_others (1);
        }
 
+       /* ditto for auditioner make sure it is never muted by solo */
+
+       if (auditioner) {
+               auditioner->mod_solo_by_others (1);
+       }
+
        solo_update_disabled = false;
        update_route_solo_state (r);
        SoloChanged (); /* EMIT SIGNAL */
@@ -2300,7 +2306,11 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
                }
 
                 if (!(*i)->is_hidden() && (*i)->listening()) {
-                        listeners++;
+                        if (Config->get_solo_control_is_listen_control()) {
+                                listeners++;
+                        } else {
+                                (*i)->set_listen (false, this);
+                        }
                 }
        }
 
index 3c3cad6e46ddd04def011484da23686568cfbe25..5c0cc3c958b6fc22a76c563b73c3b9d43eca049d 100644 (file)
 */
 
 #include <string>
-#include <climits>
 #include <iostream>
 
-#include <midi++/controllable.h>
+#include "pbd/controllable.h"
 
-#include <gtkmm2ext/gtk_ui.h>
-#include <gtkmm2ext/bindable_button.h>
+#include "gtkmm2ext/gtk_ui.h"
+#include "gtkmm2ext/bindable_button.h"
+#include "gtkmm2ext/gui_thread.h"
 
 #include "i18n.h"
 
 using namespace Gtkmm2ext;
 using namespace std;
-
-BindableToggleButton::BindableToggleButton (MIDI::Controllable *mc)
-       : prompter (Gtk::WIN_POS_MOUSE, 30000, false),
-         midi_control (mc),
-         bind_button (2),
-         bind_statemask (Gdk::CONTROL_MASK)
-
-{                        
-       init_events ();
-}
-
-BindableToggleButton::BindableToggleButton(MIDI::Controllable *mc, const string &label)
-       : StatefulButton (label),
-         prompter (Gtk::WIN_POS_MOUSE, 30000, false),
-         midi_control (mc),
-         bind_button (2),
-         bind_statemask (Gdk::CONTROL_MASK)
-{                        
-       init_events ();
-}
-
+using namespace PBD;
 
 void
-BindableToggleButton::init_events ()
+BindableToggleButton::set_controllable (boost::shared_ptr<PBD::Controllable> c)
 {
-       prompter.signal_unmap_event().connect (mem_fun (*this, &BindableToggleButton::prompter_hiding));
-       
-       prompting = false;
-       unprompting = false;
-       
-       if (midi_control) {
-               midi_control->learning_started.connect (mem_fun (*this, &BindableToggleButton::midicontrol_prompt));
-               midi_control->learning_stopped.connect (mem_fun (*this, &BindableToggleButton::midicontrol_unprompt));
-       }
+        watch_connection.disconnect ();
+        binding_proxy.set_controllable (c);
 }
 
 void
-BindableToggleButton::set_bind_button_state (guint button, guint statemask)
+BindableToggleButton::watch ()
 {
-       bind_button = button;
-       bind_statemask = statemask;
-}
+        boost::shared_ptr<Controllable> c (binding_proxy.get_controllable ());
 
-void
-BindableToggleButton::get_bind_button_state (guint &button, guint &statemask)
-{
-       button = bind_button;
-       statemask = bind_statemask;
-}
+        if (!c) {
+                warning << _("button cannot watch state of non-existing Controllable\n") << endl;
+                return;
+        }
 
-void
-BindableToggleButton::midi_learn()
-{
-       if (midi_control) {
-               prompting = true;
-               midi_control->learn_about_external_control ();
-       }
-}
-
-bool
-BindableToggleButton::prompter_hiding (GdkEventAny *ev)
-{
-       if (unprompting) {
-               if (midi_control) {
-                       midi_control->stop_learning();
-               }
-               unprompting = false;
-       }
-       
-       return false;
-}
-
-
-void
-BindableToggleButton::midicontrol_set_tip ()
-
-{
-       if (midi_control) {
-               // Gtkmm2ext::UI::instance()->set_tip (evbox, midi_control->control_description());
-       }
-}
-
-void
-BindableToggleButton::midicontrol_prompt ()
-
-{
-       if (prompting) {
-               string prompt = _("operate MIDI controller now");
-               prompter.set_text (prompt);
-               Gtkmm2ext::UI::instance()->touch_display (&prompter);
-               
-               unprompting = true;
-               prompting = false;
-       }
+        c->Changed.connect (watch_connection, invalidator(*this), boost::bind (&BindableToggleButton::controllable_changed, this), gui_context());
 }
 
 void
-BindableToggleButton::midicontrol_unprompt ()
-
+BindableToggleButton::controllable_changed ()
 {
-       if (unprompting) {
-               Gtkmm2ext::UI::instance()->touch_display (&prompter);
-               unprompting = false;
-       }
+        float val = binding_proxy.get_controllable()->get_value();
+        set_active (fabs (val) >= 0.5f);
 }
-
-
index 18e3ceb339d268b3f9811e72f2a0eacb3988fba3..5ac81d850703fbff36b1913760c0011815f0387d 100644 (file)
@@ -22,7 +22,8 @@
 
 #include <string>
 
-#include <gtkmm2ext/stateful_button.h>
+#include "pbd/signals.h"
+#include "gtkmm2ext/stateful_button.h"
 #include "binding_proxy.h"
 
 namespace PBD {
@@ -48,8 +49,13 @@ class BindableToggleButton : public Gtkmm2ext::StatefulToggleButton
        }
        
        boost::shared_ptr<PBD::Controllable> get_controllable() { return binding_proxy.get_controllable(); }
-       void set_controllable (boost::shared_ptr<PBD::Controllable> c) { binding_proxy.set_controllable (c); }
+       void set_controllable (boost::shared_ptr<PBD::Controllable> c);
+        void watch ();
+
+  protected:
+        void controllable_changed ();
+        PBD::ScopedConnection watch_connection;
+
   private:
        BindingProxy binding_proxy;
 };
diff --git a/libs/gtkmm2ext/gtkmm2ext/gui_thread.h b/libs/gtkmm2ext/gtkmm2ext/gui_thread.h
new file mode 100644 (file)
index 0000000..16dc060
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+    Copyright (C) 2000-2007 Paul Davis
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __ardour_gtk_gui_thread_h__
+#define __ardour_gtk_gui_thread_h__
+
+#include <cstdlib>
+#include <gtkmm2ext/gtk_ui.h>
+#include <boost/bind.hpp>
+#include <boost/bind/protect.hpp>
+
+namespace sigc {
+        class trackable;
+}
+
+#define ENSURE_GUI_THREAD(obj,method, ...) if (!Gtkmm2ext::UI::instance()->caller_is_self()) { abort (); } 
+
+#define gui_context() Gtkmm2ext::UI::instance() /* a UICallback-derived object that specifies the event loop for GUI signal handling */
+#define ui_bind(f, ...) boost::protect (boost::bind (f, __VA_ARGS__))
+
+extern PBD::EventLoop::InvalidationRecord* __invalidator (sigc::trackable& trackable, const char*, int);
+#define invalidator(x) __invalidator ((x), __FILE__, __LINE__)
+
+#endif /* __ardour_gtk_gui_thread_h__ */
index a3a06d39d33b85c2159d1be4af686df1a5e8bb2f..cb256bed22641886f97c3a2a746968ec08400c3b 100644 (file)
@@ -24,6 +24,8 @@
 #include <gtkmm/box.h>
 #include <gtkmm/eventbox.h>
 
+#include "gtkmm2ext/binding_proxy.h"
+
 namespace Gtk {
        class Adjustment;
        class SpinButton;
@@ -59,6 +61,8 @@ class MotionFeedback : public Gtk::VBox
        gfloat upper () { return _upper; }
        gfloat range () { return _range; }
 
+       void set_controllable (boost::shared_ptr<PBD::Controllable> c) { binding_proxy.set_controllable (c); }
+
  protected:
        gfloat _range;
        gfloat _lower;
@@ -84,6 +88,7 @@ class MotionFeedback : public Gtk::VBox
        Gtk::SpinButton*   value;
        Gtk::Adjustment*   adjustment;
        Glib::RefPtr<Gdk::Pixbuf> pixbuf;
+        BindingProxy       binding_proxy;
 
         double default_value;
        double  step_inc;
index 19ce16d8158a9b2b698995fa040ef5689d82b92f..06aa2e507e557ec86d098e3ff7706c77c7044dcd 100644 (file)
@@ -138,6 +138,10 @@ MotionFeedback::set_adjustment (Adjustment *adj)
 bool
 MotionFeedback::pixwin_button_press_event (GdkEventButton *ev) 
 { 
+        if (binding_proxy.button_press_handler (ev)) {
+                return true;
+       }
+
        switch (ev->button) {
        case 2:
                return FALSE;  /* XXX why ? */
index dec5c94c5db45622d091eabce8de3451c738ec02..1db7016a244480a0aad94280645e7a1a32b2b9e8 100644 (file)
@@ -23,6 +23,7 @@ gtkmm2ext_sources = [
        'auto_spin.cc',
        'barcontroller.cc',
        'binding_proxy.cc',
+       'bindable_button.cc',
        'cell_renderer_pixbuf_multi.cc',
        'cell_renderer_pixbuf_toggle.cc',
        'choice.cc',
index f8d8f828559038fa568b04834055cb4892c47d56..9f5ed5251b125294f203770c8bced207eca65398 100644 (file)
@@ -72,6 +72,9 @@ class Controllable : public PBD::StatefulDestructible {
        bool is_gain_like() const { return _flags & GainLike; }
        bool is_integral_only() const { return _flags & IntegerOnly; }
 
+        virtual float lower() const { return 0.0f; }
+        virtual float upper() const { return 1.0f; }
+
        Flag flags() const { return _flags; }
        void set_flags (Flag f);
 
index 6087a857be2d682e86925f16c26ee7189b580e7e..32b6ff8fe17cf9aa4e50259e2da24d426f8b723e 100644 (file)
@@ -21,6 +21,7 @@
 #include <stdint.h>
 #include <cmath>
 #include <climits>
+#include <iostream>
 
 #include "pbd/error.h"
 #include "pbd/controllable_descriptor.h"
@@ -133,18 +134,17 @@ MIDIControllable::stop_learning ()
 }
 
 float
-MIDIControllable::control_to_midi(float val)
+MIDIControllable::control_to_midi (float val)
 {
-       float control_min = 0.0f;
-       float control_max = 1.0f;
-       ARDOUR::AutomationControl* ac = dynamic_cast<ARDOUR::AutomationControl*>(controllable);
-       if (ac) {
-               control_min = ac->parameter().min();
-               control_max = ac->parameter().max();
-       }
+       const float midi_range = 127.0f; // TODO: NRPN etc.
+
+        if (controllable->is_gain_like()) {
+                return gain_to_slider_position (val/midi_range);
+        }
 
+       float control_min = controllable->lower ();
+       float control_max = controllable->upper ();
        const float control_range = control_max - control_min;
-       const float midi_range    = 127.0f; // TODO: NRPN etc.
 
        return (val - control_min) / control_range * midi_range;
 }
@@ -152,23 +152,16 @@ MIDIControllable::control_to_midi(float val)
 float
 MIDIControllable::midi_to_control(float val)
 {
-       float control_min = 0.0f;
-       float control_max = 1.0f;
-       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();
-       }
+        if (controllable->is_gain_like()) {
+                return slider_position_to_gain (val/midi_range);
+        }
 
+       float control_min = controllable->lower ();
+       float control_max = controllable->upper ();
        const float control_range = control_max - control_min;
+
        return  val / midi_range * control_range + control_min;
 }
 
@@ -254,7 +247,6 @@ MIDIControllable::midi_sense_pitchbend (Parser &, pitchbend_t pb)
                return;
        }
 
-
        if (!controllable->is_toggle()) {
                /* XXX gack - get rid of assumption about typeof pitchbend_t */
                controllable->set_value ((pb/(float) SHRT_MAX));