some changes to try to make the monitor section gain controls work as intended, and along the way start to rationalize MotionFeedback/VolumeController classes

git-svn-id: svn://localhost/ardour2/branches/3.0@9746 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2011-06-17 21:47:20 +00:00
parent cb8bc87a54
commit 01e006e46e
11 changed files with 427 additions and 373 deletions

View file

@ -37,13 +37,9 @@ MonitorSection::MonitorSection (Session* s)
, RouteUI (s) , RouteUI (s)
, main_table (2, 3) , main_table (2, 3)
, _tearoff (0) , _tearoff (0)
, 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) // 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) // 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_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"))
@ -74,7 +70,7 @@ MonitorSection::MonitorSection (Session* s)
/* Dim */ /* Dim */
dim_control = new VolumeController (little_knob_pixbuf, &dim_adjustment, false, 30, 30); dim_control = new VolumeController (little_knob_pixbuf, boost::shared_ptr<Controllable>(), 0.0, 0.01, 0.1, true, 30, 30, true, true);
HBox* dim_packer = manage (new HBox); HBox* dim_packer = manage (new HBox);
dim_packer->show (); dim_packer->show ();
@ -87,7 +83,7 @@ MonitorSection::MonitorSection (Session* s)
spin_packer->pack_start (*spin_label, false, false); spin_packer->pack_start (*spin_label, false, false);
dim_packer->set_spacing (12); dim_packer->set_spacing (12);
dim_packer->pack_start (*spin_packer, true, true); dim_packer->pack_start (*spin_packer, true, false);
/* Rude Solo */ /* Rude Solo */
@ -138,7 +134,7 @@ 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, boost::shared_ptr<Controllable>(), 0.0, 0.01, 0.1, true, 30, 30, true, true);
HBox* solo_packer = manage (new HBox); HBox* solo_packer = manage (new HBox);
solo_packer->set_spacing (12); solo_packer->set_spacing (12);
@ -151,11 +147,11 @@ MonitorSection::MonitorSection (Session* s)
spin_packer->pack_start (*solo_boost_control, false, false); spin_packer->pack_start (*solo_boost_control, false, false);
spin_packer->pack_start (*spin_label, false, false); spin_packer->pack_start (*spin_label, false, false);
solo_packer->pack_start (*spin_packer, true, true); solo_packer->pack_start (*spin_packer, false, true);
/* 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, boost::shared_ptr<Controllable>(), 0.0, 0.01, 0.1, true, 30, 30, false, false);
spin_label = manage (new Label (_("SiP Cut"))); spin_label = manage (new Label (_("SiP Cut")));
spin_packer = manage (new VBox); spin_packer = manage (new VBox);
@ -164,7 +160,7 @@ MonitorSection::MonitorSection (Session* s)
spin_packer->pack_start (*solo_cut_control, false, false); spin_packer->pack_start (*solo_cut_control, false, false);
spin_packer->pack_start (*spin_label, false, false); spin_packer->pack_start (*spin_label, false, false);
solo_packer->pack_start (*spin_packer, true, true); solo_packer->pack_start (*spin_packer, false, true);
exclusive_solo_button.set_name (X_("MonitorOptButton")); exclusive_solo_button.set_name (X_("MonitorOptButton"));
ARDOUR_UI::instance()->set_tip (&exclusive_solo_button, _("Exclusive solo means that only 1 solo is active at a time")); ARDOUR_UI::instance()->set_tip (&exclusive_solo_button, _("Exclusive solo means that only 1 solo is active at a time"));
@ -238,7 +234,7 @@ 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, boost::shared_ptr<Controllable>(), 0.781787, 0.01, 0.1, true, 80, 80, false, false);
spin_label = manage (new Label (_("Gain"))); spin_label = manage (new Label (_("Gain")));
spin_packer = manage (new VBox); spin_packer = manage (new VBox);
@ -763,26 +759,6 @@ MonitorSection::setup_knob_images ()
} }
bool
MonitorSection::nonlinear_gain_printer (SpinButton* button)
{
double val = button->get_adjustment()->get_value();
char buf[16];
snprintf (buf, sizeof (buf), "%.1f", accurate_coefficient_to_dB (slider_position_to_gain (val)));
button->set_text (buf);
return true;
}
bool
MonitorSection::linear_gain_printer (SpinButton* button)
{
double val = button->get_adjustment()->get_value();
char buf[16];
snprintf (buf, sizeof (buf), "%.1f", accurate_coefficient_to_dB (val));
button->set_text (buf);
return true;
}
void void
MonitorSection::update_solo_model () MonitorSection::update_solo_model ()
{ {
@ -818,10 +794,6 @@ MonitorSection::map_state ()
return; return;
} }
gain_control->get_adjustment()->set_value (gain_to_slider_position (_route->gain_control()->get_value()));
dim_control->get_adjustment()->set_value (_monitor->dim_level());
solo_boost_control->get_adjustment()->set_value (_monitor->solo_boost_level());
Glib::RefPtr<Action> act; Glib::RefPtr<Action> act;
update_solo_model (); update_solo_model ();
@ -987,8 +959,6 @@ MonitorSection::parameter_changed (std::string name)
if (name == "solo-control-is-listen-control" || if (name == "solo-control-is-listen-control" ||
name == "listen-position") { name == "listen-position") {
update_solo_model (); update_solo_model ();
} else if (name == "solo-mute-gain") {
solo_cut_adjustment.set_value (gain_to_slider_position (Config->get_solo_mute_gain()));
} }
} }
@ -1005,7 +975,6 @@ MonitorSection::assign_controllables ()
if (_session) { if (_session) {
boost::shared_ptr<Controllable> c = _session->solo_cut_control(); boost::shared_ptr<Controllable> c = _session->solo_cut_control();
solo_cut_control->set_controllable (c); solo_cut_control->set_controllable (c);
solo_cut_control->get_adjustment()->set_value (gain_to_slider_position (c->get_value()));
} else { } else {
solo_cut_control->set_controllable (none); solo_cut_control->set_controllable (none);
} }
@ -1025,16 +994,8 @@ MonitorSection::assign_controllables ()
mono_button.set_controllable (_monitor->mono_control()); mono_button.set_controllable (_monitor->mono_control());
mono_button.watch (); mono_button.watch ();
boost::shared_ptr<Controllable> c (_monitor->dim_level_control ()); dim_control->set_controllable (_monitor->dim_level_control ());
solo_boost_control->set_controllable (_monitor->solo_boost_control ());
dim_control->set_controllable (c);
dim_adjustment.set_lower (c->lower());
dim_adjustment.set_upper (c->upper());
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());
} else { } else {

View file

@ -64,13 +64,9 @@ class MonitorSection : public RouteUI
typedef std::vector<ChannelButtonSet*> ChannelButtons; typedef std::vector<ChannelButtonSet*> ChannelButtons;
ChannelButtons _channel_buttons; ChannelButtons _channel_buttons;
Gtk::Adjustment gain_adjustment;
VolumeController* gain_control; VolumeController* gain_control;
Gtk::Adjustment dim_adjustment;
VolumeController* dim_control; VolumeController* dim_control;
Gtk::Adjustment solo_boost_adjustment;
VolumeController* solo_boost_control; VolumeController* solo_boost_control;
Gtk::Adjustment solo_cut_adjustment;
VolumeController* solo_cut_control; VolumeController* solo_cut_control;
void populate_buttons (); void populate_buttons ();
@ -99,9 +95,6 @@ class MonitorSection : public RouteUI
void solo_boost_changed (); void solo_boost_changed ();
void gain_value_changed (); void gain_value_changed ();
bool nonlinear_gain_printer (Gtk::SpinButton*);
bool linear_gain_printer (Gtk::SpinButton*);
Gtk::RadioButtonGroup solo_model_group; Gtk::RadioButtonGroup solo_model_group;
Gtk::RadioButton solo_in_place_button; Gtk::RadioButton solo_in_place_button;
Gtk::RadioButton afl_button; Gtk::RadioButton afl_button;

View file

@ -17,12 +17,17 @@
$Id: volume_controller.cc,v 1.4 2000/05/03 15:54:21 pbd Exp $ $Id: volume_controller.cc,v 1.4 2000/05/03 15:54:21 pbd Exp $
*/ */
#include <algorithm>
#include <string.h> #include <string.h>
#include <limits.h> #include <limits.h>
#include "pbd/controllable.h" #include "pbd/controllable.h"
#include "pbd/stacktrace.h"
#include "gtkmm2ext/gui_thread.h" #include "gtkmm2ext/gui_thread.h"
#include "ardour/dB.h"
#include "ardour/utils.h" #include "ardour/utils.h"
#include "volume_controller.h" #include "volume_controller.h"
@ -30,45 +35,99 @@
using namespace Gtk; using namespace Gtk;
VolumeController::VolumeController (Glib::RefPtr<Gdk::Pixbuf> p, VolumeController::VolumeController (Glib::RefPtr<Gdk::Pixbuf> p,
Gtk::Adjustment *adj, boost::shared_ptr<PBD::Controllable> c,
double def,
double step,
double page,
bool with_numeric, bool with_numeric,
int subw, int subh) int subw,
int subh,
bool linear,
bool dB)
: MotionFeedback (p, MotionFeedback::Rotary, "", adj, with_numeric, subw, subh) : MotionFeedback (p, MotionFeedback::Rotary, c, def, step, page, "", with_numeric, subw, subh)
, _linear (linear)
, _controllable_uses_dB (dB)
{ {
get_adjustment()->signal_value_changed().connect (mem_fun (*this,&VolumeController::adjustment_value_changed)); set_print_func (VolumeController::_dB_printer, this);
if (step < 1.0) {
value->set_width_chars (6 + abs ((int) ceil (log10 (step))));
} else {
value->set_width_chars (5); // -NNdB
}
} }
void void
VolumeController::set_controllable (boost::shared_ptr<PBD::Controllable> c) VolumeController::_dB_printer (char buf[32], const boost::shared_ptr<PBD::Controllable>& c, void* arg)
{ {
MotionFeedback::set_controllable (c); VolumeController* vc = reinterpret_cast<VolumeController*>(arg);
vc->dB_printer (buf, c);
controllable_connection.disconnect ();
if (c) {
c->Changed.connect (controllable_connection, MISSING_INVALIDATOR, boost::bind (&VolumeController::controllable_value_changed, this), gui_context());
}
controllable_value_changed ();
} }
void void
VolumeController::controllable_value_changed () VolumeController::dB_printer (char buf[32], const boost::shared_ptr<PBD::Controllable>& c)
{ {
boost::shared_ptr<PBD::Controllable> c = controllable(); if (c) {
if (c) {
get_adjustment()->set_value (gain_to_slider_position (c->get_value ())); if (_linear) {
} /* controllable units are in dB so just show the value */
if (step_inc < 1.0) {
snprintf (buf, 32, "%.2f dB", c->get_value());
} else {
snprintf (buf, 32, "%ld dB", lrint (c->get_value()));
}
} else {
double gain_coefficient;
if (!_controllable_uses_dB) {
gain_coefficient = c->get_value();
} else {
double fract = (c->get_value() - c->lower()) / (c->upper() - c->lower());
gain_coefficient = slider_position_to_gain (fract);
}
if (step_inc < 1.0) {
snprintf (buf, 32, "%.2f dB", accurate_coefficient_to_dB (gain_coefficient));
} else {
snprintf (buf, 32, "%ld dB", lrint (accurate_coefficient_to_dB (gain_coefficient)));
}
}
} else {
snprintf (buf, sizeof (buf), "--");
}
} }
void double
VolumeController::adjustment_value_changed () VolumeController::to_control_value (double display_value)
{ {
boost::shared_ptr<PBD::Controllable> c = controllable(); double v;
if (c) {
c->set_value (slider_position_to_gain (get_adjustment()->get_value())); /* display value is always clamped to 0.0 .. 1.0 */
} display_value = std::max (0.0, std::min (1.0, display_value));
if (_linear) {
v = _controllable->lower() + ((_controllable->upper() - _controllable->lower()) * display_value);
} else {
v = slider_position_to_gain (display_value);
}
return v;
} }
double
VolumeController::to_display_value (double control_value)
{
double v;
if (_linear) {
v = (control_value - _controllable->lower ()) / (_controllable->upper() - _controllable->lower());
} else {
v = gain_to_slider_position (control_value);
}
return v;
}

