diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2011-06-17 21:47:20 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2011-06-17 21:47:20 +0000 |
commit | 01e006e46e6d4dd0ab25e08bd44d13dd1e195886 (patch) | |
tree | 3ed1b959caa20fd0e117de2fcfa557738ae5aa86 | |
parent | cb8bc87a542e35794a12e76a23594e63b3bad521 (diff) |
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
-rw-r--r-- | gtk2_ardour/monitor_section.cc | 57 | ||||
-rw-r--r-- | gtk2_ardour/monitor_section.h | 7 | ||||
-rw-r--r-- | gtk2_ardour/volume_controller.cc | 111 | ||||
-rw-r--r-- | gtk2_ardour/volume_controller.h | 26 | ||||
-rw-r--r-- | libs/ardour/ardour/dB.h | 2 | ||||
-rw-r--r-- | libs/ardour/ardour/monitor_processor.h | 5 | ||||
-rw-r--r-- | libs/ardour/ardour/proxy_controllable.h | 8 | ||||
-rw-r--r-- | libs/ardour/monitor_processor.cc | 17 | ||||
-rw-r--r-- | libs/evoral/evoral/midi_util.h | 1 | ||||
-rw-r--r-- | libs/gtkmm2ext/gtkmm2ext/motionfeedback.h | 64 | ||||
-rw-r--r-- | libs/gtkmm2ext/motionfeedback.cc | 502 |
11 files changed, 427 insertions, 373 deletions
diff --git a/gtk2_ardour/monitor_section.cc b/gtk2_ardour/monitor_section.cc index c4729a857b..9d1811b1d4 100644 --- a/gtk2_ardour/monitor_section.cc +++ b/gtk2_ardour/monitor_section.cc @@ -37,13 +37,9 @@ MonitorSection::MonitorSection (Session* s) , RouteUI (s) , main_table (2, 3) , _tearoff (0) - , gain_adjustment (0.781787, 0.0, 1.0, 0.01, 0.1) // initial value is unity gain , gain_control (0) - , dim_adjustment (0.2, 0.0, 1.0, 0.01, 0.1) // upper+lower will be reset to match model , 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_cut_adjustment (0.0, 0.0, 1.0, 0.01, 0.1) , solo_cut_control (0) , solo_in_place_button (solo_model_group, _("SiP")) , afl_button (solo_model_group, _("AFL")) @@ -74,7 +70,7 @@ MonitorSection::MonitorSection (Session* s) /* 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); dim_packer->show (); @@ -87,7 +83,7 @@ MonitorSection::MonitorSection (Session* s) spin_packer->pack_start (*spin_label, false, false); dim_packer->set_spacing (12); - dim_packer->pack_start (*spin_packer, true, true); + dim_packer->pack_start (*spin_packer, true, false); /* Rude Solo */ @@ -138,7 +134,7 @@ MonitorSection::MonitorSection (Session* s) /* 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); 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 (*spin_label, false, false); - solo_packer->pack_start (*spin_packer, true, true); + solo_packer->pack_start (*spin_packer, false, true); /* 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_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 (*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")); 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_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_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 MonitorSection::update_solo_model () { @@ -818,10 +794,6 @@ MonitorSection::map_state () 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; update_solo_model (); @@ -987,8 +959,6 @@ MonitorSection::parameter_changed (std::string name) if (name == "solo-control-is-listen-control" || name == "listen-position") { 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) { boost::shared_ptr<Controllable> c = _session->solo_cut_control(); solo_cut_control->set_controllable (c); - solo_cut_control->get_adjustment()->set_value (gain_to_slider_position (c->get_value())); } else { solo_cut_control->set_controllable (none); } @@ -1025,16 +994,8 @@ MonitorSection::assign_controllables () 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()); - - 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()); + dim_control->set_controllable (_monitor->dim_level_control ()); + solo_boost_control->set_controllable (_monitor->solo_boost_control ()); } else { diff --git a/gtk2_ardour/monitor_section.h b/gtk2_ardour/monitor_section.h index 6a8f25c7e3..133e773186 100644 --- a/gtk2_ardour/monitor_section.h +++ b/gtk2_ardour/monitor_section.h @@ -64,13 +64,9 @@ class MonitorSection : public RouteUI typedef std::vector<ChannelButtonSet*> ChannelButtons; ChannelButtons _channel_buttons; - Gtk::Adjustment gain_adjustment; VolumeController* gain_control; - Gtk::Adjustment dim_adjustment; VolumeController* dim_control; - Gtk::Adjustment solo_boost_adjustment; VolumeController* solo_boost_control; - Gtk::Adjustment solo_cut_adjustment; VolumeController* solo_cut_control; void populate_buttons (); @@ -99,9 +95,6 @@ class MonitorSection : public RouteUI void solo_boost_changed (); void gain_value_changed (); - bool nonlinear_gain_printer (Gtk::SpinButton*); - bool linear_gain_printer (Gtk::SpinButton*); - Gtk::RadioButtonGroup solo_model_group; Gtk::RadioButton solo_in_place_button; Gtk::RadioButton afl_button; diff --git a/gtk2_ardour/volume_controller.cc b/gtk2_ardour/volume_controller.cc index bb6a5f1db3..8a382f6cb1 100644 --- a/gtk2_ardour/volume_controller.cc +++ b/gtk2_ardour/volume_controller.cc @@ -17,12 +17,17 @@ $Id: volume_controller.cc,v 1.4 2000/05/03 15:54:21 pbd Exp $ */ +#include <algorithm> + #include <string.h> #include <limits.h> #include "pbd/controllable.h" +#include "pbd/stacktrace.h" #include "gtkmm2ext/gui_thread.h" + +#include "ardour/dB.h" #include "ardour/utils.h" #include "volume_controller.h" @@ -30,45 +35,99 @@ using namespace Gtk; 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, - int subw, int subh) - - : MotionFeedback (p, MotionFeedback::Rotary, "", adj, with_numeric, subw, subh) + int subw, + int subh, + bool linear, + bool dB) + + : 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 -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); - - controllable_connection.disconnect (); - - if (c) { - c->Changed.connect (controllable_connection, MISSING_INVALIDATOR, boost::bind (&VolumeController::controllable_value_changed, this), gui_context()); - } - - controllable_value_changed (); + VolumeController* vc = reinterpret_cast<VolumeController*>(arg); + vc->dB_printer (buf, c); } 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) { - get_adjustment()->set_value (gain_to_slider_position (c->get_value ())); - } + if (c) { + + 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 -VolumeController::adjustment_value_changed () +double +VolumeController::to_control_value (double display_value) { - boost::shared_ptr<PBD::Controllable> c = controllable(); - if (c) { - c->set_value (slider_position_to_gain (get_adjustment()->get_value())); - } + double v; + + /* 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; +} diff --git a/gtk2_ardour/volume_controller.h b/gtk2_ardour/volume_controller.h index d55e654930..8693f5f0da 100644 --- a/gtk2_ardour/volume_controller.h +++ b/gtk2_ardour/volume_controller.h @@ -24,27 +24,33 @@ #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 { public: VolumeController (Glib::RefPtr<Gdk::Pixbuf>, - Gtk::Adjustment *adj, + boost::shared_ptr<PBD::Controllable>, + double def, + double step, + double page, bool with_numeric = true, int image_width = 40, - int image_height = 40); + int image_height = 40, + bool linear = true, + bool dB = false); 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: - Gtk::Adjustment *adjustment; - PBD::ScopedConnection controllable_connection; + bool _linear; + bool _controllable_uses_dB; - void adjustment_value_changed (); - void controllable_value_changed (); + void dB_printer (char buf[32], const boost::shared_ptr<PBD::Controllable>& adj); }; #endif // __gtk_ardour_vol_controller_h__ diff --git a/libs/ardour/ardour/dB.h b/libs/ardour/ardour/dB.h index 209ab11edc..e5865aabd8 100644 --- a/libs/ardour/ardour/dB.h +++ b/libs/ardour/ardour/dB.h @@ -34,4 +34,6 @@ static inline float accurate_coefficient_to_dB (float coeff) { return 20.0f * log10 (coeff); } +static const double zero_db_as_fraction = 0.781787; + #endif /* __ardour_dB_h__ */ diff --git a/libs/ardour/ardour/monitor_processor.h b/libs/ardour/ardour/monitor_processor.h index 023dacb619..5b724b5e8d 100644 --- a/libs/ardour/ardour/monitor_processor.h +++ b/libs/ardour/ardour/monitor_processor.h @@ -20,6 +20,7 @@ #ifndef __ardour_monitor_processor_h__ #define __ardour_monitor_processor_h__ +#include <algorithm> #include <iostream> #include <vector> @@ -52,7 +53,7 @@ public: void set_value (double v) { T newval = (T) v; if (newval != _value) { - _value = newval; + _value = std::max (_lower, std::min (_upper, newval)); Changed(); /* EMIT SIGNAL */ } } @@ -68,7 +69,7 @@ public: MPControl& operator=(const T& v) { if (v != _value) { - _value = v; + _value = std::max (_lower, std::min (_upper, v)); Changed (); /* EMIT SIGNAL */ } return *this; diff --git a/libs/ardour/ardour/proxy_controllable.h b/libs/ardour/ardour/proxy_controllable.h index cbbdcbd6ca..169f60f9f5 100644 --- a/libs/ardour/ardour/proxy_controllable.h +++ b/libs/ardour/ardour/proxy_controllable.h @@ -33,18 +33,18 @@ namespace ARDOUR { class ProxyControllable : public PBD::Controllable { public: ProxyControllable (const std::string& name, PBD::Controllable::Flag flags, - boost::function1<void,double> setter, + boost::function1<bool,double> setter, boost::function0<double> getter) : PBD::Controllable (name, flags) , _setter (setter) , _getter (getter) {} - void set_value (double v) { _setter (v); } - double get_value () const { return _getter (); } + void set_value (double v) { if (_setter (v)) { Changed(); /* EMIT SIGNAL */ } } + double get_value () const { return _getter (); } private: - boost::function1<void,double> _setter; + boost::function1<bool,double> _setter; boost::function0<double> _getter; }; diff --git a/libs/ardour/monitor_processor.cc b/libs/ardour/monitor_processor.cc index 57d4a57c74..1125dedc64 100644 --- a/libs/ardour/monitor_processor.cc +++ b/libs/ardour/monitor_processor.cc @@ -34,11 +34,11 @@ MonitorProcessor::MonitorProcessor (Session& s) , _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_level_ptr (new MPControl<volatile gain_t> /* units in dB */ + (-12.0, _("monitor dim level"), Controllable::Flag (0), -20.0f, 0.0f)) + , _solo_boost_level_ptr (new MPControl<volatile gain_t> /* units in dB */ + (0.0, _("monitor solo boost level"), Controllable::Flag (0), 0.0, 20.0)) + , _dim_all_control (_dim_all_ptr) , _cut_all_control (_cut_all_ptr) , _mono_control (_mono_ptr) @@ -255,7 +255,8 @@ MonitorProcessor::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t / gain_t solo_boost; 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 { 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); + /* dim level is in dB */ + + dim_level = dB_to_coefficient (dim_level); + if (_channels[chn]->soloed) { target_gain = _channels[chn]->polarity * _channels[chn]->cut * dim_level * global_cut * global_dim * solo_boost; } else { diff --git a/libs/evoral/evoral/midi_util.h b/libs/evoral/evoral/midi_util.h index c30aa861ff..da7051aefa 100644 --- a/libs/evoral/evoral/midi_util.h +++ b/libs/evoral/evoral/midi_util.h @@ -88,6 +88,7 @@ midi_event_size(const uint8_t* buffer) // see http://www.midi.org/techspecs/midimessages.php if (status == MIDI_CMD_COMMON_SYSEX) { int end; + for (end = 1; buffer[end] != MIDI_CMD_COMMON_SYSEX_END; end++) { assert((buffer[end] & 0x80) == 0); } diff --git a/libs/gtkmm2ext/gtkmm2ext/motionfeedback.h b/libs/gtkmm2ext/gtkmm2ext/motionfeedback.h index 84b2ae7154..0eaf78ed55 100644 --- a/libs/gtkmm2ext/gtkmm2ext/motionfeedback.h +++ b/libs/gtkmm2ext/gtkmm2ext/motionfeedback.h @@ -20,6 +20,8 @@ #ifndef __gtkmm2ext_motion_feedback_h__ #define __gtkmm2ext_motion_feedback_h__ +#include "pbd/signals.h" + #include <gdkmm/pixbuf.h> #include <gtkmm/box.h> #include <gtkmm/eventbox.h> @@ -45,33 +47,38 @@ class MotionFeedback : public Gtk::VBox MotionFeedback (Glib::RefPtr<Gdk::Pixbuf>, Type type, + boost::shared_ptr<PBD::Controllable>, + double default_value, + double step_increment, + double page_increment, const char *widget_name = NULL, - Gtk::Adjustment *adj = NULL, bool with_numeric_display = true, int sub_image_width = 40, int sub_image_height = 40); virtual ~MotionFeedback (); - void set_adjustment (Gtk::Adjustment *adj); - Gtk::Adjustment *get_adjustment () { return adjustment; } - - Gtk::Widget& eventwin () { return pixwin; } - Gtk::SpinButton& spinner() const { return *value; } + Gtk::Widget& eventwin () { return pixwin; } + Gtk::Entry& value_display() const { return *value; } - gfloat lower () { return _lower; } - gfloat upper () { return _upper; } - gfloat range () { return _range; } - boost::shared_ptr<PBD::Controllable> controllable() const; virtual void set_controllable (boost::shared_ptr<PBD::Controllable> c); + void set_lamp_color (const Gdk::Color&); 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: - gfloat _range; - gfloat _lower; - gfloat _upper; + boost::shared_ptr<PBD::Controllable> _controllable; + Gtk::Entry* value; + double default_value; + double step_inc; + double page_inc; void pixwin_size_request (GtkRequisition *); @@ -85,32 +92,37 @@ class MotionFeedback : public Gtk::VBox bool pixwin_focus_out_event (GdkEventFocus *); bool pixwin_expose_event (GdkEventExpose*); 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: Type type; Gtk::EventBox pixwin; Gtk::HBox* value_packer; - Gtk::SpinButton* value; - Gtk::Adjustment* adjustment; Glib::RefPtr<Gdk::Pixbuf> pixbuf; BindingProxy binding_proxy; - double default_value; - double step_inc; - double page_inc; + void (*print_func) (char buf[32], const boost::shared_ptr<PBD::Controllable>&, void *); + void *print_arg; + static void default_printer (char buf[32], const boost::shared_ptr<PBD::Controllable>&, void *); + bool grab_is_fine; double grabbed_y; double grabbed_x; - bool i_own_my_adjustment; int subwidth; int subheight; - void adjustment_changed (); - - ProlooksHSV* lamp_hsv; - Gdk::Color _lamp_color; - GdkColor lamp_bright; - GdkColor lamp_dark; + void controllable_value_changed (); + PBD::ScopedConnection controller_connection; static void core_draw (cairo_t*, int, double, double, double, double, const GdkColor* bright, const GdkColor* dark); }; diff --git a/libs/gtkmm2ext/motionfeedback.cc b/libs/gtkmm2ext/motionfeedback.cc index 43c8531b8e..da16da57ce 100644 --- a/libs/gtkmm2ext/motionfeedback.cc +++ b/libs/gtkmm2ext/motionfeedback.cc @@ -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 it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or @@ -27,9 +28,12 @@ #include <gdk/gdkkeysyms.h> #include <gtkmm.h> +#include "pbd/controllable.h" + #include "gtkmm2ext/motionfeedback.h" #include "gtkmm2ext/keyboard.h" #include "gtkmm2ext/prolooks-helpers.h" +#include "gtkmm2ext/gui_thread.h" using namespace Gtk; using namespace Gtkmm2ext; @@ -37,32 +41,33 @@ using namespace sigc; MotionFeedback::MotionFeedback (Glib::RefPtr<Gdk::Pixbuf> pix, Type t, + boost::shared_ptr<PBD::Controllable> c, + double default_val, + double step_increment, + double page_increment, const char *widget_name, - Adjustment *adj, bool with_numeric_display, int subw, int subh) - : type (t) - , value_packer (0) + : _controllable (c) , value (0) + , default_value (default_val) + , step_inc (step_increment) + , page_inc (page_increment) + , type (t) + , value_packer (0) , pixbuf (pix) , subwidth (subw) , subheight (subh) { char value_name[1024]; - if (adj == NULL) { - i_own_my_adjustment = true; - set_adjustment (new Adjustment (0, 0, 10000, 1, 10, 0)); - } else { - i_own_my_adjustment = false; - set_adjustment (adj); - } + print_func = default_printer; + print_arg = 0; - default_value = adjustment->get_value(); HBox* hpacker = manage (new HBox); - hpacker->pack_start (pixwin, true, false); + hpacker->pack_start (pixwin, true, true); hpacker->show (); pack_start (*hpacker, false, false); pixwin.show (); @@ -70,25 +75,30 @@ MotionFeedback::MotionFeedback (Glib::RefPtr<Gdk::Pixbuf> pix, if (with_numeric_display) { value_packer = new HBox; - value = new SpinButton (*adjustment); + value = new Entry; + value->set_editable (false); value_packer->pack_start (*value, false, false); - - if (step_inc < 1) { - value->set_digits (abs ((int) ceil (log10 (step_inc)))); - } - pack_start (*value_packer, false, false); + hpacker = manage (new HBox); + hpacker->pack_start (*value_packer, true, false); + hpacker->show (); + + pack_start (*hpacker, false, false); if (widget_name) { snprintf (value_name, sizeof(value_name), "%sValue", widget_name); value->set_name (value_name); } + if (_controllable) { + char buf[32]; + print_func (buf, _controllable, print_arg); + value->set_text (buf); + } + value->show (); } - adjustment->signal_value_changed().connect (mem_fun (*this, &MotionFeedback::adjustment_changed)); - pixwin.set_events (Gdk::BUTTON_PRESS_MASK| Gdk::BUTTON_RELEASE_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_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_realize().connect(mem_fun (*this,&MotionFeedback::pixwin_realized)); } MotionFeedback::~MotionFeedback() - { - if (i_own_my_adjustment) { - delete adjustment; - } - delete value; 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 MotionFeedback::pixwin_button_press_event (GdkEventButton *ev) { @@ -172,6 +160,10 @@ MotionFeedback::pixwin_button_press_event (GdkEventButton *ev) bool MotionFeedback::pixwin_button_release_event (GdkEventButton *ev) { + if (!_controllable) { + return false; + } + switch (ev->button) { case 1: if (pixwin.has_grab()) { @@ -182,7 +174,7 @@ MotionFeedback::pixwin_button_release_event (GdkEventButton *ev) } if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { /* shift click back to the default */ - adjustment->set_value (default_value); + _controllable->set_value (default_value); return true; } break; @@ -203,6 +195,10 @@ MotionFeedback::pixwin_button_release_event (GdkEventButton *ev) bool MotionFeedback::pixwin_motion_notify_event (GdkEventMotion *ev) { + if (!_controllable) { + return false; + } + gfloat multiplier; gfloat x_delta; gfloat y_delta; @@ -212,9 +208,8 @@ MotionFeedback::pixwin_motion_notify_event (GdkEventMotion *ev) } multiplier = ((ev->state & Keyboard::TertiaryModifier) ? 100 : 1) * - ((ev->state & Keyboard::SecondaryModifier) ? 10 : 1) * - ((ev->state & Keyboard::PrimaryModifier) ? 2 : 1); - + ((ev->state & Keyboard::PrimaryModifier) ? 10 : 1) * + ((ev->state & Keyboard::SecondaryModifier) ? 0.1 : 1); if (ev->state & Gdk::BUTTON1_MASK) { @@ -229,12 +224,11 @@ MotionFeedback::pixwin_motion_notify_event (GdkEventMotion *ev) y_delta *= multiplier; y_delta /= 10; - adjustment->set_value (adjustment->get_value() + - ((grab_is_fine ? step_inc : page_inc) * y_delta)); + _controllable->set_value (adjust (_controllable->get_value(), + ((grab_is_fine ? step_inc : page_inc) * y_delta))); } else if (ev->state & Gdk::BUTTON3_MASK) { - double range = adjustment->get_upper() - adjustment->get_lower(); double x = ev->x - subwidth/2; double y = - ev->y + subwidth/2; 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 *= range; 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; } +double +MotionFeedback::adjust (double control_value, double display_delta) +{ + return to_control_value (to_display_value (control_value) + display_delta); +} + bool MotionFeedback::pixwin_key_press_event (GdkEventKey *ev) { + if (!_controllable) { + return false; + } + bool retval = false; - gfloat curval; - gfloat multiplier; + double curval = _controllable->get_value (); + double multiplier; multiplier = ((ev->state & Keyboard::TertiaryModifier) ? 100 : 1) * ((ev->state & Keyboard::SecondaryModifier) ? 10 : 1) * @@ -283,48 +285,237 @@ MotionFeedback::pixwin_key_press_event (GdkEventKey *ev) switch (ev->keyval) { case GDK_Page_Up: retval = true; - curval = adjustment->get_value(); - adjustment->set_value (curval + (multiplier * page_inc)); + _controllable->set_value (adjust (curval, multiplier * page_inc)); break; case GDK_Page_Down: retval = true; - curval = adjustment->get_value(); - adjustment->set_value (curval - (multiplier * page_inc)); + _controllable->set_value (adjust (curval, multiplier * page_inc)); break; case GDK_Up: retval = true; - curval = adjustment->get_value(); - adjustment->set_value (curval + (multiplier * step_inc)); + _controllable->set_value (adjust (curval, multiplier * step_inc)); break; case GDK_Down: retval = true; - curval = adjustment->get_value(); - adjustment->set_value (curval - (multiplier * step_inc)); + _controllable->set_value (adjust (curval, multiplier * step_inc)); break; case GDK_Home: retval = true; - adjustment->set_value (_lower); + _controllable->set_value (_controllable->lower()); break; case GDK_End: retval = true; - adjustment->set_value (_upper); + _controllable->set_value (_controllable->upper()); break; } return retval; } +bool +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::adjustment_changed () +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 (); +} + +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 MotionFeedback::core_draw (cairo_t* cr, int phase, double size, double progress_width, double xorigin, double yorigin, 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); } - -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 (); -} - |