Separate "add master bus" (and add Lua bindings)
[ardour.git] / libs / ardour / session_state.cc
index c398b47ed025d17d685c974f4ca9ce9be264200e..051eca4b688d6ed2cab5b331f11b772e03dd4ac3 100644 (file)
@@ -50,6 +50,7 @@
 
 #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"
@@ -263,7 +265,15 @@ Session::post_engine_init ()
                _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;
@@ -286,7 +296,7 @@ Session::post_engine_init ()
                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
@@ -349,15 +359,14 @@ Session::post_engine_init ()
                _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"));
@@ -648,54 +657,18 @@ Session::create (const string& session_template, BusProfile* bus_profile)
        /* 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;
@@ -799,10 +772,10 @@ Session::save_state (string snapshot_name, bool pending, bool switch_to_snapshot
        }
        _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
@@ -831,7 +804,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()) {
@@ -1138,7 +1111,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");
@@ -1237,19 +1210,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()) {
+                                       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());
                }
        }