change exposed type of various objects' gain controls; remove Amp::gain() as a shortcut
[ardour.git] / libs / ardour / session.cc
index 479b433f2937b95f9b33ec6da9b565df73b4570f..75547b534897c5d077544c727ac3775daedb5fee 100644 (file)
 #include "ardour/engine_state_controller.h"
 #endif
 #include "ardour/filename_extensions.h"
+#include "ardour/gain_control.h"
 #include "ardour/graph.h"
 #include "ardour/midiport_manager.h"
 #include "ardour/scene_changer.h"
+#include "ardour/midi_patch_manager.h"
 #include "ardour/midi_track.h"
 #include "ardour/midi_ui.h"
 #include "ardour/operations.h"
@@ -179,9 +181,10 @@ Session::Session (AudioEngine &eng,
        , current_block_size (0)
        , _worst_output_latency (0)
        , _worst_input_latency (0)
-       , _worst_track_latency (0)
+       , _worst_track_latency (0)
        , _have_captured (false)
        , _non_soloed_outs_muted (false)
+       , _listening (false)
        , _listen_cnt (0)
        , _solo_isolated_cnt (0)
        , _writable (false)
@@ -316,6 +319,11 @@ Session::Session (AudioEngine &eng,
                        throw SessionException (_("Cannot connect to audio/midi engine"));
                }
 
+               // set samplerate for plugins added early
+               // e.g from templates or MB channelstrip
+               set_block_size (_engine.samples_per_cycle());
+               set_frame_rate (_engine.sample_rate());
+
                if (create (mix_template, bus_profile)) {
                        destroy ();
                        throw SessionException (_("Session initialization failed"));
@@ -543,6 +551,8 @@ Session::destroy ()
 
        remove_pending_capture_state ();
 
+       Analyser::flush ();
+
        _state_of_the_state = StateOfTheState (CannotSave|Deletion);
 
        /* disconnect from any and all signals that we are connected to */
@@ -555,6 +565,8 @@ Session::destroy ()
 
        ControlProtocolManager::instance().drop_protocols ();
 
+       MIDI::Name::MidiPatchManager::instance().remove_search_path(session_directory().midi_patch_path());
+
        _engine.remove_session ();
 
 #ifdef USE_TRACKS_CODE_FEATURES
@@ -722,8 +734,12 @@ void
 Session::setup_click ()
 {
        _clicking = false;
+
+       boost::shared_ptr<AutomationList> gl (new AutomationList (Evoral::Parameter (GainAutomation)));
+       boost::shared_ptr<GainControl> gain_control = boost::shared_ptr<GainControl> (new GainControl (*this, Evoral::Parameter(GainAutomation), gl));
+
        _click_io.reset (new ClickIO (*this, X_("Click")));
-       _click_gain.reset (new Amp (*this));
+       _click_gain.reset (new Amp (*this, _("Fader"), gain_control, true));
        _click_gain->activate ();
        if (state_tree) {
                setup_click_state (state_tree->root());
@@ -1012,6 +1028,7 @@ Session::remove_monitor_section ()
        if (auditioner) {
                auditioner->connect ();
        }
+       Config->ParameterChanged ("use-monitor-bus");
 }
 
 void
@@ -1163,6 +1180,7 @@ Session::add_monitor_section ()
        if (auditioner) {
                auditioner->connect ();
        }
+       Config->ParameterChanged ("use-monitor-bus");
 }
 
 void
@@ -1814,6 +1832,19 @@ Session::enable_record ()
        }
 }
 
+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);
+               }
+       }
+}
+
+
 void
 Session::disable_record (bool rt_context, bool force)
 {
@@ -2064,6 +2095,10 @@ Session::resort_routes ()
                return;
        }
 
+       if (_route_deletion_in_progress) {
+               return;
+       }
+
        {
                RCUWriter<RouteList> writer (routes);
                boost::shared_ptr<RouteList> r = writer.get_copy ();
@@ -2207,9 +2242,14 @@ Session::find_route_name (string const & base, uint32_t& id, string& name, bool
 
        for (vector<string>::const_iterator reserved = reserved_io_names.begin(); reserved != reserved_io_names.end(); ++reserved) {
                if (base == *reserved) {
-                       definitely_add_number = true;
-                       if (id < 1) {
-                               id = 1;
+                       /* Check if this reserved name already exists, and if
+                          so, disallow it without a numeric suffix.
+                       */
+                       if (route_by_name (*reserved)) {
+                               definitely_add_number = true;
+                               if (id < 1) {
+                                       id = 1;
+                               }
                        }
                        break;
                }
@@ -2853,7 +2893,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), 0);
+                                       track->set_gain (dB_to_coefficient (0), Controllable::NoGroup);
                                }
                        }
 
