MIDI/Controllables for monitor section, and related fixes

git-svn-id: svn://localhost/ardour2/branches/3.0@6863 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2010-04-06 16:57:35 +00:00
parent 3a7487d3fa
commit e69aca2842
18 changed files with 489 additions and 257 deletions

View 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);
}

View 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"

View 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);
}
}

View 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 ();
};

View 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());
}

View File

@ -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__ */

View 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;
};

View File

@ -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);
};

View 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>();
}

View 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);
}
}
}

View File

@ -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 ());
if (!c) {
warning << _("button cannot watch state of non-existing Controllable\n") << endl;
return;
}
c->Changed.connect (watch_connection, invalidator(*this), boost::bind (&BindableToggleButton::controllable_changed, this), gui_context());
}
void
BindableToggleButton::get_bind_button_state (guint &button, guint &statemask)
BindableToggleButton::controllable_changed ()
{
button = bind_button;
statemask = bind_statemask;
float val = binding_proxy.get_controllable()->get_value();
set_active (fabs (val) >= 0.5f);
}
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;
}
}
void
BindableToggleButton::midicontrol_unprompt ()
{
if (unprompting) {
Gtkmm2ext::UI::instance()->touch_display (&prompter);
unprompting = false;
}
}

View 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;
};

View File

@ -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__ */

View 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;

View 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 ? */

View 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',

View 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);

View 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));