remove InputConfigurationChange from session events, since it is a no-op in this...
[ardour.git] / libs / ardour / session.cc
index 416f972ea784c4da35434d7f800bbb9a8646e105..b658487969c3827479096ab426e66c538c20892d 100644 (file)
@@ -51,7 +51,6 @@
 #include "ardour/analyser.h"
 #include "ardour/async_midi_port.h"
 #include "ardour/audio_buffer.h"
-#include "ardour/audio_diskstream.h"
 #include "ardour/audio_port.h"
 #include "ardour/audio_track.h"
 #include "ardour/audioengine.h"
@@ -66,6 +65,7 @@
 #include "ardour/control_protocol_manager.h"
 #include "ardour/data_type.h"
 #include "ardour/debug.h"
+#include "ardour/disk_reader.h"
 #include "ardour/directory_names.h"
 #ifdef USE_TRACKS_CODE_FEATURES
 #include "ardour/engine_state_controller.h"
@@ -98,6 +98,7 @@
 #include "ardour/session.h"
 #include "ardour/session_directory.h"
 #include "ardour/session_playlists.h"
+#include "ardour/slave.h"
 #include "ardour/smf_source.h"
 #include "ardour/solo_isolate_control.h"
 #include "ardour/source_factory.h"
@@ -186,6 +187,7 @@ Session::Session (AudioEngine &eng,
        , _transport_speed (0)
        , _default_transport_speed (1.0)
        , _last_transport_speed (0)
+       , _signalled_varispeed (0)
        , _target_transport_speed (0.0)
        , auto_play_legal (false)
        , _last_slave_transport_frame (0)
@@ -378,8 +380,12 @@ Session::Session (AudioEngine &eng,
                 */
 
                if (!mix_template.empty()) {
-                       if (load_state (_current_snapshot_name)) {
-                               throw SessionException (_("Failed to load template/snapshot state"));
+                       try {
+                               if (load_state (_current_snapshot_name)) {
+                                       throw SessionException (_("Failed to load template/snapshot state"));
+                               }
+                       } catch (PBD::unknown_enumeration& e) {
+                               throw SessionException (_("Failed to parse template/snapshot state"));
                        }
                        store_recent_templates (mix_template);
                }
@@ -412,9 +418,27 @@ Session::Session (AudioEngine &eng,
                }
        }
 
-       if (post_engine_init ()) {
+       int err = post_engine_init ();
+       if (err) {
                destroy ();
-               throw SessionException (_("Cannot configure audio/midi engine with session parameters"));
+               switch (err) {
+                       case -1:
+                               throw SessionException (string_compose (_("Cannot initialize session/engine: %1"), _("Failed to create background threads.")));
+                               break;
+                       case -2:
+                       case -3:
+                               throw SessionException (string_compose (_("Cannot initialize session/engine: %1"), _("Invalid TempoMap in session-file.")));
+                               break;
+                       case -4:
+                               throw SessionException (string_compose (_("Cannot initialize session/engine: %1"), _("Invalid or corrupt session state.")));
+                               break;
+                       case -5:
+                               throw SessionException (string_compose (_("Cannot initialize session/engine: %1"), _("Port registration failed.")));
+                               break;
+                       default:
+                               throw SessionException (string_compose (_("Cannot initialize session/engine: %1"), _("Unexpected exception during session setup, possibly invalid audio/midi engine parameters. Please see stdout/stderr for details")));
+                               break;
+               }
        }
 
        store_recent_sessions (_name, _path);
@@ -490,8 +514,16 @@ Session::Session (AudioEngine &eng,
 
        ensure_subdirs (); // archived or zipped sessions may lack peaks/ analysis/ etc
 
-       _is_new = false;
+       if (!mix_template.empty ()) {
+               /* ::create() unsets _is_new after creating the session.
+                * But for templated sessions, the sample-rate is initially unset
+                * (not read from template), so we need to save it (again).
+                */
+               _is_new = true;
+       }
+
        session_loaded ();
+       _is_new = false;
 
        BootMessage (_("Session loading complete"));
 }
@@ -617,14 +649,26 @@ Session::destroy ()
 
        _state_of_the_state = StateOfTheState (CannotSave|Deletion);
 
+       {
+               Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               ltc_tx_cleanup();
+               delete _slave;
+               _slave = 0;
+       }
+
        /* 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
-          and the engine to move data to any devices.
-       */
+        * and the engine to move data to any devices.
+        */
+
+       /* remove I/O objects before unsetting the engine session */
+       _click_io.reset ();
+       _ltc_input.reset ();
+       _ltc_output.reset ();
 
        ControlProtocolManager::instance().drop_protocols ();
 
@@ -645,8 +689,6 @@ Session::destroy ()
 
        Port::PortDrop (); /* EMIT SIGNAL */
 
-       ltc_tx_cleanup();
-
        /* clear history so that no references to objects are held any more */
 
        _history.clear ();
@@ -656,17 +698,20 @@ Session::destroy ()
        delete state_tree;
        state_tree = 0;
 
-       // unregister all lua functions, drop held references (if any)
-       (*_lua_cleanup)();
-       lua.do_command ("Session = nil");
-       delete _lua_run;
-       delete _lua_add;
-       delete _lua_del;
-       delete _lua_list;
-       delete _lua_save;
-       delete _lua_load;
-       delete _lua_cleanup;
-       lua.collect_garbage ();
+       {
+               /* unregister all lua functions, drop held references (if any) */
+               Glib::Threads::Mutex::Lock tm (lua_lock, Glib::Threads::TRY_LOCK);
+               (*_lua_cleanup)();
+               lua.do_command ("Session = nil");
+               delete _lua_run;
+               delete _lua_add;
+               delete _lua_del;
+               delete _lua_list;
+               delete _lua_save;
+               delete _lua_load;
+               delete _lua_cleanup;
+               lua.collect_garbage ();
+       }
 
        /* reset dynamic state version back to default */
        Stateful::loading_state_version = 0;
@@ -694,6 +739,7 @@ Session::destroy ()
 
        /* need to remove auditioner before monitoring section
         * otherwise it is re-connected */
+       auditioner->drop_references ();
        auditioner.reset ();
 
        /* drop references to routes held by the monitoring section
@@ -705,7 +751,7 @@ Session::destroy ()
        routes.flush ();
        _bundles.flush ();
 
-       AudioDiskstream::free_working_buffers();
+       DiskReader::free_working_buffers();
 
        /* tell everyone who is still standing that we're about to die */
        drop_references ();
@@ -818,6 +864,10 @@ Session::destroy ()
                AudioEngine::instance()->clear_pending_port_deletions ();
        }
 
+       DEBUG_TRACE (DEBUG::Destruction, "delete selection\n");
+       delete _selection;
+       _selection = 0;
+
        DEBUG_TRACE (DEBUG::Destruction, "Session::destroy() done\n");
 
        BOOST_SHOW_POINTERS ();
@@ -1450,6 +1500,33 @@ Session::reset_monitor_section ()
        }
 }
 
