X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession.cc;h=b658487969c3827479096ab426e66c538c20892d;hb=cb0d9f5607da6465efa619cc7f5fb2152bf1b498;hp=416f972ea784c4da35434d7f800bbb9a8646e105;hpb=a1c5d1be43007265ef1257889a3138b0fa75c2ac;p=ardour.git diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 416f972ea7..b658487969 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -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 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 r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - boost::shared_ptr tr = boost::dynamic_pointer_cast (*i); - if (tr && !tr->is_auditioner()) { - in += tr->n_inputs(); - out += tr->n_outputs(); + boost::shared_ptr tr = boost::dynamic_pointer_cast (*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 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 r = boost::dynamic_pointer_cast(*i); @@ -4246,7 +4310,8 @@ Session::cancel_all_mute () std::vector > muted; boost::shared_ptr 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 r = boost::dynamic_pointer_cast (*i); @@ -4466,21 +4531,6 @@ Session::processor_by_id (PBD::ID id) const return boost::shared_ptr (); } -boost::shared_ptr -Session::track_by_diskstream_id (PBD::ID id) const -{ - boost::shared_ptr r = routes.reader (); - - for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - boost::shared_ptr t = boost::dynamic_pointer_cast (*i); - if (t && t->using_diskstream_id (id)) { - return t; - } - } - - return boost::shared_ptr (); -} - boost::shared_ptr 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 (*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 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::iterator i; uint32_t existing = 0; for (vector::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 ()); } - } 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 tr = boost::dynamic_pointer_cast (*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 (*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 r = routes.reader (); framecnt_t max_latency = 0; @@ -6861,7 +6907,8 @@ Session::post_playback_latency () boost::shared_ptr 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 tr = boost::dynamic_pointer_cast (*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 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 Session::ltc_input_port () const { + assert (_ltc_input); return _ltc_input->nth (0); } boost::shared_ptr Session::ltc_output_port () const { - return _ltc_output->nth (0); + return _ltc_output ? _ltc_output->nth (0) : boost::shared_ptr (); } 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" */