@@ -3020,30 +3060,39 @@ Session::new_audio_route (int input_channels, int output_channels, RouteGroup* r
 }
 
 RouteList
-Session::new_route_from_template (uint32_t how_many, const std::string& template_path, const std::string& name_base)
+Session::new_route_from_template (uint32_t how_many, const std::string& template_path, const std::string& name_base, PlaylistDisposition pd)
 {
-       RouteList ret;
-       uint32_t control_id;
        XMLTree tree;
-       uint32_t number = 0;
-       const uint32_t being_added = how_many;
 
        if (!tree.read (template_path.c_str())) {
-               return ret;
+               return RouteList();
        }
 
-       XMLNode* node = tree.root();
+       return new_route_from_template (how_many, *tree.root(), name_base, pd);
+}
 
+RouteList
+Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::string& name_base, PlaylistDisposition pd)
+{
+       RouteList ret;
+       uint32_t control_id;
+       uint32_t number = 0;
+       const uint32_t being_added = how_many;
+       /* This will prevent the use of any existing XML-provided PBD::ID
+          values by Stateful.
+       */
+       Stateful::ForceIDRegeneration force_ids;
        IO::disable_connecting ();
 
        control_id = next_control_id ();
 
        while (how_many) {
 
-               XMLNode node_copy (*node);
+               /* We're going to modify the node contents a bit so take a
+                * copy. The node may be re-used when duplicating more than once.
+                */
 
-               /* Remove IDs of everything so that new ones are used */
-               node_copy.remove_property_recursively (X_("id"));
+               XMLNode node_copy (node);
 
                try {
                        string name;
@@ -3072,7 +3121,18 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template
                        }
 
                        /* set this name in the XML description that we are about to use */
-                       Route::set_name_in_state (node_copy, name);
+
+                       bool rename_playlist;
+                       switch (pd) {
+                       case NewPlaylist:
+                               rename_playlist = true;
+                               break;
+                       case CopyPlaylist:
+                       case SharePlaylist:
+                               rename_playlist = false;
+                       }
+
+                       Route::set_name_in_state (node_copy, name, rename_playlist);
 
                        /* trim bitslots from listen sends so that new ones are used */
                        XMLNodeList children = node_copy.children ();
@@ -3110,6 +3170,21 @@ Session::new_route_from_template (uint32_t how_many, const std::string& template
                        route->set_remote_control_id (control_id);
                        ++control_id;
 
+                       boost::shared_ptr<Track> track;
+
+                       if ((track = boost::dynamic_pointer_cast<Track> (route))) {
+                               switch (pd) {
+                               case NewPlaylist:
+                                       track->use_new_playlist ();
+                                       break;
+                               case CopyPlaylist:
+                                       track->use_copy_playlist ();
+                                       break;
+                               case SharePlaylist:
+                                       break;
+                               }
+                       };
+
                        ret.push_back (route);
 
                        RouteAddedOrRemoved (true); /* EMIT SIGNAL */
@@ -3206,10 +3281,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, _2, wpr));
-               r->solo_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_changed, this, _1, _3, wpr));
-               r->solo_isolated_changed.connect_same_thread (*this, boost::bind (&Session::route_solo_isolated_changed, this, _1, wpr));
-               r->mute_changed.connect_same_thread (*this, boost::bind (&Session::route_mute_changed, this, _1));
+               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->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));
 
@@ -3281,7 +3356,7 @@ Session::globally_set_send_gains_to_zero (boost::shared_ptr<Route> dest)
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                if ((s = (*i)->internal_send_for (dest)) != 0) {
-                       s->amp()->gain_control()->set_value (GAIN_COEFF_ZERO);
+                       s->amp()->gain_control()->set_value (GAIN_COEFF_ZERO, Controllable::NoGroup);
                }
        }
 }
@@ -3294,7 +3369,7 @@ Session::globally_set_send_gains_to_unity (boost::shared_ptr<Route> dest)
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                if ((s = (*i)->internal_send_for (dest)) != 0) {
-                       s->amp()->gain_control()->set_value (GAIN_COEFF_UNITY);
+                       s->amp()->gain_control()->set_value (GAIN_COEFF_UNITY, Controllable::NoGroup);
                }
        }
 }