View file

@ -24,27 +24,33 @@
#include "gtkmm2ext/motionfeedback.h" #include "gtkmm2ext/motionfeedback.h"
// march 2010: this exists as a placeholder to add a controllable, but maybe it will
// end up in MotionFeedback
class VolumeController : public Gtkmm2ext::MotionFeedback class VolumeController : public Gtkmm2ext::MotionFeedback
{ {
public: public:
VolumeController (Glib::RefPtr<Gdk::Pixbuf>, VolumeController (Glib::RefPtr<Gdk::Pixbuf>,
Gtk::Adjustment *adj, boost::shared_ptr<PBD::Controllable>,
double def,
double step,
double page,
bool with_numeric = true, bool with_numeric = true,
int image_width = 40, int image_width = 40,
int image_height = 40); int image_height = 40,
bool linear = true,
bool dB = false);
virtual ~VolumeController () {} virtual ~VolumeController () {}
void set_controllable (boost::shared_ptr<PBD::Controllable> c);
static void _dB_printer (char buf[32], const boost::shared_ptr<PBD::Controllable>& adj, void* arg);
protected:
double to_control_value (double);
double to_display_value (double);
private: private:
Gtk::Adjustment *adjustment; bool _linear;
PBD::ScopedConnection controllable_connection; bool _controllable_uses_dB;
void adjustment_value_changed (); void dB_printer (char buf[32], const boost::shared_ptr<PBD::Controllable>& adj);
void controllable_value_changed ();
}; };
#endif // __gtk_ardour_vol_controller_h__ #endif // __gtk_ardour_vol_controller_h__

