Browse Source

Make state utils calls part of StateSave struct

tags/1.9.4
falkTX 11 years ago
parent
commit
52babe107e
9 changed files with 340 additions and 338 deletions
  1. +5
    -5
      source/backend/CarlaPlugin.hpp
  2. +9
    -12
      source/backend/engine/CarlaEngine.cpp
  3. +2
    -2
      source/backend/engine/CarlaEngineJack.cpp
  4. +8
    -11
      source/backend/engine/CarlaEngineNative.cpp
  5. +46
    -50
      source/backend/plugin/CarlaPlugin.cpp
  6. +1
    -1
      source/backend/plugin/CarlaPluginInternal.hpp
  7. +2
    -4
      source/tests/CarlaUtils4.cpp
  8. +256
    -244
      source/utils/CarlaStateUtils.cpp
  9. +11
    -9
      source/utils/CarlaStateUtils.hpp

+ 5
- 5
source/backend/CarlaPlugin.hpp View File

@@ -49,7 +49,7 @@ class CarlaEngineAudioPort;
/*! /*!
* Save state data. * Save state data.
*/ */
struct SaveState;
struct StateSave;


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------


@@ -382,16 +382,16 @@ public:
* Get the plugin's save state.\n * Get the plugin's save state.\n
* The plugin will automatically call prepareForSave() as needed. * The plugin will automatically call prepareForSave() as needed.
* *
* \see loadSaveState()
* \see loadStateSave()
*/ */
const SaveState& getSaveState();
const StateSave& getStateSave();


/*! /*!
* Get the plugin's save state. * Get the plugin's save state.
* *
* \see getSaveState()
* \see getStateSave()
*/ */
void loadSaveState(const SaveState& saveState);
void loadStateSave(const StateSave& stateSave);


