2 Copyright (C) 1999-2013 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include "libardour-config.h"
30 #include <cstdio> /* snprintf(3) ... grrr */
42 #if defined(__APPLE__) || defined(__FreeBSD__)
43 #include <sys/param.h>
44 #include <sys/mount.h>
47 #ifdef HAVE_SYS_STATVFS_H
48 #include <sys/statvfs.h>
52 #include "pbd/gstdio_compat.h"
55 #include <glibmm/threads.h>
56 #include <glibmm/fileutils.h>
58 #include <boost/algorithm/string.hpp>
60 #include "midi++/mmc.h"
61 #include "midi++/port.h"
63 #include "evoral/SMF.hpp"
65 #include "pbd/basename.h"
66 #include "pbd/debug.h"
67 #include "pbd/enumwriter.h"
68 #include "pbd/error.h"
69 #include "pbd/file_archive.h"
70 #include "pbd/file_utils.h"
71 #include "pbd/pathexpand.h"
72 #include "pbd/pthread_utils.h"
73 #include "pbd/stacktrace.h"
74 #include "pbd/types_convert.h"
75 #include "pbd/localtime_r.h"
76 #include "pbd/unwind.h"
78 #include "ardour/amp.h"
79 #include "ardour/async_midi_port.h"
80 #include "ardour/audio_diskstream.h"
81 #include "ardour/audio_track.h"
82 #include "ardour/audioengine.h"
83 #include "ardour/audiofilesource.h"
84 #include "ardour/audioregion.h"
85 #include "ardour/auditioner.h"
86 #include "ardour/automation_control.h"
87 #include "ardour/boost_debug.h"
88 #include "ardour/butler.h"
89 #include "ardour/controllable_descriptor.h"
90 #include "ardour/control_protocol_manager.h"
91 #include "ardour/directory_names.h"
92 #include "ardour/filename_extensions.h"
93 #include "ardour/graph.h"
94 #include "ardour/location.h"
96 #include "ardour/lv2_plugin.h"
98 #include "ardour/midi_model.h"
99 #include "ardour/midi_patch_manager.h"
100 #include "ardour/midi_region.h"
101 #include "ardour/midi_scene_changer.h"
102 #include "ardour/midi_source.h"
103 #include "ardour/midi_track.h"
104 #include "ardour/pannable.h"
105 #include "ardour/playlist_factory.h"
106 #include "ardour/playlist_source.h"
107 #include "ardour/port.h"
108 #include "ardour/processor.h"
109 #include "ardour/progress.h"
110 #include "ardour/profile.h"
111 #include "ardour/proxy_controllable.h"
112 #include "ardour/recent_sessions.h"
113 #include "ardour/region_factory.h"
114 #include "ardour/revision.h"
115 #include "ardour/route_group.h"
116 #include "ardour/send.h"
117 #include "ardour/session.h"
118 #include "ardour/session_directory.h"
119 #include "ardour/session_metadata.h"
120 #include "ardour/session_playlists.h"
121 #include "ardour/session_state_utils.h"
122 #include "ardour/silentfilesource.h"
123 #include "ardour/sndfilesource.h"
124 #include "ardour/source_factory.h"
125 #include "ardour/speakers.h"
126 #include "ardour/template_utils.h"
127 #include "ardour/tempo.h"
128 #include "ardour/ticker.h"
129 #include "ardour/types_convert.h"
130 #include "ardour/user_bundle.h"
131 #include "ardour/vca.h"
132 #include "ardour/vca_manager.h"
134 #include "control_protocol/control_protocol.h"
136 #include "LuaBridge/LuaBridge.h"
138 #include "pbd/i18n.h"
142 using namespace ARDOUR;
145 #define DEBUG_UNDO_HISTORY(msg) DEBUG_TRACE (PBD::DEBUG::UndoHistory, string_compose ("%1: %2\n", __LINE__, msg));
148 Session::pre_engine_init (string fullpath)
150 if (fullpath.empty()) {
152 throw failed_constructor();
155 /* discover canonical fullpath */
157 _path = canonical_path(fullpath);
160 if (Profile->get_trx() ) {
161 // Waves TracksLive has a usecase of session replacement with a new one.
162 // We should check session state file (<session_name>.ardour) existance
163 // to determine if the session is new or not
165 string full_session_name = Glib::build_filename( fullpath, _name );
166 full_session_name += statefile_suffix;
168 _is_new = !Glib::file_test (full_session_name, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
170 _is_new = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
173 /* finish initialization that can't be done in a normal C++ constructor
177 timerclear (&last_mmc_step);
178 g_atomic_int_set (&processing_prohibited, 0);
179 g_atomic_int_set (&_record_status, Disabled);
180 g_atomic_int_set (&_playback_load, 100);
181 g_atomic_int_set (&_capture_load, 100);
183 _all_route_group->set_active (true, this);
184 interpolation.add_channel_to (0, 0);
186 if (config.get_use_video_sync()) {
187 waiting_for_sync_offset = true;
189 waiting_for_sync_offset = false;
192 last_rr_session_dir = session_dirs.begin();
194 set_history_depth (Config->get_history_depth());
196 /* default: assume simple stereo speaker configuration */
198 _speakers->setup_default_speakers (2);
200 _solo_cut_control.reset (new ProxyControllable (_("solo cut control (dB)"), PBD::Controllable::GainLike,
201 boost::bind (&RCConfiguration::set_solo_mute_gain, Config, _1),
202 boost::bind (&RCConfiguration::get_solo_mute_gain, Config)));
203 add_controllable (_solo_cut_control);
205 /* These are all static "per-class" signals */
207 SourceFactory::SourceCreated.connect_same_thread (*this, boost::bind (&Session::add_source, this, _1));
208 PlaylistFactory::PlaylistCreated.connect_same_thread (*this, boost::bind (&Session::add_playlist, this, _1, _2));
209 AutomationList::AutomationListCreated.connect_same_thread (*this, boost::bind (&Session::add_automation_list, this, _1));
210 Controllable::Destroyed.connect_same_thread (*this, boost::bind (&Session::remove_controllable, this, _1));
211 IO::PortCountChanged.connect_same_thread (*this, boost::bind (&Session::ensure_buffers, this, _1));
213 /* stop IO objects from doing stuff until we're ready for them */
215 Delivery::disable_panners ();
216 IO::disable_connecting ();
220 Session::post_engine_init ()
222 BootMessage (_("Set block size and sample rate"));
224 set_block_size (_engine.samples_per_cycle());
225 set_frame_rate (_engine.sample_rate());
227 BootMessage (_("Using configuration"));
229 _midi_ports = new MidiPortManager;
231 MIDISceneChanger* msc;
233 _scene_changer = msc = new MIDISceneChanger (*this);
234 msc->set_input_port (boost::dynamic_pointer_cast<MidiPort>(scene_input_port()));
235 msc->set_output_port (boost::dynamic_pointer_cast<MidiPort>(scene_output_port()));
237 boost::function<framecnt_t(void)> timer_func (boost::bind (&Session::audible_frame, this, (bool*)(0)));
238 boost::dynamic_pointer_cast<AsyncMIDIPort>(scene_input_port())->set_timer (timer_func);
240 setup_midi_machine_control ();
242 if (_butler->start_thread()) {
243 error << _("Butler did not start") << endmsg;
247 if (start_midi_thread ()) {
248 error << _("MIDI I/O thread did not start") << endmsg;
252 setup_click_sounds (0);
253 setup_midi_control ();
255 _engine.Halted.connect_same_thread (*this, boost::bind (&Session::engine_halted, this));
256 _engine.Xrun.connect_same_thread (*this, boost::bind (&Session::xrun_recovery, this));
259 /* tempo map requires sample rate knowledge */
262 _tempo_map = new TempoMap (_current_frame_rate);
263 _tempo_map->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::tempo_map_changed, this, _1));
264 _tempo_map->MetricPositionChanged.connect_same_thread (*this, boost::bind (&Session::tempo_map_changed, this, _1));
266 /* MidiClock requires a tempo map */
269 midi_clock = new MidiClockTicker ();
270 midi_clock->set_session (this);
272 /* crossfades require sample rate knowledge */
274 SndFileSource::setup_standard_crossfades (*this, frame_rate());
275 _engine.GraphReordered.connect_same_thread (*this, boost::bind (&Session::graph_reordered, this));
276 _engine.MidiSelectionPortsChanged.connect_same_thread (*this, boost::bind (&Session::rewire_midi_selection_ports, this));
278 AudioDiskstream::allocate_working_buffers();
279 refresh_disk_space ();
281 /* we're finally ready to call set_state() ... all objects have
282 * been created, the engine is running.
286 if (set_state (*state_tree->root(), Stateful::loading_state_version)) {
287 error << _("Could not set session state from XML") << endmsg;
291 // set_state() will call setup_raid_path(), but if it's a new session we need
292 // to call setup_raid_path() here.
293 setup_raid_path (_path);
298 boost::function<void (std::string)> ff (boost::bind (&Session::config_changed, this, _1, false));
299 boost::function<void (std::string)> ft (boost::bind (&Session::config_changed, this, _1, true));
301 Config->map_parameters (ff);
302 config.map_parameters (ft);
303 _butler->map_parameters ();
305 /* Reset all panners */
307 Delivery::reset_panners ();
309 /* this will cause the CPM to instantiate any protocols that are in use
310 * (or mandatory), which will pass it this Session, and then call
311 * set_state() on each instantiated protocol to match stored state.
314 ControlProtocolManager::instance().set_session (this);
316 /* This must be done after the ControlProtocolManager set_session above,
317 as it will set states for ports which the ControlProtocolManager creates.
320 // XXX set state of MIDI::Port's
321 // MidiPortManager::instance()->set_port_states (Config->midi_port_states ());
323 /* And this must be done after the MIDI::Manager::set_port_states as
324 * it will try to make connections whose details are loaded by set_port_states.
329 /* Let control protocols know that we are now all connected, so they
330 * could start talking to surfaces if they want to.
333 ControlProtocolManager::instance().midi_connectivity_established ();
335 if (_is_new && !no_auto_connect()) {
336 Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock());
337 auto_connect_master_bus ();
340 _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty));
342 /* update latencies */
344 initialize_latencies ();
346 _locations->added.connect_same_thread (*this, boost::bind (&Session::location_added, this, _1));
347 _locations->removed.connect_same_thread (*this, boost::bind (&Session::location_removed, this, _1));
348 _locations->changed.connect_same_thread (*this, boost::bind (&Session::locations_changed, this));
350 } catch (AudioEngine::PortRegistrationFailure& err) {
351 /* handle this one in a different way than all others, so that its clear what happened */
352 error << err.what() << endmsg;
354 } catch (std::exception const & e) {
355 error << _("Unexpected exception during session setup: ") << e.what() << endmsg;
358 error << _("Unknown exception during session setup") << endmsg;
362 BootMessage (_("Reset Remote Controls"));
364 // send_full_time_code (0);
365 _engine.transport_locate (0);
367 send_immediate_mmc (MIDI::MachineControlCommand (MIDI::MachineControl::cmdMmcReset));
368 send_immediate_mmc (MIDI::MachineControlCommand (Timecode::Time ()));
370 MIDI::Name::MidiPatchManager::instance().add_search_path (session_directory().midi_patch_path() );
373 /* initial program change will be delivered later; see ::config_changed() */
375 _state_of_the_state = Clean;
377 Port::set_connecting_blocked (false);
379 DirtyChanged (); /* EMIT SIGNAL */
383 } else if (state_was_pending) {
385 remove_pending_capture_state ();
386 state_was_pending = false;
389 /* Now, finally, we can fill the playback buffers */
391 BootMessage (_("Filling playback buffers"));
393 boost::shared_ptr<RouteList> rl = routes.reader();
394 for (RouteList::iterator r = rl->begin(); r != rl->end(); ++r) {
395 boost::shared_ptr<Track> trk = boost::dynamic_pointer_cast<Track> (*r);
396 if (trk && !trk->hidden()) {
397 trk->seek (_transport_frame, true);
405 Session::session_loaded ()
409 _state_of_the_state = Clean;
411 DirtyChanged (); /* EMIT SIGNAL */
415 } else if (state_was_pending) {
417 remove_pending_capture_state ();
418 state_was_pending = false;
421 /* Now, finally, we can fill the playback buffers */
423 BootMessage (_("Filling playback buffers"));
424 force_locate (_transport_frame, false);
428 Session::raid_path () const
430 Searchpath raid_search_path;
432 for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
433 raid_search_path += (*i).path;
436 return raid_search_path.to_string ();
440 Session::setup_raid_path (string path)
449 session_dirs.clear ();
451 Searchpath search_path(path);
452 Searchpath sound_search_path;
453 Searchpath midi_search_path;
455 for (Searchpath::const_iterator i = search_path.begin(); i != search_path.end(); ++i) {
457 sp.blocks = 0; // not needed
458 session_dirs.push_back (sp);
460 SessionDirectory sdir(sp.path);
462 sound_search_path += sdir.sound_path ();
463 midi_search_path += sdir.midi_path ();
466 // reset the round-robin soundfile path thingie
467 last_rr_session_dir = session_dirs.begin();
471 Session::path_is_within_session (const std::string& path)
473 for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
474 if (PBD::path_is_within (i->path, path)) {
482 Session::ensure_subdirs ()
486 dir = session_directory().peak_path();
488 if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
489 error << string_compose(_("Session: cannot create session peakfile folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
493 dir = session_directory().sound_path();
495 if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
496 error << string_compose(_("Session: cannot create session sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
500 dir = session_directory().midi_path();
502 if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
503 error << string_compose(_("Session: cannot create session midi dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
507 dir = session_directory().dead_path();
509 if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
510 error << string_compose(_("Session: cannot create session dead sounds folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
514 dir = session_directory().export_path();
516 if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
517 error << string_compose(_("Session: cannot create session export folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
521 dir = analysis_dir ();
523 if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
524 error << string_compose(_("Session: cannot create session analysis folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
528 dir = plugins_dir ();
530 if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
531 error << string_compose(_("Session: cannot create session plugins folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
535 dir = externals_dir ();
537 if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
538 error << string_compose(_("Session: cannot create session externals folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
545 /** @param session_template directory containing session template, or empty.
546 * Caller must not hold process lock.
549 Session::create (const string& session_template, BusProfile* bus_profile)
551 if (g_mkdir_with_parents (_path.c_str(), 0755) < 0) {
552 error << string_compose(_("Session: cannot create session folder \"%1\" (%2)"), _path, strerror (errno)) << endmsg;
556 if (ensure_subdirs ()) {
560 _writable = exists_and_writable (_path);
562 if (!session_template.empty()) {
563 string in_path = (ARDOUR::Profile->get_trx () ? session_template : session_template_dir_to_file (session_template));
565 FILE* in = g_fopen (in_path.c_str(), "rb");
568 /* no need to call legalize_for_path() since the string
569 * in session_template is already a legal path name
571 string out_path = Glib::build_filename (_session_dir->root_path(), _name + statefile_suffix);
573 FILE* out = g_fopen (out_path.c_str(), "wb");
577 stringstream new_session;
580 size_t charsRead = fread (buf, sizeof(char), 1024, in);
583 error << string_compose (_("Error reading session template file %1 (%2)"), in_path, strerror (errno)) << endmsg;
588 if (charsRead == 0) {
591 new_session.write (buf, charsRead);
595 string file_contents = new_session.str();
596 size_t writeSize = file_contents.length();
597 if (fwrite (file_contents.c_str(), sizeof(char), writeSize, out) != writeSize) {
598 error << string_compose (_("Error writing session template file %1 (%2)"), out_path, strerror (errno)) << endmsg;
606 if (!ARDOUR::Profile->get_trx()) {
607 /* Copy plugin state files from template to new session */
608 std::string template_plugins = Glib::build_filename (session_template, X_("plugins"));
609 copy_recurse (template_plugins, plugins_dir ());
615 error << string_compose (_("Could not open %1 for writing session template"), out_path)
622 error << string_compose (_("Could not open session template %1 for reading"), in_path)
629 if (Profile->get_trx()) {
631 /* set initial start + end point : ARDOUR::Session::session_end_shift long.
632 * Remember that this is a brand new session. Sessions
633 * loaded from saved state will get this range from the saved state.
636 set_session_range_location (0, 0);
638 /* Initial loop location, from absolute zero, length 10 seconds */
640 Location* loc = new Location (*this, 0, 10.0 * _engine.sample_rate(), _("Loop"), Location::IsAutoLoop, 0);
641 _locations->add (loc, true);
642 set_auto_loop_location (loc);
645 _state_of_the_state = Clean;
647 /* set up Master Out and Monitor Out if necessary */
652 ChanCount count(DataType::AUDIO, bus_profile->master_out_channels);
654 // Waves Tracks: always create master bus for Tracks
655 if (ARDOUR::Profile->get_trx() || bus_profile->master_out_channels) {
656 boost::shared_ptr<Route> r (new Route (*this, _("Master"), PresentationInfo::MasterOut, DataType::AUDIO));
664 Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
665 r->input()->ensure_io (count, false, this);
666 r->output()->ensure_io (count, false, this);
672 /* prohibit auto-connect to master, because there isn't one */
673 bus_profile->output_ac = AutoConnectOption (bus_profile->output_ac & ~AutoConnectMaster);
677 add_routes (rl, false, false, false, PresentationInfo::max_order);
680 // Waves Tracks: Skip this. Always use autoconnection for Tracks
681 if (!ARDOUR::Profile->get_trx()) {
683 /* this allows the user to override settings with an environment variable.
686 if (no_auto_connect()) {
687 bus_profile->input_ac = AutoConnectOption (0);
688 bus_profile->output_ac = AutoConnectOption (0);
691 Config->set_input_auto_connect (bus_profile->input_ac);
692 Config->set_output_auto_connect (bus_profile->output_ac);
696 if (Config->get_use_monitor_bus() && bus_profile) {
697 add_monitor_section ();
704 Session::maybe_write_autosave()
706 if (dirty() && record_status() != Recording) {
707 save_state("", true);
712 Session::remove_pending_capture_state ()
714 std::string pending_state_file_path(_session_dir->root_path());
716 pending_state_file_path = Glib::build_filename (pending_state_file_path, legalize_for_path (_current_snapshot_name) + pending_suffix);
718 if (!Glib::file_test (pending_state_file_path, Glib::FILE_TEST_EXISTS)) return;
720 if (g_remove (pending_state_file_path.c_str()) != 0) {
721 error << string_compose(_("Could not remove pending capture state at path \"%1\" (%2)"),
722 pending_state_file_path, g_strerror (errno)) << endmsg;
726 /** Rename a state file.
727 * @param old_name Old snapshot name.
728 * @param new_name New snapshot name.
731 Session::rename_state (string old_name, string new_name)
733 if (old_name == _current_snapshot_name || old_name == _name) {
734 /* refuse to rename the current snapshot or the "main" one */
738 const string old_xml_filename = legalize_for_path (old_name) + statefile_suffix;
739 const string new_xml_filename = legalize_for_path (new_name) + statefile_suffix;
741 const std::string old_xml_path(Glib::build_filename (_session_dir->root_path(), old_xml_filename));
742 const std::string new_xml_path(Glib::build_filename (_session_dir->root_path(), new_xml_filename));
744 if (::g_rename (old_xml_path.c_str(), new_xml_path.c_str()) != 0) {
745 error << string_compose(_("could not rename snapshot %1 to %2 (%3)"),
746 old_name, new_name, g_strerror(errno)) << endmsg;
750 /** Remove a state file.
751 * @param snapshot_name Snapshot name.
754 Session::remove_state (string snapshot_name)
756 if (!_writable || snapshot_name == _current_snapshot_name || snapshot_name == _name) {
757 // refuse to remove the current snapshot or the "main" one
761 std::string xml_path(_session_dir->root_path());
763 xml_path = Glib::build_filename (xml_path, legalize_for_path (snapshot_name) + statefile_suffix);
765 if (!create_backup_file (xml_path)) {
766 // don't remove it if a backup can't be made
767 // create_backup_file will log the error.
772 if (g_remove (xml_path.c_str()) != 0) {
773 error << string_compose(_("Could not remove session file at path \"%1\" (%2)"),
774 xml_path, g_strerror (errno)) << endmsg;
778 /** @param snapshot_name Name to save under, without .ardour / .pending prefix */
780 Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot, bool template_only)
782 DEBUG_TRACE (DEBUG::Locale, string_compose ("Session::save_state locale '%1'\n", setlocale (LC_NUMERIC, NULL)));
785 std::string xml_path(_session_dir->root_path());
787 /* prevent concurrent saves from different threads */
789 Glib::Threads::Mutex::Lock lm (save_state_lock);
791 if (!_writable || (_state_of_the_state & CannotSave)) {
795 if (g_atomic_int_get(&_suspend_save)) {
799 _save_queued = false;
801 if (!_engine.connected ()) {
802 error << string_compose (_("the %1 audio engine is not connected and state saving would lose all I/O connections. Session not saved"), PROGRAM_NAME)
808 const int64_t save_start_time = g_get_monotonic_time();
811 /* tell sources we're saving first, in case they write out to a new file
812 * which should be saved with the state rather than the old one */
813 for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) {
815 i->second->session_saved();
816 } catch (Evoral::SMF::FileError& e) {
817 error << string_compose ("Could not write to MIDI file %1; MIDI data not saved.", e.file_name ()) << endmsg;
821 SessionSaveUnderway (); /* EMIT SIGNAL */
823 bool mark_as_clean = true;
825 if (!snapshot_name.empty() && !switch_to_snapshot) {
826 mark_as_clean = false;
830 mark_as_clean = false;
831 tree.set_root (&get_template());
833 tree.set_root (&get_state());
836 if (snapshot_name.empty()) {
837 snapshot_name = _current_snapshot_name;
838 } else if (switch_to_snapshot) {
839 set_snapshot_name (snapshot_name);
842 assert (!snapshot_name.empty());
846 /* proper save: use statefile_suffix (.ardour in English) */
848 xml_path = Glib::build_filename (xml_path, legalize_for_path (snapshot_name) + statefile_suffix);
850 /* make a backup copy of the old file */
852 if (Glib::file_test (xml_path, Glib::FILE_TEST_EXISTS) && !create_backup_file (xml_path)) {
853 // create_backup_file will log the error
859 /* pending save: use pending_suffix (.pending in English) */
860 xml_path = Glib::build_filename (xml_path, legalize_for_path (snapshot_name) + pending_suffix);
863 std::string tmp_path(_session_dir->root_path());
864 tmp_path = Glib::build_filename (tmp_path, legalize_for_path (snapshot_name) + temp_suffix);
866 cerr << "actually writing state to " << tmp_path << endl;
868 if (!tree.write (tmp_path)) {
869 error << string_compose (_("state could not be saved to %1"), tmp_path) << endmsg;
870 if (g_remove (tmp_path.c_str()) != 0) {
871 error << string_compose(_("Could not remove temporary session file at path \"%1\" (%2)"),
872 tmp_path, g_strerror (errno)) << endmsg;
878 cerr << "renaming state to " << xml_path << endl;
880 if (::g_rename (tmp_path.c_str(), xml_path.c_str()) != 0) {
881 error << string_compose (_("could not rename temporary session file %1 to %2 (%3)"),
882 tmp_path, xml_path, g_strerror(errno)) << endmsg;
883 if (g_remove (tmp_path.c_str()) != 0) {
884 error << string_compose(_("Could not remove temporary session file at path \"%1\" (%2)"),
885 tmp_path, g_strerror (errno)) << endmsg;
893 save_history (snapshot_name);
896 bool was_dirty = dirty();
898 _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty);
901 DirtyChanged (); /* EMIT SIGNAL */
905 StateSaved (snapshot_name); /* EMIT SIGNAL */
909 const int64_t elapsed_time_us = g_get_monotonic_time() - save_start_time;
910 cerr << "saved state in " << fixed << setprecision (1) << elapsed_time_us / 1000. << " ms\n";
916 Session::restore_state (string snapshot_name)
918 if (load_state (snapshot_name) == 0) {
919 set_state (*state_tree->root(), Stateful::loading_state_version);
926 Session::load_state (string snapshot_name)
931 state_was_pending = false;
933 /* check for leftover pending state from a crashed capture attempt */
935 std::string xmlpath(_session_dir->root_path());
936 xmlpath = Glib::build_filename (xmlpath, legalize_for_path (snapshot_name) + pending_suffix);
938 if (Glib::file_test (xmlpath, Glib::FILE_TEST_EXISTS)) {
940 /* there is pending state from a crashed capture attempt */
942 boost::optional<int> r = AskAboutPendingState();
943 if (r.get_value_or (1)) {
944 state_was_pending = true;
948 if (!state_was_pending) {
949 xmlpath = Glib::build_filename (_session_dir->root_path(), snapshot_name);
952 if (!Glib::file_test (xmlpath, Glib::FILE_TEST_EXISTS)) {
953 xmlpath = Glib::build_filename (_session_dir->root_path(), legalize_for_path (snapshot_name) + statefile_suffix);
954 if (!Glib::file_test (xmlpath, Glib::FILE_TEST_EXISTS)) {
955 error << string_compose(_("%1: session file \"%2\" doesn't exist!"), _name, xmlpath) << endmsg;
960 state_tree = new XMLTree;
964 _writable = exists_and_writable (xmlpath) && exists_and_writable(Glib::path_get_dirname(xmlpath));
966 if (!state_tree->read (xmlpath)) {
967 error << string_compose(_("Could not understand session file %1"), xmlpath) << endmsg;
973 XMLNode const & root (*state_tree->root());
975 if (root.name() != X_("Session")) {
976 error << string_compose (_("Session file %1 is not a session"), xmlpath) << endmsg;
983 if (root.get_property ("version", version)) {
984 if (version.find ('.') != string::npos) {
985 /* old school version format */
986 if (version[0] == '2') {
987 Stateful::loading_state_version = 2000;
989 Stateful::loading_state_version = 3000;
992 Stateful::loading_state_version = string_to<int32_t>(version);
995 /* no version implies very old version of Ardour */
996 Stateful::loading_state_version = 1000;
999 if (Stateful::loading_state_version < CURRENT_SESSION_FILE_VERSION && _writable) {
1001 std::string backup_path(_session_dir->root_path());
1002 std::string backup_filename = string_compose ("%1-%2%3", legalize_for_path (snapshot_name), Stateful::loading_state_version, statefile_suffix);
1003 backup_path = Glib::build_filename (backup_path, backup_filename);
1005 // only create a backup for a given statefile version once
1007 if (!Glib::file_test (backup_path, Glib::FILE_TEST_EXISTS)) {
1009 VersionMismatch (xmlpath, backup_path);
1011 if (!copy_file (xmlpath, backup_path)) {;
1017 save_snapshot_name (snapshot_name);
1023 Session::load_options (const XMLNode& node)
1026 config.set_variables (node);
1031 Session::save_default_options ()
1033 return config.save_state();
1037 Session::get_state()
1043 Session::get_template()
1045 /* if we don't disable rec-enable, diskstreams
1046 will believe they need to store their capture
1047 sources in their state node.
1050 disable_record (false);
1052 return state(false);
1055 typedef std::set<boost::shared_ptr<Playlist> > PlaylistSet;
1056 typedef std::set<boost::shared_ptr<Source> > SourceSet;
1059 Session::export_track_state (boost::shared_ptr<RouteList> rl, const string& path)
1061 if (Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
1064 if (g_mkdir_with_parents (path.c_str(), 0755) != 0) {
1068 PBD::Unwinder<std::string> uw (_template_state_dir, path);
1071 XMLNode* node = new XMLNode("TrackState"); // XXX
1074 PlaylistSet playlists; // SessionPlaylists
1077 // these will work with new_route_from_template()
1078 // TODO: LV2 plugin-state-dir needs to be relative (on load?)
1079 child = node->add_child ("Routes");
1080 for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
1081 if ((*i)->is_auditioner()) {
1084 if ((*i)->is_master() || (*i)->is_monitor()) {
1087 child->add_child_nocopy ((*i)->get_state());
1088 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (*i);
1090 playlists.insert (track->playlist ());
1094 // on load, Regions in the playlists need to resolve and map Source-IDs
1095 // also playlist needs to be merged or created with new-name..
1096 // ... and Diskstream in tracks adjusted to use the correct playlist
1097 child = node->add_child ("Playlists"); // SessionPlaylists::add_state
1098 for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
1099 child->add_child_nocopy ((*i)->get_state ());
1100 boost::shared_ptr<RegionList> prl = (*i)->region_list ();
1101 for (RegionList::const_iterator s = prl->begin(); s != prl->end(); ++s) {
1102 const Region::SourceList& sl = (*s)->sources ();
1103 for (Region::SourceList::const_iterator sli = sl.begin(); sli != sl.end(); ++sli) {
1104 sources.insert (*sli);
1109 child = node->add_child ("Sources");
1110 for (SourceSet::const_iterator i = sources.begin(); i != sources.end(); ++i) {
1111 child->add_child_nocopy ((*i)->get_state ());
1112 boost::shared_ptr<FileSource> fs = boost::dynamic_pointer_cast<FileSource> (*i);
1114 #ifdef PLATFORM_WINDOWS
1117 string p = fs->path ();
1118 PBD::copy_file (p, Glib::build_filename (path, Glib::path_get_basename (p)));
1122 std::string sn = Glib::build_filename (path, "share.axml");
1125 tree.set_root (node);
1126 return tree.write (sn.c_str());
1131 struct route_id_compare {
1133 operator() (const boost::shared_ptr<Route>& r1, const boost::shared_ptr<Route>& r2)
1135 return r1->id () < r2->id ();
1141 Session::state (bool full_state)
1144 XMLNode* node = new XMLNode("Session");
1147 node->set_property("version", CURRENT_SESSION_FILE_VERSION);
1149 child = node->add_child ("ProgramVersion");
1150 child->set_property("created-with", created_with);
1152 std::string modified_with = string_compose ("%1 %2", PROGRAM_NAME, revision);
1153 child->set_property("modified-with", modified_with);
1155 /* store configuration settings */
1159 node->set_property ("name", _name);
1160 node->set_property ("sample-rate", _base_frame_rate);
1162 if (session_dirs.size() > 1) {
1166 vector<space_and_path>::iterator i = session_dirs.begin();
1167 vector<space_and_path>::iterator next;
1169 ++i; /* skip the first one */
1173 while (i != session_dirs.end()) {
1177 if (next != session_dirs.end()) {
1178 p += G_SEARCHPATH_SEPARATOR;
1187 child = node->add_child ("Path");
1188 child->add_content (p);
1192 node->set_property ("end-is-free", _session_range_end_is_free);
1194 /* save the ID counter */
1196 node->set_property ("id-counter", ID::counter());
1198 node->set_property ("name-counter", name_id_counter ());
1200 /* save the event ID counter */
1202 node->set_property ("event-counter", Evoral::event_id_counter());
1204 /* save the VCA counter */
1206 node->set_property ("vca-counter", VCA::get_next_vca_number());
1208 /* various options */
1210 list<XMLNode*> midi_port_nodes = _midi_ports->get_midi_port_states();
1211 if (!midi_port_nodes.empty()) {
1212 XMLNode* midi_port_stuff = new XMLNode ("MIDIPorts");
1213 for (list<XMLNode*>::const_iterator n = midi_port_nodes.begin(); n != midi_port_nodes.end(); ++n) {
1214 midi_port_stuff->add_child_nocopy (**n);
1216 node->add_child_nocopy (*midi_port_stuff);
1219 XMLNode& cfgxml (config.get_variables ());
1221 /* exclude search-paths from template */
1222 cfgxml.remove_nodes_and_delete ("name", "audio-search-path");
1223 cfgxml.remove_nodes_and_delete ("name", "midi-search-path");
1224 cfgxml.remove_nodes_and_delete ("name", "raid-path");
1226 node->add_child_nocopy (cfgxml);
1228 node->add_child_nocopy (ARDOUR::SessionMetadata::Metadata()->get_state());
1230 child = node->add_child ("Sources");
1233 Glib::Threads::Mutex::Lock sl (source_lock);
1235 for (SourceMap::iterator siter = sources.begin(); siter != sources.end(); ++siter) {
1237 /* Don't save information about non-file Sources, or
1238 * about non-destructive file sources that are empty
1239 * and unused by any regions.
1242 boost::shared_ptr<FileSource> fs;
1244 if ((fs = boost::dynamic_pointer_cast<FileSource> (siter->second)) != 0) {
1246 if (!fs->destructive()) {
1247 if (fs->empty() && !fs->used()) {
1252 child->add_child_nocopy (siter->second->get_state());
1257 child = node->add_child ("Regions");
1260 Glib::Threads::Mutex::Lock rl (region_lock);
1261 const RegionFactory::RegionMap& region_map (RegionFactory::all_regions());
1262 for (RegionFactory::RegionMap::const_iterator i = region_map.begin(); i != region_map.end(); ++i) {
1263 boost::shared_ptr<Region> r = i->second;
1264 /* only store regions not attached to playlists */
1265 if (r->playlist() == 0) {
1266 if (boost::dynamic_pointer_cast<AudioRegion>(r)) {
1267 child->add_child_nocopy ((boost::dynamic_pointer_cast<AudioRegion>(r))->get_basic_state ());
1269 child->add_child_nocopy (r->get_state ());
1274 RegionFactory::CompoundAssociations& cassocs (RegionFactory::compound_associations());
1276 if (!cassocs.empty()) {
1277 XMLNode* ca = node->add_child (X_("CompoundAssociations"));
1279 for (RegionFactory::CompoundAssociations::iterator i = cassocs.begin(); i != cassocs.end(); ++i) {
1280 XMLNode* can = new XMLNode (X_("CompoundAssociation"));
1281 can->set_property (X_("copy"), i->first->id());
1282 can->set_property (X_("original"), i->second->id());
1283 ca->add_child_nocopy (*can);
1291 node->add_child_nocopy (_locations->get_state());
1294 Locations loc (*this);
1295 const bool was_dirty = dirty();
1296 // for a template, just create a new Locations, populate it
1297 // with the default start and end, and get the state for that.
1298 Location* range = new Location (*this, 0, 0, _("session"), Location::IsSessionRange, 0);
1299 range->set (max_framepos, 0);
1301 XMLNode& locations_state = loc.get_state();
1303 if (ARDOUR::Profile->get_trx() && _locations) {
1304 // For tracks we need stored the Auto Loop Range and all MIDI markers.
1305 for (Locations::LocationList::const_iterator i = _locations->list ().begin (); i != _locations->list ().end (); ++i) {
1306 if ((*i)->is_mark () || (*i)->is_auto_loop ()) {
1307 locations_state.add_child_nocopy ((*i)->get_state ());
1311 node->add_child_nocopy (locations_state);
1313 /* adding a location above will have marked the session
1314 * dirty. This is an artifact, so fix it if the session wasn't
1319 _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty);
1323 child = node->add_child ("Bundles");
1325 boost::shared_ptr<BundleList> bundles = _bundles.reader ();
1326 for (BundleList::iterator i = bundles->begin(); i != bundles->end(); ++i) {
1327 boost::shared_ptr<UserBundle> b = boost::dynamic_pointer_cast<UserBundle> (*i);
1329 child->add_child_nocopy (b->get_state());
1334 node->add_child_nocopy (_vca_manager->get_state());
1336 child = node->add_child ("Routes");
1338 boost::shared_ptr<RouteList> r = routes.reader ();
1340 route_id_compare cmp;
1341 RouteList xml_node_order (*r);
1342 xml_node_order.sort (cmp);
1344 for (RouteList::iterator i = xml_node_order.begin(); i != xml_node_order.end(); ++i) {
1345 if (!(*i)->is_auditioner()) {
1347 child->add_child_nocopy ((*i)->get_state());
1349 child->add_child_nocopy ((*i)->get_template());
1355 playlists->add_state (node, full_state);
1357 child = node->add_child ("RouteGroups");
1358 for (list<RouteGroup *>::iterator i = _route_groups.begin(); i != _route_groups.end(); ++i) {
1359 child->add_child_nocopy ((*i)->get_state());
1363 XMLNode* gain_child = node->add_child ("Click");
1364 gain_child->add_child_nocopy (_click_io->state (full_state));
1365 gain_child->add_child_nocopy (_click_gain->state (full_state));
1369 XMLNode* ltc_input_child = node->add_child ("LTC-In");
1370 ltc_input_child->add_child_nocopy (_ltc_input->state (full_state));
1374 XMLNode* ltc_output_child = node->add_child ("LTC-Out");
1375 ltc_output_child->add_child_nocopy (_ltc_output->state (full_state));
1378 node->add_child_nocopy (_speakers->get_state());
1379 node->add_child_nocopy (_tempo_map->get_state());
1380 node->add_child_nocopy (get_control_protocol_state());
1383 node->add_child_copy (*_extra_xml);
1387 Glib::Threads::Mutex::Lock lm (lua_lock);
1390 luabridge::LuaRef savedstate ((*_lua_save)());
1391 saved = savedstate.cast<std::string>();
1393 lua.collect_garbage ();
1396 gchar* b64 = g_base64_encode ((const guchar*)saved.c_str (), saved.size ());
1397 std::string b64s (b64);
1400 XMLNode* script_node = new XMLNode (X_("Script"));
1401 script_node->set_property (X_("lua"), LUA_VERSION);
1402 script_node->add_content (b64s);
1403 node->add_child_nocopy (*script_node);
1410 Session::get_control_protocol_state ()
1412 ControlProtocolManager& cpm (ControlProtocolManager::instance());
1413 return cpm.get_state();
1417 Session::set_state (const XMLNode& node, int version)
1424 _state_of_the_state = StateOfTheState (_state_of_the_state|CannotSave);
1426 if (node.name() != X_("Session")) {
1427 fatal << _("programming error: Session: incorrect XML node sent to set_state()") << endmsg;
1431 node.get_property ("name", _name);
1433 if (node.get_property (X_("sample-rate"), _base_frame_rate)) {
1435 _nominal_frame_rate = _base_frame_rate;
1437 assert (AudioEngine::instance()->running ());
1438 if (_base_frame_rate != AudioEngine::instance()->sample_rate ()) {
1439 boost::optional<int> r = AskAboutSampleRateMismatch (_base_frame_rate, _current_frame_rate);
1440 if (r.get_value_or (0)) {
1446 created_with = "unknown";
1447 if ((child = find_named_node (node, "ProgramVersion")) != 0) {
1448 child->get_property (X_("created-with"), created_with);
1451 setup_raid_path(_session_dir->root_path());
1453 node.get_property (X_("end-is-free"), _session_range_end_is_free);
1456 if (node.get_property (X_("id-counter"), counter)) {
1457 ID::init_counter (counter);
1459 /* old sessions used a timebased counter, so fake
1460 * the startup ID counter based on a standard
1465 ID::init_counter (now);
1468 if (node.get_property (X_("name-counter"), counter)) {
1469 init_name_id_counter (counter);
1472 if (node.get_property (X_("event-counter"), counter)) {
1473 Evoral::init_event_id_counter (counter);
1476 if (node.get_property (X_("vca-counter"), counter)) {
1477 VCA::set_next_vca_number (counter);
1479 VCA::set_next_vca_number (1);
1482 if ((child = find_named_node (node, "MIDIPorts")) != 0) {
1483 _midi_ports->set_midi_port_states (child->children());
1486 IO::disable_connecting ();
1488 Stateful::save_extra_xml (node);
1490 if (((child = find_named_node (node, "Options")) != 0)) { /* old style */
1491 load_options (*child);
1492 } else if ((child = find_named_node (node, "Config")) != 0) { /* new style */
1493 load_options (*child);
1495 error << _("Session: XML state has no options section") << endmsg;
1498 if (version >= 3000) {
1499 if ((child = find_named_node (node, "Metadata")) == 0) {
1500 warning << _("Session: XML state has no metadata section") << endmsg;
1501 } else if ( ARDOUR::SessionMetadata::Metadata()->set_state (*child, version) ) {
1506 if ((child = find_named_node (node, X_("Speakers"))) != 0) {
1507 _speakers->set_state (*child, version);
1510 if ((child = find_named_node (node, "Sources")) == 0) {
1511 error << _("Session: XML state has no sources section") << endmsg;
1513 } else if (load_sources (*child)) {
1517 if ((child = find_named_node (node, "TempoMap")) == 0) {
1518 error << _("Session: XML state has no Tempo Map section") << endmsg;
1520 } else if (_tempo_map->set_state (*child, version)) {
1524 if ((child = find_named_node (node, "Locations")) == 0) {
1525 error << _("Session: XML state has no locations section") << endmsg;
1527 } else if (_locations->set_state (*child, version)) {
1531 locations_changed ();
1533 if (_session_range_location) {
1534 AudioFileSource::set_header_position_offset (_session_range_location->start());
1537 if ((child = find_named_node (node, "Regions")) == 0) {
1538 error << _("Session: XML state has no Regions section") << endmsg;
1540 } else if (load_regions (*child)) {
1544 if ((child = find_named_node (node, "Playlists")) == 0) {
1545 error << _("Session: XML state has no playlists section") << endmsg;
1547 } else if (playlists->load (*this, *child)) {
1551 if ((child = find_named_node (node, "UnusedPlaylists")) == 0) {
1553 } else if (playlists->load_unused (*this, *child)) {
1557 if ((child = find_named_node (node, "CompoundAssociations")) != 0) {
1558 if (load_compounds (*child)) {
1563 if (version >= 3000) {
1564 if ((child = find_named_node (node, "Bundles")) == 0) {
1565 warning << _("Session: XML state has no bundles section") << endmsg;
1568 /* We can't load Bundles yet as they need to be able
1569 * to convert from port names to Port objects, which can't happen until
1571 _bundle_xml_node = new XMLNode (*child);
1575 if (version < 3000) {
1576 if ((child = find_named_node (node, X_("DiskStreams"))) == 0) {
1577 error << _("Session: XML state has no diskstreams section") << endmsg;
1579 } else if (load_diskstreams_2X (*child, version)) {
1584 if ((child = find_named_node (node, VCAManager::xml_node_name)) != 0) {
1585 _vca_manager->set_state (*child, version);
1588 if ((child = find_named_node (node, "Routes")) == 0) {
1589 error << _("Session: XML state has no routes section") << endmsg;
1591 } else if (load_routes (*child, version)) {
1595 /* Now that we have Routes and masters loaded, connect them if appropriate */
1597 Slavable::Assign (_vca_manager); /* EMIT SIGNAL */
1599 /* our diskstreams list is no longer needed as they are now all owned by their Route */
1600 _diskstreams_2X.clear ();
1602 if (version >= 3000) {
1604 if ((child = find_named_node (node, "RouteGroups")) == 0) {
1605 error << _("Session: XML state has no route groups section") << endmsg;
1607 } else if (load_route_groups (*child, version)) {
1611 } else if (version < 3000) {
1613 if ((child = find_named_node (node, "EditGroups")) == 0) {
1614 error << _("Session: XML state has no edit groups section") << endmsg;
1616 } else if (load_route_groups (*child, version)) {
1620 if ((child = find_named_node (node, "MixGroups")) == 0) {
1621 error << _("Session: XML state has no mix groups section") << endmsg;
1623 } else if (load_route_groups (*child, version)) {
1628 if ((child = find_named_node (node, "Click")) == 0) {
1629 warning << _("Session: XML state has no click section") << endmsg;
1630 } else if (_click_io) {
1631 setup_click_state (&node);
1634 if ((child = find_named_node (node, ControlProtocolManager::state_node_name)) != 0) {
1635 ControlProtocolManager::instance().set_state (*child, version);
1638 if ((child = find_named_node (node, "Script"))) {
1639 for (XMLNodeList::const_iterator n = child->children ().begin (); n != child->children ().end (); ++n) {
1640 if (!(*n)->is_content ()) { continue; }
1642 guchar* buf = g_base64_decode ((*n)->content ().c_str (), &size);
1644 Glib::Threads::Mutex::Lock lm (lua_lock);
1645 (*_lua_load)(std::string ((const char*)buf, size));
1646 } catch (luabridge::LuaException const& e) {
1647 cerr << "LuaException:" << e.what () << endl;
1653 update_route_record_state ();
1655 /* here beginneth the second phase ... */
1656 set_snapshot_name (_current_snapshot_name);
1658 StateReady (); /* EMIT SIGNAL */
1671 Session::load_routes (const XMLNode& node, int version)
1674 XMLNodeConstIterator niter;
1675 RouteList new_routes;
1677 nlist = node.children();
1681 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1683 boost::shared_ptr<Route> route;
1684 if (version < 3000) {
1685 route = XMLRouteFactory_2X (**niter, version);
1687 route = XMLRouteFactory (**niter, version);
1691 error << _("Session: cannot create Route from XML description.") << endmsg;
1695 BootMessage (string_compose (_("Loaded track/bus %1"), route->name()));
1697 new_routes.push_back (route);
1700 BootMessage (_("Tracks/busses loaded; Adding to Session"));
1702 add_routes (new_routes, false, false, false, PresentationInfo::max_order);
1704 BootMessage (_("Finished adding tracks/busses"));
1709 boost::shared_ptr<Route>
1710 Session::XMLRouteFactory (const XMLNode& node, int version)
1712 boost::shared_ptr<Route> ret;
1714 if (node.name() != "Route") {
1718 XMLNode* ds_child = find_named_node (node, X_("Diskstream"));
1720 DataType type = DataType::AUDIO;
1721 node.get_property("default-type", type);
1723 assert (type != DataType::NIL);
1727 boost::shared_ptr<Track> track;
1729 if (type == DataType::AUDIO) {
1730 track.reset (new AudioTrack (*this, X_("toBeResetFroXML")));
1732 track.reset (new MidiTrack (*this, X_("toBeResetFroXML")));
1735 if (track->init()) {
1739 if (track->set_state (node, version)) {
1743 BOOST_MARK_TRACK (track);
1747 PresentationInfo::Flag flags = PresentationInfo::get_flags (node);
1748 boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML"), flags));
1750 if (r->init () == 0 && r->set_state (node, version) == 0) {
1751 BOOST_MARK_ROUTE (r);
1759 boost::shared_ptr<Route>
1760 Session::XMLRouteFactory_2X (const XMLNode& node, int version)
1762 boost::shared_ptr<Route> ret;
1764 if (node.name() != "Route") {
1768 XMLProperty const * ds_prop = node.property (X_("diskstream-id"));
1770 ds_prop = node.property (X_("diskstream"));
1773 DataType type = DataType::AUDIO;
1774 node.get_property("default-type", type);
1776 assert (type != DataType::NIL);
1780 list<boost::shared_ptr<Diskstream> >::iterator i = _diskstreams_2X.begin ();
1781 while (i != _diskstreams_2X.end() && (*i)->id() != ds_prop->value()) {
1785 if (i == _diskstreams_2X.end()) {
1786 error << _("Could not find diskstream for route") << endmsg;
1787 return boost::shared_ptr<Route> ();
1790 boost::shared_ptr<Track> track;
1792 if (type == DataType::AUDIO) {
1793 track.reset (new AudioTrack (*this, X_("toBeResetFroXML")));
1795 track.reset (new MidiTrack (*this, X_("toBeResetFroXML")));
1798 if (track->init()) {
1802 if (track->set_state (node, version)) {
1806 track->set_diskstream (*i);
1808 BOOST_MARK_TRACK (track);
1812 PresentationInfo::Flag flags = PresentationInfo::get_flags (node);
1813 boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML"), flags));
1815 if (r->init () == 0 && r->set_state (node, version) == 0) {
1816 BOOST_MARK_ROUTE (r);
1825 Session::load_regions (const XMLNode& node)
1828 XMLNodeConstIterator niter;
1829 boost::shared_ptr<Region> region;
1831 nlist = node.children();
1835 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1836 if ((region = XMLRegionFactory (**niter, false)) == 0) {
1837 error << _("Session: cannot create Region from XML description.");
1838 XMLProperty const * name = (**niter).property("name");
1841 error << " " << string_compose (_("Can not load state for region '%1'"), name->value());
1852 Session::load_compounds (const XMLNode& node)
1854 XMLNodeList calist = node.children();
1855 XMLNodeConstIterator caiter;
1856 XMLProperty const * caprop;
1858 for (caiter = calist.begin(); caiter != calist.end(); ++caiter) {
1859 XMLNode* ca = *caiter;
1863 if ((caprop = ca->property (X_("original"))) == 0) {
1866 orig_id = caprop->value();
1868 if ((caprop = ca->property (X_("copy"))) == 0) {
1871 copy_id = caprop->value();
1873 boost::shared_ptr<Region> orig = RegionFactory::region_by_id (orig_id);
1874 boost::shared_ptr<Region> copy = RegionFactory::region_by_id (copy_id);
1876 if (!orig || !copy) {
1877 warning << string_compose (_("Regions in compound description not found (ID's %1 and %2): ignored"),
1883 RegionFactory::add_compound_association (orig, copy);
1890 Session::load_nested_sources (const XMLNode& node)
1893 XMLNodeConstIterator niter;
1895 nlist = node.children();
1897 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1898 if ((*niter)->name() == "Source") {
1900 /* it may already exist, so don't recreate it unnecessarily
1903 XMLProperty const * prop = (*niter)->property (X_("id"));
1905 error << _("Nested source has no ID info in session file! (ignored)") << endmsg;
1909 ID source_id (prop->value());
1911 if (!source_by_id (source_id)) {
1914 SourceFactory::create (*this, **niter, true);
1916 catch (failed_constructor& err) {
1917 error << string_compose (_("Cannot reconstruct nested source for region %1"), name()) << endmsg;
1924 boost::shared_ptr<Region>
1925 Session::XMLRegionFactory (const XMLNode& node, bool full)
1927 XMLProperty const * type = node.property("type");
1931 const XMLNodeList& nlist = node.children();
1933 for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
1934 XMLNode *child = (*niter);
1935 if (child->name() == "NestedSource") {
1936 load_nested_sources (*child);
1940 if (!type || type->value() == "audio") {
1941 return boost::shared_ptr<Region>(XMLAudioRegionFactory (node, full));
1942 } else if (type->value() == "midi") {
1943 return boost::shared_ptr<Region>(XMLMidiRegionFactory (node, full));
1946 } catch (failed_constructor& err) {
1947 return boost::shared_ptr<Region> ();
1950 return boost::shared_ptr<Region> ();
1953 boost::shared_ptr<AudioRegion>
1954 Session::XMLAudioRegionFactory (const XMLNode& node, bool /*full*/)
1956 XMLProperty const * prop;
1957 boost::shared_ptr<Source> source;
1958 boost::shared_ptr<AudioSource> as;
1960 SourceList master_sources;
1961 uint32_t nchans = 1;
1964 if (node.name() != X_("Region")) {
1965 return boost::shared_ptr<AudioRegion>();
1968 node.get_property (X_("channels"), nchans);
1970 if ((prop = node.property ("name")) == 0) {
1971 cerr << "no name for this region\n";
1975 if ((prop = node.property (X_("source-0"))) == 0) {
1976 if ((prop = node.property ("source")) == 0) {
1977 error << _("Session: XMLNode describing a AudioRegion is incomplete (no source)") << endmsg;
1978 return boost::shared_ptr<AudioRegion>();
1982 PBD::ID s_id (prop->value());
1984 if ((source = source_by_id (s_id)) == 0) {
1985 error << string_compose(_("Session: XMLNode describing a AudioRegion references an unknown source id =%1"), s_id) << endmsg;
1986 return boost::shared_ptr<AudioRegion>();
1989 as = boost::dynamic_pointer_cast<AudioSource>(source);
1991 error << string_compose(_("Session: XMLNode describing a AudioRegion references a non-audio source id =%1"), s_id) << endmsg;
1992 return boost::shared_ptr<AudioRegion>();
1995 sources.push_back (as);
1997 /* pickup other channels */
1999 for (uint32_t n=1; n < nchans; ++n) {
2000 snprintf (buf, sizeof(buf), X_("source-%d"), n);
2001 if ((prop = node.property (buf)) != 0) {
2003 PBD::ID id2 (prop->value());
2005 if ((source = source_by_id (id2)) == 0) {
2006 error << string_compose(_("Session: XMLNode describing a AudioRegion references an unknown source id =%1"), id2) << endmsg;
2007 return boost::shared_ptr<AudioRegion>();
2010 as = boost::dynamic_pointer_cast<AudioSource>(source);
2012 error << string_compose(_("Session: XMLNode describing a AudioRegion references a non-audio source id =%1"), id2) << endmsg;
2013 return boost::shared_ptr<AudioRegion>();
2015 sources.push_back (as);
2019 for (uint32_t n = 0; n < nchans; ++n) {
2020 snprintf (buf, sizeof(buf), X_("master-source-%d"), n);
2021 if ((prop = node.property (buf)) != 0) {
2023 PBD::ID id2 (prop->value());
2025 if ((source = source_by_id (id2)) == 0) {
2026 error << string_compose(_("Session: XMLNode describing a AudioRegion references an unknown source id =%1"), id2) << endmsg;
2027 return boost::shared_ptr<AudioRegion>();
2030 as = boost::dynamic_pointer_cast<AudioSource>(source);
2032 error << string_compose(_("Session: XMLNode describing a AudioRegion references a non-audio source id =%1"), id2) << endmsg;
2033 return boost::shared_ptr<AudioRegion>();
2035 master_sources.push_back (as);
2040 boost::shared_ptr<AudioRegion> region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (sources, node)));
2042 /* a final detail: this is the one and only place that we know how long missing files are */
2044 if (region->whole_file()) {
2045 for (SourceList::iterator sx = sources.begin(); sx != sources.end(); ++sx) {
2046 boost::shared_ptr<SilentFileSource> sfp = boost::dynamic_pointer_cast<SilentFileSource> (*sx);
2048 sfp->set_length (region->length());
2053 if (!master_sources.empty()) {
2054 if (master_sources.size() != nchans) {
2055 error << _("Session: XMLNode describing an AudioRegion is missing some master sources; ignored") << endmsg;
2057 region->set_master_sources (master_sources);
2065 catch (failed_constructor& err) {
2066 return boost::shared_ptr<AudioRegion>();
2070 boost::shared_ptr<MidiRegion>
2071 Session::XMLMidiRegionFactory (const XMLNode& node, bool /*full*/)
2073 XMLProperty const * prop;
2074 boost::shared_ptr<Source> source;
2075 boost::shared_ptr<MidiSource> ms;
2078 if (node.name() != X_("Region")) {
2079 return boost::shared_ptr<MidiRegion>();
2082 if ((prop = node.property ("name")) == 0) {
2083 cerr << "no name for this region\n";
2087 if ((prop = node.property (X_("source-0"))) == 0) {
2088 if ((prop = node.property ("source")) == 0) {
2089 error << _("Session: XMLNode describing a MidiRegion is incomplete (no source)") << endmsg;
2090 return boost::shared_ptr<MidiRegion>();
2094 PBD::ID s_id (prop->value());
2096 if ((source = source_by_id (s_id)) == 0) {
2097 error << string_compose(_("Session: XMLNode describing a MidiRegion references an unknown source id =%1"), s_id) << endmsg;
2098 return boost::shared_ptr<MidiRegion>();
2101 ms = boost::dynamic_pointer_cast<MidiSource>(source);
2103 error << string_compose(_("Session: XMLNode describing a MidiRegion references a non-midi source id =%1"), s_id) << endmsg;
2104 return boost::shared_ptr<MidiRegion>();
2107 sources.push_back (ms);
2110 boost::shared_ptr<MidiRegion> region (boost::dynamic_pointer_cast<MidiRegion> (RegionFactory::create (sources, node)));
2111 /* a final detail: this is the one and only place that we know how long missing files are */
2113 if (region->whole_file()) {
2114 for (SourceList::iterator sx = sources.begin(); sx != sources.end(); ++sx) {
2115 boost::shared_ptr<SilentFileSource> sfp = boost::dynamic_pointer_cast<SilentFileSource> (*sx);
2117 sfp->set_length (region->length());
2125 catch (failed_constructor& err) {
2126 return boost::shared_ptr<MidiRegion>();
2131 Session::get_sources_as_xml ()
2134 XMLNode* node = new XMLNode (X_("Sources"));
2135 Glib::Threads::Mutex::Lock lm (source_lock);
2137 for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
2138 node->add_child_nocopy (i->second->get_state());
2145 Session::reset_write_sources (bool mark_write_complete, bool force)
2147 boost::shared_ptr<RouteList> rl = routes.reader();
2148 for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
2149 boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*i);
2151 _state_of_the_state = StateOfTheState (_state_of_the_state|InCleanup);
2152 tr->reset_write_sources(mark_write_complete, force);
2153 _state_of_the_state = StateOfTheState (_state_of_the_state & ~InCleanup);
2159 Session::load_sources (const XMLNode& node)
2162 XMLNodeConstIterator niter;
2163 /* don't need this but it stops some
2164 * versions of gcc complaining about
2165 * discarded return values.
2167 boost::shared_ptr<Source> source;
2169 nlist = node.children();
2172 std::map<std::string, std::string> relocation;
2174 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2175 #ifdef PLATFORM_WINDOWS
2179 XMLNode srcnode (**niter);
2180 bool try_replace_abspath = true;
2184 #ifdef PLATFORM_WINDOWS
2185 // do not show "insert media" popups (files embedded from removable media).
2186 old_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
2188 if ((source = XMLSourceFactory (srcnode)) == 0) {
2189 error << _("Session: cannot create Source from XML description.") << endmsg;
2191 #ifdef PLATFORM_WINDOWS
2192 SetErrorMode(old_mode);
2195 } catch (MissingSource& err) {
2196 #ifdef PLATFORM_WINDOWS
2197 SetErrorMode(old_mode);
2200 /* try previous abs path replacements first */
2201 if (try_replace_abspath && Glib::path_is_absolute (err.path)) {
2202 std::string dir = Glib::path_get_dirname (err.path);
2203 std::map<std::string, std::string>::const_iterator rl = relocation.find (dir);
2204 if (rl != relocation.end ()) {
2205 std::string newpath = Glib::build_filename (rl->second, Glib::path_get_basename (err.path));
2206 if (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS)) {
2207 srcnode.set_property ("origin", newpath);
2208 try_replace_abspath = false;
2215 _missing_file_replacement = "";
2217 if (err.type == DataType::MIDI && Glib::path_is_absolute (err.path)) {
2218 error << string_compose (_("An external MIDI file is missing. %1 cannot currently recover from missing external MIDI files"),
2219 PROGRAM_NAME) << endmsg;
2223 if (!no_questions_about_missing_files) {
2224 user_choice = MissingFile (this, err.path, err.type).get_value_or (-1);
2229 switch (user_choice) {
2231 /* user added a new search location
2232 * or selected a new absolute path,
2234 if (Glib::path_is_absolute (err.path)) {
2235 if (!_missing_file_replacement.empty ()) {
2236 /* replace origin, in XML */
2237 std::string newpath = Glib::build_filename (
2238 _missing_file_replacement, Glib::path_get_basename (err.path));
2239 srcnode.set_property ("origin", newpath);
2240 relocation[Glib::path_get_dirname (err.path)] = _missing_file_replacement;
2241 _missing_file_replacement = "";
2248 /* user asked to quit the entire session load */
2252 no_questions_about_missing_files = true;
2256 no_questions_about_missing_files = true;
2263 case DataType::AUDIO:
2264 source = SourceFactory::createSilent (*this, **niter, max_framecnt, _current_frame_rate);
2267 case DataType::MIDI:
2268 /* The MIDI file is actually missing so
2269 * just create a new one in the same
2270 * location. Do not announce its
2274 if (!Glib::path_is_absolute (err.path)) {
2275 fullpath = Glib::build_filename (source_search_path (DataType::MIDI).front(), err.path);
2277 /* this should be an unrecoverable error: we would be creating a MIDI file outside
2282 /* Note that we do not announce the source just yet - we need to reset its ID before we do that */
2283 source = SourceFactory::createWritable (DataType::MIDI, *this, fullpath, false, _current_frame_rate, false, false);
2284 /* reset ID to match the missing one */
2285 source->set_id (**niter);
2286 /* Now we can announce it */
2287 SourceFactory::SourceCreated (source);
2298 boost::shared_ptr<Source>
2299 Session::XMLSourceFactory (const XMLNode& node)
2301 if (node.name() != "Source") {
2302 return boost::shared_ptr<Source>();
2306 /* note: do peak building in another thread when loading session state */
2307 return SourceFactory::create (*this, node, true);
2310 catch (failed_constructor& err) {
2311 error << string_compose (_("Found a sound file that cannot be used by %1. Talk to the programmers."), PROGRAM_NAME) << endmsg;
2312 return boost::shared_ptr<Source>();
2317 Session::save_template (string template_name, bool replace_existing)
2319 if ((_state_of_the_state & CannotSave) || template_name.empty ()) {
2323 bool absolute_path = Glib::path_is_absolute (template_name);
2325 /* directory to put the template in */
2326 std::string template_dir_path;
2328 if (!absolute_path) {
2329 std::string user_template_dir(user_template_directory());
2331 if (g_mkdir_with_parents (user_template_dir.c_str(), 0755) != 0) {
2332 error << string_compose(_("Could not create templates directory \"%1\" (%2)"),
2333 user_template_dir, g_strerror (errno)) << endmsg;
2337 template_dir_path = Glib::build_filename (user_template_dir, template_name);
2339 template_dir_path = template_name;
2342 if (!ARDOUR::Profile->get_trx()) {
2343 if (!replace_existing && Glib::file_test (template_dir_path, Glib::FILE_TEST_EXISTS)) {
2344 warning << string_compose(_("Template \"%1\" already exists - new version not created"),
2345 template_dir_path) << endmsg;
2349 if (g_mkdir_with_parents (template_dir_path.c_str(), 0755) != 0) {
2350 error << string_compose(_("Could not create directory for Session template\"%1\" (%2)"),
2351 template_dir_path, g_strerror (errno)) << endmsg;
2357 std::string template_file_path;
2359 if (ARDOUR::Profile->get_trx()) {
2360 template_file_path = template_name;
2362 if (absolute_path) {
2363 template_file_path = Glib::build_filename (template_dir_path, Glib::path_get_basename (template_dir_path) + template_suffix);
2365 template_file_path = Glib::build_filename (template_dir_path, template_name + template_suffix);
2369 SessionSaveUnderway (); /* EMIT SIGNAL */
2374 PBD::Unwinder<std::string> uw (_template_state_dir, template_dir_path);
2375 tree.set_root (&get_template());
2378 if (!tree.write (template_file_path)) {
2379 error << _("template not saved") << endmsg;
2383 store_recent_templates (template_file_path);
2389 Session::refresh_disk_space ()
2391 #if __APPLE__ || __FreeBSD__ || __NetBSD__ || (HAVE_SYS_VFS_H && HAVE_SYS_STATVFS_H)
2393 Glib::Threads::Mutex::Lock lm (space_lock);
2395 /* get freespace on every FS that is part of the session path */
2397 _total_free_4k_blocks = 0;
2398 _total_free_4k_blocks_uncertain = false;
2400 for (vector<space_and_path>::iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
2401 #if defined(__NetBSD__)
2402 struct statvfs statfsbuf;
2404 statvfs (i->path.c_str(), &statfsbuf);
2406 struct statfs statfsbuf;
2408 statfs (i->path.c_str(), &statfsbuf);
2410 double const scale = statfsbuf.f_bsize / 4096.0;
2412 /* See if this filesystem is read-only */
2413 struct statvfs statvfsbuf;
2414 statvfs (i->path.c_str(), &statvfsbuf);
2416 /* f_bavail can be 0 if it is undefined for whatever
2417 filesystem we are looking at; Samba shares mounted
2418 via GVFS are an example of this.
2420 if (statfsbuf.f_bavail == 0) {
2421 /* block count unknown */
2423 i->blocks_unknown = true;
2424 } else if (statvfsbuf.f_flag & ST_RDONLY) {
2425 /* read-only filesystem */
2427 i->blocks_unknown = false;
2429 /* read/write filesystem with known space */
2430 i->blocks = (uint32_t) floor (statfsbuf.f_bavail * scale);
2431 i->blocks_unknown = false;
2434 _total_free_4k_blocks += i->blocks;
2435 if (i->blocks_unknown) {
2436 _total_free_4k_blocks_uncertain = true;
2439 #elif defined PLATFORM_WINDOWS
2440 vector<string> scanned_volumes;
2441 vector<string>::iterator j;
2442 vector<space_and_path>::iterator i;
2443 DWORD nSectorsPerCluster, nBytesPerSector,
2444 nFreeClusters, nTotalClusters;
2448 _total_free_4k_blocks = 0;
2450 for (i = session_dirs.begin(); i != session_dirs.end(); i++) {
2451 strncpy (disk_drive, (*i).path.c_str(), 3);
2455 volume_found = false;
2456 if (0 != (GetDiskFreeSpace(disk_drive, &nSectorsPerCluster, &nBytesPerSector, &nFreeClusters, &nTotalClusters)))
2458 int64_t nBytesPerCluster = nBytesPerSector * nSectorsPerCluster;
2459 int64_t nFreeBytes = nBytesPerCluster * (int64_t)nFreeClusters;
2460 i->blocks = (uint32_t)(nFreeBytes / 4096);
2462 for (j = scanned_volumes.begin(); j != scanned_volumes.end(); j++) {
2463 if (0 == j->compare(disk_drive)) {
2464 volume_found = true;
2469 if (!volume_found) {
2470 scanned_volumes.push_back(disk_drive);
2471 _total_free_4k_blocks += i->blocks;
2476 if (0 == _total_free_4k_blocks) {
2477 strncpy (disk_drive, path().c_str(), 3);
2480 if (0 != (GetDiskFreeSpace(disk_drive, &nSectorsPerCluster, &nBytesPerSector, &nFreeClusters, &nTotalClusters)))
2482 int64_t nBytesPerCluster = nBytesPerSector * nSectorsPerCluster;
2483 int64_t nFreeBytes = nBytesPerCluster * (int64_t)nFreeClusters;
2484 _total_free_4k_blocks = (uint32_t)(nFreeBytes / 4096);
2491 Session::get_best_session_directory_for_new_audio ()
2493 vector<space_and_path>::iterator i;
2494 string result = _session_dir->root_path();
2496 /* handle common case without system calls */
2498 if (session_dirs.size() == 1) {
2502 /* OK, here's the algorithm we're following here:
2504 We want to select which directory to use for
2505 the next file source to be created. Ideally,
2506 we'd like to use a round-robin process so as to
2507 get maximum performance benefits from splitting
2508 the files across multiple disks.
2510 However, in situations without much diskspace, an
2511 RR approach may end up filling up a filesystem
2512 with new files while others still have space.
2513 Its therefore important to pay some attention to
2514 the freespace in the filesystem holding each
2515 directory as well. However, if we did that by
2516 itself, we'd keep creating new files in the file
2517 system with the most space until it was as full
2518 as all others, thus negating any performance
2519 benefits of this RAID-1 like approach.
2521 So, we use a user-configurable space threshold. If
2522 there are at least 2 filesystems with more than this
2523 much space available, we use RR selection between them.
2524 If not, then we pick the filesystem with the most space.
2526 This gets a good balance between the two
2530 refresh_disk_space ();
2532 int free_enough = 0;
2534 for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
2535 if ((*i).blocks * 4096 >= Config->get_disk_choice_space_threshold()) {
2540 if (free_enough >= 2) {
2541 /* use RR selection process, ensuring that the one
2545 i = last_rr_session_dir;
2548 if (++i == session_dirs.end()) {
2549 i = session_dirs.begin();
2552 if ((*i).blocks * 4096 >= Config->get_disk_choice_space_threshold()) {
2553 SessionDirectory sdir(i->path);
2554 if (sdir.create ()) {
2556 last_rr_session_dir = i;
2561 } while (i != last_rr_session_dir);
2565 /* pick FS with the most freespace (and that
2566 seems to actually work ...)
2569 vector<space_and_path> sorted;
2570 space_and_path_ascending_cmp cmp;
2572 sorted = session_dirs;
2573 sort (sorted.begin(), sorted.end(), cmp);
2575 for (i = sorted.begin(); i != sorted.end(); ++i) {
2576 SessionDirectory sdir(i->path);
2577 if (sdir.create ()) {
2579 last_rr_session_dir = i;
2589 Session::automation_dir () const
2591 return Glib::build_filename (_path, automation_dir_name);
2595 Session::analysis_dir () const
2597 return Glib::build_filename (_path, analysis_dir_name);
2601 Session::plugins_dir () const
2603 return Glib::build_filename (_path, plugins_dir_name);
2607 Session::externals_dir () const
2609 return Glib::build_filename (_path, externals_dir_name);
2613 Session::load_bundles (XMLNode const & node)
2615 XMLNodeList nlist = node.children();
2616 XMLNodeConstIterator niter;
2620 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2621 if ((*niter)->name() == "InputBundle") {
2622 add_bundle (boost::shared_ptr<UserBundle> (new UserBundle (**niter, true)));
2623 } else if ((*niter)->name() == "OutputBundle") {
2624 add_bundle (boost::shared_ptr<UserBundle> (new UserBundle (**niter, false)));
2626 error << string_compose(_("Unknown node \"%1\" found in Bundles list from session file"), (*niter)->name()) << endmsg;
2635 Session::load_route_groups (const XMLNode& node, int version)
2637 XMLNodeList nlist = node.children();
2638 XMLNodeConstIterator niter;
2642 if (version >= 3000) {
2644 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2645 if ((*niter)->name() == "RouteGroup") {
2646 RouteGroup* rg = new RouteGroup (*this, "");
2647 add_route_group (rg);
2648 rg->set_state (**niter, version);
2652 } else if (version < 3000) {
2654 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2655 if ((*niter)->name() == "EditGroup" || (*niter)->name() == "MixGroup") {
2656 RouteGroup* rg = new RouteGroup (*this, "");
2657 add_route_group (rg);
2658 rg->set_state (**niter, version);
2667 state_file_filter (const string &str, void* /*arg*/)
2669 return (str.length() > strlen(statefile_suffix) &&
2670 str.find (statefile_suffix) == (str.length() - strlen (statefile_suffix)));
2674 remove_end(string state)
2676 string statename(state);
2678 string::size_type start,end;
2679 if ((start = statename.find_last_of (G_DIR_SEPARATOR)) != string::npos) {
2680 statename = statename.substr (start+1);
2683 if ((end = statename.rfind(statefile_suffix)) == string::npos) {
2684 end = statename.length();
2687 return string(statename.substr (0, end));
2691 Session::possible_states (string path)
2693 vector<string> states;
2694 find_files_matching_filter (states, path, state_file_filter, 0, false, false);
2696 transform(states.begin(), states.end(), states.begin(), remove_end);
2698 sort (states.begin(), states.end());
2704 Session::possible_states () const
2706 return possible_states(_path);
2710 Session::new_route_group (const std::string& name)
2712 RouteGroup* rg = NULL;
2714 for (std::list<RouteGroup*>::const_iterator i = _route_groups.begin (); i != _route_groups.end (); ++i) {
2715 if ((*i)->name () == name) {
2722 rg = new RouteGroup (*this, name);
2723 add_route_group (rg);
2729 Session::add_route_group (RouteGroup* g)
2731 _route_groups.push_back (g);
2732 route_group_added (g); /* EMIT SIGNAL */
2734 g->RouteAdded.connect_same_thread (*this, boost::bind (&Session::route_added_to_route_group, this, _1, _2));
2735 g->RouteRemoved.connect_same_thread (*this, boost::bind (&Session::route_removed_from_route_group, this, _1, _2));
2736 g->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::route_group_property_changed, this, g));
2742 Session::remove_route_group (RouteGroup& rg)
2744 list<RouteGroup*>::iterator i;
2746 if ((i = find (_route_groups.begin(), _route_groups.end(), &rg)) != _route_groups.end()) {
2747 _route_groups.erase (i);
2750 route_group_removed (); /* EMIT SIGNAL */
2754 /** Set a new order for our route groups, without adding or removing any.
2755 * @param groups Route group list in the new order.
2758 Session::reorder_route_groups (list<RouteGroup*> groups)
2760 _route_groups = groups;
2762 route_groups_reordered (); /* EMIT SIGNAL */
2768 Session::route_group_by_name (string name)
2770 list<RouteGroup *>::iterator i;
2772 for (i = _route_groups.begin(); i != _route_groups.end(); ++i) {
2773 if ((*i)->name() == name) {
2781 Session::all_route_group() const
2783 return *_all_route_group;
2787 Session::add_commands (vector<Command*> const & cmds)
2789 for (vector<Command*>::const_iterator i = cmds.begin(); i != cmds.end(); ++i) {
2795 Session::add_command (Command* const cmd)
2797 assert (_current_trans);
2798 DEBUG_UNDO_HISTORY (
2799 string_compose ("Current Undo Transaction %1, adding command: %2",
2800 _current_trans->name (),
2802 _current_trans->add_command (cmd);
2805 PBD::StatefulDiffCommand*
2806 Session::add_stateful_diff_command (boost::shared_ptr<PBD::StatefulDestructible> sfd)
2808 PBD::StatefulDiffCommand* cmd = new PBD::StatefulDiffCommand (sfd);
2814 Session::begin_reversible_command (const string& name)
2816 begin_reversible_command (g_quark_from_string (name.c_str ()));
2819 /** Begin a reversible command using a GQuark to identify it.
2820 * begin_reversible_command() and commit_reversible_command() calls may be nested,
2821 * but there must be as many begin...()s as there are commit...()s.
2824 Session::begin_reversible_command (GQuark q)
2826 /* If nested begin/commit pairs are used, we create just one UndoTransaction
2827 to hold all the commands that are committed. This keeps the order of
2828 commands correct in the history.
2831 if (_current_trans == 0) {
2832 DEBUG_UNDO_HISTORY (string_compose (
2833 "Begin Reversible Command, new transaction: %1", g_quark_to_string (q)));
2835 /* start a new transaction */
2836 assert (_current_trans_quarks.empty ());
2837 _current_trans = new UndoTransaction();
2838 _current_trans->set_name (g_quark_to_string (q));
2840 DEBUG_UNDO_HISTORY (
2841 string_compose ("Begin Reversible Command, current transaction: %1",
2842 _current_trans->name ()));
2845 _current_trans_quarks.push_front (q);
2849 Session::abort_reversible_command ()
2851 if (_current_trans != 0) {
2852 DEBUG_UNDO_HISTORY (
2853 string_compose ("Abort Reversible Command: %1", _current_trans->name ()));
2854 _current_trans->clear();
2855 delete _current_trans;
2857 _current_trans_quarks.clear();
2862 Session::commit_reversible_command (Command *cmd)
2864 assert (_current_trans);
2865 assert (!_current_trans_quarks.empty ());
2870 DEBUG_UNDO_HISTORY (
2871 string_compose ("Current Undo Transaction %1, adding command: %2",
2872 _current_trans->name (),
2874 _current_trans->add_command (cmd);
2877 DEBUG_UNDO_HISTORY (
2878 string_compose ("Commit Reversible Command, current transaction: %1",
2879 _current_trans->name ()));
2881 _current_trans_quarks.pop_front ();
2883 if (!_current_trans_quarks.empty ()) {
2884 DEBUG_UNDO_HISTORY (
2885 string_compose ("Commit Reversible Command, transaction is not "
2886 "top-level, current transaction: %1",
2887 _current_trans->name ()));
2888 /* the transaction we're committing is not the top-level one */
2892 if (_current_trans->empty()) {
2893 /* no commands were added to the transaction, so just get rid of it */
2894 DEBUG_UNDO_HISTORY (
2895 string_compose ("Commit Reversible Command, No commands were "
2896 "added to current transaction: %1",
2897 _current_trans->name ()));
2898 delete _current_trans;
2903 gettimeofday (&now, 0);
2904 _current_trans->set_timestamp (now);
2906 _history.add (_current_trans);
2911 accept_all_audio_files (const string& path, void* /*arg*/)
2913 if (!Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR)) {
2917 if (!AudioFileSource::safe_audio_file_extension (path)) {
2925 accept_all_midi_files (const string& path, void* /*arg*/)
2927 if (!Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR)) {
2931 return ( (path.length() > 4 && path.find (".mid") != (path.length() - 4))
2932 || (path.length() > 4 && path.find (".smf") != (path.length() - 4))
2933 || (path.length() > 5 && path.find (".midi") != (path.length() - 5)));
2937 accept_all_state_files (const string& path, void* /*arg*/)
2939 if (!Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR)) {
2943 std::string const statefile_ext (statefile_suffix);
2944 if (path.length() >= statefile_ext.length()) {
2945 return (0 == path.compare (path.length() - statefile_ext.length(), statefile_ext.length(), statefile_ext));
2952 Session::find_all_sources (string path, set<string>& result)
2957 if (!tree.read (path)) {
2961 if ((node = find_named_node (*tree.root(), "Sources")) == 0) {
2966 XMLNodeConstIterator niter;
2968 nlist = node->children();
2972 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2974 XMLProperty const * prop;
2976 if ((prop = (*niter)->property (X_("type"))) == 0) {
2980 DataType type (prop->value());
2982 if ((prop = (*niter)->property (X_("name"))) == 0) {
2986 if (Glib::path_is_absolute (prop->value())) {
2987 /* external file, ignore */
2995 if (FileSource::find (*this, type, prop->value(), true, is_new, chan, found_path)) {
2996 result.insert (found_path);
3004 Session::find_all_sources_across_snapshots (set<string>& result, bool exclude_this_snapshot)
3006 vector<string> state_files;
3008 string this_snapshot_path;
3014 if (ripped[ripped.length()-1] == G_DIR_SEPARATOR) {
3015 ripped = ripped.substr (0, ripped.length() - 1);
3018 find_files_matching_filter (state_files, ripped, accept_all_state_files, (void *) 0, true, true);
3020 if (state_files.empty()) {
3025 this_snapshot_path = Glib::build_filename (_path, legalize_for_path (_current_snapshot_name));
3026 this_snapshot_path += statefile_suffix;
3028 for (vector<string>::iterator i = state_files.begin(); i != state_files.end(); ++i) {
3030 cerr << "Looking at snapshot " << (*i) << " ( with this = [" << this_snapshot_path << "])\n";
3032 if (exclude_this_snapshot && *i == this_snapshot_path) {
3033 cerr << "\texcluded\n";
3038 if (find_all_sources (*i, result) < 0) {
3046 struct RegionCounter {
3047 typedef std::map<PBD::ID,boost::shared_ptr<AudioSource> > AudioSourceList;
3048 AudioSourceList::iterator iter;
3049 boost::shared_ptr<Region> region;
3052 RegionCounter() : count (0) {}
3056 Session::ask_about_playlist_deletion (boost::shared_ptr<Playlist> p)
3058 boost::optional<int> r = AskAboutPlaylistDeletion (p);
3059 return r.get_value_or (1);
3063 Session::cleanup_regions ()
3065 bool removed = false;
3066 const RegionFactory::RegionMap& regions (RegionFactory::regions());
3068 for (RegionFactory::RegionMap::const_iterator i = regions.begin(); i != regions.end();) {
3070 uint32_t used = playlists->region_use_count (i->second);
3072 if (used == 0 && !i->second->automatic ()) {
3073 boost::weak_ptr<Region> w = i->second;
3076 RegionFactory::map_remove (w);
3083 // re-check to remove parent references of compound regions
3084 for (RegionFactory::RegionMap::const_iterator i = regions.begin(); i != regions.end();) {
3085 if (!(i->second->whole_file() && i->second->max_source_level() > 0)) {
3089 assert(boost::dynamic_pointer_cast<PlaylistSource>(i->second->source (0)) != 0);
3090 if (0 == playlists->region_use_count (i->second)) {
3091 boost::weak_ptr<Region> w = i->second;
3093 RegionFactory::map_remove (w);
3100 /* dump the history list */
3107 Session::can_cleanup_peakfiles () const
3109 if (deletion_in_progress()) {
3112 if (!_writable || (_state_of_the_state & CannotSave)) {
3113 warning << _("Cannot cleanup peak-files for read-only session.") << endmsg;
3116 if (record_status() == Recording) {
3117 error << _("Cannot cleanup peak-files while recording") << endmsg;
3124 Session::cleanup_peakfiles ()
3126 Glib::Threads::Mutex::Lock lm (peak_cleanup_lock, Glib::Threads::TRY_LOCK);
3131 assert (can_cleanup_peakfiles ());
3132 assert (!peaks_cleanup_in_progres());
3134 _state_of_the_state = StateOfTheState (_state_of_the_state | PeakCleanup);
3136 int timeout = 5000; // 5 seconds
3137 while (!SourceFactory::files_with_peaks.empty()) {
3138 Glib::usleep (1000);
3139 if (--timeout < 0) {
3140 warning << _("Timeout waiting for peak-file creation to terminate before cleanup, please try again later.") << endmsg;
3141 _state_of_the_state = StateOfTheState (_state_of_the_state & (~PeakCleanup));
3146 for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
3147 boost::shared_ptr<AudioSource> as;
3148 if ((as = boost::dynamic_pointer_cast<AudioSource> (i->second)) != 0) {
3149 as->close_peakfile();
3153 PBD::clear_directory (session_directory().peak_path());
3155 _state_of_the_state = StateOfTheState (_state_of_the_state & (~PeakCleanup));
3157 for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
3158 boost::shared_ptr<AudioSource> as;
3159 if ((as = boost::dynamic_pointer_cast<AudioSource> (i->second)) != 0) {
3160 SourceFactory::setup_peakfile(as, true);
3167 merge_all_sources (boost::shared_ptr<const Playlist> pl, std::set<boost::shared_ptr<Source> >* all_sources)
3169 pl->deep_sources (*all_sources);
3173 Session::cleanup_sources (CleanupReport& rep)
3175 // FIXME: needs adaptation to midi
3177 vector<boost::shared_ptr<Source> > dead_sources;
3180 vector<string> candidates;
3181 vector<string> unused;
3182 set<string> sources_used_by_all_snapshots;
3189 set<boost::shared_ptr<Source> > sources_used_by_this_snapshot;
3191 _state_of_the_state = (StateOfTheState) (_state_of_the_state | InCleanup);
3193 /* this is mostly for windows which doesn't allow file
3194 * renaming if the file is in use. But we don't special
3195 * case it because we need to know if this causes
3196 * problems, and the easiest way to notice that is to
3197 * keep it in place for all platforms.
3200 request_stop (false);
3202 _butler->wait_until_finished ();
3204 /* consider deleting all unused playlists */
3206 if (playlists->maybe_delete_unused (boost::bind (Session::ask_about_playlist_deletion, _1))) {
3211 /* sync the "all regions" property of each playlist with its current state */
3213 playlists->sync_all_regions_with_regions ();
3215 /* find all un-used sources */
3220 for (SourceMap::iterator i = sources.begin(); i != sources.end(); ) {
3222 SourceMap::iterator tmp;
3227 /* do not bother with files that are zero size, otherwise we remove the current "nascent"
3231 if (!i->second->used() && (i->second->length(i->second->timeline_position()) > 0)) {
3232 dead_sources.push_back (i->second);
3233 i->second->drop_references ();
3239 /* build a list of all the possible audio directories for the session */
3241 for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
3242 SessionDirectory sdir ((*i).path);
3243 asp += sdir.sound_path();
3245 audio_path += asp.to_string();
3248 /* build a list of all the possible midi directories for the session */
3250 for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
3251 SessionDirectory sdir ((*i).path);
3252 msp += sdir.midi_path();
3254 midi_path += msp.to_string();
3256 find_files_matching_filter (candidates, audio_path, accept_all_audio_files, (void *) 0, true, true);
3257 find_files_matching_filter (candidates, midi_path, accept_all_midi_files, (void *) 0, true, true);
3259 /* add sources from all other snapshots as "used", but don't use this
3260 snapshot because the state file on disk still references sources we
3261 may have already dropped.
3264 find_all_sources_across_snapshots (sources_used_by_all_snapshots, true);
3266 /* Although the region factory has a list of all regions ever created
3267 * for this session, we're only interested in regions actually in
3268 * playlists right now. So merge all playlist regions lists together.
3270 * This will include the playlists used within compound regions.
3273 playlists->foreach (boost::bind (merge_all_sources, _1, &sources_used_by_this_snapshot));
3275 /* add our current source list
3278 for (SourceMap::iterator i = sources.begin(); i != sources.end(); ) {
3279 boost::shared_ptr<FileSource> fs;
3280 SourceMap::iterator tmp = i;
3283 if ((fs = boost::dynamic_pointer_cast<FileSource> (i->second)) == 0) {
3289 /* this is mostly for windows which doesn't allow file
3290 * renaming if the file is in use. But we do not special
3291 * case it because we need to know if this causes
3292 * problems, and the easiest way to notice that is to
3293 * keep it in place for all platforms.
3298 if (!fs->is_stub()) {
3300 /* Note that we're checking a list of all
3301 * sources across all snapshots with the list
3302 * of sources used by this snapshot.
3305 if (sources_used_by_this_snapshot.find (i->second) != sources_used_by_this_snapshot.end()) {
3306 /* this source is in use by this snapshot */
3307 sources_used_by_all_snapshots.insert (fs->path());
3308 cerr << "Source from source list found in used_by_this_snapshot (" << fs->path() << ")\n";
3310 cerr << "Source from source list NOT found in used_by_this_snapshot (" << fs->path() << ")\n";
3311 /* this source is NOT in use by this snapshot */
3313 /* remove all related regions from RegionFactory master list */
3315 RegionFactory::remove_regions_using_source (i->second);
3317 /* remove from our current source list
3318 * also. We may not remove it from
3319 * disk, because it may be used by
3320 * other snapshots, but it isn't used inside this
3321 * snapshot anymore, so we don't need a
3332 /* now check each candidate source to see if it exists in the list of
3333 * sources_used_by_all_snapshots. If it doesn't, put it into "unused".
3336 cerr << "Candidates: " << candidates.size() << endl;
3337 cerr << "Used by others: " << sources_used_by_all_snapshots.size() << endl;
3339 for (vector<string>::iterator x = candidates.begin(); x != candidates.end(); ++x) {
3344 for (set<string>::iterator i = sources_used_by_all_snapshots.begin(); i != sources_used_by_all_snapshots.end(); ++i) {
3346 tmppath1 = canonical_path (spath);
3347 tmppath2 = canonical_path ((*i));
3349 cerr << "\t => " << tmppath2 << endl;
3351 if (tmppath1 == tmppath2) {
3358 unused.push_back (spath);
3362 cerr << "Actually unused: " << unused.size() << endl;
3364 if (unused.empty()) {
3370 /* now try to move all unused files into the "dead" directory(ies) */
3372 for (vector<string>::iterator x = unused.begin(); x != unused.end(); ++x) {
3377 /* don't move the file across filesystems, just
3378 * stick it in the `dead_dir_name' directory
3379 * on whichever filesystem it was already on.
3382 if ((*x).find ("/sounds/") != string::npos) {
3384 /* old school, go up 1 level */
3386 newpath = Glib::path_get_dirname (*x); // "sounds"
3387 newpath = Glib::path_get_dirname (newpath); // "session-name"
3391 /* new school, go up 4 levels */
3393 newpath = Glib::path_get_dirname (*x); // "audiofiles" or "midifiles"
3394 newpath = Glib::path_get_dirname (newpath); // "session-name"
3395 newpath = Glib::path_get_dirname (newpath); // "interchange"
3396 newpath = Glib::path_get_dirname (newpath); // "session-dir"
3399 newpath = Glib::build_filename (newpath, dead_dir_name);
3401 if (g_mkdir_with_parents (newpath.c_str(), 0755) < 0) {
3402 error << string_compose(_("Session: cannot create dead file folder \"%1\" (%2)"), newpath, strerror (errno)) << endmsg;
3406 newpath = Glib::build_filename (newpath, Glib::path_get_basename ((*x)));
3408 if (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS)) {
3410 /* the new path already exists, try versioning */
3412 char buf[PATH_MAX+1];
3416 snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), version);
3419 while (Glib::file_test (newpath_v.c_str(), Glib::FILE_TEST_EXISTS) && version < 999) {
3420 snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), ++version);
3424 if (version == 999) {
3425 error << string_compose (_("there are already 1000 files with names like %1; versioning discontinued"),
3429 newpath = newpath_v;
3434 if ((g_stat ((*x).c_str(), &statbuf) != 0) || (::g_rename ((*x).c_str(), newpath.c_str()) != 0)) {
3435 error << string_compose (_("cannot rename unused file source from %1 to %2 (%3)"), (*x),
3436 newpath, g_strerror (errno)) << endmsg;
3440 /* see if there an easy to find peakfile for this file, and remove it. */
3442 string base = Glib::path_get_basename (*x);
3443 base += "%A"; /* this is what we add for the channel suffix of all native files,
3444 * or for the first channel of embedded files. it will miss
3445 * some peakfiles for other channels
3447 string peakpath = construct_peak_filepath (base);
3449 if (Glib::file_test (peakpath.c_str (), Glib::FILE_TEST_EXISTS)) {
3450 if (::g_unlink (peakpath.c_str ()) != 0) {
3451 error << string_compose (_("cannot remove peakfile %1 for %2 (%3)"), peakpath, _path,
3452 g_strerror (errno)) << endmsg;
3453 /* try to back out */
3454 ::g_rename (newpath.c_str (), _path.c_str ());
3459 rep.paths.push_back (*x);
3460 rep.space += statbuf.st_size;
3463 /* dump the history list */
3467 /* save state so we don't end up a session file
3468 * referring to non-existent sources.
3475 _state_of_the_state = (StateOfTheState) (_state_of_the_state & ~InCleanup);
3481 Session::cleanup_trash_sources (CleanupReport& rep)
3483 // FIXME: needs adaptation for MIDI
3485 vector<space_and_path>::iterator i;
3491 for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
3493 dead_dir = Glib::build_filename ((*i).path, dead_dir_name);
3495 clear_directory (dead_dir, &rep.space, &rep.paths);
3502 Session::set_dirty ()
3504 /* return early if there's nothing to do */
3509 /* never mark session dirty during loading */
3510 if (_state_of_the_state & Loading) {
3514 _state_of_the_state = StateOfTheState (_state_of_the_state | Dirty);
3515 DirtyChanged(); /* EMIT SIGNAL */
3519 Session::set_clean ()
3521 bool was_dirty = dirty();
3523 _state_of_the_state = Clean;
3526 DirtyChanged(); /* EMIT SIGNAL */
3531 Session::set_deletion_in_progress ()
3533 _state_of_the_state = StateOfTheState (_state_of_the_state | Deletion);
3537 Session::clear_deletion_in_progress ()
3539 _state_of_the_state = StateOfTheState (_state_of_the_state & (~Deletion));
3543 Session::add_controllable (boost::shared_ptr<Controllable> c)
3545 /* this adds a controllable to the list managed by the Session.
3546 this is a subset of those managed by the Controllable class
3547 itself, and represents the only ones whose state will be saved
3548 as part of the session.
3551 Glib::Threads::Mutex::Lock lm (controllables_lock);
3552 controllables.insert (c);
3555 struct null_deleter { void operator()(void const *) const {} };
3558 Session::remove_controllable (Controllable* c)
3560 if (_state_of_the_state & Deletion) {
3564 Glib::Threads::Mutex::Lock lm (controllables_lock);
3566 Controllables::iterator x = controllables.find (boost::shared_ptr<Controllable>(c, null_deleter()));
3568 if (x != controllables.end()) {
3569 controllables.erase (x);
3573 boost::shared_ptr<Controllable>
3574 Session::controllable_by_id (const PBD::ID& id)
3576 Glib::Threads::Mutex::Lock lm (controllables_lock);
3578 for (Controllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
3579 if ((*i)->id() == id) {
3584 return boost::shared_ptr<Controllable>();
3587 boost::shared_ptr<Controllable>
3588 Session::controllable_by_descriptor (const ControllableDescriptor& desc)
3590 boost::shared_ptr<Controllable> c;
3591 boost::shared_ptr<Stripable> s;
3592 boost::shared_ptr<Route> r;
3594 switch (desc.top_level_type()) {
3595 case ControllableDescriptor::NamedRoute:
3597 std::string str = desc.top_level_name();
3599 if (str == "Master" || str == "master") {
3601 } else if (str == "control" || str == "listen" || str == "monitor" || str == "Monitor") {
3603 } else if (str == "auditioner") {
3606 s = route_by_name (desc.top_level_name());
3612 case ControllableDescriptor::PresentationOrderRoute:
3613 s = get_remote_nth_stripable (desc.presentation_order(), PresentationInfo::Route);
3616 case ControllableDescriptor::PresentationOrderTrack:
3617 s = get_remote_nth_stripable (desc.presentation_order(), PresentationInfo::Track);
3620 case ControllableDescriptor::PresentationOrderBus:
3621 s = get_remote_nth_stripable (desc.presentation_order(), PresentationInfo::Bus);
3624 case ControllableDescriptor::PresentationOrderVCA:
3625 s = get_remote_nth_stripable (desc.presentation_order(), PresentationInfo::VCA);
3628 case ControllableDescriptor::SelectionCount:
3629 s = route_by_selected_count (desc.selection_id());
3637 r = boost::dynamic_pointer_cast<Route> (s);
3639 switch (desc.subtype()) {
3640 case ControllableDescriptor::Gain:
3641 c = s->gain_control ();
3644 case ControllableDescriptor::Trim:
3645 c = s->trim_control ();
3648 case ControllableDescriptor::Solo:
3649 c = s->solo_control();
3652 case ControllableDescriptor::Mute:
3653 c = s->mute_control();
3656 case ControllableDescriptor::Recenable:
3657 c = s->rec_enable_control ();
3660 case ControllableDescriptor::PanDirection:
3661 c = s->pan_azimuth_control();
3664 case ControllableDescriptor::PanWidth:
3665 c = s->pan_width_control();
3668 case ControllableDescriptor::PanElevation:
3669 c = s->pan_elevation_control();
3672 case ControllableDescriptor::Balance:
3673 /* XXX simple pan control */
3676 case ControllableDescriptor::PluginParameter:
3678 uint32_t plugin = desc.target (0);
3679 uint32_t parameter_index = desc.target (1);
3681 /* revert to zero based counting */
3687 if (parameter_index > 0) {
3695 boost::shared_ptr<Processor> p = r->nth_plugin (plugin);
3698 c = boost::dynamic_pointer_cast<ARDOUR::AutomationControl>(
3699 p->control(Evoral::Parameter(PluginAutomation, 0, parameter_index)));
3704 case ControllableDescriptor::SendGain: {
3705 uint32_t send = desc.target (0);
3712 c = r->send_level_controllable (send);
3717 /* relax and return a null pointer */
3725 Session::add_instant_xml (XMLNode& node, bool write_to_config)
3728 Stateful::add_instant_xml (node, _path);
3731 if (write_to_config) {
3732 Config->add_instant_xml (node);
3737 Session::instant_xml (const string& node_name)
3739 #ifdef MIXBUS // "Safe Mode" (shift + click open) -> also ignore instant.xml
3740 if (get_disable_all_loaded_plugins ()) {
3744 return Stateful::instant_xml (node_name, _path);
3748 Session::save_history (string snapshot_name)
3756 if (!Config->get_save_history() || Config->get_saved_history_depth() < 0 ||
3757 (_history.undo_depth() == 0 && _history.redo_depth() == 0)) {
3761 if (snapshot_name.empty()) {
3762 snapshot_name = _current_snapshot_name;
3765 const string history_filename = legalize_for_path (snapshot_name) + history_suffix;
3766 const string backup_filename = history_filename + backup_suffix;
3767 const std::string xml_path(Glib::build_filename (_session_dir->root_path(), history_filename));
3768 const std::string backup_path(Glib::build_filename (_session_dir->root_path(), backup_filename));
3770 if (Glib::file_test (xml_path, Glib::FILE_TEST_EXISTS)) {
3771 if (::g_rename (xml_path.c_str(), backup_path.c_str()) != 0) {
3772 error << _("could not backup old history file, current history not saved") << endmsg;
3777 tree.set_root (&_history.get_state (Config->get_saved_history_depth()));
3779 if (!tree.write (xml_path))
3781 error << string_compose (_("history could not be saved to %1"), xml_path) << endmsg;
3783 if (g_remove (xml_path.c_str()) != 0) {
3784 error << string_compose(_("Could not remove history file at path \"%1\" (%2)"),
3785 xml_path, g_strerror (errno)) << endmsg;
3787 if (::g_rename (backup_path.c_str(), xml_path.c_str()) != 0) {
3788 error << string_compose (_("could not restore history file from backup %1 (%2)"),
3789 backup_path, g_strerror (errno)) << endmsg;
3799 Session::restore_history (string snapshot_name)
3803 if (snapshot_name.empty()) {
3804 snapshot_name = _current_snapshot_name;
3807 const std::string xml_filename = legalize_for_path (snapshot_name) + history_suffix;
3808 const std::string xml_path(Glib::build_filename (_session_dir->root_path(), xml_filename));
3810 info << "Loading history from " << xml_path << endmsg;
3812 if (!Glib::file_test (xml_path, Glib::FILE_TEST_EXISTS)) {
3813 info << string_compose (_("%1: no history file \"%2\" for this session."),
3814 _name, xml_path) << endmsg;
3818 if (!tree.read (xml_path)) {
3819 error << string_compose (_("Could not understand session history file \"%1\""),
3820 xml_path) << endmsg;
3827 for (XMLNodeConstIterator it = tree.root()->children().begin(); it != tree.root()->children().end(); ++it) {
3830 UndoTransaction* ut = new UndoTransaction ();
3836 if (!t->get_property ("name", name) || !t->get_property ("tv-sec", tv_sec) ||
3837 !t->get_property ("tv-usec", tv_usec)) {
3841 ut->set_name (name);
3845 tv.tv_usec = tv_usec;
3846 ut->set_timestamp(tv);
3848 for (XMLNodeConstIterator child_it = t->children().begin();
3849 child_it != t->children().end(); child_it++)
3851 XMLNode *n = *child_it;
3854 if (n->name() == "MementoCommand" ||
3855 n->name() == "MementoUndoCommand" ||
3856 n->name() == "MementoRedoCommand") {
3858 if ((c = memento_command_factory(n))) {
3862 } else if (n->name() == "NoteDiffCommand") {
3863 PBD::ID id (n->property("midi-source")->value());
3864 boost::shared_ptr<MidiSource> midi_source =
3865 boost::dynamic_pointer_cast<MidiSource, Source>(source_by_id(id));
3867 ut->add_command (new MidiModel::NoteDiffCommand(midi_source->model(), *n));
3869 error << _("Failed to downcast MidiSource for NoteDiffCommand") << endmsg;
3872 } else if (n->name() == "SysExDiffCommand") {
3874 PBD::ID id (n->property("midi-source")->value());
3875 boost::shared_ptr<MidiSource> midi_source =
3876 boost::dynamic_pointer_cast<MidiSource, Source>(source_by_id(id));
3878 ut->add_command (new MidiModel::SysExDiffCommand (midi_source->model(), *n));
3880 error << _("Failed to downcast MidiSource for SysExDiffCommand") << endmsg;
3883 } else if (n->name() == "PatchChangeDiffCommand") {
3885 PBD::ID id (n->property("midi-source")->value());
3886 boost::shared_ptr<MidiSource> midi_source =
3887 boost::dynamic_pointer_cast<MidiSource, Source>(source_by_id(id));
3889 ut->add_command (new MidiModel::PatchChangeDiffCommand (midi_source->model(), *n));
3891 error << _("Failed to downcast MidiSource for PatchChangeDiffCommand") << endmsg;
3894 } else if (n->name() == "StatefulDiffCommand") {
3895 if ((c = stateful_diff_command_factory (n))) {
3896 ut->add_command (c);
3899 error << string_compose(_("Couldn't figure out how to make a Command out of a %1 XMLNode."), n->name()) << endmsg;
3910 Session::config_changed (std::string p, bool ours)
3916 if (p == "seamless-loop") {
3918 } else if (p == "rf-speed") {
3920 } else if (p == "auto-loop") {
3922 } else if (p == "session-monitoring") {
3924 } else if (p == "auto-input") {
3926 if (Config->get_monitoring_model() == HardwareMonitoring && transport_rolling()) {
3927 /* auto-input only makes a difference if we're rolling */
3928 set_track_monitor_input_status (!config.get_auto_input());
3931 } else if (p == "punch-in") {
3935 if ((location = _locations->auto_punch_location()) != 0) {
3937 if (config.get_punch_in ()) {
3938 replace_event (SessionEvent::PunchIn, location->start());
3940 remove_event (location->start(), SessionEvent::PunchIn);
3944 } else if (p == "punch-out") {
3948 if ((location = _locations->auto_punch_location()) != 0) {
3950 if (config.get_punch_out()) {
3951 replace_event (SessionEvent::PunchOut, location->end());
3953 clear_events (SessionEvent::PunchOut);
3957 } else if (p == "edit-mode") {
3959 Glib::Threads::Mutex::Lock lm (playlists->lock);
3961 for (SessionPlaylists::List::iterator i = playlists->playlists.begin(); i != playlists->playlists.end(); ++i) {
3962 (*i)->set_edit_mode (Config->get_edit_mode ());
3965 } else if (p == "use-video-sync") {
3967 waiting_for_sync_offset = config.get_use_video_sync();
3969 } else if (p == "mmc-control") {
3971 //poke_midi_thread ();
3973 } else if (p == "mmc-device-id" || p == "mmc-receive-id" || p == "mmc-receive-device-id") {
3975 _mmc->set_receive_device_id (Config->get_mmc_receive_device_id());
3977 } else if (p == "mmc-send-id" || p == "mmc-send-device-id") {
3979 _mmc->set_send_device_id (Config->get_mmc_send_device_id());
3981 } else if (p == "midi-control") {
3983 //poke_midi_thread ();
3985 } else if (p == "raid-path") {
3987 setup_raid_path (config.get_raid_path());
3989 } else if (p == "timecode-format") {
3993 } else if (p == "video-pullup") {
3997 } else if (p == "seamless-loop") {
3999 if (play_loop && transport_rolling()) {
4000 // to reset diskstreams etc
4001 request_play_loop (true);
4004 } else if (p == "rf-speed") {
4006 cumulative_rf_motion = 0;
4009 } else if (p == "click-sound") {
4011 setup_click_sounds (1);
4013 } else if (p == "click-emphasis-sound") {
4015 setup_click_sounds (-1);
4017 } else if (p == "clicking") {
4019 if (Config->get_clicking()) {
4020 if (_click_io && click_data) { // don't require emphasis data
4027 } else if (p == "click-record-only") {
4029 _click_rec_only = Config->get_click_record_only();
4031 } else if (p == "click-gain") {
4034 _click_gain->gain_control()->set_value (Config->get_click_gain(), Controllable::NoGroup);
4037 } else if (p == "send-mtc") {
4039 if (Config->get_send_mtc ()) {
4040 /* mark us ready to send */
4041 next_quarter_frame_to_send = 0;
4044 } else if (p == "send-mmc") {
4046 _mmc->enable_send (Config->get_send_mmc ());
4048 } else if (p == "jack-time-master") {
4050 engine().reset_timebase ();
4052 } else if (p == "native-file-header-format") {
4054 if (!first_file_header_format_reset) {
4055 reset_native_file_format ();
4058 first_file_header_format_reset = false;
4060 } else if (p == "native-file-data-format") {
4062 if (!first_file_data_format_reset) {
4063 reset_native_file_format ();
4066 first_file_data_format_reset = false;
4068 } else if (p == "external-sync") {
4069 if (!config.get_external_sync()) {
4070 drop_sync_source ();
4072 switch_to_sync_source (Config->get_sync_source());
4074 } else if (p == "denormal-model") {
4076 } else if (p == "history-depth") {
4077 set_history_depth (Config->get_history_depth());
4078 } else if (p == "remote-model") {
4079 /* XXX DO SOMETHING HERE TO TELL THE GUI THAT WE NEED
4082 } else if (p == "initial-program-change") {
4084 if (_mmc->output_port() && Config->get_initial_program_change() >= 0) {
4087 buf[0] = MIDI::program; // channel zero by default
4088 buf[1] = (Config->get_initial_program_change() & 0x7f);
4090 _mmc->output_port()->midimsg (buf, sizeof (buf), 0);
4092 } else if (p == "solo-mute-override") {
4093 // catch_up_on_solo_mute_override ();
4094 } else if (p == "listen-position" || p == "pfl-position") {
4095 listen_position_changed ();
4096 } else if (p == "solo-control-is-listen-control") {
4097 solo_control_mode_changed ();
4098 } else if (p == "solo-mute-gain") {
4099 _solo_cut_control->Changed (true, Controllable::NoGroup);
4100 } else if (p == "timecode-offset" || p == "timecode-offset-negative") {
4101 last_timecode_valid = false;
4102 } else if (p == "playback-buffer-seconds") {
4103 AudioSource::allocate_working_buffers (frame_rate());
4104 } else if (p == "ltc-source-port") {
4105 reconnect_ltc_input ();
4106 } else if (p == "ltc-sink-port") {
4107 reconnect_ltc_output ();
4108 } else if (p == "timecode-generator-offset") {
4109 ltc_tx_parse_offset();
4110 } else if (p == "auto-return-target-list") {
4111 follow_playhead_priority ();
4118 Session::set_history_depth (uint32_t d)
4120 _history.set_depth (d);
4124 Session::load_diskstreams_2X (XMLNode const & node, int)
4127 XMLNodeConstIterator citer;
4129 clist = node.children();
4131 for (citer = clist.begin(); citer != clist.end(); ++citer) {
4134 /* diskstreams added automatically by DiskstreamCreated handler */
4135 if ((*citer)->name() == "AudioDiskstream" || (*citer)->name() == "DiskStream") {
4136 boost::shared_ptr<AudioDiskstream> dsp (new AudioDiskstream (*this, **citer));
4137 _diskstreams_2X.push_back (dsp);
4139 error << _("Session: unknown diskstream type in XML") << endmsg;
4143 catch (failed_constructor& err) {
4144 error << _("Session: could not load diskstream via XML state") << endmsg;
4152 /** Connect things to the MMC object */
4154 Session::setup_midi_machine_control ()
4156 _mmc = new MIDI::MachineControl;
4158 boost::shared_ptr<AsyncMIDIPort> async_in = boost::dynamic_pointer_cast<AsyncMIDIPort> (_midi_ports->mmc_input_port());
4159 boost::shared_ptr<AsyncMIDIPort> async_out = boost::dynamic_pointer_cast<AsyncMIDIPort> (_midi_ports->mmc_output_port());
4161 if (!async_out || !async_out) {
4165 /* XXXX argh, passing raw pointers back into libmidi++ */
4167 MIDI::Port* mmc_in = async_in.get();
4168 MIDI::Port* mmc_out = async_out.get();
4170 _mmc->set_ports (mmc_in, mmc_out);
4172 _mmc->Play.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
4173 _mmc->DeferredPlay.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
4174 _mmc->Stop.connect_same_thread (*this, boost::bind (&Session::mmc_stop, this, _1));
4175 _mmc->FastForward.connect_same_thread (*this, boost::bind (&Session::mmc_fast_forward, this, _1));
4176 _mmc->Rewind.connect_same_thread (*this, boost::bind (&Session::mmc_rewind, this, _1));
4177 _mmc->Pause.connect_same_thread (*this, boost::bind (&Session::mmc_pause, this, _1));
4178 _mmc->RecordPause.connect_same_thread (*this, boost::bind (&Session::mmc_record_pause, this, _1));
4179 _mmc->RecordStrobe.connect_same_thread (*this, boost::bind (&Session::mmc_record_strobe, this, _1));
4180 _mmc->RecordExit.connect_same_thread (*this, boost::bind (&Session::mmc_record_exit, this, _1));
4181 _mmc->Locate.connect_same_thread (*this, boost::bind (&Session::mmc_locate, this, _1, _2));
4182 _mmc->Step.connect_same_thread (*this, boost::bind (&Session::mmc_step, this, _1, _2));
4183 _mmc->Shuttle.connect_same_thread (*this, boost::bind (&Session::mmc_shuttle, this, _1, _2, _3));
4184 _mmc->TrackRecordStatusChange.connect_same_thread (*this, boost::bind (&Session::mmc_record_enable, this, _1, _2, _3));
4186 /* also handle MIDI SPP because its so common */
4188 _mmc->SPPStart.connect_same_thread (*this, boost::bind (&Session::spp_start, this));
4189 _mmc->SPPContinue.connect_same_thread (*this, boost::bind (&Session::spp_continue, this));
4190 _mmc->SPPStop.connect_same_thread (*this, boost::bind (&Session::spp_stop, this));
4193 boost::shared_ptr<Controllable>
4194 Session::solo_cut_control() const
4196 /* the solo cut control is a bit of an anomaly, at least as of Febrary 2011. There are no other
4197 * controls in Ardour that currently get presented to the user in the GUI that require
4198 * access as a Controllable and are also NOT owned by some SessionObject (e.g. Route, or MonitorProcessor).
4200 * its actually an RCConfiguration parameter, so we use a ProxyControllable to wrap
4201 * it up as a Controllable. Changes to the Controllable will just map back to the RCConfiguration
4204 return _solo_cut_control;
4208 Session::save_snapshot_name (const std::string & n)
4210 /* assure Stateful::_instant_xml is loaded
4211 * add_instant_xml() only adds to existing data and defaults
4212 * to use an empty Tree otherwise
4214 instant_xml ("LastUsedSnapshot");
4216 XMLNode* last_used_snapshot = new XMLNode ("LastUsedSnapshot");
4217 last_used_snapshot->set_property ("name", n);
4218 add_instant_xml (*last_used_snapshot, false);
4222 Session::set_snapshot_name (const std::string & n)
4224 _current_snapshot_name = n;
4225 save_snapshot_name (n);
4229 Session::rename (const std::string& new_name)
4231 string legal_name = legalize_for_path (new_name);
4237 string const old_sources_root = _session_dir->sources_root();
4239 if (!_writable || (_state_of_the_state & CannotSave)) {
4240 error << _("Cannot rename read-only session.") << endmsg;
4241 return 0; // don't show "messed up" warning
4243 if (record_status() == Recording) {
4244 error << _("Cannot rename session while recording") << endmsg;
4245 return 0; // don't show "messed up" warning
4248 StateProtector stp (this);
4253 * interchange subdirectory
4257 * Backup files are left unchanged and not renamed.
4260 /* Windows requires that we close all files before attempting the
4261 * rename. This works on other platforms, but isn't necessary there.
4262 * Leave it in place for all platforms though, since it may help
4263 * catch issues that could arise if the way Source files work ever
4264 * change (since most developers are not using Windows).
4267 for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) {
4268 boost::shared_ptr<FileSource> fs = boost::dynamic_pointer_cast<FileSource> (i->second);
4274 /* pass one: not 100% safe check that the new directory names don't
4278 for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
4282 /* this is a stupid hack because Glib::path_get_dirname() is
4283 * lexical-only, and so passing it /a/b/c/ gives a different
4284 * result than passing it /a/b/c ...
4287 if (oldstr[oldstr.length()-1] == G_DIR_SEPARATOR) {
4288 oldstr = oldstr.substr (0, oldstr.length() - 1);
4291 string base = Glib::path_get_dirname (oldstr);
4293 newstr = Glib::build_filename (base, legal_name);
4295 cerr << "Looking for " << newstr << endl;
4297 if (Glib::file_test (newstr, Glib::FILE_TEST_EXISTS)) {
4298 cerr << " exists\n";
4307 for (vector<space_and_path>::iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
4313 /* this is a stupid hack because Glib::path_get_dirname() is
4314 * lexical-only, and so passing it /a/b/c/ gives a different
4315 * result than passing it /a/b/c ...
4318 if (oldstr[oldstr.length()-1] == G_DIR_SEPARATOR) {
4319 oldstr = oldstr.substr (0, oldstr.length() - 1);
4322 string base = Glib::path_get_dirname (oldstr);
4323 newstr = Glib::build_filename (base, legal_name);
4325 cerr << "for " << oldstr << " new dir = " << newstr << endl;
4327 cerr << "Rename " << oldstr << " => " << newstr << endl;
4328 if (::g_rename (oldstr.c_str(), newstr.c_str()) != 0) {
4329 cerr << string_compose (_("renaming %s as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endl;
4330 error << string_compose (_("renaming %s as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endmsg;
4334 /* Reset path in "session dirs" */
4339 /* reset primary SessionDirectory object */
4342 (*_session_dir) = newstr;
4347 /* now rename directory below session_dir/interchange */
4349 string old_interchange_dir;
4350 string new_interchange_dir;
4352 /* use newstr here because we renamed the path
4353 * (folder/directory) that used to be oldstr to newstr above
4356 v.push_back (newstr);
4357 v.push_back (interchange_dir_name);
4358 v.push_back (Glib::path_get_basename (oldstr));
4360 old_interchange_dir = Glib::build_filename (v);
4363 v.push_back (newstr);
4364 v.push_back (interchange_dir_name);
4365 v.push_back (legal_name);
4367 new_interchange_dir = Glib::build_filename (v);
4369 cerr << "Rename " << old_interchange_dir << " => " << new_interchange_dir << endl;
4371 if (::g_rename (old_interchange_dir.c_str(), new_interchange_dir.c_str()) != 0) {
4372 cerr << string_compose (_("renaming %s as %2 failed (%3)"),
4373 old_interchange_dir, new_interchange_dir,
4376 error << string_compose (_("renaming %s as %2 failed (%3)"),
4377 old_interchange_dir, new_interchange_dir,
4386 oldstr = Glib::build_filename (new_path, _current_snapshot_name + statefile_suffix);
4387 newstr= Glib::build_filename (new_path, legal_name + statefile_suffix);
4389 cerr << "Rename " << oldstr << " => " << newstr << endl;
4391 if (::g_rename (oldstr.c_str(), newstr.c_str()) != 0) {
4392 cerr << string_compose (_("renaming %1 as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endl;
4393 error << string_compose (_("renaming %1 as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endmsg;
4399 oldstr = Glib::build_filename (new_path, _current_snapshot_name) + history_suffix;
4401 if (Glib::file_test (oldstr, Glib::FILE_TEST_EXISTS)) {
4402 newstr = Glib::build_filename (new_path, legal_name) + history_suffix;
4404 cerr << "Rename " << oldstr << " => " << newstr << endl;
4406 if (::g_rename (oldstr.c_str(), newstr.c_str()) != 0) {
4407 cerr << string_compose (_("renaming %1 as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endl;
4408 error << string_compose (_("renaming %1 as %2 failed (%3)"), oldstr, newstr, g_strerror (errno)) << endmsg;
4413 /* remove old name from recent sessions */
4414 remove_recent_sessions (_path);
4417 /* update file source paths */
4419 for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
4420 boost::shared_ptr<FileSource> fs = boost::dynamic_pointer_cast<FileSource> (i->second);
4422 string p = fs->path ();
4423 boost::replace_all (p, old_sources_root, _session_dir->sources_root());
4425 SourceFactory::setup_peakfile(i->second, true);
4429 set_snapshot_name (new_name);
4434 /* save state again to get everything just right */
4436 save_state (_current_snapshot_name);
4438 /* add to recent sessions */
4440 store_recent_sessions (new_name, _path);
4446 Session::get_info_from_path (const string& xmlpath, float& sample_rate, SampleFormat& data_format, std::string& program_version)
4448 bool found_sr = false;
4449 bool found_data_format = false;
4450 program_version = "";
4452 if (!Glib::file_test (xmlpath, Glib::FILE_TEST_EXISTS)) {
4456 xmlParserCtxtPtr ctxt = xmlNewParserCtxt();
4460 xmlDocPtr doc = xmlCtxtReadFile (ctxt, xmlpath.c_str(), NULL, XML_PARSE_HUGE);
4463 xmlFreeParserCtxt(ctxt);
4467 xmlNodePtr node = xmlDocGetRootElement(doc);
4470 xmlFreeParserCtxt(ctxt);
4478 for (attr = node->properties; attr; attr = attr->next) {
4479 if (!strcmp ((const char*)attr->name, "sample-rate") && attr->children) {
4480 sample_rate = atoi ((char*)attr->children->content);
4485 node = node->children;
4486 while (node != NULL) {
4487 if (!strcmp((const char*) node->name, "ProgramVersion")) {
4488 xmlChar* val = xmlGetProp (node, (const xmlChar*)"modified-with");
4490 program_version = string ((const char*)val);
4491 size_t sep = program_version.find_first_of("-");
4492 if (sep != string::npos) {
4493 program_version = program_version.substr (0, sep);
4498 if (strcmp((const char*) node->name, "Config")) {
4502 for (node = node->children; node; node = node->next) {
4503 xmlChar* pv = xmlGetProp (node, (const xmlChar*)"name");
4504 if (pv && !strcmp ((const char*)pv, "native-file-data-format")) {
4506 xmlChar* val = xmlGetProp (node, (const xmlChar*)"value");
4508 SampleFormat fmt = (SampleFormat) string_2_enum (string ((const char*)val), fmt);
4510 found_data_format = true;
4520 xmlFreeParserCtxt(ctxt);
4523 return !(found_sr && found_data_format); // zero if they are both found
4527 Session::get_snapshot_from_instant (const std::string& session_dir)
4529 std::string instant_xml_path = Glib::build_filename (session_dir, "instant.xml");
4531 if (!Glib::file_test (instant_xml_path, Glib::FILE_TEST_EXISTS)) {
4536 if (!tree.read (instant_xml_path)) {
4540 XMLProperty const * prop;
4541 XMLNode *last_used_snapshot = tree.root()->child("LastUsedSnapshot");
4542 if (last_used_snapshot && (prop = last_used_snapshot->property ("name")) != 0) {
4543 return prop->value();
4549 typedef std::vector<boost::shared_ptr<FileSource> > SeveralFileSources;
4550 typedef std::map<std::string,SeveralFileSources> SourcePathMap;
4553 Session::bring_all_sources_into_session (boost::function<void(uint32_t,uint32_t,string)> callback)
4557 SourcePathMap source_path_map;
4559 boost::shared_ptr<AudioFileSource> afs;
4564 Glib::Threads::Mutex::Lock lm (source_lock);
4566 cerr << " total sources = " << sources.size();
4568 for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) {
4569 boost::shared_ptr<FileSource> fs = boost::dynamic_pointer_cast<FileSource> (i->second);
4575 if (fs->within_session()) {
4579 if (source_path_map.find (fs->path()) != source_path_map.end()) {
4580 source_path_map[fs->path()].push_back (fs);
4582 SeveralFileSources v;
4584 source_path_map.insert (make_pair (fs->path(), v));
4590 cerr << " fsources = " << total << endl;
4592 for (SourcePathMap::iterator i = source_path_map.begin(); i != source_path_map.end(); ++i) {
4594 /* tell caller where we are */
4596 string old_path = i->first;
4598 callback (n, total, old_path);
4600 cerr << old_path << endl;
4604 switch (i->second.front()->type()) {
4605 case DataType::AUDIO:
4606 new_path = new_audio_source_path_for_embedded (old_path);
4609 case DataType::MIDI:
4610 /* XXX not implemented yet */
4614 if (new_path.empty()) {
4618 cerr << "Move " << old_path << " => " << new_path << endl;
4620 if (!copy_file (old_path, new_path)) {
4621 cerr << "failed !\n";
4625 /* make sure we stop looking in the external
4626 dir/folder. Remember, this is an all-or-nothing
4627 operations, it doesn't merge just some files.
4629 remove_dir_from_search_path (Glib::path_get_dirname (old_path), i->second.front()->type());
4631 for (SeveralFileSources::iterator f = i->second.begin(); f != i->second.end(); ++f) {
4632 (*f)->set_path (new_path);
4637 save_state ("", false, false);
4643 bool accept_all_files (string const &, void *)
4649 Session::save_as_bring_callback (uint32_t,uint32_t,string)
4651 /* It would be good if this did something useful vis-a-vis save-as, but the arguments doesn't provide the correct information right now to do this.
4656 make_new_media_path (string old_path, string new_session_folder, string new_session_path)
4658 /* typedir is the "midifiles" or "audiofiles" etc. part of the path. */
4660 string typedir = Glib::path_get_basename (Glib::path_get_dirname (old_path));
4662 v.push_back (new_session_folder); /* full path */
4663 v.push_back (interchange_dir_name);
4664 v.push_back (new_session_path); /* just one directory/folder */
4665 v.push_back (typedir);
4666 v.push_back (Glib::path_get_basename (old_path));
4668 return Glib::build_filename (v);
4672 Session::save_as (SaveAs& saveas)
4674 vector<string> files;
4675 string current_folder = Glib::path_get_dirname (_path);
4676 string new_folder = legalize_for_path (saveas.new_name);
4677 string to_dir = Glib::build_filename (saveas.new_parent_folder, new_folder);
4678 int64_t total_bytes = 0;
4682 int32_t internal_file_cnt = 0;
4684 vector<string> do_not_copy_extensions;
4685 do_not_copy_extensions.push_back (statefile_suffix);
4686 do_not_copy_extensions.push_back (pending_suffix);
4687 do_not_copy_extensions.push_back (backup_suffix);
4688 do_not_copy_extensions.push_back (temp_suffix);
4689 do_not_copy_extensions.push_back (history_suffix);
4691 /* get total size */
4693 for (vector<space_and_path>::const_iterator sd = session_dirs.begin(); sd != session_dirs.end(); ++sd) {
4695 /* need to clear this because
4696 * find_files_matching_filter() is cumulative
4701 find_files_matching_filter (files, (*sd).path, accept_all_files, 0, false, true, true);
4703 all += files.size();
4705 for (vector<string>::iterator i = files.begin(); i != files.end(); ++i) {
4707 g_stat ((*i).c_str(), &gsb);
4708 total_bytes += gsb.st_size;
4712 /* save old values so we can switch back if we are not switching to the new session */
4714 string old_path = _path;
4715 string old_name = _name;
4716 string old_snapshot = _current_snapshot_name;
4717 string old_sd = _session_dir->root_path();
4718 vector<string> old_search_path[DataType::num_types];
4719 string old_config_search_path[DataType::num_types];
4721 old_search_path[DataType::AUDIO] = source_search_path (DataType::AUDIO);
4722 old_search_path[DataType::MIDI] = source_search_path (DataType::MIDI);
4723 old_config_search_path[DataType::AUDIO] = config.get_audio_search_path ();
4724 old_config_search_path[DataType::MIDI] = config.get_midi_search_path ();
4726 /* switch session directory */
4728 (*_session_dir) = to_dir;
4730 /* create new tree */
4732 if (!_session_dir->create()) {
4733 saveas.failure_message = string_compose (_("Cannot create new session folder %1"), to_dir);
4738 /* copy all relevant files. Find each location in session_dirs,
4739 * and copy files from there to target.
4742 for (vector<space_and_path>::const_iterator sd = session_dirs.begin(); sd != session_dirs.end(); ++sd) {
4744 /* need to clear this because
4745 * find_files_matching_filter() is cumulative
4750 const size_t prefix_len = (*sd).path.size();
4752 /* Work just on the files within this session dir */
4754 find_files_matching_filter (files, (*sd).path, accept_all_files, 0, false, true, true);
4756 /* add dir separator to protect against collisions with
4757 * track names (e.g. track named "audiofiles" or
4761 static const std::string audiofile_dir_string = string (sound_dir_name) + G_DIR_SEPARATOR;
4762 static const std::string midifile_dir_string = string (midi_dir_name) + G_DIR_SEPARATOR;
4763 static const std::string analysis_dir_string = analysis_dir() + G_DIR_SEPARATOR;
4765 /* copy all the files. Handling is different for media files
4766 than others because of the *silly* subtree we have below the interchange
4767 folder. That really was a bad idea, but I'm not fixing it as part of
4768 implementing ::save_as().
4771 for (vector<string>::iterator i = files.begin(); i != files.end(); ++i) {
4773 std::string from = *i;
4776 string filename = Glib::path_get_basename (from);
4777 std::transform (filename.begin(), filename.end(), filename.begin(), ::toupper);
4778 if (filename == ".DS_STORE") {
4783 if (from.find (audiofile_dir_string) != string::npos) {
4785 /* audio file: only copy if asked */
4787 if (saveas.include_media && saveas.copy_media) {
4789 string to = make_new_media_path (*i, to_dir, new_folder);
4791 info << "media file copying from " << from << " to " << to << endmsg;
4793 if (!copy_file (from, to)) {
4794 throw Glib::FileError (Glib::FileError::IO_ERROR,
4795 string_compose(_("\ncopying \"%1\" failed !"), from));
4799 /* we found media files inside the session folder */
4801 internal_file_cnt++;
4803 } else if (from.find (midifile_dir_string) != string::npos) {
4805 /* midi file: always copy unless
4806 * creating an empty new session
4809 if (saveas.include_media) {
4811 string to = make_new_media_path (*i, to_dir, new_folder);
4813 info << "media file copying from " << from << " to " << to << endmsg;
4815 if (!copy_file (from, to)) {
4816 throw Glib::FileError (Glib::FileError::IO_ERROR, "copy failed");
4820 /* we found media files inside the session folder */
4822 internal_file_cnt++;
4824 } else if (from.find (analysis_dir_string) != string::npos) {
4826 /* make sure analysis dir exists in
4827 * new session folder, but we're not
4828 * copying analysis files here, see
4832 (void) g_mkdir_with_parents (analysis_dir().c_str(), 775);
4837 /* normal non-media file. Don't copy state, history, etc.
4840 bool do_copy = true;
4842 for (vector<string>::iterator v = do_not_copy_extensions.begin(); v != do_not_copy_extensions.end(); ++v) {
4843 if ((from.length() > (*v).length()) && (from.find (*v) == from.length() - (*v).length())) {
4844 /* end of filename matches extension, do not copy file */
4850 if (!saveas.copy_media && from.find (peakfile_suffix) != string::npos) {
4851 /* don't copy peakfiles if
4852 * we're not copying media
4858 string to = Glib::build_filename (to_dir, from.substr (prefix_len));
4860 info << "attempting to make directory/folder " << to << endmsg;
4862 if (g_mkdir_with_parents (Glib::path_get_dirname (to).c_str(), 0755)) {
4863 throw Glib::FileError (Glib::FileError::IO_ERROR, "cannot create required directory");
4866 info << "attempting to copy " << from << " to " << to << endmsg;
4868 if (!copy_file (from, to)) {
4869 throw Glib::FileError (Glib::FileError::IO_ERROR,
4870 string_compose(_("\ncopying \"%1\" failed !"), from));
4875 /* measure file size even if we're not going to copy so that our Progress
4876 signals are correct, since we included these do-not-copy files
4877 in the computation of the total size and file count.
4881 g_stat (from.c_str(), &gsb);
4882 copied += gsb.st_size;
4885 double fraction = (double) copied / total_bytes;
4887 bool keep_going = true;
4889 if (saveas.copy_media) {
4891 /* no need or expectation of this if
4892 * media is not being copied, because
4893 * it will be fast(ish).
4896 /* tell someone "X percent, file M of N"; M is one-based */
4898 boost::optional<bool> res = saveas.Progress (fraction, cnt, all);
4906 throw Glib::FileError (Glib::FileError::FAILED, "copy cancelled");
4912 /* copy optional folders, if any */
4914 string old = plugins_dir ();
4915 if (Glib::file_test (old, Glib::FILE_TEST_EXISTS)) {
4916 string newdir = Glib::build_filename (to_dir, Glib::path_get_basename (old));
4917 copy_files (old, newdir);
4920 old = externals_dir ();
4921 if (Glib::file_test (old, Glib::FILE_TEST_EXISTS)) {
4922 string newdir = Glib::build_filename (to_dir, Glib::path_get_basename (old));
4923 copy_files (old, newdir);
4926 old = automation_dir ();
4927 if (Glib::file_test (old, Glib::FILE_TEST_EXISTS)) {
4928 string newdir = Glib::build_filename (to_dir, Glib::path_get_basename (old));
4929 copy_files (old, newdir);
4932 if (saveas.include_media) {
4934 if (saveas.copy_media) {
4935 #ifndef PLATFORM_WINDOWS
4936 /* There are problems with analysis files on
4937 * Windows, because they used a colon in their
4938 * names as late as 4.0. Colons are not legal
4939 * under Windows even if NTFS allows them.
4941 * This is a tricky problem to solve so for
4942 * just don't copy these files. They will be
4943 * regenerated as-needed anyway, subject to the
4944 * existing issue that the filenames will be
4945 * rejected by Windows, which is a separate
4946 * problem (though related).
4949 /* only needed if we are copying media, since the
4950 * analysis data refers to media data
4953 old = analysis_dir ();
4954 if (Glib::file_test (old, Glib::FILE_TEST_EXISTS)) {
4955 string newdir = Glib::build_filename (to_dir, "analysis");
4956 copy_files (old, newdir);
4958 #endif /* PLATFORM_WINDOWS */
4963 set_snapshot_name (saveas.new_name);
4964 _name = saveas.new_name;
4966 if (saveas.include_media && !saveas.copy_media) {
4968 /* reset search paths of the new session (which we're pretending to be right now) to
4969 include the original session search path, so we can still find all audio.
4972 if (internal_file_cnt) {
4973 for (vector<string>::iterator s = old_search_path[DataType::AUDIO].begin(); s != old_search_path[DataType::AUDIO].end(); ++s) {
4974 ensure_search_path_includes (*s, DataType::AUDIO);
4975 cerr << "be sure to include " << *s << " for audio" << endl;
4978 /* we do not do this for MIDI because we copy
4979 all MIDI files if saveas.include_media is
4985 bool was_dirty = dirty ();
4987 save_default_options ();
4989 if (saveas.copy_media && saveas.copy_external) {
4990 if (bring_all_sources_into_session (boost::bind (&Session::save_as_bring_callback, this, _1, _2, _3))) {
4991 throw Glib::FileError (Glib::FileError::NO_SPACE_LEFT, "consolidate failed");
4995 saveas.final_session_folder_name = _path;
4997 store_recent_sessions (_name, _path);
4999 if (!saveas.switch_to) {
5001 /* save the new state */
5003 save_state ("", false, false, !saveas.include_media);
5005 /* switch back to the way things were */
5009 set_snapshot_name (old_snapshot);
5011 (*_session_dir) = old_sd;
5017 if (internal_file_cnt) {
5018 /* reset these to their original values */
5019 config.set_audio_search_path (old_config_search_path[DataType::AUDIO]);
5020 config.set_midi_search_path (old_config_search_path[DataType::MIDI]);
5025 /* prune session dirs, and update disk space statistics
5030 session_dirs.clear ();
5031 session_dirs.push_back (sp);
5032 refresh_disk_space ();
5034 /* ensure that all existing tracks reset their current capture source paths
5036 reset_write_sources (true, true);
5038 /* creating new write sources marks the session as
5039 dirty. If the new session is empty, then
5040 save_state() thinks we're saving a template and will
5041 not mark the session as clean. So do that here,
5042 before we save state.
5045 if (!saveas.include_media) {
5046 _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty);
5049 save_state ("", false, false, !saveas.include_media);
5051 /* the copying above was based on actually discovering files, not just iterating over the sources list.
5052 But if we're going to switch to the new (copied) session, we need to change the paths in the sources also.
5055 for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) {
5056 boost::shared_ptr<FileSource> fs = boost::dynamic_pointer_cast<FileSource> (i->second);
5062 if (fs->within_session()) {
5063 string newpath = make_new_media_path (fs->path(), to_dir, new_folder);
5064 fs->set_path (newpath);
5069 } catch (Glib::FileError& e) {
5071 saveas.failure_message = e.what();
5073 /* recursively remove all the directories */
5075 remove_directory (to_dir);
5083 saveas.failure_message = _("unknown reason");
5085 /* recursively remove all the directories */
5087 remove_directory (to_dir);
5097 static void set_progress (Progress* p, size_t n, size_t t)
5099 p->set_progress (float (n) / float(t));
5103 Session::archive_session (const std::string& dest,
5104 const std::string& name,
5105 ArchiveEncode compress_audio,
5106 bool only_used_sources,
5109 if (dest.empty () || name.empty ()) {
5113 /* save current values */
5114 bool was_dirty = dirty ();
5115 string old_path = _path;
5116 string old_name = _name;
5117 string old_snapshot = _current_snapshot_name;
5118 string old_sd = _session_dir->root_path();
5119 string old_config_search_path[DataType::num_types];
5120 old_config_search_path[DataType::AUDIO] = config.get_audio_search_path ();
5121 old_config_search_path[DataType::MIDI] = config.get_midi_search_path ();
5123 /* ensure that session-path is included in search-path */
5125 for (vector<space_and_path>::const_iterator sd = session_dirs.begin(); sd != session_dirs.end(); ++sd) {
5126 if ((*sd).path == old_path) {
5134 /* create temporary dir to save session to */
5135 #ifdef PLATFORM_WINDOWS
5136 char tmp[256] = "C:\\TEMP\\";
5137 GetTempPath (sizeof (tmp), tmp);
5139 char const* tmp = getenv("TMPDIR");
5144 if ((strlen (tmp) + 21) > 1024) {
5149 strcpy (tmptpl, tmp);
5150 strcat (tmptpl, "ardourarchive-XXXXXX");
5151 char* tmpdir = g_mkdtemp (tmptpl);
5157 std::string to_dir = std::string (tmpdir);
5159 /* switch session directory temporarily */
5160 (*_session_dir) = to_dir;
5162 if (!_session_dir->create()) {
5163 (*_session_dir) = old_sd;
5164 remove_directory (to_dir);
5168 /* prepare archive */
5169 string archive = Glib::build_filename (dest, name + ".tar.xz");
5171 PBD::ScopedConnectionList progress_connection;
5172 PBD::FileArchive ar (archive);
5174 ar.progress.connect_same_thread (progress_connection, boost::bind (&set_progress, progress, _1, _2));
5177 /* collect files to archive */
5178 std::map<string,string> filemap;
5180 vector<string> do_not_copy_extensions;
5181 do_not_copy_extensions.push_back (statefile_suffix);
5182 do_not_copy_extensions.push_back (pending_suffix);
5183 do_not_copy_extensions.push_back (backup_suffix);
5184 do_not_copy_extensions.push_back (temp_suffix);
5185 do_not_copy_extensions.push_back (history_suffix);
5187 vector<string> blacklist_dirs;
5188 blacklist_dirs.push_back (string (peak_dir_name) + G_DIR_SEPARATOR);
5189 blacklist_dirs.push_back (string (analysis_dir_name) + G_DIR_SEPARATOR);
5190 blacklist_dirs.push_back (string (dead_dir_name) + G_DIR_SEPARATOR);
5191 blacklist_dirs.push_back (string (export_dir_name) + G_DIR_SEPARATOR);
5192 blacklist_dirs.push_back (string (externals_dir_name) + G_DIR_SEPARATOR);
5193 blacklist_dirs.push_back (string (plugins_dir_name) + G_DIR_SEPARATOR);
5195 std::map<boost::shared_ptr<AudioFileSource>, std::string> orig_sources;
5196 std::map<boost::shared_ptr<AudioFileSource>, float> orig_gain;
5198 set<boost::shared_ptr<Source> > sources_used_by_this_snapshot;
5199 if (only_used_sources) {
5200 playlists->sync_all_regions_with_regions ();
5201 playlists->foreach (boost::bind (merge_all_sources, _1, &sources_used_by_this_snapshot), false);
5204 // collect audio sources for this session, calc total size for encoding
5205 // add option to only include *used* sources (see Session::cleanup_sources)
5206 size_t total_size = 0;
5208 Glib::Threads::Mutex::Lock lm (source_lock);
5209 for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) {
5210 boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (i->second);
5211 if (!afs || afs->readable_length () == 0) {
5215 if (only_used_sources) {
5219 if (sources_used_by_this_snapshot.find (afs) == sources_used_by_this_snapshot.end ()) {
5224 std::string from = afs->path();
5226 if (compress_audio != NO_ENCODE) {
5227 total_size += afs->readable_length ();
5229 if (afs->within_session()) {
5230 filemap[from] = make_new_media_path (from, name, name);
5232 filemap[from] = make_new_media_path (from, name, name);
5233 remove_dir_from_search_path (Glib::path_get_dirname (from), DataType::AUDIO);
5240 if (compress_audio != NO_ENCODE) {
5242 progress->set_progress (2); // set to "encoding"
5243 progress->set_progress (0);
5246 Glib::Threads::Mutex::Lock lm (source_lock);
5247 for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) {
5248 boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (i->second);
5249 if (!afs || afs->readable_length () == 0) {
5253 if (only_used_sources) {
5257 if (sources_used_by_this_snapshot.find (afs) == sources_used_by_this_snapshot.end ()) {
5262 orig_sources[afs] = afs->path();
5263 orig_gain[afs] = afs->gain();
5265 std::string new_path = make_new_media_path (afs->path (), to_dir, name);
5266 new_path = Glib::build_filename (Glib::path_get_dirname (new_path), PBD::basename_nosuffix (new_path) + ".flac");
5267 g_mkdir_with_parents (Glib::path_get_dirname (new_path).c_str (), 0755);
5270 progress->descend ((float)afs->readable_length () / total_size);
5274 SndFileSource* ns = new SndFileSource (*this, *(afs.get()), new_path, compress_audio == FLAC_16BIT, progress);
5275 afs->replace_file (new_path);
5276 afs->set_gain (ns->gain(), true);
5279 cerr << "failed to encode " << afs->path() << " to " << new_path << "\n";
5283 progress->ascend ();
5289 progress->set_progress (-1); // set to "archiving"
5290 progress->set_progress (0);
5293 /* index files relevant for this session */
5294 for (vector<space_and_path>::const_iterator sd = session_dirs.begin(); sd != session_dirs.end(); ++sd) {
5295 vector<string> files;
5297 size_t prefix_len = (*sd).path.size();
5298 if (prefix_len > 0 && (*sd).path.at (prefix_len - 1) != G_DIR_SEPARATOR) {
5302 find_files_matching_filter (files, (*sd).path, accept_all_files, 0, false, true, true);
5304 static const std::string audiofile_dir_string = string (sound_dir_name) + G_DIR_SEPARATOR;
5305 static const std::string videofile_dir_string = string (video_dir_name) + G_DIR_SEPARATOR;
5306 static const std::string midifile_dir_string = string (midi_dir_name) + G_DIR_SEPARATOR;
5308 for (vector<string>::const_iterator i = files.begin (); i != files.end (); ++i) {
5309 std::string from = *i;
5312 string filename = Glib::path_get_basename (from);
5313 std::transform (filename.begin(), filename.end(), filename.begin(), ::toupper);
5314 if (filename == ".DS_STORE") {
5319 if (from.find (audiofile_dir_string) != string::npos) {
5321 } else if (from.find (midifile_dir_string) != string::npos) {
5322 filemap[from] = make_new_media_path (from, name, name);
5323 } else if (from.find (videofile_dir_string) != string::npos) {
5324 filemap[from] = make_new_media_path (from, name, name);
5326 bool do_copy = true;
5327 for (vector<string>::iterator v = blacklist_dirs.begin(); v != blacklist_dirs.end(); ++v) {
5328 if (from.find (*v) != string::npos) {
5333 for (vector<string>::iterator v = do_not_copy_extensions.begin(); v != do_not_copy_extensions.end(); ++v) {
5334 if ((from.length() > (*v).length()) && (from.find (*v) == from.length() - (*v).length())) {
5341 filemap[from] = name + G_DIR_SEPARATOR + from.substr (prefix_len);
5347 /* write session file */
5349 g_mkdir_with_parents (externals_dir ().c_str (), 0755);
5351 PBD::Unwinder<bool> uw (LV2Plugin::force_state_save, true);
5354 save_default_options ();
5356 size_t prefix_len = _path.size();
5357 if (prefix_len > 0 && _path.at (prefix_len - 1) != G_DIR_SEPARATOR) {
5361 /* collect session-state files */
5362 vector<string> files;
5363 do_not_copy_extensions.clear ();
5364 do_not_copy_extensions.push_back (history_suffix);
5366 blacklist_dirs.clear ();
5367 blacklist_dirs.push_back (string (externals_dir_name) + G_DIR_SEPARATOR);
5369 find_files_matching_filter (files, to_dir, accept_all_files, 0, false, true, true);
5370 for (vector<string>::const_iterator i = files.begin (); i != files.end (); ++i) {
5371 std::string from = *i;
5372 bool do_copy = true;
5373 for (vector<string>::iterator v = blacklist_dirs.begin(); v != blacklist_dirs.end(); ++v) {
5374 if (from.find (*v) != string::npos) {
5379 for (vector<string>::iterator v = do_not_copy_extensions.begin(); v != do_not_copy_extensions.end(); ++v) {
5380 if ((from.length() > (*v).length()) && (from.find (*v) == from.length() - (*v).length())) {
5386 filemap[from] = name + G_DIR_SEPARATOR + from.substr (prefix_len);
5390 /* restore original values */
5393 set_snapshot_name (old_snapshot);
5394 (*_session_dir) = old_sd;
5398 config.set_audio_search_path (old_config_search_path[DataType::AUDIO]);
5399 config.set_midi_search_path (old_config_search_path[DataType::MIDI]);
5401 for (std::map<boost::shared_ptr<AudioFileSource>, std::string>::iterator i = orig_sources.begin (); i != orig_sources.end (); ++i) {
5402 i->first->replace_file (i->second);
5404 for (std::map<boost::shared_ptr<AudioFileSource>, float>::iterator i = orig_gain.begin (); i != orig_gain.end (); ++i) {
5405 i->first->set_gain (i->second, true);
5408 int rv = ar.create (filemap);
5409 remove_directory (to_dir);
5415 Session::undo (uint32_t n)
5417 if (actively_recording()) {
5425 Session::redo (uint32_t n)
5427 if (actively_recording()) {