fix logic for Session::solo_control_mode_changed()
[ardour.git] / libs / ardour / session.cc
index cddd85902e6e50afdec609bc3f5125230042ba0c..e42efae882b49245c53a0c5299321db61436fb69 100644 (file)
@@ -36,7 +36,6 @@
 #include <boost/algorithm/string/erase.hpp>
 
 #include "pbd/basename.h"
-#include "pbd/boost_debug.h"
 #include "pbd/convert.h"
 #include "pbd/convert.h"
 #include "pbd/error.h"
@@ -59,6 +58,7 @@
 #include "ardour/audioengine.h"
 #include "ardour/audiofilesource.h"
 #include "ardour/auditioner.h"
+#include "ardour/boost_debug.h"
 #include "ardour/buffer_manager.h"
 #include "ardour/buffer_set.h"
 #include "ardour/bundle.h"
@@ -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"
 #include "ardour/track.h"
 #include "ardour/user_bundle.h"
 #include "ardour/utils.h"
+#include "ardour/vca_manager.h"
 
 #include "midi++/port.h"
 #include "midi++/mmc.h"
@@ -301,7 +303,7 @@ Session::Session (AudioEngine &eng,
        , first_file_header_format_reset (true)
        , have_looped (false)
        , _have_rec_enabled_track (false)
-    , _have_rec_disabled_track (true)
+       , _have_rec_disabled_track (true)
        , _step_editors (0)
        , _suspend_timecode_transmission (0)
        ,  _speakers (new Speakers)
@@ -311,6 +313,7 @@ Session::Session (AudioEngine &eng,
        , _scene_changer (0)
        , _midi_ports (0)
        , _mmc (0)
+       , _vca_manager (new VCAManager (*this))
 {
        uint32_t sr = 0;
 
@@ -694,6 +697,11 @@ Session::destroy ()
        DEBUG_TRACE (DEBUG::Destruction, "delete regions\n");
        RegionFactory::delete_all_regions ();
 
+       /* Do this early so that VCAs no longer hold references to routes */
+
+       DEBUG_TRACE (DEBUG::Destruction, "delete vcas\n");
+       delete _vca_manager;
+
        DEBUG_TRACE (DEBUG::Destruction, "delete routes\n");
 
        /* reset these three references to special routes before we do the usual route delete thing */
@@ -785,9 +793,7 @@ Session::destroy ()
 
        DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
 
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-       boost_debug_list_ptrs ();
-#endif
+       BOOST_SHOW_POINTERS ();
 }
 
 void
@@ -1147,9 +1153,8 @@ Session::add_monitor_section ()
                return;
        }
 
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-       // boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
-#endif
+       BOOST_MARK_ROUTE(r);
+
        try {
                Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
                r->input()->ensure_io (_master_out->output()->n_ports(), false, this);
@@ -1502,7 +1507,7 @@ Session::set_track_monitor_input_status (bool yn)
        boost::shared_ptr<RouteList> rl = routes.reader ();
        for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
                boost::shared_ptr<AudioTrack> tr = boost::dynamic_pointer_cast<AudioTrack> (*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);
                }
@@ -1937,15 +1942,9 @@ void
 Session::set_all_tracks_record_enabled (bool enable )
 {
        boost::shared_ptr<RouteList> rl = routes.reader();
-       for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
-               boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*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)
 {
@@ -2458,9 +2457,8 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost:
 
                        track->use_new_diskstream();
 
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                       // boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
-#endif
+                       BOOST_MARK_TRACK (track);
+
                        {
                                Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
                                if (track->input()->ensure_io (input, false, this)) {
@@ -2558,9 +2556,8 @@ Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name
                                bus->set_strict_io (true);
                        }
 
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                       // boost_debug_shared_ptr_mark_interesting (bus.get(), "Route");
-#endif
+                       BOOST_MARK_ROUTE(bus);
+
                        {
                                Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
 
@@ -2979,15 +2976,14 @@ 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);
                                }
                        }
 
                        track->use_new_diskstream();
 
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                       // boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
-#endif
+                       BOOST_MARK_TRACK (track);
+
                        {
                                Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
 
@@ -3082,9 +3078,8 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r
                                bus->set_strict_io (true);
                        }
 
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-                       // boost_debug_shared_ptr_mark_interesting (bus.get(), "Route");
-#endif
+                       BOOST_MARK_ROUTE(bus);
+
                        {
                                Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
 
@@ -3217,6 +3212,7 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s
                        case NewPlaylist:
                                rename_playlist = true;
                                break;
+                       default:
                        case CopyPlaylist:
                        case SharePlaylist:
                                rename_playlist = false;
@@ -3410,10 +3406,10 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
                boost::weak_ptr<Route> wpr (*x);
                boost::shared_ptr<Route> r (*x);
 
-               r->listen_changed.connect_same_thread (*this, boost::bind (&Session::route_listen_changed, this, _1, wpr));
-               r->solo_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, _2, wpr));
-               r->solo_isolated_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_isolated_changed, this, wpr));
-               r->mute_changed.connect_same_thread (*this, boost::bind (&Session::route_mute_changed, this));
+               r->solo_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, _2, wpr));
+               r->solo_isolate_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_solo_isolated_changed, this, wpr));
+               r->mute_control()->Changed.connect_same_thread (*this, boost::bind (&Session::route_mute_changed, this));
+
                r->output()->changed.connect_same_thread (*this, boost::bind (&Session::set_worst_io_latencies_x, this, _1, _2));
                r->processors_changed.connect_same_thread (*this, boost::bind (&Session::route_processors_changed, this, _1));
 
