From d50ba0e0e0a8c7ca69e8513a0f5e897d94a71197 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 5 Jul 2014 18:48:10 +0100 Subject: [PATCH] StateUtils can now use juce --- source/tests/CarlaUtils4.cpp | 23 +- source/tests/Makefile | 6 +- source/utils/CarlaStateUtils.cpp | 409 ++++++++++++++++++++++++++++++- source/utils/CarlaStateUtils.hpp | 12 + 4 files changed, 438 insertions(+), 12 deletions(-) diff --git a/source/tests/CarlaUtils4.cpp b/source/tests/CarlaUtils4.cpp index a8545a79c..3633fac31 100644 --- a/source/tests/CarlaUtils4.cpp +++ b/source/tests/CarlaUtils4.cpp @@ -19,14 +19,33 @@ # error Build this file with debug ON please #endif -#include "CarlaStateUtils.hpp" +#include "CarlaBackendUtils.hpp" + +using namespace CarlaBackend; + +// ----------------------------------------------------------------------- + +#define HAVE_JUCE +#undef CARLA_BACKEND_START_NAMESPACE +#define CARLA_BACKEND_START_NAMESPACE namespace state_juce { +#include "CarlaStateUtils.cpp" + +// ----------------------------------------------------------------------- + +#undef CARLA_STATE_UTILS_HPP_INCLUDED + +// ----------------------------------------------------------------------- + +#undef HAVE_JUCE +#undef CARLA_BACKEND_START_NAMESPACE +#define CARLA_BACKEND_START_NAMESPACE namespace state_qt { +#include "CarlaStateUtils.cpp" // ----------------------------------------------------------------------- // main int main() { - return 0; } diff --git a/source/tests/Makefile b/source/tests/Makefile index 726c813fa..e0f8bba5d 100644 --- a/source/tests/Makefile +++ b/source/tests/Makefile @@ -87,8 +87,10 @@ CarlaUtils3: CarlaUtils3.cpp ../utils/*.hpp $(CXX) $< $(PEDANTIC_CXX_FLAGS) -o $@ -ldl -lrt valgrind --leak-check=full ./$@ -CarlaUtils4: CarlaUtils4.cpp ../utils/*.hpp - $(CXX) $< $(PEDANTIC_CXX_FLAGS) -o $@ -ldl -lrt +CarlaUtils4: CarlaUtils4.cpp ../utils/CarlaStateUtils.cpp ../utils/*.hpp + $(CXX) $< $(PEDANTIC_CXX_FLAGS) -o $@ \ + ../modules/juce_core.a -ldl -lpthread -lrt \ + $(shell pkg-config --cflags --libs QtCore QtXml) -isystem /usr/include/qt4 valgrind --leak-check=full ./$@ Exceptions: Exceptions.cpp diff --git a/source/utils/CarlaStateUtils.cpp b/source/utils/CarlaStateUtils.cpp index 569ba9362..b86e6b9db 100644 --- a/source/utils/CarlaStateUtils.cpp +++ b/source/utils/CarlaStateUtils.cpp @@ -21,8 +21,14 @@ #include "CarlaMathUtils.hpp" #include "CarlaMIDI.h" -#include -#include +#ifdef HAVE_JUCE +# include "juce_core.h" +using juce::String; +using juce::XmlElement; +#else +# include +# include +#endif CARLA_BACKEND_START_NAMESPACE @@ -170,6 +176,17 @@ void SaveState::reset() noexcept // ----------------------------------------------------------------------- // xmlSafeString +#ifdef HAVE_JUCE +static String xmlSafeString(const String& string, const bool toXml) +{ + String newString(string); + + if (toXml) + return newString.replace("&","&").replace("<","<").replace(">",">").replace("'","'").replace("\"","""); + else + return newString.replace("<","<").replace(">",">").replace("'","'").replace(""","\"").replace("&","&"); +} +#else static QString xmlSafeString(const QString& string, const bool toXml) { QString newString(string); @@ -177,21 +194,219 @@ static QString xmlSafeString(const QString& string, const bool toXml) if (toXml) return newString.replace("&","&").replace("<","<").replace(">",">").replace("'","'").replace("\"","""); else - return newString.replace("&","&").replace("<","<").replace(">",">").replace("'","'").replace(""","\""); + return newString.replace("<","<").replace(">",">").replace("'","'").replace(""","\"").replace("&","&"); } +#endif + +// ----------------------------------------------------------------------- +// xmlSafeStringCharDup +#ifdef HAVE_JUCE +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 // ----------------------------------------------------------------------- // fillSaveStateFromXmlNode +#ifdef HAVE_JUCE +void fillSaveStateFromXmlNode(SaveState& saveState, const XmlElement* const xmlElement) +{ + CARLA_SAFE_ASSERT_RETURN(xmlElement != nullptr,); + + for (XmlElement* elem = xmlElement->getFirstChildElement(); elem != nullptr; elem = elem->getNextElement()) + { + String tagName(elem->getTagName()); + + // --------------------------------------------------------------- + // Info + + if (tagName.equalsIgnoreCase("info")) + { + for (XmlElement* xmlInfo = elem->getFirstChildElement(); xmlInfo != nullptr; xmlInfo = xmlInfo->getNextElement()) + { + const String& tag(xmlInfo->getTagName()); + const String text(xmlInfo->getAllSubText().trim()); + + if (tag.equalsIgnoreCase("type")) + saveState.type = xmlSafeStringCharDup(text, false); + else if (tag.equalsIgnoreCase("name")) + saveState.name = xmlSafeStringCharDup(text, false); + else if (tag.equalsIgnoreCase("label") || tag.equalsIgnoreCase("uri")) + saveState.label = xmlSafeStringCharDup(text, false); + else if (tag.equalsIgnoreCase("binary") || tag.equalsIgnoreCase("bundle") || tag.equalsIgnoreCase("filename")) + saveState.binary = xmlSafeStringCharDup(text, false); + else if (tag.equalsIgnoreCase("uniqueid")) + saveState.uniqueId = text.getLargeIntValue(); + } + } + + // --------------------------------------------------------------- + // Data + + else if (tagName.equalsIgnoreCase("data")) + { + for (XmlElement* xmlData = elem->getFirstChildElement(); xmlData != nullptr; xmlData = xmlData->getNextElement()) + { + const String& tag(xmlData->getTagName()); + const String text(xmlData->getAllSubText().trim()); + + // ------------------------------------------------------- + // Internal Data + + if (tag.equalsIgnoreCase("active")) + { + saveState.active = (text.equalsIgnoreCase("yes") || text.equalsIgnoreCase("true")); + } + else if (tag.equalsIgnoreCase("drywet")) + { + saveState.dryWet = carla_fixValue(0.0f, 1.0f, text.getFloatValue()); + } + else if (tag.equalsIgnoreCase("volume")) + { + saveState.volume = carla_fixValue(0.0f, 1.27f, text.getFloatValue()); + } + else if (tag.equalsIgnoreCase("balanceleft") || tag.equalsIgnoreCase("balance-left")) + { + saveState.balanceLeft = carla_fixValue(-1.0f, 1.0f, text.getFloatValue()); + } + else if (tag.equalsIgnoreCase("balanceright") || tag.equalsIgnoreCase("balance-right")) + { + saveState.balanceRight = carla_fixValue(-1.0f, 1.0f, text.getFloatValue()); + } + else if (tag.equalsIgnoreCase("panning")) + { + saveState.panning = carla_fixValue(-1.0f, 1.0f, text.getFloatValue()); + } + else if (tag.equalsIgnoreCase("controlchannel") || tag.equalsIgnoreCase("control-channel")) + { + const int value(text.getIntValue()); + if (value >= 1 && value <= MAX_MIDI_CHANNELS) + saveState.ctrlChannel = static_cast(value-1); + } + + // ------------------------------------------------------- + // Program (current) + + else if (tag.equalsIgnoreCase("currentprogramindex") || tag.equalsIgnoreCase("current-program-index")) + { + const int value(text.getIntValue()); + if (value >= 1) + saveState.currentProgramIndex = value-1; + } + else if (tag.equalsIgnoreCase("currentprogramname") || tag.equalsIgnoreCase("current-program-name")) + { + saveState.currentProgramName = xmlSafeStringCharDup(text, false); + } + + // ------------------------------------------------------- + // Midi Program (current) + + else if (tag.equalsIgnoreCase("currentmidibank") || tag.equalsIgnoreCase("current-midi-bank")) + { + const int value(text.getIntValue()); + if (value >= 1) + saveState.currentMidiBank = value-1; + } + else if (tag.equalsIgnoreCase("currentmidiprogram") || tag.equalsIgnoreCase("current-midi-program")) + { + const int value(text.getIntValue()); + if (value >= 1) + saveState.currentMidiProgram = value-1; + } + + // ------------------------------------------------------- + // Parameters + + else if (tag.equalsIgnoreCase("parameter")) + { + StateParameter* const stateParameter(new StateParameter()); + + for (XmlElement* xmlSubData = xmlData->getFirstChildElement(); xmlSubData != nullptr; xmlSubData = xmlSubData->getNextElement()) + { + const String& pTag(xmlSubData->getTagName()); + const String pText(xmlSubData->getAllSubText().trim()); + + if (pTag.equalsIgnoreCase("index")) + { + const int index(pText.getIntValue()); + if (index >= 0) + stateParameter->index = index; + } + else if (pTag.equalsIgnoreCase("name")) + { + stateParameter->name = xmlSafeStringCharDup(pText, false); + } + else if (pTag.equalsIgnoreCase("symbol")) + { + stateParameter->symbol = xmlSafeStringCharDup(pText, false); + } + else if (pTag.equalsIgnoreCase("value")) + { + stateParameter->value = pText.getFloatValue(); + } + else if (pTag.equalsIgnoreCase("midichannel") || pTag.equalsIgnoreCase("midi-channel")) + { + const int channel(pText.getIntValue()); + if (channel >= 1 && channel <= MAX_MIDI_CHANNELS) + stateParameter->midiChannel = static_cast(channel-1); + } + else if (pTag.equalsIgnoreCase("midicc") || pTag.equalsIgnoreCase("midi-cc")) + { + const int cc(pText.getIntValue()); + if (cc >= 1 && cc < 0x5F) + stateParameter->midiCC = static_cast(cc); + } + } + + saveState.parameters.append(stateParameter); + } + + // ------------------------------------------------------- + // Custom Data + + else if (tag.equalsIgnoreCase("customdata") || tag.equalsIgnoreCase("custom-data")) + { + StateCustomData* const stateCustomData(new StateCustomData()); + + for (XmlElement* xmlSubData = xmlData->getFirstChildElement(); xmlSubData != nullptr; xmlSubData = xmlSubData->getNextElement()) + { + const String& cTag(xmlSubData->getTagName()); + const String cText(xmlSubData->getAllSubText().trim()); + + if (cTag.equalsIgnoreCase("type")) + stateCustomData->type = xmlSafeStringCharDup(cText, false); + else if (cTag.equalsIgnoreCase("key")) + stateCustomData->key = xmlSafeStringCharDup(cText, false); + else if (cTag.equalsIgnoreCase("value")) + stateCustomData->value = xmlSafeStringCharDup(cText, false); + } + + saveState.customData.append(stateCustomData); + } + + // ------------------------------------------------------- + // Chunk + + else if (tag.equalsIgnoreCase("chunk")) + { + saveState.chunk = xmlSafeStringCharDup(text, false); + } + } + } + } +} +#else void fillSaveStateFromXmlNode(SaveState& saveState, const QDomNode& xmlNode) { - if (xmlNode.isNull()) - return; + CARLA_SAFE_ASSERT_RETURN(! xmlNode.isNull(),); for (QDomNode node = xmlNode.firstChild(); ! node.isNull(); node = node.nextSibling()) { @@ -283,7 +498,7 @@ void fillSaveStateFromXmlNode(SaveState& saveState, const QDomNode& xmlNode) { bool ok; const short value(text.toShort(&ok)); - if (ok && value >= 1 && value < MAX_MIDI_CHANNELS) + if (ok && value >= 1 && value <= MAX_MIDI_CHANNELS) saveState.ctrlChannel = static_cast(value-1); } @@ -356,7 +571,7 @@ void fillSaveStateFromXmlNode(SaveState& saveState, const QDomNode& xmlNode) { bool ok; const ushort channel(pText.toUShort(&ok)); - if (ok && channel >= 1 && channel < MAX_MIDI_CHANNELS) + if (ok && channel >= 1 && channel <= MAX_MIDI_CHANNELS) stateParameter->midiChannel = static_cast(channel-1); } else if (pTag.compare("midicc", Qt::CaseInsensitive) == 0 || pTag.compare("midi-cc", Qt::CaseInsensitive) == 0) @@ -405,10 +620,185 @@ void fillSaveStateFromXmlNode(SaveState& saveState, const QDomNode& xmlNode) } } } +#endif // ----------------------------------------------------------------------- // fillXmlStringFromSaveState +#ifdef HAVE_JUCE +void fillXmlStringFromSaveState(String& content, const SaveState& saveState) +{ + { + String info(" \n"); + + info << " " << saveState.type << "\n"; + info << " " << xmlSafeString(saveState.name, true) << "\n"; + + switch (getPluginTypeFromString(saveState.type)) + { + case PLUGIN_NONE: + break; + case PLUGIN_INTERNAL: + info << " \n"; + break; + case PLUGIN_LADSPA: + info << " " << xmlSafeString(saveState.binary, true) << "\n"; + info << " \n"; + info << " " << saveState.uniqueId << "\n"; + break; + case PLUGIN_DSSI: + info << " " << xmlSafeString(saveState.binary, true) << "\n"; + info << " \n"; + break; + case PLUGIN_LV2: + info << " " << xmlSafeString(saveState.binary, true) << "\n"; + info << " " << xmlSafeString(saveState.label, true) << "\n"; + break; + case PLUGIN_VST: + info << " " << xmlSafeString(saveState.binary, true) << "\n"; + info << " " << saveState.uniqueId << "\n"; + break; + case PLUGIN_VST3: + // TODO? + info << " " << xmlSafeString(saveState.binary, true) << "\n"; + info << " " << saveState.uniqueId << "\n"; + break; + case PLUGIN_AU: + // TODO? + info << " " << xmlSafeString(saveState.binary, true) << "\n"; + info << " " << saveState.uniqueId << "\n"; + break; + case PLUGIN_JACK: + info << " " << xmlSafeString(saveState.binary, true) << "\n"; + break; + case PLUGIN_REWIRE: + info << " \n"; + break; + case PLUGIN_FILE_CSD: + case PLUGIN_FILE_GIG: + case PLUGIN_FILE_SF2: + case PLUGIN_FILE_SFZ: + info << " " << xmlSafeString(saveState.binary, true) << "\n"; + info << " \n"; + break; + } + + info << " \n\n"; + + content << info; + } + + content << " \n"; + + { + String data; + + data << " " << (saveState.active ? "Yes" : "No") << "\n"; + + if (saveState.dryWet != 1.0f) + data << " " << saveState.dryWet << "\n"; + if (saveState.volume != 1.0f) + data << " " << saveState.volume << "\n"; + if (saveState.balanceLeft != -1.0f) + data << " " << saveState.balanceLeft << "\n"; + if (saveState.balanceRight != 1.0f) + data << " " << saveState.balanceRight << "\n"; + if (saveState.panning != 0.0f) + data << " " << saveState.panning << "\n"; + + if (saveState.ctrlChannel < 0) + data << " N\n"; + else + data << " " << int(saveState.ctrlChannel+1) << "\n"; + + content << data; + } + + for (StateParameterItenerator it = saveState.parameters.begin(); it.valid(); it.next()) + { + StateParameter* const stateParameter(it.getValue()); + + String parameter("\n"" \n"); + + parameter << " " << (long)stateParameter->index << "\n"; // FIXME + parameter << " " << xmlSafeString(stateParameter->name, true) << "\n"; + + if (stateParameter->symbol != nullptr && stateParameter->symbol[0] != '\0') + parameter << " " << xmlSafeString(stateParameter->symbol, true) << "\n"; + + if (stateParameter->isInput) + parameter << " " << stateParameter->value << "\n"; + + if (stateParameter->midiCC > 0) + { + parameter << " " << stateParameter->midiCC << "\n"; + parameter << " " << stateParameter->midiChannel+1 << "\n"; + } + + parameter << " \n"; + + content << parameter; + } + + if (saveState.currentProgramIndex >= 0 && saveState.currentProgramName != nullptr && saveState.currentProgramName[0] != '\0') + { + // ignore 'default' program + if (saveState.currentProgramIndex > 0 || ! String(saveState.currentProgramName).equalsIgnoreCase("default")) + { + String program("\n"); + program << " " << saveState.currentProgramIndex+1 << "\n"; + program << " " << xmlSafeString(saveState.currentProgramName, true) << "\n"; + + content << program; + } + } + + if (saveState.currentMidiBank >= 0 && saveState.currentMidiProgram >= 0) + { + String midiProgram("\n"); + midiProgram << " " << saveState.currentMidiBank+1 << "\n"; + midiProgram << " " << saveState.currentMidiProgram+1 << "\n"; + + content << midiProgram; + } + + for (StateCustomDataItenerator it = saveState.customData.begin(); it.valid(); it.next()) + { + StateCustomData* const stateCustomData(it.getValue()); + + String customData("\n"" \n"); + customData << " " << xmlSafeString(stateCustomData->type, true) << "\n"; + customData << " " << xmlSafeString(stateCustomData->key, true) << "\n"; + + if (std::strcmp(stateCustomData->type, CUSTOM_DATA_TYPE_CHUNK) == 0 || std::strlen(stateCustomData->value) >= 128) + { + customData << " \n"; + customData << xmlSafeString(stateCustomData->value, true); + customData << " \n"; + } + else + { + customData << " "; + customData << xmlSafeString(stateCustomData->value, true); + customData << "\n"; + } + + customData << " \n"; + + content << customData; + } + + if (saveState.chunk != nullptr && saveState.chunk[0] != '\0') + { + String chunk("\n"" \n"); + chunk << saveState.chunk << "\n \n"; + + content << chunk; + } + + content << " \n"; +} +#else void fillXmlStringFromSaveState(QString& content, const SaveState& saveState) { { @@ -471,8 +861,10 @@ void fillXmlStringFromSaveState(QString& content, const SaveState& saveState) content += info; } + content += " \n"; + { - QString data(" \n"); + QString data; data += QString(" %1\n").arg(saveState.active ? "Yes" : "No"); @@ -576,6 +968,7 @@ void fillXmlStringFromSaveState(QString& content, const SaveState& saveState) content += " \n"; } +#endif // ----------------------------------------------------------------------- diff --git a/source/utils/CarlaStateUtils.hpp b/source/utils/CarlaStateUtils.hpp index 6c9e56b7f..1b702a5cf 100644 --- a/source/utils/CarlaStateUtils.hpp +++ b/source/utils/CarlaStateUtils.hpp @@ -21,8 +21,15 @@ #include "CarlaBackend.h" #include "LinkedList.hpp" +#ifdef HAVE_JUCE +namespace juce { +class String; +class XmlElement; +} +#else class QDomNode; class QString; +#endif // ----------------------------------------------------------------------- @@ -99,8 +106,13 @@ struct SaveState { // ----------------------------------------------------------------------- +#ifdef HAVE_JUCE +void fillSaveStateFromXmlNode(SaveState& saveState, const juce::XmlElement* const xmlElement); +void fillXmlStringFromSaveState(juce::String& content, const SaveState& saveState); +#else void fillSaveStateFromXmlNode(SaveState& saveState, const QDomNode& xmlNode); void fillXmlStringFromSaveState(QString& content, const SaveState& saveState); +#endif // -----------------------------------------------------------------------