X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;ds=sidebyside;f=libs%2Fardour%2Fsession_state.cc;h=c228f3c47b92ae1d27dbc9c9f4a9ee3d0705ecee;hb=44fd104ada0fbd8b76d34150e941d85d6de6f81b;hp=d670173bd50cec101e346272d78eb1bb8cac3650;hpb=2a52135c663e9a60b5f0cc9cc6673799c3ea5549;p=ardour.git diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index d670173bd5..c228f3c47b 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -47,21 +47,23 @@ #endif #include +#include #include #include -#include -#include +#include #include #include -#include +#include #include -#include #include #include #include +#include +#include +#include #include #include #include @@ -76,10 +78,10 @@ #include #include #include -#include +#include #include -#include -#include +#include +#include #include #include #include @@ -96,6 +98,9 @@ #include #include #include +#include +#include +#include #include @@ -133,10 +138,17 @@ Session::first_stage_init (string fullpath, string snapshot_name) _name = _current_snapshot_name = snapshot_name; + set_history_depth (Config->get_history_depth()); + _current_frame_rate = _engine.frame_rate (); + _nominal_frame_rate = _current_frame_rate; + _base_frame_rate = _current_frame_rate; + _tempo_map = new TempoMap (_current_frame_rate); _tempo_map->StateChanged.connect (mem_fun (*this, &Session::tempo_map_changed)); + + g_atomic_int_set (&processing_prohibited, 0); insert_cnt = 0; _transport_speed = 0; @@ -156,7 +168,8 @@ Session::first_stage_init (string fullpath, string snapshot_name) pending_locate_frame = 0; pending_locate_roll = false; pending_locate_flush = false; - dstream_buffer_size = 0; + audio_dstream_buffer_size = 0; + midi_dstream_buffer_size = 0; state_tree = 0; state_was_pending = false; set_next_event (); @@ -169,7 +182,8 @@ Session::first_stage_init (string fullpath, string snapshot_name) _worst_output_latency = 0; _worst_input_latency = 0; _worst_track_latency = 0; - _state_of_the_state = StateOfTheState(CannotSave|InitialConnecting|Loading|Deletion); + _state_of_the_state = StateOfTheState(CannotSave|InitialConnecting|Loading); + _slave = 0; butler_mixdown_buffer = 0; butler_gain_buffer = 0; @@ -226,9 +240,6 @@ Session::first_stage_init (string fullpath, string snapshot_name) waiting_for_sync_offset = false; } - _current_frame_rate = 48000; - _base_frame_rate = 48000; - last_smpte_when = 0; _smpte_offset = 0; _smpte_offset_negative = true; @@ -255,13 +266,13 @@ Session::first_stage_init (string fullpath, string snapshot_name) RegionFactory::CheckNewRegion.connect (mem_fun (*this, &Session::add_region)); SourceFactory::SourceCreated.connect (mem_fun (*this, &Session::add_source)); PlaylistFactory::PlaylistCreated.connect (mem_fun (*this, &Session::add_playlist)); - Redirect::RedirectCreated.connect (mem_fun (*this, &Session::add_redirect)); + Processor::ProcessorCreated.connect (mem_fun (*this, &Session::add_processor)); NamedSelection::NamedSelectionCreated.connect (mem_fun (*this, &Session::add_named_selection)); AutomationList::AutomationListCreated.connect (mem_fun (*this, &Session::add_automation_list)); Controllable::Destroyed.connect (mem_fun (*this, &Session::remove_controllable)); - IO::MoreChannels.connect (mem_fun (*this, &Session::ensure_buffers)); + IO::PortCountChanged.connect (mem_fun (*this, &Session::ensure_buffers)); /* stop IO objects from doing stuff until we're ready for them */ @@ -273,7 +284,7 @@ Session::first_stage_init (string fullpath, string snapshot_name) int Session::second_stage_init (bool new_session) { - AudioFileSource::set_peak_dir (peak_dir()); + AudioFileSource::set_peak_dir (_session_dir->peak_path().to_string()); if (!new_session) { if (load_state (_current_snapshot_name)) { @@ -286,12 +297,13 @@ Session::second_stage_init (bool new_session) return -1; } - /*if (start_midi_thread ()) { + if (start_midi_thread ()) { return -1; - }*/ + } // set_state() will call setup_raid_path(), but if it's a new session we need // to call setup_raid_path() here. + if (state_tree) { if (set_state (*state_tree->root())) { return -1; @@ -312,7 +324,7 @@ Session::second_stage_init (bool new_session) _state_of_the_state = StateOfTheState (_state_of_the_state|CannotSave|Loading); - // set_auto_input (true); + _locations.changed.connect (mem_fun (this, &Session::locations_changed)); _locations.added.connect (mem_fun (this, &Session::locations_added)); setup_click_sounds (0); @@ -339,11 +351,15 @@ Session::second_stage_init (bool new_session) return -1; } - //send_full_time_code (); + BootMessage (_("Reset Remote Controls")); + + send_full_time_code (0); _engine.transport_locate (0); deliver_mmc (MIDI::MachineControl::cmdMmcReset, 0); deliver_mmc (MIDI::MachineControl::cmdLocate, 0); + BootMessage (_("Reset Control Protocols")); + ControlProtocolManager::instance().set_session (*this); if (new_session) { @@ -351,173 +367,150 @@ Session::second_stage_init (bool new_session) } else { _end_location_is_free = false; } + + _state_of_the_state = Clean; + + DirtyChanged (); /* EMIT SIGNAL */ + + if (state_was_pending) { + save_state (_current_snapshot_name); + remove_pending_capture_state (); + state_was_pending = false; + } + BootMessage (_("Session loading complete")); + return 0; } string Session::raid_path () const { - string path; + SearchPath raid_search_path; for (vector::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) { - path += (*i).path; - path += ':'; + raid_search_path += sys::path((*i).path); } - return path.substr (0, path.length() - 1); // drop final colon + return raid_search_path.to_string (); } void Session::setup_raid_path (string path) { - string::size_type colon; - string remaining; - space_and_path sp; - string fspath; - string::size_type len = path.length(); - int colons; - - colons = 0; - - if (path.length() == 0) { + if (path.empty()) { return; } + + space_and_path sp; + string fspath; session_dirs.clear (); - for (string::size_type n = 0; n < len; ++n) { - if (path[n] == ':') { - colons++; - } - } - - if (colons == 0) { - - /* no multiple search path, just one location (common case) */ - - sp.path = path; - sp.blocks = 0; - session_dirs.push_back (sp); - - string fspath; - - /* sounds dir */ - - fspath += sp.path; - if (fspath[fspath.length()-1] != '/') { - fspath += '/'; - } - - fspath += sound_dir (false); - - AudioFileSource::set_search_path (fspath); - SMFSource::set_search_path (fspath); // FIXME: should be different + SearchPath search_path(path); + SearchPath sound_search_path; + SearchPath midi_search_path; - return; - } - - remaining = path; - - while ((colon = remaining.find_first_of (':')) != string::npos) { - - sp.blocks = 0; - sp.path = remaining.substr (0, colon); + for ( + SearchPath::const_iterator i = search_path.begin(); + i != search_path.end(); + ++i + ) + { + sp.path = (*i).to_string (); + sp.blocks = 0; // not needed session_dirs.push_back (sp); - /* add sounds to file search path */ - - fspath += sp.path; - if (fspath[fspath.length()-1] != '/') { - fspath += '/'; - } - fspath += sound_dir (false); - fspath += ':'; + SessionDirectory sdir(sp.path); - remaining = remaining.substr (colon+1); + sound_search_path += sdir.sound_path (); + midi_search_path += sdir.midi_path (); } - if (remaining.length()) { - - sp.blocks = 0; - sp.path = remaining; - - fspath += ':'; - fspath += sp.path; - if (fspath[fspath.length()-1] != '/') { - fspath += '/'; - } - fspath += sound_dir (false); - fspath += ':'; - - session_dirs.push_back (sp); - } + // set the AudioFileSource and SMFSource search path - /* set the AudioFileSource search path */ - - AudioFileSource::set_search_path (fspath); - SMFSource::set_search_path (fspath); // FIXME: should be different - - /* reset the round-robin soundfile path thingie */ + AudioFileSource::set_search_path (sound_search_path.to_string ()); + SMFSource::set_search_path (midi_search_path.to_string ()); + // reset the round-robin soundfile path thingie + last_rr_session_dir = session_dirs.begin(); } int -Session::create (bool& new_session, string* mix_template, nframes_t initial_length) +Session::ensure_subdirs () { string dir; - if (g_mkdir_with_parents (_path.c_str(), 0755) < 0) { - error << string_compose(_("Session: cannot create session dir \"%1\" (%2)"), _path, strerror (errno)) << endmsg; + dir = session_directory().peak_path().to_string(); + + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session peakfile folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg; return -1; } - dir = peak_dir (); + dir = session_directory().sound_path().to_string(); if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { - error << string_compose(_("Session: cannot create session peakfile dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + error << string_compose(_("Session: cannot create session sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; return -1; } + + dir = session_directory().midi_path().to_string(); - /* if this is is an existing session with an old "sounds" directory, just use it. see Session::sound_dir() for more details */ + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session midi dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + return -1; + } - if (!Glib::file_test (old_sound_dir(), Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_DIR)) { + dir = session_directory().dead_sound_path().to_string(); - dir = sound_dir (); - - if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { - error << string_compose(_("Session: cannot create session sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; - return -1; - } + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session dead sounds folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + return -1; } - dir = dead_sound_dir (); + dir = session_directory().export_path().to_string(); if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { - error << string_compose(_("Session: cannot create session dead sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + error << string_compose(_("Session: cannot create session export folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg; return -1; } - dir = export_dir (); + dir = analysis_dir (); if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { - error << string_compose(_("Session: cannot create session export dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + error << string_compose(_("Session: cannot create session analysis folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg; return -1; } + return 0; +} + +int +Session::create (bool& new_session, const string& mix_template, nframes_t initial_length) +{ + + if (g_mkdir_with_parents (_path.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session folder \"%1\" (%2)"), _path, strerror (errno)) << endmsg; + return -1; + } + + if (ensure_subdirs ()) { + return -1; + } /* check new_session so we don't overwrite an existing one */ - if (mix_template) { - std::string in_path = *mix_template; + if (!mix_template.empty()) { + std::string in_path = mix_template; ifstream in(in_path.c_str()); if (in){ string out_path = _path; out_path += _name; - out_path += _statefile_suffix; + out_path += statefile_suffix; ofstream out(out_path.c_str()); @@ -554,13 +547,12 @@ Session::create (bool& new_session, string* mix_template, nframes_t initial_leng _state_of_the_state = Clean; - if (save_state (_current_snapshot_name)) { - return -1; - } + save_state (""); return 0; } + int Session::load_diskstreams (const XMLNode& node) { @@ -604,13 +596,19 @@ Session::maybe_write_autosave() void Session::remove_pending_capture_state () { - string xml_path; - - xml_path = _path; - xml_path += _current_snapshot_name; - xml_path += _pending_suffix; + sys::path pending_state_file_path(_session_dir->root_path()); + + pending_state_file_path /= _current_snapshot_name + pending_suffix; - unlink (xml_path.c_str()); + try + { + sys::remove (pending_state_file_path); + } + catch(sys::filesystem_error& ex) + { + error << string_compose(_("Could remove pending capture state at path \"%1\" (%2)"), + pending_state_file_path.to_string(), ex.what()) << endmsg; + } } /** Rename a state file. @@ -623,12 +621,21 @@ Session::rename_state (string old_name, string new_name) /* refuse to rename the current snapshot or the "main" one */ return; } - - const string old_xml_path = _path + old_name + _statefile_suffix; - const string new_xml_path = _path + new_name + _statefile_suffix; - if (rename (old_xml_path.c_str(), new_xml_path.c_str()) != 0) { - error << string_compose(_("could not rename snapshot %1 to %2"), old_name, new_name) << endmsg; + const string old_xml_filename = old_name + statefile_suffix; + const string new_xml_filename = new_name + statefile_suffix; + + const sys::path old_xml_path = _session_dir->root_path() / old_xml_filename; + const sys::path new_xml_path = _session_dir->root_path() / new_xml_filename; + + try + { + sys::rename (old_xml_path, new_xml_path); + } + catch (const sys::filesystem_error& err) + { + error << string_compose(_("could not rename snapshot %1 to %2 (%3)"), + old_name, new_name, err.what()) << endmsg; } } @@ -639,28 +646,29 @@ void Session::remove_state (string snapshot_name) { if (snapshot_name == _current_snapshot_name || snapshot_name == _name) { - /* refuse to remove the current snapshot or the "main" one */ + // refuse to remove the current snapshot or the "main" one return; } - - const string xml_path = _path + snapshot_name + _statefile_suffix; - /* make a backup copy of the state file */ - const string bak_path = xml_path + ".bak"; - if (g_file_test (xml_path.c_str(), G_FILE_TEST_EXISTS)) { - copy_file (xml_path, bak_path); + sys::path xml_path(_session_dir->root_path()); + + xml_path /= snapshot_name + statefile_suffix; + + if (!create_backup_file (xml_path)) { + // don't remove it if a backup can't be made + // create_backup_file will log the error. + return; } - /* and delete it */ - unlink (xml_path.c_str()); + // and delete it + sys::remove (xml_path); } int Session::save_state (string snapshot_name, bool pending) { XMLTree tree; - string xml_path; - string bak_path; + sys::path xml_path(_session_dir->root_path()); if (_state_of_the_state & CannotSave) { return 1; @@ -672,6 +680,11 @@ Session::save_state (string snapshot_name, bool pending) return 1; } + /* tell sources we're saving first, in case they write out to a new file + * which should be saved with the state rather than the old one */ + for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) + i->second->session_saved(); + tree.set_root (&get_state()); if (snapshot_name.empty()) { @@ -680,53 +693,47 @@ Session::save_state (string snapshot_name, bool pending) if (!pending) { - /* proper save: use _statefile_suffix (.ardour in English) */ - xml_path = _path; - xml_path += snapshot_name; - xml_path += _statefile_suffix; + /* proper save: use statefile_suffix (.ardour in English) */ + + xml_path /= snapshot_name + statefile_suffix; /* make a backup copy of the old file */ - bak_path = xml_path; - bak_path += ".bak"; - - if (g_file_test (xml_path.c_str(), G_FILE_TEST_EXISTS)) { - copy_file (xml_path, bak_path); + + if (sys::exists(xml_path) && !create_backup_file (xml_path)) { + // create_backup_file will log the error + return -1; } } else { - /* pending save: use _pending_suffix (.pending in English) */ - xml_path = _path; - xml_path += snapshot_name; - xml_path += _pending_suffix; - + /* pending save: use pending_suffix (.pending in English) */ + xml_path /= snapshot_name + pending_suffix; } - string tmp_path; + sys::path tmp_path(_session_dir->root_path()); - tmp_path = _path; - tmp_path += snapshot_name; - tmp_path += ".tmp"; + tmp_path /= snapshot_name + temp_suffix; - // cerr << "actually writing state to " << xml_path << endl; + // cerr << "actually writing state to " << xml_path.to_string() << endl; - if (!tree.write (tmp_path)) { - error << string_compose (_("state could not be saved to %1"), tmp_path) << endmsg; - unlink (tmp_path.c_str()); + if (!tree.write (tmp_path.to_string())) { + error << string_compose (_("state could not be saved to %1"), tmp_path.to_string()) << endmsg; + sys::remove (tmp_path); return -1; } else { - if (rename (tmp_path.c_str(), xml_path.c_str()) != 0) { - error << string_compose (_("could not rename temporary session file %1 to %2"), tmp_path, xml_path) << endmsg; - unlink (tmp_path.c_str()); + if (rename (tmp_path.to_string().c_str(), xml_path.to_string().c_str()) != 0) { + error << string_compose (_("could not rename temporary session file %1 to %2"), + tmp_path.to_string(), xml_path.to_string()) << endmsg; + sys::remove (tmp_path); return -1; } } if (!pending) { - save_history (snapshot_name); + save_history (snapshot_name); bool was_dirty = dirty(); @@ -735,7 +742,7 @@ Session::save_state (string snapshot_name, bool pending) if (was_dirty) { DirtyChanged (); /* EMIT SIGNAL */ } - + StateSaved (snapshot_name); /* EMIT SIGNAL */ } @@ -760,17 +767,14 @@ Session::load_state (string snapshot_name) state_tree = 0; } - string xmlpath; - state_was_pending = false; /* check for leftover pending state from a crashed capture attempt */ - xmlpath = _path; - xmlpath += snapshot_name; - xmlpath += _pending_suffix; + sys::path xmlpath(_session_dir->root_path()); + xmlpath /= snapshot_name + pending_suffix; - if (Glib::file_test (xmlpath, Glib::FILE_TEST_EXISTS)) { + if (sys::exists (xmlpath)) { /* there is pending state from a crashed capture attempt */ @@ -780,14 +784,12 @@ Session::load_state (string snapshot_name) } if (!state_was_pending) { - - xmlpath = _path; - xmlpath += snapshot_name; - xmlpath += _statefile_suffix; + xmlpath = _session_dir->root_path(); + xmlpath /= snapshot_name + statefile_suffix; } - if (!Glib::file_test (xmlpath, Glib::FILE_TEST_EXISTS)) { - error << string_compose(_("%1: session state information file \"%2\" doesn't exist!"), _name, xmlpath) << endmsg; + if (!sys::exists (xmlpath)) { + error << string_compose(_("%1: session state information file \"%2\" doesn't exist!"), _name, xmlpath.to_string()) << endmsg; return 1; } @@ -795,8 +797,8 @@ Session::load_state (string snapshot_name) set_dirty(); - if (!state_tree->read (xmlpath)) { - error << string_compose(_("Could not understand ardour file %1"), xmlpath) << endmsg; + if (!state_tree->read (xmlpath.to_string())) { + error << string_compose(_("Could not understand ardour file %1"), xmlpath.to_string()) << endmsg; delete state_tree; state_tree = 0; return -1; @@ -805,7 +807,7 @@ Session::load_state (string snapshot_name) XMLNode& root (*state_tree->root()); if (root.name() != X_("Session")) { - error << string_compose (_("Session file %1 is not an Ardour session"), xmlpath) << endmsg; + error << string_compose (_("Session file %1 is not an Ardour session"), xmlpath.to_string()) << endmsg; delete state_tree; state_tree = 0; return -1; @@ -819,27 +821,38 @@ Session::load_state (string snapshot_name) is_old = true; } else { int major_version; - major_version = atoi (prop->value()); // grab just the first number before the period + major_version = atoi (prop->value().c_str()); // grab just the first number before the period if (major_version < 2) { is_old = true; } } if (is_old) { - string backup_path; - backup_path = _path; - backup_path += snapshot_name; - backup_path += "-1"; - backup_path += _statefile_suffix; + sys::path backup_path(_session_dir->root_path()); + + backup_path /= snapshot_name + "-1" + statefile_suffix; + + // only create a backup once + if (sys::exists (backup_path)) { + return 0; + } info << string_compose (_("Copying old session file %1 to %2\nUse %2 with Ardour versions before 2.0 from now on"), - xmlpath, backup_path) + xmlpath.to_string(), backup_path.to_string()) << endmsg; - copy_file (xmlpath, backup_path); - - /* if it fails, don't worry. right? */ + try + { + sys::copy_file (xmlpath, backup_path); + } + catch(sys::filesystem_error& ex) + { + error << string_compose (_("Unable to make backup of state file %1 (%2)"), + xmlpath.to_string(), ex.what()) + << endmsg; + return -1; + } } return 0; @@ -913,16 +926,16 @@ Session::state(bool full_state) // store libardour version, just in case char buf[16]; - snprintf(buf, sizeof(buf)-1, "%d.%d.%d", - libardour_major_version, libardour_minor_version, libardour_micro_version); + snprintf(buf, sizeof(buf), "%d.%d.%d", libardour3_major_version, libardour3_minor_version, libardour3_micro_version); node->add_property("version", string(buf)); /* store configuration settings */ if (full_state) { - /* store the name */ node->add_property ("name", _name); + snprintf (buf, sizeof (buf), "%" PRId32, _nominal_frame_rate); + node->add_property ("sample-rate", buf); if (session_dirs.size() > 1) { @@ -1032,12 +1045,13 @@ Session::state(bool full_state) node->add_child_nocopy (loc.get_state()); } - child = node->add_child ("Connections"); + child = node->add_child ("Bundles"); { - Glib::Mutex::Lock lm (connection_lock); - for (ConnectionList::iterator i = _connections.begin(); i != _connections.end(); ++i) { - if (!(*i)->system_dependent()) { - child->add_child_nocopy ((*i)->get_state()); + Glib::Mutex::Lock lm (bundle_lock); + for (BundleList::iterator i = _bundles.begin(); i != _bundles.end(); ++i) { + boost::shared_ptr b = boost::dynamic_pointer_cast (*i); + if (b) { + child->add_child_nocopy (b->get_state()); } } } @@ -1051,7 +1065,7 @@ Session::state(bool full_state) public_order.sort (cmp); for (RouteList::iterator i = public_order.begin(); i != public_order.end(); ++i) { - if (!(*i)->hidden()) { + if (!(*i)->is_hidden()) { if (full_state) { child->add_child_nocopy ((*i)->get_state()); } else { @@ -1150,7 +1164,18 @@ Session::set_state (const XMLNode& node) _name = prop->value (); } - setup_raid_path(_path); + if ((prop = node.property (X_("sample-rate"))) != 0) { + + _nominal_frame_rate = atoi (prop->value()); + + if (_nominal_frame_rate != _current_frame_rate) { + if (AskAboutSampleRateMismatch (_nominal_frame_rate, _current_frame_rate)) { + return -1; + } + } + } + + setup_raid_path(_session_dir->root_path().to_string()); if ((prop = node.property (X_("id-counter"))) != 0) { uint64_t x; @@ -1172,7 +1197,7 @@ Session::set_state (const XMLNode& node) /* Object loading order: - MIDI + MIDI Control Path extra Options/Config @@ -1276,13 +1301,16 @@ Session::set_state (const XMLNode& node) goto out; } - if ((child = find_named_node (node, "Connections")) == 0) { - error << _("Session: XML state has no connections section") << endmsg; - goto out; - } else if (load_connections (*child)) { - goto out; + if ((child = find_named_node (node, "Bundles")) == 0) { + warning << _("Session: XML state has no bundles section (2.0 session?)") << endmsg; + //goto out; + } else { + /* We can't load Bundles yet as they need to be able + to convert from port names to Port objects, which can't happen until + later */ + _bundle_xml_node = new XMLNode (*child); } - + if ((child = find_named_node (node, "EditGroups")) == 0) { error << _("Session: XML state has no edit groups section") << endmsg; goto out; @@ -1316,7 +1344,7 @@ Session::set_state (const XMLNode& node) } else if (_click_io) { _click_io->set_state (*child); } - + if ((child = find_named_node (node, "ControlProtocols")) != 0) { ControlProtocolManager::instance().set_protocol_states (*child); } @@ -1325,14 +1353,6 @@ Session::set_state (const XMLNode& node) StateReady (); /* EMIT SIGNAL */ - _state_of_the_state = Clean; - - if (state_was_pending) { - save_state (_current_snapshot_name); - remove_pending_capture_state (); - state_was_pending = false; - } - return 0; out: @@ -1359,10 +1379,12 @@ Session::load_routes (const XMLNode& node) return -1; } + BootMessage (string_compose (_("Loaded track/bus %1"), route->name())); + new_routes.push_back (route); } - add_routes (new_routes); + add_routes (new_routes, false); return 0; } @@ -1410,7 +1432,14 @@ Session::load_regions (const XMLNode& node) for (niter = nlist.begin(); niter != nlist.end(); ++niter) { if ((region = XMLRegionFactory (**niter, false)) == 0) { - error << _("Session: cannot create Region from XML description.") << endmsg; + error << _("Session: cannot create Region from XML description."); + const XMLProperty *name = (**niter).property("name"); + + if (name) { + error << " " << string_compose (_("Can not load state for region '%1'"), name->value()); + } + + error << endmsg; } } @@ -1448,6 +1477,7 @@ Session::XMLAudioRegionFactory (const XMLNode& node, bool full) boost::shared_ptr source; boost::shared_ptr as; SourceList sources; + SourceList master_sources; uint32_t nchans = 1; char buf[128]; @@ -1459,7 +1489,6 @@ Session::XMLAudioRegionFactory (const XMLNode& node, bool full) nchans = atoi (prop->value().c_str()); } - if ((prop = node.property ("name")) == 0) { cerr << "no name for this region\n"; abort (); @@ -1508,7 +1537,27 @@ Session::XMLAudioRegionFactory (const XMLNode& node, bool full) sources.push_back (as); } } - + + for (uint32_t n=1; n < nchans; ++n) { + snprintf (buf, sizeof(buf), X_("master-source-%d"), n); + if ((prop = node.property (buf)) != 0) { + + PBD::ID id2 (prop->value()); + + if ((source = source_by_id (id2)) == 0) { + error << string_compose(_("Session: XMLNode describing a AudioRegion references an unknown source id =%1"), id2) << endmsg; + return boost::shared_ptr(); + } + + as = boost::dynamic_pointer_cast(source); + if (!as) { + error << string_compose(_("Session: XMLNode describing a AudioRegion references a non-audio source id =%1"), id2) << endmsg; + return boost::shared_ptr(); + } + master_sources.push_back (as); + } + } + try { boost::shared_ptr region (boost::dynamic_pointer_cast (RegionFactory::create (sources, node))); @@ -1523,6 +1572,13 @@ Session::XMLAudioRegionFactory (const XMLNode& node, bool full) } } + if (!master_sources.empty()) { + if (master_sources.size() == nchans) { + error << _("Session: XMLNode describing an AudioRegion is missing some master sources; ignored") << endmsg; + } else { + region->set_master_sources (master_sources); + } + } return region; @@ -1539,7 +1595,7 @@ Session::XMLMidiRegionFactory (const XMLNode& node, bool full) const XMLProperty* prop; boost::shared_ptr source; boost::shared_ptr ms; - MidiRegion::SourceList sources; + SourceList sources; uint32_t nchans = 1; if (node.name() != X_("Region")) { @@ -1549,6 +1605,11 @@ Session::XMLMidiRegionFactory (const XMLNode& node, bool full) if ((prop = node.property (X_("channels"))) != 0) { nchans = atoi (prop->value().c_str()); } + + if ((prop = node.property ("name")) == 0) { + cerr << "no name for this region\n"; + abort (); + } // Multiple midi channels? that's just crazy talk assert(nchans == 1); @@ -1577,6 +1638,17 @@ Session::XMLMidiRegionFactory (const XMLNode& node, bool full) try { boost::shared_ptr region (boost::dynamic_pointer_cast (RegionFactory::create (sources, node))); + /* a final detail: this is the one and only place that we know how long missing files are */ + + if (region->whole_file()) { + for (SourceList::iterator sx = sources.begin(); sx != sources.end(); ++sx) { + boost::shared_ptr sfp = boost::dynamic_pointer_cast (*sx); + if (sfp) { + sfp->set_length (region->length()); + } + } + } + return region; } @@ -1596,28 +1668,33 @@ Session::get_sources_as_xml () node->add_child_nocopy (i->second->get_state()); } - /* XXX get MIDI and other sources here */ - return *node; } string -Session::path_from_region_name (string name, string identifier) +Session::path_from_region_name (DataType type, string name, string identifier) { char buf[PATH_MAX+1]; uint32_t n; - string dir = discover_best_sound_dir (); + SessionDirectory sdir(get_best_session_directory_for_new_source()); + sys::path source_dir = ((type == DataType::AUDIO) + ? sdir.sound_path() : sdir.midi_path()); + + string ext = ((type == DataType::AUDIO) ? ".wav" : ".mid"); for (n = 0; n < 999999; ++n) { if (identifier.length()) { - snprintf (buf, sizeof(buf), "%s/%s%s%" PRIu32 ".wav", dir.c_str(), name.c_str(), - identifier.c_str(), n); + snprintf (buf, sizeof(buf), "%s%s%" PRIu32 "%s", name.c_str(), + identifier.c_str(), n, ext.c_str()); } else { - snprintf (buf, sizeof(buf), "%s/%s-%" PRIu32 ".wav", dir.c_str(), name.c_str(), n); + snprintf (buf, sizeof(buf), "%s-%" PRIu32 "%s", name.c_str(), + n, ext.c_str()); } - if (!Glib::file_test (buf, Glib::FILE_TEST_EXISTS)) { - return buf; + sys::path source_path = source_dir / buf; + + if (!sys::exists (source_path)) { + return source_path.to_string(); } } @@ -1665,7 +1742,8 @@ Session::XMLSourceFactory (const XMLNode& node) } try { - return SourceFactory::create (*this, node); + /* note: do peak building in another thread when loading session state */ + return SourceFactory::create (*this, node, true); } catch (failed_constructor& err) { @@ -1678,40 +1756,37 @@ int Session::save_template (string template_name) { XMLTree tree; - string xml_path, bak_path, template_path; if (_state_of_the_state & CannotSave) { return -1; } - DIR* dp; - string dir = template_dir(); + sys::path user_template_dir(user_template_directory()); - if ((dp = opendir (dir.c_str()))) { - closedir (dp); - } else { - if (g_mkdir_with_parents (dir.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)) { - error << string_compose(_("Could not create mix templates directory \"%1\" (%2)"), dir, strerror (errno)) << endmsg; - return -1; - } + try + { + sys::create_directories (user_template_dir); + } + catch(sys::filesystem_error& ex) + { + error << string_compose(_("Could not create mix templates directory \"%1\" (%2)"), + user_template_dir.to_string(), ex.what()) << endmsg; + return -1; } tree.set_root (&get_template()); - xml_path = dir; - xml_path += template_name; - xml_path += _template_suffix; + sys::path template_file_path(user_template_dir); + template_file_path /= template_name + template_suffix; - ifstream in(xml_path.c_str()); - - if (in) { - warning << string_compose(_("Template \"%1\" already exists - new version not created"), template_name) << endmsg; + if (sys::exists (template_file_path)) + { + warning << string_compose(_("Template \"%1\" already exists - new version not created"), + template_file_path.to_string()) << endmsg; return -1; - } else { - in.close(); } - if (!tree.write (xml_path)) { + if (!tree.write (template_file_path.to_string())) { error << _("mix template not saved") << endmsg; return -1; } @@ -1719,25 +1794,6 @@ Session::save_template (string template_name) return 0; } -int -Session::rename_template (string old_name, string new_name) -{ - string old_path = template_dir() + old_name + _template_suffix; - string new_path = template_dir() + new_name + _template_suffix; - - return rename (old_path.c_str(), new_path.c_str()); -} - -int -Session::delete_template (string name) -{ - string template_path = template_dir(); - template_path += name; - template_path += _template_suffix; - - return remove (template_path.c_str()); -} - void Session::refresh_disk_space () { @@ -1762,64 +1818,16 @@ Session::refresh_disk_space () #endif } -int -Session::ensure_sound_dir (string path, string& result) -{ - string dead; - string peak; - - /* Ensure that the parent directory exists */ - - if (g_mkdir_with_parents (path.c_str(), 0775)) { - error << string_compose(_("cannot create session directory \"%1\"; ignored"), path) << endmsg; - return -1; - } - - /* Ensure that the sounds directory exists */ - - result = path; - result += '/'; - result += sound_dir_name; - - if (g_mkdir_with_parents (result.c_str(), 0775)) { - error << string_compose(_("cannot create sounds directory \"%1\"; ignored"), result) << endmsg; - return -1; - } - - dead = path; - dead += '/'; - dead += dead_sound_dir_name; - - if (g_mkdir_with_parents (dead.c_str(), 0775)) { - error << string_compose(_("cannot create dead sounds directory \"%1\"; ignored"), dead) << endmsg; - return -1; - } - - peak = path; - peak += '/'; - peak += peak_dir_name; - - if (g_mkdir_with_parents (peak.c_str(), 0775)) { - error << string_compose(_("cannot create peak file directory \"%1\"; ignored"), peak) << endmsg; - return -1; - } - - /* callers expect this to be terminated ... */ - - result += '/'; - return 0; -} - string -Session::discover_best_sound_dir (bool destructive) +Session::get_best_session_directory_for_new_source () { vector::iterator i; - string result; + string result = _session_dir->root_path().to_string(); /* handle common case without system calls */ if (session_dirs.size() == 1) { - return sound_dir(); + return result; } /* OK, here's the algorithm we're following here: @@ -1861,9 +1869,6 @@ Session::discover_best_sound_dir (bool destructive) } if (free_enough >= 2) { - - bool found_it = false; - /* use RR selection process, ensuring that the one picked works OK. */ @@ -1876,19 +1881,15 @@ Session::discover_best_sound_dir (bool destructive) } if ((*i).blocks * 4096 >= Config->get_disk_choice_space_threshold()) { - if (ensure_sound_dir ((*i).path, result) == 0) { + if (create_session_directory ((*i).path)) { + result = (*i).path; last_rr_session_dir = i; - found_it = true; - break; + return result; } } } while (i != last_rr_session_dir); - if (!found_it) { - result = sound_dir(); - } - } else { /* pick FS with the most freespace (and that @@ -1902,17 +1903,12 @@ Session::discover_best_sound_dir (bool destructive) sort (sorted.begin(), sorted.end(), cmp); for (i = sorted.begin(); i != sorted.end(); ++i) { - if (ensure_sound_dir ((*i).path, result) == 0) { + if (create_session_directory ((*i).path)) { + result = (*i).path; last_rr_session_dir = i; - break; + return result; } } - - /* if the above fails, fall back to the most simplistic solution */ - - if (i == sorted.end()) { - return sound_dir(); - } } return result; @@ -2010,91 +2006,6 @@ Session::XMLNamedSelectionFactory (const XMLNode& node) } } -string -Session::dead_sound_dir () const -{ - string res = _path; - res += dead_sound_dir_name; - - return res; -} - -string -Session::old_sound_dir (bool with_path) const -{ - string res; - - if (with_path) { - res = _path; - } - - res += old_sound_dir_name; - - return res; -} - -string -Session::sound_dir (bool with_path) const -{ - string res; - string full; - - if (with_path) { - res = _path; - } else { - full = _path; - } - - res += interchange_dir_name; - res += '/'; - res += legalize_for_path (_name); - res += '/'; - res += sound_dir_name; - - if (with_path) { - full = res; - } else { - full += res; - } - - /* if this already exists, don't check for the old session sound directory */ - - if (Glib::file_test (full, Glib::FILE_TEST_IS_DIR|Glib::FILE_TEST_EXISTS)) { - return res; - } - - /* possibly support old session structure */ - - string old_nopath; - string old_withpath; - - old_nopath += old_sound_dir_name; - old_nopath += '/'; - - old_withpath = _path; - old_withpath += old_sound_dir_name; - - if (Glib::file_test (old_withpath.c_str(), Glib::FILE_TEST_IS_DIR|Glib::FILE_TEST_EXISTS)) { - if (with_path) - return old_withpath; - - return old_nopath; - } - - /* ok, old "sounds" directory isn't there, return the new path */ - - return res; -} - -string -Session::peak_dir () const -{ - string res = _path; - res += peak_dir_name; - res += '/'; - return res; -} - string Session::automation_dir () const { @@ -2104,87 +2015,31 @@ Session::automation_dir () const } string -Session::template_dir () -{ - string path = get_user_ardour_path(); - path += "templates/"; - - return path; -} - -string -Session::export_dir () const +Session::analysis_dir () const { string res = _path; - res += export_dir_name; - res += '/'; + res += "analysis/"; return res; } -string -Session::suffixed_search_path (string suffix, bool data) -{ - string path; - - path += get_user_ardour_path(); - if (path[path.length()-1] != ':') { - path += ':'; - } - - if (data) { - path += get_system_data_path(); - } else { - path += get_system_module_path(); - } - - vector split_path; - - split (path, split_path, ':'); - path = ""; - - for (vector::iterator i = split_path.begin(); i != split_path.end(); ++i) { - path += *i; - path += suffix; - path += '/'; - - if (distance (i, split_path.end()) != 1) { - path += ':'; - } - } - - return path; -} - -string -Session::template_path () -{ - return suffixed_search_path (X_("templates"), true); -} - -string -Session::control_protocol_path () -{ - return suffixed_search_path (X_("surfaces"), false); -} - int -Session::load_connections (const XMLNode& node) +Session::load_bundles (XMLNode const & node) { - XMLNodeList nlist = node.children(); - XMLNodeConstIterator niter; + XMLNodeList nlist = node.children(); + XMLNodeConstIterator niter; - set_dirty(); + set_dirty(); - for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - if ((*niter)->name() == "InputConnection") { - add_connection (new ARDOUR::InputConnection (**niter)); - } else if ((*niter)->name() == "OutputConnection") { - add_connection (new ARDOUR::OutputConnection (**niter)); - } else { - error << string_compose(_("Unknown node \"%1\" found in Connections list from state file"), (*niter)->name()) << endmsg; - return -1; - } - } + for (niter = nlist.begin(); niter != nlist.end(); ++niter) { + if ((*niter)->name() == "InputBundle") { + add_bundle (boost::shared_ptr (new UserBundle (**niter, true))); + } else if ((*niter)->name() == "OutputBundle") { + add_bundle (boost::shared_ptr (new UserBundle (**niter, false))); + } else { + error << string_compose(_("Unknown node \"%1\" found in Bundles list from state file"), (*niter)->name()) << endmsg; + return -1; + } + } return 0; } @@ -2225,11 +2080,17 @@ Session::load_route_groups (const XMLNode& node, bool edit) return 0; } +void +Session::auto_save() +{ + save_state (_current_snapshot_name); +} + static bool state_file_filter (const string &str, void *arg) { - return (str.length() > strlen(Session::statefile_suffix()) && - str.find (Session::statefile_suffix()) == (str.length() - strlen (Session::statefile_suffix()))); + return (str.length() > strlen(statefile_suffix) && + str.find (statefile_suffix) == (str.length() - strlen (statefile_suffix))); } struct string_cmp { @@ -2275,12 +2136,6 @@ Session::possible_states () const return possible_states(_path); } -void -Session::auto_save() -{ - save_state (_current_snapshot_name); -} - RouteGroup * Session::add_edit_group (string name) { @@ -2356,7 +2211,7 @@ Session::edit_group_by_name (string name) } void -Session::begin_reversible_command (string name) +Session::begin_reversible_command (const string& name) { current_trans = new UndoTransaction; current_trans->set_name (name); @@ -2371,6 +2226,10 @@ Session::commit_reversible_command (Command *cmd) current_trans->add_command (cmd); } + if (current_trans->empty()) { + return; + } + gettimeofday (&now, 0); current_trans->set_timestamp (now); @@ -2384,7 +2243,7 @@ Session::get_global_route_boolean (bool (Route::*method)(void) const) boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->hidden()) { + if (!(*i)->is_hidden()) { RouteBooleanState v; v.first =* i; @@ -2405,7 +2264,7 @@ Session::get_global_route_metering () boost::shared_ptr r = routes.reader (); for (RouteList::iterator i = r->begin(); i != r->end(); ++i) { - if (!(*i)->hidden()) { + if (!(*i)->is_hidden()) { RouteMeterState v; v.first =* i; @@ -2489,96 +2348,10 @@ Session::global_record_enable_memento (void* src) } #endif -static bool -template_filter (const string &str, void *arg) -{ - return (str.length() > strlen(Session::template_suffix()) && - str.find (Session::template_suffix()) == (str.length() - strlen (Session::template_suffix()))); -} - -void -Session::get_template_list (list &template_names) -{ - vector *templates; - PathScanner scanner; - string path; - - path = template_path (); - - templates = scanner (path, template_filter, 0, false, true); - - vector::iterator i; - for (i = templates->begin(); i != templates->end(); ++i) { - string fullpath = *(*i); - int start, end; - - start = fullpath.find_last_of ('/') + 1; - if ((end = fullpath.find_last_of ('.')) <0) { - end = fullpath.length(); - } - - template_names.push_back(fullpath.substr(start, (end-start))); - } -} - -int -Session::read_favorite_dirs (FavoriteDirs & favs) -{ - string path = get_user_ardour_path(); - path += "/favorite_dirs"; - - ifstream fav (path.c_str()); - - favs.clear(); - - if (!fav) { - if (errno != ENOENT) { - //error << string_compose (_("cannot open favorite file %1 (%2)"), path, strerror (errno)) << endmsg; - return -1; - } else { - return 1; - } - } - - while (true) { - - string newfav; - - getline(fav, newfav); - - if (!fav.good()) { - break; - } - - favs.push_back (newfav); - } - - return 0; -} - -int -Session::write_favorite_dirs (FavoriteDirs & favs) -{ - string path = get_user_ardour_path(); - path += "/favorite_dirs"; - - ofstream fav (path.c_str()); - - if (!fav) { - return -1; - } - - for (FavoriteDirs::iterator i = favs.begin(); i != favs.end(); ++i) { - fav << (*i) << endl; - } - - return 0; -} - static bool accept_all_non_peak_files (const string& path, void *arg) { - return (path.length() > 5 && path.find (".peak") != (path.length() - 5)); + return (path.length() > 5 && path.find (peakfile_suffix) != (path.length() - 5)); } static bool @@ -2621,12 +2394,11 @@ Session::find_all_sources (string path, set& result) continue; } - string path = _path; /* /-terminated */ - path += sound_dir_name; - path += '/'; - path += prop->value(); + sys::path source_path = _session_dir->sound_path (); + + source_path /= prop->value (); - result.insert (path); + result.insert (source_path.to_string ()); } return 0; @@ -2657,7 +2429,7 @@ Session::find_all_sources_across_snapshots (set& result, bool exclude_th this_snapshot_path = _path; this_snapshot_path += _current_snapshot_name; - this_snapshot_path += _statefile_suffix; + this_snapshot_path += statefile_suffix; for (vector::iterator i = state_files->begin(); i != state_files->end(); ++i) { @@ -2685,6 +2457,8 @@ struct RegionCounter { int Session::cleanup_sources (Session::cleanup_report& rep) { + // FIXME: needs adaptation to midi + vector > dead_sources; vector > playlists_tbd; PathScanner scanner; @@ -2699,6 +2473,7 @@ Session::cleanup_sources (Session::cleanup_report& rep) int ret = -1; _state_of_the_state = (StateOfTheState) (_state_of_the_state | InCleanup); + /* step 1: consider deleting all unused playlists */ @@ -2762,8 +2537,8 @@ Session::cleanup_sources (Session::cleanup_report& rep) nexti = i; ++nexti; - sound_path += (*i).path; - sound_path += sound_dir (false); + SessionDirectory sdir ((*i).path); + sound_path += sdir.sound_path().to_string(); if (nexti != session_dirs.end()) { sound_path += ':'; @@ -2862,7 +2637,7 @@ Session::cleanup_sources (Session::cleanup_report& rep) newpath += dead_sound_dir_name; if (g_mkdir_with_parents (newpath.c_str(), 0755) < 0) { - error << string_compose(_("Session: cannot create session peakfile dir \"%1\" (%2)"), newpath, strerror (errno)) << endmsg; + error << string_compose(_("Session: cannot create session peakfile folder \"%1\" (%2)"), newpath, strerror (errno)) << endmsg; return -1; } @@ -2910,7 +2685,7 @@ Session::cleanup_sources (Session::cleanup_report& rep) */ string peakpath = (*x).substr (0, (*x).find_last_of ('.')); - peakpath += ".peak"; + peakpath += peakfile_suffix; if (access (peakpath.c_str(), W_OK) == 0) { if (::unlink (peakpath.c_str()) != 0) { @@ -2938,12 +2713,15 @@ Session::cleanup_sources (Session::cleanup_report& rep) out: _state_of_the_state = (StateOfTheState) (_state_of_the_state & ~InCleanup); + return ret; } int Session::cleanup_trash_sources (Session::cleanup_report& rep) { + // FIXME: needs adaptation for MIDI + vector::iterator i; string dead_sound_dir; struct dirent* dentry; @@ -3009,6 +2787,7 @@ Session::set_dirty () _state_of_the_state = StateOfTheState (_state_of_the_state | Dirty); + if (!was_dirty) { DirtyChanged(); /* EMIT SIGNAL */ } @@ -3022,6 +2801,7 @@ Session::set_clean () _state_of_the_state = Clean; + if (was_dirty) { DirtyChanged(); /* EMIT SIGNAL */ } @@ -3031,10 +2811,11 @@ void Session::set_deletion_in_progress () { _state_of_the_state = StateOfTheState (_state_of_the_state | Deletion); + } void -Session::add_controllable (Controllable* c) +Session::add_controllable (boost::shared_ptr c) { /* this adds a controllable to the list managed by the Session. this is a subset of those managed by the Controllable class @@ -3045,6 +2826,8 @@ Session::add_controllable (Controllable* c) Glib::Mutex::Lock lm (controllables_lock); controllables.insert (c); } + +struct null_deleter { void operator()(void const *) const {} }; void Session::remove_controllable (Controllable* c) @@ -3055,14 +2838,15 @@ Session::remove_controllable (Controllable* c) Glib::Mutex::Lock lm (controllables_lock); - Controllables::iterator x = controllables.find (c); + Controllables::iterator x = controllables.find( + boost::shared_ptr(c, null_deleter())); if (x != controllables.end()) { controllables.erase (x); } } -Controllable* +boost::shared_ptr Session::controllable_by_id (const PBD::ID& id) { Glib::Mutex::Lock lm (controllables_lock); @@ -3073,90 +2857,104 @@ Session::controllable_by_id (const PBD::ID& id) } } - return 0; + return boost::shared_ptr(); } void -Session::add_instant_xml (XMLNode& node, const std::string& dir) +Session::add_instant_xml (XMLNode& node) { - Stateful::add_instant_xml (node, dir); - Config->add_instant_xml (node, get_user_ardour_path()); + Stateful::add_instant_xml (node, _path); + Config->add_instant_xml (node); } +XMLNode* +Session::instant_xml (const string& node_name) +{ + return Stateful::instant_xml (node_name, _path); +} int Session::save_history (string snapshot_name) { - XMLTree tree; - string xml_path; - string bak_path; - - tree.set_root (&_history.get_state (Config->get_saved_history_depth())); - - if (snapshot_name.empty()) { - snapshot_name = _current_snapshot_name; - } + XMLTree tree; + + if (snapshot_name.empty()) { + snapshot_name = _current_snapshot_name; + } + + const string history_filename = snapshot_name + history_suffix; + const string backup_filename = history_filename + backup_suffix; + const sys::path xml_path = _session_dir->root_path() / history_filename; + const sys::path backup_path = _session_dir->root_path() / backup_filename; - xml_path = _path + snapshot_name + ".history"; + if (sys::exists (xml_path)) { + try + { + sys::rename (xml_path, backup_path); + } + catch (const sys::filesystem_error& err) + { + error << _("could not backup old history file, current history not saved") << endmsg; + return -1; + } + } - bak_path = xml_path + ".bak"; - if ((access (xml_path.c_str(), F_OK) == 0) && - (rename (xml_path.c_str(), bak_path.c_str()))) - { - error << _("could not backup old history file, current history not saved.") << endmsg; - return -1; - } + if (!Config->get_save_history() || Config->get_saved_history_depth() < 0) { + return 0; + } - if (!tree.write (xml_path)) - { - error << string_compose (_("history could not be saved to %1"), xml_path) << endmsg; + tree.set_root (&_history.get_state (Config->get_saved_history_depth())); - /* don't leave a corrupt file lying around if it is - * possible to fix. - */ + if (!tree.write (xml_path.to_string())) + { + error << string_compose (_("history could not be saved to %1"), xml_path.to_string()) << endmsg; - if (unlink (xml_path.c_str())) { - error << string_compose (_("could not remove corrupt history file %1"), xml_path) << endmsg; - } else { - if (rename (bak_path.c_str(), xml_path.c_str())) + try { - error << string_compose (_("could not restore history file from backup %1"), bak_path) << endmsg; + sys::remove (xml_path); + sys::rename (backup_path, xml_path); + } + catch (const sys::filesystem_error& err) + { + error << string_compose (_("could not restore history file from backup %1 (%2)"), + backup_path.to_string(), err.what()) << endmsg; } - } - return -1; - } + return -1; + } - return 0; + return 0; } int Session::restore_history (string snapshot_name) { - XMLTree tree; - string xmlpath; + XMLTree tree; - if (snapshot_name.empty()) { - snapshot_name = _current_snapshot_name; - } + if (snapshot_name.empty()) { + snapshot_name = _current_snapshot_name; + } - /* read xml */ - xmlpath = _path + snapshot_name + ".history"; - cerr << string_compose(_("Loading history from '%1'."), xmlpath) << endmsg; + const string xml_filename = snapshot_name + history_suffix; + const sys::path xml_path = _session_dir->root_path() / xml_filename; - if (access (xmlpath.c_str(), F_OK)) { - info << string_compose (_("%1: no history file \"%2\" for this session."), _name, xmlpath) << endmsg; - return 1; - } + cerr << "Loading history from " << xml_path.to_string() << endmsg; - if (!tree.read (xmlpath)) { - error << string_compose (_("Could not understand session history file \"%1\""), xmlpath) << endmsg; - return -1; - } + if (!sys::exists (xml_path)) { + info << string_compose (_("%1: no history file \"%2\" for this session."), + _name, xml_path.to_string()) << endmsg; + return 1; + } - /* replace history */ - _history.clear(); + if (!tree.read (xml_path.to_string())) { + error << string_compose (_("Could not understand session history file \"%1\""), + xml_path.to_string()) << endmsg; + return -1; + } + + // replace history + _history.clear(); for (XMLNodeConstIterator it = tree.root()->children().begin(); it != tree.root()->children().end(); it++) { @@ -3192,8 +2990,16 @@ Session::restore_history (string snapshot_name) ut->add_command (c); } + } else if (n->name() == "DeltaCommand") { + PBD::ID id(n->property("midi_source")->value()); + boost::shared_ptr midi_source = + boost::dynamic_pointer_cast(source_by_id(id)); + if(midi_source) { + ut->add_command(new MidiModel::DeltaCommand(midi_source->model(), *n)); + } else { + error << "FIXME: Failed to downcast MidiSource for DeltaCommand" << endmsg; + } } else { - error << string_compose(_("Couldn't figure out how to make a Command out of a %1 XMLNode."), n->name()) << endmsg; } } @@ -3265,20 +3071,22 @@ Session::config_changed (const char* parameter_name) } else if (PARAM_IS ("use-video-sync")) { - if (transport_stopped()) { - if (Config->get_use_video_sync()) { - waiting_for_sync_offset = true; - } - } + waiting_for_sync_offset = Config->get_use_video_sync(); } else if (PARAM_IS ("mmc-control")) { //poke_midi_thread (); - } else if (PARAM_IS ("mmc-device-id")) { + } else if (PARAM_IS ("mmc-device-id") || PARAM_IS ("mmc-receive-id")) { + + if (mmc) { + mmc->set_receive_device_id (Config->get_mmc_receive_device_id()); + } + + } else if (PARAM_IS ("mmc-send-id")) { if (mmc) { - mmc->set_device_id (Config->get_mmc_device_id()); + mmc->set_send_device_id (Config->get_mmc_send_device_id()); } } else if (PARAM_IS ("midi-control")) { @@ -3390,6 +3198,12 @@ Session::config_changed (const char* parameter_name) set_slave_source (Config->get_slave_source()); } else if (PARAM_IS ("remote-model")) { set_remote_control_ids (); + } else if (PARAM_IS ("denormal-model")) { + setup_fpu (); + } else if (PARAM_IS ("history-depth")) { + set_history_depth (Config->get_history_depth()); + } else if (PARAM_IS ("sync-all-route-ordering")) { + sync_order_keys (); } set_dirty (); @@ -3397,3 +3211,9 @@ Session::config_changed (const char* parameter_name) #undef PARAM_IS } + +void +Session::set_history_depth (uint32_t d) +{ + _history.set_depth (d); +}