Move check for a new session directory being writable to after it has been created.
[ardour2.git] / libs / ardour / session_state.cc
blobbe29639a3ba9730e796aa89bd72b5f8cb52e5348
1 /*
2 Copyright (C) 1999-2002 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.
21 #ifdef WAF_BUILD
22 #include "libardour-config.h"
23 #endif
25 #include <stdint.h>
27 #include <algorithm>
28 #include <fstream>
29 #include <string>
30 #include <cerrno>
33 #include <cstdio> /* snprintf(3) ... grrr */
34 #include <cmath>
35 #include <unistd.h>
36 #include <sys/stat.h>
37 #include <climits>
38 #include <fcntl.h>
39 #include <poll.h>
40 #include <signal.h>
41 #include <sys/mman.h>
42 #include <sys/time.h>
44 #ifdef HAVE_SYS_VFS_H
45 #include <sys/vfs.h>
46 #else
47 #include <sys/param.h>
48 #include <sys/mount.h>
49 #endif
51 #include <glibmm.h>
52 #include <glibmm/thread.h>
54 #include "midi++/mmc.h"
55 #include "midi++/port.h"
56 #include "midi++/manager.h"
58 #include "pbd/boost_debug.h"
59 #include "pbd/basename.h"
60 #include "pbd/controllable_descriptor.h"
61 #include "pbd/enumwriter.h"
62 #include "pbd/error.h"
63 #include "pbd/pathscanner.h"
64 #include "pbd/pthread_utils.h"
65 #include "pbd/search_path.h"
66 #include "pbd/stacktrace.h"
67 #include "pbd/convert.h"
68 #include "pbd/clear_dir.h"
70 #include "ardour/amp.h"
71 #include "ardour/audio_diskstream.h"
72 #include "ardour/audio_track.h"
73 #include "ardour/audioengine.h"
74 #include "ardour/audiofilesource.h"
75 #include "ardour/audioplaylist.h"
76 #include "ardour/audioregion.h"
77 #include "ardour/auditioner.h"
78 #include "ardour/automation_control.h"
79 #include "ardour/buffer.h"
80 #include "ardour/butler.h"
81 #include "ardour/configuration.h"
82 #include "ardour/control_protocol_manager.h"
83 #include "ardour/crossfade.h"
84 #include "ardour/cycle_timer.h"
85 #include "ardour/directory_names.h"
86 #include "ardour/filename_extensions.h"
87 #include "ardour/io_processor.h"
88 #include "ardour/location.h"
89 #include "ardour/midi_diskstream.h"
90 #include "ardour/midi_patch_manager.h"
91 #include "ardour/midi_playlist.h"
92 #include "ardour/midi_region.h"
93 #include "ardour/midi_source.h"
94 #include "ardour/midi_track.h"
95 #include "ardour/named_selection.h"
96 #include "ardour/pannable.h"
97 #include "ardour/processor.h"
98 #include "ardour/port.h"
99 #include "ardour/proxy_controllable.h"
100 #include "ardour/region_factory.h"
101 #include "ardour/route_group.h"
102 #include "ardour/send.h"
103 #include "ardour/session.h"
104 #include "ardour/session_directory.h"
105 #include "ardour/session_metadata.h"
106 #include "ardour/session_state_utils.h"
107 #include "ardour/session_playlists.h"
108 #include "ardour/session_utils.h"
109 #include "ardour/silentfilesource.h"
110 #include "ardour/slave.h"
111 #include "ardour/smf_source.h"
112 #include "ardour/sndfile_helpers.h"
113 #include "ardour/sndfilesource.h"
114 #include "ardour/source_factory.h"
115 #include "ardour/template_utils.h"
116 #include "ardour/tempo.h"
117 #include "ardour/ticker.h"
118 #include "ardour/user_bundle.h"
119 #include "ardour/utils.h"
120 #include "ardour/utils.h"
121 #include "ardour/version.h"
122 #include "ardour/playlist_factory.h"
124 #include "control_protocol/control_protocol.h"
126 #include "i18n.h"
127 #include <locale.h>
129 using namespace std;
130 using namespace ARDOUR;
131 using namespace PBD;
134 void
135 Session::first_stage_init (string fullpath, string snapshot_name)
137 if (fullpath.length() == 0) {
138 destroy ();
139 throw failed_constructor();
142 char buf[PATH_MAX+1];
143 if (!realpath (fullpath.c_str(), buf) && (errno != ENOENT)) {
144 error << string_compose(_("Could not use path %1 (%s)"), buf, strerror(errno)) << endmsg;
145 destroy ();
146 throw failed_constructor();
149 _path = string(buf);
151 if (_path[_path.length()-1] != G_DIR_SEPARATOR) {
152 _path += G_DIR_SEPARATOR;
155 /* these two are just provisional settings. set_state()
156 will likely override them.
159 _name = _current_snapshot_name = snapshot_name;
161 set_history_depth (Config->get_history_depth());
163 _current_frame_rate = _engine.frame_rate ();
164 _nominal_frame_rate = _current_frame_rate;
165 _base_frame_rate = _current_frame_rate;
167 _tempo_map = new TempoMap (_current_frame_rate);
168 _tempo_map->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::tempo_map_changed, this, _1));
171 _non_soloed_outs_muted = false;
172 _listen_cnt = 0;
173 _solo_isolated_cnt = 0;
174 g_atomic_int_set (&processing_prohibited, 0);
175 _transport_speed = 0;
176 _last_transport_speed = 0;
177 _target_transport_speed = 0;
178 auto_play_legal = false;
179 transport_sub_state = 0;
180 _transport_frame = 0;
181 _requested_return_frame = -1;
182 _session_range_location = 0;
183 g_atomic_int_set (&_record_status, Disabled);
184 loop_changing = false;
185 play_loop = false;
186 have_looped = false;
187 _last_roll_location = 0;
188 _last_roll_or_reversal_location = 0;
189 _last_record_location = 0;
190 pending_locate_frame = 0;
191 pending_locate_roll = false;
192 pending_locate_flush = false;
193 state_was_pending = false;
194 set_next_event ();
195 outbound_mtc_timecode_frame = 0;
196 next_quarter_frame_to_send = -1;
197 current_block_size = 0;
198 solo_update_disabled = false;
199 _have_captured = false;
200 _worst_output_latency = 0;
201 _worst_input_latency = 0;
202 _worst_track_latency = 0;
203 _state_of_the_state = StateOfTheState(CannotSave|InitialConnecting|Loading);
204 _was_seamless = Config->get_seamless_loop ();
205 _slave = 0;
206 _send_qf_mtc = false;
207 _pframes_since_last_mtc = 0;
208 g_atomic_int_set (&_playback_load, 100);
209 g_atomic_int_set (&_capture_load, 100);
210 _play_range = false;
211 _exporting = false;
212 pending_abort = false;
213 destructive_index = 0;
214 first_file_data_format_reset = true;
215 first_file_header_format_reset = true;
216 post_export_sync = false;
217 midi_control_ui = 0;
218 _step_editors = 0;
219 no_questions_about_missing_files = false;
220 _speakers.reset (new Speakers);
222 AudioDiskstream::allocate_working_buffers();
224 /* default short fade = 15ms */
226 Crossfade::set_short_xfade_length ((framecnt_t) floor (config.get_short_xfade_seconds() * frame_rate()));
227 SndFileSource::setup_standard_crossfades (*this, frame_rate());
229 last_mmc_step.tv_sec = 0;
230 last_mmc_step.tv_usec = 0;
231 step_speed = 0.0;
233 /* click sounds are unset by default, which causes us to internal
234 waveforms for clicks.
237 click_length = 0;
238 click_emphasis_length = 0;
239 _clicking = false;
241 process_function = &Session::process_with_events;
243 if (config.get_use_video_sync()) {
244 waiting_for_sync_offset = true;
245 } else {
246 waiting_for_sync_offset = false;
249 last_timecode_when = 0;
250 last_timecode_valid = false;
252 sync_time_vars ();
254 last_rr_session_dir = session_dirs.begin();
255 refresh_disk_space ();
257 /* default: assume simple stereo speaker configuration */
259 _speakers->setup_default_speakers (2);
261 /* slave stuff */
263 average_slave_delta = 1800; // !!! why 1800 ????
264 have_first_delta_accumulator = false;
265 delta_accumulator_cnt = 0;
266 _slave_state = Stopped;
268 _solo_cut_control.reset (new ProxyControllable (_("solo cut control (dB)"), PBD::Controllable::GainLike,
269 boost::bind (&RCConfiguration::set_solo_mute_gain, Config, _1),
270 boost::bind (&RCConfiguration::get_solo_mute_gain, Config)));
271 add_controllable (_solo_cut_control);
273 _engine.GraphReordered.connect_same_thread (*this, boost::bind (&Session::graph_reordered, this));
275 /* These are all static "per-class" signals */
277 SourceFactory::SourceCreated.connect_same_thread (*this, boost::bind (&Session::add_source, this, _1));
278 PlaylistFactory::PlaylistCreated.connect_same_thread (*this, boost::bind (&Session::add_playlist, this, _1, _2));
279 AutomationList::AutomationListCreated.connect_same_thread (*this, boost::bind (&Session::add_automation_list, this, _1));
280 Controllable::Destroyed.connect_same_thread (*this, boost::bind (&Session::remove_controllable, this, _1));
281 IO::PortCountChanged.connect_same_thread (*this, boost::bind (&Session::ensure_buffers, this, _1));
283 /* stop IO objects from doing stuff until we're ready for them */
285 Delivery::disable_panners ();
286 IO::disable_connecting ();
290 Session::second_stage_init ()
292 AudioFileSource::set_peak_dir (_session_dir->peak_path().to_string());
294 if (!_is_new) {
295 if (load_state (_current_snapshot_name)) {
296 return -1;
300 if (_butler->start_thread()) {
301 return -1;
304 if (start_midi_thread ()) {
305 return -1;
308 setup_midi_machine_control ();
310 // set_state() will call setup_raid_path(), but if it's a new session we need
311 // to call setup_raid_path() here.
313 if (state_tree) {
314 if (set_state (*state_tree->root(), Stateful::loading_state_version)) {
315 return -1;
317 } else {
318 setup_raid_path(_path);
321 /* we can't save till after ::when_engine_running() is called,
322 because otherwise we save state with no connections made.
323 therefore, we reset _state_of_the_state because ::set_state()
324 will have cleared it.
326 we also have to include Loading so that any events that get
327 generated between here and the end of ::when_engine_running()
328 will be processed directly rather than queued.
331 _state_of_the_state = StateOfTheState (_state_of_the_state|CannotSave|Loading);
333 _locations->changed.connect_same_thread (*this, boost::bind (&Session::locations_changed, this));
334 _locations->added.connect_same_thread (*this, boost::bind (&Session::locations_added, this, _1));
335 setup_click_sounds (0);
336 setup_midi_control ();
338 /* Pay attention ... */
340 _engine.Halted.connect_same_thread (*this, boost::bind (&Session::engine_halted, this));
341 _engine.Xrun.connect_same_thread (*this, boost::bind (&Session::xrun_recovery, this));
343 try {
344 when_engine_running ();
347 /* handle this one in a different way than all others, so that its clear what happened */
349 catch (AudioEngine::PortRegistrationFailure& err) {
350 error << err.what() << endmsg;
351 return -1;
354 catch (...) {
355 return -1;
358 BootMessage (_("Reset Remote Controls"));
360 send_full_time_code (0);
361 _engine.transport_locate (0);
363 MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (MIDI::MachineControl::cmdMmcReset));
364 MIDI::Manager::instance()->mmc()->send (MIDI::MachineControlCommand (Timecode::Time ()));
366 MidiClockTicker::instance().set_session (this);
367 MIDI::Name::MidiPatchManager::instance().set_session (this);
369 /* initial program change will be delivered later; see ::config_changed() */
371 BootMessage (_("Reset Control Protocols"));
373 ControlProtocolManager::instance().set_session (this);
375 _state_of_the_state = Clean;
377 Port::set_connecting_blocked (false);
379 DirtyChanged (); /* EMIT SIGNAL */
381 if (state_was_pending) {
382 save_state (_current_snapshot_name);
383 remove_pending_capture_state ();
384 state_was_pending = false;
387 BootMessage (_("Session loading complete"));
389 return 0;
392 string
393 Session::raid_path () const
395 SearchPath raid_search_path;
397 for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
398 raid_search_path += sys::path((*i).path);
401 return raid_search_path.to_string ();
404 void
405 Session::setup_raid_path (string path)
407 if (path.empty()) {
408 return;
411 space_and_path sp;
412 string fspath;
414 session_dirs.clear ();
416 SearchPath search_path(path);
417 SearchPath sound_search_path;
418 SearchPath midi_search_path;
420 for (SearchPath::const_iterator i = search_path.begin(); i != search_path.end(); ++i) {
421 sp.path = (*i).to_string ();
422 sp.blocks = 0; // not needed
423 session_dirs.push_back (sp);
425 SessionDirectory sdir(sp.path);
427 sound_search_path += sdir.sound_path ();
428 midi_search_path += sdir.midi_path ();
431 // reset the round-robin soundfile path thingie
432 last_rr_session_dir = session_dirs.begin();
435 bool
436 Session::path_is_within_session (const std::string& path)
438 for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
439 if (path.find ((*i).path) == 0) {
440 return true;
443 return false;
447 Session::ensure_subdirs ()
449 string dir;
451 dir = session_directory().peak_path().to_string();
453 if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
454 error << string_compose(_("Session: cannot create session peakfile folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
455 return -1;
458 dir = session_directory().sound_path().to_string();
460 if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
461 error << string_compose(_("Session: cannot create session sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
462 return -1;
465 dir = session_directory().midi_path().to_string();
467 if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
468 error << string_compose(_("Session: cannot create session midi dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
469 return -1;
472 dir = session_directory().dead_path().to_string();
474 if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
475 error << string_compose(_("Session: cannot create session dead sounds folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
476 return -1;
479 dir = session_directory().export_path().to_string();
481 if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
482 error << string_compose(_("Session: cannot create session export folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
483 return -1;
486 dir = analysis_dir ();
488 if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
489 error << string_compose(_("Session: cannot create session analysis folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
490 return -1;
493 dir = plugins_dir ();
495 if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
496 error << string_compose(_("Session: cannot create session plugins folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
497 return -1;
500 return 0;
503 /** Caller must not hold process lock */
505 Session::create (const string& mix_template, BusProfile* bus_profile)
507 if (g_mkdir_with_parents (_path.c_str(), 0755) < 0) {
508 error << string_compose(_("Session: cannot create session folder \"%1\" (%2)"), _path, strerror (errno)) << endmsg;
509 return -1;
512 if (ensure_subdirs ()) {
513 return -1;
516 _writable = exists_and_writable (sys::path (_path));
518 if (!mix_template.empty()) {
519 std::string in_path = mix_template;
521 ifstream in(in_path.c_str());
523 if (in) {
524 string out_path = _path;
525 out_path += _name;
526 out_path += statefile_suffix;
528 ofstream out(out_path.c_str());
530 if (out) {
531 out << in.rdbuf();
532 _is_new = false;
533 return 0;
535 } else {
536 error << string_compose (_("Could not open %1 for writing mix template"), out_path)
537 << endmsg;
538 return -1;
541 } else {
542 error << string_compose (_("Could not open mix template %1 for reading"), in_path)
543 << endmsg;
544 return -1;
549 /* Instantiate metadata */
551 _metadata = new SessionMetadata ();
553 /* set initial start + end point */
555 _state_of_the_state = Clean;
557 /* set up Master Out and Control Out if necessary */
559 if (bus_profile) {
561 RouteList rl;
562 int control_id = 1;
563 ChanCount count(DataType::AUDIO, bus_profile->master_out_channels);
565 if (bus_profile->master_out_channels) {
566 boost::shared_ptr<Route> r (new Route (*this, _("master"), Route::MasterOut, DataType::AUDIO));
567 if (r->init ()) {
568 return -1;
570 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
571 boost_debug_shared_ptr_mark_interesting (rt.get(), "Route");
572 #endif
574 Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
575 r->input()->ensure_io (count, false, this);
576 r->output()->ensure_io (count, false, this);
578 r->set_remote_control_id (control_id++);
580 rl.push_back (r);
582 if (Config->get_use_monitor_bus()) {
583 boost::shared_ptr<Route> r (new Route (*this, _("monitor"), Route::MonitorOut, DataType::AUDIO));
584 if (r->init ()) {
585 return -1;
587 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
588 boost_debug_shared_ptr_mark_interesting (rt, "Route");
589 #endif
591 Glib::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
592 r->input()->ensure_io (count, false, this);
593 r->output()->ensure_io (count, false, this);
595 r->set_remote_control_id (control_id);
597 rl.push_back (r);
600 } else {
601 /* prohibit auto-connect to master, because there isn't one */
602 bus_profile->output_ac = AutoConnectOption (bus_profile->output_ac & ~AutoConnectMaster);
605 if (!rl.empty()) {
606 add_routes (rl, false, false);
609 /* this allows the user to override settings with an environment variable.
612 if (no_auto_connect()) {
613 bus_profile->input_ac = AutoConnectOption (0);
614 bus_profile->output_ac = AutoConnectOption (0);
617 Config->set_input_auto_connect (bus_profile->input_ac);
618 Config->set_output_auto_connect (bus_profile->output_ac);
621 save_state ("");
623 return 0;
626 void
627 Session::maybe_write_autosave()
629 if (dirty() && record_status() != Recording) {
630 save_state("", true);
634 void
635 Session::remove_pending_capture_state ()
637 sys::path pending_state_file_path(_session_dir->root_path());
639 pending_state_file_path /= legalize_for_path (_current_snapshot_name) + pending_suffix;
643 sys::remove (pending_state_file_path);
645 catch(sys::filesystem_error& ex)
647 error << string_compose(_("Could remove pending capture state at path \"%1\" (%2)"),
648 pending_state_file_path.to_string(), ex.what()) << endmsg;
652 /** Rename a state file.
653 * @param snapshot_name Snapshot name.
655 void
656 Session::rename_state (string old_name, string new_name)
658 if (old_name == _current_snapshot_name || old_name == _name) {
659 /* refuse to rename the current snapshot or the "main" one */
660 return;
663 const string old_xml_filename = legalize_for_path (old_name) + statefile_suffix;
664 const string new_xml_filename = legalize_for_path (new_name) + statefile_suffix;
666 const sys::path old_xml_path = _session_dir->root_path() / old_xml_filename;
667 const sys::path new_xml_path = _session_dir->root_path() / new_xml_filename;
671 sys::rename (old_xml_path, new_xml_path);
673 catch (const sys::filesystem_error& err)
675 error << string_compose(_("could not rename snapshot %1 to %2 (%3)"),
676 old_name, new_name, err.what()) << endmsg;
680 /** Remove a state file.
681 * @param snapshot_name Snapshot name.
683 void
684 Session::remove_state (string snapshot_name)
686 if (snapshot_name == _current_snapshot_name || snapshot_name == _name) {
687 // refuse to remove the current snapshot or the "main" one
688 return;
691 sys::path xml_path(_session_dir->root_path());
693 xml_path /= legalize_for_path (snapshot_name) + statefile_suffix;
695 if (!create_backup_file (xml_path)) {
696 // don't remove it if a backup can't be made
697 // create_backup_file will log the error.
698 return;
701 // and delete it
702 sys::remove (xml_path);
705 #ifdef HAVE_JACK_SESSION
706 void
707 Session::jack_session_event (jack_session_event_t * event)
709 char timebuf[128];
710 time_t n;
711 struct tm local_time;
713 time (&n);
714 localtime_r (&n, &local_time);
715 strftime (timebuf, sizeof(timebuf), "JS_%FT%T", &local_time);
717 if (event->type == JackSessionSaveTemplate)
719 if (save_template( timebuf )) {
720 event->flags = JackSessionSaveError;
721 } else {
722 string cmd ("ardour3 -P -U ");
723 cmd += event->client_uuid;
724 cmd += " -T ";
725 cmd += timebuf;
727 event->command_line = strdup (cmd.c_str());
730 else
732 if (save_state (timebuf)) {
733 event->flags = JackSessionSaveError;
734 } else {
735 sys::path xml_path (_session_dir->root_path());
736 xml_path /= legalize_for_path (timebuf) + statefile_suffix;
738 string cmd ("ardour3 -P -U ");
739 cmd += event->client_uuid;
740 cmd += " \"";
741 cmd += xml_path.to_string();
742 cmd += '\"';
744 event->command_line = strdup (cmd.c_str());
748 jack_session_reply (_engine.jack(), event);
750 if (event->type == JackSessionSaveAndQuit) {
751 Quit (); /* EMIT SIGNAL */
754 jack_session_event_free( event );
756 #endif
759 Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot)
761 XMLTree tree;
762 sys::path xml_path(_session_dir->root_path());
764 if (!_writable || (_state_of_the_state & CannotSave)) {
765 return 1;
768 if (!_engine.connected ()) {
769 error << string_compose (_("the %1 audio engine is not connected and state saving would lose all I/O connections. Session not saved"),
770 PROGRAM_NAME)
771 << endmsg;
772 return 1;
775 /* tell sources we're saving first, in case they write out to a new file
776 * which should be saved with the state rather than the old one */
777 for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i) {
778 i->second->session_saved();
781 tree.set_root (&get_state());
783 if (snapshot_name.empty()) {
784 snapshot_name = _current_snapshot_name;
785 } else if (switch_to_snapshot) {
786 _current_snapshot_name = snapshot_name;
789 if (!pending) {
791 /* proper save: use statefile_suffix (.ardour in English) */
793 xml_path /= legalize_for_path (snapshot_name) + statefile_suffix;
795 /* make a backup copy of the old file */
797 if (sys::exists(xml_path) && !create_backup_file (xml_path)) {
798 // create_backup_file will log the error
799 return -1;
802 } else {
804 /* pending save: use pending_suffix (.pending in English) */
805 xml_path /= legalize_for_path (snapshot_name) + pending_suffix;
808 sys::path tmp_path(_session_dir->root_path());
810 tmp_path /= legalize_for_path (snapshot_name) + temp_suffix;
812 // cerr << "actually writing state to " << xml_path.to_string() << endl;
814 if (!tree.write (tmp_path.to_string())) {
815 error << string_compose (_("state could not be saved to %1"), tmp_path.to_string()) << endmsg;
816 sys::remove (tmp_path);
817 return -1;
819 } else {
821 if (rename (tmp_path.to_string().c_str(), xml_path.to_string().c_str()) != 0) {
822 error << string_compose (_("could not rename temporary session file %1 to %2"),
823 tmp_path.to_string(), xml_path.to_string()) << endmsg;
824 sys::remove (tmp_path);
825 return -1;
829 if (!pending) {
831 save_history (snapshot_name);
833 bool was_dirty = dirty();
835 _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty);
837 if (was_dirty) {
838 DirtyChanged (); /* EMIT SIGNAL */
841 StateSaved (snapshot_name); /* EMIT SIGNAL */
844 return 0;
848 Session::restore_state (string snapshot_name)
850 if (load_state (snapshot_name) == 0) {
851 set_state (*state_tree->root(), Stateful::loading_state_version);
854 return 0;
858 Session::load_state (string snapshot_name)
860 delete state_tree;
861 state_tree = 0;
863 state_was_pending = false;
865 /* check for leftover pending state from a crashed capture attempt */
867 sys::path xmlpath(_session_dir->root_path());
868 xmlpath /= legalize_for_path (snapshot_name) + pending_suffix;
870 if (sys::exists (xmlpath)) {
872 /* there is pending state from a crashed capture attempt */
874 boost::optional<int> r = AskAboutPendingState();
875 if (r.get_value_or (1)) {
876 state_was_pending = true;
880 if (!state_was_pending) {
881 xmlpath = _session_dir->root_path();
882 xmlpath /= snapshot_name;
885 if (!sys::exists (xmlpath)) {
886 xmlpath = _session_dir->root_path();
887 xmlpath /= legalize_for_path (snapshot_name) + statefile_suffix;
888 if (!sys::exists (xmlpath)) {
889 error << string_compose(_("%1: session state information file \"%2\" doesn't exist!"), _name, xmlpath.to_string()) << endmsg;
890 return 1;
894 state_tree = new XMLTree;
896 set_dirty();
898 _writable = exists_and_writable (xmlpath);
900 if (!state_tree->read (xmlpath.to_string())) {
901 error << string_compose(_("Could not understand ardour file %1"), xmlpath.to_string()) << endmsg;
902 delete state_tree;
903 state_tree = 0;
904 return -1;
907 XMLNode& root (*state_tree->root());
909 if (root.name() != X_("Session")) {
910 error << string_compose (_("Session file %1 is not a session"), xmlpath.to_string()) << endmsg;
911 delete state_tree;
912 state_tree = 0;
913 return -1;
916 const XMLProperty* prop;
918 if ((prop = root.property ("version")) == 0) {
919 /* no version implies very old version of Ardour */
920 Stateful::loading_state_version = 1000;
921 } else {
922 int major;
923 int minor;
924 int micro;
926 sscanf (prop->value().c_str(), "%d.%d.%d", &major, &minor, &micro);
927 Stateful::loading_state_version = (major * 1000) + minor;
930 if (Stateful::loading_state_version < CURRENT_SESSION_FILE_VERSION) {
932 sys::path backup_path(_session_dir->root_path());
934 backup_path /= legalize_for_path (snapshot_name) + "-1" + statefile_suffix;
936 // only create a backup once
937 if (sys::exists (backup_path)) {
938 return 0;
941 info << string_compose (_("Copying old session file %1 to %2\nUse %2 with %3 versions before 2.0 from now on"),
942 xmlpath.to_string(), backup_path.to_string(), PROGRAM_NAME)
943 << endmsg;
947 sys::copy_file (xmlpath, backup_path);
949 catch(sys::filesystem_error& ex)
951 error << string_compose (_("Unable to make backup of state file %1 (%2)"),
952 xmlpath.to_string(), ex.what())
953 << endmsg;
954 return -1;
958 return 0;
962 Session::load_options (const XMLNode& node)
964 LocaleGuard lg (X_("POSIX"));
965 config.set_variables (node);
966 return 0;
969 XMLNode&
970 Session::get_state()
972 return state(true);
975 XMLNode&
976 Session::get_template()
978 /* if we don't disable rec-enable, diskstreams
979 will believe they need to store their capture
980 sources in their state node.
983 disable_record (false);
985 return state(false);
988 XMLNode&
989 Session::state(bool full_state)
991 XMLNode* node = new XMLNode("Session");
992 XMLNode* child;
994 // store libardour version, just in case
995 char buf[16];
996 snprintf(buf, sizeof(buf), "%d.%d.%d", libardour3_major_version, libardour3_minor_version, libardour3_micro_version);
997 node->add_property("version", string(buf));
999 /* store configuration settings */
1001 if (full_state) {
1003 node->add_property ("name", _name);
1004 snprintf (buf, sizeof (buf), "%" PRId64, _nominal_frame_rate);
1005 node->add_property ("sample-rate", buf);
1007 if (session_dirs.size() > 1) {
1009 string p;
1011 vector<space_and_path>::iterator i = session_dirs.begin();
1012 vector<space_and_path>::iterator next;
1014 ++i; /* skip the first one */
1015 next = i;
1016 ++next;
1018 while (i != session_dirs.end()) {
1020 p += (*i).path;
1022 if (next != session_dirs.end()) {
1023 p += ':';
1024 } else {
1025 break;
1028 ++next;
1029 ++i;
1032 child = node->add_child ("Path");
1033 child->add_content (p);
1037 /* save the ID counter */
1039 snprintf (buf, sizeof (buf), "%" PRIu64, ID::counter());
1040 node->add_property ("id-counter", buf);
1042 /* save the event ID counter */
1044 snprintf (buf, sizeof (buf), "%d", Evoral::event_id_counter());
1045 node->add_property ("event-counter", buf);
1047 /* various options */
1049 node->add_child_nocopy (config.get_variables ());
1051 node->add_child_nocopy (_metadata->get_state());
1053 child = node->add_child ("Sources");
1055 if (full_state) {
1056 Glib::Mutex::Lock sl (source_lock);
1058 for (SourceMap::iterator siter = sources.begin(); siter != sources.end(); ++siter) {
1060 /* Don't save information about non-destructive file sources that are empty
1061 and unused by any regions.
1064 boost::shared_ptr<FileSource> fs;
1065 if ((fs = boost::dynamic_pointer_cast<FileSource> (siter->second)) != 0) {
1066 if (!fs->destructive()) {
1067 if (fs->empty() && !fs->used()) {
1068 continue;
1073 child->add_child_nocopy (siter->second->get_state());
1077 child = node->add_child ("Regions");
1079 if (full_state) {
1080 Glib::Mutex::Lock rl (region_lock);
1081 const RegionFactory::RegionMap& region_map (RegionFactory::all_regions());
1082 for (RegionFactory::RegionMap::const_iterator i = region_map.begin(); i != region_map.end(); ++i) {
1083 boost::shared_ptr<Region> r = i->second;
1084 /* only store regions not attached to playlists */
1085 if (r->playlist() == 0) {
1086 child->add_child_nocopy (r->state ());
1091 if (full_state) {
1092 node->add_child_nocopy (_locations->get_state());
1093 } else {
1094 // for a template, just create a new Locations, populate it
1095 // with the default start and end, and get the state for that.
1096 Locations loc (*this);
1097 Location* range = new Location (*this, 0, 0, _("session"), Location::IsSessionRange);
1098 range->set (max_framepos, 0);
1099 loc.add (range);
1100 node->add_child_nocopy (loc.get_state());
1103 child = node->add_child ("Bundles");
1105 boost::shared_ptr<BundleList> bundles = _bundles.reader ();
1106 for (BundleList::iterator i = bundles->begin(); i != bundles->end(); ++i) {
1107 boost::shared_ptr<UserBundle> b = boost::dynamic_pointer_cast<UserBundle> (*i);
1108 if (b) {
1109 child->add_child_nocopy (b->get_state());
1114 child = node->add_child ("Routes");
1116 boost::shared_ptr<RouteList> r = routes.reader ();
1118 RoutePublicOrderSorter cmp;
1119 RouteList public_order (*r);
1120 public_order.sort (cmp);
1122 /* the sort should have put control outs first */
1124 if (_monitor_out) {
1125 assert (_monitor_out == public_order.front());
1128 for (RouteList::iterator i = public_order.begin(); i != public_order.end(); ++i) {
1129 if (!(*i)->is_hidden()) {
1130 if (full_state) {
1131 child->add_child_nocopy ((*i)->get_state());
1132 } else {
1133 child->add_child_nocopy ((*i)->get_template());
1139 playlists->add_state (node, full_state);
1141 child = node->add_child ("RouteGroups");
1142 for (list<RouteGroup *>::iterator i = _route_groups.begin(); i != _route_groups.end(); ++i) {
1143 child->add_child_nocopy ((*i)->get_state());
1146 if (_click_io) {
1147 child = node->add_child ("Click");
1148 child->add_child_nocopy (_click_io->state (full_state));
1151 if (full_state) {
1152 child = node->add_child ("NamedSelections");
1153 for (NamedSelectionList::iterator i = named_selections.begin(); i != named_selections.end(); ++i) {
1154 if (full_state) {
1155 child->add_child_nocopy ((*i)->get_state());
1160 node->add_child_nocopy (_speakers->get_state());
1161 node->add_child_nocopy (_tempo_map->get_state());
1162 node->add_child_nocopy (get_control_protocol_state());
1164 if (_extra_xml) {
1165 node->add_child_copy (*_extra_xml);
1168 return *node;
1171 XMLNode&
1172 Session::get_control_protocol_state ()
1174 ControlProtocolManager& cpm (ControlProtocolManager::instance());
1175 return cpm.get_state();
1179 Session::set_state (const XMLNode& node, int version)
1181 XMLNodeList nlist;
1182 XMLNode* child;
1183 const XMLProperty* prop;
1184 int ret = -1;
1186 _state_of_the_state = StateOfTheState (_state_of_the_state|CannotSave);
1188 if (node.name() != X_("Session")) {
1189 fatal << _("programming error: Session: incorrect XML node sent to set_state()") << endmsg;
1190 return -1;
1193 if ((prop = node.property ("version")) != 0) {
1194 version = atoi (prop->value ()) * 1000;
1197 if ((prop = node.property ("name")) != 0) {
1198 _name = prop->value ();
1201 if ((prop = node.property (X_("sample-rate"))) != 0) {
1203 _nominal_frame_rate = atoi (prop->value());
1205 if (_nominal_frame_rate != _current_frame_rate) {
1206 boost::optional<int> r = AskAboutSampleRateMismatch (_nominal_frame_rate, _current_frame_rate);
1207 if (r.get_value_or (0)) {
1208 return -1;
1213 setup_raid_path(_session_dir->root_path().to_string());
1215 if ((prop = node.property (X_("id-counter"))) != 0) {
1216 uint64_t x;
1217 sscanf (prop->value().c_str(), "%" PRIu64, &x);
1218 ID::init_counter (x);
1219 } else {
1220 /* old sessions used a timebased counter, so fake
1221 the startup ID counter based on a standard
1222 timestamp.
1224 time_t now;
1225 time (&now);
1226 ID::init_counter (now);
1229 if ((prop = node.property (X_("event-counter"))) != 0) {
1230 Evoral::init_event_id_counter (atoi (prop->value()));
1233 IO::disable_connecting ();
1235 if ((child = find_named_node (node, "Extra")) != 0) {
1236 _extra_xml = new XMLNode (*child);
1239 if (((child = find_named_node (node, "Options")) != 0)) { /* old style */
1240 load_options (*child);
1241 } else if ((child = find_named_node (node, "Config")) != 0) { /* new style */
1242 load_options (*child);
1243 } else {
1244 error << _("Session: XML state has no options section") << endmsg;
1247 if (version >= 3000) {
1248 if ((child = find_named_node (node, "Metadata")) == 0) {
1249 warning << _("Session: XML state has no metadata section") << endmsg;
1250 } else if (_metadata->set_state (*child, version)) {
1251 goto out;
1255 if ((child = find_named_node (node, "Locations")) == 0) {
1256 error << _("Session: XML state has no locations section") << endmsg;
1257 goto out;
1258 } else if (_locations->set_state (*child, version)) {
1259 goto out;
1262 if ((child = find_named_node (node, X_("Speakers"))) != 0) {
1263 _speakers->set_state (*child, version);
1266 Location* location;
1268 if ((location = _locations->auto_loop_location()) != 0) {
1269 set_auto_loop_location (location);
1272 if ((location = _locations->auto_punch_location()) != 0) {
1273 set_auto_punch_location (location);
1276 if ((location = _locations->session_range_location()) != 0) {
1277 delete _session_range_location;
1278 _session_range_location = location;
1281 if (_session_range_location) {
1282 AudioFileSource::set_header_position_offset (_session_range_location->start());
1285 if ((child = find_named_node (node, "Sources")) == 0) {
1286 error << _("Session: XML state has no sources section") << endmsg;
1287 goto out;
1288 } else if (load_sources (*child)) {
1289 goto out;
1292 if ((child = find_named_node (node, "TempoMap")) == 0) {
1293 error << _("Session: XML state has no Tempo Map section") << endmsg;
1294 goto out;
1295 } else if (_tempo_map->set_state (*child, version)) {
1296 goto out;
1299 if ((child = find_named_node (node, "Regions")) == 0) {
1300 error << _("Session: XML state has no Regions section") << endmsg;
1301 goto out;
1302 } else if (load_regions (*child)) {
1303 goto out;
1306 if ((child = find_named_node (node, "Playlists")) == 0) {
1307 error << _("Session: XML state has no playlists section") << endmsg;
1308 goto out;
1309 } else if (playlists->load (*this, *child)) {
1310 goto out;
1313 if ((child = find_named_node (node, "UnusedPlaylists")) == 0) {
1314 // this is OK
1315 } else if (playlists->load_unused (*this, *child)) {
1316 goto out;
1319 if ((child = find_named_node (node, "NamedSelections")) != 0) {
1320 if (load_named_selections (*child)) {
1321 goto out;
1325 if (version >= 3000) {
1326 if ((child = find_named_node (node, "Bundles")) == 0) {
1327 warning << _("Session: XML state has no bundles section") << endmsg;
1328 //goto out;
1329 } else {
1330 /* We can't load Bundles yet as they need to be able
1331 to convert from port names to Port objects, which can't happen until
1332 later */
1333 _bundle_xml_node = new XMLNode (*child);
1337 if (version < 3000) {
1338 if ((child = find_named_node (node, X_("DiskStreams"))) == 0) {
1339 error << _("Session: XML state has no diskstreams section") << endmsg;
1340 goto out;
1341 } else if (load_diskstreams_2X (*child, version)) {
1342 goto out;
1346 if ((child = find_named_node (node, "Routes")) == 0) {
1347 error << _("Session: XML state has no routes section") << endmsg;
1348 goto out;
1349 } else if (load_routes (*child, version)) {
1350 goto out;
1353 /* our diskstreams list is no longer needed as they are now all owned by their Route */
1354 _diskstreams_2X.clear ();
1356 if (version >= 3000) {
1358 if ((child = find_named_node (node, "RouteGroups")) == 0) {
1359 error << _("Session: XML state has no route groups section") << endmsg;
1360 goto out;
1361 } else if (load_route_groups (*child, version)) {
1362 goto out;
1365 } else if (version < 3000) {
1367 if ((child = find_named_node (node, "EditGroups")) == 0) {
1368 error << _("Session: XML state has no edit groups section") << endmsg;
1369 goto out;
1370 } else if (load_route_groups (*child, version)) {
1371 goto out;
1374 if ((child = find_named_node (node, "MixGroups")) == 0) {
1375 error << _("Session: XML state has no mix groups section") << endmsg;
1376 goto out;
1377 } else if (load_route_groups (*child, version)) {
1378 goto out;
1382 if ((child = find_named_node (node, "Click")) == 0) {
1383 warning << _("Session: XML state has no click section") << endmsg;
1384 } else if (_click_io) {
1385 _click_io->set_state (*child, version);
1388 if ((child = find_named_node (node, "ControlProtocols")) != 0) {
1389 ControlProtocolManager::instance().set_protocol_states (*child);
1392 /* here beginneth the second phase ... */
1394 StateReady (); /* EMIT SIGNAL */
1396 return 0;
1398 out:
1399 return ret;
1403 Session::load_routes (const XMLNode& node, int version)
1405 XMLNodeList nlist;
1406 XMLNodeConstIterator niter;
1407 RouteList new_routes;
1409 nlist = node.children();
1411 set_dirty();
1413 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1415 boost::shared_ptr<Route> route;
1416 if (version < 3000) {
1417 route = XMLRouteFactory_2X (**niter, version);
1418 } else {
1419 route = XMLRouteFactory (**niter, version);
1422 if (route == 0) {
1423 error << _("Session: cannot create Route from XML description.") << endmsg;
1424 return -1;
1427 BootMessage (string_compose (_("Loaded track/bus %1"), route->name()));
1429 new_routes.push_back (route);
1432 add_routes (new_routes, false, false);
1434 return 0;
1437 boost::shared_ptr<Route>
1438 Session::XMLRouteFactory (const XMLNode& node, int version)
1440 boost::shared_ptr<Route> ret;
1442 if (node.name() != "Route") {
1443 return ret;
1446 XMLNode* ds_child = find_named_node (node, X_("Diskstream"));
1448 DataType type = DataType::AUDIO;
1449 const XMLProperty* prop = node.property("default-type");
1451 if (prop) {
1452 type = DataType (prop->value());
1455 assert (type != DataType::NIL);
1457 if (ds_child) {
1459 boost::shared_ptr<Track> track;
1461 if (type == DataType::AUDIO) {
1462 track.reset (new AudioTrack (*this, X_("toBeResetFroXML")));
1463 } else {
1464 track.reset (new MidiTrack (*this, X_("toBeResetFroXML")));
1467 if (track->init()) {
1468 return ret;
1471 if (track->set_state (node, version)) {
1472 return ret;
1475 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
1476 boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
1477 #endif
1478 ret = track;
1480 } else {
1481 boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML")));
1483 if (r->init () == 0 && r->set_state (node, version) == 0) {
1484 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
1485 boost_debug_shared_ptr_mark_interesting (r.get(), "Route");
1486 #endif
1487 ret = r;
1491 return ret;
1494 boost::shared_ptr<Route>
1495 Session::XMLRouteFactory_2X (const XMLNode& node, int version)
1497 boost::shared_ptr<Route> ret;
1499 if (node.name() != "Route") {
1500 return ret;
1503 XMLProperty const * ds_prop = node.property (X_("diskstream-id"));
1504 if (!ds_prop) {
1505 ds_prop = node.property (X_("diskstream"));
1508 DataType type = DataType::AUDIO;
1509 const XMLProperty* prop = node.property("default-type");
1511 if (prop) {
1512 type = DataType (prop->value());
1515 assert (type != DataType::NIL);
1517 if (ds_prop) {
1519 list<boost::shared_ptr<Diskstream> >::iterator i = _diskstreams_2X.begin ();
1520 while (i != _diskstreams_2X.end() && (*i)->id() != ds_prop->value()) {
1521 ++i;
1524 if (i == _diskstreams_2X.end()) {
1525 error << _("Could not find diskstream for route") << endmsg;
1526 return boost::shared_ptr<Route> ();
1529 boost::shared_ptr<Track> track;
1531 if (type == DataType::AUDIO) {
1532 track.reset (new AudioTrack (*this, X_("toBeResetFroXML")));
1533 } else {
1534 track.reset (new MidiTrack (*this, X_("toBeResetFroXML")));
1537 if (track->init()) {
1538 return ret;
1541 if (track->set_state (node, version)) {
1542 return ret;
1545 track->set_diskstream (*i);
1547 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
1548 boost_debug_shared_ptr_mark_interesting (track.get(), "Track");
1549 #endif
1550 ret = track;
1552 } else {
1553 boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML")));
1555 if (r->init () == 0 && r->set_state (node, version) == 0) {
1556 #ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
1557 boost_debug_shared_ptr_mark_interesting (rt, "Route");
1558 #endif
1559 ret = r;
1563 return ret;
1567 Session::load_regions (const XMLNode& node)
1569 XMLNodeList nlist;
1570 XMLNodeConstIterator niter;
1571 boost::shared_ptr<Region> region;
1573 nlist = node.children();
1575 set_dirty();
1577 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1578 if ((region = XMLRegionFactory (**niter, false)) == 0) {
1579 error << _("Session: cannot create Region from XML description.");
1580 const XMLProperty *name = (**niter).property("name");
1582 if (name) {
1583 error << " " << string_compose (_("Can not load state for region '%1'"), name->value());
1586 error << endmsg;
1590 return 0;
1593 boost::shared_ptr<Region>
1594 Session::XMLRegionFactory (const XMLNode& node, bool full)
1596 const XMLProperty* type = node.property("type");
1598 try {
1600 if (!type || type->value() == "audio") {
1601 return boost::shared_ptr<Region>(XMLAudioRegionFactory (node, full));
1602 } else if (type->value() == "midi") {
1603 return boost::shared_ptr<Region>(XMLMidiRegionFactory (node, full));
1606 } catch (failed_constructor& err) {
1607 return boost::shared_ptr<Region> ();
1610 return boost::shared_ptr<Region> ();
1613 boost::shared_ptr<AudioRegion>
1614 Session::XMLAudioRegionFactory (const XMLNode& node, bool /*full*/)
1616 const XMLProperty* prop;
1617 boost::shared_ptr<Source> source;
1618 boost::shared_ptr<AudioSource> as;
1619 SourceList sources;
1620 SourceList master_sources;
1621 uint32_t nchans = 1;
1622 char buf[128];
1624 if (node.name() != X_("Region")) {
1625 return boost::shared_ptr<AudioRegion>();
1628 if ((prop = node.property (X_("channels"))) != 0) {
1629 nchans = atoi (prop->value().c_str());
1632 if ((prop = node.property ("name")) == 0) {
1633 cerr << "no name for this region\n";
1634 abort ();
1637 if ((prop = node.property (X_("source-0"))) == 0) {
1638 if ((prop = node.property ("source")) == 0) {
1639 error << _("Session: XMLNode describing a AudioRegion is incomplete (no source)") << endmsg;
1640 return boost::shared_ptr<AudioRegion>();
1644 PBD::ID s_id (prop->value());
1646 if ((source = source_by_id (s_id)) == 0) {
1647 error << string_compose(_("Session: XMLNode describing a AudioRegion references an unknown source id =%1"), s_id) << endmsg;
1648 return boost::shared_ptr<AudioRegion>();
1651 as = boost::dynamic_pointer_cast<AudioSource>(source);
1652 if (!as) {
1653 error << string_compose(_("Session: XMLNode describing a AudioRegion references a non-audio source id =%1"), s_id) << endmsg;
1654 return boost::shared_ptr<AudioRegion>();
1657 sources.push_back (as);
1659 /* pickup other channels */
1661 for (uint32_t n=1; n < nchans; ++n) {
1662 snprintf (buf, sizeof(buf), X_("source-%d"), n);
1663 if ((prop = node.property (buf)) != 0) {
1665 PBD::ID id2 (prop->value());
1667 if ((source = source_by_id (id2)) == 0) {
1668 error << string_compose(_("Session: XMLNode describing a AudioRegion references an unknown source id =%1"), id2) << endmsg;
1669 return boost::shared_ptr<AudioRegion>();
1672 as = boost::dynamic_pointer_cast<AudioSource>(source);
1673 if (!as) {
1674 error << string_compose(_("Session: XMLNode describing a AudioRegion references a non-audio source id =%1"), id2) << endmsg;
1675 return boost::shared_ptr<AudioRegion>();
1677 sources.push_back (as);
1681 for (uint32_t n = 0; n < nchans; ++n) {
1682 snprintf (buf, sizeof(buf), X_("master-source-%d"), n);
1683 if ((prop = node.property (buf)) != 0) {
1685 PBD::ID id2 (prop->value());
1687 if ((source = source_by_id (id2)) == 0) {
1688 error << string_compose(_("Session: XMLNode describing a AudioRegion references an unknown source id =%1"), id2) << endmsg;
1689 return boost::shared_ptr<AudioRegion>();
1692 as = boost::dynamic_pointer_cast<AudioSource>(source);
1693 if (!as) {
1694 error << string_compose(_("Session: XMLNode describing a AudioRegion references a non-audio source id =%1"), id2) << endmsg;
1695 return boost::shared_ptr<AudioRegion>();
1697 master_sources.push_back (as);
1701 try {
1702 boost::shared_ptr<AudioRegion> region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (sources, node)));
1704 /* a final detail: this is the one and only place that we know how long missing files are */
1706 if (region->whole_file()) {
1707 for (SourceList::iterator sx = sources.begin(); sx != sources.end(); ++sx) {
1708 boost::shared_ptr<SilentFileSource> sfp = boost::dynamic_pointer_cast<SilentFileSource> (*sx);
1709 if (sfp) {
1710 sfp->set_length (region->length());
1715 if (!master_sources.empty()) {
1716 if (master_sources.size() != nchans) {
1717 error << _("Session: XMLNode describing an AudioRegion is missing some master sources; ignored") << endmsg;
1718 } else {
1719 region->set_master_sources (master_sources);
1723 return region;
1727 catch (failed_constructor& err) {
1728 return boost::shared_ptr<AudioRegion>();
1732 boost::shared_ptr<MidiRegion>
1733 Session::XMLMidiRegionFactory (const XMLNode& node, bool /*full*/)
1735 const XMLProperty* prop;
1736 boost::shared_ptr<Source> source;
1737 boost::shared_ptr<MidiSource> ms;
1738 SourceList sources;
1740 if (node.name() != X_("Region")) {
1741 return boost::shared_ptr<MidiRegion>();
1744 if ((prop = node.property ("name")) == 0) {
1745 cerr << "no name for this region\n";
1746 abort ();
1749 if ((prop = node.property (X_("source-0"))) == 0) {
1750 if ((prop = node.property ("source")) == 0) {
1751 error << _("Session: XMLNode describing a MidiRegion is incomplete (no source)") << endmsg;
1752 return boost::shared_ptr<MidiRegion>();
1756 PBD::ID s_id (prop->value());
1758 if ((source = source_by_id (s_id)) == 0) {
1759 error << string_compose(_("Session: XMLNode describing a MidiRegion references an unknown source id =%1"), s_id) << endmsg;
1760 return boost::shared_ptr<MidiRegion>();
1763 ms = boost::dynamic_pointer_cast<MidiSource>(source);
1764 if (!ms) {
1765 error << string_compose(_("Session: XMLNode describing a MidiRegion references a non-midi source id =%1"), s_id) << endmsg;
1766 return boost::shared_ptr<MidiRegion>();
1769 sources.push_back (ms);
1771 try {
1772 boost::shared_ptr<MidiRegion> region (boost::dynamic_pointer_cast<MidiRegion> (RegionFactory::create (sources, node)));
1773 /* a final detail: this is the one and only place that we know how long missing files are */
1775 if (region->whole_file()) {
1776 for (SourceList::iterator sx = sources.begin(); sx != sources.end(); ++sx) {
1777 boost::shared_ptr<SilentFileSource> sfp = boost::dynamic_pointer_cast<SilentFileSource> (*sx);
1778 if (sfp) {
1779 sfp->set_length (region->length());
1784 return region;
1787 catch (failed_constructor& err) {
1788 return boost::shared_ptr<MidiRegion>();
1792 XMLNode&
1793 Session::get_sources_as_xml ()
1796 XMLNode* node = new XMLNode (X_("Sources"));
1797 Glib::Mutex::Lock lm (source_lock);
1799 for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
1800 node->add_child_nocopy (i->second->get_state());
1803 return *node;
1806 string
1807 Session::path_from_region_name (DataType type, string name, string identifier)
1809 char buf[PATH_MAX+1];
1810 uint32_t n;
1811 SessionDirectory sdir(get_best_session_directory_for_new_source());
1812 sys::path source_dir = ((type == DataType::AUDIO)
1813 ? sdir.sound_path() : sdir.midi_path());
1815 string ext = native_header_format_extension (config.get_native_file_header_format(), type);
1817 for (n = 0; n < 999999; ++n) {
1818 if (identifier.length()) {
1819 snprintf (buf, sizeof(buf), "%s%s%" PRIu32 "%s", name.c_str(),
1820 identifier.c_str(), n, ext.c_str());
1821 } else {
1822 snprintf (buf, sizeof(buf), "%s-%" PRIu32 "%s", name.c_str(),
1823 n, ext.c_str());
1826 sys::path source_path = source_dir / buf;
1828 if (!sys::exists (source_path)) {
1829 return source_path.to_string();
1833 error << string_compose (_("cannot create new file from region name \"%1\" with ident = \"%2\": too many existing files with similar names"),
1834 name, identifier)
1835 << endmsg;
1837 return "";
1842 Session::load_sources (const XMLNode& node)
1844 XMLNodeList nlist;
1845 XMLNodeConstIterator niter;
1846 boost::shared_ptr<Source> source;
1848 nlist = node.children();
1850 set_dirty();
1852 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1853 retry:
1854 try {
1855 if ((source = XMLSourceFactory (**niter)) == 0) {
1856 error << _("Session: cannot create Source from XML description.") << endmsg;
1859 } catch (MissingSource& err) {
1861 int user_choice;
1863 if (!no_questions_about_missing_files) {
1864 user_choice = MissingFile (this, err.path, err.type).get_value_or (-1);
1865 } else {
1866 user_choice = -2;
1869 switch (user_choice) {
1870 case 0:
1871 /* user added a new search location, so try again */
1872 goto retry;
1875 case 1:
1876 /* user asked to quit the entire session load
1878 return -1;
1880 case 2:
1881 no_questions_about_missing_files = true;
1882 goto retry;
1884 case 3:
1885 no_questions_about_missing_files = true;
1886 /* fallthru */
1888 case -1:
1889 default:
1890 warning << _("A sound file is missing. It will be replaced by silence.") << endmsg;
1891 source = SourceFactory::createSilent (*this, **niter, max_framecnt, _current_frame_rate);
1892 break;
1897 return 0;
1900 boost::shared_ptr<Source>
1901 Session::XMLSourceFactory (const XMLNode& node)
1903 if (node.name() != "Source") {
1904 return boost::shared_ptr<Source>();
1907 try {
1908 /* note: do peak building in another thread when loading session state */
1909 return SourceFactory::create (*this, node, true);
1912 catch (failed_constructor& err) {
1913 error << string_compose (_("Found a sound file that cannot be used by %1. Talk to the progammers."), PROGRAM_NAME) << endmsg;
1914 return boost::shared_ptr<Source>();
1919 Session::save_template (string template_name)
1921 XMLTree tree;
1923 if (_state_of_the_state & CannotSave) {
1924 return -1;
1927 sys::path user_template_dir(user_template_directory());
1931 sys::create_directories (user_template_dir);
1933 catch(sys::filesystem_error& ex)
1935 error << string_compose(_("Could not create mix templates directory \"%1\" (%2)"),
1936 user_template_dir.to_string(), ex.what()) << endmsg;
1937 return -1;
1940 tree.set_root (&get_template());
1942 sys::path template_file_path(user_template_dir);
1943 template_file_path /= template_name + template_suffix;
1945 if (sys::exists (template_file_path))
1947 warning << string_compose(_("Template \"%1\" already exists - new version not created"),
1948 template_file_path.to_string()) << endmsg;
1949 return -1;
1952 if (!tree.write (template_file_path.to_string())) {
1953 error << _("template not saved") << endmsg;
1954 return -1;
1957 return 0;
1961 Session::rename_template (string old_name, string new_name)
1963 sys::path old_path (user_template_directory());
1964 old_path /= old_name + template_suffix;
1966 sys::path new_path(user_template_directory());
1967 new_path /= new_name + template_suffix;
1969 if (sys::exists (new_path)) {
1970 warning << string_compose(_("Template \"%1\" already exists - template not renamed"),
1971 new_path.to_string()) << endmsg;
1972 return -1;
1975 try {
1976 sys::rename (old_path, new_path);
1977 return 0;
1978 } catch (...) {
1979 return -1;
1984 Session::delete_template (string name)
1986 sys::path path = user_template_directory();
1987 path /= name + template_suffix;
1989 try {
1990 sys::remove (path);
1991 return 0;
1992 } catch (...) {
1993 return -1;
1997 void
1998 Session::refresh_disk_space ()
2000 #if HAVE_SYS_VFS_H
2001 struct statfs statfsbuf;
2002 vector<space_and_path>::iterator i;
2003 Glib::Mutex::Lock lm (space_lock);
2004 double scale;
2006 /* get freespace on every FS that is part of the session path */
2008 _total_free_4k_blocks = 0;
2010 for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
2011 statfs ((*i).path.c_str(), &statfsbuf);
2013 scale = statfsbuf.f_bsize/4096.0;
2015 (*i).blocks = (uint32_t) floor (statfsbuf.f_bavail * scale);
2016 _total_free_4k_blocks += (*i).blocks;
2018 #endif
2021 string
2022 Session::get_best_session_directory_for_new_source ()
2024 vector<space_and_path>::iterator i;
2025 string result = _session_dir->root_path().to_string();
2027 /* handle common case without system calls */
2029 if (session_dirs.size() == 1) {
2030 return result;
2033 /* OK, here's the algorithm we're following here:
2035 We want to select which directory to use for
2036 the next file source to be created. Ideally,
2037 we'd like to use a round-robin process so as to
2038 get maximum performance benefits from splitting
2039 the files across multiple disks.
2041 However, in situations without much diskspace, an
2042 RR approach may end up filling up a filesystem
2043 with new files while others still have space.
2044 Its therefore important to pay some attention to
2045 the freespace in the filesystem holding each
2046 directory as well. However, if we did that by
2047 itself, we'd keep creating new files in the file
2048 system with the most space until it was as full
2049 as all others, thus negating any performance
2050 benefits of this RAID-1 like approach.
2052 So, we use a user-configurable space threshold. If
2053 there are at least 2 filesystems with more than this
2054 much space available, we use RR selection between them.
2055 If not, then we pick the filesystem with the most space.
2057 This gets a good balance between the two
2058 approaches.
2061 refresh_disk_space ();
2063 int free_enough = 0;
2065 for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
2066 if ((*i).blocks * 4096 >= Config->get_disk_choice_space_threshold()) {
2067 free_enough++;
2071 if (free_enough >= 2) {
2072 /* use RR selection process, ensuring that the one
2073 picked works OK.
2076 i = last_rr_session_dir;
2078 do {
2079 if (++i == session_dirs.end()) {
2080 i = session_dirs.begin();
2083 if ((*i).blocks * 4096 >= Config->get_disk_choice_space_threshold()) {
2084 if (create_session_directory ((*i).path)) {
2085 result = (*i).path;
2086 last_rr_session_dir = i;
2087 return result;
2091 } while (i != last_rr_session_dir);
2093 } else {
2095 /* pick FS with the most freespace (and that
2096 seems to actually work ...)
2099 vector<space_and_path> sorted;
2100 space_and_path_ascending_cmp cmp;
2102 sorted = session_dirs;
2103 sort (sorted.begin(), sorted.end(), cmp);
2105 for (i = sorted.begin(); i != sorted.end(); ++i) {
2106 if (create_session_directory ((*i).path)) {
2107 result = (*i).path;
2108 last_rr_session_dir = i;
2109 return result;
2114 return result;
2118 Session::load_named_selections (const XMLNode& node)
2120 XMLNodeList nlist;
2121 XMLNodeConstIterator niter;
2122 NamedSelection *ns;
2124 nlist = node.children();
2126 set_dirty();
2128 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2130 if ((ns = XMLNamedSelectionFactory (**niter)) == 0) {
2131 error << _("Session: cannot create Named Selection from XML description.") << endmsg;
2135 return 0;
2138 NamedSelection *
2139 Session::XMLNamedSelectionFactory (const XMLNode& node)
2141 try {
2142 return new NamedSelection (*this, node);
2145 catch (failed_constructor& err) {
2146 return 0;
2150 string
2151 Session::automation_dir () const
2153 return Glib::build_filename (_path, "automation");
2156 string
2157 Session::analysis_dir () const
2159 return Glib::build_filename (_path, "analysis");
2162 string
2163 Session::plugins_dir () const
2165 return Glib::build_filename (_path, "plugins");
2169 Session::load_bundles (XMLNode const & node)
2171 XMLNodeList nlist = node.children();
2172 XMLNodeConstIterator niter;
2174 set_dirty();
2176 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2177 if ((*niter)->name() == "InputBundle") {
2178 add_bundle (boost::shared_ptr<UserBundle> (new UserBundle (**niter, true)));
2179 } else if ((*niter)->name() == "OutputBundle") {
2180 add_bundle (boost::shared_ptr<UserBundle> (new UserBundle (**niter, false)));
2181 } else {
2182 error << string_compose(_("Unknown node \"%1\" found in Bundles list from state file"), (*niter)->name()) << endmsg;
2183 return -1;
2187 return 0;
2191 Session::load_route_groups (const XMLNode& node, int version)
2193 XMLNodeList nlist = node.children();
2194 XMLNodeConstIterator niter;
2196 set_dirty ();
2198 if (version >= 3000) {
2200 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2201 if ((*niter)->name() == "RouteGroup") {
2202 RouteGroup* rg = new RouteGroup (*this, "");
2203 add_route_group (rg);
2204 rg->set_state (**niter, version);
2208 } else if (version < 3000) {
2210 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2211 if ((*niter)->name() == "EditGroup" || (*niter)->name() == "MixGroup") {
2212 RouteGroup* rg = new RouteGroup (*this, "");
2213 add_route_group (rg);
2214 rg->set_state (**niter, version);
2219 return 0;
2222 void
2223 Session::auto_save()
2225 save_state (_current_snapshot_name);
2228 static bool
2229 state_file_filter (const string &str, void */*arg*/)
2231 return (str.length() > strlen(statefile_suffix) &&
2232 str.find (statefile_suffix) == (str.length() - strlen (statefile_suffix)));
2235 struct string_cmp {
2236 bool operator()(const string* a, const string* b) {
2237 return *a < *b;
2241 static string*
2242 remove_end(string* state)
2244 string statename(*state);
2246 string::size_type start,end;
2247 if ((start = statename.find_last_of (G_DIR_SEPARATOR)) != string::npos) {
2248 statename = statename.substr (start+1);
2251 if ((end = statename.rfind(".ardour")) == string::npos) {
2252 end = statename.length();
2255 return new string(statename.substr (0, end));
2258 vector<string *> *
2259 Session::possible_states (string path)
2261 PathScanner scanner;
2262 vector<string*>* states = scanner (path, state_file_filter, 0, false, false);
2264 transform(states->begin(), states->end(), states->begin(), remove_end);
2266 string_cmp cmp;
2267 sort (states->begin(), states->end(), cmp);
2269 return states;
2272 vector<string *> *
2273 Session::possible_states () const
2275 return possible_states(_path);
2278 void
2279 Session::add_route_group (RouteGroup* g)
2281 _route_groups.push_back (g);
2282 route_group_added (g); /* EMIT SIGNAL */
2284 g->MembershipChanged.connect_same_thread (*this, boost::bind (&Session::route_group_changed, this));
2285 g->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::route_group_changed, this));
2287 set_dirty ();
2290 void
2291 Session::remove_route_group (RouteGroup& rg)
2293 list<RouteGroup*>::iterator i;
2295 if ((i = find (_route_groups.begin(), _route_groups.end(), &rg)) != _route_groups.end()) {
2296 _route_groups.erase (i);
2297 delete &rg;
2299 route_group_removed (); /* EMIT SIGNAL */
2304 RouteGroup *
2305 Session::route_group_by_name (string name)
2307 list<RouteGroup *>::iterator i;
2309 for (i = _route_groups.begin(); i != _route_groups.end(); ++i) {
2310 if ((*i)->name() == name) {
2311 return* i;
2314 return 0;
2317 RouteGroup&
2318 Session::all_route_group() const
2320 return *_all_route_group;
2323 void
2324 Session::add_commands (vector<Command*> const & cmds)
2326 for (vector<Command*>::const_iterator i = cmds.begin(); i != cmds.end(); ++i) {
2327 add_command (*i);
2331 void
2332 Session::begin_reversible_command (const string& name)
2334 begin_reversible_command (g_quark_from_string (name.c_str ()));
2337 /** Begin a reversible command using a GQuark to identify it.
2338 * begin_reversible_command() and commit_reversible_command() calls may be nested,
2339 * but there must be as many begin...()s as there are commit...()s.
2341 void
2342 Session::begin_reversible_command (GQuark q)
2344 /* If nested begin/commit pairs are used, we create just one UndoTransaction
2345 to hold all the commands that are committed. This keeps the order of
2346 commands correct in the history.
2349 if (_current_trans == 0) {
2350 /* start a new transaction */
2351 assert (_current_trans_quarks.empty ());
2352 _current_trans = new UndoTransaction();
2353 _current_trans->set_name (g_quark_to_string (q));
2356 _current_trans_quarks.push_front (q);
2359 void
2360 Session::commit_reversible_command (Command *cmd)
2362 assert (_current_trans);
2363 assert (!_current_trans_quarks.empty ());
2365 struct timeval now;
2367 if (cmd) {
2368 _current_trans->add_command (cmd);
2371 _current_trans_quarks.pop_front ();
2373 if (!_current_trans_quarks.empty ()) {
2374 /* the transaction we're committing is not the top-level one */
2375 return;
2378 if (_current_trans->empty()) {
2379 /* no commands were added to the transaction, so just get rid of it */
2380 delete _current_trans;
2381 _current_trans = 0;
2382 return;
2385 gettimeofday (&now, 0);
2386 _current_trans->set_timestamp (now);
2388 _history.add (_current_trans);
2389 _current_trans = 0;
2392 static bool
2393 accept_all_audio_files (const string& path, void */*arg*/)
2395 if (!Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR)) {
2396 return false;
2399 if (!AudioFileSource::safe_audio_file_extension (path)) {
2400 return false;
2403 return true;
2406 static bool
2407 accept_all_midi_files (const string& path, void */*arg*/)
2409 if (!Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR)) {
2410 return false;
2413 return ((path.length() > 4 && path.find (".mid") != (path.length() - 4)) ||
2414 (path.length() > 4 && path.find (".smf") != (path.length() - 4)) ||
2415 (path.length() > 5 && path.find (".midi") != (path.length() - 5)));
2418 static bool
2419 accept_all_state_files (const string& path, void */*arg*/)
2421 if (!Glib::file_test (path, Glib::FILE_TEST_IS_REGULAR)) {
2422 return false;
2425 return (path.length() > 7 && path.find (".ardour") == (path.length() - 7));
2429 Session::find_all_sources (string path, set<string>& result)
2431 XMLTree tree;
2432 XMLNode* node;
2434 if (!tree.read (path)) {
2435 return -1;
2438 if ((node = find_named_node (*tree.root(), "Sources")) == 0) {
2439 return -2;
2442 XMLNodeList nlist;
2443 XMLNodeConstIterator niter;
2445 nlist = node->children();
2447 set_dirty();
2449 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2451 XMLProperty* prop;
2453 if ((prop = (*niter)->property (X_("type"))) == 0) {
2454 continue;
2457 DataType type (prop->value());
2459 if ((prop = (*niter)->property (X_("name"))) == 0) {
2460 continue;
2463 if (Glib::path_is_absolute (prop->value())) {
2464 /* external file, ignore */
2465 continue;
2468 string found_path;
2469 bool is_new;
2470 uint16_t chan;
2472 if (FileSource::find (*this, type, prop->value(), true, is_new, chan, found_path)) {
2473 result.insert (found_path);
2477 return 0;
2481 Session::find_all_sources_across_snapshots (set<string>& result, bool exclude_this_snapshot)
2483 PathScanner scanner;
2484 vector<string*>* state_files;
2485 string ripped;
2486 string this_snapshot_path;
2488 result.clear ();
2490 ripped = _path;
2492 if (ripped[ripped.length()-1] == G_DIR_SEPARATOR) {
2493 ripped = ripped.substr (0, ripped.length() - 1);
2496 state_files = scanner (ripped, accept_all_state_files, (void *) 0, false, true);
2498 if (state_files == 0) {
2499 /* impossible! */
2500 return 0;
2503 this_snapshot_path = _path;
2504 this_snapshot_path += legalize_for_path (_current_snapshot_name);
2505 this_snapshot_path += statefile_suffix;
2507 for (vector<string*>::iterator i = state_files->begin(); i != state_files->end(); ++i) {
2509 if (exclude_this_snapshot && **i == this_snapshot_path) {
2510 continue;
2513 if (find_all_sources (**i, result) < 0) {
2514 return -1;
2518 return 0;
2521 struct RegionCounter {
2522 typedef std::map<PBD::ID,boost::shared_ptr<AudioSource> > AudioSourceList;
2523 AudioSourceList::iterator iter;
2524 boost::shared_ptr<Region> region;
2525 uint32_t count;
2527 RegionCounter() : count (0) {}
2531 Session::ask_about_playlist_deletion (boost::shared_ptr<Playlist> p)
2533 boost::optional<int> r = AskAboutPlaylistDeletion (p);
2534 return r.get_value_or (1);
2537 void
2538 Session::cleanup_regions ()
2540 const RegionFactory::RegionMap& regions (RegionFactory::regions());
2542 for (RegionFactory::RegionMap::const_iterator i = regions.begin(); i != regions.end(); ++i) {
2544 boost::shared_ptr<AudioRegion> audio_region = boost::dynamic_pointer_cast<AudioRegion>( i->second);
2546 if (!audio_region) {
2547 continue;
2550 uint32_t used = playlists->region_use_count (audio_region);
2552 if (used == 0 && !audio_region->automatic()) {
2553 RegionFactory::map_remove(i->second);
2557 /* dump the history list */
2558 _history.clear ();
2560 save_state ("");
2564 Session::cleanup_sources (CleanupReport& rep)
2566 // FIXME: needs adaptation to midi
2568 vector<boost::shared_ptr<Source> > dead_sources;
2569 PathScanner scanner;
2570 string audio_path;
2571 string midi_path;
2572 vector<space_and_path>::iterator i;
2573 vector<space_and_path>::iterator nexti;
2574 vector<string*>* candidates;
2575 vector<string*>* candidates2;
2576 vector<string> unused;
2577 set<string> all_sources;
2578 bool used;
2579 string spath;
2580 int ret = -1;
2582 _state_of_the_state = (StateOfTheState) (_state_of_the_state | InCleanup);
2584 /* consider deleting all unused playlists */
2586 if (playlists->maybe_delete_unused (boost::bind (Session::ask_about_playlist_deletion, _1))) {
2587 ret = 0;
2588 goto out;
2591 /* sync the "all regions" property of each playlist with its current state
2594 playlists->sync_all_regions_with_regions ();
2596 /* find all un-used sources */
2598 rep.paths.clear ();
2599 rep.space = 0;
2601 for (SourceMap::iterator i = sources.begin(); i != sources.end(); ) {
2603 SourceMap::iterator tmp;
2605 tmp = i;
2606 ++tmp;
2608 /* do not bother with files that are zero size, otherwise we remove the current "nascent"
2609 capture files.
2612 if (!i->second->used() && (i->second->length(i->second->timeline_position() > 0))) {
2613 dead_sources.push_back (i->second);
2614 i->second->drop_references ();
2617 i = tmp;
2620 /* build a list of all the possible audio directories for the session */
2622 for (i = session_dirs.begin(); i != session_dirs.end(); ) {
2624 nexti = i;
2625 ++nexti;
2627 SessionDirectory sdir ((*i).path);
2628 audio_path += sdir.sound_path().to_string();
2630 if (nexti != session_dirs.end()) {
2631 audio_path += ':';
2634 i = nexti;
2638 /* build a list of all the possible midi directories for the session */
2640 for (i = session_dirs.begin(); i != session_dirs.end(); ) {
2642 nexti = i;
2643 ++nexti;
2645 SessionDirectory sdir ((*i).path);
2646 midi_path += sdir.midi_path().to_string();
2648 if (nexti != session_dirs.end()) {
2649 midi_path += ':';
2652 i = nexti;
2655 candidates = scanner (audio_path, accept_all_audio_files, (void *) 0, true, true);
2656 candidates2 = scanner (midi_path, accept_all_midi_files, (void *) 0, true, true);
2658 /* merge them */
2660 if (candidates) {
2661 if (candidates2) {
2662 for (vector<string*>::iterator i = candidates2->begin(); i != candidates2->end(); ++i) {
2663 candidates->push_back (*i);
2665 delete candidates2;
2667 } else {
2668 candidates = candidates2; // might still be null
2671 /* find all sources, but don't use this snapshot because the
2672 state file on disk still references sources we may have already
2673 dropped.
2676 find_all_sources_across_snapshots (all_sources, true);
2678 /* add our current source list
2681 for (SourceMap::iterator i = sources.begin(); i != sources.end(); ) {
2682 boost::shared_ptr<FileSource> fs;
2683 SourceMap::iterator tmp = i;
2684 ++tmp;
2686 if ((fs = boost::dynamic_pointer_cast<FileSource> (i->second)) != 0) {
2687 if (playlists->source_use_count (fs) != 0) {
2688 all_sources.insert (fs->path());
2689 } else {
2691 /* we might not remove this source from disk, because it may be used
2692 by other snapshots, but its not being used in this version
2693 so lets get rid of it now, along with any representative regions
2694 in the region list.
2697 RegionFactory::remove_regions_using_source (i->second);
2698 sources.erase (i);
2702 i = tmp;
2705 char tmppath1[PATH_MAX+1];
2706 char tmppath2[PATH_MAX+1];
2708 if (candidates) {
2709 for (vector<string*>::iterator x = candidates->begin(); x != candidates->end(); ++x) {
2711 used = false;
2712 spath = **x;
2714 for (set<string>::iterator i = all_sources.begin(); i != all_sources.end(); ++i) {
2716 if (realpath(spath.c_str(), tmppath1) == 0) {
2717 error << string_compose (_("Cannot expand path %1 (%2)"),
2718 spath, strerror (errno)) << endmsg;
2719 continue;
2722 if (realpath((*i).c_str(), tmppath2) == 0) {
2723 error << string_compose (_("Cannot expand path %1 (%2)"),
2724 (*i), strerror (errno)) << endmsg;
2725 continue;
2728 if (strcmp(tmppath1, tmppath2) == 0) {
2729 used = true;
2730 break;
2734 if (!used) {
2735 unused.push_back (spath);
2738 delete *x;
2741 delete candidates;
2744 /* now try to move all unused files into the "dead" directory(ies) */
2746 for (vector<string>::iterator x = unused.begin(); x != unused.end(); ++x) {
2747 struct stat statbuf;
2749 string newpath;
2751 /* don't move the file across filesystems, just
2752 stick it in the `dead_dir_name' directory
2753 on whichever filesystem it was already on.
2756 if ((*x).find ("/sounds/") != string::npos) {
2758 /* old school, go up 1 level */
2760 newpath = Glib::path_get_dirname (*x); // "sounds"
2761 newpath = Glib::path_get_dirname (newpath); // "session-name"
2763 } else {
2765 /* new school, go up 4 levels */
2767 newpath = Glib::path_get_dirname (*x); // "audiofiles" or "midifiles"
2768 newpath = Glib::path_get_dirname (newpath); // "session-name"
2769 newpath = Glib::path_get_dirname (newpath); // "interchange"
2770 newpath = Glib::path_get_dirname (newpath); // "session-dir"
2773 newpath = Glib::build_filename (newpath, dead_dir_name);
2775 if (g_mkdir_with_parents (newpath.c_str(), 0755) < 0) {
2776 error << string_compose(_("Session: cannot create dead file folder \"%1\" (%2)"), newpath, strerror (errno)) << endmsg;
2777 return -1;
2780 newpath = Glib::build_filename (newpath, Glib::path_get_basename ((*x)));
2782 if (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS)) {
2784 /* the new path already exists, try versioning */
2786 char buf[PATH_MAX+1];
2787 int version = 1;
2788 string newpath_v;
2790 snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), version);
2791 newpath_v = buf;
2793 while (Glib::file_test (newpath_v.c_str(), Glib::FILE_TEST_EXISTS) && version < 999) {
2794 snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), ++version);
2795 newpath_v = buf;
2798 if (version == 999) {
2799 error << string_compose (_("there are already 1000 files with names like %1; versioning discontinued"),
2800 newpath)
2801 << endmsg;
2802 } else {
2803 newpath = newpath_v;
2806 } else {
2808 /* it doesn't exist, or we can't read it or something */
2812 stat ((*x).c_str(), &statbuf);
2814 if (::rename ((*x).c_str(), newpath.c_str()) != 0) {
2815 error << string_compose (_("cannot rename unused file source from %1 to %2 (%3)"),
2816 (*x), newpath, strerror (errno))
2817 << endmsg;
2818 goto out;
2821 /* see if there an easy to find peakfile for this file, and remove it.
2824 string base = basename_nosuffix (*x);
2825 base += "%A"; /* this is what we add for the channel suffix of all native files,
2826 or for the first channel of embedded files. it will miss
2827 some peakfiles for other channels
2829 string peakpath = peak_path (base);
2831 if (Glib::file_test (peakpath.c_str(), Glib::FILE_TEST_EXISTS)) {
2832 if (::unlink (peakpath.c_str()) != 0) {
2833 error << string_compose (_("cannot remove peakfile %1 for %2 (%3)"),
2834 peakpath, _path, strerror (errno))
2835 << endmsg;
2836 /* try to back out */
2837 rename (newpath.c_str(), _path.c_str());
2838 goto out;
2842 rep.paths.push_back (*x);
2843 rep.space += statbuf.st_size;
2846 /* dump the history list */
2848 _history.clear ();
2850 /* save state so we don't end up a session file
2851 referring to non-existent sources.
2854 save_state ("");
2855 ret = 0;
2857 out:
2858 _state_of_the_state = (StateOfTheState) (_state_of_the_state & ~InCleanup);
2860 return ret;
2864 Session::cleanup_trash_sources (CleanupReport& rep)
2866 // FIXME: needs adaptation for MIDI
2868 vector<space_and_path>::iterator i;
2869 string dead_dir;
2871 rep.paths.clear ();
2872 rep.space = 0;
2874 for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
2876 dead_dir = Glib::build_filename ((*i).path, dead_dir_name);
2878 clear_directory (dead_dir, &rep.space, &rep.paths);
2881 return 0;
2884 void
2885 Session::set_dirty ()
2887 bool was_dirty = dirty();
2889 _state_of_the_state = StateOfTheState (_state_of_the_state | Dirty);
2892 if (!was_dirty) {
2893 DirtyChanged(); /* EMIT SIGNAL */
2898 void
2899 Session::set_clean ()
2901 bool was_dirty = dirty();
2903 _state_of_the_state = Clean;
2906 if (was_dirty) {
2907 DirtyChanged(); /* EMIT SIGNAL */
2911 void
2912 Session::set_deletion_in_progress ()
2914 _state_of_the_state = StateOfTheState (_state_of_the_state | Deletion);
2917 void
2918 Session::clear_deletion_in_progress ()
2920 _state_of_the_state = StateOfTheState (_state_of_the_state & (~Deletion));
2923 void
2924 Session::add_controllable (boost::shared_ptr<Controllable> c)
2926 /* this adds a controllable to the list managed by the Session.
2927 this is a subset of those managed by the Controllable class
2928 itself, and represents the only ones whose state will be saved
2929 as part of the session.
2932 Glib::Mutex::Lock lm (controllables_lock);
2933 controllables.insert (c);
2936 struct null_deleter { void operator()(void const *) const {} };
2938 void
2939 Session::remove_controllable (Controllable* c)
2941 if (_state_of_the_state | Deletion) {
2942 return;
2945 Glib::Mutex::Lock lm (controllables_lock);
2947 Controllables::iterator x = controllables.find (boost::shared_ptr<Controllable>(c, null_deleter()));
2949 if (x != controllables.end()) {
2950 controllables.erase (x);
2954 boost::shared_ptr<Controllable>
2955 Session::controllable_by_id (const PBD::ID& id)
2957 Glib::Mutex::Lock lm (controllables_lock);
2959 for (Controllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
2960 if ((*i)->id() == id) {
2961 return *i;
2965 return boost::shared_ptr<Controllable>();
2968 boost::shared_ptr<Controllable>
2969 Session::controllable_by_descriptor (const ControllableDescriptor& desc)
2971 boost::shared_ptr<Controllable> c;
2972 boost::shared_ptr<Route> r;
2974 switch (desc.top_level_type()) {
2975 case ControllableDescriptor::NamedRoute:
2977 std::string str = desc.top_level_name();
2978 if (str == "master") {
2979 r = _master_out;
2980 } else if (str == "control" || str == "listen") {
2981 r = _monitor_out;
2982 } else {
2983 r = route_by_name (desc.top_level_name());
2985 break;
2988 case ControllableDescriptor::RemoteControlID:
2989 r = route_by_remote_id (desc.rid());
2990 break;
2993 if (!r) {
2994 return c;
2997 switch (desc.subtype()) {
2998 case ControllableDescriptor::Gain:
2999 c = r->gain_control ();
3000 break;
3002 case ControllableDescriptor::Solo:
3003 c = r->solo_control();
3004 break;
3006 case ControllableDescriptor::Mute:
3007 c = r->mute_control();
3008 break;
3010 case ControllableDescriptor::Recenable:
3012 boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track>(r);
3014 if (t) {
3015 c = t->rec_enable_control ();
3017 break;
3020 case ControllableDescriptor::PanDirection:
3022 c = r->pannable()->pan_azimuth_control;
3023 break;
3026 case ControllableDescriptor::PanWidth:
3028 c = r->pannable()->pan_width_control;
3029 break;
3032 case ControllableDescriptor::PanElevation:
3034 c = r->pannable()->pan_elevation_control;
3035 break;
3038 case ControllableDescriptor::Balance:
3039 /* XXX simple pan control */
3040 break;
3042 case ControllableDescriptor::PluginParameter:
3044 uint32_t plugin = desc.target (0);
3045 uint32_t parameter_index = desc.target (1);
3047 /* revert to zero based counting */
3049 if (plugin > 0) {
3050 --plugin;
3053 if (parameter_index > 0) {
3054 --parameter_index;
3057 boost::shared_ptr<Processor> p = r->nth_plugin (plugin);
3059 if (p) {
3060 c = boost::dynamic_pointer_cast<ARDOUR::AutomationControl>(
3061 p->control(Evoral::Parameter(PluginAutomation, 0, parameter_index)));
3063 break;
3066 case ControllableDescriptor::SendGain:
3068 uint32_t send = desc.target (0);
3070 /* revert to zero-based counting */
3072 if (send > 0) {
3073 --send;
3076 boost::shared_ptr<Processor> p = r->nth_send (send);
3078 if (p) {
3079 boost::shared_ptr<Send> s = boost::dynamic_pointer_cast<Send>(p);
3080 boost::shared_ptr<Amp> a = s->amp();
3082 if (a) {
3083 c = s->amp()->gain_control();
3086 break;
3089 default:
3090 /* relax and return a null pointer */
3091 break;
3094 return c;
3097 void
3098 Session::add_instant_xml (XMLNode& node, bool write_to_config)
3100 if (_writable) {
3101 Stateful::add_instant_xml (node, _path);
3104 if (write_to_config) {
3105 Config->add_instant_xml (node);
3109 XMLNode*
3110 Session::instant_xml (const string& node_name)
3112 return Stateful::instant_xml (node_name, _path);
3116 Session::save_history (string snapshot_name)
3118 XMLTree tree;
3120 if (!_writable) {
3121 return 0;
3124 if (snapshot_name.empty()) {
3125 snapshot_name = _current_snapshot_name;
3128 const string history_filename = legalize_for_path (snapshot_name) + history_suffix;
3129 const string backup_filename = history_filename + backup_suffix;
3130 const sys::path xml_path = _session_dir->root_path() / history_filename;
3131 const sys::path backup_path = _session_dir->root_path() / backup_filename;
3133 if (sys::exists (xml_path)) {
3136 sys::rename (xml_path, backup_path);
3138 catch (const sys::filesystem_error& err)
3140 error << _("could not backup old history file, current history not saved") << endmsg;
3141 return -1;
3145 if (!Config->get_save_history() || Config->get_saved_history_depth() < 0) {
3146 return 0;
3149 tree.set_root (&_history.get_state (Config->get_saved_history_depth()));
3151 if (!tree.write (xml_path.to_string()))
3153 error << string_compose (_("history could not be saved to %1"), xml_path.to_string()) << endmsg;
3157 sys::remove (xml_path);
3158 sys::rename (backup_path, xml_path);
3160 catch (const sys::filesystem_error& err)
3162 error << string_compose (_("could not restore history file from backup %1 (%2)"),
3163 backup_path.to_string(), err.what()) << endmsg;
3166 return -1;
3169 return 0;
3173 Session::restore_history (string snapshot_name)
3175 XMLTree tree;
3177 if (snapshot_name.empty()) {
3178 snapshot_name = _current_snapshot_name;
3181 const string xml_filename = legalize_for_path (snapshot_name) + history_suffix;
3182 const sys::path xml_path = _session_dir->root_path() / xml_filename;
3184 info << "Loading history from " << xml_path.to_string() << endmsg;
3186 if (!sys::exists (xml_path)) {
3187 info << string_compose (_("%1: no history file \"%2\" for this session."),
3188 _name, xml_path.to_string()) << endmsg;
3189 return 1;
3192 if (!tree.read (xml_path.to_string())) {
3193 error << string_compose (_("Could not understand session history file \"%1\""),
3194 xml_path.to_string()) << endmsg;
3195 return -1;
3198 // replace history
3199 _history.clear();
3201 for (XMLNodeConstIterator it = tree.root()->children().begin(); it != tree.root()->children().end(); it++) {
3203 XMLNode *t = *it;
3204 UndoTransaction* ut = new UndoTransaction ();
3205 struct timeval tv;
3207 ut->set_name(t->property("name")->value());
3208 stringstream ss(t->property("tv-sec")->value());
3209 ss >> tv.tv_sec;
3210 ss.str(t->property("tv-usec")->value());
3211 ss >> tv.tv_usec;
3212 ut->set_timestamp(tv);
3214 for (XMLNodeConstIterator child_it = t->children().begin();
3215 child_it != t->children().end(); child_it++)
3217 XMLNode *n = *child_it;
3218 Command *c;
3220 if (n->name() == "MementoCommand" ||
3221 n->name() == "MementoUndoCommand" ||
3222 n->name() == "MementoRedoCommand") {
3224 if ((c = memento_command_factory(n))) {
3225 ut->add_command(c);
3228 } else if (n->name() == "NoteDiffCommand") {
3229 PBD::ID id (n->property("midi-source")->value());
3230 boost::shared_ptr<MidiSource> midi_source =
3231 boost::dynamic_pointer_cast<MidiSource, Source>(source_by_id(id));
3232 if (midi_source) {
3233 ut->add_command (new MidiModel::NoteDiffCommand(midi_source->model(), *n));
3234 } else {
3235 error << _("Failed to downcast MidiSource for NoteDiffCommand") << endmsg;
3238 } else if (n->name() == "SysExDiffCommand") {
3240 PBD::ID id (n->property("midi-source")->value());
3241 boost::shared_ptr<MidiSource> midi_source =
3242 boost::dynamic_pointer_cast<MidiSource, Source>(source_by_id(id));
3243 if (midi_source) {
3244 ut->add_command (new MidiModel::SysExDiffCommand (midi_source->model(), *n));
3245 } else {
3246 error << _("Failed to downcast MidiSource for SysExDiffCommand") << endmsg;
3249 } else if (n->name() == "PatchChangeDiffCommand") {
3251 PBD::ID id (n->property("midi-source")->value());
3252 boost::shared_ptr<MidiSource> midi_source =
3253 boost::dynamic_pointer_cast<MidiSource, Source>(source_by_id(id));
3254 if (midi_source) {
3255 ut->add_command (new MidiModel::PatchChangeDiffCommand (midi_source->model(), *n));
3256 } else {
3257 error << _("Failed to downcast MidiSource for PatchChangeDiffCommand") << endmsg;
3260 } else if (n->name() == "StatefulDiffCommand") {
3261 if ((c = stateful_diff_command_factory (n))) {
3262 ut->add_command (c);
3264 } else {
3265 error << string_compose(_("Couldn't figure out how to make a Command out of a %1 XMLNode."), n->name()) << endmsg;
3269 _history.add (ut);
3272 return 0;
3275 void
3276 Session::config_changed (std::string p, bool ours)
3278 if (ours) {
3279 set_dirty ();
3282 if (p == "seamless-loop") {
3284 } else if (p == "rf-speed") {
3286 } else if (p == "auto-loop") {
3288 } else if (p == "auto-input") {
3290 if (Config->get_monitoring_model() == HardwareMonitoring && transport_rolling()) {
3291 /* auto-input only makes a difference if we're rolling */
3292 set_track_monitor_input_status (!config.get_auto_input());
3295 } else if (p == "punch-in") {
3297 Location* location;
3299 if ((location = _locations->auto_punch_location()) != 0) {
3301 if (config.get_punch_in ()) {
3302 replace_event (SessionEvent::PunchIn, location->start());
3303 } else {
3304 remove_event (location->start(), SessionEvent::PunchIn);
3308 } else if (p == "punch-out") {
3310 Location* location;
3312 if ((location = _locations->auto_punch_location()) != 0) {
3314 if (config.get_punch_out()) {
3315 replace_event (SessionEvent::PunchOut, location->end());
3316 } else {
3317 clear_events (SessionEvent::PunchOut);
3321 } else if (p == "edit-mode") {
3323 Glib::Mutex::Lock lm (playlists->lock);
3325 for (SessionPlaylists::List::iterator i = playlists->playlists.begin(); i != playlists->playlists.end(); ++i) {
3326 (*i)->set_edit_mode (Config->get_edit_mode ());
3329 } else if (p == "use-video-sync") {
3331 waiting_for_sync_offset = config.get_use_video_sync();
3333 } else if (p == "mmc-control") {
3335 //poke_midi_thread ();
3337 } else if (p == "mmc-device-id" || p == "mmc-receive-id") {
3339 MIDI::Manager::instance()->mmc()->set_receive_device_id (Config->get_mmc_receive_device_id());
3341 } else if (p == "mmc-send-id") {
3343 MIDI::Manager::instance()->mmc()->set_send_device_id (Config->get_mmc_send_device_id());
3345 } else if (p == "midi-control") {
3347 //poke_midi_thread ();
3349 } else if (p == "raid-path") {
3351 setup_raid_path (config.get_raid_path());
3353 } else if (p == "timecode-format") {
3355 sync_time_vars ();
3357 } else if (p == "video-pullup") {
3359 sync_time_vars ();
3361 } else if (p == "seamless-loop") {
3363 if (play_loop && transport_rolling()) {
3364 // to reset diskstreams etc
3365 request_play_loop (true);
3368 } else if (p == "rf-speed") {
3370 cumulative_rf_motion = 0;
3371 reset_rf_scale (0);
3373 } else if (p == "click-sound") {
3375 setup_click_sounds (1);
3377 } else if (p == "click-emphasis-sound") {
3379 setup_click_sounds (-1);
3381 } else if (p == "clicking") {
3383 if (Config->get_clicking()) {
3384 if (_click_io && click_data) { // don't require emphasis data
3385 _clicking = true;
3387 } else {
3388 _clicking = false;
3391 } else if (p == "send-mtc") {
3393 if (Config->get_send_mtc ()) {
3394 /* mark us ready to send */
3395 next_quarter_frame_to_send = 0;
3398 } else if (p == "send-mmc") {
3400 MIDI::Manager::instance()->mmc()->enable_send (Config->get_send_mmc ());
3402 } else if (p == "midi-feedback") {
3404 session_midi_feedback = Config->get_midi_feedback();
3406 } else if (p == "jack-time-master") {
3408 engine().reset_timebase ();
3410 } else if (p == "native-file-header-format") {
3412 if (!first_file_header_format_reset) {
3413 reset_native_file_format ();
3416 first_file_header_format_reset = false;
3418 } else if (p == "native-file-data-format") {
3420 if (!first_file_data_format_reset) {
3421 reset_native_file_format ();
3424 first_file_data_format_reset = false;
3426 } else if (p == "external-sync") {
3427 if (!config.get_external_sync()) {
3428 drop_sync_source ();
3429 } else {
3430 switch_to_sync_source (config.get_sync_source());
3432 } else if (p == "remote-model") {
3433 set_remote_control_ids ();
3434 } else if (p == "denormal-model") {
3435 setup_fpu ();
3436 } else if (p == "history-depth") {
3437 set_history_depth (Config->get_history_depth());
3438 } else if (p == "sync-all-route-ordering") {
3439 sync_order_keys ("session");
3440 } else if (p == "initial-program-change") {
3442 if (MIDI::Manager::instance()->mmc()->output_port() && Config->get_initial_program_change() >= 0) {
3443 MIDI::byte buf[2];
3445 buf[0] = MIDI::program; // channel zero by default
3446 buf[1] = (Config->get_initial_program_change() & 0x7f);
3448 MIDI::Manager::instance()->mmc()->output_port()->midimsg (buf, sizeof (buf), 0);
3450 } else if (p == "solo-mute-override") {
3451 // catch_up_on_solo_mute_override ();
3452 } else if (p == "listen-position" || p == "pfl-position") {
3453 listen_position_changed ();
3454 } else if (p == "solo-control-is-listen-control") {
3455 solo_control_mode_changed ();
3456 } else if (p == "timecode-offset" || p == "timecode-offset-negative") {
3457 last_timecode_valid = false;
3460 set_dirty ();
3463 void
3464 Session::set_history_depth (uint32_t d)
3466 _history.set_depth (d);
3470 Session::load_diskstreams_2X (XMLNode const & node, int)
3472 XMLNodeList clist;
3473 XMLNodeConstIterator citer;
3475 clist = node.children();
3477 for (citer = clist.begin(); citer != clist.end(); ++citer) {
3479 try {
3480 /* diskstreams added automatically by DiskstreamCreated handler */
3481 if ((*citer)->name() == "AudioDiskstream" || (*citer)->name() == "DiskStream") {
3482 boost::shared_ptr<AudioDiskstream> dsp (new AudioDiskstream (*this, **citer));
3483 _diskstreams_2X.push_back (dsp);
3484 } else {
3485 error << _("Session: unknown diskstream type in XML") << endmsg;
3489 catch (failed_constructor& err) {
3490 error << _("Session: could not load diskstream via XML state") << endmsg;
3491 return -1;
3495 return 0;
3498 /** Connect things to the MMC object */
3499 void
3500 Session::setup_midi_machine_control ()
3502 MIDI::MachineControl* mmc = MIDI::Manager::instance()->mmc ();
3504 mmc->Play.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
3505 mmc->DeferredPlay.connect_same_thread (*this, boost::bind (&Session::mmc_deferred_play, this, _1));
3506 mmc->Stop.connect_same_thread (*this, boost::bind (&Session::mmc_stop, this, _1));
3507 mmc->FastForward.connect_same_thread (*this, boost::bind (&Session::mmc_fast_forward, this, _1));
3508 mmc->Rewind.connect_same_thread (*this, boost::bind (&Session::mmc_rewind, this, _1));
3509 mmc->Pause.connect_same_thread (*this, boost::bind (&Session::mmc_pause, this, _1));
3510 mmc->RecordPause.connect_same_thread (*this, boost::bind (&Session::mmc_record_pause, this, _1));
3511 mmc->RecordStrobe.connect_same_thread (*this, boost::bind (&Session::mmc_record_strobe, this, _1));
3512 mmc->RecordExit.connect_same_thread (*this, boost::bind (&Session::mmc_record_exit, this, _1));
3513 mmc->Locate.connect_same_thread (*this, boost::bind (&Session::mmc_locate, this, _1, _2));
3514 mmc->Step.connect_same_thread (*this, boost::bind (&Session::mmc_step, this, _1, _2));
3515 mmc->Shuttle.connect_same_thread (*this, boost::bind (&Session::mmc_shuttle, this, _1, _2, _3));
3516 mmc->TrackRecordStatusChange.connect_same_thread (*this, boost::bind (&Session::mmc_record_enable, this, _1, _2, _3));
3518 /* also handle MIDI SPP because its so common */
3520 mmc->SPPStart.connect_same_thread (*this, boost::bind (&Session::spp_start, this, _1, _2));
3521 mmc->SPPContinue.connect_same_thread (*this, boost::bind (&Session::spp_continue, this, _1, _2));
3522 mmc->SPPStop.connect_same_thread (*this, boost::bind (&Session::spp_stop, this, _1, _2));
3525 boost::shared_ptr<Controllable>
3526 Session::solo_cut_control() const
3528 /* the solo cut control is a bit of an anomaly, at least as of Febrary 2011. There are no other
3529 controls in Ardour that currently get presented to the user in the GUI that require
3530 access as a Controllable and are also NOT owned by some SessionObject (e.g. Route, or MonitorProcessor).
3532 its actually an RCConfiguration parameter, so we use a ProxyControllable to wrap
3533 it up as a Controllable. Changes to the Controllable will just map back to the RCConfiguration
3534 parameter.
3537 return _solo_cut_control;