summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2010-04-06 16:57:35 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2010-04-06 16:57:35 +0000
commite69aca28426dd17a0f82ea01c7c98e217b4fdcc3 (patch)
tree32854686f9de3cd61f07a5e801886b0f2d5a7012
parent3a7487d3fa6887c846bc02d2764e376f7f209a03 (diff)
MIDI/Controllables for monitor section, and related fixes
git-svn-id: svn://localhost/ardour2/branches/3.0@6863 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r--gtk2_ardour/ardour_ui2.cc2
-rw-r--r--gtk2_ardour/gui_thread.h41
-rw-r--r--gtk2_ardour/monitor_section.cc90
-rw-r--r--gtk2_ardour/monitor_section.h3
-rw-r--r--gtk2_ardour/utils.cc39
-rw-r--r--gtk2_ardour/utils.h8
-rw-r--r--libs/ardour/ardour/automation_control.h3
-rw-r--r--libs/ardour/ardour/monitor_processor.h158
-rw-r--r--libs/ardour/monitor_processor.cc164
-rw-r--r--libs/ardour/session.cc16
-rw-r--r--libs/gtkmm2ext/bindable_button.cc115
-rw-r--r--libs/gtkmm2ext/gtkmm2ext/bindable_button.h12
-rw-r--r--libs/gtkmm2ext/gtkmm2ext/gui_thread.h40
-rw-r--r--libs/gtkmm2ext/gtkmm2ext/motionfeedback.h5
-rw-r--r--libs/gtkmm2ext/motionfeedback.cc4
-rw-r--r--libs/gtkmm2ext/wscript1
-rw-r--r--libs/pbd/pbd/controllable.h3
-rw-r--r--libs/surfaces/generic_midi/midicontrollable.cc38
18 files changed, 487 insertions, 255 deletions
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 <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"
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<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);
+ }
+}
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<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());
+}
+
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<PBD::Controllable> 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 <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);
};
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<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>();
+}
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<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);
+ }
}
}
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 <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);
}
-
-
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 <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
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 <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__ */
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 <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;
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 <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));