diff options
author | Robin Gareus <robin@gareus.org> | 2017-06-25 01:56:53 +0200 |
---|---|---|
committer | Robin Gareus <robin@gareus.org> | 2017-06-25 01:57:19 +0200 |
commit | cc7ff53e1f70c8ff6eab692de7adf815bf83ff91 (patch) | |
tree | d98382ec0ba11ed124c917f39c251297cea230fa /libs/ardour | |
parent | a1c5d1be43007265ef1257889a3138b0fa75c2ac (diff) |
Create a deep-copy of MIDI sources when saving snapshots
Diffstat (limited to 'libs/ardour')
-rw-r--r-- | libs/ardour/ardour/session.h | 8 | ||||
-rw-r--r-- | libs/ardour/ardour/smf_source.h | 2 | ||||
-rw-r--r-- | libs/ardour/session_state.cc | 84 |
3 files changed, 84 insertions, 10 deletions
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 2da54f8bbc..96adcf0981 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -1901,7 +1901,13 @@ class LIBARDOUR_API Session : public PBD::StatefulDestructible, public PBD::Scop void update_latency (bool playback); - XMLNode& state(bool); + enum snapshot_t { + NormalSave, + SnapshotKeep, + SwitchToSnapshot + }; + + XMLNode& state(bool, snapshot_t snapshot_type = NormalSave); /* click track */ typedef std::list<Click*> Clicks; diff --git a/libs/ardour/ardour/smf_source.h b/libs/ardour/ardour/smf_source.h index 979c20dd21..9bc6584355 100644 --- a/libs/ardour/ardour/smf_source.h +++ b/libs/ardour/ardour/smf_source.h @@ -70,10 +70,10 @@ public: static bool valid_midi_file (const std::string& path); void prevent_deletion (); + void set_path (const std::string& newpath); protected: void close (); - void set_path (const std::string& newpath); void flush_midi (const Lock& lock); private: diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index 0420e236de..0172ca2fed 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -122,6 +122,7 @@ #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" @@ -806,6 +807,12 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot 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 const int64_t save_start_time = g_get_monotonic_time(); #endif @@ -832,7 +839,7 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot 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()) { @@ -1139,7 +1146,7 @@ struct route_id_compare { } // anon namespace XMLNode& -Session::state (bool full_state) +Session::state (bool full_state, snapshot_t snapshot_type) { LocaleGuard lg; XMLNode* node = new XMLNode("Session"); @@ -1238,19 +1245,80 @@ Session::state (bool full_state) * 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()) { + 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()); + } + + /* 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()); } } |