|
|
@@ -44,6 +44,8 @@ |
|
|
#include "water/xml/XmlDocument.h" |
|
|
#include "water/xml/XmlDocument.h" |
|
|
#include "water/xml/XmlElement.h" |
|
|
#include "water/xml/XmlElement.h" |
|
|
|
|
|
|
|
|
|
|
|
#include <map> |
|
|
|
|
|
|
|
|
// FIXME Remove on 2.1 release |
|
|
// FIXME Remove on 2.1 release |
|
|
#include "lv2/atom.h" |
|
|
#include "lv2/atom.h" |
|
|
|
|
|
|
|
|
@@ -2355,14 +2357,6 @@ void CarlaEngine::saveProjectInternal(water::MemoryOutputStream& outStream) cons |
|
|
{ |
|
|
{ |
|
|
saveExternalConnections = false; |
|
|
saveExternalConnections = false; |
|
|
} |
|
|
} |
|
|
else if (std::getenv("LADISH_APP_NAME") != nullptr) |
|
|
|
|
|
{ |
|
|
|
|
|
saveExternalConnections = false; |
|
|
|
|
|
} |
|
|
|
|
|
else if (std::getenv("NSM_URL") != nullptr) |
|
|
|
|
|
{ |
|
|
|
|
|
saveExternalConnections = false; |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
else |
|
|
{ |
|
|
{ |
|
|
saveExternalConnections = true; |
|
|
saveExternalConnections = true; |
|
|
@@ -2524,6 +2518,7 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc, const bool alw |
|
|
if (carla_isEqual(xmlElement->getDoubleAttribute("VERSION", 0.0), 2.0) || |
|
|
if (carla_isEqual(xmlElement->getDoubleAttribute("VERSION", 0.0), 2.0) || |
|
|
xmlElement->getBoolAttribute("IgnoreClientPrefix", false)) |
|
|
xmlElement->getBoolAttribute("IgnoreClientPrefix", false)) |
|
|
{ |
|
|
{ |
|
|
|
|
|
carla_stdout("Loading project in compatibility mode, will ignore client name prefix"); |
|
|
pData->ignoreClientPrefix = true; |
|
|
pData->ignoreClientPrefix = true; |
|
|
setOption(ENGINE_OPTION_CLIENT_NAME_PREFIX, 0, ""); |
|
|
setOption(ENGINE_OPTION_CLIENT_NAME_PREFIX, 0, ""); |
|
|
} |
|
|
} |
|
|
@@ -2952,40 +2947,45 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc, const bool alw |
|
|
return false; |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
bool hasInternalConnections = false; |
|
|
|
|
|
|
|
|
// now we handle positions |
|
|
|
|
|
std::map<water::String, water::String> mapGroupNamesInternal, mapGroupNamesExternal; |
|
|
|
|
|
|
|
|
// and now we handle connections (internal) |
|
|
|
|
|
if (XmlElement* const elem = xmlElement->getChildByName("Patchbay")) |
|
|
|
|
|
|
|
|
if (XmlElement* const elemPatchbay = xmlElement->getChildByName("Patchbay")) |
|
|
{ |
|
|
{ |
|
|
hasInternalConnections = true; |
|
|
|
|
|
|
|
|
|
|
|
if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY) |
|
|
|
|
|
|
|
|
if (XmlElement* const elemPositions = elemPatchbay->getChildByName("Positions")) |
|
|
{ |
|
|
{ |
|
|
CarlaString sourcePort, targetPort; |
|
|
|
|
|
|
|
|
String name; |
|
|
|
|
|
PatchbayPosition ppos = { nullptr, -1, 0, 0, 0, 0, false }; |
|
|
|
|
|
|
|
|
for (XmlElement* patchElem = elem->getFirstChildElement(); patchElem != nullptr; patchElem = patchElem->getNextElement()) |
|
|
|
|
|
|
|
|
for (XmlElement* patchElem = elemPositions->getFirstChildElement(); patchElem != nullptr; patchElem = patchElem->getNextElement()) |
|
|
{ |
|
|
{ |
|
|
const String& patchTag(patchElem->getTagName()); |
|
|
const String& patchTag(patchElem->getTagName()); |
|
|
|
|
|
|
|
|
if (patchTag != "Connection") |
|
|
|
|
|
|
|
|
if (patchTag != "Position") |
|
|
continue; |
|
|
continue; |
|
|
|
|
|
|
|
|
sourcePort.clear(); |
|
|
|
|
|
targetPort.clear(); |
|
|
|
|
|
|
|
|
XmlElement* const patchName = patchElem->getChildByName("Name"); |
|
|
|
|
|
CARLA_SAFE_ASSERT_CONTINUE(patchName != nullptr); |
|
|
|
|
|
|
|
|
for (XmlElement* connElem = patchElem->getFirstChildElement(); connElem != nullptr; connElem = connElem->getNextElement()) |
|
|
|
|
|
|
|
|
const String nameText(patchName->getAllSubText().trim()); |
|
|
|
|
|
name = xmlSafeString(nameText, false); |
|
|
|
|
|
|
|
|
|
|
|
ppos.name = name.toRawUTF8(); |
|
|
|
|
|
ppos.x1 = patchElem->getIntAttribute("x1"); |
|
|
|
|
|
ppos.y1 = patchElem->getIntAttribute("y1"); |
|
|
|
|
|
ppos.x2 = patchElem->getIntAttribute("x2"); |
|
|
|
|
|
ppos.y2 = patchElem->getIntAttribute("y2"); |
|
|
|
|
|
ppos.pluginId = patchElem->getIntAttribute("pluginId", -1); |
|
|
|
|
|
ppos.dealloc = false; |
|
|
|
|
|
|
|
|
|
|
|
if (name.isNotEmpty() && restorePatchbayGroupPosition(false, ppos)) |
|
|
{ |
|
|
{ |
|
|
const String& tag(connElem->getTagName()); |
|
|
|
|
|
const String text(connElem->getAllSubText().trim()); |
|
|
|
|
|
|
|
|
carla_stdout("Converted client name '%s' to '%s' for this session", name.toRawUTF8(), ppos.name); |
|
|
|
|
|
mapGroupNamesInternal[name] = ppos.name; |
|
|
|
|
|
|
|
|
/**/ if (tag == "Source") |
|
|
|
|
|
sourcePort = xmlSafeString(text, false).toRawUTF8(); |
|
|
|
|
|
else if (tag == "Target") |
|
|
|
|
|
targetPort = xmlSafeString(text, false).toRawUTF8(); |
|
|
|
|
|
|
|
|
if (ppos.dealloc) |
|
|
|
|
|
std::free(const_cast<char*>(ppos.name)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (sourcePort.isNotEmpty() && targetPort.isNotEmpty()) |
|
|
|
|
|
restorePatchbayConnection(false, sourcePort, targetPort); |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
callback(true, true, ENGINE_CALLBACK_IDLE, 0, 0, 0, 0, 0.0f, nullptr); |
|
|
callback(true, true, ENGINE_CALLBACK_IDLE, 0, 0, 0, 0, 0.0f, nullptr); |
|
|
@@ -3001,8 +3001,7 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc, const bool alw |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// now we handle positions |
|
|
|
|
|
if (XmlElement* const elemPatchbay = xmlElement->getChildByName("Patchbay")) |
|
|
|
|
|
|
|
|
if (XmlElement* const elemPatchbay = xmlElement->getChildByName("ExternalPatchbay")) |
|
|
{ |
|
|
{ |
|
|
if (XmlElement* const elemPositions = elemPatchbay->getChildByName("Positions")) |
|
|
if (XmlElement* const elemPositions = elemPatchbay->getChildByName("Positions")) |
|
|
{ |
|
|
{ |
|
|
@@ -3028,9 +3027,16 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc, const bool alw |
|
|
ppos.x2 = patchElem->getIntAttribute("x2"); |
|
|
ppos.x2 = patchElem->getIntAttribute("x2"); |
|
|
ppos.y2 = patchElem->getIntAttribute("y2"); |
|
|
ppos.y2 = patchElem->getIntAttribute("y2"); |
|
|
ppos.pluginId = patchElem->getIntAttribute("pluginId", -1); |
|
|
ppos.pluginId = patchElem->getIntAttribute("pluginId", -1); |
|
|
|
|
|
ppos.dealloc = false; |
|
|
|
|
|
|
|
|
|
|
|
if (name.isNotEmpty() && restorePatchbayGroupPosition(true, ppos)) |
|
|
|
|
|
{ |
|
|
|
|
|
carla_stdout("Converted client name '%s' to '%s' for this session", name.toRawUTF8(), ppos.name); |
|
|
|
|
|
mapGroupNamesExternal[name] = ppos.name; |
|
|
|
|
|
|
|
|
if (name.isNotEmpty()) |
|
|
|
|
|
restorePatchbayGroupPosition(false, ppos); |
|
|
|
|
|
|
|
|
if (ppos.dealloc) |
|
|
|
|
|
std::free(const_cast<char*>(ppos.name)); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
callback(true, true, ENGINE_CALLBACK_IDLE, 0, 0, 0, 0, 0.0f, nullptr); |
|
|
callback(true, true, ENGINE_CALLBACK_IDLE, 0, 0, 0, 0, 0.0f, nullptr); |
|
|
@@ -3046,35 +3052,60 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc, const bool alw |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (XmlElement* const elemPatchbay = xmlElement->getChildByName("ExternalPatchbay")) |
|
|
|
|
|
|
|
|
bool hasInternalConnections = false; |
|
|
|
|
|
|
|
|
|
|
|
// and now we handle connections (internal) |
|
|
|
|
|
if (XmlElement* const elem = xmlElement->getChildByName("Patchbay")) |
|
|
{ |
|
|
{ |
|
|
if (XmlElement* const elemPositions = elemPatchbay->getChildByName("Positions")) |
|
|
|
|
|
|
|
|
hasInternalConnections = true; |
|
|
|
|
|
|
|
|
|
|
|
if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY) |
|
|
{ |
|
|
{ |
|
|
String name; |
|
|
|
|
|
PatchbayPosition ppos = { nullptr, -1, 0, 0, 0, 0, false }; |
|
|
|
|
|
|
|
|
water::String sourcePort, targetPort; |
|
|
|
|
|
|
|
|
for (XmlElement* patchElem = elemPositions->getFirstChildElement(); patchElem != nullptr; patchElem = patchElem->getNextElement()) |
|
|
|
|
|
|
|
|
for (XmlElement* patchElem = elem->getFirstChildElement(); patchElem != nullptr; patchElem = patchElem->getNextElement()) |
|
|
{ |
|
|
{ |
|
|
const String& patchTag(patchElem->getTagName()); |
|
|
const String& patchTag(patchElem->getTagName()); |
|
|
|
|
|
|
|
|
if (patchTag != "Position") |
|
|
|
|
|
|
|
|
if (patchTag != "Connection") |
|
|
continue; |
|
|
continue; |
|
|
|
|
|
|
|
|
XmlElement* const patchName = patchElem->getChildByName("Name"); |
|
|
|
|
|
CARLA_SAFE_ASSERT_CONTINUE(patchName != nullptr); |
|
|
|
|
|
|
|
|
sourcePort.clear(); |
|
|
|
|
|
targetPort.clear(); |
|
|
|
|
|
|
|
|
const String nameText(patchName->getAllSubText().trim()); |
|
|
|
|
|
name = xmlSafeString(nameText, false); |
|
|
|
|
|
|
|
|
for (XmlElement* connElem = patchElem->getFirstChildElement(); connElem != nullptr; connElem = connElem->getNextElement()) |
|
|
|
|
|
{ |
|
|
|
|
|
const String& tag(connElem->getTagName()); |
|
|
|
|
|
const String text(connElem->getAllSubText().trim()); |
|
|
|
|
|
|
|
|
ppos.name = name.toRawUTF8(); |
|
|
|
|
|
ppos.x1 = patchElem->getIntAttribute("x1"); |
|
|
|
|
|
ppos.y1 = patchElem->getIntAttribute("y1"); |
|
|
|
|
|
ppos.x2 = patchElem->getIntAttribute("x2"); |
|
|
|
|
|
ppos.y2 = patchElem->getIntAttribute("y2"); |
|
|
|
|
|
ppos.pluginId = patchElem->getIntAttribute("pluginId", -1); |
|
|
|
|
|
|
|
|
/**/ if (tag == "Source") |
|
|
|
|
|
sourcePort = xmlSafeString(text, false); |
|
|
|
|
|
else if (tag == "Target") |
|
|
|
|
|
targetPort = xmlSafeString(text, false); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (name.isNotEmpty()) |
|
|
|
|
|
restorePatchbayGroupPosition(true, ppos); |
|
|
|
|
|
|
|
|
if (sourcePort.isNotEmpty() && targetPort.isNotEmpty()) |
|
|
|
|
|
{ |
|
|
|
|
|
std::map<water::String, water::String>& map(mapGroupNamesInternal); |
|
|
|
|
|
std::map<water::String, water::String>::iterator it; |
|
|
|
|
|
if ((it = map.find(sourcePort.upToFirstOccurrenceOf(":", false, false))) != map.end()) |
|
|
|
|
|
sourcePort = it->second + sourcePort.fromFirstOccurrenceOf(":", true, false); |
|
|
|
|
|
if ((it = map.find(targetPort.upToFirstOccurrenceOf(":", false, false))) != map.end()) |
|
|
|
|
|
targetPort = it->second + targetPort.fromFirstOccurrenceOf(":", true, false); |
|
|
|
|
|
|
|
|
|
|
|
restorePatchbayConnection(false, sourcePort.toRawUTF8(), targetPort.toRawUTF8()); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
callback(true, true, ENGINE_CALLBACK_IDLE, 0, 0, 0, 0, 0.0f, nullptr); |
|
|
|
|
|
|
|
|
|
|
|
if (pData->aboutToClose) |
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
|
|
|
|
if (pData->actionCanceled) |
|
|
|
|
|
{ |
|
|
|
|
|
setLastError("Project load canceled"); |
|
|
|
|
|
return false; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@@ -3127,7 +3158,7 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc, const bool alw |
|
|
continue; |
|
|
continue; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
CarlaString sourcePort, targetPort; |
|
|
|
|
|
|
|
|
water::String sourcePort, targetPort; |
|
|
|
|
|
|
|
|
for (XmlElement* patchElem = elem->getFirstChildElement(); patchElem != nullptr; patchElem = patchElem->getNextElement()) |
|
|
for (XmlElement* patchElem = elem->getFirstChildElement(); patchElem != nullptr; patchElem = patchElem->getNextElement()) |
|
|
{ |
|
|
{ |
|
|
@@ -3145,13 +3176,23 @@ bool CarlaEngine::loadProjectInternal(water::XmlDocument& xmlDoc, const bool alw |
|
|
const String text(connElem->getAllSubText().trim()); |
|
|
const String text(connElem->getAllSubText().trim()); |
|
|
|
|
|
|
|
|
/**/ if (tag == "Source") |
|
|
/**/ if (tag == "Source") |
|
|
sourcePort = xmlSafeString(text, false).toRawUTF8(); |
|
|
|
|
|
|
|
|
sourcePort = xmlSafeString(text, false); |
|
|
else if (tag == "Target") |
|
|
else if (tag == "Target") |
|
|
targetPort = xmlSafeString(text, false).toRawUTF8(); |
|
|
|
|
|
|
|
|
targetPort = xmlSafeString(text, false); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (sourcePort.isNotEmpty() && targetPort.isNotEmpty()) |
|
|
if (sourcePort.isNotEmpty() && targetPort.isNotEmpty()) |
|
|
restorePatchbayConnection(loadingAsExternal, sourcePort, targetPort); |
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
std::map<water::String, water::String>& map(loadingAsExternal ? mapGroupNamesExternal |
|
|
|
|
|
: mapGroupNamesInternal); |
|
|
|
|
|
std::map<water::String, water::String>::iterator it; |
|
|
|
|
|
if ((it = map.find(sourcePort.upToFirstOccurrenceOf(":", false, false))) != map.end()) |
|
|
|
|
|
sourcePort = it->second + sourcePort.fromFirstOccurrenceOf(":", true, false); |
|
|
|
|
|
if ((it = map.find(targetPort.upToFirstOccurrenceOf(":", false, false))) != map.end()) |
|
|
|
|
|
targetPort = it->second + targetPort.fromFirstOccurrenceOf(":", true, false); |
|
|
|
|
|
|
|
|
|
|
|
restorePatchbayConnection(loadingAsExternal, sourcePort.toRawUTF8(), targetPort.toRawUTF8()); |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|