mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-09 16:24:57 +01:00
Implement strip import/export, focusing on processors
This commit is contained in:
parent
e8ea2029e1
commit
1bb0832159
6 changed files with 550 additions and 39 deletions
|
|
@ -432,6 +432,7 @@ public:
|
|||
XMLNode& get_state() const;
|
||||
XMLNode& get_template();
|
||||
virtual int set_state (const XMLNode&, int version);
|
||||
virtual int import_state (const XMLNode&, bool use_pbd_ids = true, bool processor_only = true);
|
||||
|
||||
XMLNode& get_processor_state ();
|
||||
void set_processor_state (const XMLNode&, int version);
|
||||
|
|
|
|||
|
|
@ -662,7 +662,17 @@ public:
|
|||
std::vector<std::string> possible_states() const;
|
||||
static std::vector<std::string> possible_states (std::string path);
|
||||
|
||||
bool export_track_state (std::shared_ptr<RouteList> rl, const std::string& path);
|
||||
|
||||
enum RouteGroupImportMode {
|
||||
IgnoreRouteGroup,
|
||||
UseRouteGroup,
|
||||
CreateRouteGroup
|
||||
};
|
||||
|
||||
bool export_route_state (std::shared_ptr<RouteList> rl, const std::string& path, bool with_sources);
|
||||
int import_route_state (const std::string& path, std::map<PBD::ID, PBD::ID> const&, RouteGroupImportMode rgim = CreateRouteGroup);
|
||||
|
||||
std::map<PBD::ID, std::string> parse_route_state (const std::string& path, bool& match_pbd_id);
|
||||
|
||||
/// The instant xml file is written to the session directory
|
||||
void add_instant_xml (XMLNode&, bool write_to_config = true);
|
||||
|
|
|
|||
|
|
@ -2379,6 +2379,12 @@ LuaBindings::common (lua_State* L)
|
|||
.beginStdList <std::shared_ptr<Processor> > ("ProcessorList")
|
||||
.endClass ()
|
||||
|
||||
.beginStdMap <PBD::ID, std::string> ("IDNameMap")
|
||||
.endClass ()
|
||||
|
||||
.beginStdMap <PBD::ID, PBD::ID> ("IDMap")
|
||||
.endClass ()
|
||||
|
||||
//std::list<std::shared_ptr<Port> > PortList
|
||||
.beginConstStdList <std::shared_ptr<Port> > ("PortList")
|
||||
.endClass ()
|
||||
|
|
@ -3531,7 +3537,9 @@ LuaBindings::non_rt (lua_State* L)
|
|||
.addFunction ("rename", &Session::rename)
|
||||
.addFunction ("set_dirty", &Session::set_dirty)
|
||||
.addFunction ("unknown_processors", &Session::unknown_processors)
|
||||
.addFunction ("export_track_state", &Session::export_track_state)
|
||||
.addFunction ("export_route_state", &Session::export_route_state)
|
||||
.addFunction ("import_route_state", &Session::import_route_state)
|
||||
.addFunction ("parse_route_state", &Session::parse_route_state)
|
||||
.addFunction ("selection", &Session::selection)
|
||||
.addFunction ("have_external_connections_for_current_backend", &Session::have_external_connections_for_current_backend)
|
||||
.addFunction ("unnamed", &Session::unnamed)
|
||||
|
|
|
|||
|
|
@ -3139,6 +3139,263 @@ Route::set_state_2X (const XMLNode& node, int version)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Route::import_state (const XMLNode& node, bool use_pbd_ids, bool processor_only)
|
||||
{
|
||||
if (!processor_only) {
|
||||
/* not yet supported */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (node.name() != "Route") {
|
||||
error << string_compose(_("Bad node sent to Route::import_state() [%1]"), node.name()) << endmsg;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int version = 0;
|
||||
if (!node.get_property ("version", version) || version < 7000) {
|
||||
error << string_compose(_("Invalid version sent to Route::import_state() [%1]"), version) << endmsg;
|
||||
return -2;
|
||||
}
|
||||
|
||||
node.get_property (X_("strict-io"), _strict_io);
|
||||
|
||||
MeterType meter_type;
|
||||
if (node.get_property (X_("meter-type"), meter_type)) {
|
||||
set_meter_type (meter_type);
|
||||
}
|
||||
|
||||
DiskIOPoint diop;
|
||||
if (node.get_property (X_("disk-io-point"), diop)) {
|
||||
if (_disk_writer) {
|
||||
_disk_writer->set_display_to_user (diop == DiskIOCustom);
|
||||
}
|
||||
if (_disk_reader) {
|
||||
_disk_reader->set_display_to_user (diop == DiskIOCustom);
|
||||
}
|
||||
if (_triggerbox) {
|
||||
_triggerbox->set_display_to_user (diop == DiskIOCustom);
|
||||
}
|
||||
set_disk_io_point (diop);
|
||||
}
|
||||
|
||||
XMLNode processor_state (X_("processor_state"));
|
||||
|
||||
|
||||
Temporal::TimeDomainProvider const& tdp (*this);
|
||||
ProcessorList new_processors;
|
||||
std::vector<PBD::ID> old;
|
||||
std::vector<PBD::ID> reuse;
|
||||
|
||||
/* when using state from other sessions, don't match PBD::IDs */
|
||||
if (use_pbd_ids) {
|
||||
foreach_processor ([&old](std::weak_ptr<Processor> wp) { std::shared_ptr<Processor> p (wp.lock ()); if (p) { old.push_back (p->id()); } } );
|
||||
}
|
||||
|
||||
for (auto const& child : node.children ()) {
|
||||
if (child->name() == X_("Processor")) {
|
||||
XMLProperty* prop = child->property ("type");
|
||||
if (!prop) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PBD::ID id;
|
||||
if (!child->get_property ("id", id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prop->value() == "ladspa" ||
|
||||
prop->value() == "lv2" ||
|
||||
prop->value() == "windows-vst" ||
|
||||
prop->value() == "mac-vst" ||
|
||||
prop->value() == "lxvst" ||
|
||||
prop->value() == "luaproc" ||
|
||||
prop->value() == "vst3" ||
|
||||
prop->value() == "audiounit") {
|
||||
|
||||
if (std::find (old.begin (), old.end(), id) == old.end()) {
|
||||
|
||||
/* skip Mixbus channelstrip */
|
||||
if (prop->value() == "ladspa") {
|
||||
const XMLProperty *prop_id = child->property ("unique-id");
|
||||
if (!prop_id) {
|
||||
continue;
|
||||
}
|
||||
int id = atoi (prop_id->value());
|
||||
if (id >= 9300 && id <= 9399) {
|
||||
std::shared_ptr<Processor> strip = plugin_by_uri (prop_id->value());
|
||||
if (strip) {
|
||||
XMLNode* proc = new XMLNode (*child);
|
||||
proc->set_property ("id", strip->id());
|
||||
processor_state.add_child_nocopy (*proc);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else if (prop->value() == "lv2") {
|
||||
const XMLProperty *prop_id = node.property ("unique-id");
|
||||
if (prop_id) {
|
||||
if (strstr (prop_id->value().c_str(), "http://harrisonconsoles.com/lv2/vbm-")) {
|
||||
std::shared_ptr<Processor> strip = plugin_by_uri (prop_id->value());
|
||||
if (strip) {
|
||||
XMLNode* proc = new XMLNode (*child);
|
||||
proc->set_property ("id", strip->id());
|
||||
processor_state.add_child_nocopy (*proc);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Add new plugin with new ID */
|
||||
PBD::Stateful::ForceIDRegeneration force_ids;
|
||||
/* compare to .. Route::set_processor_state */
|
||||
std::shared_ptr<Processor> processor;
|
||||
processor.reset (new PluginInsert (_session, tdp));
|
||||
processor->set_owner (this);
|
||||
|
||||
if (processor->set_state (*child, version) != 0) {
|
||||
cout << "Failed to configure new proc.\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
std::shared_ptr<PluginInsert> pi = std::dynamic_pointer_cast<PluginInsert> (processor);
|
||||
if (pi && _strict_io) {
|
||||
pi->set_strict_io (true);
|
||||
}
|
||||
/* subscribe to Sidechain IO changes */
|
||||
if (pi && pi->has_sidechain ()) {
|
||||
pi->sidechain_input ()->changed.connect_same_thread (*pi, std::bind (&Route::sidechain_change_handler, this, _1, _2));
|
||||
}
|
||||
new_processors.push_back (processor);
|
||||
processor_state.add_child_copy (*child);
|
||||
} else {
|
||||
/* processor was found, reuse it */
|
||||
reuse.push_back (id);
|
||||
processor_state.add_child_copy (*child);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prop->value() == "intsend") {
|
||||
/* ignore Aux sends that may not apply here */
|
||||
if (std::find (old.begin (), old.end(), id) != old.end()) {
|
||||
processor_state.add_child_copy (*child);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* special case processors with controls */
|
||||
|
||||
float value;
|
||||
if (prop->value() == "amp" && _gain_control) {
|
||||
XMLNode* ctl = child->child (Controllable::xml_node_name.c_str());
|
||||
if (ctl) {
|
||||
if (ctl->get_property ("value", value)) {
|
||||
_session.set_control (_gain_control, value, Controllable::NoGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (prop->value() == "trim" && _trim_control) {
|
||||
XMLNode* ctl = child->child (Controllable::xml_node_name.c_str());
|
||||
if (ctl) {
|
||||
if (ctl->get_property ("value", value)) {
|
||||
_session.set_control (_trim_control, value, Controllable::NoGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* internal processor, only retain order and active state */
|
||||
|
||||
XMLNode* node = new XMLNode (X_("Processor"));
|
||||
node->set_property ("type", prop->value());
|
||||
prop = child->property ("active");
|
||||
if (prop) {
|
||||
node->set_property ("active", prop->value());
|
||||
}
|
||||
processor_state.add_child_nocopy (*node);
|
||||
|
||||
} else if (child->name() == Controllable::xml_node_name) {
|
||||
std::string control_name;
|
||||
if (!child->get_property (X_("name"), control_name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float value;
|
||||
if (control_name == _solo_isolate_control->name()) {
|
||||
if (child->get_property ("solo-isolated", value)) {
|
||||
_session.set_control (_solo_isolate_control, value, Controllable::NoGroup);
|
||||
}
|
||||
} else if (control_name == _mute_control->name()) {
|
||||
if (child->get_property ("value", value)) {
|
||||
_session.set_control (_mute_control, value, Controllable::NoGroup);
|
||||
}
|
||||
} else if (control_name == _solo_safe_control->name()) {
|
||||
if (child->get_property ("solo-safe", value)) {
|
||||
_session.set_control (_solo_safe_control, value, Controllable::NoGroup);
|
||||
}
|
||||
} else if (control_name == _phase_control->name()) {
|
||||
std::string str;
|
||||
if (child->get_property (X_("phase-invert"), str)) {
|
||||
_phase_control->set_phase_invert (boost::dynamic_bitset<> (str));
|
||||
}
|
||||
}
|
||||
} else if (child->name() == MuteMaster::xml_node_name) {
|
||||
std::string mute_point;
|
||||
if (child->get_property ("mute-point", mute_point)) {
|
||||
_mute_master->set_mute_points (mute_point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert (use_pbd_ids || reuse.empty ());
|
||||
|
||||
/* now remove any plugins not present in the list.. */
|
||||
ProcessorList to_remove;
|
||||
{
|
||||
Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
|
||||
for (auto const& p : _processors) {
|
||||
if (is_internal_processor (p)) {
|
||||
continue;
|
||||
}
|
||||
std::shared_ptr<PluginInsert> pi;
|
||||
if ((pi = std::dynamic_pointer_cast<PluginInsert>(p)) != 0) {
|
||||
if (std::find (reuse.begin (), reuse.end(), pi->id ()) == reuse.end()) {
|
||||
to_remove.push_back (p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
remove_processors (to_remove, nullptr);
|
||||
|
||||
int rv = add_processors (new_processors, std::shared_ptr<Processor> (), nullptr);
|
||||
|
||||
/* drop references */
|
||||
to_remove.clear ();
|
||||
new_processors.clear ();
|
||||
|
||||
if (rv) {
|
||||
/* adding processors failed above, so ensure that
|
||||
* set_processor_state does override IDs
|
||||
*/
|
||||
PBD::Stateful::ForceIDRegeneration force_ids;
|
||||
set_processor_state (processor_state, version);
|
||||
} else {
|
||||
set_processor_state (processor_state, version);
|
||||
}
|
||||
|
||||
reset_instrument_info();
|
||||
|
||||
MeterPoint mp;
|
||||
if (node.get_property (X_("meter-point"), mp)) {
|
||||
set_meter_point (mp);
|
||||
if (_meter) {
|
||||
_meter->set_display_to_user (_meter_point == MeterCustom);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
XMLNode&
|
||||
Route::get_processor_state ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@
|
|||
#include <glib.h>
|
||||
#include "pbd/gstdio_compat.h"
|
||||
#include "pbd/locale_guard.h"
|
||||
#include "pbd/strsplit.h"
|
||||
|
||||
#include <glibmm.h>
|
||||
#include <glibmm/threads.h>
|
||||
|
|
@ -1114,7 +1115,7 @@ Session::get_template ()
|
|||
typedef std::set<std::shared_ptr<Source> > SourceSet;
|
||||
|
||||
bool
|
||||
Session::export_track_state (std::shared_ptr<RouteList> rl, const string& path)
|
||||
Session::export_route_state (std::shared_ptr<RouteList> rl, const string& path, bool with_sources)
|
||||
{
|
||||
if (Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
|
||||
return false;
|
||||
|
|
@ -1126,64 +1127,286 @@ Session::export_track_state (std::shared_ptr<RouteList> rl, const string& path)
|
|||
PBD::Unwinder<std::string> uw (_template_state_dir, path);
|
||||
|
||||
LocaleGuard lg;
|
||||
XMLNode* node = new XMLNode("TrackState"); // XXX
|
||||
XMLNode* node = new XMLNode("RouteState");
|
||||
XMLNode* child;
|
||||
|
||||
PlaylistSet playlists; // SessionPlaylists
|
||||
SourceSet sources;
|
||||
node->set_property ("uuid", _uuid.to_s());
|
||||
|
||||
// these will work with new_route_from_template()
|
||||
// TODO: LV2 plugin-state-dir needs to be relative (on load?)
|
||||
PlaylistSet playlists; // SessionPlaylists
|
||||
SourceSet sources;
|
||||
|
||||
/* these will work with new_route_from_template()
|
||||
* TODO: LV2 plugin-state-dir needs to be relative (on load?)
|
||||
*/
|
||||
child = node->add_child ("Routes");
|
||||
for (RouteList::iterator i = rl->begin(); i != rl->end(); ++i) {
|
||||
if ((*i)->is_auditioner()) {
|
||||
for (auto const& r: *rl) {
|
||||
if (r->is_auditioner()) {
|
||||
continue;
|
||||
}
|
||||
if ((*i)->is_singleton()) {
|
||||
if (r->is_singleton() && !r->is_master ()) {
|
||||
continue;
|
||||
}
|
||||
child->add_child_nocopy ((*i)->get_state());
|
||||
std::shared_ptr<Track> track = std::dynamic_pointer_cast<Track> (*i);
|
||||
if (r->is_foldbackbus()) {
|
||||
continue;
|
||||
}
|
||||
child->add_child_nocopy (r->get_state());
|
||||
std::shared_ptr<Track> track = std::dynamic_pointer_cast<Track> (r);
|
||||
if (track) {
|
||||
playlists.insert (track->playlist ());
|
||||
}
|
||||
}
|
||||
|
||||
// on load, Regions in the playlists need to resolve and map Source-IDs
|
||||
// also playlist needs to be merged or created with new-name..
|
||||
// ... and Diskstream in tracks adjusted to use the correct playlist
|
||||
child = node->add_child ("Playlists"); // SessionPlaylists::add_state
|
||||
for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
|
||||
child->add_child_nocopy ((*i)->get_state ());
|
||||
std::shared_ptr<RegionList> prl = (*i)->region_list ();
|
||||
for (RegionList::const_iterator s = prl->begin(); s != prl->end(); ++s) {
|
||||
const Region::SourceList& sl = (*s)->sources ();
|
||||
for (Region::SourceList::const_iterator sli = sl.begin(); sli != sl.end(); ++sli) {
|
||||
sources.insert (*sli);
|
||||
child = node->add_child ("RouteGroups");
|
||||
for (auto const& rg : _route_groups) {
|
||||
child->add_child_nocopy (rg->get_state ());
|
||||
}
|
||||
|
||||
if (with_sources) {
|
||||
/* on load, Regions in the playlists need to resolve and map Source-IDs
|
||||
* also playlist needs to be merged or created with new-name..
|
||||
* ... and Diskstream in tracks adjusted to use the correct playlist
|
||||
*/
|
||||
child = node->add_child ("Playlists"); // SessionPlaylists::add_state
|
||||
for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) {
|
||||
child->add_child_nocopy ((*i)->get_state ());
|
||||
std::shared_ptr<RegionList> prl = (*i)->region_list ();
|
||||
for (RegionList::const_iterator s = prl->begin(); s != prl->end(); ++s) {
|
||||
const Region::SourceList& sl = (*s)->sources ();
|
||||
for (Region::SourceList::const_iterator sli = sl.begin(); sli != sl.end(); ++sli) {
|
||||
sources.insert (*sli);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
child = node->add_child ("Sources");
|
||||
for (SourceSet::const_iterator i = sources.begin(); i != sources.end(); ++i) {
|
||||
child->add_child_nocopy ((*i)->get_state ());
|
||||
std::shared_ptr<FileSource> fs = std::dynamic_pointer_cast<FileSource> (*i);
|
||||
if (fs) {
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
fs->close ();
|
||||
#endif
|
||||
string p = fs->path ();
|
||||
PBD::copy_file (p, Glib::build_filename (path, Glib::path_get_basename (p)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
child = node->add_child ("Sources");
|
||||
for (SourceSet::const_iterator i = sources.begin(); i != sources.end(); ++i) {
|
||||
child->add_child_nocopy ((*i)->get_state ());
|
||||
std::shared_ptr<FileSource> fs = std::dynamic_pointer_cast<FileSource> (*i);
|
||||
if (fs) {
|
||||
#ifdef PLATFORM_WINDOWS
|
||||
fs->close ();
|
||||
#endif
|
||||
string p = fs->path ();
|
||||
PBD::copy_file (p, Glib::build_filename (path, Glib::path_get_basename (p)));
|
||||
}
|
||||
}
|
||||
|
||||
std::string sn = Glib::build_filename (path, "share.axml");
|
||||
std::string sn = Glib::build_filename (path, PBD::basename_nosuffix (path) + routestate_suffix);
|
||||
|
||||
XMLTree tree;
|
||||
tree.set_root (node);
|
||||
return tree.write (sn.c_str());
|
||||
}
|
||||
|
||||
static bool
|
||||
allow_import_route_state (XMLNode const& node, int version)
|
||||
{
|
||||
XMLNode* pnode = node.child (PresentationInfo::state_node_name.c_str ());
|
||||
if (!pnode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PresentationInfo pi (PresentationInfo::Flag (0));
|
||||
pi.set_state (*pnode, version);
|
||||
|
||||
if (pi.special (false)) { // |SurroundMaster|MonitorOut|Auditioner
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pi.flags() & (PresentationInfo::FoldbackBus | PresentationInfo::VBMAny)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::map<PBD::ID, std::string>
|
||||
Session::parse_route_state (const string& path, bool& match_pbd_id)
|
||||
{
|
||||
std::map<PBD::ID, std::string> rv;
|
||||
|
||||
XMLTree tree;
|
||||
if (!tree.read (path)) {
|
||||
error << string_compose (_("Could not understand state file \"%1\""), path) << endmsg;
|
||||
return rv;
|
||||
}
|
||||
if (tree.root()->name() != X_("RouteState") && tree.root()->name() != X_("Session")) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
XMLProperty const* prop;
|
||||
if ((prop = tree.root()->property ("uuid")) && _uuid == PBD::UUID (prop->value())) {
|
||||
match_pbd_id = true;
|
||||
} else {
|
||||
match_pbd_id = false;
|
||||
}
|
||||
|
||||
XMLNode* xroutes = tree.root()->child ("Routes");
|
||||
if (xroutes) {
|
||||
/* foreach route .. */
|
||||
for (auto const rxml : xroutes->children()) {
|
||||
int version = 0;
|
||||
if (!rxml->get_property ("version", version)) {
|
||||
continue;
|
||||
}
|
||||
PBD::ID id;
|
||||
if (!rxml->get_property ("id", id)) {
|
||||
continue;
|
||||
}
|
||||
std::string name;
|
||||
if (!rxml->get_property ("name", name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!allow_import_route_state (*rxml, version)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
rv[id] = name;
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
Session::import_route_state (const string& path, std::map<PBD::ID, PBD::ID> const& idmap, RouteGroupImportMode rgim)
|
||||
{
|
||||
/* idmap: <local route ID : extern/XML route ID>
|
||||
* a given route may only be set to the state of one extern ID,
|
||||
* but extern state can be applied to multiple routes (or create new ones)
|
||||
*/
|
||||
XMLTree tree;
|
||||
if (!tree.read (path)) {
|
||||
error << string_compose (_("Could not understand state file \"%1\""),_path) << endmsg;
|
||||
return -1;
|
||||
}
|
||||
if (tree.root()->name() != X_("RouteState") && tree.root()->name() != X_("Session")) { // XXX
|
||||
return -2;
|
||||
}
|
||||
|
||||
int version = 0;
|
||||
|
||||
/* session has a global property */
|
||||
tree.root()->get_property ("version", version);
|
||||
|
||||
bool from_this_session;
|
||||
XMLProperty const* prop;
|
||||
if ((prop = tree.root()->property ("uuid")) && _uuid == PBD::UUID (prop->value())) {
|
||||
from_this_session = true;
|
||||
} else {
|
||||
from_this_session = false;
|
||||
}
|
||||
|
||||
std::map<PBD::ID, std::string> route_groupname;
|
||||
|
||||
XMLNode* xgroups = tree.root()->child ("RouteGroups");
|
||||
if (xgroups) {
|
||||
for (auto const& rgxml : xgroups->children()) {
|
||||
/* see also Session::load_route_groups */
|
||||
if (rgxml->name() != "RouteGroup") {
|
||||
continue;
|
||||
}
|
||||
std::string name;
|
||||
if (!rgxml->get_property ("name", name)) {
|
||||
continue;
|
||||
}
|
||||
/* see also RouteGroup::set_state */
|
||||
std::string routes;
|
||||
if (rgxml->get_property ("routes", routes)) {
|
||||
stringstream str (routes);
|
||||
vector<string> ids;
|
||||
split (str.str(), ids, ' ');
|
||||
for (auto const& i : ids) {
|
||||
PBD::ID id (i);
|
||||
route_groupname[id] = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
XMLNode* xroutes = tree.root()->child ("Routes");
|
||||
if (xroutes) {
|
||||
/* foreach route .. */
|
||||
for (auto const rxml : xroutes->children()) {
|
||||
/* track-state includes version per route */
|
||||
if (!rxml->get_property ("version", version) || version == 0) {
|
||||
continue;
|
||||
}
|
||||
PBD::ID id;
|
||||
if (!rxml->get_property ("id", id)) {
|
||||
continue;
|
||||
}
|
||||
for (auto [dst, src] : idmap) {
|
||||
if (src != id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
XMLNode* pnode = rxml->child (PresentationInfo::state_node_name.c_str ());
|
||||
PresentationInfo pi (PresentationInfo::Flag (0));
|
||||
pi.set_state (*pnode, version);
|
||||
|
||||
std::shared_ptr<Route> r = route_by_id (dst);
|
||||
|
||||
/* note: audtioner, monitor-out, etc are skipped in `allow_import_route_state` */
|
||||
#ifdef MIXBUS
|
||||
static const int special_pi = PresentationInfo::Mixbus | PresentationInfo::VBMAny | PresentationInfo::MasterOut;
|
||||
#else
|
||||
static const int special_pi = PresentationInfo::Mixbus | PresentationInfo::VBMAny;
|
||||
#endif
|
||||
|
||||
/* special case, new track from special routes */
|
||||
if (!r && 0 != (pi.flags () & special_pi)) {
|
||||
auto rl = new_audio_track (1, 2, 0, 1, "", PresentationInfo::max_order, Normal, true, false);
|
||||
assert (rl.size () < 2);
|
||||
if (rl.size () > 0) {
|
||||
r = rl.front ();
|
||||
}
|
||||
}
|
||||
|
||||
if (r) {
|
||||
r->import_state (*rxml, from_this_session);
|
||||
} else if (allow_import_route_state (*rxml, version)) {
|
||||
/* invalid ID (e.g. 0, -1 (int64_t max) -> new track */
|
||||
if (pi.flags () & special_pi) {
|
||||
continue;
|
||||
}
|
||||
pi.set_flags (PresentationInfo::Flag (pi.flags () & (PresentationInfo::AudioTrack | PresentationInfo::MidiTrack | PresentationInfo::AudioBus | PresentationInfo::MidiBus)));
|
||||
|
||||
XMLNode copy (*rxml);
|
||||
copy.remove_nodes_and_delete ("PresentationInfo"); // "Master"
|
||||
copy.add_child_nocopy (pi.get_state());
|
||||
|
||||
RouteList rl = new_route_from_template (1, PresentationInfo::max_order, copy, "", NewPlaylist);
|
||||
assert (rl.size () < 2);
|
||||
if (rl.size () > 0) {
|
||||
r = rl.front ();
|
||||
}
|
||||
}
|
||||
/* set route-group */
|
||||
if (r && route_groupname.find (src) != route_groupname.end ()) {
|
||||
RouteGroup* rg;
|
||||
switch (rgim) {
|
||||
case IgnoreRouteGroup:
|
||||
rg = nullptr;
|
||||
break;
|
||||
case UseRouteGroup:
|
||||
rg = route_group_by_name (route_groupname[src]);
|
||||
break;
|
||||
case CreateRouteGroup:
|
||||
rg = new_route_group (route_groupname[src]);
|
||||
break;
|
||||
}
|
||||
if (rg) {
|
||||
rg->add (r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
merge_all_sources (std::shared_ptr<const Playlist> pl, std::set<std::shared_ptr<Source> >* all_sources)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,5 +6,17 @@ function factory () return function ()
|
|||
for r in sel.tracks:routelist ():iter () do
|
||||
rlp:push_back (r)
|
||||
end
|
||||
print (Session:export_track_state (rlp, "/tmp/rexport"))
|
||||
print (Session:export_route_state (rlp, "/tmp/rexport", false))
|
||||
--[[
|
||||
|
||||
local idmap = ARDOUR.IDMap ()
|
||||
local nm = Session:parse_route_state ("/tmp/rexport/rexport.routestate", false)
|
||||
for id, name in pairs (nm:table()) do
|
||||
print (id:to_s(), name)
|
||||
idmap:add ({[id] = id})
|
||||
end
|
||||
|
||||
print (Session:import_route_state ("/tmp/rexport/rexport.routestate", idmap))
|
||||
|
||||
--]]
|
||||
end end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue