diff --git a/gtk2_ardour/add_route_dialog.h b/gtk2_ardour/add_route_dialog.h index 51fccce4c9..f10fc11292 100644 --- a/gtk2_ardour/add_route_dialog.h +++ b/gtk2_ardour/add_route_dialog.h @@ -54,7 +54,7 @@ class AddRouteDialog : public ArdourDialog MidiTrack, MixedTrack, AudioBus, - MidiBus + MidiBus, VCAMaster, }; TypeWanted type_wanted() const; diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index 1a5e131d3c..65b882fe6d 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -92,7 +92,9 @@ #include "ardour/source_factory.h" #include "ardour/slave.h" #include "ardour/system_exec.h" +#include "ardour/track.h" #include "ardour/vca_manager.h" +#include "ardour/utils.h" #include "LuaBridge/LuaBridge.h" @@ -1570,7 +1572,7 @@ void ARDOUR_UI::count_recenabled_streams (Route& route) { Track* track = dynamic_cast(&route); - if (track && track->record_enabled()) { + if (track && track->rec_enable_control()->get_value()) { rec_enabled_streams += track->n_inputs().n_total(); } } @@ -2083,14 +2085,14 @@ ARDOUR_UI::trx_record_enable_all_tracks () boost::shared_ptr t = boost::dynamic_pointer_cast (*r); assert (t); - if (t->record_enabled()) { + if (t->rec_enable_control()->get_value()) { none_record_enabled = false; break; } } if (none_record_enabled) { - _session->set_record_enabled (rl, true, Session::rt_cleanup); + _session->set_controls (route_list_to_control_list (rl, &Track::rec_enable_control), 1.0, Controllable::NoGroup); } return none_record_enabled; @@ -2388,7 +2390,7 @@ ARDOUR_UI::toggle_record_enable (uint32_t rid) boost::shared_ptr t; if ((t = boost::dynamic_pointer_cast(r)) != 0) { - t->set_record_enabled (!t->record_enabled(), Controllable::UseGroup); + t->rec_enable_control()->set_value (!t->rec_enable_control()->get_value(), Controllable::UseGroup); } } } @@ -5587,12 +5589,6 @@ void ARDOUR_UI::cancel_solo () { if (_session) { - if (_session->soloing()) { - _session->set_solo (_session->get_routes(), false); - } else if (_session->listening()) { - _session->set_listen (_session->get_routes(), false); - } - _session->clear_all_solo_state (_session->get_routes()); // safeguard, ideally this won't do anything, check the log-window } } diff --git a/gtk2_ardour/audio_streamview.cc b/gtk2_ardour/audio_streamview.cc index 2b0aea34cb..9351a5da1f 100644 --- a/gtk2_ardour/audio_streamview.cc +++ b/gtk2_ardour/audio_streamview.cc @@ -186,7 +186,7 @@ AudioStreamView::setup_rec_box () if (!rec_active && _trackview.session()->record_status() == Session::Recording && - _trackview.track()->record_enabled()) { + _trackview.track()->rec_enable_control()->get_value()) { if (_trackview.audio_track()->mode() == Normal && UIConfiguration::instance().get_show_waveforms_while_recording() && rec_regions.size() == rec_rects.size()) { /* add a new region, but don't bother if they set show-waveforms-while-recording mid-record */ @@ -240,7 +240,7 @@ AudioStreamView::setup_rec_box () } else if (rec_active && (_trackview.session()->record_status() != Session::Recording || - !_trackview.track()->record_enabled())) { + !_trackview.track()->rec_enable_control()->get_value())) { screen_update_connection.disconnect(); rec_active = false; rec_updating = false; diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index f7617e4793..eb84428f92 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -5699,11 +5699,11 @@ Editor::toggle_record_enable () continue; if (first) { - new_state = !rtav->track()->record_enabled(); + new_state = !rtav->track()->rec_enable_control()->get_value(); first = false; } - rtav->track()->set_record_enabled (new_state, Controllable::UseGroup); + rtav->track()->rec_enable_control()->set_value (new_state, Controllable::UseGroup); } } @@ -5712,7 +5712,7 @@ Editor::toggle_solo () { bool new_state = false; bool first = true; - boost::shared_ptr rl (new RouteList); + boost::shared_ptr cl (new ControlList); for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { RouteTimeAxisView *rtav = dynamic_cast(*i); @@ -5726,10 +5726,10 @@ Editor::toggle_solo () first = false; } - rl->push_back (rtav->route()); + cl->push_back (rtav->route()->solo_control()); } - _session->set_solo (rl, new_state, Session::rt_cleanup, Controllable::UseGroup); + _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup); } void @@ -5754,7 +5754,7 @@ Editor::toggle_mute () rl->push_back (rtav->route()); } - _session->set_mute (rl, new_state, Session::rt_cleanup, Controllable::UseGroup); + _session->set_controls (route_list_to_control_list (rl, &Route::mute_control), new_state, Controllable::UseGroup); } void diff --git a/gtk2_ardour/editor_routes.cc b/gtk2_ardour/editor_routes.cc index 6197e03629..9a2153f99f 100644 --- a/gtk2_ardour/editor_routes.cc +++ b/gtk2_ardour/editor_routes.cc @@ -31,6 +31,8 @@ #include "ardour/midi_track.h" #include "ardour/route.h" #include "ardour/session.h" +#include "ardour/solo_isolate_control.h" +#include "ardour/utils.h" #include "gtkmm2ext/cell_renderer_pixbuf_multi.h" #include "gtkmm2ext/cell_renderer_pixbuf_toggle.h" @@ -422,11 +424,8 @@ EditorRoutes::on_tv_rec_enable_changed (std::string const & path_string) RouteTimeAxisView *rtv = dynamic_cast (tv); if (rtv && rtv->track()) { - DisplaySuspender ds; - boost::shared_ptr rl (new RouteList); // TODO check rec-safe and ... - rl->push_back (rtv->route()); - _session->set_record_enabled (rl, !rtv->track()->record_enabled(), Session::rt_cleanup); + _session->set_control (rtv->track()->rec_enable_control(), !rtv->track()->rec_enable_control()->get_value(), Controllable::UseGroup); } } @@ -455,9 +454,7 @@ EditorRoutes::on_tv_mute_enable_toggled (std::string const & path_string) RouteTimeAxisView *rtv = dynamic_cast (tv); if (rtv != 0) { - boost::shared_ptr rl (new RouteList); - rl->push_back (rtv->route()); - _session->set_mute (rl, !rtv->route()->muted(), Session::rt_cleanup); + _session->set_control (rtv->route()->mute_control(), rtv->route()->mute_control()->get_value() ? 0.0 : 1.0, Controllable::UseGroup); } } @@ -471,13 +468,15 @@ EditorRoutes::on_tv_solo_enable_toggled (std::string const & path_string) RouteTimeAxisView* rtv = dynamic_cast (tv); if (rtv != 0) { - boost::shared_ptr rl (new RouteList); - rl->push_back (rtv->route()); + bool newval; + if (Config->get_solo_control_is_listen_control()) { - _session->set_listen (rl, !rtv->route()->listening_via_monitor(), Session::rt_cleanup); + newval = !rtv->route()->listening_via_monitor(); } else { - _session->set_solo (rl, !rtv->route()->self_soloed(), Session::rt_cleanup); + newval = !rtv->route()->self_soloed(); } + + rtv->route()->solo_control()->set_value (newval ? 1.0 : 0.0, Controllable::UseGroup); } } @@ -491,7 +490,7 @@ EditorRoutes::on_tv_solo_isolate_toggled (std::string const & path_string) RouteTimeAxisView* rtv = dynamic_cast (tv); if (rtv) { - rtv->route()->set_solo_isolated (!rtv->route()->solo_isolated(), Controllable::UseGroup); + rtv->route()->solo_isolate_control()->set_value (rtv->route()->solo_isolate_control()->get_value() ? 0.0 : 1.0, Controllable::UseGroup); } } @@ -505,7 +504,7 @@ EditorRoutes::on_tv_solo_safe_toggled (std::string const & path_string) RouteTimeAxisView* rtv = dynamic_cast (tv); if (rtv) { - rtv->route()->set_solo_safe (!rtv->route()->solo_safe(), Controllable::UseGroup); + rtv->route()->solo_safe_control()->set_value (rtv->route()->solo_safe_control()->get_value() ? 0.0 : 1.0, Controllable::UseGroup); } } @@ -734,8 +733,8 @@ EditorRoutes::routes_added (list routes) row[_columns.mute_state] = (*x)->route()->muted() ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off; row[_columns.solo_state] = RouteUI::solo_active_state ((*x)->route()); row[_columns.solo_visible] = !(*x)->route()->is_master (); - row[_columns.solo_isolate_state] = (*x)->route()->solo_isolated(); - row[_columns.solo_safe_state] = (*x)->route()->solo_safe(); + row[_columns.solo_isolate_state] = (*x)->route()->solo_isolate_control()->solo_isolated(); + row[_columns.solo_safe_state] = (*x)->route()->solo_safe_control()->solo_safe(); row[_columns.name_editable] = true; boost::weak_ptr wr ((*x)->route()); @@ -745,8 +744,8 @@ EditorRoutes::routes_added (list routes) if ((*x)->is_track()) { boost::shared_ptr t = boost::dynamic_pointer_cast ((*x)->route()); - t->RecordEnableChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context()); - t->RecordSafeChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context()); + t->rec_enable_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context()); + t->rec_safe_control()->Changed.connect (*this, MISSING_INVALIDATOR, boost::bind (&EditorRoutes::update_rec_display, this), gui_context()); } if ((*x)->is_midi_track()) { @@ -1280,25 +1279,27 @@ EditorRoutes::key_press (GdkEventKey* ev) case 'm': if (get_relevant_routes (rl)) { - _session->set_mute (rl, !rl->front()->muted(), Session::rt_cleanup); + _session->set_controls (route_list_to_control_list (rl, &Route::mute_control), rl->front()->muted() ? 0.0 : 1.0, Controllable::NoGroup); } return true; break; case 's': if (get_relevant_routes (rl)) { - if (Config->get_solo_control_is_listen_control()) { - _session->set_listen (rl, !rl->front()->listening_via_monitor(), Session::rt_cleanup); - } else { - _session->set_solo (rl, !rl->front()->self_soloed(), Session::rt_cleanup); - } + _session->set_controls (route_list_to_control_list (rl, &Route::solo_control), rl->front()->self_soloed() ? 0.0 : 1.0, Controllable::NoGroup); } return true; break; case 'r': if (get_relevant_routes (rl)) { - _session->set_record_enabled (rl, !rl->front()->record_enabled(), Session::rt_cleanup); + for (RouteList::const_iterator r = rl->begin(); r != rl->end(); ++r) { + boost::shared_ptr t = boost::dynamic_pointer_cast (*r); + if (t) { + _session->set_controls (route_list_to_control_list (rl, &Track::rec_enable_control), !t->rec_enable_control()->get_value(), Controllable::NoGroup); + break; + } + } } break; @@ -1645,10 +1646,13 @@ EditorRoutes::idle_update_mute_rec_solo_etc() (*i)[_columns.solo_isolate_state] = RouteUI::solo_isolate_active_state (route) ? 1 : 0; (*i)[_columns.solo_safe_state] = RouteUI::solo_safe_active_state (route) ? 1 : 0; (*i)[_columns.active] = route->active (); - if (boost::dynamic_pointer_cast (route)) { + + boost::shared_ptr trk (boost::dynamic_pointer_cast(route)); + + if (trk) { boost::shared_ptr mt = boost::dynamic_pointer_cast (route); - if (route->record_enabled()) { + if (trk->rec_enable_control()->get_value()) { if (_session->record_status() == Session::Recording) { (*i)[_columns.rec_state] = 1; } else { @@ -1660,8 +1664,8 @@ EditorRoutes::idle_update_mute_rec_solo_etc() (*i)[_columns.rec_state] = 0; } - (*i)[_columns.rec_safe] = route->record_safe () ? 1 : 0; - (*i)[_columns.name_editable] = !route->record_enabled (); + (*i)[_columns.rec_safe] = !trk->rec_safe_control()->get_value(); + (*i)[_columns.name_editable] = !trk->rec_enable_control()->get_value(); } } diff --git a/gtk2_ardour/gain_meter.cc b/gtk2_ardour/gain_meter.cc index b38e653bac..baf4193d27 100644 --- a/gtk2_ardour/gain_meter.cc +++ b/gtk2_ardour/gain_meter.cc @@ -479,10 +479,10 @@ GainMeterBase::gain_activated () /* clamp to displayable values */ if (_data_type == DataType::AUDIO) { f = min (f, 6.0f); - _control->set_value (dB_to_coefficient(f), Controllable::NoGroup); + _control->set_value (dB_to_coefficient(f), Controllable::UseGroup); } else { f = min (fabs (f), 2.0f); - _control->set_value (f, Controllable::NoGroup); + _control->set_value (f, Controllable::UseGroup); } if (gain_display.has_focus()) { @@ -541,11 +541,7 @@ GainMeterBase::fader_moved () value = gain_adjustment.get_value(); } - if (_route && _control == _route->gain_control()) { - _route->set_gain (value, Controllable::UseGroup); - } else { - _control->set_value (value, Controllable::NoGroup); - } + _control->set_value (value, Controllable::UseGroup); } show_gain (); diff --git a/gtk2_ardour/group_tabs.cc b/gtk2_ardour/group_tabs.cc index 0e429e562a..a930fbbce4 100644 --- a/gtk2_ardour/group_tabs.cc +++ b/gtk2_ardour/group_tabs.cc @@ -366,7 +366,8 @@ GroupTabs::new_from_rec_enabled () RouteList rec_enabled; for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - if ((*i)->record_enabled()) { + boost::shared_ptr trk (boost::dynamic_pointer_cast (*i)); + if (trk && trk->rec_enable_control()->get_value()) { rec_enabled.push_back (*i); } } diff --git a/gtk2_ardour/meter_strip.cc b/gtk2_ardour/meter_strip.cc index 82acd1dad5..b324a846ff 100644 --- a/gtk2_ardour/meter_strip.cc +++ b/gtk2_ardour/meter_strip.cc @@ -366,7 +366,7 @@ MeterStrip::set_button_names() { mute_button->set_text (S_("Mute|M")); - if (_route && _route->solo_safe()) { + if (_route && _route->solo_safe_control()->solo_safe()) { solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive)); } else { solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive)); diff --git a/gtk2_ardour/midi_streamview.cc b/gtk2_ardour/midi_streamview.cc index 9258cf027d..e369c5842d 100644 --- a/gtk2_ardour/midi_streamview.cc +++ b/gtk2_ardour/midi_streamview.cc @@ -440,7 +440,7 @@ MidiStreamView::setup_rec_box () if (!rec_active && _trackview.session()->record_status() == Session::Recording && - _trackview.track()->record_enabled()) { + _trackview.track()->rec_enable_control()->get_value()) { if (UIConfiguration::instance().get_show_waveforms_while_recording() && rec_regions.size() == rec_rects.size()) { @@ -512,7 +512,7 @@ MidiStreamView::setup_rec_box () } else if (rec_active && (_trackview.session()->record_status() != Session::Recording || - !_trackview.track()->record_enabled())) { + !_trackview.track()->rec_enable_control()->get_value())) { screen_update_connection.disconnect(); rec_active = false; rec_updating = false; diff --git a/gtk2_ardour/mixer_actor.cc b/gtk2_ardour/mixer_actor.cc index 7ffcc6f3a8..403f198d4f 100644 --- a/gtk2_ardour/mixer_actor.cc +++ b/gtk2_ardour/mixer_actor.cc @@ -172,7 +172,7 @@ printf("setting gain to unity (?)"); BOOST_FOREACH(RouteUI* r, _route_targets) { boost::shared_ptr rp = r->route(); if (rp) { - rp->set_gain (1.0, Controllable::NoGroup); + rp->gain_control()->set_value (1.0, Controllable::NoGroup); } } } diff --git a/gtk2_ardour/mixer_strip.cc b/gtk2_ardour/mixer_strip.cc index 4b4b5f1110..5c13030127 100644 --- a/gtk2_ardour/mixer_strip.cc +++ b/gtk2_ardour/mixer_strip.cc @@ -950,7 +950,7 @@ MixerStrip::input_press (GdkEventButton *ev) return true; } - if (_session->actively_recording() && _route->record_enabled()) + if (_session->actively_recording() && is_track() && track()->rec_enable_control()->get_value()) return true; switch (ev->button) { @@ -1665,7 +1665,7 @@ MixerStrip::name_button_button_press (GdkEventButton* ev) list_route_operations (); /* do not allow rename if the track is record-enabled */ - rename_menu_item->set_sensitive (!_route->record_enabled()); + rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value()); route_ops_menu->popup (1, ev->time); return true; @@ -1681,7 +1681,7 @@ MixerStrip::name_button_button_release (GdkEventButton* ev) list_route_operations (); /* do not allow rename if the track is record-enabled */ - rename_menu_item->set_sensitive (!_route->record_enabled()); + rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value()); route_ops_menu->popup (1, ev->time); } @@ -1695,7 +1695,7 @@ MixerStrip::number_button_button_press (GdkEventButton* ev) list_route_operations (); /* do not allow rename if the track is record-enabled */ - rename_menu_item->set_sensitive (!_route->record_enabled()); + rename_menu_item->set_sensitive (!is_track() || !track()->rec_enable_control()->get_value()); route_ops_menu->popup (1, ev->time); return true; @@ -2141,7 +2141,7 @@ MixerStrip::set_button_names () monitor_section_button->set_text (_("Mon")); } - if (_route && _route->solo_safe()) { + if (_route && _route->solo_safe_control()->solo_safe()) { solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive)); } else { solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive)); @@ -2170,7 +2170,7 @@ MixerStrip::set_button_names () monitor_section_button->set_text (S_("Mon|O")); } - if (_route && _route->solo_safe()) { + if (_route && _route->solo_safe_control()->solo_safe()) { solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive)); } else { solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive)); diff --git a/gtk2_ardour/monitor_section.cc b/gtk2_ardour/monitor_section.cc index f24cab7c3e..e14b571723 100644 --- a/gtk2_ardour/monitor_section.cc +++ b/gtk2_ardour/monitor_section.cc @@ -36,6 +36,7 @@ #include "ardour/monitor_processor.h" #include "ardour/port.h" #include "ardour/route.h" +#include "ardour/solo_isolate_control.h" #include "ardour/user_bundle.h" #include "ardour/plugin_manager.h" @@ -1246,7 +1247,7 @@ MonitorSection::cancel_isolate (GdkEventButton*) { if (_session) { boost::shared_ptr rl (_session->get_routes ()); - _session->set_solo_isolated (rl, false, Session::rt_cleanup, Controllable::NoGroup); + _session->set_controls (route_list_to_control_list (rl, &Route::solo_isolate_control), 0.0, Controllable::NoGroup); } return true; diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index 8c475f5acc..f291af5dc5 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -2493,7 +2493,11 @@ RouteTimeAxisView::can_edit_name () const { /* we do not allow track name changes if it is record enabled */ - return !_route->record_enabled(); + boost::shared_ptr trk (boost::dynamic_pointer_cast (_route)); + if (!trk) { + return true; + } + return !trk->rec_enable_control()->get_value(); } void @@ -2724,7 +2728,7 @@ RouteTimeAxisView::remove_underlay (StreamView* v) void RouteTimeAxisView::set_button_names () { - if (_route && _route->solo_safe()) { + if (_route && _route->solo_safe_control()->solo_safe()) { solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive)); } else { solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() & ~Gtkmm2ext::Insensitive)); diff --git a/gtk2_ardour/route_ui.cc b/gtk2_ardour/route_ui.cc index afaf4561c8..09a9dc8972 100644 --- a/gtk2_ardour/route_ui.cc +++ b/gtk2_ardour/route_ui.cc @@ -34,8 +34,21 @@ #include "ardour/dB.h" #include "ardour/route_group.h" +#include "ardour/solo_isolate_control.h" #include "ardour/vca.h" #include "ardour/vca_manager.h" +#include "ardour/audio_track.h" +#include "ardour/audioengine.h" +#include "ardour/filename_extensions.h" +#include "ardour/midi_track.h" +#include "ardour/monitor_control.h" +#include "ardour/internal_send.h" +#include "ardour/profile.h" +#include "ardour/phase_control.h" +#include "ardour/send.h" +#include "ardour/route.h" +#include "ardour/session.h" +#include "ardour/template_utils.h" #include "ardour_ui.h" #include "editor.h" @@ -55,16 +68,6 @@ #include "timers.h" #include "ui_config.h" -#include "ardour/audio_track.h" -#include "ardour/audioengine.h" -#include "ardour/filename_extensions.h" -#include "ardour/midi_track.h" -#include "ardour/internal_send.h" -#include "ardour/profile.h" -#include "ardour/send.h" -#include "ardour/route.h" -#include "ardour/session.h" -#include "ardour/template_utils.h" #include "i18n.h" using namespace Gtk; @@ -270,10 +273,7 @@ RouteUI::set_route (boost::shared_ptr rp) _route->solo_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context()); _route->solo_safe_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context()); _route->solo_isolate_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::update_solo_display, this), gui_context()); - - if (_route->phase_control()) { - _route->phase_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::polarity_changed, this), gui_context()); - } + _route->phase_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::polarity_changed, this), gui_context()); if (is_track()) { track()->FreezeChange.connect (*this, invalidator (*this), boost::bind (&RouteUI::map_frozen, this), gui_context()); @@ -290,8 +290,8 @@ RouteUI::set_route (boost::shared_ptr rp) if (_session->writable() && is_track()) { boost::shared_ptr t = boost::dynamic_pointer_cast(_route); - t->RecordEnableChanged.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_rec_enable_changed, this), gui_context()); - t->RecordSafeChanged.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_rec_enable_changed, this), gui_context()); + t->rec_enable_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_rec_enable_changed, this), gui_context()); + t->rec_safe_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::route_rec_enable_changed, this), gui_context()); rec_enable_button->show(); rec_enable_button->set_controllable (t->rec_enable_control()); @@ -309,7 +309,7 @@ RouteUI::set_route (boost::shared_ptr rp) if (is_track()) { boost::shared_ptr t = boost::dynamic_pointer_cast(_route); - t->MonitoringChanged.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::monitoring_changed, this), gui_context()); + t->monitoring_control()->Changed.connect (route_connections, invalidator (*this), boost::bind (&RouteUI::monitoring_changed, this, _1, _2), gui_context()); update_monitoring_display (); } @@ -383,7 +383,7 @@ RouteUI::mute_press (GdkEventButton* ev) if (Keyboard::is_button2_event (ev)) { // button2-click is "momentary" - _mute_release = new SoloMuteRelease (_route->muted ()); + _mute_release = new SoloMuteRelease (_route->mute_control()->muted ()); } if (ev->button == 1 || Keyboard::is_button2_event (ev)) { @@ -415,7 +415,7 @@ RouteUI::mute_press (GdkEventButton* ev) } DisplaySuspender ds; - _session->set_mute (copy, !_route->muted()); + _session->set_controls (route_list_to_control_list (copy, &Route::mute_control), _route->muted() ? 0.0 : 1.0, Controllable::UseGroup); } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { @@ -444,7 +444,7 @@ RouteUI::mute_press (GdkEventButton* ev) } DisplaySuspender ds; - _session->set_mute (rl, !_route->muted(), Session::rt_cleanup, Controllable::InverseGroup); + _session->set_controls (route_list_to_control_list (rl, &Route::mute_control), _route->muted() ? 0.0 : 1.0, Controllable::InverseGroup); } } else { @@ -458,7 +458,7 @@ RouteUI::mute_press (GdkEventButton* ev) _mute_release->routes = rl; } - _session->set_mute (rl, !_route->muted()); + _session->set_control (_route->mute_control(), _route->muted() ? 0.0 : 1.0, Controllable::UseGroup); } } @@ -472,7 +472,7 @@ RouteUI::mute_release (GdkEventButton* /*ev*/) { if (_mute_release){ DisplaySuspender ds; - _session->set_mute (_mute_release->routes, _mute_release->active, Session::rt_cleanup, Controllable::UseGroup); + _session->set_controls (route_list_to_control_list (_mute_release->routes, &Route::mute_control), _mute_release->active, Controllable::UseGroup); delete _mute_release; _mute_release = 0; } @@ -572,11 +572,7 @@ RouteUI::solo_press(GdkEventButton* ev) } DisplaySuspender ds; - if (Config->get_solo_control_is_listen_control()) { - _session->set_listen (_session->get_routes(), !_route->listening_via_monitor(), Session::rt_cleanup, Controllable::UseGroup); - } else { - _session->set_solo (_session->get_routes(), !_route->self_soloed(), Session::rt_cleanup, Controllable::UseGroup); - } + _session->set_controls (route_list_to_control_list (_session->get_routes(), &Route::solo_control), !_route->solo_control()->get_value(), Controllable::UseGroup); } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) { @@ -600,14 +596,16 @@ RouteUI::solo_press(GdkEventButton* ev) /* ??? we need a just_one_listen() method */ } else { DisplaySuspender ds; - _session->set_just_one_solo (_route, true); + boost::shared_ptr cl (new ControlList); + cl->push_back (_route->solo_control()); + _session->set_controls (cl, 1.0, Controllable::NoGroup); } } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) { // shift-click: toggle solo isolated status - _route->set_solo_isolated (!_route->solo_isolated(), Controllable::UseGroup); + _route->solo_isolate_control()->set_value (_route->solo_isolate_control()->get_value() ? 0.0 : 1.0, Controllable::UseGroup); delete _solo_release; _solo_release = 0; @@ -647,11 +645,7 @@ RouteUI::solo_press(GdkEventButton* ev) DisplaySuspender ds; - if (Config->get_solo_control_is_listen_control()) { - _session->set_listen (rl, !_route->listening_via_monitor(), Session::rt_cleanup, Controllable::InverseGroup); - } else { - _session->set_solo (rl, !_route->self_soloed(), Session::rt_cleanup, Controllable::InverseGroup); - } + _session->set_controls (route_list_to_control_list (rl, &Route::solo_control), !_route->self_soloed(), Controllable::InverseGroup); } delete _solo_release; @@ -669,11 +663,7 @@ RouteUI::solo_press(GdkEventButton* ev) } DisplaySuspender ds; - if (Config->get_solo_control_is_listen_control()) { - _session->set_listen (rl, !_route->listening_via_monitor()); - } else { - _session->set_solo (rl, !_route->self_soloed()); - } + _session->set_controls (route_list_to_control_list (rl, &Route::solo_control), !_route->self_soloed(), Controllable::UseGroup); } } } @@ -690,11 +680,7 @@ RouteUI::solo_release (GdkEventButton* /*ev*/) } else { DisplaySuspender ds; - if (Config->get_solo_control_is_listen_control()) { - _session->set_listen (_solo_release->routes, _solo_release->active, Session::rt_cleanup, Controllable::UseGroup); - } else { - _session->set_solo (_solo_release->routes, _solo_release->active, Session::rt_cleanup, Controllable::UseGroup); - } + _session->set_controls (route_list_to_control_list (_solo_release->routes, &Route::solo_control), _solo_release->active ? 1.0 : 0.0, Controllable::UseGroup); } delete _solo_release; @@ -741,7 +727,7 @@ RouteUI::rec_enable_press(GdkEventButton* ev) } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) { DisplaySuspender ds; - _session->set_record_enabled (_session->get_routes(), !_route->record_enabled()); + _session->set_controls (route_list_to_control_list (_session->get_routes(), &Track::rec_enable_control), !track()->rec_enable_control()->get_value(), Controllable::NoGroup); } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) { @@ -757,7 +743,7 @@ RouteUI::rec_enable_press(GdkEventButton* ev) rl->push_back (_route); DisplaySuspender ds; - _session->set_record_enabled (rl, !_route->record_enabled(), Session::rt_cleanup, Controllable::InverseGroup); + _session->set_controls (route_list_to_control_list (rl, &Track::rec_enable_control), !track()->rec_enable_control()->get_value(), Controllable::InverseGroup); } } else if (Keyboard::is_context_menu_event (ev)) { @@ -766,10 +752,8 @@ RouteUI::rec_enable_press(GdkEventButton* ev) } else { - boost::shared_ptr rl (new RouteList); - rl->push_back (route()); - DisplaySuspender ds; - _session->set_record_enabled (rl, !_route->record_enabled()); + boost::shared_ptr trk = track(); + _session->set_control (trk->rec_enable_control(), !trk->rec_enable_control()->get_value(), Controllable::UseGroup); } } @@ -777,7 +761,7 @@ RouteUI::rec_enable_press(GdkEventButton* ev) } void -RouteUI::monitoring_changed () +RouteUI::monitoring_changed (bool, Controllable::GroupControlDisposition) { update_monitoring_display (); } @@ -797,7 +781,7 @@ RouteUI::update_monitoring_display () MonitorState ms = t->monitoring_state(); - if (t->monitoring_choice() & MonitorInput) { + if (t->monitoring_control()->monitoring_choice() & MonitorInput) { monitor_input_button->set_active_state (Gtkmm2ext::ExplicitActive); } else { if (ms & MonitoringInput) { @@ -807,7 +791,7 @@ RouteUI::update_monitoring_display () } } - if (t->monitoring_choice() & MonitorDisk) { + if (t->monitoring_control()->monitoring_choice() & MonitorDisk) { monitor_disk_button->set_active_state (Gtkmm2ext::ExplicitActive); } else { if (ms & MonitoringDisk) { @@ -863,8 +847,8 @@ RouteUI::monitor_release (GdkEventButton* ev, MonitorChoice monitor_choice) signal together, which requires yet more buffers. */ - if (t->monitoring_choice() & monitor_choice) { - mc = MonitorChoice (t->monitoring_choice() & ~monitor_choice); + if (t->monitoring_control()->monitoring_choice() & monitor_choice) { + mc = MonitorChoice (t->monitoring_control()->monitoring_choice() & ~monitor_choice); } else { /* this line will change when the options are non-orthogonal */ // mc = MonitorChoice (t->monitoring_choice() | monitor_choice); @@ -887,7 +871,7 @@ RouteUI::monitor_release (GdkEventButton* ev, MonitorChoice monitor_choice) } DisplaySuspender ds; - _session->set_monitoring (rl, mc, Session::rt_cleanup, Controllable::UseGroup); + _session->set_controls (route_list_to_control_list (rl, &Route::monitoring_control), (double) mc, Controllable::UseGroup); return false; } @@ -912,7 +896,9 @@ RouteUI::build_record_menu () } if (step_edit_item) { - step_edit_item->set_sensitive (!_route->record_enabled()); + if (track()->rec_enable_control()->get_value()) { + step_edit_item->set_sensitive (false); + } step_edit_item->set_active (midi_track()->step_editing()); } if (rec_safe_item) { @@ -924,7 +910,7 @@ RouteUI::build_record_menu () void RouteUI::toggle_step_edit () { - if (!is_midi_track() || _route->record_enabled()) { + if (!is_midi_track() || track()->rec_enable_control()->get_value()) { return; } @@ -1168,7 +1154,7 @@ RouteUI::solo_isolate_active_state (boost::shared_ptr r) return Gtkmm2ext::Off; } - if (r->solo_isolated()) { + if (r->solo_isolate_control()->solo_isolated()) { return Gtkmm2ext::ExplicitActive; } else { return Gtkmm2ext::Off; @@ -1182,7 +1168,7 @@ RouteUI::solo_safe_active_state (boost::shared_ptr r) return Gtkmm2ext::Off; } - if (r->solo_safe()) { + if (r->solo_safe_control()->solo_safe()) { return Gtkmm2ext::ExplicitActive; } else { return Gtkmm2ext::Off; @@ -1192,13 +1178,13 @@ RouteUI::solo_safe_active_state (boost::shared_ptr r) void RouteUI::update_solo_display () { - bool yn = _route->solo_safe (); + bool yn = _route->solo_safe_control()->solo_safe (); if (solo_safe_check && solo_safe_check->get_active() != yn) { solo_safe_check->set_active (yn); } - yn = _route->solo_isolated (); + yn = _route->solo_isolate_control()->solo_isolated (); if (solo_isolated_check && solo_isolated_check->get_active() != yn) { solo_isolated_check->set_active (yn); @@ -1207,7 +1193,7 @@ RouteUI::update_solo_display () set_button_names (); if (solo_isolated_led) { - if (_route->solo_isolated()) { + if (_route->solo_isolate_control()->solo_isolated()) { solo_isolated_led->set_active_state (Gtkmm2ext::ExplicitActive); } else { solo_isolated_led->unset_active_state (); @@ -1215,7 +1201,7 @@ RouteUI::update_solo_display () } if (solo_safe_led) { - if (_route->solo_safe()) { + if (_route->solo_safe_control()->solo_safe()) { solo_safe_led->set_active_state (Gtkmm2ext::ExplicitActive); } else { solo_safe_led->unset_active_state (); @@ -1248,7 +1234,7 @@ RouteUI::mute_active_state (Session* s, boost::shared_ptr r) if (r->muted ()) { /* full mute */ return Gtkmm2ext::ExplicitActive; - } else if (r->muted_by_others ()) { + } else if (r->muted_by_others_soloing ()) { /* this will reflect both solo mutes AND master mutes */ return Gtkmm2ext::ImplicitActive; } else { @@ -1261,10 +1247,11 @@ RouteUI::mute_active_state (Session* s, boost::shared_ptr r) if (r->muted()) { /* full mute */ return Gtkmm2ext::ExplicitActive; - } else if (r->mute_master()->muted_by_others()) { + } else if (r->muted_by_others()) { /* note the direct use of MuteMaster API here. We are not interested in showing - others-soloed-so-this-muted status in this branch. + others-soloed-so-this-muted status in this + conditional branch. */ return Gtkmm2ext::ImplicitActive; } else { @@ -1335,11 +1322,16 @@ RouteUI::blink_rec_display (bool blinkOn) if (!rec_enable_button || !_route) { return; } + if (boost::dynamic_pointer_cast(_current_delivery)) { return; } - if (_route->record_enabled()) { + if (!is_track()) { + return; + } + + if (track()->rec_enable_control()->get_value()) { switch (_session->record_status ()) { case Session::Recording: rec_enable_button->set_active_state (Gtkmm2ext::ExplicitActive); @@ -1347,12 +1339,12 @@ RouteUI::blink_rec_display (bool blinkOn) case Session::Disabled: case Session::Enabled: - if ( UIConfiguration::instance().get_blink_rec_arm() ) - rec_enable_button->set_active_state ( blinkOn ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off ); - else - rec_enable_button->set_active_state ( ImplicitActive ); + if (UIConfiguration::instance().get_blink_rec_arm()) { + rec_enable_button->set_active_state ( blinkOn ? Gtkmm2ext::ExplicitActive : Gtkmm2ext::Off ); + } else { + rec_enable_button->set_active_state ( ImplicitActive ); + } break; - } if (step_edit_item) { @@ -1381,14 +1373,14 @@ RouteUI::build_solo_menu (void) Gtk::CheckMenuItem* check; check = new Gtk::CheckMenuItem(_("Solo Isolate")); - check->set_active (_route->solo_isolated()); + check->set_active (_route->solo_isolate_control()->solo_isolated()); check->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_solo_isolated), check)); items.push_back (CheckMenuElem(*check)); solo_isolated_check = dynamic_cast(&items.back()); check->show_all(); check = new Gtk::CheckMenuItem(_("Solo Safe")); - check->set_active (_route->solo_safe()); + check->set_active (_route->solo_safe_control()->solo_safe()); check->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::toggle_solo_safe), check)); items.push_back (CheckMenuElem(*check)); solo_safe_check = dynamic_cast(&items.back()); @@ -1442,16 +1434,16 @@ RouteUI::build_mute_menu(void) void RouteUI::init_mute_menu(MuteMaster::MutePoint mp, Gtk::CheckMenuItem* check) { - check->set_active (_route->mute_points() & mp); + check->set_active (_route->mute_control()->mute_points() & mp); } void RouteUI::toggle_mute_menu(MuteMaster::MutePoint mp, Gtk::CheckMenuItem* check) { if (check->get_active()) { - _route->set_mute_points (MuteMaster::MutePoint (_route->mute_points() | mp)); + _route->mute_control()->set_mute_points (MuteMaster::MutePoint (_route->mute_control()->mute_points() | mp)); } else { - _route->set_mute_points (MuteMaster::MutePoint (_route->mute_points() & ~mp)); + _route->mute_control()->set_mute_points (MuteMaster::MutePoint (_route->mute_control()->mute_points() & ~mp)); } } @@ -1461,7 +1453,7 @@ RouteUI::muting_change () ENSURE_GUI_THREAD (*this, &RouteUI::muting_change) bool yn; - MuteMaster::MutePoint current = _route->mute_points (); + MuteMaster::MutePoint current = _route->mute_control()->mute_points (); yn = (current & MuteMaster::PreFader); @@ -1496,7 +1488,7 @@ RouteUI::solo_isolate_button_release (GdkEventButton* ev) } bool view = solo_isolated_led->active_state(); - bool model = _route->solo_isolated(); + bool model = _route->solo_isolate_control()->solo_isolated(); /* called BEFORE the view has changed */ @@ -1506,11 +1498,11 @@ RouteUI::solo_isolate_button_release (GdkEventButton* ev) if (model) { /* disable isolate for all routes */ DisplaySuspender ds; - _session->set_solo_isolated (_session->get_routes(), false, Session::rt_cleanup, Controllable::NoGroup); + _session->set_controls (route_list_to_control_list (_session->get_routes(), &Route::solo_isolate_control), 0.0, Controllable::NoGroup); } else { /* enable isolate for all routes */ DisplaySuspender ds; - _session->set_solo_isolated (_session->get_routes(), true, Session::rt_cleanup, Controllable::NoGroup); + _session->set_controls (route_list_to_control_list (_session->get_routes(), &Route::solo_isolate_control), 1.0, Controllable::NoGroup); } } else { @@ -1522,7 +1514,7 @@ RouteUI::solo_isolate_button_release (GdkEventButton* ev) boost::shared_ptr rl (new RouteList); rl->push_back (_route); DisplaySuspender ds; - _session->set_solo_isolated (rl, !view, Session::rt_cleanup, Controllable::NoGroup); + _session->set_controls (route_list_to_control_list (rl, &Route::solo_isolate_control), view ? 0.0 : 1.0, Controllable::NoGroup); } } } @@ -1538,7 +1530,7 @@ RouteUI::solo_safe_button_release (GdkEventButton* ev) } bool view = solo_safe_led->active_state(); - bool model = _route->solo_safe(); + bool model = _route->solo_safe_control()->solo_safe(); if (ev->button == 1) { if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) { @@ -1547,20 +1539,20 @@ RouteUI::solo_safe_button_release (GdkEventButton* ev) /* disable solo safe for all routes */ DisplaySuspender ds; for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - (*i)->set_solo_safe (false, Controllable::NoGroup); + (*i)->solo_safe_control()->set_value (0.0, Controllable::NoGroup); } } else { /* enable solo safe for all routes */ DisplaySuspender ds; for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - (*i)->set_solo_safe (true, Controllable::NoGroup); + (*i)->solo_safe_control()->set_value (1.0, Controllable::NoGroup); } } } else { if (model == view) { /* flip just this route */ - _route->set_solo_safe (!view, Controllable::NoGroup); + _route->solo_safe_control()->set_value (view ? 0.0 : 1.0, Controllable::NoGroup); } } } @@ -1572,19 +1564,19 @@ void RouteUI::toggle_solo_isolated (Gtk::CheckMenuItem* check) { bool view = check->get_active(); - bool model = _route->solo_isolated(); + bool model = _route->solo_isolate_control()->solo_isolated(); /* called AFTER the view has changed */ if (model != view) { - _route->set_solo_isolated (view, Controllable::UseGroup); + _route->solo_isolate_control()->set_value (view ? 1.0 : 0.0, Controllable::UseGroup); } } void RouteUI::toggle_solo_safe (Gtk::CheckMenuItem* check) { - _route->set_solo_safe (check->get_active(), Controllable::UseGroup); + _route->solo_safe_control()->set_value (check->get_active() ? 1.0 : 0.0, Controllable::UseGroup); } /** Ask the user to choose a colour, and then apply that color to my route @@ -2022,25 +2014,25 @@ RouteUI::parameter_changed (string const & p) void RouteUI::step_gain_up () { - _route->set_gain (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) + 0.1), Controllable::UseGroup); + _route->gain_control()->set_value (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) + 0.1), Controllable::UseGroup); } void RouteUI::page_gain_up () { - _route->set_gain (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) + 0.5), Controllable::UseGroup); + _route->gain_control()->set_value (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) + 0.5), Controllable::UseGroup); } void RouteUI::step_gain_down () { - _route->set_gain (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) - 0.1), Controllable::UseGroup); + _route->gain_control()->set_value (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) - 0.1), Controllable::UseGroup); } void RouteUI::page_gain_down () { - _route->set_gain (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) - 0.5), Controllable::UseGroup); + _route->gain_control()->set_value (dB_to_coefficient (accurate_coefficient_to_dB (_route->gain_control()->get_value()) - 0.5), Controllable::UseGroup); } void @@ -2158,9 +2150,9 @@ RouteUI::set_invert_button_state () ArdourButton* b = _invert_buttons.front (); - if (_route->phase_invert().count() == _route->phase_invert().size()) { + if (_route->phase_control()->count() == _route->phase_control()->size()) { b->set_active_state (Gtkmm2ext::ExplicitActive); - } else if (_route->phase_invert().any()) { + } else if (_route->phase_control()->any()) { b->set_active_state (Gtkmm2ext::ImplicitActive); } else { b->set_active_state (Gtkmm2ext::Off); @@ -2172,7 +2164,7 @@ RouteUI::set_invert_button_state () int j = 0; for (vector::iterator i = _invert_buttons.begin(); i != _invert_buttons.end(); ++i, ++j) { - (*i)->set_active (_route->phase_invert (j)); + (*i)->set_active (_route->phase_control()->inverted (j)); } } @@ -2185,7 +2177,7 @@ RouteUI::invert_release (GdkEventButton* ev, uint32_t i) uint32_t const N = _route->input()->n_ports().n_audio (); if (N <= _max_invert_buttons) { /* left-click inverts phase so long as we have a button per channel */ - _route->set_phase_invert (i, !_invert_buttons[i]->get_active()); + _route->phase_control()->set_phase_invert (i, !_invert_buttons[i]->get_active()); return false; } } @@ -2216,7 +2208,7 @@ RouteUI::invert_press (GdkEventButton* ev) items.push_back (CheckMenuElem (string_compose (X_("Ø%1"), i + 1), sigc::bind (sigc::mem_fun (*this, &RouteUI::invert_menu_toggled), i))); Gtk::CheckMenuItem* e = dynamic_cast (&items.back ()); ++_i_am_the_modifier; - e->set_active (_route->phase_invert (i)); + e->set_active (_route->phase_control()->inverted (i)); --_i_am_the_modifier; } @@ -2232,7 +2224,8 @@ RouteUI::invert_menu_toggled (uint32_t c) return; } - _route->set_phase_invert (c, !_route->phase_invert (c)); + + _route->phase_control()->set_phase_invert (c, !_route->phase_control()->inverted (c)); } void diff --git a/gtk2_ardour/route_ui.h b/gtk2_ardour/route_ui.h index 788c65101b..8be2c227ff 100644 --- a/gtk2_ardour/route_ui.h +++ b/gtk2_ardour/route_ui.h @@ -156,7 +156,7 @@ class RouteUI : public virtual AxisView bool monitor_input_release(GdkEventButton*); bool monitor_disk_press(GdkEventButton*); bool monitor_disk_release(GdkEventButton*); - void monitoring_changed (); + void monitoring_changed (bool, PBD::Controllable::GroupControlDisposition); void update_monitoring_display (); void edit_input_configuration (); diff --git a/gtk2_ardour/streamview.cc b/gtk2_ardour/streamview.cc index 28dd1ab8f1..bf14eaf2e2 100644 --- a/gtk2_ardour/streamview.cc +++ b/gtk2_ardour/streamview.cc @@ -78,7 +78,7 @@ StreamView::StreamView (RouteTimeAxisView& tv, ArdourCanvas::Container* canvas_g if (_trackview.is_track()) { _trackview.track()->DiskstreamChanged.connect (*this, invalidator (*this), boost::bind (&StreamView::diskstream_changed, this), gui_context()); - _trackview.track()->RecordEnableChanged.connect (*this, invalidator (*this), boost::bind (&StreamView::rec_enable_changed, this), gui_context()); + _trackview.track()->rec_enable_control()->Changed.connect (*this, invalidator (*this), boost::bind (&StreamView::rec_enable_changed, this), gui_context()); _trackview.session()->TransportStateChange.connect (*this, invalidator (*this), boost::bind (&StreamView::transport_changed, this), gui_context()); _trackview.session()->TransportLooped.connect (*this, invalidator (*this), boost::bind (&StreamView::transport_looped, this), gui_context()); diff --git a/gtk2_ardour/time_axis_view.cc b/gtk2_ardour/time_axis_view.cc index 6b780a7fc7..2ce40844c5 100644 --- a/gtk2_ardour/time_axis_view.cc +++ b/gtk2_ardour/time_axis_view.cc @@ -763,7 +763,7 @@ TimeAxisView::end_name_edit (int response) RouteTimeAxisView* rtav = dynamic_cast(*i); - if (rtav && rtav->route()->record_enabled()) { + if (rtav && (!rtav->is_track() || rtav->track()->rec_enable_control()->get_value())) { continue; } @@ -794,7 +794,7 @@ TimeAxisView::end_name_edit (int response) RouteTimeAxisView* rtav = dynamic_cast(*i); - if (rtav && rtav->route()->record_enabled()) { + if (rtav && (!rtav->is_track() || rtav->track()->rec_enable_control()->get_value())) { continue; } diff --git a/libs/ardour/ardour/automation_control.h b/libs/ardour/ardour/automation_control.h index 455e8891c1..d84f0a091f 100644 --- a/libs/ardour/ardour/automation_control.h +++ b/libs/ardour/ardour/automation_control.h @@ -41,7 +41,7 @@ namespace ARDOUR { class Session; class Automatable; - +class ControlGroup; /** A PBD::Controllable with associated automation data (AutomationList) */ @@ -50,7 +50,7 @@ class LIBARDOUR_API AutomationControl , public Evoral::Control , public boost::enable_shared_from_this { -public: + public: AutomationControl(ARDOUR::Session&, const Evoral::Parameter& parameter, const ParameterDescriptor& desc, @@ -87,8 +87,6 @@ public: void stop_touch(bool mark, double when); /* inherited from PBD::Controllable. - * Derived classes MUST call ::writable() to verify - * that writing to the parameter is legal at that time. */ double get_value () const; /* inherited from PBD::Controllable. @@ -99,10 +97,11 @@ public: /* automation related value setting */ virtual bool writable () const; /* Call to ::set_value() with no test for writable() because - * this is only used by automation playback. We would like - * to make it pure virtual + * this is only used by automation playback. */ - virtual void set_value_unchecked (double val) {} + void set_value_unchecked (double val) { + actually_set_value (val, PBD::Controllable::NoGroup); + } double lower() const { return _desc.lower; } double upper() const { return _desc.upper; } @@ -117,6 +116,37 @@ public: const ARDOUR::Session& session() const { return _session; } void commit_transaction (bool did_write); + void set_group (boost::shared_ptr); + + protected: + ARDOUR::Session& _session; + boost::shared_ptr _group; + + const ParameterDescriptor _desc; + + bool check_rt (double val, Controllable::GroupControlDisposition gcd); + + /* derived classes may reimplement this, but should either + call this explicitly inside their version OR make sure that the + Controllable::Changed signal is emitted when necessary. + */ + + virtual void actually_set_value (double value, PBD::Controllable::GroupControlDisposition); +}; + +class SlavableAutomationControl : public AutomationControl +{ + public: + SlavableAutomationControl(ARDOUR::Session&, + const Evoral::Parameter& parameter, + const ParameterDescriptor& desc, + boost::shared_ptr l=boost::shared_ptr(), + const std::string& name=""); + + ~SlavableAutomationControl (); + + double get_value () const; + void add_master (boost::shared_ptr); void remove_master (boost::shared_ptr); void clear_masters (); @@ -126,11 +156,7 @@ public: PBD::Signal0 MasterStatusChange; - protected: - ARDOUR::Session& _session; - - const ParameterDescriptor _desc; - + protected: class MasterRecord { public: @@ -155,12 +181,12 @@ public: typedef std::map Masters; Masters _masters; PBD::ScopedConnectionList masters_connections; - virtual void master_changed (bool from_self, GroupControlDisposition gcd); void master_going_away (boost::weak_ptr); virtual void recompute_masters_ratios (double val) { /* do nothing by default */} virtual double get_masters_value_locked () const; double get_value_locked() const; + }; diff --git a/libs/ardour/ardour/gain_control.h b/libs/ardour/ardour/gain_control.h index 4ec538e698..f68ec00452 100644 --- a/libs/ardour/ardour/gain_control.h +++ b/libs/ardour/ardour/gain_control.h @@ -34,14 +34,11 @@ namespace ARDOUR { class Session; -class LIBARDOUR_API GainControl : public AutomationControl { +class LIBARDOUR_API GainControl : public SlavableAutomationControl { public: GainControl (Session& session, const Evoral::Parameter ¶m, boost::shared_ptr al = boost::shared_ptr()); - void set_value (double val, PBD::Controllable::GroupControlDisposition group_override); - void set_value_unchecked (double); - double internal_to_interface (double) const; double interface_to_internal (double) const; double internal_to_user (double) const; @@ -54,6 +51,8 @@ class LIBARDOUR_API GainControl : public AutomationControl { int set_state (XMLNode const&, int); XMLNode& get_state(); + void inc_gain (gain_t); + private: std::string masters_string; PBD::ScopedConnection vca_loaded_connection; @@ -61,7 +60,7 @@ class LIBARDOUR_API GainControl : public AutomationControl { void vcas_loaded(); void recompute_masters_ratios (double val); - void _set_value (double val, PBD::Controllable::GroupControlDisposition group_override); + void actually_set_value (double val, PBD::Controllable::GroupControlDisposition group_override); }; } /* namespace */ diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h index ed13832360..69f4981047 100644 --- a/libs/ardour/ardour/midi_track.h +++ b/libs/ardour/ardour/midi_track.h @@ -50,8 +50,9 @@ public: boost::shared_ptr create_diskstream (); void set_diskstream (boost::shared_ptr); - void set_record_enabled (bool yn, PBD::Controllable::GroupControlDisposition); - void set_record_safe (bool yn, PBD::Controllable::GroupControlDisposition); + + bool can_be_record_enabled (); + bool can_be_record_safe (); DataType data_type () const { return DataType::MIDI; @@ -89,14 +90,12 @@ public: , _route (route) {} - void set_value (double val, PBD::Controllable::GroupControlDisposition group_override); - void set_value_unchecked (double); bool writable() const { return true; } MidiTrack* _route; private: - void _set_value (double val, PBD::Controllable::GroupControlDisposition group_override); + void actually_set_value (double val, PBD::Controllable::GroupControlDisposition group_override); }; virtual void set_parameter_automation_state (Evoral::Parameter param, AutoState); @@ -133,7 +132,6 @@ public: PBD::Signal1 > DataRecorded; boost::shared_ptr get_gui_feed_buffer () const; - void set_monitoring (MonitorChoice, PBD::Controllable::GroupControlDisposition); MonitorState monitoring_state () const; void set_input_active (bool); @@ -144,6 +142,7 @@ protected: XMLNode& state (bool full); void act_on_mute (); + void monitoring_changed (bool, PBD::Controllable::GroupControlDisposition); private: MidiRingBuffer _immediate_events; diff --git a/libs/ardour/ardour/mute_master.h b/libs/ardour/ardour/mute_master.h index 14597cb56b..d147a58771 100644 --- a/libs/ardour/ardour/mute_master.h +++ b/libs/ardour/ardour/mute_master.h @@ -20,12 +20,15 @@ #ifndef __ardour_mute_master_h__ #define __ardour_mute_master_h__ -#include "evoral/Parameter.hpp" -#include "pbd/signals.h" -#include "pbd/stateful.h" #include +#include "pbd/signals.h" +#include "pbd/stateful.h" + +#include "evoral/Parameter.hpp" + #include "ardour/session_handle.h" +#include "ardour/types.h" namespace ARDOUR { @@ -73,6 +76,7 @@ class LIBARDOUR_API MuteMaster : public SessionHandleRef, public PBD::Stateful XMLNode& get_state(); int set_state(const XMLNode&, int version); + static const std::string xml_node_name; private: MutePoint _mute_point; diff --git a/libs/ardour/ardour/pan_controllable.h b/libs/ardour/ardour/pan_controllable.h index 85a4efe2fc..26f0707ff7 100644 --- a/libs/ardour/ardour/pan_controllable.h +++ b/libs/ardour/ardour/pan_controllable.h @@ -47,12 +47,10 @@ class LIBARDOUR_API PanControllable : public AutomationControl {} double lower () const; - void set_value (double, PBD::Controllable::GroupControlDisposition group_override); - void set_value_unchecked (double); private: Pannable* owner; - void _set_value (double, PBD::Controllable::GroupControlDisposition group_override); + void actually_set_value (double, PBD::Controllable::GroupControlDisposition group_override); }; } // namespace diff --git a/libs/ardour/ardour/plugin_insert.h b/libs/ardour/ardour/plugin_insert.h index 4b5f38964a..70bbd76a71 100644 --- a/libs/ardour/ardour/plugin_insert.h +++ b/libs/ardour/ardour/plugin_insert.h @@ -185,15 +185,13 @@ class LIBARDOUR_API PluginInsert : public Processor const ParameterDescriptor& desc, boost::shared_ptr list=boost::shared_ptr()); - void set_value (double val, PBD::Controllable::GroupControlDisposition group_override); - void set_value_unchecked (double); double get_value (void) const; void catch_up_with_external_value (double val); XMLNode& get_state(); private: PluginInsert* _plugin; - void _set_value (double val, PBD::Controllable::GroupControlDisposition group_override); + void actually_set_value (double val, PBD::Controllable::GroupControlDisposition group_override); }; /** A control that manipulates a plugin property (message). */ @@ -204,10 +202,10 @@ class LIBARDOUR_API PluginInsert : public Processor const ParameterDescriptor& desc, boost::shared_ptr list=boost::shared_ptr()); - void set_value (double val, PBD::Controllable::GroupControlDisposition group_override); - void set_value_unchecked (double); double get_value (void) const; XMLNode& get_state(); + protected: + void actually_set_value (double value, PBD::Controllable::GroupControlDisposition); private: PluginInsert* _plugin; diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 9abe56afcb..6c0380963d 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -28,7 +28,6 @@ #include #include -#include #include #include @@ -46,12 +45,18 @@ #include "ardour/io_vector.h" #include "ardour/libardour_visibility.h" #include "ardour/types.h" +#include "ardour/monitorable.h" +#include "ardour/muteable.h" #include "ardour/mute_master.h" +#include "ardour/mute_control.h" #include "ardour/route_group_member.h" #include "ardour/stripable.h" #include "ardour/graphnode.h" #include "ardour/automatable.h" #include "ardour/unknown_processor.h" +#include "ardour/soloable.h" +#include "ardour/solo_control.h" +#include "ardour/solo_safe_control.h" class RoutePinWindowProxy; @@ -74,8 +79,18 @@ class Pannable; class CapturingProcessor; class InternalSend; class VCA; +class SoloIsolateControl; +class PhaseControl; +class MonitorControl; -class LIBARDOUR_API Route : public Stripable, public Automatable, public RouteGroupMember, public GraphNode, public boost::enable_shared_from_this +class LIBARDOUR_API Route : public Stripable, + public Soloable, + public Muteable, + public Monitorable, + public Automatable, + public RouteGroupMember, + public GraphNode, + public boost::enable_shared_from_this { public: @@ -119,7 +134,7 @@ public: bool is_master() const { return _flags & MasterOut; } bool is_monitor() const { return _flags & MonitorOut; } - virtual MonitorState monitoring_state () const; + MonitorState monitoring_state () const; virtual MeterState metering_state () const; /* these are the core of the API of a Route. see the protected sections as well */ @@ -135,10 +150,6 @@ public: virtual bool can_record() { return false; } - virtual void set_record_enabled (bool /*yn*/, PBD::Controllable::GroupControlDisposition) {} - virtual bool record_enabled() const { return false; } - virtual void set_record_safe (bool /*yn*/, PBD::Controllable::GroupControlDisposition) {} - virtual bool record_safe () const {return false; } virtual void nonrealtime_handle_transport_stopped (bool abort, bool did_locate, bool flush_processors); virtual void realtime_handle_transport_stopped () {} virtual void realtime_locate () {} @@ -149,47 +160,31 @@ public: void shift (framepos_t, framecnt_t); - void set_gain (gain_t val, PBD::Controllable::GroupControlDisposition); - void inc_gain (gain_t delta); - void set_trim (gain_t val, PBD::Controllable::GroupControlDisposition); - void set_mute_points (MuteMaster::MutePoint); - MuteMaster::MutePoint mute_points () const; - - bool muted () const; - void set_mute (bool yn, PBD::Controllable::GroupControlDisposition); - - bool muted_by_others_soloing () const; - bool muted_by_others () const; - /* controls use set_solo() to modify this route's solo state */ - void set_solo (bool yn, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); - bool soloed () const { return self_soloed () || soloed_by_others (); } void clear_all_solo_state (); - bool soloed_by_others () const { return _soloed_by_others_upstream||_soloed_by_others_downstream; } - bool soloed_by_others_upstream () const { return _soloed_by_others_upstream; } - bool soloed_by_others_downstream () const { return _soloed_by_others_downstream; } - bool self_soloed () const { return _self_solo; } + bool soloed_by_others () const { return _solo_control->soloed_by_others(); } + bool soloed_by_others_upstream () const { return _solo_control->soloed_by_others_upstream(); } + bool soloed_by_others_downstream () const { return _solo_control->soloed_by_others_downstream(); } + bool self_soloed () const { return _solo_control->self_soloed(); } + bool soloed () const { return self_soloed () || soloed_by_others (); } - void set_solo_isolated (bool yn, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); - bool solo_isolated() const; + void push_solo_upstream (int32_t delta); + void push_solo_isolate_upstream (int32_t delta); + bool can_solo () const { + return !(is_master() || is_monitor() || is_auditioner()); + } + bool is_safe () const { + return _solo_safe_control->get_value(); + } - void set_solo_safe (bool yn, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); - bool solo_safe() const; - - void set_listen (bool yn, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); bool listening_via_monitor () const; void enable_monitor_send (); - void set_phase_invert (uint32_t, bool yn); - void set_phase_invert (boost::dynamic_bitset<>); - bool phase_invert (uint32_t) const; - boost::dynamic_bitset<> phase_invert () const; - void set_denormal_protection (bool yn); bool denormal_protection() const; @@ -353,7 +348,6 @@ public: PBD::Signal0 active_changed; PBD::Signal0 denormal_protection_changed; PBD::Signal0 comment_changed; - PBD::Signal0 mute_points_changed; /** track numbers - assigned by session * nubers > 0 indicate tracks (audio+midi) @@ -456,185 +450,32 @@ public: boost::shared_ptr get_control (const Evoral::Parameter& param); - class RouteAutomationControl : public AutomationControl { - public: - RouteAutomationControl (const std::string& name, - AutomationType atype, - boost::shared_ptr alist, - boost::shared_ptr route); - protected: - friend class Route; - - void route_set_value (double val) { - AutomationControl::set_value (val, Controllable::NoGroup); - } - - boost::weak_ptr _route; - }; - - class BooleanRouteAutomationControl : public RouteAutomationControl { - public: - BooleanRouteAutomationControl (const std::string& name, - AutomationType atype, - boost::shared_ptr alist, - boost::shared_ptr route) - : RouteAutomationControl (name, atype, alist, route) {} - protected: - double get_masters_value_locked() const; - - }; - - class GainControllable : public GainControl { - public: - GainControllable (Session& session, - AutomationType type, - boost::shared_ptr route); - - void set_value (double val, PBD::Controllable::GroupControlDisposition group_override) { - boost::shared_ptr r = _route.lock(); - if (r) { - /* Route must mediate group control */ - r->set_control ((AutomationType) parameter().type(), val, group_override); - } - } - - protected: - friend class Route; - - void route_set_value (double val) { - GainControl::set_value (val, Controllable::NoGroup); - } - - boost::weak_ptr _route; - }; - - class SoloControllable : public BooleanRouteAutomationControl { - public: - SoloControllable (std::string name, boost::shared_ptr); - void set_value (double, PBD::Controllable::GroupControlDisposition group_override); - void set_value_unchecked (double); - double get_value () const; - - /* Export additional API so that objects that only get access - * to a Controllable/AutomationControl can do more fine-grained - * operations with respect to solo. Obviously, they would need - * to dynamic_cast first. - * - * Solo state is not representable by a single scalar value, - * so this AutomationControl maps set_value() and get_value() - * to r->set_self_solo() and r->soloed() respectively. This - * means that the Controllable is technically asymmetric. It is - * possible to call ::set_value (0.0) to disable (self)solo, - * and then call ::get_value() and get a return of 1.0 because - * the Route owner is soloed by upstream/downstream. - */ - - void set_self_solo (bool yn) { - boost::shared_ptr r(_route.lock()); if (r) r->set_self_solo (yn); - } - void mod_solo_by_others_upstream (int32_t delta) { - boost::shared_ptr r(_route.lock()); if (r) r->mod_solo_by_others_upstream (delta); - } - void mod_solo_by_others_downstream (int32_t delta) { - boost::shared_ptr r(_route.lock()); if (r) r->mod_solo_by_others_downstream (delta); - } - bool soloed_by_others () const { - boost::shared_ptr r(_route.lock()); if (r) return r->soloed_by_others(); else return false; - } - bool soloed_by_others_upstream () const { - boost::shared_ptr r(_route.lock()); if (r) return r->soloed_by_others_upstream(); else return false; - } - bool soloed_by_others_downstream () const { - boost::shared_ptr r(_route.lock()); if (r) return r->soloed_by_others_downstream(); else return false; - } - bool self_soloed () const { - boost::shared_ptr r(_route.lock()); if (r) return r->self_soloed(); else return false; - } - - protected: - void master_changed (bool, PBD::Controllable::GroupControlDisposition); - - private: - void _set_value (double, PBD::Controllable::GroupControlDisposition group_override); - }; - - struct MuteControllable : public BooleanRouteAutomationControl { - public: - MuteControllable (std::string name, boost::shared_ptr); - void set_value (double, PBD::Controllable::GroupControlDisposition group_override); - void set_value_unchecked (double); - double get_value () const; - - /* Pretend to change value, but do not affect actual route mute. */ - void set_superficial_value(bool muted); - protected: - void master_changed (bool, PBD::Controllable::GroupControlDisposition); - - private: - boost::weak_ptr _route; - void _set_value (double, PBD::Controllable::GroupControlDisposition group_override); - }; - - class LIBARDOUR_API PhaseControllable : public BooleanRouteAutomationControl { - public: - PhaseControllable (std::string name, boost::shared_ptr); - void set_value (double, PBD::Controllable::GroupControlDisposition group_override); - /* currently no automation, so no need for set_value_unchecked() */ - void set_channel (uint32_t); - double get_value () const; - uint32_t channel() const; - private: - uint32_t _current_phase; - void _set_value (double, PBD::Controllable::GroupControlDisposition group_override); - }; - - class LIBARDOUR_API SoloIsolateControllable : public BooleanRouteAutomationControl { - public: - SoloIsolateControllable (std::string name, boost::shared_ptr); - void set_value (double, PBD::Controllable::GroupControlDisposition group_override); - /* currently no automation, so no need for set_value_unchecked() */ - double get_value () const; - private: - void _set_value (double, PBD::Controllable::GroupControlDisposition group_override); - }; - - class LIBARDOUR_API SoloSafeControllable : public BooleanRouteAutomationControl { - public: - SoloSafeControllable (std::string name, boost::shared_ptr); - void set_value (double, PBD::Controllable::GroupControlDisposition group_override); - /* currently no automation, so no need for set_value_unchecked() */ - double get_value () const; - private: - void _set_value (double, PBD::Controllable::GroupControlDisposition group_override); - }; - - void set_control (AutomationType, double val, PBD::Controllable::GroupControlDisposition group_override); - - boost::shared_ptr solo_control() const { + boost::shared_ptr solo_control() const { return _solo_control; } - boost::shared_ptr mute_control() const { + boost::shared_ptr mute_control() const { return _mute_control; } - boost::shared_ptr mute_master() const { - return _mute_master; - } + bool can_be_muted_by_others () const { return !is_master(); } + bool muted () const { return _mute_control->muted(); } + bool muted_by_others_soloing () const; + bool muted_by_others () const; - boost::shared_ptr solo_isolate_control() const { + boost::shared_ptr solo_isolate_control() const { return _solo_isolate_control; } - boost::shared_ptr solo_safe_control() const { + boost::shared_ptr solo_safe_control() const { return _solo_safe_control; } - boost::shared_ptr monitoring_control() const { + boost::shared_ptr monitoring_control() const { /* tracks override this to provide actual monitoring control; busses have no possible choices except input monitoring. */ - return boost::shared_ptr (); + return boost::shared_ptr (); } /* Route doesn't own these items, but sub-objects that it does own have them @@ -647,8 +488,8 @@ public: boost::shared_ptr pannable() const; boost::shared_ptr gain_control() const; - boost::shared_ptr trim_control() const; - boost::shared_ptr phase_control() const; + boost::shared_ptr trim_control() const; + boost::shared_ptr phase_control() const; /** Return the first processor that accepts has at least one MIDI input @@ -766,8 +607,8 @@ public: friend class Session; void catch_up_on_solo_mute_override (); - void mod_solo_by_others_upstream (int32_t); - void mod_solo_by_others_downstream (int32_t); + void set_listen (bool); + void curve_reallocate (); virtual void set_block_size (pframes_t nframes); @@ -829,14 +670,6 @@ protected: MeterPoint _meter_point; MeterPoint _pending_meter_point; MeterType _meter_type; - boost::dynamic_bitset<> _phase_invert; - bool _self_solo; - uint32_t _soloed_by_others_upstream; - uint32_t _soloed_by_others_downstream; - bool _solo_isolated; - uint32_t _solo_isolated_by_upstream; - - void mod_solo_isolated_by_upstream (bool); bool _denormal_protection; @@ -844,18 +677,14 @@ protected: bool _silent : 1; bool _declickable : 1; - boost::shared_ptr _solo_control; - boost::shared_ptr _mute_control; - boost::shared_ptr _mute_master; - boost::shared_ptr _phase_control; - boost::shared_ptr _solo_isolate_control; - boost::shared_ptr _solo_safe_control; - - virtual void act_on_mute () {} + boost::shared_ptr _solo_control; + boost::shared_ptr _mute_control; + boost::shared_ptr _phase_control; + boost::shared_ptr _solo_isolate_control; + boost::shared_ptr _solo_safe_control; std::string _comment; bool _have_internal_generator; - bool _solo_safe; DataType _default_type; FedBy _fed_by; @@ -882,9 +711,9 @@ protected: virtual void maybe_declick (BufferSet&, framecnt_t, int); - boost::shared_ptr _gain_control; + boost::shared_ptr _gain_control; boost::shared_ptr _amp; - boost::shared_ptr _trim_control; + boost::shared_ptr _trim_control; boost::shared_ptr _trim; boost::shared_ptr _meter; boost::shared_ptr _delayline; @@ -928,7 +757,6 @@ private: void placement_range (Placement p, ProcessorList::iterator& start, ProcessorList::iterator& end); void set_self_solo (bool yn); - void set_mute_master_solo (); void set_processor_positions (); framecnt_t update_port_latencies (PortSet& ports, PortSet& feeders, bool playback, framecnt_t) const; @@ -985,6 +813,7 @@ private: void reset_instrument_info (); void set_remote_control_id_internal (uint32_t id, bool notify_class_listeners = true); + void solo_control_changed (bool self, PBD::Controllable::GroupControlDisposition); }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/route_group.h b/libs/ardour/ardour/route_group.h index 95affaeaf5..feeac6a467 100644 --- a/libs/ardour/ardour/route_group.h +++ b/libs/ardour/ardour/route_group.h @@ -29,10 +29,12 @@ #include "pbd/signals.h" #include "pbd/stateful.h" -#include "ardour/libardour_visibility.h" +#include "ardour/control_group.h" #include "ardour/types.h" #include "ardour/session_object.h" +#include "ardour/libardour_visibility.h" + namespace ARDOUR { namespace Properties { @@ -157,8 +159,17 @@ class LIBARDOUR_API RouteGroup : public SessionObject PBD::Property _color; PBD::Property _monitoring; + boost::shared_ptr _solo_group; + boost::shared_ptr _mute_group; + boost::shared_ptr _rec_enable_group; + boost::shared_ptr _gain_group; + boost::shared_ptr _monitoring_group; + void remove_when_going_away (boost::weak_ptr); int set_state_2X (const XMLNode&, int); + + void post_set (PBD::PropertyChange const &); + void push_to_groups (); }; } /* namespace */ diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 18507ee5d9..786751dea5 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -791,16 +791,13 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop static const SessionEvent::RTeventCallback rt_cleanup; - void set_solo (boost::shared_ptr, bool, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); - void set_implicit_solo (boost::shared_ptr, int delta, bool up_or_downstream, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); void clear_all_solo_state (boost::shared_ptr); - void set_just_one_solo (boost::shared_ptr, bool, SessionEvent::RTeventCallback after = rt_cleanup); - void set_mute (boost::shared_ptr, bool, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); - void set_listen (boost::shared_ptr, bool, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); - void set_record_enabled (boost::shared_ptr, bool, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); - void set_record_safe (boost::shared_ptr, bool yn, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); - void set_solo_isolated (boost::shared_ptr, bool, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); - void set_monitoring (boost::shared_ptr, MonitorChoice, SessionEvent::RTeventCallback after = rt_cleanup, PBD::Controllable::GroupControlDisposition group_override = PBD::Controllable::UseGroup); + + /* Control-based methods */ + + void set_controls (boost::shared_ptr, double val, PBD::Controllable::GroupControlDisposition); + void set_control (boost::shared_ptr, double val, PBD::Controllable::GroupControlDisposition); + void set_exclusive_input_active (boost::shared_ptr rt, bool onoff, bool flip_others = false); PBD::Signal1 SoloActive; @@ -1936,16 +1933,18 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop return ev; } - void rt_set_solo (boost::shared_ptr, bool yn, PBD::Controllable::GroupControlDisposition group_override); - void rt_set_implicit_solo (boost::shared_ptr, int delta, bool up_or_downstream, PBD::Controllable::GroupControlDisposition); + /* specialized version realtime "apply to set of controls" operations */ + SessionEvent* get_rt_event (boost::shared_ptr cl, double arg, PBD::Controllable::GroupControlDisposition group_override) { + SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0); + ev->rt_slot = boost::bind (&Session::rt_set_controls, this, cl, arg, group_override); + ev->rt_return = Session::rt_cleanup; + ev->event_loop = PBD::EventLoop::get_event_loop_for_thread (); + + return ev; + } + + void rt_set_controls (boost::shared_ptr, double val, PBD::Controllable::GroupControlDisposition group_override); void rt_clear_all_solo_state (boost::shared_ptr, bool yn, PBD::Controllable::GroupControlDisposition group_override); - void rt_set_just_one_solo (boost::shared_ptr, bool yn, PBD::Controllable::GroupControlDisposition /* ignored*/ ); - void rt_set_mute (boost::shared_ptr, bool yn, PBD::Controllable::GroupControlDisposition group_override); - void rt_set_listen (boost::shared_ptr, bool yn, PBD::Controllable::GroupControlDisposition group_override); - void rt_set_solo_isolated (boost::shared_ptr, bool yn, PBD::Controllable::GroupControlDisposition group_override); - void rt_set_record_enabled (boost::shared_ptr, bool yn, PBD::Controllable::GroupControlDisposition group_override); - void rt_set_record_safe (boost::shared_ptr, bool yn, PBD::Controllable::GroupControlDisposition group_override); - void rt_set_monitoring (boost::shared_ptr, MonitorChoice, PBD::Controllable::GroupControlDisposition group_override); /** temporary list of Diskstreams used only during load of 2.X sessions */ std::list > _diskstreams_2X; diff --git a/libs/ardour/ardour/session_event.h b/libs/ardour/ardour/session_event.h index cca7f1274e..8bb160b90e 100644 --- a/libs/ardour/ardour/session_event.h +++ b/libs/ardour/ardour/session_event.h @@ -84,26 +84,28 @@ public: union { void* ptr; bool yes_or_no; - framepos_t target2_frame; + framepos_t target2_frame; Slave* slave; Route* route; }; union { bool second_yes_or_no; + double control_value; }; union { bool third_yes_or_no; }; - /* 4 members to handle a multi-group event handled in RT context */ + /* 5 members to handle a multi-group event handled in RT context */ typedef boost::function RTeventCallback; - boost::shared_ptr routes; /* apply to */ - boost::function rt_slot; /* what to call in RT context */ - RTeventCallback rt_return; /* called after rt_slot, with this event as an argument */ + boost::shared_ptr controls; /* apply to */ + boost::shared_ptr routes; /* apply to */ + boost::function rt_slot; /* what to call in RT context */ + RTeventCallback rt_return; /* called after rt_slot, with this event as an argument */ PBD::EventLoop* event_loop; std::list audio_range; diff --git a/libs/ardour/ardour/stripable.h b/libs/ardour/ardour/stripable.h index e97fe79103..f68cb07b91 100644 --- a/libs/ardour/ardour/stripable.h +++ b/libs/ardour/ardour/stripable.h @@ -26,13 +26,19 @@ #include #include -#include "ardour/gain_control.h" #include "ardour/session_object.h" namespace ARDOUR { class AutomationControl; +class GainControl; class PeakMeter; +class SoloControl; +class MuteControl; +class PhaseControl; +class SoloIsolateControl; +class SoloSafeControl; +class MonitorControl; /* This is a virtual base class for any object that needs to be potentially * represented by a control-centric user interface using the general model of a @@ -58,12 +64,13 @@ class Stripable : public SessionObject { virtual boost::shared_ptr gain_control() const = 0; - virtual boost::shared_ptr solo_control() const = 0; - virtual boost::shared_ptr mute_control() const = 0; - virtual boost::shared_ptr phase_control() const = 0; - virtual boost::shared_ptr trim_control() const = 0; + virtual boost::shared_ptr solo_control() const = 0; + virtual boost::shared_ptr mute_control() const = 0; - virtual boost::shared_ptr monitoring_control() const = 0; + virtual boost::shared_ptr phase_control() const = 0; + virtual boost::shared_ptr trim_control() const = 0; + + virtual boost::shared_ptr monitoring_control() const = 0; virtual boost::shared_ptr recenable_control() const { return boost::shared_ptr(); } /* "well-known" controls for panning. Any or all of these may return @@ -131,6 +138,9 @@ class Stripable : public SessionObject { * the route. */ virtual boost::shared_ptr master_send_enable_controllable () const = 0; + + virtual bool muted_by_others_soloing () const = 0; + virtual bool muted_by_others () const = 0; }; diff --git a/libs/ardour/ardour/track.h b/libs/ardour/ardour/track.h index b378021bce..bb955265c8 100644 --- a/libs/ardour/ardour/track.h +++ b/libs/ardour/ardour/track.h @@ -22,6 +22,7 @@ #include #include "ardour/interthread_info.h" +#include "ardour/recordable.h" #include "ardour/route.h" #include "ardour/public_diskstream.h" @@ -34,13 +35,14 @@ class Source; class Region; class Diskstream; class IO; +class MonitorControl; /** A track is an route (bus) with a recordable diskstream and * related objects relevant to tracking, playback and editing. * * Specifically a track has regions and playlist objects. */ -class LIBARDOUR_API Track : public Route, public PublicDiskstream +class LIBARDOUR_API Track : public Route, public Recordable, public PublicDiskstream { public: Track (Session&, std::string name, Route::Flag f = Route::Flag (0), TrackMode m = Normal, DataType default_type = DataType::AUDIO); @@ -56,23 +58,9 @@ class LIBARDOUR_API Track : public Route, public PublicDiskstream virtual bool can_use_mode (TrackMode /*m*/, bool& /*bounce_required*/) { return false; } PBD::Signal0 TrackModeChanged; - class LIBARDOUR_API MonitoringControllable : public RouteAutomationControl { - public: - MonitoringControllable (std::string name, boost::shared_ptr); - void set_value (double, PBD::Controllable::GroupControlDisposition group_override); - /* currently no automation, so no need for set_value_unchecked() */ - double get_value () const; - private: - void _set_value (double, PBD::Controllable::GroupControlDisposition group_override); - }; - - void set_monitoring (MonitorChoice, PBD::Controllable::GroupControlDisposition group_override); - MonitorChoice monitoring_choice() const { return _monitoring; } - MonitorState monitoring_state () const; - PBD::Signal0 MonitoringChanged; - - boost::shared_ptr monitoring_control() const { return _monitoring_control; } + boost::shared_ptr monitoring_control() const { return _monitoring_control; } + MonitorState monitoring_state () const; MeterState metering_state () const; virtual int no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, @@ -142,13 +130,12 @@ class LIBARDOUR_API Track : public Route, public PublicDiskstream virtual int set_state (const XMLNode&, int version); static void zero_diskstream_id_in_xml (XMLNode&); - boost::shared_ptr rec_enable_control() { return _rec_enable_control; } + boost::shared_ptr rec_enable_control() const { return _record_enable_control; } + boost::shared_ptr rec_safe_control() const { return _record_safe_control; } - bool record_enabled() const; - bool record_safe () const; - void set_record_enabled (bool yn, PBD::Controllable::GroupControlDisposition); - void set_record_safe (bool yn, PBD::Controllable::GroupControlDisposition); - void prep_record_enabled (bool yn, PBD::Controllable::GroupControlDisposition); + int prep_record_enabled (bool); + bool can_be_record_enabled (); + bool can_be_record_safe (); bool using_diskstream_id (PBD::ID) const; @@ -204,8 +191,6 @@ class LIBARDOUR_API Track : public Route, public PublicDiskstream PBD::Signal0 FreezeChange; /* Emitted when our diskstream is set to use a different playlist */ PBD::Signal0 PlaylistChanged; - PBD::Signal0 RecordEnableChanged; - PBD::Signal0 RecordSafeChanged; PBD::Signal0 SpeedChanged; PBD::Signal0 AlignmentStyleChanged; @@ -216,8 +201,7 @@ class LIBARDOUR_API Track : public Route, public PublicDiskstream MeterPoint _saved_meter_point; TrackMode _mode; bool _needs_butler; - MonitorChoice _monitoring; - boost::shared_ptr _monitoring_control; + boost::shared_ptr _monitoring_control; //private: (FIXME) struct FreezeRecordProcessorInfo { @@ -242,20 +226,6 @@ class LIBARDOUR_API Track : public Route, public PublicDiskstream FreezeState state; }; - class RecEnableControl : public AutomationControl { - public: - RecEnableControl (boost::shared_ptr t); - - void set_value (double, PBD::Controllable::GroupControlDisposition); - void set_value_unchecked (double); - double get_value (void) const; - - boost::weak_ptr track; - - private: - void _set_value (double, PBD::Controllable::GroupControlDisposition); - }; - virtual void set_state_part_two () = 0; FreezeRecord _freeze_record; @@ -264,17 +234,20 @@ class LIBARDOUR_API Track : public Route, public PublicDiskstream void maybe_declick (BufferSet&, framecnt_t, int); - boost::shared_ptr _rec_enable_control; + boost::shared_ptr _record_enable_control; + boost::shared_ptr _record_safe_control; + + virtual void record_enable_changed (bool, PBD::Controllable::GroupControlDisposition); + virtual void record_safe_changed (bool, PBD::Controllable::GroupControlDisposition); framecnt_t check_initial_delay (framecnt_t nframes, framepos_t&); + virtual void monitoring_changed (bool, PBD::Controllable::GroupControlDisposition); private: virtual boost::shared_ptr diskstream_factory (XMLNode const &) = 0; void diskstream_playlist_changed (); - void diskstream_record_enable_changed (); - void diskstream_record_safe_changed (); void diskstream_speed_changed (); void diskstream_alignment_style_changed (); void parameter_changed (std::string const & p); diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index 626109557f..bd866141d5 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -53,6 +53,7 @@ namespace ARDOUR { class Route; class Region; class VCA; + class AutomationControl; typedef float Sample; typedef float pan_t; @@ -149,6 +150,7 @@ namespace ARDOUR { FadeOutAutomation, EnvelopeAutomation, RecEnableAutomation, + RecSafeAutomation, TrimAutomation, PhaseAutomation, MonitoringAutomation, @@ -565,6 +567,7 @@ namespace ARDOUR { typedef std::list > RouteList; typedef std::list > WeakRouteList; + typedef std::list > ControlList; typedef std::list > VCAList; diff --git a/libs/ardour/ardour/utils.h b/libs/ardour/ardour/utils.h index f3767d471a..1dc7c2baf4 100644 --- a/libs/ardour/ardour/utils.h +++ b/libs/ardour/ardour/utils.h @@ -28,19 +28,26 @@ #include #include +#include "boost/shared_ptr.hpp" + #if __APPLE__ #include #endif /* __APPLE__ */ -#include "ardour/libardour_visibility.h" #include "ardour/ardour.h" #include "ardour/data_type.h" #include "ardour/dB.h" +#include "ardour/types.h" + +#include "ardour/libardour_visibility.h" class XMLNode; namespace ARDOUR { +class Route; +class Track; + LIBARDOUR_API std::string legalize_for_path (const std::string& str); LIBARDOUR_API std::string legalize_for_universal_path (const std::string& str); LIBARDOUR_API std::string legalize_for_uri (const std::string& str); @@ -169,6 +176,29 @@ LIBARDOUR_API bool matching_unsuffixed_filename_exists_in (const std::string& di LIBARDOUR_API uint32_t how_many_dsp_threads (); +template boost::shared_ptr route_list_to_control_list (boost::shared_ptr rl, boost::shared_ptr (Route::*get_control)() const) { + boost::shared_ptr cl (new ControlList); + for (RouteList::const_iterator r = rl->begin(); r != rl->end(); ++r) { + boost::shared_ptr ac = ((*r).get()->*get_control)(); + if (ac) { + cl->push_back (ac); + } + } + return cl; +} + +template boost::shared_ptr route_list_to_control_list (boost::shared_ptr rl, boost::shared_ptr (Track::*get_control)() const) { + boost::shared_ptr cl (new ControlList); + for (RouteList::const_iterator r = rl->begin(); r != rl->end(); ++r) { + boost::shared_ptr t = boost::dynamic_pointer_cast (*r); + boost::shared_ptr ac = (t.get()->*get_control)(); + if (ac) { + cl->push_back (ac); + } + } + return cl; +} + #if __APPLE__ LIBARDOUR_API std::string CFStringRefToStdString(CFStringRef stringRef); #endif // __APPLE__ @@ -176,4 +206,3 @@ LIBARDOUR_API std::string CFStringRefToStdString(CFStringRef stringRef); } //namespave #endif /* __ardour_utils_h__ */ - diff --git a/libs/ardour/ardour/vca.h b/libs/ardour/ardour/vca.h index dc48ffa480..1c247c0e92 100644 --- a/libs/ardour/ardour/vca.h +++ b/libs/ardour/ardour/vca.h @@ -27,14 +27,19 @@ #include "pbd/statefuldestructible.h" #include "ardour/automatable.h" +#include "ardour/muteable.h" +#include "ardour/soloable.h" #include "ardour/stripable.h" namespace ARDOUR { -class GainControl; class Route; +class GainControl; +class SoloControl; +class MuteControl; +class MonitorControl; -class LIBARDOUR_API VCA : public Stripable, public Automatable, public boost::enable_shared_from_this { +class LIBARDOUR_API VCA : public Stripable, public Soloable, public Muteable, public Automatable, public boost::enable_shared_from_this { public: VCA (Session& session, uint32_t num, const std::string& name); ~VCA(); @@ -47,7 +52,15 @@ class LIBARDOUR_API VCA : public Stripable, public Automatable, public boost::en int set_state (XMLNode const&, int version); bool soloed () const; + void push_solo_upstream (int32_t) {} + void push_solo_isolate_upstream (int32_t) {} + bool can_solo() const { return true; } + bool is_safe () const { return false; } + bool muted () const; + bool can_be_muted_by_others () const { return true; } + bool muted_by_others_soloing() const { return false; } + bool muted_by_others() const { return false; } static std::string default_name_template (); static int next_vca_number (); @@ -58,16 +71,16 @@ class LIBARDOUR_API VCA : public Stripable, public Automatable, public boost::en static void set_next_vca_number (uint32_t); virtual boost::shared_ptr gain_control() const { return _gain_control; } - virtual boost::shared_ptr solo_control() const { return _solo_control; } - virtual boost::shared_ptr mute_control() const { return _mute_control; } + virtual boost::shared_ptr solo_control() const { return _solo_control; } + virtual boost::shared_ptr mute_control() const { return _mute_control; } /* null Stripable API, because VCAs don't have any of this */ - virtual boost::shared_ptr peak_meter() { return boost::shared_ptr(); } - virtual boost::shared_ptr peak_meter() const { return boost::shared_ptr(); } - virtual boost::shared_ptr phase_control() const { return boost::shared_ptr(); } - virtual boost::shared_ptr trim_control() const { return boost::shared_ptr(); } - virtual boost::shared_ptr monitoring_control() const { return boost::shared_ptr(); } + virtual boost::shared_ptr peak_meter() { return boost::shared_ptr(); } + virtual boost::shared_ptr peak_meter() const { return boost::shared_ptr(); } + virtual boost::shared_ptr phase_control() const { return boost::shared_ptr(); } + virtual boost::shared_ptr trim_control() const { return boost::shared_ptr(); } + virtual boost::shared_ptr monitoring_control() const { return boost::shared_ptr(); } virtual boost::shared_ptr recenable_control() const { return boost::shared_ptr(); } virtual boost::shared_ptr pan_azimuth_control() const { return boost::shared_ptr(); } virtual boost::shared_ptr pan_elevation_control() const { return boost::shared_ptr(); } @@ -96,36 +109,12 @@ class LIBARDOUR_API VCA : public Stripable, public Automatable, public boost::en virtual boost::shared_ptr master_send_enable_controllable () const { return boost::shared_ptr(); } private: - class VCASoloControllable : public AutomationControl { - public: - VCASoloControllable (std::string const & name, boost::shared_ptr vca); - void set_value (double, PBD::Controllable::GroupControlDisposition group_override); - void set_value_unchecked (double); - double get_value () const; - private: - void _set_value (double, PBD::Controllable::GroupControlDisposition group_override); - boost::weak_ptr _vca; - }; - - class VCAMuteControllable : public AutomationControl { - public: - VCAMuteControllable (std::string const & name, boost::shared_ptr vca); - void set_value (double, PBD::Controllable::GroupControlDisposition group_override); - void set_value_unchecked (double); - double get_value () const; - private: - void _set_value (double, PBD::Controllable::GroupControlDisposition group_override); - boost::weak_ptr _vca; - }; - - friend class VCASoloControllable; - friend class VCAMuteControllable; - uint32_t _number; boost::shared_ptr _gain_control; - boost::shared_ptr _solo_control; - boost::shared_ptr _mute_control; + boost::shared_ptr _solo_control; + boost::shared_ptr _mute_control; + bool _solo_requested; bool _mute_requested; diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc index 579b0064bc..2ed200d6f6 100644 --- a/libs/ardour/audio_track.cc +++ b/libs/ardour/audio_track.cc @@ -33,6 +33,7 @@ #include "ardour/buffer_set.h" #include "ardour/delivery.h" #include "ardour/meter.h" +#include "ardour/monitor_control.h" #include "ardour/playlist_factory.h" #include "ardour/processor.h" #include "ardour/profile.h" @@ -349,7 +350,7 @@ AudioTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_fram if (!_active) { silence (nframes); - if (_meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled())) { + if (_meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _diskstream->record_enabled())) { _meter->reset(); } return 0; @@ -391,7 +392,7 @@ AudioTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_fram fill_buffers_with_input (bufs, _input, nframes); - if (_meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled())) { + if (_meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _diskstream->record_enabled())) { _meter->run (bufs, start_frame, end_frame, nframes, true); } @@ -629,8 +630,9 @@ AudioTrack::freeze_me (InterThreadInfo& itt) /* reset stuff that has already been accounted for in the freeze process */ - set_gain (GAIN_COEFF_UNITY, Controllable::NoGroup); - _amp->gain_control()->set_automation_state (Off); + gain_control()->set_value (GAIN_COEFF_UNITY, Controllable::NoGroup); + gain_control()->set_automation_state (Off); + /* XXX need to use _main_outs _panner->set_automation_state (Off); */ _freeze_record.state = Frozen; diff --git a/libs/ardour/automation_control.cc b/libs/ardour/automation_control.cc index b00c615625..7efaa07f23 100644 --- a/libs/ardour/automation_control.cc +++ b/libs/ardour/automation_control.cc @@ -20,14 +20,17 @@ #include #include -#include "ardour/automation_control.h" -#include "ardour/automation_watch.h" -#include "ardour/event_type_map.h" -#include "ardour/session.h" #include "pbd/memento_command.h" #include "pbd/stacktrace.h" +#include "ardour/audioengine.h" +#include "ardour/automation_control.h" +#include "ardour/automation_watch.h" +#include "ardour/control_group.h" +#include "ardour/event_type_map.h" +#include "ardour/session.h" + #include "i18n.h" #ifdef COMPILER_MSVC @@ -69,42 +72,38 @@ AutomationControl::writable() const return true; } -double -AutomationControl::get_masters_value_locked () const -{ - gain_t v = 1.0; - - for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) { - /* get current master value, scale by our current ratio with that master */ - v *= mr->second.master()->get_value () * mr->second.ratio(); - } - - return min (_desc.upper, v); -} - -double -AutomationControl::get_value_locked() const -{ - /* read or write masters lock must be held */ - - if (_masters.empty()) { - return Control::get_double (false, _session.transport_frame()); - } - - return get_masters_value_locked (); -} - /** Get the current effective `user' value based on automation state */ double AutomationControl::get_value() const { - bool from_list = _list && ((AutomationList*)_list.get())->automation_playback(); + bool from_list = _list && boost::dynamic_pointer_cast(_list)->automation_playback(); + return Control::get_double (from_list, _session.transport_frame()); +} - if (!from_list) { - Glib::Threads::RWLock::ReaderLock lm (master_lock); - return get_value_locked (); +void +AutomationControl::set_value (double val, PBD::Controllable::GroupControlDisposition gcd) +{ + if (!writable()) { + return; + } + + /* enforce strict double/boolean value mapping */ + + if (_desc.toggled) { + if (val != 0.0) { + val = 1.0; + } + } + + if (check_rt (val, gcd)) { + /* change has been queued to take place in an RT context */ + return; + } + + if (_group && _group->use_me (gcd)) { + _group->set_group_value (shared_from_this(), val); } else { - return Control::get_double (from_list, _session.transport_frame()); + actually_set_value (val, gcd); } } @@ -113,12 +112,15 @@ AutomationControl::get_value() const * @param value `user' value */ void -AutomationControl::set_value (double value, PBD::Controllable::GroupControlDisposition gcd) +AutomationControl::actually_set_value (double value, PBD::Controllable::GroupControlDisposition gcd) { - bool to_list = _list && ((AutomationList*)_list.get())->automation_write(); + bool to_list = _list && boost::dynamic_pointer_cast(_list)->automation_write(); Control::set_double (value, _session.transport_frame(), to_list); + AutomationType at = (AutomationType) _parameter.type(); + + std::cerr << "++++ Changed (" << enum_2_string (at) << ", " << enum_2_string (gcd) << ") = " << value << " @ " << this << std::endl; Changed (true, gcd); } @@ -263,147 +265,25 @@ AutomationControl::interface_to_internal (double val) const return val; } - void -AutomationControl::add_master (boost::shared_ptr m) +AutomationControl::set_group (boost::shared_ptr cg) { - double current_value; - double new_value; - std::pair res; - - { - Glib::Threads::RWLock::WriterLock lm (master_lock); - current_value = get_value_locked (); - - /* ratio will be recomputed below */ - - res = _masters.insert (make_pair (m->id(), MasterRecord (m, 1.0))); - - if (res.second) { - - recompute_masters_ratios (current_value); - - /* note that we bind @param m as a weak_ptr, thus - avoiding holding a reference to the control in the binding - itself. - */ - - m->DropReferences.connect_same_thread (masters_connections, boost::bind (&AutomationControl::master_going_away, this, m)); - - /* Store the connection inside the MasterRecord, so that when we destroy it, the connection is destroyed - and we no longer hear about changes to the AutomationControl. - - Note that we fix the "from_self" argument that will - be given to our own Changed signal to "false", - because the change came from the master. - */ - - - m->Changed.connect_same_thread (res.first->second.connection, boost::bind (&AutomationControl::master_changed, this, _1, _2)); - } - - new_value = get_value_locked (); - } - - if (res.second) { - /* this will notify everyone that we're now slaved to the master */ - MasterStatusChange (); /* EMIT SIGNAL */ - } - - if (new_value != current_value) { - /* force a call to to ::master_changed() to carry the - * consequences that would occur if the master assumed - * its current value WHILE we were slaved. - */ - master_changed (false, Controllable::NoGroup); - /* effective value changed by master */ - Changed (false, Controllable::NoGroup); - } - -} - -void -AutomationControl::master_changed (bool /*from_self*/, GroupControlDisposition gcd) -{ - /* our value has (likely) changed, but not because we were - * modified. Just the master. - */ - - Changed (false, gcd); /* EMIT SIGNAL */ -} - -void -AutomationControl::master_going_away (boost::weak_ptr wm) -{ - boost::shared_ptr m = wm.lock(); - if (m) { - remove_master (m); - } -} - -void -AutomationControl::remove_master (boost::shared_ptr m) -{ - double current_value; - double new_value; - Masters::size_type erased = 0; - - { - Glib::Threads::RWLock::WriterLock lm (master_lock); - current_value = get_value_locked (); - erased = _masters.erase (m->id()); - if (erased) { - recompute_masters_ratios (current_value); - } - new_value = get_value_locked (); - } - - if (erased) { - MasterStatusChange (); /* EMIT SIGNAL */ - } - - if (new_value != current_value) { - Changed (false, Controllable::NoGroup); - } -} - -void -AutomationControl::clear_masters () -{ - double current_value; - double new_value; - bool had_masters = false; - - { - Glib::Threads::RWLock::WriterLock lm (master_lock); - current_value = get_value_locked (); - if (!_masters.empty()) { - had_masters = true; - } - _masters.clear (); - new_value = get_value_locked (); - } - - if (had_masters) { - MasterStatusChange (); /* EMIT SIGNAL */ - } - - if (new_value != current_value) { - Changed (false, Controllable::NoGroup); + if (_group) { + _group->remove_control (shared_from_this()); } + _group = cg; } bool -AutomationControl::slaved_to (boost::shared_ptr m) const +AutomationControl::check_rt (double val, Controllable::GroupControlDisposition gcd) { - Glib::Threads::RWLock::ReaderLock lm (master_lock); - return _masters.find (m->id()) != _masters.end(); -} + if ((flags() & Controllable::RealTime) && !AudioEngine::instance()->in_process_thread()) { + /* queue change in RT context */ + std::cerr << "::set_value (" << val << ", " << enum_2_string (gcd) << ") called for " << enum_2_string ((AutomationType) _parameter.type()) << ", queueing in RT context\n"; + _session.set_control (shared_from_this(), val, gcd); + return true; + } -bool -AutomationControl::slaved () const -{ - Glib::Threads::RWLock::ReaderLock lm (master_lock); - return !_masters.empty(); + return false; } diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc index b7e77952cb..85634640b9 100644 --- a/libs/ardour/enums.cc +++ b/libs/ardour/enums.cc @@ -140,21 +140,30 @@ setup_enum_writer () #define REGISTER_ENUM(e) i.push_back (e); s.push_back (#e) #define REGISTER_CLASS_ENUM(t,e) i.push_back (t::e); s.push_back (#e) + REGISTER_ENUM (NullAutomation); REGISTER_ENUM (GainAutomation); - REGISTER_ENUM (TrimAutomation); REGISTER_ENUM (PanAzimuthAutomation); REGISTER_ENUM (PanElevationAutomation); REGISTER_ENUM (PanWidthAutomation); + REGISTER_ENUM (PanFrontBackAutomation); + REGISTER_ENUM (PanLFEAutomation); REGISTER_ENUM (PluginAutomation); REGISTER_ENUM (PluginPropertyAutomation); REGISTER_ENUM (SoloAutomation); + REGISTER_ENUM (SoloIsolateAutomation); + REGISTER_ENUM (SoloSafeAutomation); REGISTER_ENUM (MuteAutomation); REGISTER_ENUM (MidiCCAutomation); + REGISTER_ENUM (MidiPgmChangeAutomation); + REGISTER_ENUM (MidiPitchBenderAutomation); + REGISTER_ENUM (MidiChannelPressureAutomation); + REGISTER_ENUM (MidiSystemExclusiveAutomation); REGISTER_ENUM (FadeInAutomation); REGISTER_ENUM (FadeOutAutomation); REGISTER_ENUM (EnvelopeAutomation); - REGISTER_ENUM (SoloIsolateAutomation); - REGISTER_ENUM (SoloSafeAutomation); + REGISTER_ENUM (RecEnableAutomation); + REGISTER_ENUM (RecSafeAutomation); + REGISTER_ENUM (TrimAutomation); REGISTER_ENUM (PhaseAutomation); REGISTER_ENUM (MonitoringAutomation); REGISTER_ENUM (EQGain); diff --git a/libs/ardour/gain_control.cc b/libs/ardour/gain_control.cc index 456fd9b248..3cb8230198 100644 --- a/libs/ardour/gain_control.cc +++ b/libs/ardour/gain_control.cc @@ -33,9 +33,9 @@ using namespace ARDOUR; using namespace std; GainControl::GainControl (Session& session, const Evoral::Parameter ¶m, boost::shared_ptr al) - : AutomationControl (session, param, ParameterDescriptor(param), - al ? al : boost::shared_ptr (new AutomationList (param)), - param.type() == GainAutomation ? X_("gaincontrol") : X_("trimcontrol")) { + : SlavableAutomationControl (session, param, ParameterDescriptor(param), + al ? al : boost::shared_ptr (new AutomationList (param)), + param.type() == GainAutomation ? X_("gaincontrol") : X_("trimcontrol")) { alist()->reset_default (1.0); @@ -44,22 +44,7 @@ GainControl::GainControl (Session& session, const Evoral::Parameter ¶m, boos } void -GainControl::set_value (double val, PBD::Controllable::GroupControlDisposition group_override) -{ - if (writable()) { - _set_value (val, group_override); - } -} - -void -GainControl::set_value_unchecked (double val) -{ - /* used only automation playback */ - _set_value (val, Controllable::NoGroup); -} - -void -GainControl::_set_value (double val, Controllable::GroupControlDisposition group_override) +GainControl::actually_set_value (double val, Controllable::GroupControlDisposition group_override) { val = std::max (std::min (val, (double)_desc.upper), (double)_desc.lower); @@ -75,7 +60,7 @@ GainControl::_set_value (double val, Controllable::GroupControlDisposition group be retrieved by AutomationControl::get_value () */ - AutomationControl::set_value (val, group_override); + AutomationControl::actually_set_value (val, group_override); _session.set_dirty (); } @@ -119,6 +104,23 @@ GainControl::get_user_string () const return std::string(theBuf); } +void +GainControl::inc_gain (gain_t factor) +{ + /* To be used ONLY when doing group-relative gain adjustment, from + * ControlGroup::set_group_values(). + */ + + const float desired_gain = user_double(); + + if (fabsf (desired_gain) < GAIN_COEFF_SMALL) { + // really?! what's the idea here? + actually_set_value (0.000001f + (0.000001f * factor), Controllable::ForGroup); + } else { + actually_set_value (desired_gain + (desired_gain * factor), Controllable::ForGroup); + } +} + void GainControl::recompute_masters_ratios (double val) { diff --git a/libs/ardour/luabindings.cc b/libs/ardour/luabindings.cc index 23dc0d26f0..28214b7e62 100644 --- a/libs/ardour/luabindings.cc +++ b/libs/ardour/luabindings.cc @@ -583,10 +583,10 @@ LuaBindings::common (lua_State* L) .addCast ("to_midi_track") .addFunction ("set_name", &Track::set_name) .addFunction ("can_record", &Track::can_record) - .addFunction ("record_enabled", &Track::record_enabled) - .addFunction ("record_safe", &Track::record_safe) - .addFunction ("set_record_enabled", &Track::set_record_enabled) - .addFunction ("set_record_safe", &Track::set_record_safe) + //.addFunction ("record_enabled", &Track::record_enabled) + //.addFunction ("record_safe", &Track::record_safe) + //.addFunction ("set_record_enabled", &Track::set_record_enabled) + //.addFunction ("set_record_safe", &Track::set_record_safe) .addFunction ("bounceable", &Track::bounceable) .addFunction ("bounce", &Track::bounce) .addFunction ("bounce_range", &Track::bounce_range) diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc index 9a38f9f05a..7c1b3a9399 100644 --- a/libs/ardour/midi_track.cc +++ b/libs/ardour/midi_track.cc @@ -44,6 +44,7 @@ #include "ardour/midi_port.h" #include "ardour/midi_region.h" #include "ardour/midi_track.h" +#include "ardour/monitor_control.h" #include "ardour/parameter_types.h" #include "ardour/port.h" #include "ardour/processor.h" @@ -103,24 +104,24 @@ MidiTrack::create_diskstream () } -void -MidiTrack::set_record_enabled (bool yn, Controllable::GroupControlDisposition group_override) +bool +MidiTrack::can_be_record_safe () { if (_step_editing) { - return; + return false; } - Track::set_record_enabled (yn, group_override); + return Track::can_be_record_safe (); } -void -MidiTrack::set_record_safe (bool yn, Controllable::GroupControlDisposition group_override) +bool +MidiTrack::can_be_record_enabled () { - if (_step_editing) { /* REQUIRES REVIEW */ - return; + if (_step_editing) { + return false; } - Track::set_record_safe (yn, group_override); + return Track::can_be_record_enabled (); } void @@ -372,7 +373,7 @@ MidiTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame if (!_active) { silence (nframes); - if (_meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled())) { + if (_meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _diskstream->record_enabled())) { _meter->reset(); } return 0; @@ -412,7 +413,7 @@ MidiTrack::roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame /* filter captured data before meter sees it */ _capture_filter.filter (bufs); - if (_meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled())) { + if (_meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _diskstream->record_enabled())) { _meter->run (bufs, start_frame, end_frame, nframes, true); } @@ -726,22 +727,7 @@ MidiTrack::set_parameter_automation_state (Evoral::Parameter param, AutoState st } void -MidiTrack::MidiControl::set_value (double val, PBD::Controllable::GroupControlDisposition group_override) -{ - if (writable()) { - _set_value (val, group_override); - } -} - -void -MidiTrack::MidiControl::set_value_unchecked (double val) -{ - /* used only by automation playback */ - _set_value (val, Controllable::NoGroup); -} - -void -MidiTrack::MidiControl::_set_value (double val, PBD::Controllable::GroupControlDisposition group_override) +MidiTrack::MidiControl::actually_set_value (double val, PBD::Controllable::GroupControlDisposition group_override) { const Evoral::Parameter ¶meter = _list ? _list->parameter() : Control::parameter(); const Evoral::ParameterDescriptor &desc = EventTypeMap::instance().descriptor(parameter); @@ -798,7 +784,7 @@ MidiTrack::MidiControl::_set_value (double val, PBD::Controllable::GroupControlD _route->write_immediate_event(size, ev); } - AutomationControl::set_value(val, group_override); + AutomationControl::actually_set_value(val, group_override); } void @@ -958,35 +944,27 @@ MidiTrack::act_on_mute () } void -MidiTrack::set_monitoring (MonitorChoice mc, Controllable::GroupControlDisposition gcd) +MidiTrack::monitoring_changed (bool self, Controllable::GroupControlDisposition gcd) { - if (use_group (gcd, &RouteGroup::is_monitoring)) { - _route_group->apply (&Track::set_monitoring, mc, Controllable::NoGroup); - return; + Track::monitoring_changed (self, gcd); + + /* monitoring state changed, so flush out any on notes at the + * port level. + */ + + PortSet& ports (_output->ports()); + + for (PortSet::iterator p = ports.begin(); p != ports.end(); ++p) { + boost::shared_ptr mp = boost::dynamic_pointer_cast (*p); + if (mp) { + mp->require_resolve (); + } } - if (mc != _monitoring) { + boost::shared_ptr md (midi_diskstream()); - Track::set_monitoring (mc, gcd); - - /* monitoring state changed, so flush out any on notes at the - * port level. - */ - - PortSet& ports (_output->ports()); - - for (PortSet::iterator p = ports.begin(); p != ports.end(); ++p) { - boost::shared_ptr mp = boost::dynamic_pointer_cast (*p); - if (mp) { - mp->require_resolve (); - } - } - - boost::shared_ptr md (midi_diskstream()); - - if (md) { - md->reset_tracker (); - } + if (md) { + md->reset_tracker (); } } diff --git a/libs/ardour/mute_master.cc b/libs/ardour/mute_master.cc index dc40d1fd6b..6817374dc7 100644 --- a/libs/ardour/mute_master.cc +++ b/libs/ardour/mute_master.cc @@ -31,6 +31,8 @@ using namespace ARDOUR; using namespace std; +const string MuteMaster::xml_node_name (X_("MuteMaster")); + const MuteMaster::MutePoint MuteMaster::AllPoints = MuteMaster::MutePoint( PreFader|PostFader|Listen|Main); @@ -155,7 +157,7 @@ MuteMaster::set_state (const XMLNode& node, int /*version*/) XMLNode& MuteMaster::get_state() { - XMLNode* node = new XMLNode (X_("MuteMaster")); + XMLNode* node = new XMLNode (xml_node_name); node->add_property ("mute-point", enum_2_string (_mute_point)); node->add_property ("muted", (_muted_by_self ? X_("yes") : X_("no"))); return *node; diff --git a/libs/ardour/pan_controllable.cc b/libs/ardour/pan_controllable.cc index a6a96787a2..624f397b77 100644 --- a/libs/ardour/pan_controllable.cc +++ b/libs/ardour/pan_controllable.cc @@ -35,27 +35,13 @@ PanControllable::lower () const } void -PanControllable::set_value (double v, PBD::Controllable::GroupControlDisposition group_override) -{ - if (writable()) { - _set_value (v, group_override); - } -} -void -PanControllable::set_value_unchecked (double v) -{ - /* used only automation playback */ - _set_value (v, Controllable::NoGroup); -} - -void -PanControllable::_set_value (double v, Controllable::GroupControlDisposition group_override) +PanControllable::actually_set_value (double v, Controllable::GroupControlDisposition group_override) { boost::shared_ptr p = owner->panner(); if (!p) { /* no panner: just do it */ - AutomationControl::set_value (v, group_override); + AutomationControl::actually_set_value (v, group_override); return; } @@ -76,7 +62,7 @@ PanControllable::_set_value (double v, Controllable::GroupControlDisposition gro } if (can_set) { - AutomationControl::set_value (v, group_override); + AutomationControl::actually_set_value (v, group_override); } } diff --git a/libs/ardour/parameter_descriptor.cc b/libs/ardour/parameter_descriptor.cc index 9fb6b89a0c..e28dbecd35 100644 --- a/libs/ardour/parameter_descriptor.cc +++ b/libs/ardour/parameter_descriptor.cc @@ -75,6 +75,7 @@ ParameterDescriptor::ParameterDescriptor(const Evoral::Parameter& parameter) normal = 0.0f; break; case RecEnableAutomation: + case RecSafeAutomation: lower = 0.0; upper = 1.0; toggled = true; diff --git a/libs/ardour/plugin_insert.cc b/libs/ardour/plugin_insert.cc index 451f801f1d..b0d4d2f9ac 100644 --- a/libs/ardour/plugin_insert.cc +++ b/libs/ardour/plugin_insert.cc @@ -2614,22 +2614,9 @@ PluginInsert::PluginControl::PluginControl (PluginInsert* p, } /** @param val `user' value */ -void -PluginInsert::PluginControl::set_value (double user_val, PBD::Controllable::GroupControlDisposition group_override) -{ - if (writable()) { - _set_value (user_val, group_override); - } -} -void -PluginInsert::PluginControl::set_value_unchecked (double user_val) -{ - /* used only by automation playback */ - _set_value (user_val, Controllable::NoGroup); -} void -PluginInsert::PluginControl::_set_value (double user_val, PBD::Controllable::GroupControlDisposition group_override) +PluginInsert::PluginControl::actually_set_value (double user_val, PBD::Controllable::GroupControlDisposition group_override) { /* FIXME: probably should be taking out some lock here.. */ @@ -2642,13 +2629,13 @@ PluginInsert::PluginControl::_set_value (double user_val, PBD::Controllable::Gro iasp->set_parameter (_list->parameter().id(), user_val); } - AutomationControl::set_value (user_val, group_override); + AutomationControl::actually_set_value (user_val, group_override); } void PluginInsert::PluginControl::catch_up_with_external_value (double user_val) { - AutomationControl::set_value (user_val, Controllable::NoGroup); + AutomationControl::actually_set_value (user_val, Controllable::NoGroup); } XMLNode& @@ -2700,15 +2687,7 @@ PluginInsert::PluginPropertyControl::PluginPropertyControl (PluginInsert* } void -PluginInsert::PluginPropertyControl::set_value (double user_val, PBD::Controllable::GroupControlDisposition /* group_override*/) -{ - if (writable()) { - set_value_unchecked (user_val); - } -} - -void -PluginInsert::PluginPropertyControl::set_value_unchecked (double user_val) +PluginInsert::PluginPropertyControl::actually_set_value (double user_val, Controllable::GroupControlDisposition gcd) { /* Old numeric set_value(), coerce to appropriate datatype if possible. This is lossy, but better than nothing until Ardour's automation system @@ -2724,7 +2703,8 @@ PluginInsert::PluginPropertyControl::set_value_unchecked (double user_val) } _value = value; - AutomationControl::set_value (user_val, Controllable::NoGroup); + + AutomationControl::actually_set_value (user_val, gcd); } XMLNode& diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 8207176729..7a3d37d2a7 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -58,6 +58,7 @@ #include "ardour/panner.h" #include "ardour/panner_shell.h" #include "ardour/parameter_descriptor.h" +#include "ardour/phase_control.h" #include "ardour/plugin_insert.h" #include "ardour/port.h" #include "ardour/port_insert.h" @@ -67,6 +68,8 @@ #include "ardour/route_group.h" #include "ardour/send.h" #include "ardour/session.h" +#include "ardour/solo_control.h" +#include "ardour/solo_isolate_control.h" #include "ardour/unknown_processor.h" #include "ardour/utils.h" #include "ardour/vca.h" @@ -84,6 +87,7 @@ PBD::Signal3, boost::shared_ptr, Rout /** Base class for all routable/mixable objects (tracks and busses) */ Route::Route (Session& sess, string name, Flag flg, DataType default_type) : Stripable (sess, name) + , Muteable (sess, name) , Automatable (sess) , GraphNode (sess._process_graph) , _active (true) @@ -99,18 +103,11 @@ Route::Route (Session& sess, string name, Flag flg, DataType default_type) , _meter_point (MeterPostFader) , _pending_meter_point (MeterPostFader) , _meter_type (MeterPeak) - , _self_solo (false) - , _soloed_by_others_upstream (0) - , _soloed_by_others_downstream (0) - , _solo_isolated (false) - , _solo_isolated_by_upstream (0) , _denormal_protection (false) , _recordable (true) , _silent (false) , _declickable (false) - , _mute_master (new MuteMaster (sess, name)) , _have_internal_generator (false) - , _solo_safe (false) , _default_type (default_type) , _order_key (0) , _has_order_key (false) @@ -141,21 +138,30 @@ Route::init () /* add standard controls */ - _solo_control.reset (new SoloControllable (X_("solo"), shared_from_this ())); - _mute_control.reset (new MuteControllable (X_("mute"), shared_from_this ())); - _phase_control.reset (new PhaseControllable (X_("phase"), shared_from_this ())); + _gain_control.reset (new GainControl (_session, GainAutomation)); + add_control (_gain_control); - _solo_isolate_control.reset (new SoloIsolateControllable (X_("solo-iso"), shared_from_this ())); - _solo_safe_control.reset (new SoloSafeControllable (X_("solo-safe"), shared_from_this ())); + _trim_control.reset (new GainControl (_session, TrimAutomation)); + add_control (_trim_control); + _solo_control.reset (new SoloControl (_session, X_("solo"), *this, *this)); _solo_control->set_flags (Controllable::Flag (_solo_control->flags() | Controllable::Toggle)); - _mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle)); - _phase_control->set_flags (Controllable::Flag (_phase_control->flags() | Controllable::Toggle)); - add_control (_solo_control); + _solo_control->Changed.connect_same_thread (*this, boost::bind (&Route::solo_control_changed, this, _1, _2)); + + _mute_control.reset (new MuteControl (_session, X_("mute"), *this)); + _mute_control->set_flags (Controllable::Flag (_mute_control->flags() | Controllable::Toggle)); add_control (_mute_control); + + _phase_control.reset (new PhaseControl (_session, X_("phase"))); add_control (_phase_control); + _solo_isolate_control.reset (new SoloIsolateControl (_session, X_("solo-iso"), *this, *this)); + add_control (_solo_isolate_control); + + _solo_safe_control.reset (new SoloSafeControl (_session, X_("solo-safe"))); + add_control (_solo_safe_control); + /* panning */ if (!(_flags & Route::MonitorOut)) { @@ -177,9 +183,6 @@ Route::init () * it should be the first processor to be added on every route. */ - _gain_control = boost::shared_ptr (new GainControllable (_session, GainAutomation, shared_from_this ())); - add_control (_gain_control); - _amp.reset (new Amp (_session, X_("Fader"), _gain_control, true)); add_processor (_amp, PostFader); @@ -196,9 +199,6 @@ Route::init () /* and input trim */ - _trim_control = boost::shared_ptr (new GainControllable (_session, TrimAutomation, shared_from_this ())); - add_control (_trim_control); - _trim.reset (new Amp (_session, X_("Trim"), _trim_control, false)); _trim->set_display_to_user (false); @@ -398,83 +398,11 @@ Route::ensure_track_or_route_name(string name, Session &session) return newname; } -void -Route::inc_gain (gain_t factor) -{ - /* To be used ONLY when doing group-relative gain adjustment, from - * ::set_gain() - */ - - float desired_gain = _gain_control->user_double(); - - if (fabsf (desired_gain) < GAIN_COEFF_SMALL) { - // really?! what's the idea here? - _gain_control->route_set_value (0.000001f + (0.000001f * factor)); - } else { - _gain_control->route_set_value (desired_gain + (desired_gain * factor)); - } -} - -void -Route::set_gain (gain_t val, Controllable::GroupControlDisposition gcd) -{ - if (use_group (gcd, &RouteGroup::is_gain)) { - - if (_route_group->is_relative()) { - - gain_t usable_gain = _gain_control->get_value(); - if (usable_gain < 0.000001f) { - usable_gain = 0.000001f; - } - - gain_t delta = val; - if (delta < 0.000001f) { - delta = 0.000001f; - } - - delta -= usable_gain; - - if (delta == 0.0f) - return; - - gain_t factor = delta / usable_gain; - - if (factor > 0.0f) { - factor = _route_group->get_max_factor(factor); - if (factor == 0.0f) { - _amp->gain_control()->Changed (true, gcd); /* EMIT SIGNAL */ - return; - } - } else { - factor = _route_group->get_min_factor(factor); - if (factor == 0.0f) { - _amp->gain_control()->Changed (true, gcd); /* EMIT SIGNAL */ - return; - } - } - - _route_group->foreach_route (boost::bind (&Route::inc_gain, _1, factor)); - - } else { - - _route_group->foreach_route (boost::bind (&Route::set_gain, _1, val, Controllable::NoGroup)); - } - - return; - } - - if (val == _gain_control->get_value()) { - return; - } - - _gain_control->route_set_value (val); -} - void Route::set_trim (gain_t val, Controllable::GroupControlDisposition /* group override */) { // TODO route group, see set_gain() - _trim_control->route_set_value (val); + // _trim_control->route_set_value (val); } void @@ -551,7 +479,7 @@ Route::process_output_buffers (BufferSet& bufs, DENORMAL CONTROL/PHASE INVERT ----------------------------------------------------------------------------------------- */ - if (_phase_invert.any ()) { + if (!_phase_control->none()) { int chn = 0; @@ -560,7 +488,7 @@ Route::process_output_buffers (BufferSet& bufs, for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) { Sample* const sp = i->data(); - if (_phase_invert[chn]) { + if (_phase_control->inverted (chn)) { for (pframes_t nx = 0; nx < nframes; ++nx) { sp[nx] = -sp[nx]; sp[nx] += 1.0e-27f; @@ -577,7 +505,7 @@ Route::process_output_buffers (BufferSet& bufs, for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++chn) { Sample* const sp = i->data(); - if (_phase_invert[chn]) { + if (_phase_control->inverted (chn)) { for (pframes_t nx = 0; nx < nframes; ++nx) { sp[nx] = -sp[nx]; } @@ -808,31 +736,24 @@ Route::passthru_silence (framepos_t start_frame, framepos_t end_frame, pframes_t } void -Route::set_listen (bool yn, Controllable::GroupControlDisposition group_override) +Route::set_listen (bool yn) { - if (_solo_safe) { - return; + if (yn) { + _monitor_send->activate (); + } else { + _monitor_send->deactivate (); } +} - if (use_group (group_override, &RouteGroup::is_solo)) { - _route_group->foreach_route (boost::bind (&Route::set_listen, _1, yn, Controllable::ForGroup)); - return; - } +void +Route::solo_control_changed (bool, Controllable::GroupControlDisposition) +{ + /* nothing to do if we're not using AFL/PFL. But if we are, we need + to alter the active state of the monitor send. + */ - if (_monitor_send) { - if (yn != _monitor_send->active()) { - if (yn) { - _monitor_send->activate (); - _mute_master->set_soloed_by_self (true); - } else { - _monitor_send->deactivate (); - _mute_master->set_soloed_by_self (false); - } - _mute_master->set_soloed_by_others (false); - - /* first argument won't matter because solo <=> listen right now */ - _solo_control->Changed (false, group_override); /* EMIT SIGNAL */ - } + if (Config->get_solo_control_is_listen_control ()) { + set_listen (_solo_control->self_soloed()); } } @@ -847,244 +768,14 @@ Route::listening_via_monitor () const } void -Route::set_solo_safe (bool yn, Controllable::GroupControlDisposition gcd) +Route::push_solo_isolate_upstream (int32_t delta) { - if (_solo_safe != yn) { - _solo_safe = yn; - _solo_safe_control->Changed (true, gcd); /* EMIT SIGNAL */ - } -} - -bool -Route::solo_safe() const -{ - return _solo_safe; -} - -void -Route::clear_all_solo_state () -{ - // ideally this function will never do anything, it only exists to forestall Murphy - bool emit_changed = false; - -#ifndef NDEBUG - // these are really debug messages, but of possible interest. - if (_self_solo) { - PBD::info << string_compose (_("Cleared Explicit solo: %1\n"), name()); - } - if (_soloed_by_others_upstream || _soloed_by_others_downstream) { - PBD::info << string_compose (_("Cleared Implicit solo: %1 up:%2 down:%3\n"), - name(), _soloed_by_others_upstream, _soloed_by_others_downstream); - } -#endif - - if (!_self_solo && (_soloed_by_others_upstream || _soloed_by_others_downstream)) { - // if self-soled, set_solo() will do signal emission - emit_changed = true; - } - - _soloed_by_others_upstream = 0; - _soloed_by_others_downstream = 0; - - { - PBD::Unwinder uw (_solo_safe, false); - set_solo (false, Controllable::NoGroup); - } - - if (emit_changed) { - set_mute_master_solo (); - _solo_control->Changed (false, Controllable::UseGroup); /* EMIT SIGNAL */ - } -} - -void -Route::set_solo (bool yn, Controllable::GroupControlDisposition group_override) -{ - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set solo => %2, grp ? %3 currently self-soloed ? %4\n", - name(), yn, enum_2_string(group_override), self_soloed())); - - if (_solo_safe) { - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change due to solo-safe\n", name())); - return; - } - - if (is_master() || is_monitor() || is_auditioner()) { - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 ignore solo change (master, monitor or auditioner)\n", name())); - return; - } - - if (use_group (group_override, &RouteGroup::is_solo)) { - _route_group->foreach_route (boost::bind (&Route::set_solo, _1, yn, Controllable::ForGroup)); - return; - } - - if (self_soloed() != yn) { - set_self_solo (yn); - _solo_control->Changed (true, group_override); /* EMIT SIGNAL */ - } - - assert (Config->get_solo_control_is_listen_control() || !_monitor_send || !_monitor_send->active()); -} - -void -Route::set_self_solo (bool yn) -{ - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1: set SELF solo => %2\n", name(), yn)); - _self_solo = yn; - set_mute_master_solo (); -} - -void -Route::mod_solo_by_others_upstream (int32_t delta) -{ - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-upstream by %2, current up = %3 down = %4\n", - name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream)); - - uint32_t old_sbu = _soloed_by_others_upstream; - - if (delta < 0) { - if (_soloed_by_others_upstream >= (uint32_t) abs (delta)) { - _soloed_by_others_upstream += delta; - } else { - _soloed_by_others_upstream = 0; - } - } else { - _soloed_by_others_upstream += delta; - } - - DEBUG_TRACE (DEBUG::Solo, string_compose ( - "%1 SbU delta %2 = %3 old = %4 sbd %5 ss %6 exclusive %7\n", - name(), delta, _soloed_by_others_upstream, old_sbu, - _soloed_by_others_downstream, _self_solo, Config->get_exclusive_solo())); - - /* push the inverse solo change to everything that feeds us. - - This is important for solo-within-group. When we solo 1 track out of N that - feed a bus, that track will cause mod_solo_by_upstream (+1) to be called - on the bus. The bus then needs to call mod_solo_by_downstream (-1) on all - tracks that feed it. This will silence them if they were audible because - of a bus solo, but the newly soloed track will still be audible (because - it is self-soloed). - - but .. do this only when we are being told to solo-by-upstream (i.e delta = +1), - not in reverse. - */ - - if ((_self_solo || _soloed_by_others_downstream) && - ((old_sbu == 0 && _soloed_by_others_upstream > 0) || - (old_sbu > 0 && _soloed_by_others_upstream == 0))) { - - if (delta > 0 || !Config->get_exclusive_solo()) { - DEBUG_TRACE (DEBUG::Solo, string_compose("\t ... INVERT push from %1\n", _name)); - for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) { - if (i->sends_only) { - continue; - } - boost::shared_ptr sr = i->r.lock(); - if (sr) { - sr->mod_solo_by_others_downstream (-delta); - } - } - } - } - - set_mute_master_solo (); - cerr << name() << " SC->Changed (false, UseGroup)\n"; - _solo_control->Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */ -} - -void -Route::mod_solo_by_others_downstream (int32_t delta) -{ - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod solo-by-downstream by %2, current up = %3 down = %4\n", - name(), delta, _soloed_by_others_upstream, _soloed_by_others_downstream)); - - if (delta < 0) { - if (_soloed_by_others_downstream >= (uint32_t) abs (delta)) { - _soloed_by_others_downstream += delta; - } else { - _soloed_by_others_downstream = 0; - } - } else { - _soloed_by_others_downstream += delta; - } - - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 SbD delta %2 = %3\n", name(), delta, _soloed_by_others_downstream)); - - set_mute_master_solo (); - _solo_control->Changed (false, Controllable::UseGroup); /* EMIT SIGNAL */ -} - -void -Route::set_mute_master_solo () -{ - _mute_master->set_soloed_by_self (self_soloed()); - _mute_master->set_soloed_by_others (soloed_by_others_downstream() || soloed_by_others_upstream()); -} - -void -Route::mod_solo_isolated_by_upstream (bool yn) -{ - bool old = solo_isolated (); - DEBUG_TRACE (DEBUG::Solo, string_compose ("%1 mod_solo_isolated_by_upstream cur: %2 d: %3\n", - name(), _solo_isolated_by_upstream, yn ? "+1" : "-1")); - - if (!yn) { - if (_solo_isolated_by_upstream >= 1) { - _solo_isolated_by_upstream--; - } else { - _solo_isolated_by_upstream = 0; - } - } else { - _solo_isolated_by_upstream++; - } - - if (solo_isolated() != old) { - /* solo isolated status changed */ - _mute_master->set_solo_ignore (solo_isolated()); - _solo_isolate_control->Changed (false, Controllable::NoGroup); /* EMIT SIGNAL */ - } -} - -void -Route::set_solo_isolated (bool yn, Controllable::GroupControlDisposition group_override) -{ - if (is_master() || is_monitor() || is_auditioner()) { - return; - } - - if (use_group (group_override, &RouteGroup::is_solo)) { - _route_group->foreach_route (boost::bind (&Route::set_solo_isolated, _1, yn, Controllable::ForGroup)); - return; - } - - bool changed = false; - - if (yn) { - if (_solo_isolated == false) { - _mute_master->set_solo_ignore (true); - changed = true; - } - _solo_isolated = true; - } else { - if (_solo_isolated == true) { - _solo_isolated = false; - _mute_master->set_solo_ignore (false); - changed = true; - } - } - - - if (!changed) { - return; - } - /* forward propagate solo-isolate status to everything fed by this route, but not those via sends only */ boost::shared_ptr routes = _session.get_routes (); for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { - if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { + if ((*i).get() == this || !(*i)->can_solo()) { continue; } @@ -1092,87 +783,26 @@ Route::set_solo_isolated (bool yn, Controllable::GroupControlDisposition group_o bool does_feed = feeds (*i, &sends_only); if (does_feed && !sends_only) { - (*i)->mod_solo_isolated_by_upstream (yn); + (*i)->solo_isolate_control()->mod_solo_isolated_by_upstream (delta); } } - - /* XXX should we back-propagate as well? (April 2010: myself and chris goddard think not) */ - - _solo_isolate_control->Changed (true, group_override); /* EMIT SIGNAL */ -} - -bool -Route::solo_isolated () const -{ - return (_solo_isolated == true) || (_solo_isolated_by_upstream > 0); } void -Route::set_mute_points (MuteMaster::MutePoint mp) +Route::push_solo_upstream (int delta) { - _mute_master->set_mute_points (mp); - mute_points_changed (); /* EMIT SIGNAL */ - - if (_mute_master->muted_by_self()) { - _mute_control->Changed (true, Controllable::UseGroup); /* EMIT SIGNAL */ + DEBUG_TRACE (DEBUG::Solo, string_compose("\t ... INVERT push from %1\n", _name)); + for (FedBy::iterator i = _fed_by.begin(); i != _fed_by.end(); ++i) { + if (i->sends_only) { + continue; + } + boost::shared_ptr sr (i->r.lock()); + if (sr) { + sr->solo_control()->mod_solo_by_others_downstream (-delta); + } } } -void -Route::set_mute (bool yn, Controllable::GroupControlDisposition group_override) -{ - if (use_group (group_override, &RouteGroup::is_mute)) { - _route_group->foreach_route (boost::bind (&Route::set_mute, _1, yn, Controllable::ForGroup)); - return; - } - - if (muted() != yn) { - _mute_master->set_muted_by_self (yn); - /* allow any derived classes to respond to the mute change - before anybody else knows about it. - */ - act_on_mute (); - /* tell everyone else */ - _mute_control->Changed (true, Controllable::NoGroup); /* EMIT SIGNAL */ - } -} - -bool -Route::muted () const -{ - return _mute_master->muted_by_self(); -} - -bool -Route::muted_by_others_soloing () const -{ - // This method is only used by route_ui for display state. - // The real thing is MuteMaster::muted_by_others_at() - - //master is never muted by others - if (is_master()) - return false; - - //now check to see if something is soloed (and I am not) - //see also MuteMaster::mute_gain_at() - return _session.soloing() && !soloed() && !solo_isolated(); -} - -bool -Route::muted_by_others () const -{ - // This method is only used by route_ui for display state. - // The real thing is MuteMaster::muted_by_others_at() - - //master is never muted by others - if (is_master()) - return false; - - //now check to see if something is soloed (and I am not) - //see also MuteMaster::mute_gain_at() - return _mute_master->muted_by_others() || (_session.soloing() && !soloed() && !solo_isolated()); -} - #if 0 static void dump_processors(const string& name, const list >& procs) @@ -2743,8 +2373,6 @@ Route::state(bool full_state) node->add_property("active", _active?"yes":"no"); string p; - boost::to_string (_phase_invert, p); - node->add_property("phase-invert", p); node->add_property("denormal-protection", _denormal_protection?"yes":"no"); node->add_property("meter-point", enum_2_string (_meter_point)); @@ -2756,18 +2384,9 @@ Route::state(bool full_state) snprintf (buf, sizeof (buf), "%d", _order_key); node->add_property ("order-key", buf); - node->add_property ("self-solo", (_self_solo ? "yes" : "no")); - snprintf (buf, sizeof (buf), "%d", _soloed_by_others_upstream); - node->add_property ("soloed-by-upstream", buf); - snprintf (buf, sizeof (buf), "%d", _soloed_by_others_downstream); - node->add_property ("soloed-by-downstream", buf); - node->add_property ("solo-isolated", solo_isolated() ? "yes" : "no"); - node->add_property ("solo-safe", _solo_safe ? "yes" : "no"); node->add_child_nocopy (_input->state (full_state)); node->add_child_nocopy (_output->state (full_state)); - node->add_child_nocopy (_solo_control->get_state ()); - node->add_child_nocopy (_mute_control->get_state ()); node->add_child_nocopy (_mute_master->get_state ()); if (full_state) { @@ -2866,7 +2485,7 @@ Route::set_state (const XMLNode& node, int version) _strict_io = string_is_affirmative (prop->value()); } - if (is_master() || is_monitor() || is_auditioner()) { + if (!can_solo()) { _mute_master->set_solo_ignore (true); } @@ -2933,31 +2552,10 @@ Route::set_state (const XMLNode& node, int version) // this looks up the internal instrument in processors reset_instrument_info(); - if ((prop = node.property ("self-solo")) != 0) { - set_self_solo (string_is_affirmative (prop->value())); - } - - if ((prop = node.property ("soloed-by-upstream")) != 0) { - _soloed_by_others_upstream = 0; // needed for mod_.... () to work - mod_solo_by_others_upstream (atoi (prop->value())); - } - - if ((prop = node.property ("soloed-by-downstream")) != 0) { - _soloed_by_others_downstream = 0; // needed for mod_.... () to work - mod_solo_by_others_downstream (atoi (prop->value())); - } - - if ((prop = node.property ("solo-isolated")) != 0) { - set_solo_isolated (string_is_affirmative (prop->value()), Controllable::NoGroup); - } - - if ((prop = node.property ("solo-safe")) != 0) { - set_solo_safe (string_is_affirmative (prop->value()), Controllable::NoGroup); - } - - if ((prop = node.property (X_("phase-invert"))) != 0) { - set_phase_invert (boost::dynamic_bitset<> (prop->value ())); - } + _solo_control->set_state (node, version); + _solo_safe_control->set_state (node, version); + _solo_isolate_control->set_state (node, version); + _mute_control->set_state (node, version); if ((prop = node.property (X_("denormal-protection"))) != 0) { set_denormal_protection (string_is_affirmative (prop->value())); @@ -3046,7 +2644,7 @@ Route::set_state (const XMLNode& node, int version) set_remote_control_id_internal (x); } - } else if (child->name() == X_("MuteMaster")) { + } else if (child->name() == MuteMaster::xml_node_name) { _mute_master->set_state (*child, version); } else if (child->name() == Automatable::xml_node_name) { @@ -3089,26 +2687,10 @@ Route::set_state_2X (const XMLNode& node, int version) _mute_master->set_solo_ignore (true); } - if ((prop = node.property (X_("phase-invert"))) != 0) { - boost::dynamic_bitset<> p (_input->n_ports().n_audio ()); - if (string_is_affirmative (prop->value ())) { - p.set (); - } - set_phase_invert (p); - } - if ((prop = node.property (X_("denormal-protection"))) != 0) { set_denormal_protection (string_is_affirmative (prop->value())); } - if ((prop = node.property (X_("soloed"))) != 0) { - bool yn = string_is_affirmative (prop->value()); - - /* XXX force reset of solo status */ - - set_solo (yn); - } - if ((prop = node.property (X_("muted"))) != 0) { bool first = true; @@ -3870,11 +3452,11 @@ Route::input_change_handler (IOChange change, void * /*src*/) contains ConfigurationChanged */ configure_processors (0); - _phase_invert.resize (_input->n_ports().n_audio ()); + _phase_control->resize (_input->n_ports().n_audio ()); io_changed (); /* EMIT SIGNAL */ } - if (_soloed_by_others_upstream || _solo_isolated_by_upstream) { + if (_solo_control->soloed_by_others_upstream() || _solo_isolate_control->solo_isolated_by_upstream()) { int sbou = 0; int ibou = 0; boost::shared_ptr routes = _session.get_routes (); @@ -3889,38 +3471,36 @@ Route::input_change_handler (IOChange change, void * /*src*/) if ((*i)->soloed()) { ++sbou; } - if ((*i)->solo_isolated()) { + if ((*i)->solo_isolate_control()->solo_isolated()) { ++ibou; } } } } - int delta = sbou - _soloed_by_others_upstream; - int idelta = ibou - _solo_isolated_by_upstream; + int delta = sbou - _solo_control->soloed_by_others_upstream(); + int idelta = ibou - _solo_isolate_control->solo_isolated_by_upstream(); if (idelta < -1) { PBD::warning << string_compose ( _("Invalid Solo-Isolate propagation: from:%1 new:%2 - old:%3 = delta:%4"), - _name, ibou, _solo_isolated_by_upstream, idelta) + _name, ibou, _solo_isolate_control->solo_isolated_by_upstream(), idelta) << endmsg; } - if (_soloed_by_others_upstream) { + if (_solo_control->soloed_by_others_upstream()) { // ignore new connections (they're not propagated) if (delta <= 0) { - mod_solo_by_others_upstream (delta); + _solo_control->mod_solo_by_others_upstream (delta); } } - if (_solo_isolated_by_upstream) { + if (_solo_isolate_control->solo_isolated_by_upstream()) { // solo-isolate currently only propagates downstream if (idelta < 0) { - mod_solo_isolated_by_upstream (false); + _solo_isolate_control->mod_solo_isolated_by_upstream (1); } - // TODO think: mod_solo_isolated_by_upstream() does not take delta arg, - // but idelta can't be smaller than -1, can it? //_solo_isolated_by_upstream = ibou; } @@ -3933,11 +3513,11 @@ Route::input_change_handler (IOChange change, void * /*src*/) bool sends_only; bool does_feed = feeds (*i, &sends_only); if (delta <= 0 && does_feed && !sends_only) { - (*i)->mod_solo_by_others_upstream (delta); + (*i)->solo_control()->mod_solo_by_others_upstream (delta); } if (idelta < 0 && does_feed && !sends_only) { - (*i)->mod_solo_isolated_by_upstream (false); + (*i)->solo_isolate_control()->mod_solo_isolated_by_upstream (-1); } } } @@ -3963,7 +3543,7 @@ Route::output_change_handler (IOChange change, void * /*src*/) io_changed (); /* EMIT SIGNAL */ } - if (_soloed_by_others_downstream) { + if (_solo_control->soloed_by_others_downstream()) { int sbod = 0; /* checking all all downstream routes for * explicit of implict solo is a rather drastic measure, @@ -3986,20 +3566,20 @@ Route::output_change_handler (IOChange change, void * /*src*/) } } } - int delta = sbod - _soloed_by_others_downstream; + int delta = sbod - _solo_control->soloed_by_others_downstream(); if (delta <= 0) { // do not allow new connections to change implicit solo (no propagation) - mod_solo_by_others_downstream (delta); + _solo_control->mod_solo_by_others_downstream (delta); // Session::route_solo_changed() does not propagate indirect solo-changes // propagate upstream to tracks for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { - if ((*i).get() == this || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { + if ((*i).get() == this || !can_solo()) { continue; } bool sends_only; bool does_feed = (*i)->feeds (shared_from_this(), &sends_only); if (delta != 0 && does_feed && !sends_only) { - (*i)->mod_solo_by_others_downstream (delta); + (*i)->solo_control()->mod_solo_by_others_downstream (delta); } } @@ -4639,41 +4219,6 @@ Route::internal_send_for (boost::shared_ptr target) const return boost::shared_ptr(); } -/** @param c Audio channel index. - * @param yn true to invert phase, otherwise false. - */ -void -Route::set_phase_invert (uint32_t c, bool yn) -{ - if (_phase_invert[c] != yn) { - _phase_invert[c] = yn; - _phase_control->Changed (true, Controllable::NoGroup); /* EMIT SIGNAL */ - _session.set_dirty (); - } -} - -void -Route::set_phase_invert (boost::dynamic_bitset<> p) -{ - if (_phase_invert != p) { - _phase_invert = p; - _phase_control->Changed (true, Controllable::NoGroup); /* EMIT SIGNAL */ - _session.set_dirty (); - } -} - -bool -Route::phase_invert (uint32_t c) const -{ - return _phase_invert[c]; -} - -boost::dynamic_bitset<> -Route::phase_invert () const -{ - return _phase_invert; -} - void Route::set_denormal_protection (bool yn) { @@ -4735,20 +4280,16 @@ Route::gain_control() const return _gain_control; } -boost::shared_ptr +boost::shared_ptr Route::trim_control() const { return _trim_control; } -boost::shared_ptr +boost::shared_ptr Route::phase_control() const { - if (phase_invert().size()) { - return _phase_control; - } else { - return boost::shared_ptr(); - } + return _phase_control; } boost::shared_ptr @@ -4841,12 +4382,6 @@ Route::has_io_processor_named (const string& name) return false; } -MuteMaster::MutePoint -Route::mute_points () const -{ - return _mute_master->mute_points (); -} - void Route::set_processor_positions () { @@ -5903,7 +5438,7 @@ Route::vca_assign (boost::shared_ptr vca) { _gain_control->add_master (vca->gain_control()); _solo_control->add_master (vca->solo_control()); - _mute_control->add_master (vca->mute_control()); + // _mute_control->add_master (vca->mute_control()); } void @@ -5913,10 +5448,48 @@ Route::vca_unassign (boost::shared_ptr vca) /* unassign from all */ _gain_control->clear_masters (); _solo_control->clear_masters (); - _mute_control->clear_masters (); + //_mute_control->clear_masters (); } else { _gain_control->remove_master (vca->gain_control()); _solo_control->remove_master (vca->solo_control()); - _mute_control->remove_master (vca->mute_control()); + //_mute_control->remove_master (vca->mute_control()); + } +} + +bool +Route::muted_by_others_soloing () const +{ + // This method is only used by route_ui for display state. + // The DSP version is MuteMaster::muted_by_others_at() + + if (!can_be_muted_by_others ()) { + return false; + } + + return _session.soloing() && !_solo_control->soloed() && !_solo_isolate_control->solo_isolated(); +} + +bool +Route::muted_by_others () const +{ + // This method is only used by route_ui for display state. + // The DSP version is MuteMaster::muted_by_others_at() + + if (!can_be_muted_by_others()) { + return false; + } + + return _mute_master->muted_by_others(); +} + +void +Route::clear_all_solo_state () +{ + double v = _solo_safe_control->get_value (); + + _solo_control->clear_all_solo_state (); + + if (v != 0.0) { + _solo_safe_control->set_value (v, Controllable::NoGroup); } } diff --git a/libs/ardour/route_controls.cc b/libs/ardour/route_controls.cc deleted file mode 100644 index ad5408d06d..0000000000 --- a/libs/ardour/route_controls.cc +++ /dev/null @@ -1,431 +0,0 @@ -/* - Copyright (C) 2000 Paul Davis - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#ifdef WAF_BUILD -#include "libardour-config.h" -#endif - -#include "ardour/automation_control.h" -#include "ardour/parameter_descriptor.h" -#include "ardour/route.h" -#include "ardour/session.h" - -#include "i18n.h" - -using namespace std; -using namespace ARDOUR; -using namespace PBD; - -void -Route::set_control (AutomationType type, double val, PBD::Controllable::GroupControlDisposition group_override) -{ - boost::shared_ptr rl; - - switch (type) { - case GainAutomation: - /* route must mediate group control */ - set_gain (val, group_override); - break; - - case TrimAutomation: - /* route must mediate group control */ - set_trim (val, group_override); - break; - - case RecEnableAutomation: - /* session must mediate group control */ - rl.reset (new RouteList); - rl->push_back (shared_from_this()); - _session.set_record_enabled (rl, val >= 0.5 ? true : false, Session::rt_cleanup, group_override); - break; - - case SoloAutomation: - /* session must mediate group control */ - rl.reset (new RouteList); - rl->push_back (shared_from_this()); - if (Config->get_solo_control_is_listen_control()) { - _session.set_listen (rl, val >= 0.5 ? true : false, Session::rt_cleanup, group_override); - } else { - _session.set_solo (rl, val >= 0.5 ? true : false, Session::rt_cleanup, group_override); - } - break; - - case MuteAutomation: - /* session must mediate group control */ - rl.reset (new RouteList); - rl->push_back (shared_from_this()); - _session.set_mute (rl, val >= 0.5 ? true : false, Session::rt_cleanup, group_override); - return; - break; - - default: - /* Not a route automation control */ - fatal << string_compose (_("programming error: %1%2\n"), X_("illegal type of route automation control passed to Route::set_control(): "), enum_2_string(type)) << endmsg; - /*NOTREACHED*/ - return; - } -} - - -Route::RouteAutomationControl::RouteAutomationControl (const std::string& name, - AutomationType atype, - boost::shared_ptr alist, - boost::shared_ptr r) - : AutomationControl (r->session(), Evoral::Parameter (atype), - ParameterDescriptor (Evoral::Parameter (atype)), - alist, name) - , _route (r) -{ -} - -double -Route::BooleanRouteAutomationControl::get_masters_value_locked () const -{ - /* masters (read/write) lock must be held */ - - /* if any master is enabled (val > 0.0) then we consider the master - value to be 1.0 - */ - - for (Masters::const_iterator mr = _masters.begin(); mr != _masters.end(); ++mr) { - if (mr->second.master()->get_value()) { - return 1.0; - } - } - - return 0.0; -} - - - -Route::GainControllable::GainControllable (Session& s, AutomationType atype, boost::shared_ptr r) - : GainControl (s, Evoral::Parameter(atype)) - , _route (r) -{ - -} - -Route::SoloControllable::SoloControllable (std::string name, boost::shared_ptr r) - : BooleanRouteAutomationControl (name, SoloAutomation, boost::shared_ptr(), r) -{ - boost::shared_ptr gl(new AutomationList(Evoral::Parameter(SoloAutomation))); - gl->set_interpolation(Evoral::ControlList::Discrete); - set_list (gl); -} - -void -Route::SoloControllable::master_changed (bool from_self, PBD::Controllable::GroupControlDisposition gcd) -{ - boost::shared_ptr r = _route.lock (); - - if (!r) { - return; - } - - bool master_soloed; - - { - Glib::Threads::RWLock::ReaderLock lm (master_lock); - master_soloed = (bool) get_masters_value_locked (); - } - - /* Master is considered equivalent to an upstream solo control, not - * direct control over self-soloed. - */ - - r->mod_solo_by_others_upstream (master_soloed ? 1 : -1); - - AutomationControl::master_changed (false, gcd); -} - -void -Route::SoloControllable::set_value (double val, PBD::Controllable::GroupControlDisposition group_override) -{ - if (writable()) { - _set_value (val, group_override); - } -} - -void -Route::SoloControllable::_set_value (double val, PBD::Controllable::GroupControlDisposition group_override) -{ - boost::shared_ptr r = _route.lock (); - if (!r) { - return; - } - r->set_control (SoloAutomation, val, group_override); -} - -void -Route::SoloControllable::set_value_unchecked (double val) -{ - /* Used only by automation playback */ - - _set_value (val, Controllable::NoGroup); -} - -double -Route::SoloControllable::get_value () const -{ - if (slaved()) { - Glib::Threads::RWLock::ReaderLock lm (master_lock); - return get_masters_value_locked () ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO; - } - - if (_list && ((AutomationList*)_list.get())->automation_playback()) { - // Playing back automation, get the value from the list - return AutomationControl::get_value(); - } - - boost::shared_ptr r = _route.lock (); - - if (!r) { - return 0; - } - - if (Config->get_solo_control_is_listen_control()) { - return r->listening_via_monitor() ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO; - } else { - return r->self_soloed() ? GAIN_COEFF_UNITY : GAIN_COEFF_ZERO; - } -} - -Route::MuteControllable::MuteControllable (std::string name, boost::shared_ptr r) - : BooleanRouteAutomationControl (name, MuteAutomation, boost::shared_ptr(), r) - , _route (r) -{ - boost::shared_ptr gl(new AutomationList(Evoral::Parameter(MuteAutomation))); - gl->set_interpolation(Evoral::ControlList::Discrete); - set_list (gl); -} - -void -Route::MuteControllable::set_superficial_value(bool muted) -{ - /* Note we can not use AutomationControl::set_value here since it will emit - Changed(), but the value will not be correct to the observer. */ - - const bool to_list = _list && ((AutomationList*)_list.get ())->automation_write (); - const double where = _session.audible_frame (); - - if (to_list) { - /* Note that we really need this: - * if (as == Touch && _list->in_new_write_pass ()) { - * alist->start_write_pass (_session.audible_frame ()); - * } - * here in the case of the user calling from a GUI or whatever. - * Without the ability to distinguish between user and - * automation-initiated changes, we lose the "touch mute" - * behaviour we have in AutomationController::toggled (). - */ - _list->set_in_write_pass (true, false, where); - } - - Control::set_double (muted, where, to_list); -} - -void -Route::MuteControllable::master_changed (bool from_self, PBD::Controllable::GroupControlDisposition gcd) -{ - bool master_muted; - - { - Glib::Threads::RWLock::ReaderLock lm (master_lock); - master_muted = (bool) get_masters_value_locked (); - } - - boost::shared_ptr r (_route.lock()); - if (r) { - r->mute_master()->mod_muted_by_others (master_muted ? 1 : -1); - } - - AutomationControl::master_changed (false, gcd); -} - -void -Route::MuteControllable::set_value (double val, PBD::Controllable::GroupControlDisposition group_override) -{ - if (writable()) { - _set_value (val, group_override); - } -} - -void -Route::MuteControllable::set_value_unchecked (double val) -{ - /* used only automation playback */ - _set_value (val, Controllable::NoGroup); -} - -void -Route::MuteControllable::_set_value (double val, Controllable::GroupControlDisposition group_override) -{ - boost::shared_ptr r = _route.lock (); - - if (!r) { - return; - } - - if (_list && ((AutomationList*)_list.get())->automation_playback()) { - // Set superficial/automation value to drive controller (and possibly record) - const bool bval = ((val >= 0.5) ? true : false); - set_superficial_value (bval); - // Playing back automation, set route mute directly - r->set_mute (bval, Controllable::NoGroup); - } else { - r->set_control (MuteAutomation, val, group_override); - } -} - -double -Route::MuteControllable::get_value () const -{ - if (slaved()) { - Glib::Threads::RWLock::ReaderLock lm (master_lock); - return get_masters_value_locked () ? 1.0 : 0.0; - } - - if (_list && ((AutomationList*)_list.get())->automation_playback()) { - // Playing back automation, get the value from the list - return AutomationControl::get_value(); - } - - // Not playing back automation, get the actual route mute value - boost::shared_ptr r = _route.lock (); - return (r && r->muted()) ? 1.0 : 0.0; -} - -Route::PhaseControllable::PhaseControllable (std::string name, boost::shared_ptr r) - : BooleanRouteAutomationControl (name, PhaseAutomation, boost::shared_ptr(), r) - , _current_phase (0) -{ - boost::shared_ptr gl(new AutomationList(Evoral::Parameter(PhaseAutomation))); - gl->set_interpolation(Evoral::ControlList::Discrete); - set_list (gl); -} - -void -Route::PhaseControllable::set_value (double v, PBD::Controllable::GroupControlDisposition /* group_override */) -{ - boost::shared_ptr r = _route.lock (); - if (r->phase_invert().size()) { - if (v == 0 || (v < 1 && v > 0.9) ) { - r->set_phase_invert (_current_phase, false); - } else { - r->set_phase_invert (_current_phase, true); - } - } -} - -double -Route::PhaseControllable::get_value () const -{ - boost::shared_ptr r = _route.lock (); - if (!r) { - return 0.0; - } - return (double) r->phase_invert (_current_phase); -} - -void -Route::PhaseControllable::set_channel (uint32_t c) -{ - _current_phase = c; -} - -uint32_t -Route::PhaseControllable::channel () const -{ - return _current_phase; -} - -Route::SoloIsolateControllable::SoloIsolateControllable (std::string name, boost::shared_ptr r) - : BooleanRouteAutomationControl (name, SoloIsolateAutomation, boost::shared_ptr(), r) -{ - boost::shared_ptr gl(new AutomationList(Evoral::Parameter(SoloIsolateAutomation))); - gl->set_interpolation(Evoral::ControlList::Discrete); - set_list (gl); -} - - -double -Route::SoloIsolateControllable::get_value () const -{ - boost::shared_ptr r = _route.lock (); - if (!r) { - return 0.0; /* "false" */ - } - - return r->solo_isolated() ? 1.0 : 0.0; -} - -void -Route::SoloIsolateControllable::set_value (double val, PBD::Controllable::GroupControlDisposition gcd) -{ - _set_value (val, gcd); -} - -void -Route::SoloIsolateControllable::_set_value (double val, PBD::Controllable::GroupControlDisposition) -{ - boost::shared_ptr r = _route.lock (); - if (!r) { - return; - } - - /* no group semantics yet */ - r->set_solo_isolated (val >= 0.5 ? true : false); -} - -Route::SoloSafeControllable::SoloSafeControllable (std::string name, boost::shared_ptr r) - : BooleanRouteAutomationControl (name, SoloSafeAutomation, boost::shared_ptr(), r) -{ - boost::shared_ptr gl(new AutomationList(Evoral::Parameter(SoloSafeAutomation))); - gl->set_interpolation(Evoral::ControlList::Discrete); - set_list (gl); -} - -void -Route::SoloSafeControllable::set_value (double val, PBD::Controllable::GroupControlDisposition gcd) -{ - _set_value (val, gcd); -} - -void -Route::SoloSafeControllable::_set_value (double val, PBD::Controllable::GroupControlDisposition) -{ - boost::shared_ptr r = _route.lock (); - if (!r) { - return; - } - - /* no group semantics yet */ - r->set_solo_safe (val >= 0.5 ? true : false); -} - -double -Route::SoloSafeControllable::get_value () const -{ - boost::shared_ptr r = _route.lock (); - if (!r) { - return 0.0; /* "false" */ - } - - return r->solo_safe() ? 1.0 : 0.0; -} diff --git a/libs/ardour/route_graph.cc b/libs/ardour/route_graph.cc index 910141a440..70b9b48d6f 100644 --- a/libs/ardour/route_graph.cc +++ b/libs/ardour/route_graph.cc @@ -20,6 +20,7 @@ #include "ardour/route.h" #include "ardour/route_graph.h" +#include "ardour/track.h" #include "i18n.h" @@ -195,21 +196,39 @@ struct RouteRecEnabledComparator { bool operator () (GraphVertex r1, GraphVertex r2) const { - if (r1->record_enabled()) { - if (r2->record_enabled()) { - /* both rec-enabled, just use signal order */ + boost::shared_ptr t1 (boost::dynamic_pointer_cast(r1)); + boost::shared_ptr t2 (boost::dynamic_pointer_cast(r2)); + + if (!t1) { + if (!t2) { + /* makes no difference which is first, use signal order */ return r1->order_key () < r2->order_key (); } else { - /* r1 rec-enabled, r2 not rec-enabled, run r2 early */ + /* r1 is not a track, r2 is, run it early */ + return false; + } + } + + if (!t2) { + /* we already tested !t1, so just use signal order */ + return r1->order_key () < r2->order_key (); + } + + if (t1->rec_enable_control()->get_value()) { + if (t2->rec_enable_control()->get_value()) { + /* both rec-enabled, just use signal order */ + return t1->order_key () < t2->order_key (); + } else { + /* t1 rec-enabled, t2 not rec-enabled, run t2 early */ return false; } } else { - if (r2->record_enabled()) { - /* r2 rec-enabled, r1 not rec-enabled, run r1 early */ + if (t2->rec_enable_control()->get_value()) { + /* t2 rec-enabled, t1 not rec-enabled, run t1 early */ return true; } else { /* neither rec-enabled, use signal order */ - return r1->order_key () < r2->order_key (); + return t1->order_key () < t2->order_key (); } } } diff --git a/libs/ardour/route_group.cc b/libs/ardour/route_group.cc index b482bb85d3..137e2c4734 100644 --- a/libs/ardour/route_group.cc +++ b/libs/ardour/route_group.cc @@ -28,6 +28,7 @@ #include "ardour/amp.h" #include "ardour/audio_track.h" +#include "ardour/monitor_control.h" #include "ardour/route.h" #include "ardour/route_group.h" #include "ardour/session.h" @@ -57,21 +58,21 @@ void RouteGroup::make_property_quarks () { Properties::relative.property_id = g_quark_from_static_string (X_("relative")); - DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for relative = %1\n", Properties::relative.property_id)); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for relative = %1\n", Properties::relative.property_id)); Properties::active.property_id = g_quark_from_static_string (X_("active")); - DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for active = %1\n", Properties::active.property_id)); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for active = %1\n", Properties::active.property_id)); Properties::hidden.property_id = g_quark_from_static_string (X_("hidden")); - DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id)); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for hidden = %1\n", Properties::hidden.property_id)); Properties::gain.property_id = g_quark_from_static_string (X_("gain")); - DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for gain = %1\n", Properties::gain.property_id)); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for gain = %1\n", Properties::gain.property_id)); Properties::mute.property_id = g_quark_from_static_string (X_("mute")); - DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for mute = %1\n", Properties::mute.property_id)); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for mute = %1\n", Properties::mute.property_id)); Properties::solo.property_id = g_quark_from_static_string (X_("solo")); - DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for solo = %1\n", Properties::solo.property_id)); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for solo = %1\n", Properties::solo.property_id)); Properties::recenable.property_id = g_quark_from_static_string (X_("recenable")); - DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for recenable = %1\n", Properties::recenable.property_id)); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for recenable = %1\n", Properties::recenable.property_id)); Properties::select.property_id = g_quark_from_static_string (X_("select")); - DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for select = %1\n", Properties::select.property_id)); + DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for select = %1\n", Properties::select.property_id)); Properties::route_active.property_id = g_quark_from_static_string (X_("route-active")); DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for route-active = %1\n", Properties::route_active.property_id)); Properties::color.property_id = g_quark_from_static_string (X_("color")); @@ -96,6 +97,11 @@ RouteGroup::RouteGroup (Session& s, const string &n) : SessionObject (s, n) , routes (new RouteList) , ROUTE_GROUP_DEFAULT_PROPERTIES + , _solo_group (new ControlGroup (SoloAutomation)) + , _mute_group (new ControlGroup (MuteAutomation)) + , _rec_enable_group (new ControlGroup (RecEnableAutomation)) + , _gain_group (new ControlGroup (GainAutomation)) + , _monitoring_group (new ControlGroup (MonitoringAutomation)) { _xml_node_name = X_("RouteGroup"); @@ -114,6 +120,12 @@ RouteGroup::RouteGroup (Session& s, const string &n) RouteGroup::~RouteGroup () { + _solo_group->clear (); + _mute_group->clear (); + _gain_group->clear (); + _rec_enable_group->clear (); + _monitoring_group->clear (); + for (RouteList::iterator i = routes->begin(); i != routes->end();) { RouteList::iterator tmp = i; ++tmp; @@ -140,6 +152,15 @@ RouteGroup::add (boost::shared_ptr r) routes->push_back (r); + _solo_group->add_control (r->solo_control()); + _mute_group->add_control (r->mute_control()); + _gain_group->add_control (r->gain_control()); + boost::shared_ptr trk = boost::dynamic_pointer_cast (r); + if (trk) { + _rec_enable_group->add_control (trk->rec_enable_control()); + _monitoring_group->add_control (trk->monitoring_control()); + } + r->set_route_group (this); r->DropReferences.connect_same_thread (*this, boost::bind (&RouteGroup::remove_when_going_away, this, boost::weak_ptr (r))); @@ -165,6 +186,14 @@ RouteGroup::remove (boost::shared_ptr r) if ((i = find (routes->begin(), routes->end(), r)) != routes->end()) { r->set_route_group (0); + _solo_group->remove_control (r->solo_control()); + _mute_group->remove_control (r->mute_control()); + _gain_group->remove_control (r->gain_control()); + boost::shared_ptr trk = boost::dynamic_pointer_cast (r); + if (trk) { + _rec_enable_group->remove_control (trk->rec_enable_control()); + _monitoring_group->remove_control (trk->monitoring_control()); + } routes->erase (i); _session.set_dirty (); RouteRemoved (this, boost::weak_ptr (r)); /* EMIT SIGNAL */ @@ -175,49 +204,6 @@ RouteGroup::remove (boost::shared_ptr r) } -gain_t -RouteGroup::get_min_factor (gain_t factor) -{ - for (RouteList::iterator i = routes->begin(); i != routes->end(); ++i) { - gain_t const g = (*i)->gain_control()->get_value(); - - if ((g + g * factor) >= 0.0f) { - continue; - } - - if (g <= 0.0000003f) { - return 0.0f; - } - - factor = 0.0000003f / g - 1.0f; - } - - return factor; -} - -gain_t -RouteGroup::get_max_factor (gain_t factor) -{ - for (RouteList::iterator i = routes->begin(); i != routes->end(); i++) { - gain_t const g = (*i)->gain_control()->get_value(); - - // if the current factor woulnd't raise this route above maximum - if ((g + g * factor) <= 1.99526231f) { - continue; - } - - // if route gain is already at peak, return 0.0f factor - if (g >= 1.99526231f) { - return 0.0f; - } - - // factor is calculated so that it would raise current route to max - factor = 1.99526231f / g - 1.0f; - } - - return factor; -} - XMLNode& RouteGroup::get_state () { @@ -269,6 +255,8 @@ RouteGroup::set_state (const XMLNode& node, int version) } } + push_to_groups (); + return 0; } @@ -293,6 +281,8 @@ RouteGroup::set_state_2X (const XMLNode& node, int /*version*/) _color = false; } + push_to_groups (); + return 0; } @@ -303,6 +293,8 @@ RouteGroup::set_gain (bool yn) return; } _gain = yn; + _gain_group->set_active (yn); + send_change (PropertyChange (Properties::gain)); } @@ -313,6 +305,7 @@ RouteGroup::set_mute (bool yn) return; } _mute = yn; + _mute_group->set_active (yn); send_change (PropertyChange (Properties::mute)); } @@ -323,6 +316,7 @@ RouteGroup::set_solo (bool yn) return; } _solo = yn; + _solo_group->set_active (yn); send_change (PropertyChange (Properties::solo)); } @@ -333,6 +327,7 @@ RouteGroup::set_recenable (bool yn) return; } _recenable = yn; + _rec_enable_group->set_active (yn); send_change (PropertyChange (Properties::recenable)); } @@ -384,6 +379,8 @@ RouteGroup::set_monitoring (bool yn) } _monitoring = yn; + _monitoring_group->set_active (yn); + send_change (PropertyChange (Properties::monitoring)); _session.set_dirty (); @@ -531,3 +528,19 @@ RouteGroup::enabled_property (PBD::PropertyID prop) return dynamic_cast* > (i->second)->val (); } + +void +RouteGroup::post_set (PBD::PropertyChange const &) +{ + push_to_groups (); +} + +void +RouteGroup::push_to_groups () +{ + _gain_group->set_active (_gain); + _solo_group->set_active (_solo); + _mute_group->set_active (_mute); + _rec_enable_group->set_active (_recenable); + _monitoring_group->set_active (_monitoring); +} diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index ee70d2a40f..7658ff270d 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -98,6 +98,7 @@ #include "ardour/session_directory.h" #include "ardour/session_playlists.h" #include "ardour/smf_source.h" +#include "ardour/solo_isolate_control.h" #include "ardour/source_factory.h" #include "ardour/speakers.h" #include "ardour/tempo.h" @@ -1506,7 +1507,7 @@ Session::set_track_monitor_input_status (bool yn) boost::shared_ptr rl = routes.reader (); for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (tr && tr->record_enabled ()) { + if (tr && tr->rec_enable_control()->get_value()) { //cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl; tr->request_input_monitoring (yn); } @@ -1941,15 +1942,9 @@ void Session::set_all_tracks_record_enabled (bool enable ) { boost::shared_ptr rl = routes.reader(); - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (tr) { - tr->set_record_enabled (enable, Controllable::NoGroup); - } - } + set_controls (route_list_to_control_list (rl, &Track::rec_enable_control), enable, Controllable::NoGroup); } - void Session::disable_record (bool rt_context, bool force) { @@ -2981,7 +2976,7 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod // 0 for Stereo Out mode // 0 Multi Out mode if (Config->get_output_auto_connect() & AutoConnectMaster) { - track->set_gain (dB_to_coefficient (0), Controllable::NoGroup); + track->gain_control()->set_value (dB_to_coefficient (0), Controllable::NoGroup); } } @@ -3430,7 +3425,7 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool if (tr) { tr->PlaylistChanged.connect_same_thread (*this, boost::bind (&Session::track_playlist_changed, this, boost::weak_ptr (tr))); track_playlist_changed (boost::weak_ptr (tr)); - tr->RecordEnableChanged.connect_same_thread (*this, boost::bind (&Session::update_route_record_state, this)); + tr->rec_enable_control()->Changed.connect_same_thread (*this, boost::bind (&Session::update_route_record_state, this)); boost::shared_ptr mt = boost::dynamic_pointer_cast (tr); if (mt) { @@ -3580,7 +3575,7 @@ Session::remove_routes (boost::shared_ptr routes_to_remove) continue; } - (*iter)->set_solo (false, Controllable::NoGroup); + (*iter)->solo_control()->set_value (0.0, Controllable::NoGroup); rs->remove (*iter); @@ -3721,7 +3716,7 @@ Session::route_listen_changed (Controllable::GroupControlDisposition group_overr continue; } - if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { + if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) { /* route does not get solo propagated to it */ continue; } @@ -3734,7 +3729,7 @@ Session::route_listen_changed (Controllable::GroupControlDisposition group_overr */ continue; } - (*i)->set_listen (false, Controllable::NoGroup); + (*i)->solo_control()->set_value (0.0, Controllable::NoGroup); } } @@ -3759,7 +3754,7 @@ Session::route_solo_isolated_changed (boost::weak_ptr wpr) bool send_changed = false; - if (route->solo_isolated()) { + if (route->solo_isolate_control()->solo_isolated()) { if (_solo_isolated_cnt == 0) { send_changed = true; } @@ -3838,7 +3833,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi continue; } - if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { + if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) { /* route does not get solo propagated to it */ continue; } @@ -3852,7 +3847,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi continue; } - (*i)->set_solo (false, group_override); + (*i)->solo_control()->set_value (0.0, group_override); } } @@ -3871,7 +3866,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi continue; } - if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) { + if ((*i)->solo_isolate_control()->solo_isolated() || !(*i)->can_solo()) { /* route does not get solo propagated to it */ continue; } @@ -3893,7 +3888,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi DEBUG_TRACE (DEBUG::Solo, string_compose ("\tthere is a feed from %1\n", (*i)->name())); if (!via_sends_only) { if (!route->soloed_by_others_upstream()) { - (*i)->mod_solo_by_others_downstream (delta); + (*i)->solo_control()->mod_solo_by_others_downstream (delta); } else { DEBUG_TRACE (DEBUG::Solo, "\talready soloed by others upstream\n"); } @@ -3922,7 +3917,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi if (!via_sends_only) { //NB. Triggers Invert Push, which handles soloed by downstream DEBUG_TRACE (DEBUG::Solo, string_compose ("\tmod %1 by %2\n", (*i)->name(), delta)); - (*i)->mod_solo_by_others_upstream (delta); + (*i)->solo_control()->mod_solo_by_others_upstream (delta); } else { DEBUG_TRACE (DEBUG::Solo, string_compose ("\tfeed to %1 ignored, sends-only\n", (*i)->name())); } @@ -3969,7 +3964,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_monitor() && !(*i)->is_auditioner() && (*i)->self_soloed()) { + if ((*i)->can_solo() && (*i)->self_soloed()) { something_soloed = true; } @@ -3978,11 +3973,11 @@ Session::update_route_solo_state (boost::shared_ptr r) listeners++; something_listening = true; } else { - (*i)->set_listen (false, Controllable::NoGroup); + (*i)->set_listen (false); } } - if ((*i)->solo_isolated()) { + if ((*i)->solo_isolate_control()->solo_isolated()) { isolated++; } } @@ -6078,7 +6073,7 @@ Session::update_route_record_state () while (i != rl->end ()) { boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (tr && tr->record_enabled ()) { + if (tr && tr->rec_enable_control()->get_value()) { break; } @@ -6095,7 +6090,7 @@ Session::update_route_record_state () for (i = rl->begin(); i != rl->end (); ++i) { boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (tr && !tr->record_enabled ()) { + if (tr && !tr->rec_enable_control()->get_value()) { break; } } @@ -6124,12 +6119,7 @@ void Session::solo_control_mode_changed () { /* cancel all solo or all listen when solo control mode changes */ - - if (soloing()) { - set_solo (get_routes(), false); - } else if (listening()) { - set_listen (get_routes(), false); - } + clear_all_solo_state (get_routes()); } /** Called when a property of one of our route groups changes */ diff --git a/libs/ardour/session_midi.cc b/libs/ardour/session_midi.cc index c2010fd592..390f1de32f 100644 --- a/libs/ardour/session_midi.cc +++ b/libs/ardour/session_midi.cc @@ -350,7 +350,7 @@ Session::mmc_record_enable (MIDI::MachineControl &mmc, size_t trk, bool enabled) if ((at = dynamic_cast((*i).get())) != 0) { if (trk == at->remote_control_id()) { - at->set_record_enabled (enabled, Controllable::UseGroup); + at->rec_enable_control()->set_value (enabled, Controllable::UseGroup); break; } } diff --git a/libs/ardour/session_rtevents.cc b/libs/ardour/session_rtevents.cc index 6b807fbf52..67249d48b9 100644 --- a/libs/ardour/session_rtevents.cc +++ b/libs/ardour/session_rtevents.cc @@ -21,8 +21,9 @@ #include "pbd/error.h" #include "pbd/compose.h" -#include "ardour/session.h" +#include "ardour/monitor_control.h" #include "ardour/route.h" +#include "ardour/session.h" #include "ardour/track.h" #include "i18n.h" @@ -33,26 +34,26 @@ using namespace ARDOUR; using namespace Glib; void -Session::set_monitoring (boost::shared_ptr rl, MonitorChoice mc, - SessionEvent::RTeventCallback after, - Controllable::GroupControlDisposition group_override) +Session::set_controls (boost::shared_ptr cl, double val, Controllable::GroupControlDisposition gcd) { - queue_event (get_rt_event (rl, mc, after, group_override, &Session::rt_set_monitoring)); + std::cerr << "Session::set_controls called on " << cl->size() << " controls, group = " << enum_2_string (gcd) << std::endl; + queue_event (get_rt_event (cl, val, gcd)); } void -Session::rt_set_monitoring (boost::shared_ptr rl, MonitorChoice mc, Controllable::GroupControlDisposition group_override) +Session::set_control (boost::shared_ptr ac, double val, Controllable::GroupControlDisposition gcd) { - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - if (!(*i)->is_auditioner()) { - boost::shared_ptr t = boost::dynamic_pointer_cast (*i); - if (t) { - t->set_monitoring (mc, group_override); - } - } - } + boost::shared_ptr cl (new ControlList); + cl->push_back (ac); + set_controls (cl, val, gcd); +} - set_dirty(); +void +Session::rt_set_controls (boost::shared_ptr cl, double val, Controllable::GroupControlDisposition gcd) +{ + for (ControlList::iterator c = cl->begin(); c != cl->end(); ++c) { + (*c)->set_value (val, gcd); + } } void @@ -73,224 +74,6 @@ Session::rt_clear_all_solo_state (boost::shared_ptr rl, bool /* yn */ set_dirty(); } -void -Session::set_solo (boost::shared_ptr rl, bool yn, SessionEvent::RTeventCallback after, - Controllable::GroupControlDisposition group_override) -{ - queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_solo)); -} - -void -Session::rt_set_solo (boost::shared_ptr rl, bool yn, Controllable::GroupControlDisposition group_override) -{ - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - if (!(*i)->is_auditioner()) { - (*i)->set_solo (yn, group_override); - } - } - - set_dirty(); - - /* XXX boost::shared_ptr goes out of scope here and is likley free()ed in RT context - * because boost's shared_ptr does reference counting and free/delete in the dtor. - * (this also applies to other rt_ methods here) - */ -} - -void -Session::set_implicit_solo (boost::shared_ptr rl, int delta, bool upstream, SessionEvent::RTeventCallback after, - Controllable::GroupControlDisposition group_override) -{ - queue_event (get_rt_event (rl, delta, upstream, after, group_override, &Session::rt_set_implicit_solo)); -} - -void -Session::rt_set_implicit_solo (boost::shared_ptr rl, int delta, bool upstream, PBD::Controllable::GroupControlDisposition) -{ - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - if (!(*i)->is_auditioner()) { - if (upstream) { - std::cerr << "Changing " << (*i)->name() << " upstream by " << delta << std::endl; - (*i)->mod_solo_by_others_upstream (delta); - } else { - std::cerr << "Changing " << (*i)->name() << " downstream by " << delta << std::endl; - (*i)->mod_solo_by_others_downstream (delta); - } - } - } - - set_dirty(); - - /* XXX boost::shared_ptr goes out of scope here and is likley free()ed in RT context - * because boost's shared_ptr does reference counting and free/delete in the dtor. - * (this also applies to other rt_ methods here) - */ -} - -void -Session::set_just_one_solo (boost::shared_ptr r, bool yn, SessionEvent::RTeventCallback after) -{ - /* its a bit silly to have to do this, but it keeps the API for this public method sane (we're - only going to solo one route) and keeps our ability to use get_rt_event() for the internal - private method. - */ - - boost::shared_ptr rl (new RouteList); - rl->push_back (r); - - queue_event (get_rt_event (rl, yn, after, Controllable::NoGroup, &Session::rt_set_just_one_solo)); -} - -void -Session::rt_set_just_one_solo (boost::shared_ptr just_one, bool yn, Controllable::GroupControlDisposition /*ignored*/) -{ - boost::shared_ptr rl = routes.reader (); - boost::shared_ptr r = just_one->front(); - - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - if (!(*i)->is_auditioner() && r != *i) { - (*i)->set_solo (!yn, Controllable::NoGroup); - } - } - - r->set_solo (yn, Controllable::NoGroup); - - set_dirty(); -} - -void -Session::set_listen (boost::shared_ptr rl, bool yn, SessionEvent::RTeventCallback after, Controllable::GroupControlDisposition group_override) -{ - queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_listen)); -} - -void -Session::rt_set_listen (boost::shared_ptr rl, bool yn, Controllable::GroupControlDisposition group_override) -{ - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - if (!(*i)->is_auditioner()) { - (*i)->set_listen (yn, group_override); - } - } - - set_dirty(); -} - -void -Session::set_mute (boost::shared_ptr rl, bool yn, SessionEvent::RTeventCallback after, Controllable::GroupControlDisposition group_override) -{ - /* Set superficial value of mute controls for automation. */ - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - boost::shared_ptr mc = boost::dynamic_pointer_cast ((*i)->mute_control()); - mc->set_superficial_value(yn); - } - - queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_mute)); -} - -void -Session::rt_set_mute (boost::shared_ptr rl, bool yn, Controllable::GroupControlDisposition group_override) -{ - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - if (!(*i)->is_monitor() && !(*i)->is_auditioner()) { - (*i)->set_mute (yn, group_override); - } - } - - set_dirty(); -} - -void -Session::set_solo_isolated (boost::shared_ptr rl, bool yn, SessionEvent::RTeventCallback after, Controllable::GroupControlDisposition group_override) -{ - queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_solo_isolated)); -} - -void -Session::rt_set_solo_isolated (boost::shared_ptr rl, bool yn, Controllable::GroupControlDisposition group_override) -{ - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_auditioner()) { - (*i)->set_solo_isolated (yn, group_override); - } - } - - set_dirty(); -} - -void -Session::set_record_enabled (boost::shared_ptr rl, bool yn, SessionEvent::RTeventCallback after, Controllable::GroupControlDisposition group_override) -{ - if (!writable()) { - return; - } - - /* do the non-RT part of rec-enabling first - the RT part will be done - * on the next process cycle. This does mean that theoretically we are - * doing things provisionally on the assumption that the rec-enable - * change will work, but this had better be a solid assumption for - * other reasons. - */ - - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - if ((*i)->is_auditioner() || (*i)->record_safe ()) { - continue; - } - - boost::shared_ptr t; - - if ((t = boost::dynamic_pointer_cast(*i)) != 0) { - t->prep_record_enabled (yn, group_override); - } - } - - queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_record_enabled)); -} - -void -Session::rt_set_record_enabled (boost::shared_ptr rl, bool yn, Controllable::GroupControlDisposition group_override) -{ - for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { - if ((*i)->is_auditioner() || (*i)->record_safe ()) { - continue; - } - - boost::shared_ptr t; - - if ((t = boost::dynamic_pointer_cast(*i)) != 0) { - t->set_record_enabled (yn, group_override); - } - } - - set_dirty (); -} - - -void -Session::set_record_safe (boost::shared_ptr rl, bool yn, SessionEvent::RTeventCallback after, Controllable::GroupControlDisposition group_override) -{ - set_record_enabled (rl, false, after, group_override); - queue_event (get_rt_event (rl, yn, after, group_override, &Session::rt_set_record_safe)); -} - -void -Session::rt_set_record_safe (boost::shared_ptr rl, bool yn, Controllable::GroupControlDisposition group_override) -{ - for (RouteList::iterator i = rl->begin (); i != rl->end (); ++i) { - if ((*i)->is_auditioner ()) { // REQUIRES REVIEW Can audiotioner be in Record Safe mode? - continue; - } - - boost::shared_ptr t; - - if ((t = boost::dynamic_pointer_cast(*i)) != 0) { - t->set_record_safe (yn, group_override); - } - } - - set_dirty (); -} - void Session::process_rtop (SessionEvent* ev) { diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index 4e59da7137..47c7885b85 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -1265,7 +1265,7 @@ Session::locate (framepos_t target_frame, bool with_roll, bool with_flush, bool for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) { boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (tr && tr->record_enabled ()) { + if (tr && tr->rec_enable_control()->get_value()) { // tell it we've looped, so it can deal with the record state tr->transport_looped (_transport_frame); } diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc index 8aebf09259..84a6d71349 100644 --- a/libs/ardour/track.cc +++ b/libs/ardour/track.cc @@ -23,9 +23,11 @@ #include "ardour/diskstream.h" #include "ardour/io_processor.h" #include "ardour/meter.h" +#include "ardour/monitor_control.h" #include "ardour/playlist.h" #include "ardour/port.h" #include "ardour/processor.h" +#include "ardour/record_enable_control.h" #include "ardour/route_group_specialized.h" #include "ardour/session.h" #include "ardour/session_playlists.h" @@ -42,7 +44,6 @@ Track::Track (Session& sess, string name, Route::Flag flag, TrackMode mode, Data : Route (sess, name, flag, default_type) , _saved_meter_point (_meter_point) , _mode (mode) - , _monitoring (MonitorAuto) { _freeze_record.state = NoFreeze; _declickable = true; @@ -62,17 +63,24 @@ Track::init () boost::shared_ptr rp (shared_from_this()); boost::shared_ptr rt = boost::dynamic_pointer_cast (rp); - _rec_enable_control = boost::shared_ptr (new RecEnableControl(rt)); - _rec_enable_control->set_flags (Controllable::Toggle); - _monitoring_control.reset (new MonitoringControllable (X_("monitoring"), rt)); - /* don't add rec_enable_control to controls because we don't want it to - * appear as an automatable parameter - */ + _record_enable_control.reset (new RecordEnableControl (_session, X_("recenable"), *this)); + _record_enable_control->set_flags (Controllable::Toggle); + + _monitoring_control.reset (new MonitorControl (_session, X_("monitoring"), *this)); + + _record_safe_control.reset (new AutomationControl (_session, RecSafeAutomation, ParameterDescriptor (RecSafeAutomation), + boost::shared_ptr (new AutomationList (Evoral::Parameter (RecSafeAutomation))), + X_("recsafe"))); + track_number_changed.connect_same_thread (*this, boost::bind (&Track::resync_track_name, this)); _session.config.ParameterChanged.connect_same_thread (*this, boost::bind (&Track::parameter_changed, this, _1)); - return 0; + _monitoring_control->Changed.connect_same_thread (*this, boost::bind (&Track::monitoring_changed, this, _1, _2)); + _record_safe_control->Changed.connect_same_thread (*this, boost::bind (&Track::record_safe_changed, this, _1, _2)); + _record_enable_control->Changed.connect_same_thread (*this, boost::bind (&Track::record_enable_changed, this, _1, _2)); + + return 0; } void @@ -97,9 +105,7 @@ XMLNode& Track::state (bool full) { XMLNode& root (Route::state (full)); - root.add_property (X_("monitoring"), enum_2_string (_monitoring)); root.add_property (X_("saved-meter-point"), enum_2_string (_saved_meter_point)); - root.add_child_nocopy (_rec_enable_control->get_state()); root.add_child_nocopy (_diskstream->get_state ()); return root; @@ -137,19 +143,13 @@ Track::set_state (const XMLNode& node, int version) XMLProperty const * prop; if (child->name() == Controllable::xml_node_name && (prop = child->property ("name")) != 0) { if (prop->value() == X_("recenable")) { - _rec_enable_control->set_state (*child, version); + _record_enable_control->set_state (*child, version); } } } XMLProperty const * prop; - if ((prop = node.property (X_("monitoring"))) != 0) { - _monitoring = MonitorChoice (string_2_enum (prop->value(), _monitoring)); - } else { - _monitoring = MonitorAuto; - } - if ((prop = node.property (X_("saved-meter-point"))) != 0) { _saved_meter_point = MeterPoint (string_2_enum (prop->value(), _saved_meter_point)); } else { @@ -178,62 +178,6 @@ Track::freeze_state() const return _freeze_record.state; } -Track::RecEnableControl::RecEnableControl (boost::shared_ptr t) - : AutomationControl (t->session(), - RecEnableAutomation, - ParameterDescriptor(Evoral::Parameter(RecEnableAutomation)), - boost::shared_ptr(), - X_("recenable")) - , track (t) -{ - boost::shared_ptr gl(new AutomationList(Evoral::Parameter(RecEnableAutomation))); - set_list (gl); -} - -void -Track::RecEnableControl::set_value (double val, Controllable::GroupControlDisposition group_override) -{ - if (writable()) { - _set_value (val, group_override); - } -} - -void -Track::RecEnableControl::set_value_unchecked (double val) -{ - if (writable()) { - _set_value (val, Controllable::NoGroup); - } -} - -void -Track::RecEnableControl::_set_value (double val, Controllable::GroupControlDisposition group_override) -{ - boost::shared_ptr t = track.lock (); - if (!t) { - return; - } - - t->set_record_enabled (val >= 0.5 ? true : false, group_override); -} - -double -Track::RecEnableControl::get_value () const -{ - boost::shared_ptr t = track.lock (); - if (!t) { - return 0; - } - - return (t->record_enabled() ? 1.0 : 0.0); -} - -bool -Track::record_enabled () const -{ - return _diskstream && _diskstream->record_enabled (); -} - bool Track::can_record() { @@ -246,24 +190,15 @@ Track::can_record() return will_record; } -void -Track::prep_record_enabled (bool yn, Controllable::GroupControlDisposition group_override) +int +Track::prep_record_enabled (bool yn) { - if (yn && record_safe ()) { - return; + if (yn && _record_safe_control->get_value()) { + return -1; } - if (!_session.writable()) { - return; - } - - if (_freeze_record.state == Frozen) { - return; - } - - if (use_group (group_override, &RouteGroup::is_recenable)) { - _route_group->apply (&Track::prep_record_enabled, yn, Controllable::NoGroup); - return; + if (!can_be_record_enabled()) { + return -1; } /* keep track of the meter point as it was before we rec-enabled */ @@ -288,31 +223,20 @@ Track::prep_record_enabled (bool yn, Controllable::GroupControlDisposition group set_meter_point (_saved_meter_point); } } + + return 0; } void -Track::set_record_enabled (bool yn, Controllable::GroupControlDisposition gcd) +Track::record_enable_changed (bool, Controllable::GroupControlDisposition) { - if (_diskstream->record_safe ()) { - return; - } + _diskstream->set_record_enabled (_record_enable_control->get_value()); +} - if (!_session.writable()) { - return; - } - - if (_freeze_record.state == Frozen) { - return; - } - - if (use_group (gcd, &RouteGroup::is_recenable)) { - _route_group->apply (&Track::set_record_enabled, yn, Controllable::NoGroup); - return; - } - - _diskstream->set_record_enabled (yn); - - _rec_enable_control->Changed (true, gcd); +void +Track::record_safe_changed (bool, Controllable::GroupControlDisposition) +{ + _diskstream->set_record_safe (_record_safe_control->get_value()); } bool @@ -336,12 +260,7 @@ Track::set_record_safe (bool yn, Controllable::GroupControlDisposition group_ove return; } - if (use_group (group_override, &RouteGroup::is_recenable)) { - _route_group->apply (&Track::set_record_safe, yn, Controllable::NoGroup); - return; - } - - _diskstream->set_record_safe (yn); + _rec_safe_control->set_value (yn, group_override); } void @@ -371,7 +290,7 @@ Track::set_name (const string& str) { bool ret; - if (record_enabled() && _session.actively_recording()) { + if (_record_enable_control->get_value() && _session.actively_recording()) { /* this messes things up if done while recording */ return false; } @@ -452,7 +371,7 @@ Track::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, if (!_active) { silence (nframes); - if (_meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled())) { + if (_meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _diskstream->record_enabled())) { _meter->reset(); } return 0; @@ -611,8 +530,6 @@ Track::set_diskstream (boost::shared_ptr ds) ds->PlaylistChanged.connect_same_thread (*this, boost::bind (&Track::diskstream_playlist_changed, this)); diskstream_playlist_changed (); - ds->RecordEnableChanged.connect_same_thread (*this, boost::bind (&Track::diskstream_record_enable_changed, this)); - ds->RecordSafeChanged.connect_same_thread (*this, boost::bind (&Track::diskstream_record_safe_changed, this)); ds->SpeedChanged.connect_same_thread (*this, boost::bind (&Track::diskstream_speed_changed, this)); ds->AlignmentStyleChanged.connect_same_thread (*this, boost::bind (&Track::diskstream_alignment_style_changed, this)); } @@ -623,18 +540,6 @@ Track::diskstream_playlist_changed () PlaylistChanged (); /* EMIT SIGNAL */ } -void -Track::diskstream_record_enable_changed () -{ - RecordEnableChanged (); /* EMIT SIGNAL */ -} - -void -Track::diskstream_record_safe_changed () -{ - RecordSafeChanged (); /* EMIT SIGNAL */ -} - void Track::diskstream_speed_changed () { @@ -1014,12 +919,13 @@ MonitorState Track::monitoring_state () const { /* Explicit requests */ + MonitorChoice m (_monitoring_control->monitoring_choice()); - if (_monitoring & MonitorInput) { + if (m & MonitorInput) { return MonitoringInput; } - if (_monitoring & MonitorDisk) { + if (m & MonitorDisk) { return MonitoringDisk; } @@ -1086,7 +992,7 @@ Track::maybe_declick (BufferSet& bufs, framecnt_t nframes, int declick) ditto if we are monitoring inputs. */ - if (_have_internal_generator || monitoring_choice() == MonitorInput) { + if (_have_internal_generator || (_monitoring_control->monitoring_choice() == MonitorInput)) { return; } @@ -1136,22 +1042,10 @@ Track::check_initial_delay (framecnt_t nframes, framepos_t& transport_frame) } void -Track::set_monitoring (MonitorChoice mc, Controllable::GroupControlDisposition gcd) +Track::monitoring_changed (bool, Controllable::GroupControlDisposition) { - if (use_group (gcd, &RouteGroup::is_monitoring)) { - _route_group->apply (&Track::set_monitoring, mc, Controllable::NoGroup); - return; - } - - if (mc != _monitoring) { - _monitoring = mc; - - for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - (*i)->monitoring_changed (); - } - - MonitoringChanged (); /* EMIT SIGNAL */ - _monitoring_control->Changed (true, gcd); + for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { + (*i)->monitoring_changed (); } } @@ -1161,64 +1055,10 @@ Track::metering_state () const bool rv; if (_session.transport_rolling ()) { // audio_track.cc || midi_track.cc roll() runs meter IFF: - rv = _meter_point == MeterInput && (_monitoring & MonitorInput || _diskstream->record_enabled()); + rv = _meter_point == MeterInput && ((_monitoring_control->monitoring_choice() & MonitorInput) || _diskstream->record_enabled()); } else { // track no_roll() always metering if rv = _meter_point == MeterInput; } return rv ? MeteringInput : MeteringRoute; } - -Track::MonitoringControllable::MonitoringControllable (std::string name, boost::shared_ptr r) - : RouteAutomationControl (name, MonitoringAutomation, boost::shared_ptr(), r) -{ - boost::shared_ptr gl(new AutomationList(Evoral::Parameter(MonitoringAutomation))); - gl->set_interpolation(Evoral::ControlList::Discrete); - set_list (gl); -} - -void -Track::MonitoringControllable::set_value (double val, Controllable::GroupControlDisposition gcd) -{ - _set_value (val, gcd); -} - -void -Track::MonitoringControllable::_set_value (double val, Controllable::GroupControlDisposition gcd) -{ - boost::shared_ptr r = _route.lock(); - if (!r) { - return; - } - - boost::shared_ptr t = boost::dynamic_pointer_cast (r); - if (!t) { - return; - } - - int mc = (int) val; - - if (mc < MonitorAuto || mc > MonitorDisk) { - return; - } - - /* no group effect at present */ - - t->set_monitoring ((MonitorChoice) mc, gcd); -} - -double -Track::MonitoringControllable::get_value () const -{ - boost::shared_ptr r = _route.lock(); - if (!r) { - return 0.0; - } - - boost::shared_ptr t = boost::dynamic_pointer_cast (r); - if (!t) { - return 0.0; - } - - return t->monitoring_choice(); -} diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc index 08bb61065b..0ae1c569c8 100644 --- a/libs/ardour/utils.cc +++ b/libs/ardour/utils.cc @@ -754,3 +754,4 @@ ARDOUR::slider_position_to_gain_with_max (double g, double max_gain) extern "C" { void c_stacktrace() { stacktrace (cerr); } } + diff --git a/libs/ardour/vca.cc b/libs/ardour/vca.cc index d92fa67c03..39303fc8ca 100644 --- a/libs/ardour/vca.cc +++ b/libs/ardour/vca.cc @@ -63,6 +63,7 @@ VCA::get_next_vca_number () VCA::VCA (Session& s, uint32_t num, const string& name) : Stripable (s, name) + , Muteable (s, name) , Automatable (s) , _number (num) , _gain_control (new GainControl (s, Evoral::Parameter (GainAutomation), boost::shared_ptr ())) @@ -74,8 +75,8 @@ VCA::VCA (Session& s, uint32_t num, const string& name) int VCA::init () { - _solo_control.reset (new VCASoloControllable (X_("solo"), shared_from_this())); - _mute_control.reset (new VCAMuteControllable (X_("mute"), shared_from_this())); + _solo_control.reset (new SoloControl (_session, X_("solo"), *this, *this)); + _mute_control.reset (new MuteControl (_session, X_("mute"), *this)); add_control (_gain_control); add_control (_solo_control); @@ -159,98 +160,3 @@ VCA::muted () const { return _mute_requested; } - -VCA::VCASoloControllable::VCASoloControllable (string const & name, boost::shared_ptr vca) - : AutomationControl (vca->session(), Evoral::Parameter (SoloAutomation), ParameterDescriptor (Evoral::Parameter (SoloAutomation)), - boost::shared_ptr(), name) - , _vca (vca) -{ -} - -void -VCA::VCASoloControllable::set_value (double val, PBD::Controllable::GroupControlDisposition gcd) -{ - if (writable()) { - _set_value (val, gcd); - } -} - -void -VCA::VCASoloControllable::_set_value (double val, PBD::Controllable::GroupControlDisposition gcd) -{ - boost::shared_ptr vca = _vca.lock(); - if (!vca) { - return; - } - - vca->set_solo (val >= 0.5); - - AutomationControl::set_value (val, gcd); -} - -void -VCA::VCASoloControllable::set_value_unchecked (double val) -{ - /* used only by automation playback */ - _set_value (val, Controllable::NoGroup); -} - -double -VCA::VCASoloControllable::get_value() const -{ - boost::shared_ptr vca = _vca.lock(); - if (!vca) { - return 0.0; - } - - return vca->soloed() ? 1.0 : 0.0; -} - -/*----*/ - -VCA::VCAMuteControllable::VCAMuteControllable (string const & name, boost::shared_ptr vca) - : AutomationControl (vca->session(), Evoral::Parameter (MuteAutomation), ParameterDescriptor (Evoral::Parameter (MuteAutomation)), - boost::shared_ptr(), name) - , _vca (vca) -{ -} - -void -VCA::VCAMuteControllable::set_value (double val, PBD::Controllable::GroupControlDisposition gcd) -{ - if (writable()) { - _set_value (val, gcd); - } -} - -void -VCA::VCAMuteControllable::_set_value (double val, PBD::Controllable::GroupControlDisposition gcd) -{ - boost::shared_ptr vca = _vca.lock(); - - if (!vca) { - return; - } - - vca->set_mute (val >= 0.5); - - AutomationControl::set_value (val, gcd); -} - -void -VCA::VCAMuteControllable::set_value_unchecked (double val) -{ - /* used only by automation playback */ - _set_value (val, Controllable::NoGroup); -} - -double -VCA::VCAMuteControllable::get_value() const -{ - boost::shared_ptr vca = _vca.lock(); - if (!vca) { - return 0.0; - } - - return vca->muted() ? 1.0 : 0.0; -} diff --git a/libs/ardour/wscript b/libs/ardour/wscript index 625bac8742..17876f889b 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -57,6 +57,7 @@ libardour_sources = [ 'chan_count.cc', 'chan_mapping.cc', 'config_text.cc', + 'control_group.cc', 'control_protocol_manager.cc', 'cycle_timer.cc', 'data_type.cc', @@ -137,12 +138,15 @@ libardour_sources = [ 'midi_stretch.cc', 'midi_track.cc', 'midi_ui.cc', + 'mididm.cc', 'midiport_manager.cc', 'mix.cc', + 'monitor_control.cc', 'monitor_processor.cc', 'mtc_slave.cc', - 'mididm.cc', 'mtdm.cc', + 'muteable.cc', + 'mute_control.cc', 'mute_master.cc', 'note_fixer.cc', 'onset_detector.cc', @@ -154,6 +158,7 @@ libardour_sources = [ 'panner_shell.cc', 'parameter_descriptor.cc', 'pcm_utils.cc', + 'phase_control.cc', 'playlist.cc', 'playlist_factory.cc', 'playlist_source.cc', @@ -170,13 +175,13 @@ libardour_sources = [ 'quantize.cc', 'rc_configuration.cc', 'recent_sessions.cc', + 'record_enable_control.cc', 'region_factory.cc', 'resampled_source.cc', 'region.cc', 'return.cc', 'reverse.cc', 'route.cc', - 'route_controls.cc', 'route_graph.cc', 'route_group.cc', 'route_group_member.cc', @@ -206,10 +211,14 @@ libardour_sources = [ 'session_transport.cc', 'sidechain.cc', 'slave.cc', + 'slavable_automation_control.cc', 'smf_source.cc', 'sndfile_helpers.cc', 'sndfileimportable.cc', 'sndfilesource.cc', + 'solo_control.cc', + 'solo_isolate_control.cc', + 'solo_safe_control.cc', 'soundcloud_upload.cc', 'source.cc', 'source_factory.cc', diff --git a/libs/pbd/pbd/controllable.h b/libs/pbd/pbd/controllable.h index 078671c91b..11ba979198 100644 --- a/libs/pbd/pbd/controllable.h +++ b/libs/pbd/pbd/controllable.h @@ -54,6 +54,7 @@ class LIBPBD_API Controllable : public PBD::StatefulDestructible { enum Flag { Toggle = 0x1, GainLike = 0x2, + RealTime = 0x4 }; Controllable (const std::string& name, Flag f = Flag (0)); diff --git a/libs/surfaces/control_protocol/control_protocol.cc b/libs/surfaces/control_protocol/control_protocol.cc index 5470232ad7..f413ad82d6 100644 --- a/libs/surfaces/control_protocol/control_protocol.cc +++ b/libs/surfaces/control_protocol/control_protocol.cc @@ -199,7 +199,7 @@ ControlProtocol::route_set_rec_enable (uint32_t table_index, bool yn) boost::shared_ptr at = boost::dynamic_pointer_cast(r); if (at) { - at->set_record_enabled (yn, Controllable::NoGroup); + at->rec_enable_control()->set_value (1.0, Controllable::UseGroup); } } @@ -215,7 +215,7 @@ ControlProtocol::route_get_rec_enable (uint32_t table_index) boost::shared_ptr at = boost::dynamic_pointer_cast(r); if (at) { - return at->record_enabled (); + return at->rec_enable_control()->get_value(); } return false; @@ -248,7 +248,7 @@ ControlProtocol::route_set_gain (uint32_t table_index, float gain) boost::shared_ptr r = route_table[table_index]; if (r != 0) { - r->set_gain (gain, Controllable::UseGroup); + r->gain_control()->set_value (gain, Controllable::UseGroup); } } @@ -298,7 +298,7 @@ ControlProtocol::route_get_muted (uint32_t table_index) return false; } - return r->muted (); + return r->mute_control()->muted (); } void @@ -311,7 +311,7 @@ ControlProtocol::route_set_muted (uint32_t table_index, bool yn) boost::shared_ptr r = route_table[table_index]; if (r != 0) { - r->set_mute (yn, Controllable::UseGroup); + r->mute_control()->set_value (yn ? 1.0 : 0.0, Controllable::UseGroup); } } @@ -342,7 +342,7 @@ ControlProtocol::route_set_soloed (uint32_t table_index, bool yn) boost::shared_ptr r = route_table[table_index]; if (r != 0) { - r->set_solo (yn, Controllable::UseGroup); + r->solo_control()->set_value (yn ? 1.0 : 0.0, Controllable::UseGroup); } } diff --git a/libs/surfaces/faderport/faderport.cc b/libs/surfaces/faderport/faderport.cc index d08264a42d..1df2fc4865 100644 --- a/libs/surfaces/faderport/faderport.cc +++ b/libs/surfaces/faderport/faderport.cc @@ -507,7 +507,7 @@ FaderPort::fader_handler (MIDI::Parser &, MIDI::EventTwoBytes* tb) single route at a time, allow the fader to modify the group, if appropriate. */ - _current_route->set_gain (val, Controllable::UseGroup); + _current_route->gain_control()->set_value (val, Controllable::UseGroup); } } } @@ -1147,7 +1147,7 @@ FaderPort::set_current_route (boost::shared_ptr r) boost::shared_ptr t = boost::dynamic_pointer_cast (_current_route); if (t) { - t->RecordEnableChanged.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_recenable, this), this); + t->rec_enable_control()->Changed.connect (route_connections, MISSING_INVALIDATOR, boost::bind (&FaderPort::map_recenable, this), this); } boost::shared_ptr control = _current_route->gain_control (); @@ -1227,7 +1227,7 @@ FaderPort::map_mute () if (_current_route->muted()) { stop_blinking (Mute); get_button (Mute).set_led_state (_output_port, true); - } else if (_current_route->muted_by_others()) { + } else if (_current_route->mute_control()->muted_by_others()) { start_blinking (Mute); } else { stop_blinking (Mute); @@ -1252,7 +1252,7 @@ FaderPort::map_recenable () { boost::shared_ptr t = boost::dynamic_pointer_cast (_current_route); if (t) { - get_button (Rec).set_led_state (_output_port, t->record_enabled()); + get_button (Rec).set_led_state (_output_port, t->rec_enable_control()->get_value()); } else { get_button (Rec).set_led_state (_output_port, false); } diff --git a/libs/surfaces/faderport/operations.cc b/libs/surfaces/faderport/operations.cc index bb72794504..592b26da23 100644 --- a/libs/surfaces/faderport/operations.cc +++ b/libs/surfaces/faderport/operations.cc @@ -129,9 +129,9 @@ FaderPort::mute () return; } - boost::shared_ptr rl (new RouteList); - rl->push_back (_current_route); - session->set_mute (rl, !_current_route->muted()); + boost::shared_ptr cl (new ControlList); + cl->push_back (_current_route->mute_control()); + session->set_controls (cl, !_current_route->muted(), PBD::Controllable::UseGroup); } void @@ -141,14 +141,15 @@ FaderPort::solo () return; } - boost::shared_ptr rl (new RouteList); - rl->push_back (_current_route); + bool yn; if (Config->get_solo_control_is_listen_control()) { - session->set_listen (rl, !_current_route->listening_via_monitor()); + yn = !_current_route->listening_via_monitor(); } else { - session->set_solo (rl, !_current_route->soloed()); + yn = !_current_route->soloed(); } + + _current_route->solo_control()->set_value (yn ? 1.0 : 0.0, PBD::Controllable::UseGroup); } void @@ -164,10 +165,7 @@ FaderPort::rec_enable () return; } - boost::shared_ptr rl (new RouteList); - rl->push_back (_current_route); - - session->set_record_enabled (rl, !t->record_enabled()); + t->rec_enable_control()->set_value (!t->rec_enable_control()->get_value(), Controllable::UseGroup); } void diff --git a/libs/surfaces/mackie/mcp_buttons.cc b/libs/surfaces/mackie/mcp_buttons.cc index 1869d5e231..025d56d25a 100644 --- a/libs/surfaces/mackie/mcp_buttons.cc +++ b/libs/surfaces/mackie/mcp_buttons.cc @@ -383,7 +383,7 @@ MackieControlProtocol::save_press (Button &) } else { save_state (); } - + return none; } @@ -886,15 +886,9 @@ MackieControlProtocol::clearsolo_press (Mackie::Button&) access_action ("Editor/set-session-from-edit-range"); return none; } - - if (session) { - if (session->soloing()) { - session->set_solo (session->get_routes(), false); - } else if (session->listening()) { - session->set_listen (session->get_routes(), false); - } - session->clear_all_solo_state (session->get_routes()); // safeguard, ideally this won't do anything, check the log-window + if (session) { + session->clear_all_solo_state (session->get_routes()); } return none; } @@ -1063,7 +1057,7 @@ MackieControlProtocol::nudge_release (Mackie::Button&) _modifier_state &= ~MODIFIER_NUDGE; /* XXX these action names are stupid, because the action can affect - * regions, markers or the playhead depending on selection state. + * regions, markers or the playhead depending on selection state. */ if (main_modifier_state() & MODIFIER_SHIFT) { diff --git a/libs/surfaces/mackie/strip.cc b/libs/surfaces/mackie/strip.cc index caa3a3bc66..0062c3b4fb 100644 --- a/libs/surfaces/mackie/strip.cc +++ b/libs/surfaces/mackie/strip.cc @@ -37,14 +37,17 @@ #include "ardour/debug.h" #include "ardour/midi_ui.h" #include "ardour/meter.h" +#include "ardour/monitor_control.h" #include "ardour/plugin_insert.h" #include "ardour/pannable.h" #include "ardour/panner.h" #include "ardour/panner_shell.h" +#include "ardour/phase_control.h" #include "ardour/rc_configuration.h" #include "ardour/route.h" #include "ardour/session.h" #include "ardour/send.h" +#include "ardour/solo_isolate_control.h" #include "ardour/track.h" #include "ardour/midi_track.h" #include "ardour/user_bundle.h" @@ -302,7 +305,10 @@ void Strip::notify_record_enable_changed () { if (_route && _recenable) { - _surface->write (_recenable->set_state (_route->record_enabled() ? on : off)); + boost::shared_ptr trk = boost::dynamic_pointer_cast (_route); + if (trk) { + _surface->write (_recenable->set_state (trk->rec_enable_control()->get_value() ? on : off)); + } } } diff --git a/libs/surfaces/osc/osc.cc b/libs/surfaces/osc/osc.cc index b26c2d8597..d758e29f03 100644 --- a/libs/surfaces/osc/osc.cc +++ b/libs/surfaces/osc/osc.cc @@ -974,7 +974,7 @@ OSC::routes_list (lo_message msg) || boost::dynamic_pointer_cast(r)) { boost::shared_ptr t = boost::dynamic_pointer_cast(r); - lo_message_add_int32 (reply, t->record_enabled()); + lo_message_add_int32 (reply, (int32_t) t->rec_enable_control()->get_value()); } //Automatically listen to routes listed @@ -1054,7 +1054,7 @@ OSC::route_mute (int rid, int yn) boost::shared_ptr r = session->route_by_remote_id (rid); if (r) { - r->set_mute (yn, PBD::Controllable::NoGroup); + r->mute_control()->set_value (yn ? 1.0 : 0.0, PBD::Controllable::NoGroup); } return 0; @@ -1068,7 +1068,7 @@ OSC::route_solo (int rid, int yn) boost::shared_ptr r = session->route_by_remote_id (rid); if (r) { - r->solo_control()->set_value(yn, PBD::Controllable::NoGroup); + r->solo_control()->set_value (yn ? 1.0 : 0.0, PBD::Controllable::NoGroup); } return 0; @@ -1082,7 +1082,10 @@ OSC::route_recenable (int rid, int yn) boost::shared_ptr r = session->route_by_remote_id (rid); if (r) { - r->set_record_enabled (yn, PBD::Controllable::NoGroup); + boost::shared_ptr trk = boost::dynamic_pointer_cast (r); + if (trk) { + trk->rec_enable_control()->set_value (yn, PBD::Controllable::UseGroup); + } } return 0; @@ -1096,7 +1099,7 @@ OSC::route_set_gain_abs (int rid, float level) boost::shared_ptr r = session->route_by_remote_id (rid); if (r) { - r->set_gain (level, PBD::Controllable::NoGroup); + r->gain_control()->set_value (level, PBD::Controllable::NoGroup); } return 0;