@@ -3429,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<Track> (tr)));
                        track_playlist_changed (boost::weak_ptr<Track> (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<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (tr);
                        if (mt) {
@@ -3564,7 +3560,6 @@ Session::add_internal_send (boost::shared_ptr<Route> dest, boost::shared_ptr<Pro
        graph_reordered ();
 }
 
-
 void
 Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
 {
@@ -3580,7 +3575,7 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
                                continue;
                        }
 
-                       (*iter)->set_solo (false, Controllable::NoGroup);
+                       (*iter)->solo_control()->set_value (0.0, Controllable::NoGroup);
 
                        rs->remove (*iter);
 
@@ -3700,9 +3695,9 @@ Session::route_mute_changed ()
 void
 Session::route_listen_changed (Controllable::GroupControlDisposition group_override, boost::weak_ptr<Route> wpr)
 {
-       boost::shared_ptr<Route> route = wpr.lock();
+       boost::shared_ptr<Route> route (wpr.lock());
+
        if (!route) {
-               error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_listen_changed")) << endmsg;
                return;
        }
 
@@ -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);
                        }
                }
 
@@ -3747,20 +3742,19 @@ Session::route_listen_changed (Controllable::GroupControlDisposition group_overr
 
        update_route_solo_state ();
 }
+
 void
 Session::route_solo_isolated_changed (boost::weak_ptr<Route> wpr)
 {
-       boost::shared_ptr<Route> route = wpr.lock ();
+       boost::shared_ptr<Route> route (wpr.lock());
 
        if (!route) {
-               /* should not happen */
-               error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_solo_isolated_changed")) << endmsg;
                return;
        }
 
        bool send_changed = false;
 
-       if (route->solo_isolated()) {
+       if (route->solo_isolate_control()->solo_isolated()) {
                if (_solo_isolated_cnt == 0) {
                        send_changed = true;
                }
@@ -3782,14 +3776,22 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi
 {
        DEBUG_TRACE (DEBUG::Solo, string_compose ("route solo change, self = %1\n", self_solo_change));
 
+       boost::shared_ptr<Route> route (wpr.lock());
+
+       if (!route) {
+               return;
+       }
+
+       if (Config->get_solo_control_is_listen_control()) {
+               route_listen_changed (group_override, wpr);
+               return;
+       }
+
        if (!self_solo_change) {
                // session doesn't care about changes to soloed-by-others
                return;
        }
 
-       boost::shared_ptr<Route> route = wpr.lock ();
-       assert (route);
-
        boost::shared_ptr<RouteList> r = routes.reader ();
        int32_t delta;
 
@@ -3829,7 +3831,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;
                        }
@@ -3843,7 +3845,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);
                }
        }
 
@@ -3862,7 +3864,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;
                }
@@ -3884,7 +3886,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");
                                }
@@ -3913,7 +3915,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()));
                        }
@@ -3938,7 +3940,7 @@ Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDi
        for (RouteList::iterator i = uninvolved.begin(); i != uninvolved.end(); ++i) {
                DEBUG_TRACE (DEBUG::Solo, string_compose ("mute change for %1, which neither feeds or is fed by %2\n", (*i)->name(), route->name()));
                (*i)->act_on_mute ();
-               (*i)->mute_changed ();
+               (*i)->mute_control()->Changed (false, Controllable::NoGroup);
        }
 
        SoloChanged (); /* EMIT SIGNAL */