+int
+Session::add_master_bus (ChanCount const& count)
+{
+       if (master_out ()) {
+               return -1;
+       }
+
+       RouteList rl;
+
+       boost::shared_ptr<Route> r (new Route (*this, _("Master"), PresentationInfo::MasterOut, DataType::AUDIO));
+       if (r->init ()) {
+               return -1;
+       }
+
+       BOOST_MARK_ROUTE(r);
+
+       {
+               Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+               r->input()->ensure_io (count, false, this);
+               r->output()->ensure_io (count, false, this);
+       }
+
+       rl.push_back (r);
+       add_routes (rl, false, false, false, PresentationInfo::max_order);
+       return 0;
+}
+
 void
 Session::hookup_io ()
 {
@@ -1470,7 +1547,6 @@ Session::hookup_io ()
                        if (a->init()) {
                                throw failed_constructor ();
                        }
-                       a->use_new_diskstream ();
                        auditioner = a;
                }
 
@@ -1623,7 +1699,7 @@ Session::auto_loop_changed (Location* location)
                }
                else if (Config->get_seamless_loop() && !loop_changing) {
 
-                       // schedule a locate-roll to refill the diskstreams at the
+                       // schedule a locate-roll to refill the disk readers at the
                        // previous loop end
                        loop_changing = true;
 
@@ -2481,11 +2557,13 @@ Session::count_existing_track_channels (ChanCount& in, ChanCount& out)
        boost::shared_ptr<RouteList> r = routes.reader ();
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-                boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
-               if (tr && !tr->is_auditioner()) {
-                       in  += tr->n_inputs();
-                       out += tr->n_outputs();
+               boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
+               if (!tr) {
+                       continue;
                }
+               assert (!tr->is_auditioner()); // XXX remove me
+               in  += tr->n_inputs();
+               out += tr->n_outputs();
        }
 }
 
@@ -2547,8 +2625,6 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, bool s
                                track->set_strict_io (true);
                        }
 
