From e69aca28426dd17a0f82ea01c7c98e217b4fdcc3 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 6 Apr 2010 16:57:35 +0000 Subject: [PATCH] MIDI/Controllables for monitor section, and related fixes git-svn-id: svn://localhost/ardour2/branches/3.0@6863 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/ardour_ui2.cc | 2 + gtk2_ardour/gui_thread.h | 41 +---- gtk2_ardour/monitor_section.cc | 90 ++++++---- gtk2_ardour/monitor_section.h | 3 + gtk2_ardour/utils.cc | 39 +++++ gtk2_ardour/utils.h | 8 + libs/ardour/ardour/automation_control.h | 3 + libs/ardour/ardour/monitor_processor.h | 158 +++++++++++++++-- libs/ardour/monitor_processor.cc | 164 +++++++++++++----- libs/ardour/session.cc | 16 +- libs/gtkmm2ext/bindable_button.cc | 115 ++---------- libs/gtkmm2ext/gtkmm2ext/bindable_button.h | 12 +- libs/gtkmm2ext/gtkmm2ext/gui_thread.h | 40 +++++ libs/gtkmm2ext/gtkmm2ext/motionfeedback.h | 5 + libs/gtkmm2ext/motionfeedback.cc | 4 + libs/gtkmm2ext/wscript | 1 + libs/pbd/pbd/controllable.h | 3 + .../surfaces/generic_midi/midicontrollable.cc | 38 ++-- 18 files changed, 487 insertions(+), 255 deletions(-) create mode 100644 libs/gtkmm2ext/gtkmm2ext/gui_thread.h diff --git a/gtk2_ardour/ardour_ui2.cc b/gtk2_ardour/ardour_ui2.cc index 9747685d8a..05b6b5d5bd 100644 --- a/gtk2_ardour/ardour_ui2.cc +++ b/gtk2_ardour/ardour_ui2.cc @@ -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); } diff --git a/gtk2_ardour/gui_thread.h b/gtk2_ardour/gui_thread.h index 16dc0600a1..cd453c996a 100644 --- a/gtk2_ardour/gui_thread.h +++ b/gtk2_ardour/gui_thread.h @@ -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 -#include -#include -#include - -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" diff --git a/gtk2_ardour/monitor_section.cc b/gtk2_ardour/monitor_section.cc index f455ee515c..b9e4c1b8a5 100644 --- a/gtk2_ardour/monitor_section.cc +++ b/gtk2_ardour/monitor_section.cc @@ -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 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 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); + } +} diff --git a/gtk2_ardour/monitor_section.h b/gtk2_ardour/monitor_section.h index 65d460d84d..da20857aea 100644 --- a/gtk2_ardour/monitor_section.h +++ b/gtk2_ardour/monitor_section.h @@ -121,4 +121,7 @@ class MonitorSection : public RouteUI void parameter_changed (std::string); PBD::ScopedConnection config_connection; + PBD::ScopedConnectionList control_connections; + + void assign_controllables (); }; diff --git a/gtk2_ardour/utils.cc b/gtk2_ardour/utils.cc index 0bf7c5657f..aa57a3b773 100644 --- a/gtk2_ardour/utils.cc +++ b/gtk2_ardour/utils.cc @@ -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 wcont) +{ + boost::shared_ptr 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 wcont) +{ + boost::shared_ptr 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 c, Gtk::Adjustment& a) +{ + boost::weak_ptr 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()); +} + diff --git a/gtk2_ardour/utils.h b/gtk2_ardour/utils.h index ca1d751539..bfde7807d5 100644 --- a/gtk2_ardour/utils.h +++ b/gtk2_ardour/utils.h @@ -31,10 +31,16 @@ #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 c, Gtk::Adjustment& a); + #endif /* __ardour_gtk_utils_h__ */ diff --git a/libs/ardour/ardour/automation_control.h b/libs/ardour/ardour/automation_control.h index 15bac5fef8..3c832ae704 100644 --- a/libs/ardour/ardour/automation_control.h +++ b/libs/ardour/ardour/automation_control.h @@ -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; }; diff --git a/libs/ardour/ardour/monitor_processor.h b/libs/ardour/ardour/monitor_processor.h index 18c50e5104..8b8e90573d 100644 --- a/libs/ardour/ardour/monitor_processor.h +++ b/libs/ardour/ardour/monitor_processor.h @@ -20,9 +20,12 @@ #ifndef __ardour_monitor_processor_h__ #define __ardour_monitor_processor_h__ +#include #include #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 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 Changed; + + boost::shared_ptr channel_cut_control (uint32_t) const; + boost::shared_ptr channel_dim_control (uint32_t) const; + boost::shared_ptr channel_polarity_control (uint32_t) const; + boost::shared_ptr channel_solo_control (uint32_t) const; + boost::shared_ptr dim_control () const { return _dim_all_control; } + boost::shared_ptr cut_control () const { return _cut_all_control; } + boost::shared_ptr mono_control () const { return _mono_control; } + boost::shared_ptr dim_level_control () const { return _dim_level_control; } + boost::shared_ptr 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* cut_ptr; + MPControl* dim_ptr; + MPControl* polarity_ptr; + MPControl* soloed_ptr; + + /* shared ptr access and lifetime management, for external users */ + + boost::shared_ptr cut_control; + boost::shared_ptr dim_control; + boost::shared_ptr polarity_control; + boost::shared_ptr soloed_control; + + /* typed controllables for internal use */ + + MPControl& cut; + MPControl& dim; + MPControl& polarity; + MPControl& soloed; + + ChannelRecord (uint32_t chn) : current_gain(1.0) + , cut_ptr (new MPControl (1.0, string_compose (_("cut control %1"), chn), PBD::Controllable::GainLike)) + , dim_ptr (new MPControl (false, string_compose (_("dim control"), chn), PBD::Controllable::Toggle)) + , polarity_ptr (new MPControl (1.0, string_compose (_("polarity control"), chn), PBD::Controllable::Toggle)) + , soloed_ptr (new MPControl (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 _channels; + + std::vector _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* _dim_all_ptr; + MPControl* _cut_all_ptr; + MPControl* _mono_ptr; + MPControl* _dim_level_ptr; + MPControl* _solo_boost_level_ptr; + + /* shared ptr access and lifetime management, for external users */ + + boost::shared_ptr _dim_all_control; + boost::shared_ptr _cut_all_control; + boost::shared_ptr _mono_control; + boost::shared_ptr _dim_level_control; + boost::shared_ptr _solo_boost_level_control; + + /* typed controllables for internal use */ + + MPControl& _dim_all; + MPControl& _cut_all; + MPControl& _mono; + MPControl& _dim_level; + MPControl& _solo_boost_level; void allocate_channels (uint32_t); }; diff --git a/libs/ardour/monitor_processor.cc b/libs/ardour/monitor_processor.cc index 51bd30a389..80ed2b8e5d 100644 --- a/libs/ardour/monitor_processor.cc +++ b/libs/ardour/monitor_processor.cc @@ -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::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 (false, _("monitor dim"), Controllable::Toggle)) + , _cut_all_ptr (new MPControl (false, _("monitor cut"), Controllable::Toggle)) + , _mono_ptr (new MPControl (false, _("monitor mono"), Controllable::Toggle)) + , _dim_level_ptr (new MPControl + (0.2, _("monitor mono"), Controllable::Flag (0), 0.0f, 1.0f)) + , _solo_boost_level_ptr (new MPControl + (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::const_iterator x = _channels.begin(); x != _channels.end(); ++x) { - if (x->soloed) { + for (vector::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::const_iterator x = _channels.begin(); x != _channels.end(); ++x, ++chn) { + for (vector::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 +MonitorProcessor::channel_cut_control (uint32_t chn) const +{ + if (chn < _channels.size()) { + return _channels[chn]->cut_control; + } + return boost::shared_ptr(); +} + +boost::shared_ptr +MonitorProcessor::channel_dim_control (uint32_t chn) const +{ + if (chn < _channels.size()) { + return _channels[chn]->dim_control; + } + return boost::shared_ptr(); +} + +boost::shared_ptr +MonitorProcessor::channel_polarity_control (uint32_t chn) const +{ + if (chn < _channels.size()) { + return _channels[chn]->polarity_control; + } + return boost::shared_ptr(); +} + +boost::shared_ptr +MonitorProcessor::channel_solo_control (uint32_t chn) const +{ + if (chn < _channels.size()) { + return _channels[chn]->soloed_control; + } + return boost::shared_ptr(); +} diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index ad04552820..22d2fc8037 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -2245,7 +2245,7 @@ Session::route_solo_changed (void* /*src*/, boost::weak_ptr 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 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 r) } if (!(*i)->is_hidden() && (*i)->listening()) { - listeners++; + if (Config->get_solo_control_is_listen_control()) { + listeners++; + } else { + (*i)->set_listen (false, this); + } } } diff --git a/libs/gtkmm2ext/bindable_button.cc b/libs/gtkmm2ext/bindable_button.cc index 3c3cad6e46..5c0cc3c958 100644 --- a/libs/gtkmm2ext/bindable_button.cc +++ b/libs/gtkmm2ext/bindable_button.cc @@ -18,122 +18,43 @@ */ #include -#include #include -#include +#include "pbd/controllable.h" -#include -#include +#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 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 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); } - - diff --git a/libs/gtkmm2ext/gtkmm2ext/bindable_button.h b/libs/gtkmm2ext/gtkmm2ext/bindable_button.h index 18e3ceb339..5ac81d8507 100644 --- a/libs/gtkmm2ext/gtkmm2ext/bindable_button.h +++ b/libs/gtkmm2ext/gtkmm2ext/bindable_button.h @@ -22,7 +22,8 @@ #include -#include +#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 get_controllable() { return binding_proxy.get_controllable(); } - void set_controllable (boost::shared_ptr c) { binding_proxy.set_controllable (c); } - + void set_controllable (boost::shared_ptr 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 index 0000000000..16dc0600a1 --- /dev/null +++ b/libs/gtkmm2ext/gtkmm2ext/gui_thread.h @@ -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 +#include +#include +#include + +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__ */ diff --git a/libs/gtkmm2ext/gtkmm2ext/motionfeedback.h b/libs/gtkmm2ext/gtkmm2ext/motionfeedback.h index a3a06d39d3..cb256bed22 100644 --- a/libs/gtkmm2ext/gtkmm2ext/motionfeedback.h +++ b/libs/gtkmm2ext/gtkmm2ext/motionfeedback.h @@ -24,6 +24,8 @@ #include #include +#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 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 pixbuf; + BindingProxy binding_proxy; double default_value; double step_inc; diff --git a/libs/gtkmm2ext/motionfeedback.cc b/libs/gtkmm2ext/motionfeedback.cc index 19ce16d815..06aa2e507e 100644 --- a/libs/gtkmm2ext/motionfeedback.cc +++ b/libs/gtkmm2ext/motionfeedback.cc @@ -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 ? */ diff --git a/libs/gtkmm2ext/wscript b/libs/gtkmm2ext/wscript index dec5c94c5d..1db7016a24 100644 --- a/libs/gtkmm2ext/wscript +++ b/libs/gtkmm2ext/wscript @@ -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', diff --git a/libs/pbd/pbd/controllable.h b/libs/pbd/pbd/controllable.h index f8d8f82855..9f5ed5251b 100644 --- a/libs/pbd/pbd/controllable.h +++ b/libs/pbd/pbd/controllable.h @@ -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); diff --git a/libs/surfaces/generic_midi/midicontrollable.cc b/libs/surfaces/generic_midi/midicontrollable.cc index 6087a857be..32b6ff8fe1 100644 --- a/libs/surfaces/generic_midi/midicontrollable.cc +++ b/libs/surfaces/generic_midi/midicontrollable.cc @@ -21,6 +21,7 @@ #include #include #include +#include #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(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(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)); -- 2.30.2