/*! /*!
* Save the current plugin state to \a filename. * Save the current plugin state to \a filename.


+ 9
- 12
source/backend/engine/CarlaEngine.cpp View File

@@ -1353,7 +1353,7 @@ bool CarlaEngine::clonePlugin(const uint id)
CARLA_SAFE_ASSERT_RETURN_ERR(pluginCountBefore+1 == pData->curPluginCount, "No new plugin found"); CARLA_SAFE_ASSERT_RETURN_ERR(pluginCountBefore+1 == pData->curPluginCount, "No new plugin found");


if (CarlaPlugin* const newPlugin = pData->plugins[pluginCountBefore].plugin) if (CarlaPlugin* const newPlugin = pData->plugins[pluginCountBefore].plugin)
newPlugin->loadSaveState(plugin->getSaveState());
newPlugin->loadStateSave(plugin->getStateSave());


return true; return true;
} }
@@ -1680,21 +1680,21 @@ bool CarlaEngine::loadProject(const char* const filename)
{ {
if (isPreset || node.toElement().tagName().compare("plugin", Qt::CaseInsensitive) == 0) if (isPreset || node.toElement().tagName().compare("plugin", Qt::CaseInsensitive) == 0)
{ {
SaveState saveState;
fillSaveStateFromXmlNode(saveState, isPreset ? xmlNode : node);
StateSave stateSave;
stateSave.fillFromXmlNode(isPreset ? xmlNode : node);


callback(ENGINE_CALLBACK_IDLE, 0, 0, 0, 0.0f, nullptr); callback(ENGINE_CALLBACK_IDLE, 0, 0, 0, 0.0f, nullptr);


CARLA_SAFE_ASSERT_CONTINUE(saveState.type != nullptr);
CARLA_SAFE_ASSERT_CONTINUE(stateSave.type != nullptr);


const void* extraStuff = nullptr; const void* extraStuff = nullptr;


// check if using GIG, SF2 or SFZ 16outs // check if using GIG, SF2 or SFZ 16outs
static const char kUse16OutsSuffix[] = " (16 outs)"; static const char kUse16OutsSuffix[] = " (16 outs)";


const PluginType ptype(getPluginTypeFromString(saveState.type));
const PluginType ptype(getPluginTypeFromString(stateSave.type));


if (CarlaString(saveState.label).endsWith(kUse16OutsSuffix))
if (CarlaString(stateSave.label).endsWith(kUse16OutsSuffix))
{ {
if (ptype == PLUGIN_FILE_GIG || ptype == PLUGIN_FILE_SF2) if (ptype == PLUGIN_FILE_GIG || ptype == PLUGIN_FILE_SF2)
extraStuff = "true"; extraStuff = "true";
@@ -1702,10 +1702,10 @@ bool CarlaEngine::loadProject(const char* const filename)


// TODO - proper find&load plugins // TODO - proper find&load plugins


if (addPlugin(ptype, saveState.binary, saveState.name, saveState.label, saveState.uniqueId, extraStuff))
if (addPlugin(ptype, stateSave.binary, stateSave.name, stateSave.label, stateSave.uniqueId, extraStuff))
{ {
if (CarlaPlugin* const plugin = getPlugin(pData->curPluginCount-1)) if (CarlaPlugin* const plugin = getPlugin(pData->curPluginCount-1))
plugin->loadSaveState(saveState);
plugin->loadStateSave(stateSave);
} }
else else
carla_stderr2("Failed to load a plugin, error was:%s\n", getLastError()); carla_stderr2("Failed to load a plugin, error was:%s\n", getLastError());
@@ -1796,11 +1796,8 @@ bool CarlaEngine::saveProject(const char* const filename)
//if (strBuf[0] != '\0') //if (strBuf[0] != '\0')
// out << QString(" <!-- %1 -->\n").arg(xmlSafeString(strBuf, true)); // out << QString(" <!-- %1 -->\n").arg(xmlSafeString(strBuf, true));


QString content;
fillXmlStringFromSaveState(content, plugin->getSaveState());

out << " <Plugin>\n"; out << " <Plugin>\n";
out << content;
out << plugin->getStateSave().toString();
out << " </Plugin>\n"; out << " </Plugin>\n";


firstPlugin = false; firstPlugin = false;


+ 2
- 2
source/backend/engine/CarlaEngineJack.cpp View File

@@ -1156,9 +1156,9 @@ public:
if (needsReinit) if (needsReinit)
{ {
// reload plugin to recreate its ports // reload plugin to recreate its ports
const SaveState& saveState(plugin->getSaveState());
const StateSave& saveState(plugin->getStateSave());
plugin->reload(); plugin->reload();
plugin->loadSaveState(saveState);
plugin->loadStateSave(saveState);
} }


return plugin->getName(); return plugin->getName();


+ 8
- 11
source/backend/engine/CarlaEngineNative.cpp View File

@@ -1313,11 +1313,8 @@ protected:
//if (strBuf[0] != '\0') //if (strBuf[0] != '\0')
// out << QString(" <!-- %1 -->\n").arg(xmlSafeString(strBuf, true)); // out << QString(" <!-- %1 -->\n").arg(xmlSafeString(strBuf, true));


QString content;
fillXmlStringFromSaveState(content, plugin->getSaveState());

out << " <Plugin>\n"; out << " <Plugin>\n";
out << content;
out << plugin->getStateSave().toString();
out << " </Plugin>\n"; out << " </Plugin>\n";


firstPlugin = false; firstPlugin = false;
@@ -1348,19 +1345,19 @@ protected:
{ {
if (node.toElement().tagName().compare("plugin", Qt::CaseInsensitive) == 0) if (node.toElement().tagName().compare("plugin", Qt::CaseInsensitive) == 0)
{ {
SaveState saveState;
fillSaveStateFromXmlNode(saveState, node);
StateSave stateSave;
stateSave.fillFromXmlNode(node);


CARLA_SAFE_ASSERT_CONTINUE(saveState.type != nullptr);
CARLA_SAFE_ASSERT_CONTINUE(stateSave.type != nullptr);


const void* extraStuff = nullptr; const void* extraStuff = nullptr;


// check if using GIG, SF2 or SFZ 16outs // check if using GIG, SF2 or SFZ 16outs
static const char kUse16OutsSuffix[] = " (16 outs)"; static const char kUse16OutsSuffix[] = " (16 outs)";


const PluginType ptype(getPluginTypeFromString(saveState.type));
const PluginType ptype(getPluginTypeFromString(stateSave.type));


if (CarlaString(saveState.label).endsWith(kUse16OutsSuffix))
if (CarlaString(stateSave.label).endsWith(kUse16OutsSuffix))
{ {
if (ptype == PLUGIN_FILE_GIG || ptype == PLUGIN_FILE_SF2) if (ptype == PLUGIN_FILE_GIG || ptype == PLUGIN_FILE_SF2)
extraStuff = "true"; extraStuff = "true";
@@ -1368,10 +1365,10 @@ protected:


// TODO - proper find&load plugins // TODO - proper find&load plugins


if (addPlugin(ptype, saveState.binary, saveState.name, saveState.label, saveState.uniqueId, extraStuff))
if (addPlugin(ptype, stateSave.binary, stateSave.name, stateSave.label, stateSave.uniqueId, extraStuff))
{ {
if (CarlaPlugin* const plugin = getPlugin(pData->curPluginCount-1)) if (CarlaPlugin* const plugin = getPlugin(pData->curPluginCount-1))
plugin->loadSaveState(saveState);
plugin->loadStateSave(stateSave);
} }


//pluginsAdded = true; //pluginsAdded = true;


+ 46
- 50
source/backend/plugin/CarlaPlugin.cpp View File

@@ -41,7 +41,7 @@ static const CustomData kCustomDataNull = { nullptr, nullptr, nullptr
static bool gIsLoadingProject = false; static bool gIsLoadingProject = false;


// ------------------------------------------------------------------- // -------------------------------------------------------------------
// ParamSymbol struct, needed for CarlaPlugin::loadSaveState()
// ParamSymbol struct, needed for CarlaPlugin::loadStateSave()


struct ParamSymbol { struct ParamSymbol {
int32_t index; int32_t index;
@@ -507,9 +507,9 @@ void CarlaPlugin::randomizeParameters() noexcept
} }
} }


const SaveState& CarlaPlugin::getSaveState()
const StateSave& CarlaPlugin::getStateSave()
{ {
pData->saveState.reset();
pData->stateSave.clear();
prepareForSave(); prepareForSave();


char strBuf[STR_MAX+1]; char strBuf[STR_MAX+1];
@@ -519,26 +519,26 @@ const SaveState& CarlaPlugin::getSaveState()


getLabel(strBuf); getLabel(strBuf);


pData->saveState.type = carla_strdup(getPluginTypeAsString(getType()));
pData->saveState.name = carla_strdup(pData->name);
pData->saveState.label = carla_strdup(strBuf);
pData->saveState.uniqueId = getUniqueId();
pData->stateSave.type = carla_strdup(getPluginTypeAsString(getType()));
pData->stateSave.name = carla_strdup(pData->name);
pData->stateSave.label = carla_strdup(strBuf);
pData->stateSave.uniqueId = getUniqueId();


if (pData->filename != nullptr) if (pData->filename != nullptr)
pData->saveState.binary = carla_strdup(pData->filename);
pData->stateSave.binary = carla_strdup(pData->filename);


// --------------------------------------------------------------- // ---------------------------------------------------------------
// Internals // Internals


pData->saveState.active = pData->active;
pData->stateSave.active = pData->active;


#ifndef BUILD_BRIDGE #ifndef BUILD_BRIDGE
pData->saveState.dryWet = pData->postProc.dryWet;
pData->saveState.volume = pData->postProc.volume;
pData->saveState.balanceLeft = pData->postProc.balanceLeft;
pData->saveState.balanceRight = pData->postProc.balanceRight;
pData->saveState.panning = pData->postProc.panning;
pData->saveState.ctrlChannel = pData->ctrlChannel;
pData->stateSave.dryWet = pData->postProc.dryWet;
pData->stateSave.volume = pData->postProc.volume;
pData->stateSave.balanceLeft = pData->postProc.balanceLeft;
pData->stateSave.balanceRight = pData->postProc.balanceRight;
pData->stateSave.panning = pData->postProc.panning;
pData->stateSave.ctrlChannel = pData->ctrlChannel;
#endif #endif


// --------------------------------------------------------------- // ---------------------------------------------------------------
@@ -551,10 +551,10 @@ const SaveState& CarlaPlugin::getSaveState()


if (data != nullptr && dataSize > 0) if (data != nullptr && dataSize > 0)
{ {
pData->saveState.chunk = carla_strdup(QByteArray((char*)data, dataSize).toBase64().constData());
pData->stateSave.chunk = carla_strdup(QByteArray((char*)data, dataSize).toBase64().constData());


// Don't save anything else if using chunks // Don't save anything else if using chunks
return pData->saveState;
return pData->stateSave;
} }
} }


@@ -563,8 +563,8 @@ const SaveState& CarlaPlugin::getSaveState()


if (pData->prog.current >= 0 && getType() != PLUGIN_LV2) if (pData->prog.current >= 0 && getType() != PLUGIN_LV2)
{ {
pData->saveState.currentProgramIndex = pData->prog.current;
pData->saveState.currentProgramName = carla_strdup(pData->prog.names[pData->prog.current]);
pData->stateSave.currentProgramIndex = pData->prog.current;
pData->stateSave.currentProgramName = carla_strdup(pData->prog.names[pData->prog.current]);
} }


// --------------------------------------------------------------- // ---------------------------------------------------------------
@@ -574,8 +574,8 @@ const SaveState& CarlaPlugin::getSaveState()
{ {
const MidiProgramData& mpData(pData->midiprog.getCurrent()); const MidiProgramData& mpData(pData->midiprog.getCurrent());


pData->saveState.currentMidiBank = static_cast<int32_t>(mpData.bank);
pData->saveState.currentMidiProgram = static_cast<int32_t>(mpData.program);
pData->stateSave.currentMidiBank = static_cast<int32_t>(mpData.bank);
pData->stateSave.currentMidiProgram = static_cast<int32_t>(mpData.program);
} }


// --------------------------------------------------------------- // ---------------------------------------------------------------
@@ -608,7 +608,7 @@ const SaveState& CarlaPlugin::getSaveState()
if (paramData.hints & PARAMETER_USES_SAMPLERATE) if (paramData.hints & PARAMETER_USES_SAMPLERATE)
stateParameter->value /= sampleRate; stateParameter->value /= sampleRate;


pData->saveState.parameters.append(stateParameter);
pData->stateSave.parameters.append(stateParameter);
} }


// --------------------------------------------------------------- // ---------------------------------------------------------------
@@ -624,13 +624,13 @@ const SaveState& CarlaPlugin::getSaveState()
stateCustomData->key = carla_strdup(cData.key); stateCustomData->key = carla_strdup(cData.key);
stateCustomData->value = carla_strdup(cData.value); stateCustomData->value = carla_strdup(cData.value);


pData->saveState.customData.append(stateCustomData);
pData->stateSave.customData.append(stateCustomData);
} }


return pData->saveState;
return pData->stateSave;
} }


void CarlaPlugin::loadSaveState(const SaveState& saveState)
void CarlaPlugin::loadStateSave(const StateSave& stateSave)
{ {
char strBuf[STR_MAX+1]; char strBuf[STR_MAX+1];
const bool usesMultiProgs(pData->extraHints & PLUGIN_EXTRA_HINT_USES_MULTI_PROGS); const bool usesMultiProgs(pData->extraHints & PLUGIN_EXTRA_HINT_USES_MULTI_PROGS);
@@ -641,7 +641,7 @@ void CarlaPlugin::loadSaveState(const SaveState& saveState)
// --------------------------------------------------------------- // ---------------------------------------------------------------
// Part 1 - PRE-set custom data (only that which reload programs) // Part 1 - PRE-set custom data (only that which reload programs)


for (LinkedList<StateCustomData*>::Itenerator it = saveState.customData.begin(); it.valid(); it.next())
for (LinkedList<StateCustomData*>::Itenerator it = stateSave.customData.begin(); it.valid(); it.next())
{ {
const StateCustomData* const stateCustomData(it.getValue()); const StateCustomData* const stateCustomData(it.getValue());
const char* const key(stateCustomData->key); const char* const key(stateCustomData->key);
@@ -660,14 +660,14 @@ void CarlaPlugin::loadSaveState(const SaveState& saveState)
// --------------------------------------------------------------- // ---------------------------------------------------------------
// Part 2 - set program // Part 2 - set program


if (saveState.currentProgramIndex >= 0 && saveState.currentProgramName != nullptr)
if (stateSave.currentProgramIndex >= 0 && stateSave.currentProgramName != nullptr)
{ {
int32_t programId = -1; int32_t programId = -1;


// index < count // index < count
if (saveState.currentProgramIndex < static_cast<int32_t>(pData->prog.count))
if (stateSave.currentProgramIndex < static_cast<int32_t>(pData->prog.count))
{ {
programId = saveState.currentProgramIndex;
programId = stateSave.currentProgramIndex;
} }
// index not valid, try to find by name // index not valid, try to find by name
else else
@@ -677,7 +677,7 @@ void CarlaPlugin::loadSaveState(const SaveState& saveState)
strBuf[0] = '\0'; strBuf[0] = '\0';
getProgramName(i, strBuf); getProgramName(i, strBuf);


if (strBuf[0] != '\0' && std::strcmp(saveState.currentProgramName, strBuf) == 0)
if (strBuf[0] != '\0' && std::strcmp(stateSave.currentProgramName, strBuf) == 0)
{ {
programId = static_cast<int32_t>(i); programId = static_cast<int32_t>(i);
break; break;
@@ -693,8 +693,8 @@ void CarlaPlugin::loadSaveState(const SaveState& saveState)
// --------------------------------------------------------------- // ---------------------------------------------------------------
// Part 3 - set midi program // Part 3 - set midi program


if (saveState.currentMidiBank >= 0 && saveState.currentMidiProgram >= 0 && ! usesMultiProgs)
setMidiProgramById(static_cast<uint32_t>(saveState.currentMidiBank), static_cast<uint32_t>(saveState.currentMidiProgram), true, true, true);
if (stateSave.currentMidiBank >= 0 && stateSave.currentMidiProgram >= 0 && ! usesMultiProgs)
setMidiProgramById(static_cast<uint32_t>(stateSave.currentMidiBank), static_cast<uint32_t>(stateSave.currentMidiProgram), true, true, true);


// --------------------------------------------------------------- // ---------------------------------------------------------------
// Part 4a - get plugin parameter symbols // Part 4a - get plugin parameter symbols
@@ -721,7 +721,7 @@ void CarlaPlugin::loadSaveState(const SaveState& saveState)


const float sampleRate(static_cast<float>(pData->engine->getSampleRate())); const float sampleRate(static_cast<float>(pData->engine->getSampleRate()));


for (LinkedList<StateParameter*>::Itenerator it = saveState.parameters.begin(); it.valid(); it.next())
for (LinkedList<StateParameter*>::Itenerator it = stateSave.parameters.begin(); it.valid(); it.next())
{ {
StateParameter* const stateParameter(it.getValue()); StateParameter* const stateParameter(it.getValue());


@@ -811,7 +811,7 @@ void CarlaPlugin::loadSaveState(const SaveState& saveState)
// --------------------------------------------------------------- // ---------------------------------------------------------------
// Part 5 - set custom data // Part 5 - set custom data


for (LinkedList<StateCustomData*>::Itenerator it = saveState.customData.begin(); it.valid(); it.next())
for (LinkedList<StateCustomData*>::Itenerator it = stateSave.customData.begin(); it.valid(); it.next())
{ {
const StateCustomData* const stateCustomData(it.getValue()); const StateCustomData* const stateCustomData(it.getValue());
const char* const key(stateCustomData->key); const char* const key(stateCustomData->key);
@@ -833,22 +833,22 @@ void CarlaPlugin::loadSaveState(const SaveState& saveState)
// --------------------------------------------------------------- // ---------------------------------------------------------------
// Part 6 - set chunk // Part 6 - set chunk


if (saveState.chunk != nullptr && (pData->options & PLUGIN_OPTION_USE_CHUNKS) != 0)
setChunkData(saveState.chunk);
if (stateSave.chunk != nullptr && (pData->options & PLUGIN_OPTION_USE_CHUNKS) != 0)
setChunkData(stateSave.chunk);


// --------------------------------------------------------------- // ---------------------------------------------------------------
// Part 6 - set internal stuff // Part 6 - set internal stuff


#ifndef BUILD_BRIDGE #ifndef BUILD_BRIDGE
setDryWet(saveState.dryWet, true, true);
setVolume(saveState.volume, true, true);
setBalanceLeft(saveState.balanceLeft, true, true);
setBalanceRight(saveState.balanceRight, true, true);
setPanning(saveState.panning, true, true);
setCtrlChannel(saveState.ctrlChannel, true, true);
setDryWet(stateSave.dryWet, true, true);
setVolume(stateSave.volume, true, true);
setBalanceLeft(stateSave.balanceLeft, true, true);
setBalanceRight(stateSave.balanceRight, true, true);
setPanning(stateSave.panning, true, true);
setCtrlChannel(stateSave.ctrlChannel, true, true);
#endif #endif


setActive(saveState.active, true, true);
setActive(stateSave.active, true, true);
} }


bool CarlaPlugin::saveStateToFile(const char* const filename) bool CarlaPlugin::saveStateToFile(const char* const filename)
@@ -861,14 +861,11 @@ bool CarlaPlugin::saveStateToFile(const char* const filename)
if (! file.open(QIODevice::WriteOnly | QIODevice::Text)) if (! file.open(QIODevice::WriteOnly | QIODevice::Text))
return false; return false;


QString content;
fillXmlStringFromSaveState(content, getSaveState());

QTextStream out(&file); QTextStream out(&file);
out << "<?xml version='1.0' encoding='UTF-8'?>\n"; out << "<?xml version='1.0' encoding='UTF-8'?>\n";
out << "<!DOCTYPE CARLA-PRESET>\n"; out << "<!DOCTYPE CARLA-PRESET>\n";
out << "<CARLA-PRESET VERSION='2.0'>\n"; out << "<CARLA-PRESET VERSION='2.0'>\n";
out << content;
out << getStateSave().toString();
out << "</CARLA-PRESET>\n"; out << "</CARLA-PRESET>\n";


file.close(); file.close();
@@ -897,9 +894,8 @@ bool CarlaPlugin::loadStateFromFile(const char* const filename)
return false; return false;
} }


pData->saveState.reset();
fillSaveStateFromXmlNode(pData->saveState, xmlNode);
loadSaveState(pData->saveState);
pData->stateSave.fillFromXmlNode(xmlNode);
loadStateSave(pData->stateSave);


return true; return true;
} }


+ 1
- 1
source/backend/plugin/CarlaPluginInternal.hpp View File

@@ -257,7 +257,7 @@ struct CarlaPlugin::ProtectedData {
PluginMidiProgramData midiprog; PluginMidiProgramData midiprog;
LinkedList<CustomData> custom; LinkedList<CustomData> custom;


SaveState saveState;
StateSave stateSave;


CarlaMutex masterMutex; // global master lock CarlaMutex masterMutex; // global master lock
CarlaMutex singleMutex; // small lock used only in processSingle() CarlaMutex singleMutex; // small lock used only in processSingle()


+ 2
- 4
source/tests/CarlaUtils4.cpp View File

@@ -50,13 +50,11 @@ int main()
{ {
state_juce::StateSave jsave; state_juce::StateSave jsave;
jsave.type = carla_strdup("NONE"); jsave.type = carla_strdup("NONE");
juce::String js;
state_juce::fillXmlStringFromStateSave(js, jsave);
carla_stdout(jsave.toString().toRawUTF8());


state_qt::StateSave qsave; state_qt::StateSave qsave;
qsave.type = carla_strdup("NONE"); qsave.type = carla_strdup("NONE");
QString qs;
state_qt::fillXmlStringFromStateSave(qs, qsave);
carla_stdout(qsave.toString().toUtf8().constData());


return 0; return 0;
} }


+ 256
- 244
source/utils/CarlaStateUtils.cpp View File

@@ -32,6 +32,46 @@ using juce::XmlElement;


CARLA_BACKEND_START_NAMESPACE CARLA_BACKEND_START_NAMESPACE


// -----------------------------------------------------------------------
// xmlSafeString

#ifdef HAVE_JUCE_LATER
static String xmlSafeString(const String& string, const bool toXml)
{
String newString(string);

if (toXml)
return newString.replace("&","&amp;").replace("<","&lt;").replace(">","&gt;").replace("'","&apos;").replace("\"","&quot;");
else
return newString.replace("&lt;","<").replace("&gt;",">").replace("&apos;","'").replace("&quot;","\"").replace("&amp;","&");
}
#else
static QString xmlSafeString(const QString& string, const bool toXml)
{
QString newString(string);

if (toXml)
return newString.replace("&","&amp;").replace("<","&lt;").replace(">","&gt;").replace("'","&apos;").replace("\"","&quot;");
else
return newString.replace("&lt;","<").replace("&gt;",">").replace("&apos;","'").replace("&quot;","\"").replace("&amp;","&");
}
#endif

// -----------------------------------------------------------------------
// xmlSafeStringCharDup

#ifdef HAVE_JUCE_LATER
static const char* xmlSafeStringCharDup(const String& string, const bool toXml)
{
return carla_strdup(xmlSafeString(string, toXml).toRawUTF8());
}
#else
static const char* xmlSafeStringCharDup(const QString& string, const bool toXml)
{
return carla_strdup(xmlSafeString(string, toXml).toUtf8().constData());
}
#endif

// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// StateParameter // StateParameter


@@ -109,10 +149,10 @@ StateSave::StateSave() noexcept


StateSave::~StateSave() noexcept StateSave::~StateSave() noexcept
{ {
reset();
clear();
} }


void StateSave::reset() noexcept
void StateSave::clear() noexcept
{ {
if (type != nullptr) if (type != nullptr)
{ {
@@ -174,51 +214,13 @@ void StateSave::reset() noexcept
} }


// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// xmlSafeString

#ifdef HAVE_JUCE_LATER
static String xmlSafeString(const String& string, const bool toXml)
{
String newString(string);

if (toXml)
return newString.replace("&","&amp;").replace("<","&lt;").replace(">","&gt;").replace("'","&apos;").replace("\"","&quot;");
else
return newString.replace("&lt;","<").replace("&gt;",">").replace("&apos;","'").replace("&quot;","\"").replace("&amp;","&");
}
#else
static QString xmlSafeString(const QString& string, const bool toXml)
{
QString newString(string);

if (toXml)
return newString.replace("&","&amp;").replace("<","&lt;").replace(">","&gt;").replace("'","&apos;").replace("\"","&quot;");
else
return newString.replace("&lt;","<").replace("&gt;",">").replace("&apos;","'").replace("&quot;","\"").replace("&amp;","&");
}
#endif

// -----------------------------------------------------------------------
// xmlSafeStringCharDup
// fillFromXmlElement


#ifdef HAVE_JUCE_LATER #ifdef HAVE_JUCE_LATER
static const char* xmlSafeStringCharDup(const String& string, const bool toXml)
{
return carla_strdup(xmlSafeString(string, toXml).toRawUTF8());
}
#else
static const char* xmlSafeStringCharDup(const QString& string, const bool toXml)
void StateSave::fillFromXmlElement(const XmlElement* const xmlElement)
{ {
return carla_strdup(xmlSafeString(string, toXml).toUtf8().constData());
}
#endif

// -----------------------------------------------------------------------
// fillStateSaveFromXmlNode
clear();


#ifdef HAVE_JUCE_LATER
void fillStateSaveFromXmlElement(StateSave& stateSave, const XmlElement* const xmlElement)
{
CARLA_SAFE_ASSERT_RETURN(xmlElement != nullptr,); CARLA_SAFE_ASSERT_RETURN(xmlElement != nullptr,);


for (XmlElement* elem = xmlElement->getFirstChildElement(); elem != nullptr; elem = elem->getNextElement()) for (XmlElement* elem = xmlElement->getFirstChildElement(); elem != nullptr; elem = elem->getNextElement())
@@ -236,15 +238,15 @@ void fillStateSaveFromXmlElement(StateSave& stateSave, const XmlElement* const x
const String text(xmlInfo->getAllSubText().trim()); const String text(xmlInfo->getAllSubText().trim());


if (tag.equalsIgnoreCase("type")) if (tag.equalsIgnoreCase("type"))
stateSave.type = xmlSafeStringCharDup(text, false);
type = xmlSafeStringCharDup(text, false);
else if (tag.equalsIgnoreCase("name")) else if (tag.equalsIgnoreCase("name"))
stateSave.name = xmlSafeStringCharDup(text, false);
name = xmlSafeStringCharDup(text, false);
else if (tag.equalsIgnoreCase("label") || tag.equalsIgnoreCase("uri")) else if (tag.equalsIgnoreCase("label") || tag.equalsIgnoreCase("uri"))
stateSave.label = xmlSafeStringCharDup(text, false);
label = xmlSafeStringCharDup(text, false);
else if (tag.equalsIgnoreCase("binary") || tag.equalsIgnoreCase("bundle") || tag.equalsIgnoreCase("filename")) else if (tag.equalsIgnoreCase("binary") || tag.equalsIgnoreCase("bundle") || tag.equalsIgnoreCase("filename"))
stateSave.binary = xmlSafeStringCharDup(text, false);
binary = xmlSafeStringCharDup(text, false);
else if (tag.equalsIgnoreCase("uniqueid")) else if (tag.equalsIgnoreCase("uniqueid"))
stateSave.uniqueId = text.getLargeIntValue();
uniqueId = text.getLargeIntValue();
} }
} }


@@ -263,33 +265,33 @@ void fillStateSaveFromXmlElement(StateSave& stateSave, const XmlElement* const x


if (tag.equalsIgnoreCase("active")) if (tag.equalsIgnoreCase("active"))
{ {
stateSave.active = (text.equalsIgnoreCase("yes") || text.equalsIgnoreCase("true"));
active = (text.equalsIgnoreCase("yes") || text.equalsIgnoreCase("true"));
} }
else if (tag.equalsIgnoreCase("drywet")) else if (tag.equalsIgnoreCase("drywet"))
{ {
stateSave.dryWet = carla_fixValue(0.0f, 1.0f, text.getFloatValue());
dryWet = carla_fixValue(0.0f, 1.0f, text.getFloatValue());
} }
else if (tag.equalsIgnoreCase("volume")) else if (tag.equalsIgnoreCase("volume"))
{ {
stateSave.volume = carla_fixValue(0.0f, 1.27f, text.getFloatValue());
volume = carla_fixValue(0.0f, 1.27f, text.getFloatValue());
} }
else if (tag.equalsIgnoreCase("balanceleft") || tag.equalsIgnoreCase("balance-left")) else if (tag.equalsIgnoreCase("balanceleft") || tag.equalsIgnoreCase("balance-left"))
{ {
stateSave.balanceLeft = carla_fixValue(-1.0f, 1.0f, text.getFloatValue());
balanceLeft = carla_fixValue(-1.0f, 1.0f, text.getFloatValue());
} }
else if (tag.equalsIgnoreCase("balanceright") || tag.equalsIgnoreCase("balance-right")) else if (tag.equalsIgnoreCase("balanceright") || tag.equalsIgnoreCase("balance-right"))
{ {
stateSave.balanceRight = carla_fixValue(-1.0f, 1.0f, text.getFloatValue());
balanceRight = carla_fixValue(-1.0f, 1.0f, text.getFloatValue());
} }
else if (tag.equalsIgnoreCase("panning")) else if (tag.equalsIgnoreCase("panning"))
{ {
stateSave.panning = carla_fixValue(-1.0f, 1.0f, text.getFloatValue());
panning = carla_fixValue(-1.0f, 1.0f, text.getFloatValue());
} }
else if (tag.equalsIgnoreCase("controlchannel") || tag.equalsIgnoreCase("control-channel")) else if (tag.equalsIgnoreCase("controlchannel") || tag.equalsIgnoreCase("control-channel"))
{ {
const int value(text.getIntValue()); const int value(text.getIntValue());
if (value >= 1 && value <= MAX_MIDI_CHANNELS) if (value >= 1 && value <= MAX_MIDI_CHANNELS)
stateSave.ctrlChannel = static_cast<int8_t>(value-1);
ctrlChannel = static_cast<int8_t>(value-1);
} }


// ------------------------------------------------------- // -------------------------------------------------------
@@ -299,11 +301,11 @@ void fillStateSaveFromXmlElement(StateSave& stateSave, const XmlElement* const x
{ {
const int value(text.getIntValue()); const int value(text.getIntValue());
if (value >= 1) if (value >= 1)
stateSave.currentProgramIndex = value-1;
currentProgramIndex = value-1;
} }
else if (tag.equalsIgnoreCase("currentprogramname") || tag.equalsIgnoreCase("current-program-name")) else if (tag.equalsIgnoreCase("currentprogramname") || tag.equalsIgnoreCase("current-program-name"))
{ {
stateSave.currentProgramName = xmlSafeStringCharDup(text, false);
currentProgramName = xmlSafeStringCharDup(text, false);
} }


// ------------------------------------------------------- // -------------------------------------------------------
@@ -313,13 +315,13 @@ void fillStateSaveFromXmlElement(StateSave& stateSave, const XmlElement* const x
{ {
const int value(text.getIntValue()); const int value(text.getIntValue());
if (value >= 1) if (value >= 1)
stateSave.currentMidiBank = value-1;
currentMidiBank = value-1;
} }
else if (tag.equalsIgnoreCase("currentmidiprogram") || tag.equalsIgnoreCase("current-midi-program")) else if (tag.equalsIgnoreCase("currentmidiprogram") || tag.equalsIgnoreCase("current-midi-program"))
{ {
const int value(text.getIntValue()); const int value(text.getIntValue());
if (value >= 1) if (value >= 1)
stateSave.currentMidiProgram = value-1;
currentMidiProgram = value-1;
} }


// ------------------------------------------------------- // -------------------------------------------------------
@@ -366,7 +368,7 @@ void fillStateSaveFromXmlElement(StateSave& stateSave, const XmlElement* const x
} }
} }


stateSave.parameters.append(stateParameter);
parameters.append(stateParameter);
} }


// ------------------------------------------------------- // -------------------------------------------------------
@@ -389,7 +391,7 @@ void fillStateSaveFromXmlElement(StateSave& stateSave, const XmlElement* const x
stateCustomData->value = xmlSafeStringCharDup(cText, false); stateCustomData->value = xmlSafeStringCharDup(cText, false);
} }


stateSave.customData.append(stateCustomData);
customData.append(stateCustomData);
} }


// ------------------------------------------------------- // -------------------------------------------------------
@@ -397,15 +399,17 @@ void fillStateSaveFromXmlElement(StateSave& stateSave, const XmlElement* const x


else if (tag.equalsIgnoreCase("chunk")) else if (tag.equalsIgnoreCase("chunk"))
{ {
stateSave.chunk = xmlSafeStringCharDup(text, false);
chunk = xmlSafeStringCharDup(text, false);
} }
} }
} }
} }
} }
#else #else
void fillStateSaveFromXmlNode(StateSave& stateSave, const QDomNode& xmlNode)
void StateSave::fillFromXmlNode(const QDomNode& xmlNode)
{ {
clear();

CARLA_SAFE_ASSERT_RETURN(! xmlNode.isNull(),); CARLA_SAFE_ASSERT_RETURN(! xmlNode.isNull(),);


for (QDomNode node = xmlNode.firstChild(); ! node.isNull(); node = node.nextSibling()) for (QDomNode node = xmlNode.firstChild(); ! node.isNull(); node = node.nextSibling())
@@ -424,25 +428,25 @@ void fillStateSaveFromXmlNode(StateSave& stateSave, const QDomNode& xmlNode)


if (tag.compare("type", Qt::CaseInsensitive) == 0) if (tag.compare("type", Qt::CaseInsensitive) == 0)
{ {
stateSave.type = xmlSafeStringCharDup(text, false);
type = xmlSafeStringCharDup(text, false);
} }
else if (tag.compare("name", Qt::CaseInsensitive) == 0) else if (tag.compare("name", Qt::CaseInsensitive) == 0)
{ {
stateSave.name = xmlSafeStringCharDup(text, false);
name = xmlSafeStringCharDup(text, false);
} }
else if (tag.compare("label", Qt::CaseInsensitive) == 0 || tag.compare("uri", Qt::CaseInsensitive) == 0) else if (tag.compare("label", Qt::CaseInsensitive) == 0 || tag.compare("uri", Qt::CaseInsensitive) == 0)
{ {
stateSave.label = xmlSafeStringCharDup(text, false);
label = xmlSafeStringCharDup(text, false);
} }
else if (tag.compare("binary", Qt::CaseInsensitive) == 0 || tag.compare("bundle", Qt::CaseInsensitive) == 0 || tag.compare("filename", Qt::CaseInsensitive) == 0) else if (tag.compare("binary", Qt::CaseInsensitive) == 0 || tag.compare("bundle", Qt::CaseInsensitive) == 0 || tag.compare("filename", Qt::CaseInsensitive) == 0)
{ {
stateSave.binary = xmlSafeStringCharDup(text, false);
binary = xmlSafeStringCharDup(text, false);
} }
else if (tag.compare("uniqueid", Qt::CaseInsensitive) == 0) else if (tag.compare("uniqueid", Qt::CaseInsensitive) == 0)
{ {
bool ok; bool ok;
const qlonglong uniqueId(text.toLongLong(&ok));
if (ok) stateSave.uniqueId = static_cast<int64_t>(uniqueId);
const qlonglong uniqueIdTry(text.toLongLong(&ok));
if (ok) uniqueId = static_cast<int64_t>(uniqueIdTry);
} }
} }
} }
@@ -462,44 +466,44 @@ void fillStateSaveFromXmlNode(StateSave& stateSave, const QDomNode& xmlNode)


if (tag.compare("active", Qt::CaseInsensitive) == 0) if (tag.compare("active", Qt::CaseInsensitive) == 0)
{ {
stateSave.active = (text.compare("yes", Qt::CaseInsensitive) == 0 || text.compare("true", Qt::CaseInsensitive) == 0);
active = (text.compare("yes", Qt::CaseInsensitive) == 0 || text.compare("true", Qt::CaseInsensitive) == 0);
} }
else if (tag.compare("drywet", Qt::CaseInsensitive) == 0) else if (tag.compare("drywet", Qt::CaseInsensitive) == 0)
{ {
bool ok; bool ok;
const float value(text.toFloat(&ok)); const float value(text.toFloat(&ok));
if (ok) stateSave.dryWet = carla_fixValue(0.0f, 1.0f, value);
if (ok) dryWet = carla_fixValue(0.0f, 1.0f, value);
} }
else if (tag.compare("volume", Qt::CaseInsensitive) == 0) else if (tag.compare("volume", Qt::CaseInsensitive) == 0)
{ {
bool ok; bool ok;
const float value(text.toFloat(&ok)); const float value(text.toFloat(&ok));
if (ok) stateSave.volume = carla_fixValue(0.0f, 1.27f, value);
if (ok) volume = carla_fixValue(0.0f, 1.27f, value);
} }
else if (tag.compare("balanceleft", Qt::CaseInsensitive) == 0 || tag.compare("balance-left", Qt::CaseInsensitive) == 0) else if (tag.compare("balanceleft", Qt::CaseInsensitive) == 0 || tag.compare("balance-left", Qt::CaseInsensitive) == 0)
{ {
bool ok; bool ok;
const float value(text.toFloat(&ok)); const float value(text.toFloat(&ok));
if (ok) stateSave.balanceLeft = carla_fixValue(-1.0f, 1.0f, value);
if (ok) balanceLeft = carla_fixValue(-1.0f, 1.0f, value);
} }
else if (tag.compare("balanceright", Qt::CaseInsensitive) == 0 || tag.compare("balance-right", Qt::CaseInsensitive) == 0) else if (tag.compare("balanceright", Qt::CaseInsensitive) == 0 || tag.compare("balance-right", Qt::CaseInsensitive) == 0)
{ {
bool ok; bool ok;
const float value(text.toFloat(&ok)); const float value(text.toFloat(&ok));
if (ok) stateSave.balanceRight = carla_fixValue(-1.0f, 1.0f, value);
if (ok) balanceRight = carla_fixValue(-1.0f, 1.0f, value);
} }
else if (tag.compare("panning", Qt::CaseInsensitive) == 0) else if (tag.compare("panning", Qt::CaseInsensitive) == 0)
{ {
bool ok; bool ok;
const float value(text.toFloat(&ok)); const float value(text.toFloat(&ok));
if (ok) stateSave.panning = carla_fixValue(-1.0f, 1.0f, value);
if (ok) panning = carla_fixValue(-1.0f, 1.0f, value);
} }
else if (tag.compare("controlchannel", Qt::CaseInsensitive) == 0 || tag.compare("control-channel", Qt::CaseInsensitive) == 0) else if (tag.compare("controlchannel", Qt::CaseInsensitive) == 0 || tag.compare("control-channel", Qt::CaseInsensitive) == 0)
{ {
bool ok; bool ok;
const short value(text.toShort(&ok)); const short value(text.toShort(&ok));
if (ok && value >= 1 && value <= MAX_MIDI_CHANNELS) if (ok && value >= 1 && value <= MAX_MIDI_CHANNELS)
stateSave.ctrlChannel = static_cast<int8_t>(value-1);
ctrlChannel = static_cast<int8_t>(value-1);
} }


// ------------------------------------------------------- // -------------------------------------------------------
@@ -510,11 +514,11 @@ void fillStateSaveFromXmlNode(StateSave& stateSave, const QDomNode& xmlNode)
bool ok; bool ok;
const int value(text.toInt(&ok)); const int value(text.toInt(&ok));
if (ok && value >= 1) if (ok && value >= 1)
stateSave.currentProgramIndex = value-1;
currentProgramIndex = value-1;
} }
else if (tag.compare("currentprogramname", Qt::CaseInsensitive) == 0 || tag.compare("current-program-name", Qt::CaseInsensitive) == 0) else if (tag.compare("currentprogramname", Qt::CaseInsensitive) == 0 || tag.compare("current-program-name", Qt::CaseInsensitive) == 0)
{ {
stateSave.currentProgramName = xmlSafeStringCharDup(text, false);
currentProgramName = xmlSafeStringCharDup(text, false);
} }


// ------------------------------------------------------- // -------------------------------------------------------
@@ -525,14 +529,14 @@ void fillStateSaveFromXmlNode(StateSave& stateSave, const QDomNode& xmlNode)
bool ok; bool ok;
const int value(text.toInt(&ok)); const int value(text.toInt(&ok));
if (ok && value >= 1) if (ok && value >= 1)
stateSave.currentMidiBank = value-1;
currentMidiBank = value-1;
} }
else if (tag.compare("currentmidiprogram", Qt::CaseInsensitive) == 0 || tag.compare("current-midi-program", Qt::CaseInsensitive) == 0) else if (tag.compare("currentmidiprogram", Qt::CaseInsensitive) == 0 || tag.compare("current-midi-program", Qt::CaseInsensitive) == 0)
{ {
bool ok; bool ok;
const int value(text.toInt(&ok)); const int value(text.toInt(&ok));
if (ok && value >= 1) if (ok && value >= 1)
stateSave.currentMidiProgram = value-1;
currentMidiProgram = value-1;
} }


// ------------------------------------------------------- // -------------------------------------------------------
@@ -583,7 +587,7 @@ void fillStateSaveFromXmlNode(StateSave& stateSave, const QDomNode& xmlNode)
} }
} }


stateSave.parameters.append(stateParameter);
parameters.append(stateParameter);
} }


// ------------------------------------------------------- // -------------------------------------------------------
@@ -606,7 +610,7 @@ void fillStateSaveFromXmlNode(StateSave& stateSave, const QDomNode& xmlNode)
stateCustomData->value = xmlSafeStringCharDup(cText, false); stateCustomData->value = xmlSafeStringCharDup(cText, false);
} }


stateSave.customData.append(stateCustomData);
customData.append(stateCustomData);
} }


// ------------------------------------------------------- // -------------------------------------------------------
@@ -614,7 +618,7 @@ void fillStateSaveFromXmlNode(StateSave& stateSave, const QDomNode& xmlNode)


else if (tag.compare("chunk", Qt::CaseInsensitive) == 0) else if (tag.compare("chunk", Qt::CaseInsensitive) == 0)
{ {
stateSave.chunk = xmlSafeStringCharDup(text, false);
chunk = xmlSafeStringCharDup(text, false);
} }
} }
} }
@@ -626,353 +630,361 @@ void fillStateSaveFromXmlNode(StateSave& stateSave, const QDomNode& xmlNode)
// fillXmlStringFromStateSave // fillXmlStringFromStateSave


#ifdef HAVE_JUCE_LATER #ifdef HAVE_JUCE_LATER
void fillXmlStringFromStateSave(String& content, const StateSave& stateSave)
String StateSave::toString() const
{ {
String content;

{ {
String info(" <Info>\n");
String infoXml(" <Info>\n");


info << " <Type>" << String(stateSave.type != nullptr ? stateSave.type : "") << "</Type>\n";
info << " <Name>" << xmlSafeString(stateSave.name, true) << "</Name>\n";
infoXml << " <Type>" << String(type != nullptr ? type : "") << "</Type>\n";
infoXml << " <Name>" << xmlSafeString(name, true) << "</Name>\n";


switch (getPluginTypeFromString(stateSave.type))
switch (getPluginTypeFromString(type))
{ {
case PLUGIN_NONE: case PLUGIN_NONE:
break; break;
case PLUGIN_INTERNAL: case PLUGIN_INTERNAL:
info << " <Label>" << xmlSafeString(stateSave.label, true) << "</Label>\n";
infoXml << " <Label>" << xmlSafeString(label, true) << "</Label>\n";
break; break;
case PLUGIN_LADSPA: case PLUGIN_LADSPA:
info << " <Binary>" << xmlSafeString(stateSave.binary, true) << "</Binary>\n";
info << " <Label>" << xmlSafeString(stateSave.label, true) << "</Label>\n";
info << " <UniqueID>" << stateSave.uniqueId << "</UniqueID>\n";
infoXml << " <Binary>" << xmlSafeString(binary, true) << "</Binary>\n";
infoXml << " <Label>" << xmlSafeString(label, true) << "</Label>\n";
infoXml << " <UniqueID>" << uniqueId << "</UniqueID>\n";
break; break;
case PLUGIN_DSSI: case PLUGIN_DSSI:
info << " <Binary>" << xmlSafeString(stateSave.binary, true) << "</Binary>\n";
info << " <Label>" << xmlSafeString(stateSave.label, true) << "</Label>\n";
infoXml << " <Binary>" << xmlSafeString(binary, true) << "</Binary>\n";
infoXml << " <Label>" << xmlSafeString(label, true) << "</Label>\n";
break; break;
case PLUGIN_LV2: case PLUGIN_LV2:
info << " <Bundle>" << xmlSafeString(stateSave.binary, true) << "</Bundle>\n";
info << " <URI>" << xmlSafeString(stateSave.label, true) << "</URI>\n";
infoXml << " <Bundle>" << xmlSafeString(binary, true) << "</Bundle>\n";
infoXml << " <URI>" << xmlSafeString(label, true) << "</URI>\n";
break; break;
case PLUGIN_VST: case PLUGIN_VST:
info << " <Binary>" << xmlSafeString(stateSave.binary, true) << "</Binary>\n";
info << " <UniqueID>" << stateSave.uniqueId << "</UniqueID>\n";
infoXml << " <Binary>" << xmlSafeString(binary, true) << "</Binary>\n";
infoXml << " <UniqueID>" << uniqueId << "</UniqueID>\n";
break; break;
case PLUGIN_VST3: case PLUGIN_VST3:
// TODO? // TODO?
info << " <Binary>" << xmlSafeString(stateSave.binary, true) << "</Binary>\n";
info << " <UniqueID>" << stateSave.uniqueId << "</UniqueID>\n";
infoXml << " <Binary>" << xmlSafeString(binary, true) << "</Binary>\n";
infoXml << " <UniqueID>" << uniqueId << "</UniqueID>\n";
break; break;
case PLUGIN_AU: case PLUGIN_AU:
// TODO? // TODO?
info << " <Binary>" << xmlSafeString(stateSave.binary, true) << "</Binary>\n";
info << " <UniqueID>" << stateSave.uniqueId << "</UniqueID>\n";
infoXml << " <Binary>" << xmlSafeString(binary, true) << "</Binary>\n";
infoXml << " <UniqueID>" << uniqueId << "</UniqueID>\n";
break; break;
case PLUGIN_JACK: case PLUGIN_JACK:
info << " <Binary>" << xmlSafeString(stateSave.binary, true) << "</Binary>\n";
infoXml << " <Binary>" << xmlSafeString(binary, true) << "</Binary>\n";
break; break;
case PLUGIN_REWIRE: case PLUGIN_REWIRE:
info << " <Label>" << xmlSafeString(stateSave.label, true) << "</Label>\n";
infoXml << " <Label>" << xmlSafeString(label, true) << "</Label>\n";
break; break;
case PLUGIN_FILE_CSD: case PLUGIN_FILE_CSD:
case PLUGIN_FILE_GIG: case PLUGIN_FILE_GIG:
case PLUGIN_FILE_SF2: case PLUGIN_FILE_SF2:
case PLUGIN_FILE_SFZ: case PLUGIN_FILE_SFZ:
info << " <Filename>" << xmlSafeString(stateSave.binary, true) << "</Filename>\n";
info << " <Label>" << xmlSafeString(stateSave.label, true) << "</Label>\n";
infoXml << " <Filename>" << xmlSafeString(binary, true) << "</Filename>\n";
infoXml << " <Label>" << xmlSafeString(label, true) << "</Label>\n";
break; break;
} }


info << " </Info>\n\n";
infoXml << " </Info>\n\n";


content << info;
content << infoXml;
} }


content << " <Data>\n"; content << " <Data>\n";


{ {
String data;
data << " <Active>" << (stateSave.active ? "Yes" : "No") << "</Active>\n";
if (stateSave.dryWet != 1.0f)
data << " <DryWet>" << stateSave.dryWet << "</DryWet>\n";
if (stateSave.volume != 1.0f)
data << " <Volume>" << stateSave.volume << "</Volume>\n";
if (stateSave.balanceLeft != -1.0f)
data << " <Balance-Left>" << stateSave.balanceLeft << "</Balance-Left>\n";
if (stateSave.balanceRight != 1.0f)
data << " <Balance-Right>" << stateSave.balanceRight << "</Balance-Right>\n";
if (stateSave.panning != 0.0f)
data << " <Panning>" << stateSave.panning << "</Panning>\n";
if (stateSave.ctrlChannel < 0)
data << " <ControlChannel>N</ControlChannel>\n";
String dataXml;
dataXml << " <Active>" << (active ? "Yes" : "No") << "</Active>\n";
if (dryWet != 1.0f)
dataXml << " <DryWet>" << dryWet << "</DryWet>\n";
if (volume != 1.0f)
dataXml << " <Volume>" << volume << "</Volume>\n";
if (balanceLeft != -1.0f)
dataXml << " <Balance-Left>" << balanceLeft << "</Balance-Left>\n";
if (balanceRight != 1.0f)
dataXml << " <Balance-Right>" << balanceRight << "</Balance-Right>\n";
if (panning != 0.0f)
dataXml << " <Panning>" << panning << "</Panning>\n";
if (ctrlChannel < 0)
dataXml << " <ControlChannel>N</ControlChannel>\n";
else else
data << " <ControlChannel>" << int(stateSave.ctrlChannel+1) << "</ControlChannel>\n";
dataXml << " <ControlChannel>" << int(ctrlChannel+1) << "</ControlChannel>\n";


content << data;
content << dataXml;
} }


for (StateParameterItenerator it = stateSave.parameters.begin(); it.valid(); it.next())
for (StateParameterItenerator it = parameters.begin(); it.valid(); it.next())
{ {
StateParameter* const stateParameter(it.getValue()); StateParameter* const stateParameter(it.getValue());


String parameter("\n"" <Parameter>\n");
String parameterXml("\n"" <Parameter>\n");


parameter << " <Index>" << String(stateParameter->index) << "</Index>\n";
parameter << " <Name>" << xmlSafeString(stateParameter->name, true) << "</Name>\n";
parameterXml << " <Index>" << String(stateParameter->index) << "</Index>\n";
parameterXml << " <Name>" << xmlSafeString(stateParameter->name, true) << "</Name>\n";


if (stateParameter->symbol != nullptr && stateParameter->symbol[0] != '\0') if (stateParameter->symbol != nullptr && stateParameter->symbol[0] != '\0')
parameter << " <Symbol>" << xmlSafeString(stateParameter->symbol, true) << "</Symbol>\n";
parameterXml << " <Symbol>" << xmlSafeString(stateParameter->symbol, true) << "</Symbol>\n";


if (stateParameter->isInput) if (stateParameter->isInput)
parameter << " <Value>" << stateParameter->value << "</Value>\n";
parameterXml << " <Value>" << stateParameter->value << "</Value>\n";


if (stateParameter->midiCC > 0) if (stateParameter->midiCC > 0)
{ {
parameter << " <MidiCC>" << stateParameter->midiCC << "</MidiCC>\n";
parameter << " <MidiChannel>" << stateParameter->midiChannel+1 << "</MidiChannel>\n";
parameterXml << " <MidiCC>" << stateParameter->midiCC << "</MidiCC>\n";
parameterXml << " <MidiChannel>" << stateParameter->midiChannel+1 << "</MidiChannel>\n";
} }


parameter << " </Parameter>\n";
parameterXml << " </Parameter>\n";


content << parameter;
content << parameterXml;
} }


if (stateSave.currentProgramIndex >= 0 && stateSave.currentProgramName != nullptr && stateSave.currentProgramName[0] != '\0')
if (currentProgramIndex >= 0 && currentProgramName != nullptr && currentProgramName[0] != '\0')
{ {
// ignore 'default' program // ignore 'default' program
if (stateSave.currentProgramIndex > 0 || ! String(stateSave.currentProgramName).equalsIgnoreCase("default"))
if (currentProgramIndex > 0 || ! String(currentProgramName).equalsIgnoreCase("default"))
{ {
String program("\n");
program << " <CurrentProgramIndex>" << stateSave.currentProgramIndex+1 << "</CurrentProgramIndex>\n";
program << " <CurrentProgramName>" << xmlSafeString(stateSave.currentProgramName, true) << "</CurrentProgramName>\n";
String programXml("\n");
programXml << " <CurrentProgramIndex>" << currentProgramIndex+1 << "</CurrentProgramIndex>\n";
programXml << " <CurrentProgramName>" << xmlSafeString(currentProgramName, true) << "</CurrentProgramName>\n";


content << program;
content << programXml;
} }
} }


if (stateSave.currentMidiBank >= 0 && stateSave.currentMidiProgram >= 0)
if (currentMidiBank >= 0 && currentMidiProgram >= 0)
{ {
String midiProgram("\n");
midiProgram << " <CurrentMidiBank>" << stateSave.currentMidiBank+1 << "</CurrentMidiBank>\n";
midiProgram << " <CurrentMidiProgram>" << stateSave.currentMidiProgram+1 << "</CurrentMidiProgram>\n";
String midiProgramXml("\n");
midiProgramXml << " <CurrentMidiBank>" << currentMidiBank+1 << "</CurrentMidiBank>\n";
midiProgramXml << " <CurrentMidiProgram>" << currentMidiProgram+1 << "</CurrentMidiProgram>\n";


content << midiProgram;
content << midiProgramXml;
} }


for (StateCustomDataItenerator it = stateSave.customData.begin(); it.valid(); it.next())
for (StateCustomDataItenerator it = customData.begin(); it.valid(); it.next())
{ {
StateCustomData* const stateCustomData(it.getValue()); StateCustomData* const stateCustomData(it.getValue());
CARLA_SAFE_ASSERT_CONTINUE(stateCustomData->type != nullptr && stateCustomData->type[0] != '\0'); CARLA_SAFE_ASSERT_CONTINUE(stateCustomData->type != nullptr && stateCustomData->type[0] != '\0');
CARLA_SAFE_ASSERT_CONTINUE(stateCustomData->key != nullptr && stateCustomData->key[0] != '\0'); CARLA_SAFE_ASSERT_CONTINUE(stateCustomData->key != nullptr && stateCustomData->key[0] != '\0');
CARLA_SAFE_ASSERT_CONTINUE(stateCustomData->value != nullptr); CARLA_SAFE_ASSERT_CONTINUE(stateCustomData->value != nullptr);


String customData("\n"" <CustomData>\n");
customData << " <Type>" << xmlSafeString(stateCustomData->type, true) << "</Type>\n";
customData << " <Key>" << xmlSafeString(stateCustomData->key, true) << "</Key>\n";
String customDataXml("\n"" <CustomData>\n");
customDataXml << " <Type>" << xmlSafeString(stateCustomData->type, true) << "</Type>\n";
customDataXml << " <Key>" << xmlSafeString(stateCustomData->key, true) << "</Key>\n";


if (std::strcmp(stateCustomData->type, CUSTOM_DATA_TYPE_CHUNK) == 0 || std::strlen(stateCustomData->value) >= 128) if (std::strcmp(stateCustomData->type, CUSTOM_DATA_TYPE_CHUNK) == 0 || std::strlen(stateCustomData->value) >= 128)
{ {
customData << " <Value>\n";
customData << xmlSafeString(stateCustomData->value, true);
customData << " </Value>\n";
customDataXml << " <Value>\n";
customDataXml << xmlSafeString(stateCustomData->value, true);
customDataXml << " </Value>\n";
} }
else else
{ {
customData << " <Value>";
customData << xmlSafeString(stateCustomData->value, true);
customData << "</Value>\n";
customDataXml << " <Value>";
customDataXml << xmlSafeString(stateCustomData->value, true);
customDataXml << "</Value>\n";
} }


customData << " </CustomData>\n";
customDataXml << " </CustomData>\n";


content << customData;
content << customDataXml;
} }


if (stateSave.chunk != nullptr && stateSave.chunk[0] != '\0')
if (chunk != nullptr && chunk[0] != '\0')
{ {
String chunk("\n"" <Chunk>\n");
chunk << stateSave.chunk << "\n </Chunk>\n";
String chunkXml("\n"" <Chunk>\n");
chunkXml << chunk << "\n </Chunk>\n";


content << chunk;
content << chunkXml;
} }


content << " </Data>\n"; content << " </Data>\n";

return content;
} }
#else #else
void fillXmlStringFromStateSave(QString& content, const StateSave& stateSave)
QString StateSave::toString() const
{ {
QString content;

{ {
QString info(" <Info>\n");
QString infoXml(" <Info>\n");


info += QString(" <Type>%1</Type>\n").arg((stateSave.type != nullptr) ? stateSave.type : "");
info += QString(" <Name>%1</Name>\n").arg(xmlSafeString(stateSave.name, true));
infoXml += QString(" <Type>%1</Type>\n").arg((type != nullptr) ? type : "");
infoXml += QString(" <Name>%1</Name>\n").arg(xmlSafeString(name, true));


switch (getPluginTypeFromString(stateSave.type))
switch (getPluginTypeFromString(type))
{ {
case PLUGIN_NONE: case PLUGIN_NONE:
break; break;
case PLUGIN_INTERNAL: case PLUGIN_INTERNAL:
info += QString(" <Label>%1</Label>\n").arg(xmlSafeString(stateSave.label, true));
infoXml += QString(" <Label>%1</Label>\n").arg(xmlSafeString(label, true));
break; break;
case PLUGIN_LADSPA: case PLUGIN_LADSPA:
info += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(stateSave.binary, true));
info += QString(" <Label>%1</Label>\n").arg(xmlSafeString(stateSave.label, true));
info += QString(" <UniqueID>%1</UniqueID>\n").arg(stateSave.uniqueId);
infoXml += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(binary, true));
infoXml += QString(" <Label>%1</Label>\n").arg(xmlSafeString(label, true));
infoXml += QString(" <UniqueID>%1</UniqueID>\n").arg(uniqueId);
break; break;
case PLUGIN_DSSI: case PLUGIN_DSSI:
info += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(stateSave.binary, true));
info += QString(" <Label>%1</Label>\n").arg(xmlSafeString(stateSave.label, true));
infoXml += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(binary, true));
infoXml += QString(" <Label>%1</Label>\n").arg(xmlSafeString(label, true));
break; break;
case PLUGIN_LV2: case PLUGIN_LV2:
info += QString(" <Bundle>%1</Bundle>\n").arg(xmlSafeString(stateSave.binary, true));
info += QString(" <URI>%1</URI>\n").arg(xmlSafeString(stateSave.label, true));
infoXml += QString(" <Bundle>%1</Bundle>\n").arg(xmlSafeString(binary, true));
infoXml += QString(" <URI>%1</URI>\n").arg(xmlSafeString(label, true));
break; break;
case PLUGIN_VST: case PLUGIN_VST:
info += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(stateSave.binary, true));
info += QString(" <UniqueID>%1</UniqueID>\n").arg(stateSave.uniqueId);
infoXml += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(binary, true));
infoXml += QString(" <UniqueID>%1</UniqueID>\n").arg(uniqueId);
break; break;
case PLUGIN_VST3: case PLUGIN_VST3:
// TODO? // TODO?
info += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(stateSave.binary, true));
info += QString(" <UniqueID>%1</UniqueID>\n").arg(stateSave.uniqueId);
infoXml += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(binary, true));
infoXml += QString(" <UniqueID>%1</UniqueID>\n").arg(uniqueId);
break; break;
case PLUGIN_AU: case PLUGIN_AU:
// TODO? // TODO?
info += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(stateSave.binary, true));
info += QString(" <UniqueID>%1</UniqueID>\n").arg(stateSave.uniqueId);
infoXml += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(binary, true));
infoXml += QString(" <UniqueID>%1</UniqueID>\n").arg(uniqueId);
break; break;
case PLUGIN_JACK: case PLUGIN_JACK:
info += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(stateSave.binary, true));
infoXml += QString(" <Binary>%1</Binary>\n").arg(xmlSafeString(binary, true));
break; break;
case PLUGIN_REWIRE: case PLUGIN_REWIRE:
info += QString(" <Label>%1</Label>\n").arg(xmlSafeString(stateSave.label, true));
infoXml += QString(" <Label>%1</Label>\n").arg(xmlSafeString(label, true));
break; break;
case PLUGIN_FILE_CSD: case PLUGIN_FILE_CSD:
case PLUGIN_FILE_GIG: case PLUGIN_FILE_GIG:
case PLUGIN_FILE_SF2: case PLUGIN_FILE_SF2:
case PLUGIN_FILE_SFZ: case PLUGIN_FILE_SFZ:
info += QString(" <Filename>%1</Filename>\n").arg(xmlSafeString(stateSave.binary, true));
info += QString(" <Label>%1</Label>\n").arg(xmlSafeString(stateSave.label, true));
infoXml += QString(" <Filename>%1</Filename>\n").arg(xmlSafeString(binary, true));
infoXml += QString(" <Label>%1</Label>\n").arg(xmlSafeString(label, true));
break; break;
} }


info += " </Info>\n\n";
infoXml += " </Info>\n\n";


content += info;
content += infoXml;
} }


content += " <Data>\n"; content += " <Data>\n";


{ {
QString data;
data += QString(" <Active>%1</Active>\n").arg(stateSave.active ? "Yes" : "No");
if (stateSave.dryWet != 1.0f)
data += QString(" <DryWet>%1</DryWet>\n").arg(stateSave.dryWet, 0, 'g', 7);
if (stateSave.volume != 1.0f)
data += QString(" <Volume>%1</Volume>\n").arg(stateSave.volume, 0, 'g', 7);
if (stateSave.balanceLeft != -1.0f)
data += QString(" <Balance-Left>%1</Balance-Left>\n").arg(stateSave.balanceLeft, 0, 'g', 7);
if (stateSave.balanceRight != 1.0f)
data += QString(" <Balance-Right>%1</Balance-Right>\n").arg(stateSave.balanceRight, 0, 'g', 7);
if (stateSave.panning != 0.0f)
data += QString(" <Panning>%1</Panning>\n").arg(stateSave.panning, 0, 'g', 7);
if (stateSave.ctrlChannel < 0)
data += QString(" <ControlChannel>N</ControlChannel>\n");
QString dataXml;
dataXml += QString(" <Active>%1</Active>\n").arg(active ? "Yes" : "No");
if (dryWet != 1.0f)
dataXml += QString(" <DryWet>%1</DryWet>\n").arg(dryWet, 0, 'g', 7);
if (volume != 1.0f)
dataXml += QString(" <Volume>%1</Volume>\n").arg(volume, 0, 'g', 7);
if (balanceLeft != -1.0f)
dataXml += QString(" <Balance-Left>%1</Balance-Left>\n").arg(balanceLeft, 0, 'g', 7);
if (balanceRight != 1.0f)
dataXml += QString(" <Balance-Right>%1</Balance-Right>\n").arg(balanceRight, 0, 'g', 7);
if (panning != 0.0f)
dataXml += QString(" <Panning>%1</Panning>\n").arg(panning, 0, 'g', 7);
if (ctrlChannel < 0)
dataXml += QString(" <ControlChannel>N</ControlChannel>\n");
else else
data += QString(" <ControlChannel>%1</ControlChannel>\n").arg(stateSave.ctrlChannel+1);
dataXml += QString(" <ControlChannel>%1</ControlChannel>\n").arg(ctrlChannel+1);


content += data;
content += dataXml;
} }


for (StateParameterItenerator it = stateSave.parameters.begin(); it.valid(); it.next())
for (StateParameterItenerator it = parameters.begin(); it.valid(); it.next())
{ {
StateParameter* const stateParameter(it.getValue()); StateParameter* const stateParameter(it.getValue());


QString parameter("\n"" <Parameter>\n");
QString parameterXml("\n"" <Parameter>\n");


parameter += QString(" <Index>%1</Index>\n").arg(stateParameter->index);
parameter += QString(" <Name>%1</Name>\n").arg(xmlSafeString(stateParameter->name, true));
parameterXml += QString(" <Index>%1</Index>\n").arg(stateParameter->index);
parameterXml += QString(" <Name>%1</Name>\n").arg(xmlSafeString(stateParameter->name, true));


if (stateParameter->symbol != nullptr && stateParameter->symbol[0] != '\0') if (stateParameter->symbol != nullptr && stateParameter->symbol[0] != '\0')
parameter += QString(" <Symbol>%1</Symbol>\n").arg(xmlSafeString(stateParameter->symbol, true));
parameterXml += QString(" <Symbol>%1</Symbol>\n").arg(xmlSafeString(stateParameter->symbol, true));


if (stateParameter->isInput) if (stateParameter->isInput)
parameter += QString(" <Value>%1</Value>\n").arg(stateParameter->value, 0, 'g', 15);
parameterXml += QString(" <Value>%1</Value>\n").arg(stateParameter->value, 0, 'g', 15);


if (stateParameter->midiCC > 0) if (stateParameter->midiCC > 0)
{ {
parameter += QString(" <MidiCC>%1</MidiCC>\n").arg(stateParameter->midiCC);
parameter += QString(" <MidiChannel>%1</MidiChannel>\n").arg(stateParameter->midiChannel+1);
parameterXml += QString(" <MidiCC>%1</MidiCC>\n").arg(stateParameter->midiCC);
parameterXml += QString(" <MidiChannel>%1</MidiChannel>\n").arg(stateParameter->midiChannel+1);
} }


parameter += " </Parameter>\n";
parameterXml += " </Parameter>\n";


content += parameter;
content += parameterXml;
} }


if (stateSave.currentProgramIndex >= 0 && stateSave.currentProgramName != nullptr && stateSave.currentProgramName[0] != '\0')
if (currentProgramIndex >= 0 && currentProgramName != nullptr && currentProgramName[0] != '\0')
{ {
// ignore 'default' program // ignore 'default' program
if (stateSave.currentProgramIndex > 0 || QString(stateSave.currentProgramName).compare("default", Qt::CaseInsensitive) != 0)
if (currentProgramIndex > 0 || QString(currentProgramName).compare("default", Qt::CaseInsensitive) != 0)
{ {
QString program("\n");
program += QString(" <CurrentProgramIndex>%1</CurrentProgramIndex>\n").arg(stateSave.currentProgramIndex+1);
program += QString(" <CurrentProgramName>%1</CurrentProgramName>\n").arg(xmlSafeString(stateSave.currentProgramName, true));
QString programXml("\n");
programXml += QString(" <CurrentProgramIndex>%1</CurrentProgramIndex>\n").arg(currentProgramIndex+1);
programXml += QString(" <CurrentProgramName>%1</CurrentProgramName>\n").arg(xmlSafeString(currentProgramName, true));


content += program;
content += programXml;
} }
} }


if (stateSave.currentMidiBank >= 0 && stateSave.currentMidiProgram >= 0)
if (currentMidiBank >= 0 && currentMidiProgram >= 0)
{ {
QString midiProgram("\n");
midiProgram += QString(" <CurrentMidiBank>%1</CurrentMidiBank>\n").arg(stateSave.currentMidiBank+1);
midiProgram += QString(" <CurrentMidiProgram>%1</CurrentMidiProgram>\n").arg(stateSave.currentMidiProgram+1);
QString midiProgramXml("\n");
midiProgramXml += QString(" <CurrentMidiBank>%1</CurrentMidiBank>\n").arg(currentMidiBank+1);
midiProgramXml += QString(" <CurrentMidiProgram>%1</CurrentMidiProgram>\n").arg(currentMidiProgram+1);


content += midiProgram;
content += midiProgramXml;
} }


for (StateCustomDataItenerator it = stateSave.customData.begin(); it.valid(); it.next())
for (StateCustomDataItenerator it = customData.begin(); it.valid(); it.next())
{ {
StateCustomData* const stateCustomData(it.getValue()); StateCustomData* const stateCustomData(it.getValue());
CARLA_SAFE_ASSERT_CONTINUE(stateCustomData->type != nullptr && stateCustomData->type[0] != '\0'); CARLA_SAFE_ASSERT_CONTINUE(stateCustomData->type != nullptr && stateCustomData->type[0] != '\0');
CARLA_SAFE_ASSERT_CONTINUE(stateCustomData->key != nullptr && stateCustomData->key[0] != '\0'); CARLA_SAFE_ASSERT_CONTINUE(stateCustomData->key != nullptr && stateCustomData->key[0] != '\0');
CARLA_SAFE_ASSERT_CONTINUE(stateCustomData->value != nullptr); CARLA_SAFE_ASSERT_CONTINUE(stateCustomData->value != nullptr);


QString customData("\n"" <CustomData>\n");
customData += QString(" <Type>%1</Type>\n").arg(xmlSafeString(stateCustomData->type, true));
customData += QString(" <Key>%1</Key>\n").arg(xmlSafeString(stateCustomData->key, true));
QString customDataXml("\n"" <CustomData>\n");
customDataXml += QString(" <Type>%1</Type>\n").arg(xmlSafeString(stateCustomData->type, true));
customDataXml += QString(" <Key>%1</Key>\n").arg(xmlSafeString(stateCustomData->key, true));


if (std::strcmp(stateCustomData->type, CUSTOM_DATA_TYPE_CHUNK) == 0 || std::strlen(stateCustomData->value) >= 128) if (std::strcmp(stateCustomData->type, CUSTOM_DATA_TYPE_CHUNK) == 0 || std::strlen(stateCustomData->value) >= 128)
{ {
customData += " <Value>\n";
customData += QString("%1\n").arg(xmlSafeString(stateCustomData->value, true));
customData += " </Value>\n";
customDataXml += " <Value>\n";
customDataXml += QString("%1\n").arg(xmlSafeString(stateCustomData->value, true));
customDataXml += " </Value>\n";
} }
else else
customData += QString(" <Value>%1</Value>\n").arg(xmlSafeString(stateCustomData->value, true));
customDataXml += QString(" <Value>%1</Value>\n").arg(xmlSafeString(stateCustomData->value, true));


customData += " </CustomData>\n";
customDataXml += " </CustomData>\n";


content += customData;
content += customDataXml;
} }


if (stateSave.chunk != nullptr && stateSave.chunk[0] != '\0')
if (chunk != nullptr && chunk[0] != '\0')
{ {
QString chunk("\n"" <Chunk>\n");
chunk += QString("%1\n").arg(stateSave.chunk);
chunk += " </Chunk>\n";
QString chunkXml("\n"" <Chunk>\n");
chunkXml += QString("%1\n").arg(chunk);
chunkXml += " </Chunk>\n";


content += chunk;
content += chunkXml;
} }


content += " </Data>\n"; content += " </Data>\n";

return content;
} }
#endif #endif




+ 11
- 9
source/utils/CarlaStateUtils.hpp View File

@@ -99,21 +99,23 @@ struct StateSave {


StateSave() noexcept; StateSave() noexcept;
~StateSave() noexcept; ~StateSave() noexcept;
void reset() noexcept;
void clear() noexcept;


CARLA_DECLARE_NON_COPY_STRUCT(StateSave)
};

// -----------------------------------------------------------------------
#ifdef HAVE_JUCE_LATER
void fillFromXmlElement(const juce::XmlElement* const xmlElement);
#else
void fillFromXmlNode(const QDomNode& xmlNode);
#endif


#ifdef HAVE_JUCE_LATER #ifdef HAVE_JUCE_LATER
void fillStateSaveFromXmlElement(StateSave& stateSave, const juce::XmlElement* const xmlElement);
void fillXmlStringFromStateSave(juce::String& content, const StateSave& stateSave);
juce::String toString() const;
#else #else
void fillStateSaveFromXmlNode(StateSave& stateSave, const QDomNode& xmlNode);
void fillXmlStringFromStateSave(QString& content, const StateSave& stateSave);
QString toString() const;
#endif #endif


CARLA_DECLARE_NON_COPY_STRUCT(StateSave)
};

// ----------------------------------------------------------------------- // -----------------------------------------------------------------------


CARLA_BACKEND_END_NAMESPACE CARLA_BACKEND_END_NAMESPACE


Loading…
Cancel
Save