X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fardour%2Fsession_state.cc;h=0abe1b652ef22278e3d7b23300ca5536ac748298;hb=ccdd99afcec1118c3a3080e6d109ebae55665336;hp=28d4a44174bd9e8243c24981fc8f499ad394f268;hpb=87a5518eb1e2bbdf7e6bffed20d153cfa1eac87d;p=ardour.git diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 28d4a44174..0abe1b652e 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -15,7 +15,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id$ */ #include @@ -45,12 +44,12 @@ #endif #include +#include #include #include -#include -#include +#include #include #include #include @@ -64,7 +63,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -117,6 +117,17 @@ Session::first_stage_init (string fullpath, string snapshot_name) _path += '/'; } + if (Glib::file_test (_path, Glib::FILE_TEST_EXISTS) && ::access (_path.c_str(), W_OK)) { + cerr << "Session non-writable based on " << _path << endl; + _writable = false; + } else { + cerr << "Session writable based on " << _path << endl; + _writable = true; + } + + set_history_depth (Config->get_history_depth()); + + /* these two are just provisional settings. set_state() will likely override them. */ @@ -124,29 +135,35 @@ Session::first_stage_init (string fullpath, string snapshot_name) _name = _current_snapshot_name = snapshot_name; _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); + post_transport_work = PostTransportWork (0); insert_cnt = 0; _transport_speed = 0; _last_transport_speed = 0; + auto_play_legal = false; transport_sub_state = 0; _transport_frame = 0; last_stop_frame = 0; + _requested_return_frame = -1; end_location = new Location (0, 0, _("end"), Location::Flags ((Location::IsMark|Location::IsEnd))); start_location = new Location (0, 0, _("start"), Location::Flags ((Location::IsMark|Location::IsStart))); _end_location_is_free = true; g_atomic_int_set (&_record_status, Disabled); loop_changing = false; play_loop = false; + have_looped = false; _last_roll_location = 0; _last_record_location = 0; pending_locate_frame = 0; pending_locate_roll = false; pending_locate_flush = false; dstream_buffer_size = 0; - state_tree = 0; state_was_pending = false; set_next_event (); outbound_mtc_smpte_frame = 0; @@ -154,15 +171,15 @@ Session::first_stage_init (string fullpath, string snapshot_name) current_block_size = 0; solo_update_disabled = false; currently_soloing = false; + _was_seamless = Config->get_seamless_loop (); _have_captured = false; _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; - mmc = 0; + _silent = false; session_send_mmc = false; session_send_mtc = false; post_transport_work = PostTransportWork (0); @@ -173,16 +190,17 @@ Session::first_stage_init (string fullpath, string snapshot_name) g_atomic_int_set (&_playback_load_min, 100); g_atomic_int_set (&_capture_load_min, 100); _play_range = false; - waiting_to_start = false; _exporting = false; _gain_automation_buffer = 0; _pan_automation_buffer = 0; _npan_buffers = 0; pending_abort = false; + pending_clear_substate = false; destructive_index = 0; current_trans = 0; first_file_data_format_reset = true; first_file_header_format_reset = true; + butler_thread = (pthread_t) 0; AudioDiskstream::allocate_working_buffers(); @@ -199,8 +217,6 @@ Session::first_stage_init (string fullpath, string snapshot_name) waveforms for clicks. */ - click_data = 0; - click_emphasis_data = 0; click_length = 0; click_emphasis_length = 0; _clicking = false; @@ -213,9 +229,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; @@ -230,7 +243,7 @@ Session::first_stage_init (string fullpath, string snapshot_name) /* slave stuff */ - average_slave_delta = 1800; + average_slave_delta = 1800; // !!! why 1800 ???? have_first_delta_accumulator = false; delta_accumulator_cnt = 0; slave_state = Stopped; @@ -279,6 +292,7 @@ Session::second_stage_init (bool new_session) // 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; @@ -299,7 +313,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); @@ -315,22 +329,27 @@ Session::second_stage_init (bool new_session) } /* handle this one in a different way than all others, so that its clear what happened */ - + catch (AudioEngine::PortRegistrationFailure& err) { - error << _("Unable to create all required ports") - << endmsg; - return -1; + destroy (); + throw; } catch (...) { return -1; } + BootMessage (_("Reset Remote Controls")); + send_full_time_code (); _engine.transport_locate (0); deliver_mmc (MIDI::MachineControl::cmdMmcReset, 0); deliver_mmc (MIDI::MachineControl::cmdLocate, 0); + /* initial program change will be delivered later; see ::config_changed() */ + + BootMessage (_("Reset Control Protocols")); + ControlProtocolManager::instance().set_session (*this); if (new_session) { @@ -338,7 +357,19 @@ 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; } @@ -386,20 +417,9 @@ Session::setup_raid_path (string path) 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); - + AudioFileSource::set_search_path (Glib::build_filename(sp.path, sound_dir (false))); return; } @@ -413,11 +433,7 @@ Session::setup_raid_path (string path) /* add sounds to file search path */ - fspath += sp.path; - if (fspath[fspath.length()-1] != '/') { - fspath += '/'; - } - fspath += sound_dir (false); + fspath += Glib::build_filename(sp.path, sound_dir (false)); fspath += ':'; remaining = remaining.substr (colon+1); @@ -429,11 +445,7 @@ Session::setup_raid_path (string path) sp.path = remaining; fspath += ':'; - fspath += sp.path; - if (fspath[fspath.length()-1] != '/') { - fspath += '/'; - } - fspath += sound_dir (false); + fspath += Glib::build_filename(sp.path, sound_dir (false)); fspath += ':'; session_dirs.push_back (sp); @@ -449,48 +461,70 @@ Session::setup_raid_path (string path) } 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 = peak_dir (); + + 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 (); + /* if this is is an existing session with an old "sounds" directory, just use it. see Session::sound_dir() for more details */ + + if (!Glib::file_test (old_sound_dir(), Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_DIR)) { + + dir = sound_dir (); + + if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { + error << string_compose(_("Session: cannot create session sounds folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg; + return -1; + } + } + + dir = dead_sound_dir (); 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 dead sounds folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg; return -1; } - dir = sound_dir (); + dir = export_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; + error << string_compose(_("Session: cannot create session export folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg; return -1; } - dir = dead_sound_dir (); + dir = analysis_dir (); 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 analysis folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg; return -1; } - dir = automation_dir (); + return 0; +} - if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) { - error << string_compose(_("Session: cannot create session automation dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg; +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()); @@ -534,9 +568,7 @@ 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; } @@ -566,6 +598,14 @@ Session::load_diskstreams (const XMLNode& node) return 0; } +void +Session::maybe_write_autosave() +{ + if (dirty() && record_status() != Recording) { + save_state("", true); + } +} + void Session::remove_pending_capture_state () { @@ -578,6 +618,48 @@ Session::remove_pending_capture_state () unlink (xml_path.c_str()); } +/** Rename a state file. + * @param snapshot_name Snapshot name. + */ +void +Session::rename_state (string old_name, string new_name) +{ + if (old_name == _current_snapshot_name || old_name == _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; + } +} + +/** Remove a state file. + * @param snapshot_name Snapshot name. + */ +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 */ + 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); + } + + /* and delete it */ + unlink (xml_path.c_str()); +} + int Session::save_state (string snapshot_name, bool pending) { @@ -585,7 +667,13 @@ Session::save_state (string snapshot_name, bool pending) string xml_path; string bak_path; - if (_state_of_the_state & CannotSave) { + if (!_writable || (_state_of_the_state & CannotSave)) { + return 1; + } + + if (!_engine.connected ()) { + error << _("Ardour's audio engine is not connected and state saving would lose all I/O connections. Session not saved") + << endmsg; return 1; } @@ -597,10 +685,12 @@ 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; + /* make a backup copy of the old file */ bak_path = xml_path; bak_path += ".bak"; @@ -610,6 +700,7 @@ Session::save_state (string snapshot_name, bool pending) } else { + /* pending save: use _pending_suffix (.pending in English) */ xml_path = _path; xml_path += snapshot_name; xml_path += _pending_suffix; @@ -622,7 +713,7 @@ Session::save_state (string snapshot_name, bool pending) tmp_path += snapshot_name; tmp_path += ".tmp"; - cerr << "actually writing state\n"; + // cerr << "actually writing state to " << xml_path << endl; if (!tree.write (tmp_path)) { error << string_compose (_("state could not be saved to %1"), tmp_path) << endmsg; @@ -645,6 +736,7 @@ Session::save_state (string snapshot_name, bool pending) bool was_dirty = dirty(); _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty); + if (was_dirty) { DirtyChanged (); /* EMIT SIGNAL */ @@ -684,7 +776,7 @@ Session::load_state (string snapshot_name) xmlpath += snapshot_name; xmlpath += _pending_suffix; - if (!access (xmlpath.c_str(), F_OK)) { + if (Glib::file_test (xmlpath, Glib::FILE_TEST_EXISTS)) { /* there is pending state from a crashed capture attempt */ @@ -699,8 +791,8 @@ Session::load_state (string snapshot_name) xmlpath += snapshot_name; xmlpath += _statefile_suffix; } - - if (access (xmlpath.c_str(), F_OK)) { + + if (!Glib::file_test (xmlpath, Glib::FILE_TEST_EXISTS)) { error << string_compose(_("%1: session state information file \"%2\" doesn't exist!"), _name, xmlpath) << endmsg; return 1; } @@ -709,6 +801,15 @@ Session::load_state (string snapshot_name) set_dirty(); + /* writable() really reflects the whole folder, but if for any + reason the session state file can't be written to, still + make us unwritable. + */ + + if (::access (xmlpath.c_str(), W_OK) != 0) { + _writable = false; + } + if (!state_tree->read (xmlpath)) { error << string_compose(_("Could not understand ardour file %1"), xmlpath) << endmsg; delete state_tree; @@ -742,16 +843,22 @@ Session::load_state (string snapshot_name) if (is_old) { string backup_path; - backup_path = xmlpath; - backup_path += ".1"; + backup_path = _path; + backup_path += snapshot_name; + backup_path += "-1"; + backup_path += _statefile_suffix; - info << string_compose (_("Copying old session file %1 to %2\nUse %2 with Ardour versions before 2.0 from now on"), - xmlpath, backup_path) - << endmsg; + /* don't make another copy if it already exists */ - copy_file (xmlpath, backup_path); - - /* if it fails, don't worry. right? */ + if (!Glib::file_test (backup_path, Glib::FILE_TEST_EXISTS)) { + info << string_compose (_("Copying old session file %1 to %2\nUse %2 with Ardour versions before 2.0 from now on"), + xmlpath, backup_path) + << endmsg; + + copy_file (xmlpath, backup_path); + + /* if it fails, don't worry. right? */ + } } return 0; @@ -766,9 +873,15 @@ Session::load_options (const XMLNode& node) Config->set_variables (node, ConfigVariableBase::Session); + /* now reset MIDI ports because the session can have its own + MIDI configuration. + */ + + setup_midi (); + if ((child = find_named_node (node, "end-marker-is-free")) != 0) { if ((prop = child->property ("val")) != 0) { - _end_location_is_free = (prop->value() == "yes"); + _end_location_is_free = string_is_affirmative (prop->value()); } } @@ -825,16 +938,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", libardour2_major_version, libardour2_minor_version, libardour2_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) { @@ -1040,19 +1153,7 @@ XMLNode& Session::get_control_protocol_state () { ControlProtocolManager& cpm (ControlProtocolManager::instance()); - XMLNode* node = new XMLNode (X_("ControlProtocols")); - - cpm.foreach_known_protocol (bind (mem_fun (*this, &Session::add_control_protocol), node)); - - return *node; -} - -void -Session::add_control_protocol (const ControlProtocolInfo* const cpi, XMLNode* node) -{ - if (cpi->protocol) { - node->add_child_nocopy (cpi->protocol->get_state()); - } + return cpm.get_state(); } int @@ -1074,6 +1175,17 @@ Session::set_state (const XMLNode& node) _name = prop->value (); } + 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)) { + throw SRMismatchRejected(); + } + } + } + setup_raid_path(_path); if ((prop = node.property (X_("id-counter"))) != 0) { @@ -1096,10 +1208,10 @@ Session::set_state (const XMLNode& node) /* Object loading order: - MIDI Path extra Options/Config + MIDI <= relies on data from Options/Config Locations Sources AudioRegions @@ -1112,9 +1224,6 @@ Session::set_state (const XMLNode& node) ControlProtocols */ - if (use_config_midi_ports ()) { - } - if ((child = find_named_node (node, "extra")) != 0) { _extra_xml = new XMLNode (*child); } @@ -1127,6 +1236,9 @@ Session::set_state (const XMLNode& node) error << _("Session: XML state has no options section") << endmsg; } + if (use_config_midi_ports ()) { + } + if ((child = find_named_node (node, "Locations")) == 0) { error << _("Session: XML state has no locations section") << endmsg; goto out; @@ -1249,14 +1361,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: @@ -1275,6 +1379,13 @@ Session::load_routes (const XMLNode& node) set_dirty(); for (niter = nlist.begin(); niter != nlist.end(); ++niter) { + XMLProperty* prop = (*niter)->property ("default-type"); + + if (prop && prop->value() == "unknown" ) { + std::cout << "ignoring route with type unknown. (video-track)" << std::endl; + // Note: this may mess up remote_control IDs or more.. + continue; + } boost::shared_ptr route (XMLRouteFactory (**niter)); @@ -1283,10 +1394,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; } @@ -1320,7 +1433,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; } } @@ -1334,6 +1454,7 @@ Session::XMLRegionFactory (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]; @@ -1394,9 +1515,49 @@ Session::XMLRegionFactory (const XMLNode& node, bool full) sources.push_back (as); } } - + + for (uint32_t n=0; 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))); + + /* 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()); + } + } + } + + 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; } @@ -1436,11 +1597,16 @@ Session::path_from_region_name (string name, string identifier) } else { snprintf (buf, sizeof(buf), "%s/%s-%" PRIu32 ".wav", dir.c_str(), name.c_str(), n); } - if (access (buf, F_OK) != 0) { + + if (!Glib::file_test (buf, Glib::FILE_TEST_EXISTS)) { return buf; } } + error << string_compose (_("cannot create new file from region name \"%1\" with ident = \"%2\": too many existing files with similar names"), + name, identifier) + << endmsg; + return ""; } @@ -1458,10 +1624,16 @@ Session::load_sources (const XMLNode& node) for (niter = nlist.begin(); niter != nlist.end(); ++niter) { - if ((source = XMLSourceFactory (**niter)) == 0) { - error << _("Session: cannot create Source from XML description.") << endmsg; + try { + if ((source = XMLSourceFactory (**niter)) == 0) { + error << _("Session: cannot create Source from XML description.") << endmsg; + } } + catch (non_existent_source& err) { + warning << _("A sound file is missing. It will be replaced by silence.") << endmsg; + source = SourceFactory::createSilent (*this, **niter, max_frames, _current_frame_rate); + } } return 0; @@ -1475,9 +1647,10 @@ 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) { error << _("Found a sound file that cannot be used by Ardour. Talk to the progammers.") << endmsg; return boost::shared_ptr(); @@ -1508,9 +1681,7 @@ Session::save_template (string template_name) tree.set_root (&get_template()); - xml_path = dir; - xml_path += template_name; - xml_path += _template_suffix; + xml_path = Glib::build_filename(dir, template_name + _template_suffix); ifstream in(xml_path.c_str()); @@ -1532,8 +1703,8 @@ Session::save_template (string template_name) 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; + string old_path = Glib::build_filename(template_dir(), old_name + _template_suffix); + string new_path = Glib::build_filename(template_dir(), new_name + _template_suffix); return rename (old_path.c_str(), new_path.c_str()); } @@ -1541,9 +1712,7 @@ Session::rename_template (string old_name, string new_name) int Session::delete_template (string name) { - string template_path = template_dir(); - template_path += name; - template_path += _template_suffix; + string template_path = Glib::build_filename(template_dir(), name + _template_suffix); return remove (template_path.c_str()); } @@ -1587,27 +1756,21 @@ Session::ensure_sound_dir (string path, string& result) /* Ensure that the sounds directory exists */ - result = path; - result += '/'; - result += sound_dir_name; + result = Glib::build_filename(path, 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; + dead = Glib::build_filename(path, 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; + peak = Glib::build_filename(path, peak_dir_name); if (g_mkdir_with_parents (peak.c_str(), 0775)) { error << string_compose(_("cannot create peak file directory \"%1\"; ignored"), peak) << endmsg; @@ -1826,16 +1989,56 @@ Session::dead_sound_dir () const { string res = _path; res += dead_sound_dir_name; - res += '/'; + + 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 { - /* support old session structure */ + string res; + string full; + vector parts; + + if (with_path) { + res = _path; + } else { + full = _path; + } + + parts.push_back(interchange_dir_name); + parts.push_back(legalize_for_path (_name)); + parts.push_back(sound_dir_name); + + res += Glib::build_filename(parts); + 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 */ - struct stat statbuf; string old_nopath; string old_withpath; @@ -1844,25 +2047,15 @@ Session::sound_dir (bool with_path) const old_withpath = _path; old_withpath += old_sound_dir_name; - - if (stat (old_withpath.c_str(), &statbuf) == 0) { + + 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; } - - string res; - - if (with_path) { - res = _path; - } - - res += interchange_dir_name; - res += '/'; - res += legalize_for_path (_name); - res += '/'; - res += sound_dir_name; + + /* ok, old "sounds" directory isn't there, return the new path */ return res; } @@ -1870,27 +2063,37 @@ Session::sound_dir (bool with_path) const string Session::peak_dir () const { - string res = _path; - res += peak_dir_name; - res += '/'; - return res; + return Glib::build_filename (_path, peak_dir_name); } string Session::automation_dir () const { - string res = _path; - res += "automation/"; - return res; + return Glib::build_filename (_path, "automation"); +} + +string +Session::analysis_dir () const +{ + return Glib::build_filename (_path, "analysis"); } string Session::template_dir () { - string path = get_user_ardour_path(); - path += "templates/"; + return Glib::build_filename (get_user_ardour_path(), "templates"); +} - return path; +string +Session::route_template_dir () +{ + return Glib::build_filename (get_user_ardour_path(), "route_templates"); +} + +string +Session::export_dir () const +{ + return Glib::build_filename (_path, export_dir_name); } string @@ -1933,9 +2136,20 @@ Session::template_path () return suffixed_search_path (X_("templates"), true); } + +string +Session::route_template_path () +{ + return suffixed_search_path (X_("route_templates"), true); +} + string Session::control_protocol_path () { + char *p = getenv ("ARDOUR_CONTROL_SURFACE_PATH"); + if (p && *p) { + return p; + } return suffixed_search_path (X_("surfaces"), false); } @@ -2143,6 +2357,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); @@ -2293,11 +2511,47 @@ Session::get_template_list (list &template_names) } } +void +Session::get_route_templates (vector& template_names) +{ + vector *templates; + PathScanner scanner; + string path; + + path = route_template_path (); + + templates = scanner (path, template_filter, 0, false, true); + + if (!templates) { + return; + } + + for (vector::iterator i = templates->begin(); i != templates->end(); ++i) { + string fullpath = *(*i); + + XMLTree tree; + + if (!tree.read (fullpath.c_str())) { + continue; + } + + XMLNode* root = tree.root(); + + RouteTemplateInfo rti; + + rti.name = IO::name_from_state (*root->children().front()); + rti.path = fullpath; + + template_names.push_back (rti); + } + + delete templates; +} + int Session::read_favorite_dirs (FavoriteDirs & favs) { - string path = get_user_ardour_path(); - path += "/favorite_dirs"; + Glib::ustring path = Glib::build_filename (get_user_ardour_path(), "favorite_dirs"); ifstream fav (path.c_str()); @@ -2331,8 +2585,7 @@ Session::read_favorite_dirs (FavoriteDirs & favs) int Session::write_favorite_dirs (FavoriteDirs & favs) { - string path = get_user_ardour_path(); - path += "/favorite_dirs"; + Glib::ustring path = Glib::build_filename (get_user_ardour_path(), "favorite_dirs"); ofstream fav (path.c_str()); @@ -2393,12 +2646,17 @@ Session::find_all_sources (string path, set& result) continue; } - string path = _path; /* /-terminated */ - path += sound_dir_name; - path += '/'; - path += prop->value(); + /* now we have to actually find the file */ - result.insert (path); + bool is_new; + uint16_t chan; + Glib::ustring path; + std::string name; + + if (AudioFileSource::find (prop->value(), true, false, is_new, chan, path, name)) { + cerr << "Got " << path << " from XML source with prop = " << prop->value() << endl; + result.insert (path); + } } return 0; @@ -2471,6 +2729,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 */ @@ -2572,6 +2831,9 @@ Session::cleanup_sources (Session::cleanup_report& rep) } } + char tmppath1[PATH_MAX+1]; + char tmppath2[PATH_MAX+1]; + for (vector::iterator x = soundfiles->begin(); x != soundfiles->end(); ++x) { used = false; @@ -2579,7 +2841,12 @@ Session::cleanup_sources (Session::cleanup_report& rep) for (set::iterator i = all_sources.begin(); i != all_sources.end(); ++i) { - if (spath == *i) { + realpath(spath.c_str(), tmppath1); + realpath((*i).c_str(), tmppath2); + + cerr << "comparing " << tmppath1 << " and " << tmppath2 << endl; + + if (strcmp(tmppath1, tmppath2) == 0) { used = true; break; } @@ -2607,18 +2874,28 @@ Session::cleanup_sources (Session::cleanup_report& rep) on whichever filesystem it was already on. */ - /* XXX this is a hack ... go up 4 levels */ + if ((*x).find ("/sounds/") != string::npos) { + + /* old school, go up 1 level */ + + newpath = Glib::path_get_dirname (*x); // "sounds" + newpath = Glib::path_get_dirname (newpath); // "session-name" + + } else { - newpath = Glib::path_get_dirname (*x); // "audiofiles" - newpath = Glib::path_get_dirname (newpath); // "session-name" - newpath = Glib::path_get_dirname (newpath); // "interchange" - newpath = Glib::path_get_dirname (newpath); // "session-dir" + /* new school, go up 4 levels */ + + newpath = Glib::path_get_dirname (*x); // "audiofiles" + newpath = Glib::path_get_dirname (newpath); // "session-name" + newpath = Glib::path_get_dirname (newpath); // "interchange" + newpath = Glib::path_get_dirname (newpath); // "session-dir" + } newpath += '/'; 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; } @@ -2694,6 +2971,7 @@ Session::cleanup_sources (Session::cleanup_report& rep) out: _state_of_the_state = (StateOfTheState) (_state_of_the_state & ~InCleanup); + return ret; } @@ -2765,6 +3043,7 @@ Session::set_dirty () _state_of_the_state = StateOfTheState (_state_of_the_state | Dirty); + if (!was_dirty) { DirtyChanged(); /* EMIT SIGNAL */ } @@ -2778,6 +3057,7 @@ Session::set_clean () _state_of_the_state = Clean; + if (was_dirty) { DirtyChanged(); /* EMIT SIGNAL */ } @@ -2787,11 +3067,18 @@ void Session::set_deletion_in_progress () { _state_of_the_state = StateOfTheState (_state_of_the_state | Deletion); + } void Session::add_controllable (Controllable* c) { + /* this adds a controllable to the list managed by the Session. + this is a subset of those managed by the Controllable class + itself, and represents the only ones whose state will be saved + as part of the session. + */ + Glib::Mutex::Lock lm (controllables_lock); controllables.insert (c); } @@ -2829,11 +3116,12 @@ Session::controllable_by_id (const PBD::ID& id) void Session::add_instant_xml (XMLNode& node, const std::string& dir) { - Stateful::add_instant_xml (node, dir); + if (_writable) { + Stateful::add_instant_xml (node, dir); + } Config->add_instant_xml (node, get_user_ardour_path()); } - int Session::save_history (string snapshot_name) { @@ -2841,7 +3129,9 @@ Session::save_history (string snapshot_name) string xml_path; string bak_path; - tree.set_root (&_history.get_state (Config->get_saved_history_depth())); + if (!_writable) { + return 0; + } if (snapshot_name.empty()) { snapshot_name = _current_snapshot_name; @@ -2851,13 +3141,17 @@ Session::save_history (string snapshot_name) bak_path = xml_path + ".bak"; - if ((access (xml_path.c_str(), F_OK) == 0) && - (rename (xml_path.c_str(), bak_path.c_str()))) - { + if (Glib::file_test (xml_path, Glib::FILE_TEST_EXISTS) && ::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; + } + + tree.set_root (&_history.get_state (Config->get_saved_history_depth())); + if (!tree.write (xml_path)) { error << string_compose (_("history could not be saved to %1"), xml_path) << endmsg; @@ -2893,10 +3187,9 @@ Session::restore_history (string snapshot_name) /* read xml */ xmlpath = _path + snapshot_name + ".history"; - cerr << string_compose(_("Loading history from '%1'."), xmlpath) << endmsg; + info << string_compose(_("Loading history from '%1'."), xmlpath) << endmsg; - if (access (xmlpath.c_str(), F_OK)) { - info << string_compose (_("%1: no history file \"%2\" for this session."), _name, xmlpath) << endmsg; + if (!Glib::file_test (xmlpath, Glib::FILE_TEST_EXISTS)) { return 1; } @@ -3015,16 +3308,20 @@ 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") || PARAM_IS ("mmc-receive-device-id")) { + + set_mmc_receive_device_id (Config->get_mmc_receive_device_id()); + + } else if (PARAM_IS ("mmc-send-device-id")) { + + set_mmc_send_device_id (Config->get_mmc_send_device_id()); + } else if (PARAM_IS ("midi-control")) { poke_midi_thread (); @@ -3083,6 +3380,8 @@ Session::config_changed (const char* parameter_name) /* mark us ready to send */ next_quarter_frame_to_send = 0; } + } else { + session_send_mtc = false; } } else if (PARAM_IS ("send-mmc")) { @@ -3093,6 +3392,9 @@ Session::config_changed (const char* parameter_name) if (_mmc_port != 0) { session_send_mmc = Config->get_send_mmc(); + } else { + mmc = 0; + session_send_mmc = false; } } else if (PARAM_IS ("midi-feedback")) { @@ -3124,10 +3426,38 @@ Session::config_changed (const char* parameter_name) } first_file_data_format_reset = false; + + } else if (PARAM_IS ("slave-source")) { + 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 ("session"); + } else if (PARAM_IS ("initial-program-change")) { + + if (_mmc_port && Config->get_initial_program_change() >= 0) { + MIDI::byte* buf = new MIDI::byte[2]; + + buf[0] = MIDI::program; // channel zero by default + buf[1] = (Config->get_initial_program_change() & 0x7f); + deliver_midi (_mmc_port, buf, 2); + } + } else if (PARAM_IS ("solo-mute-override")) { + catch_up_on_solo_mute_override (); } - + set_dirty (); #undef PARAM_IS } + +void +Session::set_history_depth (uint32_t d) +{ + _history.set_depth (d); +}