@@ -3960,20 +3962,21 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
        }
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_auditioner() && (*i)->self_soloed()) {
-                       something_soloed = true;
-               }
-
-               if (!(*i)->is_auditioner() && (*i)->listening_via_monitor()) {
+               if ((*i)->can_solo()) {
                        if (Config->get_solo_control_is_listen_control()) {
-                               listeners++;
-                               something_listening = true;
+                               if ((*i)->self_soloed()) {
+                                       listeners++;
+                                       something_listening = true;
+                               }
                        } else {
-                               (*i)->set_listen (false, Controllable::NoGroup);
+                               (*i)->set_listen (false);
+                               if ((*i)->can_solo() && (*i)->self_soloed()) {
+                                       something_soloed = true;
+                               }
                        }
                }
 
-               if ((*i)->solo_isolated()) {
+               if ((*i)->solo_isolate_control()->solo_isolated()) {
                        isolated++;
                }
        }
@@ -4195,6 +4198,21 @@ Session::route_by_remote_id (uint32_t id)
 }
 
 
+boost::shared_ptr<Stripable>
+Session::stripable_by_remote_id (uint32_t id)
+{
+       boost::shared_ptr<RouteList> r = routes.reader ();
+
+       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+               if ((*i)->remote_control_id() == id) {
+                       return *i;
+               }
+       }
+
+       return boost::shared_ptr<Route> ((Route*) 0);
+}
+
+
 boost::shared_ptr<Route>
 Session::route_by_selected_count (uint32_t id)
 {
@@ -5116,7 +5134,7 @@ Session::setup_lua ()
                        "   assert(self.scripts[n] == nil, 'Callback \"'.. n ..'\" already exists.')"
                        "   self.scripts[n] = { ['f'] = f, ['a'] = a }"
                        "   local env = _ENV;  env.f = nil env.io = nil env.os = nil env.loadfile = nil env.require = nil env.dofile = nil env.package = nil env.debug = nil"
-                       "   local env = { print = print, Session = Session, tostring = tostring, assert = assert, ipairs = ipairs, error = error, select = select, string = string, type = type, tonumber = tonumber, collectgarbage = collectgarbage, pairs = pairs, math = math, table = table, pcall = pcall }"
+                       "   local env = { print = print, tostring = tostring, assert = assert, ipairs = ipairs, error = error, select = select, string = string, type = type, tonumber = tonumber, collectgarbage = collectgarbage, pairs = pairs, math = math, table = table, pcall = pcall, Session = Session, PBD = PBD, Timecode = Timecode, Evoral = Evoral, C = C, ARDOUR = ARDOUR }"
                        "   self.instances[n] = load (string.dump(f, true), nil, nil, env)(a)"
                        "   Session:scripts_changed()" // call back
                        "  end"
@@ -5445,6 +5463,16 @@ Session::tempo_map_changed (const PropertyChange&)
        set_dirty ();
 }
 
+void
+Session::gui_tempo_map_changed ()
+{
+       clear_clicks ();
+
+       playlists->update_after_tempo_map_change ();
+
+       _locations->apply (*this, &Session::update_locations_after_tempo_map_change);
+}
+
 void
 Session::update_locations_after_tempo_map_change (const Locations::LocationList& loc)
 {
@@ -5927,7 +5955,6 @@ Session::write_one_track (Track& track, framepos_t start, framepos_t end,
        }
 
        unblock_processing ();
-       itt.done = true;
 
        return result;
 }
@@ -6045,7 +6072,7 @@ Session::update_route_record_state ()
        while (i != rl->end ()) {
 
                boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
-               if (tr && tr->record_enabled ()) {
+                                   if (tr && tr->rec_enable_control()->get_value()) {
                        break;
                }
 
@@ -6062,7 +6089,7 @@ Session::update_route_record_state ()
 
        for (i = rl->begin(); i != rl->end (); ++i) {
                boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
-               if (tr && !tr->record_enabled ()) {
+               if (tr && !tr->rec_enable_control()->get_value()) {
                        break;
                }
        }
@@ -6090,12 +6117,15 @@ Session::listen_position_changed ()
 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);
+       if (soloing() || listening()) {
+               /* We can't use ::clear_all_solo_state() here because during
+                  session loading at program startup, that will queue a call
+                  to rt_clear_all_solo_state() that will not execute until
+                  AFTER solo states have been established (thus throwing away
+                  the session's saved solo state). So just explicitly turn
+                  them all off.
+               */
+               set_controls (route_list_to_control_list (get_routes(), &Route::solo_control), 0.0, Controllable::NoGroup);
        }
 }