mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-07 23:35:03 +01:00
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:
parent
3a7487d3fa
commit
e69aca2842
18 changed files with 489 additions and 257 deletions
|
|
@ -468,6 +468,8 @@ ARDOUR_UI::reattach_tearoff (Box* b, Widget* w, int32_t n)
|
||||||
void
|
void
|
||||||
ARDOUR_UI::soloing_changed (bool onoff)
|
ARDOUR_UI::soloing_changed (bool onoff)
|
||||||
{
|
{
|
||||||
|
cerr << "solo change, " << onoff << endl;
|
||||||
|
|
||||||
if (solo_alert_button.get_active() != onoff) {
|
if (solo_alert_button.get_active() != onoff) {
|
||||||
solo_alert_button.set_active (onoff);
|
solo_alert_button.set_active (onoff);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1 @@
|
||||||
/*
|
#include "gtkmm2ext/gui_thread.h"
|
||||||
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__ */
|
|
||||||
|
|
|
||||||
|
|
@ -38,11 +38,11 @@ MonitorSection::MonitorSection (Session* s)
|
||||||
, _tearoff (0)
|
, _tearoff (0)
|
||||||
, gain_adjustment (0.781787, 0.0, 1.0, 0.01, 0.1) // initial value is unity gain
|
, gain_adjustment (0.781787, 0.0, 1.0, 0.01, 0.1) // initial value is unity gain
|
||||||
, gain_control (0)
|
, 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)
|
, 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_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_cut_control (0)
|
||||||
, solo_in_place_button (solo_model_group, _("SiP"))
|
, solo_in_place_button (solo_model_group, _("SiP"))
|
||||||
, afl_button (solo_model_group, _("AFL"))
|
, afl_button (solo_model_group, _("AFL"))
|
||||||
|
|
@ -64,14 +64,13 @@ MonitorSection::MonitorSection (Session* s)
|
||||||
}
|
}
|
||||||
|
|
||||||
set_session (s);
|
set_session (s);
|
||||||
|
|
||||||
VBox* spin_packer;
|
VBox* spin_packer;
|
||||||
Label* spin_label;
|
Label* spin_label;
|
||||||
|
|
||||||
/* Dim */
|
/* Dim */
|
||||||
|
|
||||||
dim_control = new VolumeController (little_knob_pixbuf, &dim_adjustment, false, 30, 30);
|
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);
|
HBox* dim_packer = manage (new HBox);
|
||||||
dim_packer->show ();
|
dim_packer->show ();
|
||||||
|
|
@ -125,7 +124,6 @@ MonitorSection::MonitorSection (Session* s)
|
||||||
/* Solo Boost */
|
/* Solo Boost */
|
||||||
|
|
||||||
solo_boost_control = new VolumeController (little_knob_pixbuf, &solo_boost_adjustment, false, 30, 30);
|
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);
|
HBox* solo_packer = manage (new HBox);
|
||||||
solo_packer->set_spacing (12);
|
solo_packer->set_spacing (12);
|
||||||
|
|
@ -143,7 +141,6 @@ MonitorSection::MonitorSection (Session* s)
|
||||||
/* Solo (SiP) cut */
|
/* Solo (SiP) cut */
|
||||||
|
|
||||||
solo_cut_control = new VolumeController (little_knob_pixbuf, &solo_cut_adjustment, false, 30, 30);
|
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_label = manage (new Label (_("SiP Cut")));
|
||||||
spin_packer = manage (new VBox);
|
spin_packer = manage (new VBox);
|
||||||
|
|
@ -197,7 +194,6 @@ MonitorSection::MonitorSection (Session* s)
|
||||||
/* Gain */
|
/* Gain */
|
||||||
|
|
||||||
gain_control = new VolumeController (big_knob_pixbuf, &gain_adjustment, false, 80, 80);
|
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_label = manage (new Label (_("Gain")));
|
||||||
spin_packer = manage (new VBox);
|
spin_packer = manage (new VBox);
|
||||||
|
|
@ -231,6 +227,7 @@ MonitorSection::MonitorSection (Session* s)
|
||||||
|
|
||||||
populate_buttons ();
|
populate_buttons ();
|
||||||
map_state ();
|
map_state ();
|
||||||
|
assign_controllables ();
|
||||||
|
|
||||||
_tearoff = new TearOff (hpacker);
|
_tearoff = new TearOff (hpacker);
|
||||||
|
|
||||||
|
|
@ -270,16 +267,19 @@ MonitorSection::set_session (Session* s)
|
||||||
if (_route) {
|
if (_route) {
|
||||||
/* session with control outs */
|
/* session with control outs */
|
||||||
_monitor = _route->monitor_control ();
|
_monitor = _route->monitor_control ();
|
||||||
|
assign_controllables ();
|
||||||
} else {
|
} else {
|
||||||
/* session with no control outs */
|
/* session with no control outs */
|
||||||
_monitor.reset ();
|
_monitor.reset ();
|
||||||
_route.reset ();
|
_route.reset ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
/* no session */
|
/* no session */
|
||||||
_monitor.reset ();
|
_monitor.reset ();
|
||||||
_route.reset ();
|
_route.reset ();
|
||||||
|
control_connections.drop_connections ();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* both might be null */
|
/* 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
|
bool
|
||||||
MonitorSection::nonlinear_gain_printer (SpinButton* button)
|
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()));
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -121,4 +121,7 @@ class MonitorSection : public RouteUI
|
||||||
void parameter_changed (std::string);
|
void parameter_changed (std::string);
|
||||||
|
|
||||||
PBD::ScopedConnection config_connection;
|
PBD::ScopedConnection config_connection;
|
||||||
|
PBD::ScopedConnectionList control_connections;
|
||||||
|
|
||||||
|
void assign_controllables ();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@
|
||||||
#include "i18n.h"
|
#include "i18n.h"
|
||||||
#include "rgb_macros.h"
|
#include "rgb_macros.h"
|
||||||
#include "canvas_impl.h"
|
#include "canvas_impl.h"
|
||||||
|
#include "gui_thread.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace Gtk;
|
using namespace Gtk;
|
||||||
|
|
@ -1040,3 +1041,41 @@ escape_underscores (string const & s)
|
||||||
|
|
||||||
return o;
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,16 @@
|
||||||
|
|
||||||
#include "canvas.h"
|
#include "canvas.h"
|
||||||
|
|
||||||
|
namespace PBD {
|
||||||
|
class Controllable;
|
||||||
|
class ScopedConnectionList;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Gtk {
|
namespace Gtk {
|
||||||
class Window;
|
class Window;
|
||||||
class ComboBoxText;
|
class ComboBoxText;
|
||||||
class Paned;
|
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);
|
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 &);
|
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__ */
|
#endif /* __ardour_gtk_utils_h__ */
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,9 @@ public:
|
||||||
*/
|
*/
|
||||||
float get_value() const;
|
float get_value() const;
|
||||||
|
|
||||||
|
float lower() const { return parameter().min(); }
|
||||||
|
float upper() const { return parameter().max(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ARDOUR::Session& _session;
|
ARDOUR::Session& _session;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,12 @@
|
||||||
#ifndef __ardour_monitor_processor_h__
|
#ifndef __ardour_monitor_processor_h__
|
||||||
#define __ardour_monitor_processor_h__
|
#define __ardour_monitor_processor_h__
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "pbd/signals.h"
|
#include "pbd/signals.h"
|
||||||
|
#include "pbd/compose.h"
|
||||||
|
#include "pbd/controllable.h"
|
||||||
|
|
||||||
#include "ardour/types.h"
|
#include "ardour/types.h"
|
||||||
#include "ardour/processor.h"
|
#include "ardour/processor.h"
|
||||||
|
|
@ -33,10 +36,77 @@ namespace ARDOUR {
|
||||||
|
|
||||||
class Session;
|
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
|
class MonitorProcessor : public Processor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MonitorProcessor (Session&);
|
MonitorProcessor (Session&);
|
||||||
|
~MonitorProcessor ();
|
||||||
|
|
||||||
bool display_to_user() const;
|
bool display_to_user() const;
|
||||||
|
|
||||||
|
|
@ -71,27 +141,89 @@ class MonitorProcessor : public Processor
|
||||||
bool mono () const;
|
bool mono () const;
|
||||||
|
|
||||||
PBD::Signal0<void> Changed;
|
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:
|
private:
|
||||||
struct ChannelRecord {
|
struct ChannelRecord {
|
||||||
gain_t current_gain;
|
gain_t current_gain;
|
||||||
gain_t cut;
|
|
||||||
bool dim;
|
|
||||||
gain_t polarity;
|
|
||||||
bool soloed;
|
|
||||||
|
|
||||||
ChannelRecord ()
|
/* pointers - created first, but managed by boost::shared_ptr<> */
|
||||||
: current_gain(1.0), cut(1.0), dim(false), polarity(1.0), soloed (false) {}
|
|
||||||
|
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;
|
uint32_t solo_cnt;
|
||||||
bool _dim_all;
|
|
||||||
bool _cut_all;
|
/* pointers - created first, but managed by boost::shared_ptr<> */
|
||||||
bool _mono;
|
|
||||||
volatile gain_t _dim_level;
|
MPControl<bool>* _dim_all_ptr;
|
||||||
volatile gain_t _solo_boost_level;
|
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);
|
void allocate_channels (uint32_t);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -15,30 +15,68 @@ using namespace ARDOUR;
|
||||||
using namespace PBD;
|
using namespace PBD;
|
||||||
using namespace std;
|
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)
|
MonitorProcessor::MonitorProcessor (Session& s)
|
||||||
: Processor (s, X_("MonitorOut"))
|
: 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;
|
MonitorProcessor::~MonitorProcessor ()
|
||||||
_dim_level = 0.2;
|
{
|
||||||
_solo_boost_level = 1.0;
|
allocate_channels (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MonitorProcessor::allocate_channels (uint32_t size)
|
MonitorProcessor::allocate_channels (uint32_t size)
|
||||||
{
|
{
|
||||||
while (_channels.size() > size) {
|
while (_channels.size() > size) {
|
||||||
if (_channels.back().soloed) {
|
if (_channels.back()->soloed) {
|
||||||
if (solo_cnt > 0) {
|
if (solo_cnt > 0) {
|
||||||
--solo_cnt;
|
--solo_cnt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ChannelRecord* cr = _channels.back();
|
||||||
_channels.pop_back();
|
_channels.pop_back();
|
||||||
|
delete cr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t n = _channels.size() + 1;
|
||||||
|
|
||||||
while (_channels.size() < size) {
|
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()));
|
allocate_channels (atoi (prop->value()));
|
||||||
|
|
||||||
if ((prop = node.property (X_("dim-level"))) != 0) {
|
if ((prop = node.property (X_("dim-level"))) != 0) {
|
||||||
double val = atof (prop->value());
|
gain_t val = atof (prop->value());
|
||||||
_dim_level = val;
|
_dim_level = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((prop = node.property (X_("solo-boost-level"))) != 0) {
|
if ((prop = node.property (X_("solo-boost-level"))) != 0) {
|
||||||
double val = atof (prop->value());
|
gain_t val = atof (prop->value());
|
||||||
_solo_boost_level = val;
|
_solo_boost_level = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,7 +156,7 @@ MonitorProcessor::set_state (const XMLNode& node, int version)
|
||||||
<< endmsg;
|
<< endmsg;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
ChannelRecord& cr (_channels[chn]);
|
ChannelRecord& cr (*_channels[chn]);
|
||||||
|
|
||||||
if ((prop = (*i)->property ("cut")) != 0) {
|
if ((prop = (*i)->property ("cut")) != 0) {
|
||||||
if (string_is_affirmative (prop->value())){
|
if (string_is_affirmative (prop->value())){
|
||||||
|
|
@ -152,8 +190,8 @@ MonitorProcessor::set_state (const XMLNode& node, int version)
|
||||||
|
|
||||||
solo_cnt = 0;
|
solo_cnt = 0;
|
||||||
|
|
||||||
for (vector<ChannelRecord>::const_iterator x = _channels.begin(); x != _channels.end(); ++x) {
|
for (vector<ChannelRecord*>::const_iterator x = _channels.begin(); x != _channels.end(); ++x) {
|
||||||
if (x->soloed) {
|
if ((*x)->soloed) {
|
||||||
solo_cnt++;
|
solo_cnt++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -171,10 +209,10 @@ MonitorProcessor::state (bool full)
|
||||||
|
|
||||||
node.add_property (X_("type"), X_("monitor"));
|
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);
|
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_("solo-boost-level"), buf);
|
||||||
|
|
||||||
node.add_property (X_("cut-all"), (_cut_all ? "yes" : "no"));
|
node.add_property (X_("cut-all"), (_cut_all ? "yes" : "no"));
|
||||||
|
|
@ -189,16 +227,16 @@ MonitorProcessor::state (bool full)
|
||||||
XMLNode* chn_node;
|
XMLNode* chn_node;
|
||||||
uint32_t chn = 0;
|
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"));
|
chn_node = new XMLNode (X_("Channel"));
|
||||||
|
|
||||||
snprintf (buf, sizeof (buf), "%u", chn);
|
snprintf (buf, sizeof (buf), "%u", chn);
|
||||||
chn_node->add_property ("id", buf);
|
chn_node->add_property ("id", buf);
|
||||||
|
|
||||||
chn_node->add_property (X_("cut"), x->cut == 1.0 ? "no" : "yes");
|
chn_node->add_property (X_("cut"), (*x)->cut == 1.0f ? "no" : "yes");
|
||||||
chn_node->add_property (X_("invert"), x->polarity == 1.0 ? "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_("dim"), (*x)->dim ? "yes" : "no");
|
||||||
chn_node->add_property (X_("solo"), x->soloed ? "yes" : "no");
|
chn_node->add_property (X_("solo"), (*x)->soloed ? "yes" : "no");
|
||||||
|
|
||||||
node.add_child_nocopy (*chn_node);
|
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 */
|
/* 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) {
|
if (_channels[chn]->soloed) {
|
||||||
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 {
|
} else {
|
||||||
if (solo_cnt == 0) {
|
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 {
|
} else {
|
||||||
target_gain = 0.0;
|
target_gain = 0.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_TRACE (DEBUG::Monitor,
|
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,
|
chn,
|
||||||
solo_boost,
|
solo_boost,
|
||||||
global_cut,
|
global_cut,
|
||||||
global_dim,
|
global_dim,
|
||||||
_channels[chn].dim,
|
_channels[chn]->dim,
|
||||||
dim_level,
|
dim_level,
|
||||||
_channels[chn].polarity,
|
_channels[chn]->polarity,
|
||||||
_channels[chn].cut,
|
_channels[chn]->cut,
|
||||||
_channels[chn].soloed,
|
_channels[chn]->soloed,
|
||||||
solo_cnt,
|
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);
|
Amp::apply_gain (*b, nframes, _channels[chn]->current_gain, target_gain);
|
||||||
_channels[chn].current_gain = target_gain;
|
_channels[chn]->current_gain = target_gain;
|
||||||
}
|
}
|
||||||
|
|
||||||
++chn;
|
++chn;
|
||||||
|
|
@ -317,33 +357,33 @@ void
|
||||||
MonitorProcessor::set_polarity (uint32_t chn, bool invert)
|
MonitorProcessor::set_polarity (uint32_t chn, bool invert)
|
||||||
{
|
{
|
||||||
if (invert) {
|
if (invert) {
|
||||||
_channels[chn].polarity = -1.0f;
|
_channels[chn]->polarity = -1.0f;
|
||||||
} else {
|
} else {
|
||||||
_channels[chn].polarity = 1.0f;
|
_channels[chn]->polarity = 1.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MonitorProcessor::set_dim (uint32_t chn, bool yn)
|
MonitorProcessor::set_dim (uint32_t chn, bool yn)
|
||||||
{
|
{
|
||||||
_channels[chn].dim = yn;
|
_channels[chn]->dim = yn;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MonitorProcessor::set_cut (uint32_t chn, bool yn)
|
MonitorProcessor::set_cut (uint32_t chn, bool yn)
|
||||||
{
|
{
|
||||||
if (yn) {
|
if (yn) {
|
||||||
_channels[chn].cut = 0.0f;
|
_channels[chn]->cut = 0.0f;
|
||||||
} else {
|
} else {
|
||||||
_channels[chn].cut = 1.0f;
|
_channels[chn]->cut = 1.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MonitorProcessor::set_solo (uint32_t chn, bool solo)
|
MonitorProcessor::set_solo (uint32_t chn, bool solo)
|
||||||
{
|
{
|
||||||
if (solo != _channels[chn].soloed) {
|
if (solo != _channels[chn]->soloed) {
|
||||||
_channels[chn].soloed = solo;
|
_channels[chn]->soloed = solo;
|
||||||
|
|
||||||
if (solo) {
|
if (solo) {
|
||||||
solo_cnt++;
|
solo_cnt++;
|
||||||
|
|
@ -394,27 +434,27 @@ MonitorProcessor::set_solo_boost_level (gain_t val)
|
||||||
bool
|
bool
|
||||||
MonitorProcessor::soloed (uint32_t chn) const
|
MonitorProcessor::soloed (uint32_t chn) const
|
||||||
{
|
{
|
||||||
return _channels[chn].soloed;
|
return _channels[chn]->soloed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
MonitorProcessor::inverted (uint32_t chn) const
|
MonitorProcessor::inverted (uint32_t chn) const
|
||||||
{
|
{
|
||||||
return _channels[chn].polarity < 0.0f;
|
return _channels[chn]->polarity < 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
MonitorProcessor::cut (uint32_t chn) const
|
MonitorProcessor::cut (uint32_t chn) const
|
||||||
{
|
{
|
||||||
return _channels[chn].cut == 0.0f;
|
return _channels[chn]->cut == 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
MonitorProcessor::dimmed (uint32_t chn) const
|
MonitorProcessor::dimmed (uint32_t chn) const
|
||||||
{
|
{
|
||||||
return _channels[chn].dim;
|
return _channels[chn]->dim;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
@ -434,3 +474,39 @@ MonitorProcessor::cut_all () const
|
||||||
{
|
{
|
||||||
return _cut_all;
|
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>();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2245,7 +2245,7 @@ Session::route_solo_changed (void* /*src*/, boost::weak_ptr<Route> wpr)
|
||||||
delta = -1;
|
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.
|
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);
|
_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) {
|
if (_monitor_out && route != _monitor_out && _monitor_out && _monitor_out->soloed_by_others() == 0) {
|
||||||
_monitor_out->mod_solo_by_others (1);
|
_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;
|
solo_update_disabled = false;
|
||||||
update_route_solo_state (r);
|
update_route_solo_state (r);
|
||||||
SoloChanged (); /* EMIT SIGNAL */
|
SoloChanged (); /* EMIT SIGNAL */
|
||||||
|
|
@ -2300,7 +2306,11 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(*i)->is_hidden() && (*i)->listening()) {
|
if (!(*i)->is_hidden() && (*i)->listening()) {
|
||||||
listeners++;
|
if (Config->get_solo_control_is_listen_control()) {
|
||||||
|
listeners++;
|
||||||
|
} else {
|
||||||
|
(*i)->set_listen (false, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,122 +18,43 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <climits>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <midi++/controllable.h>
|
#include "pbd/controllable.h"
|
||||||
|
|
||||||
#include <gtkmm2ext/gtk_ui.h>
|
#include "gtkmm2ext/gtk_ui.h"
|
||||||
#include <gtkmm2ext/bindable_button.h>
|
#include "gtkmm2ext/bindable_button.h"
|
||||||
|
#include "gtkmm2ext/gui_thread.h"
|
||||||
|
|
||||||
#include "i18n.h"
|
#include "i18n.h"
|
||||||
|
|
||||||
using namespace Gtkmm2ext;
|
using namespace Gtkmm2ext;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace PBD;
|
||||||
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 ();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
BindableToggleButton::init_events ()
|
BindableToggleButton::set_controllable (boost::shared_ptr<PBD::Controllable> c)
|
||||||
{
|
{
|
||||||
prompter.signal_unmap_event().connect (mem_fun (*this, &BindableToggleButton::prompter_hiding));
|
watch_connection.disconnect ();
|
||||||
|
binding_proxy.set_controllable (c);
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
BindableToggleButton::set_bind_button_state (guint button, guint statemask)
|
BindableToggleButton::watch ()
|
||||||
{
|
{
|
||||||
bind_button = button;
|
boost::shared_ptr<Controllable> c (binding_proxy.get_controllable ());
|
||||||
bind_statemask = statemask;
|
|
||||||
|
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
|
void
|
||||||
BindableToggleButton::get_bind_button_state (guint &button, guint &statemask)
|
BindableToggleButton::controllable_changed ()
|
||||||
{
|
{
|
||||||
button = bind_button;
|
float val = binding_proxy.get_controllable()->get_value();
|
||||||
statemask = bind_statemask;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,8 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <gtkmm2ext/stateful_button.h>
|
#include "pbd/signals.h"
|
||||||
|
#include "gtkmm2ext/stateful_button.h"
|
||||||
#include "binding_proxy.h"
|
#include "binding_proxy.h"
|
||||||
|
|
||||||
namespace PBD {
|
namespace PBD {
|
||||||
|
|
@ -48,8 +49,13 @@ class BindableToggleButton : public Gtkmm2ext::StatefulToggleButton
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::shared_ptr<PBD::Controllable> get_controllable() { return binding_proxy.get_controllable(); }
|
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:
|
private:
|
||||||
BindingProxy binding_proxy;
|
BindingProxy binding_proxy;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
40
libs/gtkmm2ext/gtkmm2ext/gui_thread.h
Normal file
40
libs/gtkmm2ext/gtkmm2ext/gui_thread.h
Normal 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__ */
|
||||||
|
|
@ -24,6 +24,8 @@
|
||||||
#include <gtkmm/box.h>
|
#include <gtkmm/box.h>
|
||||||
#include <gtkmm/eventbox.h>
|
#include <gtkmm/eventbox.h>
|
||||||
|
|
||||||
|
#include "gtkmm2ext/binding_proxy.h"
|
||||||
|
|
||||||
namespace Gtk {
|
namespace Gtk {
|
||||||
class Adjustment;
|
class Adjustment;
|
||||||
class SpinButton;
|
class SpinButton;
|
||||||
|
|
@ -59,6 +61,8 @@ class MotionFeedback : public Gtk::VBox
|
||||||
gfloat upper () { return _upper; }
|
gfloat upper () { return _upper; }
|
||||||
gfloat range () { return _range; }
|
gfloat range () { return _range; }
|
||||||
|
|
||||||
|
void set_controllable (boost::shared_ptr<PBD::Controllable> c) { binding_proxy.set_controllable (c); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
gfloat _range;
|
gfloat _range;
|
||||||
gfloat _lower;
|
gfloat _lower;
|
||||||
|
|
@ -84,6 +88,7 @@ class MotionFeedback : public Gtk::VBox
|
||||||
Gtk::SpinButton* value;
|
Gtk::SpinButton* value;
|
||||||
Gtk::Adjustment* adjustment;
|
Gtk::Adjustment* adjustment;
|
||||||
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
|
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
|
||||||
|
BindingProxy binding_proxy;
|
||||||
|
|
||||||
double default_value;
|
double default_value;
|
||||||
double step_inc;
|
double step_inc;
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,10 @@ MotionFeedback::set_adjustment (Adjustment *adj)
|
||||||
bool
|
bool
|
||||||
MotionFeedback::pixwin_button_press_event (GdkEventButton *ev)
|
MotionFeedback::pixwin_button_press_event (GdkEventButton *ev)
|
||||||
{
|
{
|
||||||
|
if (binding_proxy.button_press_handler (ev)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
switch (ev->button) {
|
switch (ev->button) {
|
||||||
case 2:
|
case 2:
|
||||||
return FALSE; /* XXX why ? */
|
return FALSE; /* XXX why ? */
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ gtkmm2ext_sources = [
|
||||||
'auto_spin.cc',
|
'auto_spin.cc',
|
||||||
'barcontroller.cc',
|
'barcontroller.cc',
|
||||||
'binding_proxy.cc',
|
'binding_proxy.cc',
|
||||||
|
'bindable_button.cc',
|
||||||
'cell_renderer_pixbuf_multi.cc',
|
'cell_renderer_pixbuf_multi.cc',
|
||||||
'cell_renderer_pixbuf_toggle.cc',
|
'cell_renderer_pixbuf_toggle.cc',
|
||||||
'choice.cc',
|
'choice.cc',
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,9 @@ class Controllable : public PBD::StatefulDestructible {
|
||||||
bool is_gain_like() const { return _flags & GainLike; }
|
bool is_gain_like() const { return _flags & GainLike; }
|
||||||
bool is_integral_only() const { return _flags & IntegerOnly; }
|
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; }
|
Flag flags() const { return _flags; }
|
||||||
void set_flags (Flag f);
|
void set_flags (Flag f);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
#include "pbd/error.h"
|
#include "pbd/error.h"
|
||||||
#include "pbd/controllable_descriptor.h"
|
#include "pbd/controllable_descriptor.h"
|
||||||
|
|
@ -133,18 +134,17 @@ MIDIControllable::stop_learning ()
|
||||||
}
|
}
|
||||||
|
|
||||||
float
|
float
|
||||||
MIDIControllable::control_to_midi(float val)
|
MIDIControllable::control_to_midi (float val)
|
||||||
{
|
{
|
||||||
float control_min = 0.0f;
|
const float midi_range = 127.0f; // TODO: NRPN etc.
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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 control_range = control_max - control_min;
|
||||||
const float midi_range = 127.0f; // TODO: NRPN etc.
|
|
||||||
|
|
||||||
return (val - control_min) / control_range * midi_range;
|
return (val - control_min) / control_range * midi_range;
|
||||||
}
|
}
|
||||||
|
|
@ -152,23 +152,16 @@ MIDIControllable::control_to_midi(float val)
|
||||||
float
|
float
|
||||||
MIDIControllable::midi_to_control(float val)
|
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.
|
const float midi_range = 127.0f; // TODO: NRPN etc.
|
||||||
|
|
||||||
if (ac) {
|
|
||||||
|
|
||||||
if (ac->is_gain_like()) {
|
if (controllable->is_gain_like()) {
|
||||||
return slider_position_to_gain (val/midi_range);
|
return slider_position_to_gain (val/midi_range);
|
||||||
}
|
}
|
||||||
|
|
||||||
control_min = ac->parameter().min();
|
|
||||||
control_max = ac->parameter().max();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
float control_min = controllable->lower ();
|
||||||
|
float control_max = controllable->upper ();
|
||||||
const float control_range = control_max - control_min;
|
const float control_range = control_max - control_min;
|
||||||
|
|
||||||
return val / midi_range * control_range + control_min;
|
return val / midi_range * control_range + control_min;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -254,7 +247,6 @@ MIDIControllable::midi_sense_pitchbend (Parser &, pitchbend_t pb)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!controllable->is_toggle()) {
|
if (!controllable->is_toggle()) {
|
||||||
/* XXX gack - get rid of assumption about typeof pitchbend_t */
|
/* XXX gack - get rid of assumption about typeof pitchbend_t */
|
||||||
controllable->set_value ((pb/(float) SHRT_MAX));
|
controllable->set_value ((pb/(float) SHRT_MAX));
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue