first pass at making the GUI for a VCA visible
[ardour.git] / libs / ardour / session.cc
index 3b03985ece78fe1203ba94ee9f40f2097fe98551..4ff42730ca328b6f95a55a14444df7bf6b70599d 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"
 #include "ardour/source_factory.h"
 #include "ardour/speakers.h"
 #include "ardour/tempo.h"
+#include "ardour/ticker.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"
@@ -300,15 +302,17 @@ 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)
        , _order_hint (-1)
        , ignore_route_processor_changes (false)
+       , midi_clock (0)
        , _scene_changer (0)
        , _midi_ports (0)
        , _mmc (0)
+       , _vca_manager (new VCAManager (*this))
 {
        uint32_t sr = 0;
 
@@ -331,7 +335,7 @@ Session::Session (AudioEngine &eng,
 #ifdef USE_TRACKS_CODE_FEATURES
                sr = EngineStateController::instance()->get_current_sample_rate();
 #endif
-               if (ensure_engine (sr)) {
+               if (ensure_engine (sr, true)) {
                        destroy ();
                        throw SessionException (_("Cannot connect to audio/midi engine"));
                }
@@ -379,13 +383,14 @@ Session::Session (AudioEngine &eng,
                 */
 
                if (state_tree) {
-                       const XMLProperty* prop;
-                       if ((prop = state_tree->root()->property (X_("sample-rate"))) != 0) {
+                       XMLProperty const * prop;
+                       XMLNode const * root (state_tree->root());
+                       if ((prop = root->property (X_("sample-rate"))) != 0) {
                                sr = atoi (prop->value());
                        }
                }
 
-               if (ensure_engine (sr)) {
+               if (ensure_engine (sr, false)) {
                        destroy ();
                        throw SessionException (_("Cannot connect to audio/midi engine"));
                }
@@ -498,7 +503,7 @@ Session::init_name_id_counter (guint n)
 }
 
 int
-Session::ensure_engine (uint32_t desired_sample_rate)
+Session::ensure_engine (uint32_t desired_sample_rate, bool isnew)
 {
        if (_engine.current_backend() == 0) {
                /* backend is unknown ... */
@@ -506,6 +511,8 @@ Session::ensure_engine (uint32_t desired_sample_rate)
                if (r.get_value_or (-1) != 0) {
                        return -1;
                }
+       } else if (!isnew && _engine.running() && _engine.sample_rate () == desired_sample_rate) {
+               /* keep engine */
        } else if (_engine.setup_required()) {
                /* backend is known, but setup is needed */
                boost::optional<int> r = AudioEngineSetupRequired (desired_sample_rate);
@@ -518,8 +525,7 @@ Session::ensure_engine (uint32_t desired_sample_rate)
                }
        }
 
-       /* at this point the engine should be running
-       */
+       /* at this point the engine should be running */
 
        if (!_engine.running()) {
                return -1;
@@ -557,6 +563,7 @@ Session::immediately_post_engine ()
        }
 
        try {
+               LocaleGuard lg;
                BootMessage (_("Set up LTC"));
                setup_ltc ();
                BootMessage (_("Set up Click"));
@@ -596,6 +603,7 @@ Session::destroy ()
 
        /* disconnect from any and all signals that we are connected to */
 
+       Port::PortSignalDrop (); /* EMIT SIGNAL */
        drop_connections ();
 
        /* shutdown control surface protocols while we still have ports
@@ -616,7 +624,6 @@ Session::destroy ()
         * callbacks from the engine any more.
         */
 
-       Port::PortSignalDrop (); /* EMIT SIGNAL */
        Port::PortDrop (); /* EMIT SIGNAL */
 
        ltc_tx_cleanup();
@@ -739,13 +746,49 @@ Session::destroy ()
        delete _midi_ports; _midi_ports = 0;
        delete _locations; _locations = 0;
 
+       delete midi_clock;
        delete _tempo_map;
+       delete _vca_manager;
+
+       /* clear event queue, the session is gone, nobody is interested in
+        * those anymore, but they do leak memory if not removed
+        */
+       while (!immediate_events.empty ()) {
+               Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               SessionEvent *ev = immediate_events.front ();
+               DEBUG_TRACE (DEBUG::SessionEvents, string_compose ("Drop event: %1\n", enum_2_string (ev->type)));
+               immediate_events.pop_front ();
+               bool remove = true;
+               bool del = true;
+               switch (ev->type) {
+                       case SessionEvent::AutoLoop:
+                       case SessionEvent::AutoLoopDeclick:
+                       case SessionEvent::Skip:
+                       case SessionEvent::PunchIn:
+                       case SessionEvent::PunchOut:
+                       case SessionEvent::StopOnce:
+                       case SessionEvent::RangeStop:
+                       case SessionEvent::RangeLocate:
+                               remove = false;
+                               del = false;
+                               break;
+                       case SessionEvent::RealTimeOperation:
+                               process_rtop (ev);
+                               del = false;
+                       default:
+                               break;
+               }
+               if (remove) {
+                       del = del && !_remove_event (ev);
+               }
+               if (del) {
+                       delete ev;
+               }
+       }
 
        DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
 
-#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
-       boost_debug_list_ptrs ();
-#endif
+       BOOST_SHOW_POINTERS ();
 }
 
 void
@@ -1077,11 +1120,16 @@ Session::remove_monitor_section ()
        }
 
        remove_route (_monitor_out);
+       if (_state_of_the_state & Deletion) {
+               return;
+       }
+
        auto_connect_master_bus ();
 
        if (auditioner) {
                auditioner->connect ();
        }
+
        Config->ParameterChanged ("use-monitor-bus");
 }
 
@@ -1100,9 +1148,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);
@@ -2166,16 +2213,18 @@ Session::resort_routes ()
        }
 
 #ifndef NDEBUG
-       boost::shared_ptr<RouteList> rl = routes.reader ();
-       for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
-               DEBUG_TRACE (DEBUG::Graph, string_compose ("%1 fed by ...\n", (*i)->name()));
+       if (DEBUG_ENABLED(DEBUG::Graph)) {
+               boost::shared_ptr<RouteList> rl = routes.reader ();
+               for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
+                       DEBUG_TRACE (DEBUG::Graph, string_compose ("%1 fed by ...\n", (*i)->name()));
 
-               const Route::FedBy& fb ((*i)->fed_by());
+                       const Route::FedBy& fb ((*i)->fed_by());
 
-               for (Route::FedBy::const_iterator f = fb.begin(); f != fb.end(); ++f) {
-                       boost::shared_ptr<Route> sf = f->r.lock();
-                       if (sf) {
-                               DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 (sends only ? %2)\n", sf->name(), f->sends_only));
+                       for (Route::FedBy::const_iterator f = fb.begin(); f != fb.end(); ++f) {
+                               boost::shared_ptr<Route> sf = f->r.lock();
+                               if (sf) {
+                                       DEBUG_TRACE (DEBUG::Graph, string_compose ("\t%1 (sends only ? %2)\n", sf->name(), f->sends_only));
+                               }
                        }
                }
        }
@@ -2377,7 +2426,7 @@ Session::default_track_name_pattern (DataType t)
  */
 list<boost::shared_ptr<MidiTrack> >
 Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost::shared_ptr<PluginInfo> instrument,
-                        TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template)
+                        TrackMode mode, RouteGroup* route_group, uint32_t how_many, string name_template, Plugin::PresetRecord* pset)
 {
        string track_name;
        uint32_t track_id = 0;
@@ -2409,9 +2458,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)) {
@@ -2469,6 +2517,9 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost:
                if (instrument) {
                        for (RouteList::iterator r = new_routes.begin(); r != new_routes.end(); ++r) {
                                PluginPtr plugin = instrument->load (*this);
+                               if (pset) {
+                                       plugin->load_preset (*pset);
+                               }
                                boost::shared_ptr<Processor> p (new PluginInsert (*this, plugin));
                                (*r)->add_processor (p, PreFader);
 
@@ -2480,7 +2531,7 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, boost:
 }
 
 RouteList
-Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name_template, boost::shared_ptr<PluginInfo> instrument)
+Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name_template, boost::shared_ptr<PluginInfo> instrument, Plugin::PresetRecord* pset)
 {
        string bus_name;
        uint32_t bus_id = 0;
@@ -2506,9 +2557,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 ());
 
@@ -2558,6 +2608,9 @@ Session::new_midi_route (RouteGroup* route_group, uint32_t how_many, string name
                if (instrument) {
                        for (RouteList::iterator r = ret.begin(); r != ret.end(); ++r) {
                                PluginPtr plugin = instrument->load (*this);
+                               if (pset) {
+                                       plugin->load_preset (*pset);
+                               }
                                boost::shared_ptr<Processor> p (new PluginInsert (*this, plugin));
                                (*r)->add_processor (p, PreFader);
                        }
@@ -2930,9 +2983,8 @@ Session::new_audio_track (int input_channels, int output_channels, TrackMode mod
 
                        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 ());
 
@@ -3027,9 +3079,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 ());
 
@@ -3162,6 +3213,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;
@@ -3173,10 +3225,48 @@ Session::new_route_from_template (uint32_t how_many, XMLNode& node, const std::s
                        XMLNodeList children = node_copy.children ();
                        for (XMLNodeList::iterator i = children.begin(); i != children.end(); ++i) {
                                if ((*i)->name() == X_("Processor")) {
-                                       XMLProperty* role = (*i)->property (X_("role"));
+                                       /* ForceIDRegeneration does not catch the following */
+                                       XMLProperty const * role = (*i)->property (X_("role"));
+                                       XMLProperty const * type = (*i)->property (X_("type"));
+                                       if (role && role->value() == X_("Aux")) {
+                                               /* check if the target bus exists.
+                                                * we should not save aux-sends in templates.
+                                                */
+                                               XMLProperty const * target = (*i)->property (X_("target"));
+                                               if (!target) {
+                                                       (*i)->add_property ("type", "dangling-aux-send");
+                                                       continue;
+                                               }
+                                               boost::shared_ptr<Route> r = route_by_id (target->value());
+                                               if (!r || boost::dynamic_pointer_cast<Track>(r)) {
+                                                       (*i)->add_property ("type", "dangling-aux-send");
+                                                       continue;
+                                               }
+                                       }
                                        if (role && role->value() == X_("Listen")) {
                                                (*i)->remove_property (X_("bitslot"));
                                        }
+                                       else if (role && (role->value() == X_("Send") || role->value() == X_("Aux"))) {
+                                               char buf[32];
+                                               Delivery::Role xrole;
+                                               uint32_t bitslot = 0;
+                                               xrole = Delivery::Role (string_2_enum (role->value(), xrole));
+                                               std::string name = Send::name_and_id_new_send(*this, xrole, bitslot, false);
+                                               snprintf (buf, sizeof (buf), "%" PRIu32, bitslot);
+                                               (*i)->remove_property (X_("bitslot"));
+                                               (*i)->remove_property (X_("name"));
+                                               (*i)->add_property ("bitslot", buf);
+                                               (*i)->add_property ("name", name);
+                                       }
+                                       else if (type && type->value() == X_("return")) {
+                                               // Return::set_state() generates a new one
+                                               (*i)->remove_property (X_("bitslot"));
+                                       }
+                                       else if (type && type->value() == X_("port")) {
+                                               // PortInsert::set_state() handles the bitslot
+                                               (*i)->remove_property (X_("bitslot"));
+                                               (*i)->add_property ("ignore-name", "1");
+                                       }
                                }
                        }
 
@@ -3471,7 +3561,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)
 {
@@ -3557,7 +3646,7 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
                resort_routes ();
 #endif
 
-       if (_process_graph) {
+       if (_process_graph && !(_state_of_the_state & Deletion)) {
                _process_graph->clear_other_chain ();
        }
 
@@ -3574,6 +3663,10 @@ Session::remove_routes (boost::shared_ptr<RouteList> routes_to_remove)
                (*iter)->drop_references ();
        }
 
+       if (_state_of_the_state & Deletion) {
+               return;
+       }
+
        Route::RemoteControlIDChange(); /* EMIT SIGNAL */
 
        /* save the new state of the world */
@@ -5019,7 +5112,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"
@@ -5348,6 +5441,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)
 {
@@ -5830,7 +5933,6 @@ Session::write_one_track (Track& track, framepos_t start, framepos_t end,
        }
 
        unblock_processing ();
-       itt.done = true;
 
        return result;
 }
@@ -6314,9 +6416,10 @@ Session::unknown_processors () const
 void
 Session::update_latency (bool playback)
 {
+
        DEBUG_TRACE (DEBUG::Latency, string_compose ("JACK latency callback: %1\n", (playback ? "PLAYBACK" : "CAPTURE")));
 
-       if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress) {
+       if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress || _route_deletion_in_progress) {
                return;
        }
 
@@ -6707,9 +6810,6 @@ Session::auto_connect (const AutoConnectRequest& ar)
                return;
        }
 
-       //why would we need the process lock ??
-       //Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
-
        /* If both inputs and outputs are auto-connected to physical ports,
         * use the max of input and output offsets to ensure auto-connected
         * port numbers always match up (e.g. the first audio input and the
@@ -6841,18 +6941,27 @@ void
 Session::auto_connect_thread_run ()
 {
        pthread_set_name (X_("autoconnect"));
-       SessionEvent::create_per_thread_pool (X_("autoconnect"), 256);
-       PBD::notify_event_loops_about_thread_creation (pthread_self(), X_("autoconnect"), 256);
+       SessionEvent::create_per_thread_pool (X_("autoconnect"), 1024);
+       PBD::notify_event_loops_about_thread_creation (pthread_self(), X_("autoconnect"), 1024);
        pthread_mutex_lock (&_auto_connect_mutex);
        while (_ac_thread_active) {
 
-               while (!_auto_connect_queue.empty ()) {
+               if (!_auto_connect_queue.empty ()) {
+                       // Why would we need the process lock ??
+                       // A: if ports are added while we're connecting, the backend's iterator may be invalidated:
+                       //   graph_order_callback() -> resort_routes() -> direct_feeds_according_to_reality () -> backend::connected_to()
+                       //   All ardour-internal backends use a std::vector   xxxAudioBackend::find_port()
+                       //   We have control over those, but what does jack do?
+                       Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+
                        Glib::Threads::Mutex::Lock lx (_auto_connect_queue_lock);
-                       if (_auto_connect_queue.empty ()) { break; } // re-check with lock
-                       const AutoConnectRequest ar (_auto_connect_queue.front());
-                       _auto_connect_queue.pop ();
-                       lx.release ();
-                       auto_connect (ar);
+                       while (!_auto_connect_queue.empty ()) {
+                               const AutoConnectRequest ar (_auto_connect_queue.front());
+                               _auto_connect_queue.pop ();
+                               lx.release ();
+                               auto_connect (ar);
+                               lx.acquire ();
+                       }
                }
 
                pthread_cond_wait (&_auto_connect_cond, &_auto_connect_mutex);