diff --git a/gtk2_ardour/editor_routes.cc b/gtk2_ardour/editor_routes.cc index 35eec34c75..8feebe9df3 100644 --- a/gtk2_ardour/editor_routes.cc +++ b/gtk2_ardour/editor_routes.cc @@ -43,6 +43,7 @@ #include "ardour/route.h" +#include "gtkmm2ext/cell_renderer_pixbuf_multi.h" #include "gtkmm2ext/cell_renderer_pixbuf_toggle.h" #include "i18n.h" @@ -82,27 +83,27 @@ EditorRoutes::EditorRoutes (Editor* e) rec_state_column->add_attribute(rec_col_renderer->property_visible(), _columns.is_track); // Mute enable toggle - CellRendererPixbufToggle* mute_col_renderer = manage (new CellRendererPixbufToggle()); + CellRendererPixbufMulti* mute_col_renderer = manage (new CellRendererPixbufMulti()); - mute_col_renderer->set_active_pixbuf (::get_icon("mute-enabled")); - mute_col_renderer->set_inactive_pixbuf (::get_icon("act-disabled")); - mute_col_renderer->signal_toggled().connect (mem_fun (*this, &EditorRoutes::on_tv_mute_enable_toggled)); + mute_col_renderer->set_pixbuf (0, ::get_icon("act-disabled")); + mute_col_renderer->set_pixbuf (1, ::get_icon("mute-enabled")); + mute_col_renderer->signal_changed().connect (mem_fun (*this, &EditorRoutes::on_tv_mute_enable_toggled)); Gtk::TreeViewColumn* mute_state_column = manage (new TreeViewColumn("M", *mute_col_renderer)); - mute_state_column->add_attribute(mute_col_renderer->property_active(), _columns.mute_enabled); + mute_state_column->add_attribute(mute_col_renderer->property_state(), _columns.mute_state); mute_state_column->add_attribute(mute_col_renderer->property_visible(), _columns.is_track); // Solo enable toggle - CellRendererPixbufToggle* solo_col_renderer = manage (new CellRendererPixbufToggle()); + CellRendererPixbufMulti* solo_col_renderer = manage (new CellRendererPixbufMulti()); - solo_col_renderer->set_active_pixbuf (::get_icon("solo-enabled")); - solo_col_renderer->set_inactive_pixbuf (::get_icon("act-disabled")); - solo_col_renderer->signal_toggled().connect (mem_fun (*this, &EditorRoutes::on_tv_solo_enable_toggled)); + solo_col_renderer->set_pixbuf (0, ::get_icon("act-disabled")); + solo_col_renderer->set_pixbuf (1, ::get_icon("solo-enabled")); + solo_col_renderer->signal_changed().connect (mem_fun (*this, &EditorRoutes::on_tv_solo_enable_toggled)); Gtk::TreeViewColumn* solo_state_column = manage (new TreeViewColumn("S", *solo_col_renderer)); - solo_state_column->add_attribute(solo_col_renderer->property_active(), _columns.solo_enabled); + solo_state_column->add_attribute(solo_col_renderer->property_state(), _columns.solo_state); solo_state_column->add_attribute(solo_col_renderer->property_visible(), _columns.is_track); @@ -167,7 +168,6 @@ EditorRoutes::on_tv_rec_enable_toggled (Glib::ustring const & path_string) } } - void EditorRoutes::on_tv_mute_enable_toggled (Glib::ustring const & path_string) { @@ -182,8 +182,6 @@ EditorRoutes::on_tv_mute_enable_toggled (Glib::ustring const & path_string) } } - - void EditorRoutes::on_tv_solo_enable_toggled (Glib::ustring const & path_string) { @@ -198,7 +196,6 @@ EditorRoutes::on_tv_solo_enable_toggled (Glib::ustring const & path_string) } } - void EditorRoutes::build_menu () { @@ -914,9 +911,9 @@ EditorRoutes::update_mute_display (void* /*src*/) if (boost::dynamic_pointer_cast(route)) { if (route->muted()){ - (*i)[_columns.mute_enabled] = true; + (*i)[_columns.mute_state] = 1; } else { - (*i)[_columns.mute_enabled] = false; + (*i)[_columns.mute_state] = 0; } } } @@ -934,9 +931,9 @@ EditorRoutes::update_solo_display (void* /*src*/) if (boost::dynamic_pointer_cast(route)) { if (route->soloed()){ - (*i)[_columns.solo_enabled] = true; + (*i)[_columns.solo_state] = 1; } else { - (*i)[_columns.solo_enabled] = false; + (*i)[_columns.solo_state] = 0; } } } diff --git a/gtk2_ardour/editor_routes.h b/gtk2_ardour/editor_routes.h index 72554129c8..0a8947fdd1 100644 --- a/gtk2_ardour/editor_routes.h +++ b/gtk2_ardour/editor_routes.h @@ -89,8 +89,8 @@ private: add (text); add (visible); add (rec_enabled); - add (mute_enabled); - add (solo_enabled); + add (mute_state); + add (solo_state); add (is_track); add (tv); add (route); @@ -99,8 +99,8 @@ private: Gtk::TreeModelColumn text; Gtk::TreeModelColumn visible; Gtk::TreeModelColumn rec_enabled; - Gtk::TreeModelColumn mute_enabled; - Gtk::TreeModelColumn solo_enabled; + Gtk::TreeModelColumn mute_state; + Gtk::TreeModelColumn solo_state; Gtk::TreeModelColumn is_track; Gtk::TreeModelColumn tv; Gtk::TreeModelColumn > route; diff --git a/gtk2_ardour/route_ui.cc b/gtk2_ardour/route_ui.cc index e06b9b18c4..95cb6b66ef 100644 --- a/gtk2_ardour/route_ui.cc +++ b/gtk2_ardour/route_ui.cc @@ -737,16 +737,21 @@ RouteUI::update_mute_display () if (Config->get_show_solo_mutes()) { if (_route->muted()) { + /* full mute */ mute_button->set_visual_state (2); - } else if (!_route->soloed() && _session.soloing()) { + } else if (_session.soloing() && !_route->soloed() && !_route->solo_isolated()) { + /* mute-because-not-soloed */ mute_button->set_visual_state (1); } else { + /* no mute at all */ mute_button->set_visual_state (0); } } else { if (_route->muted()) { + /* full mute */ mute_button->set_visual_state (2); } else { + /* no mute at all */ mute_button->set_visual_state (0); } } diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 1a60e2f8cd..db67cab17b 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -129,8 +129,11 @@ class Route : public SessionObject, public AutomatableControls */ void set_solo (bool yn, void *src); - bool soloed () const { return (bool) _solo_level; } + bool soloed_by_others () const { return !_solo_isolated && _soloed_by_others; } + bool self_soloed () const { return _self_solo; } + bool soloed () const {return self_soloed () || soloed_by_others (); } + void set_solo_isolated (bool yn, void *src); bool solo_isolated() const; @@ -310,8 +313,7 @@ class Route : public SessionObject, public AutomatableControls friend class Session; void catch_up_on_solo_mute_override (); - void mod_solo_level (int32_t); - uint32_t solo_level () const { return _solo_level; } + void mod_solo_by_others (int32_t); void set_block_size (nframes_t nframes); bool has_external_redirects() const; void curve_reallocate (); @@ -347,7 +349,8 @@ class Route : public SessionObject, public AutomatableControls int _pending_declick; MeterPoint _meter_point; uint32_t _phase_invert; - uint32_t _solo_level; + bool _self_solo; + uint32_t _soloed_by_others; bool _solo_isolated; bool _denormal_protection; @@ -411,6 +414,9 @@ class Route : public SessionObject, public AutomatableControls bool add_processor_from_xml_2X (const XMLNode&, int, ProcessorList::iterator iter); void placement_range (Placement p, ProcessorList::iterator& start, ProcessorList::iterator& end); + + void set_self_solo (bool yn); + void set_delivery_solo (); }; } // namespace ARDOUR diff --git a/libs/ardour/delivery.cc b/libs/ardour/delivery.cc index 113c434388..72f24c065e 100644 --- a/libs/ardour/delivery.cc +++ b/libs/ardour/delivery.cc @@ -529,6 +529,7 @@ Delivery::target_gain () gain_t desired_gain; + if (_solo_level) { desired_gain = 1.0; } else { @@ -549,18 +550,12 @@ Delivery::target_gain () mp = MuteMaster::PreFader; break; } - - if (_solo_isolated) { - - /* ... but we are isolated from all that nonsense */ - - desired_gain = _mute_master->mute_gain_at (mp); - - } else if (_session.soloing()) { - + + if (!_solo_isolated && _session.soloing()) { desired_gain = min (Config->get_solo_mute_gain(), _mute_master->mute_gain_at (mp)); } else { + desired_gain = _mute_master->mute_gain_at (mp); } diff --git a/libs/ardour/io_processor.cc b/libs/ardour/io_processor.cc index 21a2b10313..6bdac9fe19 100644 --- a/libs/ardour/io_processor.cc +++ b/libs/ardour/io_processor.cc @@ -133,7 +133,6 @@ IOProcessor::state (bool full_state) node.add_property ("own-output", "yes"); if (_output) { XMLNode& o (_output->state (full_state)); - // o.name() = X_("output"); node.add_child_nocopy (o); } } else { @@ -166,12 +165,21 @@ IOProcessor::set_state (const XMLNode& node, int version) XMLNodeList nlist = node.children(); XMLNodeIterator niter; + const string instr = enum_2_string (IO::Input); + const string outstr = enum_2_string (IO::Output); if (_own_input) { for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - if ((*niter)->name() == "input") { - io_node = (*niter); - break; + const XMLProperty* prop; + if ((prop = (*niter)->property ("name")) != 0) { + if (prop->value() == _name) { + if ((prop = (*niter)->property ("direction")) != 0) { + if (prop->value() == instr) { + io_node = (*niter); + break; + } + } + } } } @@ -184,16 +192,25 @@ IOProcessor::set_state (const XMLNode& node, int version) } } else { - /* no input */ + /* no input, which is OK */ } } if (_own_output) { for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - if ((*niter)->name() == "output") { - io_node = (*niter); - break; + if ((*niter)->name() == "IO") { + const XMLProperty* prop; + if ((prop = (*niter)->property ("name")) != 0) { + if (prop->value() == _name) { + if ((prop = (*niter)->property ("direction")) != 0) { + if (prop->value() == outstr) { + io_node = (*niter); + break; + } + } + } + } } } @@ -205,7 +222,7 @@ IOProcessor::set_state (const XMLNode& node, int version) set_name (_output->name()); } } else { - /* no output */ + /* no output, which is OK */ } } diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index d6f740e33c..372e9ade68 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -116,11 +116,12 @@ Route::Route (Session& sess, const XMLNode& node, DataType default_type) void Route::init () { - _solo_level = 0; + _self_solo = false; + _soloed_by_others = 0; _solo_isolated = false; + _solo_safe = false; _active = true; processor_max_streams.reset(); - _solo_safe = false; _recordable = true; order_keys[N_("signal")] = order_key_cnt++; _silent = false; @@ -523,7 +524,7 @@ Route::listening () const void Route::set_solo (bool yn, void *src) { - if (_solo_safe || _solo_isolated) { + if (_solo_safe) { return; } @@ -532,40 +533,51 @@ Route::set_solo (bool yn, void *src) return; } - if (soloed() != yn) { - mod_solo_level (yn ? 1 : -1); + if (self_soloed() != yn) { + set_self_solo (yn); + set_delivery_solo (); solo_changed (src); /* EMIT SIGNAL */ _solo_control->Changed (); /* EMIT SIGNAL */ } } void -Route::mod_solo_level (int32_t delta) +Route::set_self_solo (bool yn) +{ + _self_solo = yn; +} + +void +Route::mod_solo_by_others (int32_t delta) { if (delta < 0) { - if (_solo_level >= (uint32_t) delta) { - _solo_level += delta; + if (_soloed_by_others >= (uint32_t) delta) { + _soloed_by_others += delta; } else { - _solo_level = 0; + _soloed_by_others = 0; } } else { - _solo_level += delta; + _soloed_by_others += delta; } - { - /* tell all delivery processors what the solo situation is, so that they keep - delivering even though Session::soloing() is true and they were not - explicitly soloed. - */ - - Glib::RWLock::ReaderLock rm (_processor_lock); - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - boost::shared_ptr d; + set_delivery_solo (); +} - if ((d = boost::dynamic_pointer_cast (*i)) != 0) { - d->set_solo_level (_solo_level); - d->set_solo_isolated (_solo_isolated); - } +void +Route::set_delivery_solo () +{ + /* tell all delivery processors what the solo situation is, so that they keep + delivering even though Session::soloing() is true and they were not + explicitly soloed. + */ + + Glib::RWLock::ReaderLock rm (_processor_lock); + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + boost::shared_ptr d; + + if ((d = boost::dynamic_pointer_cast (*i)) != 0) { + d->set_solo_level (soloed ()); + d->set_solo_isolated (solo_isolated()); } } } @@ -580,13 +592,7 @@ Route::set_solo_isolated (bool yn, void *src) if (yn != _solo_isolated) { _solo_isolated = yn; - - /* tell main outs what the solo situation is - */ - - _main_outs->set_solo_level (_solo_level); - _main_outs->set_solo_isolated (_solo_isolated); - + set_delivery_solo (); solo_isolated_changed (src); } } @@ -1679,6 +1685,9 @@ Route::state(bool full_state) order_string += ':'; } node->add_property ("order-keys", order_string); + node->add_property ("self-solo", (_self_solo ? "yes" : "no")); + snprintf (buf, sizeof (buf), "%d", _soloed_by_others); + node->add_property ("soloed-by-others", buf); node->add_child_nocopy (_input->state (full_state)); node->add_child_nocopy (_output->state (full_state)); @@ -1772,9 +1781,13 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) set_processor_state (processor_state); - if ((prop = node.property ("solo_level")) != 0) { - _solo_level = 0; // needed for mod_solo_level() to work - mod_solo_level (atoi (prop->value())); + if ((prop = node.property ("self-solo")) != 0) { + set_self_solo (string_is_affirmative (prop->value())); + } + + if ((prop = node.property ("soloed-by-others")) != 0) { + _soloed_by_others = 0; // needed for mod_solo_by_others () to work + mod_solo_by_others (atoi (prop->value())); } if ((prop = node.property ("solo-isolated")) != 0) { @@ -1795,14 +1808,6 @@ Route::_set_state (const XMLNode& node, int version, bool /*call_base*/) set_active (yn); } - if ((prop = node.property (X_("soloed"))) != 0) { - bool yn = string_is_affirmative (prop->value()); - - /* XXX force reset of solo status */ - - set_solo (yn, this); - } - if ((prop = node.property (X_("meter-point"))) != 0) { _meter_point = MeterPoint (string_2_enum (prop->value (), _meter_point)); if (_meter) { @@ -2830,7 +2835,7 @@ Route::SoloControllable::set_value (float val) float Route::SoloControllable::get_value (void) const { - return route.soloed() ? 1.0f : 0.0f; + return route.self_soloed() ? 1.0f : 0.0f; } void diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 0d5cdcacb5..ce1fbca846 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -2426,7 +2426,7 @@ Session::route_solo_changed (void* /*src*/, boost::weak_ptr wpr) shared_ptr r = routes.reader (); int32_t delta; - if (route->soloed()) { + if (route->self_soloed()) { delta = 1; } else { delta = -1; @@ -2437,27 +2437,30 @@ Session::route_solo_changed (void* /*src*/, boost::weak_ptr wpr) */ solo_update_disabled = true; + for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { bool via_sends_only; - if ((*i)->feeds (route, &via_sends_only) && !(*i)->is_hidden() && !(*i)->is_master() && !(*i)->is_control()) { + + if ((*i) == route || !(*i)->solo_isolated() || !(*i)->is_master() || !(*i)->is_control() || (*i)->is_hidden()) { + continue; + } else if ((*i)->feeds (route, &via_sends_only)) { if (!via_sends_only) { - /* do it */ - (*i)->mod_solo_level (delta); - } - } + (*i)->mod_solo_by_others (delta); + } + } } /* make sure master is never muted by solo */ - if (_master_out && route != _master_out && _master_out->solo_level() == 0 && !_master_out->soloed()) { - _master_out->mod_solo_level (1); - } - + if (_master_out && route != _master_out && _master_out->soloed_by_others() == 0 && !_master_out->soloed()) { + _master_out->mod_solo_by_others (1); + } + /* ditto for control outs make sure master is never muted by solo */ - if (_control_out && route != _control_out && _control_out && _control_out->solo_level() == 0) { - _control_out->mod_solo_level (1); + if (_control_out && route != _control_out && _control_out && _control_out->soloed_by_others() == 0) { + _control_out->mod_solo_by_others (1); } solo_update_disabled = false; @@ -2478,7 +2481,7 @@ Session::update_route_solo_state (boost::shared_ptr r) } for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->is_master() && !(*i)->is_control() && !(*i)->is_hidden() && (*i)->soloed()) { + if (!(*i)->is_master() && !(*i)->is_control() && !(*i)->is_hidden() && (*i)->self_soloed()) { something_soloed = true; break; } diff --git a/libs/gtkmm2ext/wscript b/libs/gtkmm2ext/wscript index 7c8f4cda99..5ca8d5344d 100644 --- a/libs/gtkmm2ext/wscript +++ b/libs/gtkmm2ext/wscript @@ -22,6 +22,7 @@ gtkmm2ext_sources = [ 'auto_spin.cc', 'barcontroller.cc', 'binding_proxy.cc', + 'cell_renderer_pixbuf_multi.cc', 'cell_renderer_pixbuf_toggle.cc', 'choice.cc', 'click_box.cc',