View file

@ -34,4 +34,6 @@ static inline float accurate_coefficient_to_dB (float coeff) {
return 20.0f * log10 (coeff); return 20.0f * log10 (coeff);
} }
static const double zero_db_as_fraction = 0.781787;
#endif /* __ardour_dB_h__ */ #endif /* __ardour_dB_h__ */

View file

@ -20,6 +20,7 @@
#ifndef __ardour_monitor_processor_h__ #ifndef __ardour_monitor_processor_h__
#define __ardour_monitor_processor_h__ #define __ardour_monitor_processor_h__
#include <algorithm>
#include <iostream> #include <iostream>
#include <vector> #include <vector>
@ -52,7 +53,7 @@ public:
void set_value (double v) { void set_value (double v) {
T newval = (T) v; T newval = (T) v;
if (newval != _value) { if (newval != _value) {
_value = newval; _value = std::max (_lower, std::min (_upper, newval));
Changed(); /* EMIT SIGNAL */ Changed(); /* EMIT SIGNAL */
} }
} }
@ -68,7 +69,7 @@ public:
MPControl& operator=(const T& v) { MPControl& operator=(const T& v) {
if (v != _value) { if (v != _value) {
_value = v; _value = std::max (_lower, std::min (_upper, v));
Changed (); /* EMIT SIGNAL */ Changed (); /* EMIT SIGNAL */
} }
return *this; return *this;

View file

@ -33,18 +33,18 @@ namespace ARDOUR {
class ProxyControllable : public PBD::Controllable { class ProxyControllable : public PBD::Controllable {
public: public:
ProxyControllable (const std::string& name, PBD::Controllable::Flag flags, ProxyControllable (const std::string& name, PBD::Controllable::Flag flags,
boost::function1<void,double> setter, boost::function1<bool,double> setter,
boost::function0<double> getter) boost::function0<double> getter)
: PBD::Controllable (name, flags) : PBD::Controllable (name, flags)
, _setter (setter) , _setter (setter)
, _getter (getter) , _getter (getter)
{} {}
void set_value (double v) { _setter (v); } void set_value (double v) { if (_setter (v)) { Changed(); /* EMIT SIGNAL */ } }
double get_value () const { return _getter (); } double get_value () const { return _getter (); }
private: private:
boost::function1<void,double> _setter; boost::function1<bool,double> _setter;
boost::function0<double> _getter; boost::function0<double> _getter;
}; };

View file

@ -34,10 +34,10 @@ MonitorProcessor::MonitorProcessor (Session& s)
, _dim_all_ptr (new MPControl<bool> (false, _("monitor dim"), Controllable::Toggle)) , _dim_all_ptr (new MPControl<bool> (false, _("monitor dim"), Controllable::Toggle))
, _cut_all_ptr (new MPControl<bool> (false, _("monitor cut"), Controllable::Toggle)) , _cut_all_ptr (new MPControl<bool> (false, _("monitor cut"), Controllable::Toggle))
, _mono_ptr (new MPControl<bool> (false, _("monitor mono"), Controllable::Toggle)) , _mono_ptr (new MPControl<bool> (false, _("monitor mono"), Controllable::Toggle))
, _dim_level_ptr (new MPControl<volatile gain_t> , _dim_level_ptr (new MPControl<volatile gain_t> /* units in dB */
(0.2, _("monitor mono"), Controllable::Flag (0), 0.0f, 1.0f)) (-12.0, _("monitor dim level"), Controllable::Flag (0), -20.0f, 0.0f))
, _solo_boost_level_ptr (new MPControl<volatile gain_t> , _solo_boost_level_ptr (new MPControl<volatile gain_t> /* units in dB */
(1.0, _("monitor mono"), Controllable::Flag (0), 1.0f, 3.0f)) (0.0, _("monitor solo boost level"), Controllable::Flag (0), 0.0, 20.0))
, _dim_all_control (_dim_all_ptr) , _dim_all_control (_dim_all_ptr)
, _cut_all_control (_cut_all_ptr) , _cut_all_control (_cut_all_ptr)
@ -255,7 +255,8 @@ MonitorProcessor::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /
gain_t solo_boost; gain_t solo_boost;
if (_session.listening() || _session.soloing()) { if (_session.listening() || _session.soloing()) {
solo_boost = _solo_boost_level; /* solo boost controller is in dB */
solo_boost = dB_to_coefficient (_solo_boost_level);
} else { } else {
solo_boost = 1.0; solo_boost = 1.0;
} }
@ -266,6 +267,10 @@ MonitorProcessor::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /
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);
/* dim level is in dB */
dim_level = dB_to_coefficient (dim_level);
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 {

View file

@ -88,6 +88,7 @@ midi_event_size(const uint8_t* buffer)
// see http://www.midi.org/techspecs/midimessages.php // see http://www.midi.org/techspecs/midimessages.php
if (status == MIDI_CMD_COMMON_SYSEX) { if (status == MIDI_CMD_COMMON_SYSEX) {
int end; int end;
for (end = 1; buffer[end] != MIDI_CMD_COMMON_SYSEX_END; end++) { for (end = 1; buffer[end] != MIDI_CMD_COMMON_SYSEX_END; end++) {
assert((buffer[end] & 0x80) == 0); assert((buffer[end] & 0x80) == 0);
} }

View file

@ -20,6 +20,8 @@
#ifndef __gtkmm2ext_motion_feedback_h__ #ifndef __gtkmm2ext_motion_feedback_h__
#define __gtkmm2ext_motion_feedback_h__ #define __gtkmm2ext_motion_feedback_h__
#include "pbd/signals.h"
#include <gdkmm/pixbuf.h> #include <gdkmm/pixbuf.h>
#include <gtkmm/box.h> #include <gtkmm/box.h>
#include <gtkmm/eventbox.h> #include <gtkmm/eventbox.h>
@ -45,33 +47,38 @@ class MotionFeedback : public Gtk::VBox
MotionFeedback (Glib::RefPtr<Gdk::Pixbuf>, MotionFeedback (Glib::RefPtr<Gdk::Pixbuf>,
Type type, Type type,
boost::shared_ptr<PBD::Controllable>,
double default_value,
double step_increment,
double page_increment,
const char *widget_name = NULL, const char *widget_name = NULL,
Gtk::Adjustment *adj = NULL,
bool with_numeric_display = true, bool with_numeric_display = true,
int sub_image_width = 40, int sub_image_width = 40,
int sub_image_height = 40); int sub_image_height = 40);
virtual ~MotionFeedback (); virtual ~MotionFeedback ();
void set_adjustment (Gtk::Adjustment *adj); Gtk::Widget& eventwin () { return pixwin; }
Gtk::Adjustment *get_adjustment () { return adjustment; } Gtk::Entry& value_display() const { return *value; }
Gtk::Widget& eventwin () { return pixwin; }
Gtk::SpinButton& spinner() const { return *value; }
gfloat lower () { return _lower; }
gfloat upper () { return _upper; }
gfloat range () { return _range; }
boost::shared_ptr<PBD::Controllable> controllable() const; boost::shared_ptr<PBD::Controllable> controllable() const;
virtual void set_controllable (boost::shared_ptr<PBD::Controllable> c); virtual void set_controllable (boost::shared_ptr<PBD::Controllable> c);
void set_lamp_color (const Gdk::Color&); void set_lamp_color (const Gdk::Color&);
static Glib::RefPtr<Gdk::Pixbuf> render_pixbuf (int size); static Glib::RefPtr<Gdk::Pixbuf> render_pixbuf (int size);
void set_print_func(void (*pf)(char buf[32], const boost::shared_ptr<PBD::Controllable>&, void *),
void *arg) {
print_func = pf;
print_arg = arg;
};
protected: protected:
gfloat _range; boost::shared_ptr<PBD::Controllable> _controllable;
gfloat _lower; Gtk::Entry* value;
gfloat _upper; double default_value;
double step_inc;
double page_inc;
void pixwin_size_request (GtkRequisition *); void pixwin_size_request (GtkRequisition *);
@ -85,32 +92,37 @@ class MotionFeedback : public Gtk::VBox
bool pixwin_focus_out_event (GdkEventFocus *); bool pixwin_focus_out_event (GdkEventFocus *);
bool pixwin_expose_event (GdkEventExpose*); bool pixwin_expose_event (GdkEventExpose*);
bool pixwin_scroll_event (GdkEventScroll*); bool pixwin_scroll_event (GdkEventScroll*);
void pixwin_realized ();
/* map a display value (0.0 .. 1.0) to a control
value (controllable->lower() .. controllable()->upper)
*/
virtual double to_control_value (double) = 0;
/* map a control value (controllable->lower() .. controllable()->upper)
to a display value (0.0 .. 1.0)
*/
virtual double to_display_value (double) = 0;
double adjust (double control_value, double display_delta);
private: private:
Type type; Type type;
Gtk::EventBox pixwin; Gtk::EventBox pixwin;
Gtk::HBox* value_packer; Gtk::HBox* value_packer;
Gtk::SpinButton* value;
Gtk::Adjustment* adjustment;
Glib::RefPtr<Gdk::Pixbuf> pixbuf; Glib::RefPtr<Gdk::Pixbuf> pixbuf;
BindingProxy binding_proxy; BindingProxy binding_proxy;
double default_value; void (*print_func) (char buf[32], const boost::shared_ptr<PBD::Controllable>&, void *);
double step_inc; void *print_arg;
double page_inc; static void default_printer (char buf[32], const boost::shared_ptr<PBD::Controllable>&, void *);
bool grab_is_fine; bool grab_is_fine;
double grabbed_y; double grabbed_y;
double grabbed_x; double grabbed_x;
bool i_own_my_adjustment;
int subwidth; int subwidth;
int subheight; int subheight;
void adjustment_changed (); void controllable_value_changed ();
PBD::ScopedConnection controller_connection;
ProlooksHSV* lamp_hsv;
Gdk::Color _lamp_color;
GdkColor lamp_bright;
GdkColor lamp_dark;
static void core_draw (cairo_t*, int, double, double, double, double, const GdkColor* bright, const GdkColor* dark); static void core_draw (cairo_t*, int, double, double, double, double, const GdkColor* bright, const GdkColor* dark);
}; };

View file

@ -1,5 +1,6 @@
/* /*
Copyright (C) 1998-99 Paul Barton-Davis Copyright (C) 2010-2011 Paul Davis
This program is free software; you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or the Free Software Foundation; either version 2 of the License, or
@ -27,9 +28,12 @@
#include <gdk/gdkkeysyms.h> #include <gdk/gdkkeysyms.h>
#include <gtkmm.h> #include <gtkmm.h>
#include "pbd/controllable.h"
#include "gtkmm2ext/motionfeedback.h" #include "gtkmm2ext/motionfeedback.h"
#include "gtkmm2ext/keyboard.h" #include "gtkmm2ext/keyboard.h"
#include "gtkmm2ext/prolooks-helpers.h" #include "gtkmm2ext/prolooks-helpers.h"
#include "gtkmm2ext/gui_thread.h"
using namespace Gtk; using namespace Gtk;
using namespace Gtkmm2ext; using namespace Gtkmm2ext;
@ -37,32 +41,33 @@ using namespace sigc;
MotionFeedback::MotionFeedback (Glib::RefPtr<Gdk::Pixbuf> pix, MotionFeedback::MotionFeedback (Glib::RefPtr<Gdk::Pixbuf> pix,
Type t, Type t,
boost::shared_ptr<PBD::Controllable> c,
double default_val,
double step_increment,
double page_increment,
const char *widget_name, const char *widget_name,
Adjustment *adj,
bool with_numeric_display, bool with_numeric_display,
int subw, int subw,
int subh) int subh)
: type (t) : _controllable (c)
, value_packer (0)
, value (0) , value (0)
, default_value (default_val)
, step_inc (step_increment)
, page_inc (page_increment)
, type (t)
, value_packer (0)
, pixbuf (pix) , pixbuf (pix)
, subwidth (subw) , subwidth (subw)
, subheight (subh) , subheight (subh)
{ {
char value_name[1024]; char value_name[1024];
if (adj == NULL) { print_func = default_printer;
i_own_my_adjustment = true; print_arg = 0;
set_adjustment (new Adjustment (0, 0, 10000, 1, 10, 0));
} else {
i_own_my_adjustment = false;
set_adjustment (adj);
}
default_value = adjustment->get_value();
HBox* hpacker = manage (new HBox); HBox* hpacker = manage (new HBox);
hpacker->pack_start (pixwin, true, false); hpacker->pack_start (pixwin, true, true);
hpacker->show (); hpacker->show ();
pack_start (*hpacker, false, false); pack_start (*hpacker, false, false);
pixwin.show (); pixwin.show ();
@ -70,25 +75,30 @@ MotionFeedback::MotionFeedback (Glib::RefPtr<Gdk::Pixbuf> pix,
if (with_numeric_display) { if (with_numeric_display) {
value_packer = new HBox; value_packer = new HBox;
value = new SpinButton (*adjustment); value = new Entry;
value->set_editable (false);
value_packer->pack_start (*value, false, false); value_packer->pack_start (*value, false, false);
if (step_inc < 1) { hpacker = manage (new HBox);
value->set_digits (abs ((int) ceil (log10 (step_inc)))); hpacker->pack_start (*value_packer, true, false);
} hpacker->show ();
pack_start (*value_packer, false, false); pack_start (*hpacker, false, false);
if (widget_name) { if (widget_name) {
snprintf (value_name, sizeof(value_name), "%sValue", widget_name); snprintf (value_name, sizeof(value_name), "%sValue", widget_name);
value->set_name (value_name); value->set_name (value_name);
} }
if (_controllable) {
char buf[32];
print_func (buf, _controllable, print_arg);
value->set_text (buf);
}
value->show (); value->show ();
} }
adjustment->signal_value_changed().connect (mem_fun (*this, &MotionFeedback::adjustment_changed));
pixwin.set_events (Gdk::BUTTON_PRESS_MASK| pixwin.set_events (Gdk::BUTTON_PRESS_MASK|
Gdk::BUTTON_RELEASE_MASK| Gdk::BUTTON_RELEASE_MASK|
Gdk::POINTER_MOTION_MASK| Gdk::POINTER_MOTION_MASK|
@ -111,36 +121,14 @@ MotionFeedback::MotionFeedback (Glib::RefPtr<Gdk::Pixbuf> pix,
pixwin.signal_scroll_event().connect(mem_fun (*this,&MotionFeedback::pixwin_scroll_event)); pixwin.signal_scroll_event().connect(mem_fun (*this,&MotionFeedback::pixwin_scroll_event));
pixwin.signal_expose_event().connect(mem_fun (*this,&MotionFeedback::pixwin_expose_event), true); pixwin.signal_expose_event().connect(mem_fun (*this,&MotionFeedback::pixwin_expose_event), true);
pixwin.signal_size_request().connect(mem_fun (*this,&MotionFeedback::pixwin_size_request)); pixwin.signal_size_request().connect(mem_fun (*this,&MotionFeedback::pixwin_size_request));
pixwin.signal_realize().connect(mem_fun (*this,&MotionFeedback::pixwin_realized));
} }
MotionFeedback::~MotionFeedback() MotionFeedback::~MotionFeedback()
{ {
if (i_own_my_adjustment) {
delete adjustment;
}
delete value; delete value;
delete value_packer; delete value_packer;
} }
void
MotionFeedback::set_adjustment (Adjustment *adj)
{
adjustment = adj;
if (value) {
value->set_adjustment (*adj);
}
_lower = adj->get_lower();
_upper = adj->get_upper();
_range = _upper - _lower;
step_inc = adj->get_step_increment();
page_inc = adj->get_page_increment();
}
bool bool
MotionFeedback::pixwin_button_press_event (GdkEventButton *ev) MotionFeedback::pixwin_button_press_event (GdkEventButton *ev)
{ {
@ -172,6 +160,10 @@ MotionFeedback::pixwin_button_press_event (GdkEventButton *ev)
bool bool
MotionFeedback::pixwin_button_release_event (GdkEventButton *ev) MotionFeedback::pixwin_button_release_event (GdkEventButton *ev)
{ {
if (!_controllable) {
return false;
}
switch (ev->button) { switch (ev->button) {
case 1: case 1:
if (pixwin.has_grab()) { if (pixwin.has_grab()) {
@ -182,7 +174,7 @@ MotionFeedback::pixwin_button_release_event (GdkEventButton *ev)
} }
if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
/* shift click back to the default */ /* shift click back to the default */
adjustment->set_value (default_value); _controllable->set_value (default_value);
return true; return true;
} }
break; break;
@ -203,6 +195,10 @@ MotionFeedback::pixwin_button_release_event (GdkEventButton *ev)
bool bool
MotionFeedback::pixwin_motion_notify_event (GdkEventMotion *ev) MotionFeedback::pixwin_motion_notify_event (GdkEventMotion *ev)
{ {
if (!_controllable) {
return false;
}
gfloat multiplier; gfloat multiplier;
gfloat x_delta; gfloat x_delta;
gfloat y_delta; gfloat y_delta;
@ -212,9 +208,8 @@ MotionFeedback::pixwin_motion_notify_event (GdkEventMotion *ev)
} }
multiplier = ((ev->state & Keyboard::TertiaryModifier) ? 100 : 1) * multiplier = ((ev->state & Keyboard::TertiaryModifier) ? 100 : 1) *
((ev->state & Keyboard::SecondaryModifier) ? 10 : 1) * ((ev->state & Keyboard::PrimaryModifier) ? 10 : 1) *
((ev->state & Keyboard::PrimaryModifier) ? 2 : 1); ((ev->state & Keyboard::SecondaryModifier) ? 0.1 : 1);
if (ev->state & Gdk::BUTTON1_MASK) { if (ev->state & Gdk::BUTTON1_MASK) {
@ -229,12 +224,11 @@ MotionFeedback::pixwin_motion_notify_event (GdkEventMotion *ev)
y_delta *= multiplier; y_delta *= multiplier;
y_delta /= 10; y_delta /= 10;
adjustment->set_value (adjustment->get_value() + _controllable->set_value (adjust (_controllable->get_value(),
((grab_is_fine ? step_inc : page_inc) * y_delta)); ((grab_is_fine ? step_inc : page_inc) * y_delta)));
} else if (ev->state & Gdk::BUTTON3_MASK) { } else if (ev->state & Gdk::BUTTON3_MASK) {
double range = adjustment->get_upper() - adjustment->get_lower();
double x = ev->x - subwidth/2; double x = ev->x - subwidth/2;
double y = - ev->y + subwidth/2; double y = - ev->y + subwidth/2;
double angle = std::atan2 (y, x) / M_PI; double angle = std::atan2 (y, x) / M_PI;
@ -244,11 +238,9 @@ MotionFeedback::pixwin_motion_notify_event (GdkEventMotion *ev)
} }
angle = -(2.0/3.0) * (angle - 1.25); angle = -(2.0/3.0) * (angle - 1.25);
angle *= range;
angle *= multiplier; angle *= multiplier;
angle += adjustment->get_lower();
adjustment->set_value (angle); _controllable->set_value (to_control_value (angle));
} }
@ -269,12 +261,22 @@ MotionFeedback::pixwin_leave_notify_event (GdkEventCrossing *ev)
return false; return false;
} }
double
MotionFeedback::adjust (double control_value, double display_delta)
{
return to_control_value (to_display_value (control_value) + display_delta);
}
bool bool
MotionFeedback::pixwin_key_press_event (GdkEventKey *ev) MotionFeedback::pixwin_key_press_event (GdkEventKey *ev)
{ {
if (!_controllable) {
return false;
}
bool retval = false; bool retval = false;
gfloat curval; double curval = _controllable->get_value ();
gfloat multiplier; double multiplier;
multiplier = ((ev->state & Keyboard::TertiaryModifier) ? 100 : 1) * multiplier = ((ev->state & Keyboard::TertiaryModifier) ? 100 : 1) *
((ev->state & Keyboard::SecondaryModifier) ? 10 : 1) * ((ev->state & Keyboard::SecondaryModifier) ? 10 : 1) *
@ -283,48 +285,237 @@ MotionFeedback::pixwin_key_press_event (GdkEventKey *ev)
switch (ev->keyval) { switch (ev->keyval) {
case GDK_Page_Up: case GDK_Page_Up:
retval = true; retval = true;
curval = adjustment->get_value(); _controllable->set_value (adjust (curval, multiplier * page_inc));
adjustment->set_value (curval + (multiplier * page_inc));
break; break;
case GDK_Page_Down: case GDK_Page_Down:
retval = true; retval = true;
curval = adjustment->get_value(); _controllable->set_value (adjust (curval, multiplier * page_inc));
adjustment->set_value (curval - (multiplier * page_inc));
break; break;
case GDK_Up: case GDK_Up:
retval = true; retval = true;
curval = adjustment->get_value(); _controllable->set_value (adjust (curval, multiplier * step_inc));
adjustment->set_value (curval + (multiplier * step_inc));
break; break;
case GDK_Down: case GDK_Down:
retval = true; retval = true;
curval = adjustment->get_value(); _controllable->set_value (adjust (curval, multiplier * step_inc));
adjustment->set_value (curval - (multiplier * step_inc));
break; break;
case GDK_Home: case GDK_Home:
retval = true; retval = true;
adjustment->set_value (_lower); _controllable->set_value (_controllable->lower());
break; break;
case GDK_End: case GDK_End:
retval = true; retval = true;
adjustment->set_value (_upper); _controllable->set_value (_controllable->upper());
break; break;
} }
return retval; return retval;
} }
void bool
MotionFeedback::adjustment_changed () MotionFeedback::pixwin_expose_event (GdkEventExpose* ev)
{ {
if (!_controllable) {
return true;
}
GdkWindow *window = pixwin.get_window()->gobj();
double display_val = to_display_value (_controllable->get_value());
int32_t phase = lrint (display_val * 64.0);
// skip middle phase except for true middle value
if (type == Rotary && phase == 32) {
double pt = (display_val * 2.0) - 1.0;
if (pt < 0)
phase = 31;
if (pt > 0)
phase = 33;
}
// endless knob: skip 90deg highlights unless the value is really a multiple of 90deg
if (type == Endless && !(phase % 16)) {
if (phase == 64) {
phase = 0;
}
double nom = phase / 64.0;
double diff = display_val - nom;
if (diff > 0.0001)
phase = (phase + 1) % 64;
if (diff < -0.0001)
phase = (phase + 63) % 64;
}
phase = std::min (phase, (int32_t) 63);
GtkWidget* widget = GTK_WIDGET(pixwin.gobj());
gdk_draw_pixbuf (GDK_DRAWABLE(window), widget->style->fg_gc[0],
pixbuf->gobj(),
phase * subwidth, type * subheight,
/* center image in allocated area */
(get_width() - subwidth)/2,
0,
subwidth, subheight, GDK_RGB_DITHER_NORMAL, 0, 0);
return true;
}
bool
MotionFeedback::pixwin_scroll_event (GdkEventScroll* ev)
{
double scale;
if (!_controllable) {
return false;
}
if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
scale = 0.01;
} else if (ev->state & Keyboard::PrimaryModifier) {
scale = 0.1;
} else {
scale = 1.0;
}
switch (ev->direction) {
case GDK_SCROLL_UP:
case GDK_SCROLL_RIGHT:
_controllable->set_value (adjust (_controllable->get_value(), (scale * step_inc)));
break;
case GDK_SCROLL_DOWN:
case GDK_SCROLL_LEFT:
_controllable->set_value (adjust (_controllable->get_value(), -(scale * step_inc)));
break;
}
return true;
}
void
MotionFeedback::pixwin_size_request (GtkRequisition* req)
{
req->width = subwidth;
req->height = subheight;
}
void
MotionFeedback::controllable_value_changed ()
{
if (value) {
char buf[32];
print_func (buf, _controllable, print_arg);
value->set_text (buf);
}
pixwin.queue_draw (); pixwin.queue_draw ();
} }
void
MotionFeedback::set_controllable (boost::shared_ptr<PBD::Controllable> c)
{
_controllable = c;
binding_proxy.set_controllable (c);
controller_connection.disconnect ();
if (c) {
c->Changed.connect (controller_connection, MISSING_INVALIDATOR, boost::bind (&MotionFeedback::controllable_value_changed, this), gui_context());
char buf[32];
print_func (buf, _controllable, print_arg);
value->set_text (buf);
}
pixwin.queue_draw ();
}
boost::shared_ptr<PBD::Controllable>
MotionFeedback::controllable () const
{
return _controllable;
}
void
MotionFeedback::default_printer (char buf[32], const boost::shared_ptr<PBD::Controllable>& c, void *)
{
if (c) {
sprintf (buf, "%.2f", c->get_value());
} else {
buf[0] = '\0';
}
}
Glib::RefPtr<Gdk::Pixbuf>
MotionFeedback::render_pixbuf (int size)
{
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
char path[32];
int fd;
snprintf (path, sizeof (path), "/tmp/mfimg%dXXXXXX", size);
if ((fd = mkstemp (path)) < 0) {
return pixbuf;
}
GdkColor col2 = {0,0,0,0};
GdkColor col3 = {0,0,0,0};
Gdk::Color base ("#b9feff");
GdkColor dark;
GdkColor bright;
ProlooksHSV* hsv;
hsv = prolooks_hsv_new_for_gdk_color (base.gobj());
bright = (prolooks_hsv_to_gdk_color (hsv, &col2), col2);
prolooks_hsv_set_saturation (hsv, 0.66);
prolooks_hsv_set_value (hsv, 0.67);
dark = (prolooks_hsv_to_gdk_color (hsv, &col3), col3);
cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, size * 64, size);
cairo_t* cr = cairo_create (surface);
for (int i = 0; i < 64; ++i) {
cairo_save (cr);
core_draw (cr, i, size, 20, size*i, 0, &bright, &dark);
cairo_restore (cr);
}
if (cairo_surface_write_to_png (surface, path) != CAIRO_STATUS_SUCCESS) {
std::cerr << "could not save image set to " << path << std::endl;
return pixbuf;
}
close (fd);
cairo_destroy (cr);
cairo_surface_destroy (surface);
try {
pixbuf = Gdk::Pixbuf::create_from_file (path);
} catch (const Gdk::PixbufError &e) {
std::cerr << "Caught PixbufError: " << e.what() << std::endl;
unlink (path);
throw;
} catch (...) {
unlink (path);
g_message("Caught ... ");
throw;
}
unlink (path);
return pixbuf;
}
void void
MotionFeedback::core_draw (cairo_t* cr, int phase, double size, double progress_width, double xorigin, double yorigin, MotionFeedback::core_draw (cairo_t* cr, int phase, double size, double progress_width, double xorigin, double yorigin,
const GdkColor* bright, const GdkColor* dark) const GdkColor* bright, const GdkColor* dark)
@ -540,180 +731,3 @@ MotionFeedback::core_draw (cairo_t* cr, int phase, double size, double progress_
cairo_pattern_destroy (knob_ripples); cairo_pattern_destroy (knob_ripples);
} }
bool
MotionFeedback::pixwin_expose_event (GdkEventExpose* ev)
{
GdkWindow *window = pixwin.get_window()->gobj();
GtkAdjustment* adj = adjustment->gobj();
int phase = (int)((adj->value - adj->lower) * 64 /
(adj->upper - adj->lower));
// skip middle phase except for true middle value
if (type == Rotary && phase == 32) {
double pt = (adj->value - adj->lower) * 2.0 /
(adj->upper - adj->lower) - 1.0;
if (pt < 0)
phase = 31;
if (pt > 0)
phase = 33;
}
// endless knob: skip 90deg highlights unless the value is really a multiple of 90deg
if (type == Endless && !(phase % 16)) {
if (phase == 64) {
phase = 0;
}
double nom = adj->lower + phase * (adj->upper - adj->lower) / 64.0;
double diff = (adj->value - nom) / (adj->upper - adj->lower);
if (diff > 0.0001)
phase = (phase + 1) % 64;
if (diff < -0.0001)
phase = (phase + 63) % 64;
}
phase = std::min (phase, 63);
GtkWidget* widget = GTK_WIDGET(pixwin.gobj());
gdk_draw_pixbuf (GDK_DRAWABLE(window), widget->style->fg_gc[0],
pixbuf->gobj(),
phase * subwidth, type * subheight,
0, 0, subwidth, subheight, GDK_RGB_DITHER_NORMAL, 0, 0);
return true;
}
bool
MotionFeedback::pixwin_scroll_event (GdkEventScroll* ev)
{
double scale;
if ((ev->state & (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) == (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier)) {
scale = 0.01;
} else if (ev->state & Keyboard::PrimaryModifier) {
scale = 0.1;
} else {
scale = 1.0;
}
switch (ev->direction) {
case GDK_SCROLL_UP:
case GDK_SCROLL_RIGHT:
adjustment->set_value (adjustment->get_value() + (scale * adjustment->get_step_increment()));
break;
case GDK_SCROLL_DOWN:
case GDK_SCROLL_LEFT:
adjustment->set_value (adjustment->get_value() - (scale * adjustment->get_step_increment()));
break;
}
return true;
}
void
MotionFeedback::pixwin_size_request (GtkRequisition* req)
{
req->width = subwidth;
req->height = subheight;
}
void
MotionFeedback::pixwin_realized ()
{
set_lamp_color (Gdk::Color ("#b9feff"));
}
void
MotionFeedback::set_lamp_color (const Gdk::Color& c)
{
GdkColor col2 = {0,0,0,0};
GdkColor col3 = {0,0,0,0};
_lamp_color = c;
lamp_hsv = prolooks_hsv_new_for_gdk_color (_lamp_color.gobj());
lamp_bright = (prolooks_hsv_to_gdk_color (lamp_hsv, &col2), col2);
prolooks_hsv_set_saturation (lamp_hsv, 0.66);
prolooks_hsv_set_value (lamp_hsv, 0.67);
lamp_dark = (prolooks_hsv_to_gdk_color (lamp_hsv, &col3), col3);
}
Glib::RefPtr<Gdk::Pixbuf>
MotionFeedback::render_pixbuf (int size)
{
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
char path[32];
int fd;
snprintf (path, sizeof (path), "/tmp/mfimg%dXXXXXX", size);
if ((fd = mkstemp (path)) < 0) {
return pixbuf;
}
GdkColor col2 = {0,0,0,0};
GdkColor col3 = {0,0,0,0};
Gdk::Color base ("#b9feff");
GdkColor dark;
GdkColor bright;
ProlooksHSV* hsv;
hsv = prolooks_hsv_new_for_gdk_color (base.gobj());
bright = (prolooks_hsv_to_gdk_color (hsv, &col2), col2);
prolooks_hsv_set_saturation (hsv, 0.66);
prolooks_hsv_set_value (hsv, 0.67);
dark = (prolooks_hsv_to_gdk_color (hsv, &col3), col3);
cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, size * 64, size);
cairo_t* cr = cairo_create (surface);
for (int i = 0; i < 64; ++i) {
cairo_save (cr);
core_draw (cr, i, size, 20, size*i, 0, &bright, &dark);
cairo_restore (cr);
}
if (cairo_surface_write_to_png (surface, path) != CAIRO_STATUS_SUCCESS) {
std::cerr << "could not save image set to " << path << std::endl;
return pixbuf;
}
close (fd);
cairo_destroy (cr);
cairo_surface_destroy (surface);
try {
pixbuf = Gdk::Pixbuf::create_from_file (path);
} catch (const Gdk::PixbufError &e) {
std::cerr << "Caught PixbufError: " << e.what() << std::endl;
unlink (path);
throw;
} catch (...) {
unlink (path);
g_message("Caught ... ");
throw;
}
unlink (path);
return pixbuf;
}
void
MotionFeedback::set_controllable (boost::shared_ptr<PBD::Controllable> c)
{
binding_proxy.set_controllable (c);
}
boost::shared_ptr<PBD::Controllable>
MotionFeedback::controllable () const
{
return binding_proxy.get_controllable ();
}