-                       track->use_new_diskstream();
-
                        BOOST_MARK_TRACK (track);
 
                        {
@@ -2564,14 +2640,10 @@ Session::new_midi_track (const ChanCount& input, const ChanCount& output, bool s
                                }
                        }
 
-                       track->non_realtime_input_change();
-
                        if (route_group) {
                                route_group->add (track);
                        }
 
-                       track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this));
-
                        new_routes.push_back (track);
                        ret.push_back (track);
                }
@@ -3048,7 +3120,8 @@ Session::ensure_stripable_sort_order ()
 
        for (StripableList::iterator si = sl.begin(); si != sl.end(); ++si) {
                boost::shared_ptr<Stripable> s (*si);
-               if (s->is_monitor () || s->is_auditioner ()) {
+               assert (!s->is_auditioner ()); // XXX remove me
+               if (s->is_monitor ()) {
                        continue;
                }
                if (order != s->presentation_info().order()) {
@@ -3144,8 +3217,6 @@ Session::new_audio_track (int input_channels, int output_channels, RouteGroup* r
                                }
                        }
 
-                       track->use_new_diskstream();
-
                        BOOST_MARK_TRACK (track);
 
                        {
@@ -3172,10 +3243,6 @@ Session::new_audio_track (int input_channels, int output_channels, RouteGroup* r
                                route_group->add (track);
                        }
 
-                       track->non_realtime_input_change();
-
-                       track->DiskstreamChanged.connect_same_thread (*this, boost::bind (&Session::resort_routes, this));
-
                        new_routes.push_back (track);
                        ret.push_back (track);
                }
@@ -3540,11 +3607,7 @@ Session::add_routes_inner (RouteList& new_routes, bool input_auto_connect, bool
                }
        }
 
-       /* auditioner and monitor routes are not part of the order */
-       if (auditioner) {
-               assert (n_routes > 0);
-               --n_routes;
-       }
+       /* monitor is not part of the order */
        if (_monitor_out) {
                assert (n_routes > 0);
                --n_routes;
@@ -4222,7 +4285,8 @@ Session::muted () const
        StripableList all;
        get_stripables (all);
        for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) {
-               if ((*i)->is_auditioner() || (*i)->is_monitor()) {
+               assert (!(*i)->is_auditioner()); // XXX remove me
+               if ((*i)->is_monitor()) {
                        continue;
                }
                boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route>(*i);
@@ -4246,7 +4310,8 @@ Session::cancel_all_mute ()
        std::vector<boost::weak_ptr<AutomationControl> > muted;
        boost::shared_ptr<ControlList> cl (new ControlList);
        for (StripableList::const_iterator i = all.begin(); i != all.end(); ++i) {
-               if ((*i)->is_auditioner() || (*i)->is_monitor()) {
+               assert (!(*i)->is_auditioner());
+               if ((*i)->is_monitor()) {
                        continue;
                }
                boost::shared_ptr<Route> r = boost::dynamic_pointer_cast<Route> (*i);
@@ -4466,21 +4531,6 @@ Session::processor_by_id (PBD::ID id) const
        return boost::shared_ptr<Processor> ();
 }
 
-boost::shared_ptr<Track>
-Session::track_by_diskstream_id (PBD::ID id) const
-{
-       boost::shared_ptr<RouteList> r = routes.reader ();
-
-       for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (*i);
-               if (t && t->using_diskstream_id (id)) {
-                       return t;
-               }
-       }
-
-       return boost::shared_ptr<Track> ();
-}
-
 boost::shared_ptr<Route>
 Session::get_remote_nth_route (PresentationInfo::order_t n) const
 {
@@ -4557,10 +4607,11 @@ Session::reassign_track_numbers ()
        StateProtector sp (this);
 
        for (RouteList::iterator i = r.begin(); i != r.end(); ++i) {
+               assert (!(*i)->is_auditioner());
                if (boost::dynamic_pointer_cast<Track> (*i)) {
                        (*i)->set_track_number(++tn);
                }
-               else if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_auditioner()) {
+               else if (!(*i)->is_master() && !(*i)->is_monitor()) {
                        (*i)->set_track_number(--bn);
                }
        }
@@ -5199,15 +5250,10 @@ Session::new_audio_source_path (const string& base, uint32_t nchan, uint32_t cha
 string
 Session::new_midi_source_path (const string& base, bool need_lock)
 {
-       uint32_t cnt;
-       char buf[PATH_MAX+1];
-       const uint32_t limit = 10000;
-       string legalized;
        string possible_path;
        string possible_name;
 
-       buf[0] = '\0';
-       legalized = legalize_for_path (base);
+       possible_name = legalize_for_path (base);
 
        // Find a "version" of the file name that doesn't exist in any of the possible directories.
        std::vector<string> sdirs = source_search_path(DataType::MIDI);
@@ -5222,17 +5268,15 @@ Session::new_midi_source_path (const string& base, bool need_lock)
         */
        std::reverse(sdirs.begin(), sdirs.end());
 
-       for (cnt = 1; cnt <= limit; ++cnt) {
+       while (true) {
+               possible_name = bump_name_once (possible_name, '-');
 
                vector<space_and_path>::iterator i;
                uint32_t existing = 0;
 
                for (vector<string>::const_iterator i = sdirs.begin(); i != sdirs.end(); ++i) {
 
-                       snprintf (buf, sizeof(buf), "%s-%u.mid", legalized.c_str(), cnt);
-                       possible_name = buf;
-
-                       possible_path = Glib::build_filename (*i, possible_name);
+                       possible_path = Glib::build_filename (*i, possible_name + ".mid");
 
                        if (Glib::file_test (possible_path, Glib::FILE_TEST_EXISTS)) {
                                existing++;
@@ -5243,17 +5287,17 @@ Session::new_midi_source_path (const string& base, bool need_lock)
                        }
                }
 
-               if (existing == 0) {
-                       break;
-               }
-
-               if (cnt > limit) {
+               if (possible_path.size () >= PATH_MAX) {
                        error << string_compose(
-                                       _("There are already %1 recordings for %2, which I consider too many."),
-                                       limit, base) << endmsg;
+                                       _("There are already many recordings for %1, resulting in a too long file-path %2."),
+                                       base, possible_path) << endmsg;
                        destroy ();
                        return 0;
                }
+
+               if (existing == 0) {
+                       break;
+               }
        }
 
        /* No need to "find best location" for software/app-based RAID, because
@@ -5435,7 +5479,7 @@ Session::registered_lua_functions ()
                        if (!i.key ().isString ()) { assert(0); continue; }
                        rv.push_back (i.key ().cast<std::string> ());
                }
-       } catch (luabridge::LuaException const& e) { }
+       } catch (...) { }
        return rv;
 }
 
@@ -5451,7 +5495,7 @@ Session::try_run_lua (pframes_t nframes)
        if (_n_lua_scripts == 0) return;
        Glib::Threads::Mutex::Lock tm (lua_lock, Glib::Threads::TRY_LOCK);
        if (tm.locked ()) {
-               try { (*_lua_run)(nframes); } catch (luabridge::LuaException const& e) { }
+               try { (*_lua_run)(nframes); } catch (...) { }
                lua.collect_garbage_step ();
        }
 }
@@ -5463,6 +5507,7 @@ Session::setup_lua ()
        lua.Print.connect (&_lua_print);
 #endif
        lua.tweak_rt_gc ();
+       lua.sandbox (true);
        lua.do_command (
                        "function ArdourSession ()"
                        "  local self = { scripts = {}, instances = {} }"
@@ -5479,7 +5524,6 @@ Session::setup_lua ()
                        "   assert(type(a) == 'table' or type(a) == 'nil', 'Given argument is invalid')"
                        "   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, 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, bit32=bit32, 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
@@ -5583,7 +5627,12 @@ Session::setup_lua ()
                _lua_cleanup = new luabridge::LuaRef(lua_sess["cleanup"]);
        } catch (luabridge::LuaException const& e) {
                fatal << string_compose (_("programming error: %1"),
-                               X_("Failed to setup Lua interpreter"))
+                               std::string ("Failed to setup session Lua interpreter") + e.what ())
+                       << endmsg;
+               abort(); /*NOTREACHED*/
+       } catch (...) {
+               fatal << string_compose (_("programming error: %1"),
+                               X_("Failed to setup session Lua interpreter"))
                        << endmsg;
                abort(); /*NOTREACHED*/
        }
@@ -5609,6 +5658,11 @@ Session::scripts_changed ()
                }
                _n_lua_scripts = cnt;
        } catch (luabridge::LuaException const& e) {
+               fatal << string_compose (_("programming error: %1"),
+                               std::string ("Indexing Lua Session Scripts failed.") + e.what ())
+                       << endmsg;
+               abort(); /*NOTREACHED*/
+       } catch (...) {
                fatal << string_compose (_("programming error: %1"),
                                X_("Indexing Lua Session Scripts failed."))
                        << endmsg;
@@ -5667,12 +5721,6 @@ Session::graph_reordered ()
                return;
        }
 
-       /* every track/bus asked for this to be handled but it was deferred because
-          we were connecting. do it now.
-       */
-
-       request_input_change_handling ();
-
        resort_routes ();
 
        /* force all diskstreams to update their capture offset values to
@@ -5683,7 +5731,7 @@ Session::graph_reordered ()
        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_capture_offset ();
+                       tr->update_latency_information ();
                }
        }
 }
@@ -6375,19 +6423,15 @@ Session::nbusses () const
 }
 
 uint32_t
-Session::nstripables (bool with_auditioner_and_monitor) const
+Session::nstripables (bool with_monitor) const
 {
        uint32_t rv = routes.reader()->size ();
        rv += _vca_manager->vcas ().size ();
 
-       if (with_auditioner_and_monitor) {
+       if (with_monitor) {
                return rv;
        }
 
-       if (auditioner) {
-               assert (rv > 0);
-               --rv;
-       }
        if (_monitor_out) {
                assert (rv > 0);
                --rv;
@@ -6518,9 +6562,8 @@ Session::get_tracks () const
 
        for (RouteList::const_iterator r = rl->begin(); r != rl->end(); ++r) {
                if (boost::dynamic_pointer_cast<Track> (*r)) {
-                       if (!(*r)->is_auditioner()) {
-                               tl->push_back (*r);
-                       }
+                       assert (!(*r)->is_auditioner()); // XXX remove me
+                       tl->push_back (*r);
                }
        }
        return tl;
@@ -6808,6 +6851,9 @@ Session::update_latency (bool playback)
        if ((_state_of_the_state & (InitialConnecting|Deletion)) || _adding_routes_in_progress || _route_deletion_in_progress) {
                return;
        }
+       if (!_engine.running()) {
+               return;
+       }
 
        boost::shared_ptr<RouteList> r = routes.reader ();
        framecnt_t max_latency = 0;
@@ -6861,7 +6907,8 @@ Session::post_playback_latency ()
        boost::shared_ptr<RouteList> r = routes.reader ();
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               if (!(*i)->is_auditioner() && ((*i)->active())) {
+               assert (!(*i)->is_auditioner()); // XXX remove me
+               if ((*i)->active()) {
                        _worst_track_latency = max (_worst_track_latency, (*i)->update_signal_latency ());
                }
        }
@@ -6883,7 +6930,7 @@ Session::post_capture_latency ()
        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_capture_offset ();
+                       tr->update_latency_information ();
                }
        }
 }
@@ -6967,7 +7014,8 @@ Session::update_latency_compensation (bool force_whole_graph)
        boost::shared_ptr<RouteList> r = routes.reader ();
 
        for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
-               if (!(*i)->is_auditioner() && ((*i)->active())) {
+               assert (!(*i)->is_auditioner()); // XXX remove me
+               if ((*i)->active()) {
                        framecnt_t tl;
                        if ((*i)->signal_latency () != (tl = (*i)->update_signal_latency ())) {
                                some_track_latency_changed = true;
@@ -6991,7 +7039,7 @@ Session::update_latency_compensation (bool force_whole_graph)
                if (!tr) {
                        continue;
                }
-               tr->set_capture_offset ();
+               tr->update_latency_information ();
        }
 }
 
@@ -7036,13 +7084,14 @@ Session::operation_in_progress (GQuark op) const
 boost::shared_ptr<Port>
 Session::ltc_input_port () const
 {
+       assert (_ltc_input);
        return _ltc_input->nth (0);
 }
 
 boost::shared_ptr<Port>
 Session::ltc_output_port () const
 {
-       return _ltc_output->nth (0);
+       return _ltc_output ? _ltc_output->nth (0) : boost::shared_ptr<Port> ();
 }
 
 void
@@ -7323,8 +7372,8 @@ Session::auto_connect_thread_run ()
                        /* this is only used for updating plugin latencies, the
                         * graph does not change. so it's safe in general.
                         * BUT..
-                        * .. update_latency_compensation () entails set_capture_offset()
-                        * which calls Diskstream::set_capture_offset () which
+                        * .. update_latency_compensation () entails Track::update_latency_information()
+                        * which calls DiskWriter::set_capture_offset () which
                         * modifies the capture offset... which can be a proplem
                         * in "prepare_to_stop"
                         */