#include <glib.h>
#include "pbd/gstdio_compat.h"
+#include "pbd/locale_guard.h"
#include <glibmm.h>
#include <glibmm/threads.h>
#include "ardour/session_playlists.h"
#include "ardour/session_state_utils.h"
#include "ardour/silentfilesource.h"
+#include "ardour/smf_source.h"
#include "ardour/sndfilesource.h"
#include "ardour/source_factory.h"
#include "ardour/speakers.h"
_tempo_map = new TempoMap (_current_frame_rate);
_tempo_map->PropertyChanged.connect_same_thread (*this, boost::bind (&Session::tempo_map_changed, this, _1));
_tempo_map->MetricPositionChanged.connect_same_thread (*this, boost::bind (&Session::tempo_map_changed, this, _1));
+ } catch (std::exception const & e) {
+ error << _("Unexpected exception during session setup: ") << e.what() << endmsg;
+ return -2;
+ } catch (...) {
+ error << _("Unknown exception during session setup") << endmsg;
+ return -3;
+ }
+ try {
/* MidiClock requires a tempo map */
delete midi_clock;
if (state_tree) {
if (set_state (*state_tree->root(), Stateful::loading_state_version)) {
error << _("Could not set session state from XML") << endmsg;
- return -1;
+ return -4;
}
} else {
// set_state() will call setup_raid_path(), but if it's a new session we need
_locations->changed.connect_same_thread (*this, boost::bind (&Session::locations_changed, this));
} catch (AudioEngine::PortRegistrationFailure& err) {
- /* handle this one in a different way than all others, so that its clear what happened */
error << err.what() << endmsg;
- return -1;
+ return -5;
} catch (std::exception const & e) {
error << _("Unexpected exception during session setup: ") << e.what() << endmsg;
- return -1;
+ return -6;
} catch (...) {
error << _("Unknown exception during session setup") << endmsg;
- return -1;
+ return -7;
}
BootMessage (_("Reset Remote Controls"));
/* set up Master Out and Monitor Out if necessary */
if (bus_profile) {
-
RouteList rl;
ChanCount count(DataType::AUDIO, bus_profile->master_out_channels);
+ if (bus_profile->master_out_channels) {
+ int rv = add_master_bus (count);
- // Waves Tracks: always create master bus for Tracks
- if (ARDOUR::Profile->get_trx() || bus_profile->master_out_channels) {
- boost::shared_ptr<Route> r (new Route (*this, _("Master"), PresentationInfo::MasterOut, DataType::AUDIO));
- if (r->init ()) {
- return -1;
+ if (rv) {
+ return rv;
}
- BOOST_MARK_ROUTE(r);
-
- {
- Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
- r->input()->ensure_io (count, false, this);
- r->output()->ensure_io (count, false, this);
- }
-
- rl.push_back (r);
-
- } else {
- /* prohibit auto-connect to master, because there isn't one */
- bus_profile->output_ac = AutoConnectOption (bus_profile->output_ac & ~AutoConnectMaster);
- }
-
- if (!rl.empty()) {
- add_routes (rl, false, false, false, PresentationInfo::max_order);
+ if (Config->get_use_monitor_bus())
+ add_monitor_section ();
}
-
- // Waves Tracks: Skip this. Always use autoconnection for Tracks
- if (!ARDOUR::Profile->get_trx()) {
-
- /* this allows the user to override settings with an environment variable.
- */
-
- if (no_auto_connect()) {
- bus_profile->input_ac = AutoConnectOption (0);
- bus_profile->output_ac = AutoConnectOption (0);
- }
-
- Config->set_input_auto_connect (bus_profile->input_ac);
- Config->set_output_auto_connect (bus_profile->output_ac);
- }
- }
-
- if (Config->get_use_monitor_bus() && bus_profile) {
- add_monitor_section ();
}
return 0;
}
_save_queued = false;
- if (!_engine.connected ()) {
- error << string_compose (_("the %1 audio engine is not connected and state saving would lose all I/O connections. Session not saved"), PROGRAM_NAME)
- << endmsg;
- return 1;
+ snapshot_t fork_state = NormalSave;
+ if (!snapshot_name.empty() && snapshot_name != _current_snapshot_name && !template_only && !pending) {
+ /* snapshot, close midi */
+ fork_state = switch_to_snapshot ? SwitchToSnapshot : SnapshotKeep;
}
#ifndef NDEBUG
mark_as_clean = false;
tree.set_root (&get_template());
} else {
- tree.set_root (&get_state());
+ tree.set_root (&state (true, fork_state));
}
if (snapshot_name.empty()) {
} // anon namespace
XMLNode&
-Session::state (bool full_state)
+Session::state (bool full_state, snapshot_t snapshot_type)
{
LocaleGuard lg;
XMLNode* node = new XMLNode("Session");
* about non-destructive file sources that are empty
* and unused by any regions.
*/
-
boost::shared_ptr<FileSource> fs;
- if ((fs = boost::dynamic_pointer_cast<FileSource> (siter->second)) != 0) {
+ if ((fs = boost::dynamic_pointer_cast<FileSource> (siter->second)) == 0) {
+ continue;
+ }
+
+ if (!fs->destructive()) {
+ if (fs->empty() && !fs->used()) {
+ continue;
+ }
+ }
+
+ if (snapshot_type != NormalSave && fs->within_session ()) {
+ /* copy MIDI sources to new file
+ *
+ * We cannot replace the midi-source and MidiRegion::clobber_sources,
+ * because the GUI (midi_region) has a direct pointer to the midi-model
+ * of the source, as does UndoTransaction.
+ *
+ * On the upside, .mid files are not kept open. The file is only open
+ * when reading the model initially and when flushing the model to disk:
+ * source->session_saved () or export.
+ *
+ * We can change the _path of the existing source under the hood, keeping
+ * all IDs, references and pointers intact.
+ * */
+ boost::shared_ptr<SMFSource> ms;
+ if ((ms = boost::dynamic_pointer_cast<SMFSource> (siter->second)) != 0) {
+ const std::string ancestor_name = ms->ancestor_name();
+ const std::string base = PBD::basename_nosuffix(ancestor_name);
+ const string path = new_midi_source_path (base, false);
+
+ /* use SMF-API to clone data (use the midi_model, not data on disk) */
+ boost::shared_ptr<SMFSource> newsrc (new SMFSource (*this, path, SndFileSource::default_writable_flags));
+ Source::Lock lm (ms->mutex());
+
+ // TODO special-case empty, removable() files: just create a new removable.
+ // (load + write flushes the model and creates the file)
+ if (!ms->model()) {
+ ms->load_model (lm);
+ }
+ if (ms->write_to (lm, newsrc, Evoral::MinBeats, Evoral::MaxBeats)) {
+ error << string_compose (_("Session-Save: Failed to copy MIDI Source '%1' for snapshot"), ancestor_name) << endmsg;
+ } else {
+ if (snapshot_type == SnapshotKeep) {
+ /* keep working on current session.
+ *
+ * Save snapshot-state with the original filename.
+ * Switch to use new path for future saves of the main session.
+ */
+ child->add_child_nocopy (ms->get_state());
+ }
- if (!fs->destructive()) {
- if (fs->empty() && !fs->used()) {
+ /* swap file-paths.
+ * ~SMFSource unlinks removable() files.
+ */
+ std::string npath (ms->path ());
+ ms->replace_file (newsrc->path ());
+ newsrc->replace_file (npath);
+
+ if (snapshot_type == SwitchToSnapshot) {
+ /* save and switch to snapshot.
+ *
+ * Leave the old file in place (as is).
+ * Snapshot uses new source directly
+ */
+ child->add_child_nocopy (ms->get_state());
+ }
continue;
}
}
-
- child->add_child_nocopy (siter->second->get_state());
}
+
+ child->add_child_nocopy (siter->second->get_state());
}
}