| @@ -0,0 +1,430 @@ | |||
| /* | |||
| * Carla Backend utils | |||
| * Copyright (C) 2011-2013 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * This program is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU General Public License as | |||
| * published by the Free Software Foundation; either version 2 of | |||
| * the License, or any later version. | |||
| * | |||
| * This program is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| * GNU General Public License for more details. | |||
| * | |||
| * For a full copy of the GNU General Public License see the GPL.txt file | |||
| */ | |||
| #ifndef __CARLA_STATE_UTILS_HPP__ | |||
| #define __CARLA_STATE_UTILS_HPP__ | |||
| #include "carla_backend.hpp" | |||
| #include "carla_utils.hpp" | |||
| #include <QtXml/QDomNode> | |||
| CARLA_BACKEND_START_NAMESPACE | |||
| // ------------------------------------------------- | |||
| // Carla GUI stuff | |||
| struct StateParameter { | |||
| uint32_t index; | |||
| const char* name; | |||
| const char* symbol; | |||
| double value; | |||
| uint8_t midiChannel; | |||
| int16_t midiCC; | |||
| StateParameter() | |||
| : index(0), | |||
| name(nullptr), | |||
| symbol(nullptr), | |||
| value(0.0), | |||
| midiChannel(1), | |||
| midiCC(-1) {} | |||
| ~StateParameter() | |||
| { | |||
| std::free((void*)name); | |||
| std::free((void*)symbol); | |||
| } | |||
| }; | |||
| struct StateCustomData { | |||
| const char* type; | |||
| const char* key; | |||
| const char* value; | |||
| StateCustomData() | |||
| : type(nullptr), | |||
| key(nullptr), | |||
| value(nullptr) {} | |||
| ~StateCustomData() | |||
| { | |||
| std::free((void*)type); | |||
| std::free((void*)key); | |||
| std::free((void*)value); | |||
| } | |||
| }; | |||
| typedef std::vector<StateParameter> StateParameterVector; | |||
| typedef std::vector<StateCustomData> StateCustomDataVector; | |||
| struct SaveState { | |||
| const char* type; | |||
| const char* name; | |||
| const char* label; | |||
| const char* binary; | |||
| long uniqueID; | |||
| bool active; | |||
| double dryWet; | |||
| double volume; | |||
| double balanceLeft; | |||
| double balanceRight; | |||
| double panning; | |||
| int32_t currentProgramIndex; | |||
| const char* currentProgramName; | |||
| int32_t currentMidiBank; | |||
| int32_t currentMidiProgram; | |||
| const char* chunk; | |||
| StateParameterVector parameters; | |||
| StateCustomDataVector customData; | |||
| SaveState() | |||
| : type(nullptr), | |||
| name(nullptr), | |||
| label(nullptr), | |||
| binary(nullptr), | |||
| uniqueID(0), | |||
| active(false), | |||
| dryWet(1.0), | |||
| volume(1.0), | |||
| balanceLeft(-1.0), | |||
| balanceRight(1.0), | |||
| panning(0.0), | |||
| currentProgramIndex(-1), | |||
| currentProgramName(nullptr), | |||
| currentMidiBank(-1), | |||
| currentMidiProgram(-1), | |||
| chunk(nullptr) {} | |||
| ~SaveState() | |||
| { | |||
| reset(); | |||
| } | |||
| void reset() | |||
| { | |||
| if (type != nullptr) | |||
| { | |||
| std::free((void*)type); | |||
| type = nullptr; | |||
| } | |||
| if (name != nullptr) | |||
| { | |||
| std::free((void*)name); | |||
| name = nullptr; | |||
| } | |||
| if (label != nullptr) | |||
| { | |||
| std::free((void*)label); | |||
| label = nullptr; | |||
| } | |||
| if (binary != nullptr) | |||
| { | |||
| std::free((void*)binary); | |||
| binary = nullptr; | |||
| } | |||
| if (currentProgramName != nullptr) | |||
| { | |||
| std::free((void*)currentProgramName); | |||
| currentProgramName = nullptr; | |||
| } | |||
| if (chunk != nullptr) | |||
| { | |||
| std::free((void*)chunk); | |||
| chunk = nullptr; | |||
| } | |||
| uniqueID = 0; | |||
| active = false; | |||
| dryWet = 1.0; | |||
| volume = 1.0; | |||
| balanceLeft = -1.0; | |||
| balanceRight = 1.0; | |||
| panning = 0.0; | |||
| currentProgramIndex = -1; | |||
| currentMidiBank = -1; | |||
| currentMidiProgram = -1; | |||
| parameters.clear(); | |||
| customData.clear(); | |||
| } | |||
| }; | |||
| // ------------------------------------------------- | |||
| // Carla XML helpers (xml to state) | |||
| static inline | |||
| QString xmlSafeString(const QString& string, const bool toXml) | |||
| { | |||
| QString newString(string); | |||
| if (toXml) | |||
| return newString.replace("&", "&").replace("<","<").replace(">",">").replace("'","'").replace("\"","""); | |||
| else | |||
| return newString.replace("&", "&").replace("<","<").replace(">",">").replace("'","'").replace(""","\""); | |||
| } | |||
| static inline | |||
| const char* xmlSafeStringChar(const QString& string, const bool toXml) | |||
| { | |||
| return strdup(xmlSafeString(string, toXml).toUtf8().constData()); | |||
| } | |||
| static inline | |||
| const SaveState& getSaveStateDictFromXML(const QDomNode& xmlNode) | |||
| { | |||
| static SaveState saveState; | |||
| saveState.reset(); | |||
| QDomNode node(xmlNode.firstChild()); | |||
| while (! node.isNull()) | |||
| { | |||
| // ------------------------------------------------------ | |||
| // Info | |||
| if (node.toElement().tagName() == "Info") | |||
| { | |||
| QDomNode xmlInfo(node.toElement().firstChild()); | |||
| while (! xmlInfo.isNull()) | |||
| { | |||
| const QString tag = xmlInfo.toElement().tagName(); | |||
| const QString text = xmlInfo.toElement().text(); //.strip(); | |||
| if (tag == "Type") | |||
| saveState.type = xmlSafeStringChar(text, false); | |||
| else if (tag == "Name") | |||
| saveState.name = xmlSafeStringChar(text, false); | |||
| else if (tag == "Label" || tag == "URI") | |||
| saveState.label = xmlSafeStringChar(text, false); | |||
| else if (tag == "Binary") | |||
| saveState.binary = xmlSafeStringChar(text, false); | |||
| else if (tag == "UniqueID") | |||
| { | |||
| bool ok; | |||
| long uniqueID = text.toLong(&ok); | |||
| if (ok) saveState.uniqueID = uniqueID; | |||
| } | |||
| xmlInfo = xmlInfo.nextSibling(); | |||
| } | |||
| } | |||
| // ------------------------------------------------------ | |||
| // Data | |||
| else if (node.toElement().tagName() == "Data") | |||
| { | |||
| QDomNode xmlData(node.toElement().firstChild()); | |||
| while (! xmlData.isNull()) | |||
| { | |||
| const QString tag = xmlData.toElement().tagName(); | |||
| const QString text = xmlData.toElement().text(); //.strip(); | |||
| // ---------------------------------------------- | |||
| // Internal Data | |||
| if (tag == "Active") | |||
| { | |||
| saveState.active = bool(text == "Yes"); | |||
| } | |||
| else if (tag == "DryWet") | |||
| { | |||
| bool ok; | |||
| double value = text.toDouble(&ok); | |||
| if (ok) saveState.dryWet = value; | |||
| } | |||
| else if (tag == "Volume") | |||
| { | |||
| bool ok; | |||
| double value = text.toDouble(&ok); | |||
| if (ok) saveState.volume = value; | |||
| } | |||
| else if (tag == "Balance-Left") | |||
| { | |||
| bool ok; | |||
| double value = text.toDouble(&ok); | |||
| if (ok) saveState.balanceLeft = value; | |||
| } | |||
| else if (tag == "Balance-Right") | |||
| { | |||
| bool ok; | |||
| double value = text.toDouble(&ok); | |||
| if (ok) saveState.balanceRight = value; | |||
| } | |||
| // ---------------------------------------------- | |||
| // Program (current) | |||
| else if (tag == "CurrentProgramIndex") | |||
| { | |||
| bool ok; | |||
| int value = text.toInt(&ok); | |||
| if (ok) saveState.currentProgramIndex = value; | |||
| } | |||
| else if (tag == "CurrentProgramName") | |||
| { | |||
| saveState.currentProgramName = xmlSafeStringChar(text, false); | |||
| } | |||
| // ---------------------------------------------- | |||
| // Midi Program (current) | |||
| else if (tag == "CurrentMidiBank") | |||
| { | |||
| bool ok; | |||
| int value = text.toInt(&ok); | |||
| if (ok) saveState.currentMidiBank = value; | |||
| } | |||
| else if (tag == "CurrentMidiProgram") | |||
| { | |||
| bool ok; | |||
| int value = text.toInt(&ok); | |||
| if (ok) saveState.currentMidiProgram = value; | |||
| } | |||
| // ---------------------------------------------- | |||
| // Parameters | |||
| else if (tag == "Parameter") | |||
| { | |||
| StateParameter stateParameter; | |||
| QDomNode xmlSubData(xmlData.toElement().firstChild()); | |||
| while (! xmlSubData.isNull()) | |||
| { | |||
| const QString pTag = xmlSubData.toElement().tagName(); | |||
| const QString pText = xmlSubData.toElement().text(); //.strip(); | |||
| if (pTag == "index") | |||
| { | |||
| bool ok; | |||
| uint index = pText.toUInt(&ok); | |||
| if (ok) stateParameter.index = index; | |||
| } | |||
| else if (pTag == "name") | |||
| { | |||
| stateParameter.name = xmlSafeStringChar(pText, false); | |||
| } | |||
| else if (pTag == "symbol") | |||
| { | |||
| stateParameter.symbol = xmlSafeStringChar(pText, false); | |||
| } | |||
| else if (pTag == "value") | |||
| { | |||
| bool ok; | |||
| double value = pText.toDouble(&ok); | |||
| if (ok) stateParameter.value = value; | |||
| } | |||
| else if (pTag == "midiChannel") | |||
| { | |||
| bool ok; | |||
| uint channel = pText.toUInt(&ok); | |||
| if (ok && channel < 16) | |||
| stateParameter.midiChannel = static_cast<uint8_t>(channel); | |||
| } | |||
| else if (pTag == "midiCC") | |||
| { | |||
| bool ok; | |||
| int cc = pText.toInt(&ok); | |||
| if (ok && cc < INT16_MAX) | |||
| stateParameter.midiCC = static_cast<int16_t>(cc); | |||
| } | |||
| xmlSubData = xmlSubData.nextSibling(); | |||
| } | |||
| saveState.parameters.push_back(stateParameter); | |||
| } | |||
| // ---------------------------------------------- | |||
| // Custom Data | |||
| else if (tag == "CustomData") | |||
| { | |||
| StateCustomData stateCustomData; | |||
| QDomNode xmlSubData(xmlData.toElement().firstChild()); | |||
| while (! xmlSubData.isNull()) | |||
| { | |||
| const QString cTag = xmlSubData.toElement().tagName(); | |||
| const QString cText = xmlSubData.toElement().text(); //.strip(); | |||
| if (cTag == "type") | |||
| stateCustomData.type = xmlSafeStringChar(cText, false); | |||
| else if (cTag == "key") | |||
| stateCustomData.key = xmlSafeStringChar(cText, false); | |||
| else if (cTag == "value") | |||
| stateCustomData.value = xmlSafeStringChar(cText, false); | |||
| xmlSubData = xmlSubData.nextSibling(); | |||
| } | |||
| saveState.customData.push_back(stateCustomData); | |||
| } | |||
| // ---------------------------------------------- | |||
| // Chunk | |||
| else if (tag == "Chunk") | |||
| { | |||
| saveState.chunk = xmlSafeStringChar(text, false); | |||
| } | |||
| // ---------------------------------------------- | |||
| xmlData = xmlData.nextSibling(); | |||
| } | |||
| } | |||
| // ------------------------------------------------------ | |||
| node = node.nextSibling(); | |||
| } | |||
| return saveState; | |||
| } | |||
| static inline | |||
| QString getXMLFromSaveState(const SaveState& saveState) | |||
| { | |||
| return ""; | |||
| // TODO | |||
| Q_UNUSED(saveState); | |||
| } | |||
| // ------------------------------------------------- | |||
| // Carla XML helpers (state to xml) | |||
| // ------------------------------------------------- | |||
| CARLA_BACKEND_END_NAMESPACE | |||
| #endif // __CARLA_STATE_UTILS_HPP__ | |||