@@ -3307,7 +3382,7 @@ Session::globally_set_send_gains_from_track(boost::shared_ptr<Route> dest)
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
                if ((s = (*i)->internal_send_for (dest)) != 0) {
-                       s->amp()->gain_control()->set_value ((*i)->gain_control()->get_value());
+                       s->amp()->gain_control()->set_value ((*i)->gain_control()->get_value(), Controllable::NoGroup);
                }
        }
 }
@@ -3363,9 +3438,8 @@ Session::add_internal_send (boost::shared_ptr<Route> dest, boost::shared_ptr<Pro
 void
 Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
 {
-       PBD::Unwinder<bool> uw_flag (_route_deletion_in_progress, true);
-
        { // RCU Writer scope
+               PBD::Unwinder<bool> uw_flag (_route_deletion_in_progress, true);
                RCUWriter<RouteList> writer (routes);
                boost::shared_ptr<RouteList> rs = writer.get_copy ();
 
@@ -3376,7 +3450,7 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
                                continue;
                        }
 
-                       (*iter)->set_solo (false, this);
+                       (*iter)->set_solo (false, Controllable::NoGroup);
 
                        rs->remove (*iter);
 
@@ -3484,13 +3558,13 @@ Session::remove_route (boost::shared_ptr<Route> route)
 }
 
 void
-Session::route_mute_changed (void* /*src*/)
+Session::route_mute_changed ()
 {
        set_dirty ();
 }
 
 void
-Session::route_listen_changed (bool group_override, boost::weak_ptr<Route> wpr)
+Session::route_listen_changed (Controllable::GroupControlDisposition group_override, boost::weak_ptr<Route> wpr)
 {
        boost::shared_ptr<Route> route = wpr.lock();
        if (!route) {
@@ -3501,18 +3575,32 @@ Session::route_listen_changed (bool group_override, boost::weak_ptr<Route> wpr)
        if (route->listening_via_monitor ()) {
 
                if (Config->get_exclusive_solo()) {
-                       /* new listen: disable all other listen, except solo-grouped channels */
+
                        RouteGroup* rg = route->route_group ();
-                       bool leave_group_alone = (rg && rg->is_active() && rg->is_solo());
-                       if (group_override && rg) {
-                               leave_group_alone = !leave_group_alone;
-                       }
+                       const bool group_already_accounted_for = route->use_group (group_override, &RouteGroup::is_solo);
+
                        boost::shared_ptr<RouteList> r = routes.reader ();
+
                        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-                               if ((*i) == route || (*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner() || (leave_group_alone && ((*i)->route_group() == rg))) {
+                               if ((*i) == route) {
+                                       /* already changed */
+                                       continue;
+                               }
+
+                               if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+                                       /* route does not get solo propagated to it */
+                                       continue;
+                               }
+
+                               if ((group_already_accounted_for && (*i)->route_group() && (*i)->route_group() == rg)) {
+                                       /* this route is a part of the same solo group as the route
+                                        * that was changed. Changing that route did change or will
+                                        * change all group members appropriately, so we can ignore it
+                                        * here
+                                        */
                                        continue;
                                }
-                               (*i)->set_listen (false, this, group_override);
+                               (*i)->set_listen (false, Controllable::NoGroup);
                        }
                }
 
@@ -3526,7 +3614,7 @@ Session::route_listen_changed (bool group_override, boost::weak_ptr<Route> wpr)
        update_route_solo_state ();
 }
 void
-Session::route_solo_isolated_changed (void* /*src*/, boost::weak_ptr<Route> wpr)
+Session::route_solo_isolated_changed (boost::weak_ptr<Route> wpr)
 {
        boost::shared_ptr<Route> route = wpr.lock ();
 
@@ -3556,7 +3644,7 @@ Session::route_solo_isolated_changed (void* /*src*/, boost::weak_ptr<Route> wpr)
 }
 
 void
-Session::route_solo_changed (bool self_solo_change, bool group_override,  boost::weak_ptr<Route> wpr)
+Session::route_solo_changed (bool self_solo_change, Controllable::GroupControlDisposition group_override,  boost::weak_ptr<Route> wpr)
 {
        DEBUG_TRACE (DEBUG::Solo, string_compose ("route solo change, self = %1\n", self_solo_change));
 
@@ -3577,21 +3665,51 @@ Session::route_solo_changed (bool self_solo_change, bool group_override,  boost:
                delta = -1;
        }
 
+       /* the route may be a member of a group that has shared-solo
+        * semantics. If so, then all members of that group should follow the
+        * solo of the changed route. But ... this is optional, controlled by a
+        * Controllable::GroupControlDisposition.
+        *
+        * The first argument to the signal that this method is connected to is the
+        * GroupControlDisposition value that was used to change solo.
+        *
+        * If the solo change was done with group semantics (either InverseGroup
+        * (force the entire group to change even if the group shared solo is
+        * disabled) or UseGroup (use the group, which may or may not have the
+        * shared solo property enabled)) then as we propagate the change to
+        * the entire session we should IGNORE THE GROUP that the changed route
+        * belongs to.
+        */
+
        RouteGroup* rg = route->route_group ();
-       bool leave_group_alone = (rg && rg->is_active() && rg->is_solo());
-       if (group_override && rg) {
-               leave_group_alone = !leave_group_alone;
-       }
+       const bool group_already_accounted_for = route->use_group (group_override, &RouteGroup::is_solo);
+
        if (delta == 1 && Config->get_exclusive_solo()) {
 
                /* new solo: disable all other solos, but not the group if its solo-enabled */
 
                for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-                       if ((*i) == route || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner() ||
-                           (leave_group_alone && ((*i)->route_group() == rg))) {
+
+                       if ((*i) == route) {
+                               /* already changed */
+                               continue;
+                       }
+
+                       if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+                               /* route does not get solo propagated to it */
                                continue;
                        }
-                       (*i)->set_solo (false, this, group_override);
+
+                       if ((group_already_accounted_for && (*i)->route_group() && (*i)->route_group() == rg)) {
+                               /* this route is a part of the same solo group as the route
+                                * that was changed. Changing that route did change or will
+                                * change all group members appropriately, so we can ignore it
+                                * here
+                                */
+                               continue;
+                       }
+
+                       (*i)->set_solo (false, group_override);
                }
        }
 
@@ -3605,8 +3723,22 @@ Session::route_solo_changed (bool self_solo_change, bool group_override,  boost:
                bool via_sends_only;
                bool in_signal_flow;
 
-               if ((*i) == route || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner() ||
-                   (leave_group_alone && ((*i)->route_group() == rg))) {
+               if ((*i) == route) {
+                       /* already changed */
+                       continue;
+               }
+
+               if ((*i)->solo_isolated() || (*i)->is_master() || (*i)->is_monitor() || (*i)->is_auditioner()) {
+                       /* route does not get solo propagated to it */
+                       continue;
+               }
+
+               if ((group_already_accounted_for && (*i)->route_group() && (*i)->route_group() == rg)) {
+                       /* this route is a part of the same solo group as the route
+                        * that was changed. Changing that route did change or will
+                        * change all group members appropriately, so we can ignore it
+                        * here
+                        */
                        continue;
                }
 
@@ -3672,7 +3804,7 @@ Session::route_solo_changed (bool self_solo_change, bool group_override,  boost:
        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 (this);
+               (*i)->mute_changed ();
        }
 
        SoloChanged (); /* EMIT SIGNAL */
@@ -3685,6 +3817,7 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
        /* now figure out if anything that matters is soloed (or is "listening")*/
 
        bool something_soloed = false;
+       bool something_listening = false;
        uint32_t listeners = 0;
        uint32_t isolated = 0;
 
@@ -3700,8 +3833,9 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
                if (!(*i)->is_auditioner() && (*i)->listening_via_monitor()) {
                        if (Config->get_solo_control_is_listen_control()) {
                                listeners++;
+                               something_listening = true;
                        } else {
-                               (*i)->set_listen (false, this);
+                               (*i)->set_listen (false, Controllable::NoGroup);
                        }
                }
 
@@ -3715,6 +3849,11 @@ Session::update_route_solo_state (boost::shared_ptr<RouteList> r)
                SoloActive (_non_soloed_outs_muted); /* EMIT SIGNAL */
        }
 
+       if (something_listening != _listening) {
+               _listening = something_listening;
+               SoloActive (_listening);
+       }
+
        _listen_cnt = listeners;
 
        if (isolated != _solo_isolated_cnt) {
@@ -3747,6 +3886,11 @@ Session::io_name_is_legal (const std::string& name)
 
        for (vector<string>::const_iterator reserved = reserved_io_names.begin(); reserved != reserved_io_names.end(); ++reserved) {
                if (name == *reserved) {
+                       if (!route_by_name (*reserved)) {
+                               /* first instance of a reserved name is allowed */
+                               return true;
+                       }
+                       /* all other instances of a reserved name are not allowed */
                        return false;
                }
        }