X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession_state.cc;h=19bfc4c41c44a54c6e2fd9e4f52883f202610019;hb=650953ed146a8dc3b6fbe27ccffa1027012df9f5;hp=ec00bf52725880354d6cb5d3c31ca122e5ed649a;hpb=3af9fdad0ac408a16a2f8aafedce458533338543;p=ardour.git diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index ec00bf5272..19bfc4c41c 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -50,6 +50,7 @@ #include #include "pbd/gstdio_compat.h" +#include "pbd/locale_guard.h" #include #include @@ -77,7 +78,6 @@ #include "ardour/amp.h" #include "ardour/async_midi_port.h" -#include "ardour/audio_diskstream.h" #include "ardour/audio_track.h" #include "ardour/audioengine.h" #include "ardour/audiofilesource.h" @@ -89,6 +89,7 @@ #include "ardour/controllable_descriptor.h" #include "ardour/control_protocol_manager.h" #include "ardour/directory_names.h" +#include "ardour/disk_reader.h" #include "ardour/filename_extensions.h" #include "ardour/graph.h" #include "ardour/location.h" @@ -114,12 +115,14 @@ #include "ardour/revision.h" #include "ardour/route_group.h" #include "ardour/send.h" +#include "ardour/selection.h" #include "ardour/session.h" #include "ardour/session_directory.h" #include "ardour/session_metadata.h" #include "ardour/session_playlists.h" #include "ardour/session_state_utils.h" #include "ardour/silentfilesource.h" +#include "ardour/smf_source.h" #include "ardour/sndfilesource.h" #include "ardour/source_factory.h" #include "ardour/speakers.h" @@ -262,7 +265,15 @@ Session::post_engine_init () _tempo_map = new TempoMap (_current_frame_rate); _tempo_map->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::tempo_map_changed, this, _1)); _tempo_map->MetricPositionChanged.connect_same_thread (*this, boost::bind (&Session::tempo_map_changed, this, _1)); + } catch (std::exception const & e) { + error << _("Unexpected exception during session setup: ") << e.what() << endmsg; + return -2; + } catch (...) { + error << _("Unknown exception during session setup") << endmsg; + return -3; + } + try { /* MidiClock requires a tempo map */ delete midi_clock; @@ -275,7 +286,7 @@ Session::post_engine_init () _engine.GraphReordered.connect_same_thread (*this, boost::bind (&Session::graph_reordered, this)); _engine.MidiSelectionPortsChanged.connect_same_thread (*this, boost::bind (&Session::rewire_midi_selection_ports, this)); - AudioDiskstream::allocate_working_buffers(); + DiskReader::allocate_working_buffers(); refresh_disk_space (); /* we're finally ready to call set_state() ... all objects have @@ -283,9 +294,14 @@ Session::post_engine_init () */ if (state_tree) { - if (set_state (*state_tree->root(), Stateful::loading_state_version)) { - error << _("Could not set session state from XML") << endmsg; - return -1; + try { + if (set_state (*state_tree->root(), Stateful::loading_state_version)) { + error << _("Could not set session state from XML") << endmsg; + return -4; + } + } catch (PBD::unknown_enumeration& e) { + error << _("Session state: ") << e.what() << endmsg; + return -4; } } else { // set_state() will call setup_raid_path(), but if it's a new session we need @@ -348,15 +364,14 @@ Session::post_engine_init () _locations->changed.connect_same_thread (*this, boost::bind (&Session::locations_changed, this)); } catch (AudioEngine::PortRegistrationFailure& err) { - /* handle this one in a different way than all others, so that its clear what happened */ error << err.what() << endmsg; - return -1; + return -5; } catch (std::exception const & e) { error << _("Unexpected exception during session setup: ") << e.what() << endmsg; - return -1; + return -6; } catch (...) { error << _("Unknown exception during session setup") << endmsg; - return -1; + return -7; } BootMessage (_("Reset Remote Controls")); @@ -393,7 +408,7 @@ Session::post_engine_init () boost::shared_ptr rl = routes.reader(); for (RouteList::iterator r = rl->begin(); r != rl->end(); ++r) { boost::shared_ptr trk = boost::dynamic_pointer_cast (*r); - if (trk && !trk->hidden()) { + if (trk && !trk->is_private_route()) { trk->seek (_transport_frame, true); } } @@ -647,56 +662,20 @@ Session::create (const string& session_template, BusProfile* bus_profile) /* set up Master Out and Monitor Out if necessary */ if (bus_profile) { - RouteList rl; ChanCount count(DataType::AUDIO, bus_profile->master_out_channels); + if (bus_profile->master_out_channels) { + int rv = add_master_bus (count); - // Waves Tracks: always create master bus for Tracks - if (ARDOUR::Profile->get_trx() || bus_profile->master_out_channels) { - 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); - - } else { - /* prohibit auto-connect to master, because there isn't one */ - bus_profile->output_ac = AutoConnectOption (bus_profile->output_ac & ~AutoConnectMaster); - } - - if (!rl.empty()) { - add_routes (rl, false, false, false, PresentationInfo::max_order); - } - - // Waves Tracks: Skip this. Always use autoconnection for Tracks - if (!ARDOUR::Profile->get_trx()) { - - /* this allows the user to override settings with an environment variable. - */ - - if (no_auto_connect()) { - bus_profile->input_ac = AutoConnectOption (0); - bus_profile->output_ac = AutoConnectOption (0); + if (rv) { + return rv; } - Config->set_input_auto_connect (bus_profile->input_ac); - Config->set_output_auto_connect (bus_profile->output_ac); + if (Config->get_use_monitor_bus()) + add_monitor_section (); } } - if (Config->get_use_monitor_bus() && bus_profile) { - add_monitor_section (); - } - return 0; } @@ -798,10 +777,10 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot } _save_queued = false; - if (!_engine.connected ()) { - error << string_compose (_("the %1 audio engine is not connected and state saving would lose all I/O connections. Session not saved"), PROGRAM_NAME) - << endmsg; - return 1; + snapshot_t fork_state = NormalSave; + if (!snapshot_name.empty() && snapshot_name != _current_snapshot_name && !template_only && !pending) { + /* snapshot, close midi */ + fork_state = switch_to_snapshot ? SwitchToSnapshot : SnapshotKeep; } #ifndef NDEBUG @@ -830,7 +809,7 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot mark_as_clean = false; tree.set_root (&get_template()); } else { - tree.set_root (&get_state()); + tree.set_root (&state (true, fork_state)); } if (snapshot_name.empty()) { @@ -915,8 +894,14 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot int Session::restore_state (string snapshot_name) { - if (load_state (snapshot_name) == 0) { - set_state (*state_tree->root(), Stateful::loading_state_version); + try { + if (load_state (snapshot_name) == 0) { + set_state (*state_tree->root(), Stateful::loading_state_version); + } + } catch (...) { + // SessionException + // unknown_enumeration + return -1; } return 0; @@ -980,20 +965,12 @@ Session::load_state (string snapshot_name) } std::string version; - if (root.get_property ("version", version)) { - if (version.find ('.') != string::npos) { - /* old school version format */ - if (version[0] == '2') { - Stateful::loading_state_version = 2000; - } else { - Stateful::loading_state_version = 3000; - } - } else { - Stateful::loading_state_version = string_to(version); - } - } else { - /* no version implies very old version of Ardour */ - Stateful::loading_state_version = 1000; + root.get_property ("version", version); + Stateful::loading_state_version = parse_stateful_loading_version (version); + + if ((Stateful::loading_state_version / 1000L) > (CURRENT_SESSION_FILE_VERSION / 1000L)) { + cerr << "Session-version: " << Stateful::loading_state_version << " is not supported. Current: " << CURRENT_SESSION_FILE_VERSION << "\n"; + throw SessionException (string_compose (_("Incomatible Session Version. That session was created with a newer version of %1"), PROGRAM_NAME)); } if (Stateful::loading_state_version < CURRENT_SESSION_FILE_VERSION && _writable) { @@ -1022,7 +999,6 @@ Session::load_state (string snapshot_name) int Session::load_options (const XMLNode& node) { - LocaleGuard lg; config.set_variables (node); return 0; } @@ -1138,7 +1114,7 @@ struct route_id_compare { } // anon namespace XMLNode& -Session::state (bool full_state) +Session::state (bool full_state, snapshot_t snapshot_type) { LocaleGuard lg; XMLNode* node = new XMLNode("Session"); @@ -1190,8 +1166,6 @@ Session::state (bool full_state) node->set_property ("end-is-free", _session_range_end_is_free); } - node->set_property ("end-is-free", _session_range_end_is_free); - /* save the ID counter */ node->set_property ("id-counter", ID::counter()); @@ -1239,19 +1213,80 @@ Session::state (bool full_state) * about non-destructive file sources that are empty * and unused by any regions. */ - boost::shared_ptr fs; - if ((fs = boost::dynamic_pointer_cast (siter->second)) != 0) { + if ((fs = boost::dynamic_pointer_cast (siter->second)) == 0) { + continue; + } - if (!fs->destructive()) { - if (fs->empty() && !fs->used()) { + if (!fs->destructive()) { + if (fs->empty() && !fs->used()) { + continue; + } + } + + if (snapshot_type != NormalSave && fs->within_session ()) { + /* copy MIDI sources to new file + * + * We cannot replace the midi-source and MidiRegion::clobber_sources, + * because the GUI (midi_region) has a direct pointer to the midi-model + * of the source, as does UndoTransaction. + * + * On the upside, .mid files are not kept open. The file is only open + * when reading the model initially and when flushing the model to disk: + * source->session_saved () or export. + * + * We can change the _path of the existing source under the hood, keeping + * all IDs, references and pointers intact. + * */ + boost::shared_ptr ms; + if ((ms = boost::dynamic_pointer_cast (siter->second)) != 0) { + const std::string ancestor_name = ms->ancestor_name(); + const std::string base = PBD::basename_nosuffix(ancestor_name); + const string path = new_midi_source_path (base, false); + + /* use SMF-API to clone data (use the midi_model, not data on disk) */ + boost::shared_ptr newsrc (new SMFSource (*this, path, SndFileSource::default_writable_flags)); + Source::Lock lm (ms->mutex()); + + // TODO special-case empty, removable() files: just create a new removable. + // (load + write flushes the model and creates the file) + if (!ms->model()) { + ms->load_model (lm); + } + if (ms->write_to (lm, newsrc, Evoral::MinBeats, Evoral::MaxBeats)) { + error << string_compose (_("Session-Save: Failed to copy MIDI Source '%1' for snapshot"), ancestor_name) << endmsg; + } else { + if (snapshot_type == SnapshotKeep) { + /* keep working on current session. + * + * Save snapshot-state with the original filename. + * Switch to use new path for future saves of the main session. + */ + child->add_child_nocopy (ms->get_state()); + } + + /* swap file-paths. + * ~SMFSource unlinks removable() files. + */ + std::string npath (ms->path ()); + ms->replace_file (newsrc->path ()); + newsrc->replace_file (npath); + + if (snapshot_type == SwitchToSnapshot) { + /* save and switch to snapshot. + * + * Leave the old file in place (as is). + * Snapshot uses new source directly + */ + child->add_child_nocopy (ms->get_state()); + } continue; } } - - child->add_child_nocopy (siter->second->get_state()); } + + child->add_child_nocopy (siter->second->get_state()); } } @@ -1288,6 +1323,8 @@ Session::state (bool full_state) if (full_state) { + node->add_child_nocopy (_selection->get_state()); + if (_locations) { node->add_child_nocopy (_locations->get_state()); } @@ -1573,15 +1610,6 @@ Session::set_state (const XMLNode& node, int version) } } - if (version < 3000) { - if ((child = find_named_node (node, X_("DiskStreams"))) == 0) { - error << _("Session: XML state has no diskstreams section") << endmsg; - goto out; - } else if (load_diskstreams_2X (*child, version)) { - goto out; - } - } - if ((child = find_named_node (node, VCAManager::xml_node_name)) != 0) { _vca_manager->set_state (*child, version); } @@ -1597,9 +1625,6 @@ Session::set_state (const XMLNode& node, int version) Slavable::Assign (_vca_manager); /* EMIT SIGNAL */ - /* our diskstreams list is no longer needed as they are now all owned by their Route */ - _diskstreams_2X.clear (); - if (version >= 3000) { if ((child = find_named_node (node, "RouteGroups")) == 0) { @@ -1646,11 +1671,15 @@ Session::set_state (const XMLNode& node, int version) (*_lua_load)(std::string ((const char*)buf, size)); } catch (luabridge::LuaException const& e) { cerr << "LuaException:" << e.what () << endl; - } + } catch (...) { } g_free (buf); } } + if ((child = find_named_node (node, X_("Selection")))) { + _selection->set_state (*child, version); + } + update_route_record_state (); /* here beginneth the second phase ... */ @@ -1716,14 +1745,20 @@ Session::XMLRouteFactory (const XMLNode& node, int version) return ret; } - XMLNode* ds_child = find_named_node (node, X_("Diskstream")); + XMLProperty const * pl_prop = node.property (X_("audio-playlist")); + + if (!pl_prop) { + pl_prop = node.property (X_("midi-playlist")); + } DataType type = DataType::AUDIO; node.get_property("default-type", type); assert (type != DataType::NIL); - if (ds_child) { + if (pl_prop) { + + /* has at least 1 playlist, therefore a track ... */ boost::shared_ptr track; @@ -1778,15 +1813,12 @@ Session::XMLRouteFactory_2X (const XMLNode& node, int version) if (ds_prop) { - list >::iterator i = _diskstreams_2X.begin (); - while (i != _diskstreams_2X.end() && (*i)->id() != ds_prop->value()) { - ++i; - } + /* see comment in current ::set_state() regarding diskstream + * state and DiskReader/DiskWRiter. + */ - if (i == _diskstreams_2X.end()) { - error << _("Could not find diskstream for route") << endmsg; - return boost::shared_ptr (); - } + error << _("Could not find diskstream for route") << endmsg; + return boost::shared_ptr (); boost::shared_ptr track; @@ -1804,8 +1836,6 @@ Session::XMLRouteFactory_2X (const XMLNode& node, int version) return ret; } - track->set_diskstream (*i); - BOOST_MARK_TRACK (track); ret = track; @@ -2315,7 +2345,7 @@ Session::XMLSourceFactory (const XMLNode& node) } int -Session::save_template (string template_name, bool replace_existing) +Session::save_template (const string& template_name, const string& description, bool replace_existing) { if ((_state_of_the_state & CannotSave) || template_name.empty ()) { return -1; @@ -2370,12 +2400,24 @@ Session::save_template (string template_name, bool replace_existing) SessionSaveUnderway (); /* EMIT SIGNAL */ XMLTree tree; - + XMLNode* root; { PBD::Unwinder uw (_template_state_dir, template_dir_path); - tree.set_root (&get_template()); + root = &get_template (); + } + + root->remove_nodes_and_delete (X_("description")); + + if (!description.empty()) { + XMLNode* desc = new XMLNode (X_("description")); + XMLNode* desc_cont = new XMLNode (X_("content"), description); + desc->add_child_nocopy (*desc_cont); + + root->add_child_nocopy (*desc); } + tree.set_root (root); + if (!tree.write (template_file_path)) { error << _("template not saved") << endmsg; return -1; @@ -3585,6 +3627,12 @@ Session::controllable_by_id (const PBD::ID& id) return boost::shared_ptr(); } +boost::shared_ptr +Session::automation_control_by_id (const PBD::ID& id) +{ + return boost::dynamic_pointer_cast (controllable_by_id (id)); +} + boost::shared_ptr Session::controllable_by_descriptor (const ControllableDescriptor& desc) { @@ -4121,35 +4169,6 @@ Session::set_history_depth (uint32_t d) _history.set_depth (d); } -int -Session::load_diskstreams_2X (XMLNode const & node, int) -{ - XMLNodeList clist; - XMLNodeConstIterator citer; - - clist = node.children(); - - for (citer = clist.begin(); citer != clist.end(); ++citer) { - - try { - /* diskstreams added automatically by DiskstreamCreated handler */ - if ((*citer)->name() == "AudioDiskstream" || (*citer)->name() == "DiskStream") { - boost::shared_ptr dsp (new AudioDiskstream (*this, **citer)); - _diskstreams_2X.push_back (dsp); - } else { - error << _("Session: unknown diskstream type in XML") << endmsg; - } - } - - catch (failed_constructor& err) { - error << _("Session: could not load diskstream via XML state") << endmsg; - return -1; - } - } - - return 0; -} - /** Connect things to the MMC object */ void Session::setup_midi_machine_control () @@ -4443,11 +4462,32 @@ Session::rename (const std::string& new_name) return 0; } +int +Session::parse_stateful_loading_version (const std::string& version) +{ + if (version.empty ()) { + /* no version implies very old version of Ardour */ + return 1000; + } + + if (version.find ('.') != string::npos) { + /* old school version format */ + if (version[0] == '2') { + return 2000; + } else { + return 3000; + } + } else { + return string_to(version); + } +} + int Session::get_info_from_path (const string& xmlpath, float& sample_rate, SampleFormat& data_format, std::string& program_version) { bool found_sr = false; bool found_data_format = false; + std::string version; program_version = ""; if (!Glib::file_test (xmlpath, Glib::FILE_TEST_EXISTS)) { @@ -4473,16 +4513,23 @@ Session::get_info_from_path (const string& xmlpath, float& sample_rate, SampleFo return -1; } - /* sample rate */ + /* sample rate & version*/ xmlAttrPtr attr; for (attr = node->properties; attr; attr = attr->next) { + if (!strcmp ((const char*)attr->name, "version") && attr->children) { + version = std::string ((char*)attr->children->content); + } if (!strcmp ((const char*)attr->name, "sample-rate") && attr->children) { sample_rate = atoi ((char*)attr->children->content); found_sr = true; } } + if ((parse_stateful_loading_version(version) / 1000L) > (CURRENT_SESSION_FILE_VERSION / 1000L)) { + return -1; + } + node = node->children; while (node != NULL) { if (!strcmp((const char*) node->name, "ProgramVersion")) { @@ -4506,9 +4553,11 @@ Session::get_info_from_path (const string& xmlpath, float& sample_rate, SampleFo xmlFree (pv); xmlChar* val = xmlGetProp (node, (const xmlChar*)"value"); if (val) { - SampleFormat fmt = (SampleFormat) string_2_enum (string ((const char*)val), fmt); - data_format = fmt; - found_data_format = true; + try { + SampleFormat fmt = (SampleFormat) string_2_enum (string ((const char*)val), fmt); + data_format = fmt; + found_data_format = true; + } catch (PBD::unknown_enumeration& e) {} } xmlFree (val); break; @@ -4521,7 +4570,7 @@ Session::get_info_from_path (const string& xmlpath, float& sample_rate, SampleFo xmlFreeParserCtxt(ctxt); xmlFreeDoc (doc); - return !(found_sr && found_data_format); // zero if they are both found + return (found_sr && found_data_format) ? 0 : 1; } std::string @@ -5032,6 +5081,8 @@ Session::save_as (SaveAs& saveas) session_dirs.push_back (sp); refresh_disk_space (); + _writable = exists_and_writable (_path); + /* ensure that all existing tracks reset their current capture source paths */ reset_write_sources (true, true);