|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588 |
- /*
- * Carla State utils
- * Copyright (C) 2012-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 doc/GPL.txt file.
- */
-
- #ifndef CARLA_STATE_UTILS_HPP_INCLUDED
- #define CARLA_STATE_UTILS_HPP_INCLUDED
-
- #include "CarlaBackendUtils.hpp"
- #include "CarlaMIDI.h"
- #include "List.hpp"
-
- #ifdef USE_JUCE
- # include "juce_core.h"
- #endif
-
- CARLA_BACKEND_START_NAMESPACE
-
- // -----------------------------------------------------------------------
-
- struct StateParameter {
- uint32_t index;
- const char* name;
- const char* symbol;
- float value;
- uint8_t midiChannel;
- int16_t midiCC;
-
- StateParameter() noexcept
- : index(0),
- name(nullptr),
- symbol(nullptr),
- value(0.0f),
- midiChannel(0),
- midiCC(-1) {}
-
- ~StateParameter()
- {
- if (name != nullptr)
- {
- delete[] name;
- name = nullptr;
- }
- if (symbol != nullptr)
- {
- delete[] symbol;
- symbol = nullptr;
- }
- }
-
- CARLA_DECLARE_NON_COPY_STRUCT(StateParameter)
- };
-
- struct StateCustomData {
- const char* type;
- const char* key;
- const char* value;
-
- StateCustomData() noexcept
- : type(nullptr),
- key(nullptr),
- value(nullptr) {}
-
- ~StateCustomData()
- {
- if (type != nullptr)
- {
- delete[] type;
- type = nullptr;
- }
- if (key != nullptr)
- {
- delete[] key;
- key = nullptr;
- }
- if (value != nullptr)
- {
- delete[] value;
- value = nullptr;
- }
- }
-
- CARLA_DECLARE_NON_COPY_STRUCT(StateCustomData)
- };
-
- typedef List<StateParameter*> StateParameterList;
- typedef List<StateCustomData*> StateCustomDataList;
-
- typedef List<StateParameter*>::Itenerator StateParameterItenerator;
- typedef List<StateCustomData*>::Itenerator StateCustomDataItenerator;
-
- struct SaveState {
- const char* type;
- const char* name;
- const char* label;
- const char* binary;
- long uniqueID;
-
- bool active;
- float dryWet;
- float volume;
- float balanceLeft;
- float balanceRight;
- float panning;
- int8_t ctrlChannel;
-
- int32_t currentProgramIndex;
- const char* currentProgramName;
- int32_t currentMidiBank;
- int32_t currentMidiProgram;
- const char* chunk;
-
- StateParameterList parameters;
- StateCustomDataList customData;
-
- SaveState() noexcept
- : type(nullptr),
- name(nullptr),
- label(nullptr),
- binary(nullptr),
- uniqueID(0),
- active(false),
- dryWet(1.0f),
- volume(1.0f),
- balanceLeft(-1.0f),
- balanceRight(1.0f),
- panning(0.0f),
- ctrlChannel(-1),
- currentProgramIndex(-1),
- currentProgramName(nullptr),
- currentMidiBank(-1),
- currentMidiProgram(-1),
- chunk(nullptr) {}
-
- ~SaveState()
- {
- reset();
- }
-
- void reset()
- {
- if (type != nullptr)
- {
- delete[] type;
- type = nullptr;
- }
- if (name != nullptr)
- {
- delete[] name;
- name = nullptr;
- }
- if (label != nullptr)
- {
- delete[] label;
- label = nullptr;
- }
- if (binary != nullptr)
- {
- delete[] binary;
- binary = nullptr;
- }
- if (currentProgramName != nullptr)
- {
- delete[] currentProgramName;
- currentProgramName = nullptr;
- }
- if (chunk != nullptr)
- {
- delete[] chunk;
- chunk = nullptr;
- }
-
- uniqueID = 0;
- active = false;
- dryWet = 1.0f;
- volume = 1.0f;
- balanceLeft = -1.0f;
- balanceRight = 1.0f;
- panning = 0.0f;
- ctrlChannel = -1;
- currentProgramIndex = -1;
- currentMidiBank = -1;
- currentMidiProgram = -1;
-
- for (StateParameterItenerator it = parameters.begin(); it.valid(); it.next())
- {
- StateParameter* const stateParameter(*it);
- delete stateParameter;
- }
-
- for (StateCustomDataItenerator it = customData.begin(); it.valid(); it.next())
- {
- StateCustomData* const stateCustomData(*it);
- delete stateCustomData;
- }
-
- parameters.clear();
- customData.clear();
- }
-
- CARLA_DECLARE_NON_COPY_STRUCT(SaveState)
- };
-
- #ifdef USE_JUCE
- // -----------------------------------------------------------------------
-
- static inline
- juce::String xmlSafeString(const juce::String& string, const bool toXml)
- {
- juce::String newString(string);
-
- if (toXml)
- return newString.replace("&","&").replace("<","<").replace(">",">").replace("'","'").replace("\"",""");
- else
- return newString.replace("&","&").replace("<","<").replace(">",">").replace("'","'").replace(""","\"");
- }
-
- static inline
- const char* xmlSafeStringCharDup(const juce::String& string, const bool toXml)
- {
- return carla_strdup(xmlSafeString(string, toXml).toRawUTF8());
- }
-
- // -----------------------------------------------------------------------
-
- static inline
- void fillSaveStateFromXmlElement(SaveState& saveState, const juce::XmlElement* const xmlElement)
- {
- using namespace juce;
-
- for (XmlElement* elem = xmlElement->getFirstChildElement(); elem != nullptr; elem = elem->getNextElement())
- {
- // ---------------------------------------------------------------
- // Info
-
- if (elem->getTagName().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("filename"))
- saveState.binary = xmlSafeStringCharDup(text, false);
- else if (tag.equalsIgnoreCase("uniqueid"))
- saveState.uniqueID = text.getLargeIntValue();
- }
- }
-
- // ---------------------------------------------------------------
- // Data
-
- else if (elem->getTagName().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"));
- }
- 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<int8_t>(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 = static_cast<uint32_t>(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<uint8_t>(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<int16_t>(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);
- }
- }
- }
- }
- }
-
- // -----------------------------------------------------------------------
-
- static inline
- void fillXmlStringFromSaveState(juce::String& content, const SaveState& saveState)
- {
- using namespace juce;
-
- {
- String info(" <Info>\n");
-
- info << " <Type>" << saveState.type << "</Type>\n";
- info << " <Name>" << xmlSafeString(saveState.name, true) << "</Name>\n";
-
- switch (getPluginTypeFromString(saveState.type))
- {
- case PLUGIN_NONE:
- break;
- case PLUGIN_INTERNAL:
- info << " <Label>" << xmlSafeString(saveState.label, true) << "</Label>\n";
- break;
- case PLUGIN_LADSPA:
- info << " <Binary>" << xmlSafeString(saveState.binary, true) << "</Binary>\n";
- info << " <Label>" << xmlSafeString(saveState.label, true) << "</Label>\n";
- info << " <UniqueID>" << saveState.uniqueID << "</UniqueID>\n";
- break;
- case PLUGIN_DSSI:
- info << " <Binary>" << xmlSafeString(saveState.binary, true) << "</Binary>\n";
- info << " <Label>" << xmlSafeString(saveState.label, true) << "</Label>\n";
- break;
- case PLUGIN_LV2:
- info << " <URI>" << xmlSafeString(saveState.label, true) << "</URI>\n";
- break;
- case PLUGIN_VST:
- info << " <Binary>" << xmlSafeString(saveState.binary, true) << "</Binary>\n";
- info << " <UniqueID>" << saveState.uniqueID << "</UniqueID>\n";
- break;
- case PLUGIN_AU:
- // TODO?
- info << " <Binary>" << xmlSafeString(saveState.binary, true) << "</Binary>\n";
- info << " <UniqueID>" << saveState.uniqueID << "</UniqueID>\n";
- break;
- case PLUGIN_CSOUND:
- case PLUGIN_GIG:
- case PLUGIN_SF2:
- case PLUGIN_SFZ:
- info << " <Filename>" << xmlSafeString(saveState.binary, true) << "</Filename>\n";
- info << " <Label>" << xmlSafeString(saveState.label, true) << "</Label>\n";
- break;
- }
-
- info << " </Info>\n\n";
-
- content << info;
- }
-
- {
- String data(" <Data>\n");
-
- data << " <Active>" << (saveState.active ? "Yes" : "No") << "</Active>\n";
-
- if (saveState.dryWet != 1.0f)
- data << " <DryWet>" << saveState.dryWet << "</DryWet>\n";
- if (saveState.volume != 1.0f)
- data << " <Volume>" << saveState.volume << "</Volume>\n";
- if (saveState.balanceLeft != -1.0f)
- data << " <Balance-Left>" << saveState.balanceLeft << "</Balance-Left>\n";
- if (saveState.balanceRight != 1.0f)
- data << " <Balance-Right>" << saveState.balanceRight << "</Balance-Right>\n";
- if (saveState.panning != 0.0f)
- data << " <Panning>" << saveState.panning << "</Panning>\n";
-
- if (saveState.ctrlChannel < 0)
- data << " <ControlChannel>N</ControlChannel>\n";
- else
- data << " <ControlChannel>" << saveState.ctrlChannel+1 << "</ControlChannel>\n";
-
- content << data;
- }
-
- for (StateParameterItenerator it = saveState.parameters.begin(); it.valid(); it.next())
- {
- StateParameter* const stateParameter(*it);
-
- String parameter("\n"" <Parameter>\n");
-
- parameter << " <Index>" << (long)stateParameter->index << "</Index>\n"; // FIXME
- parameter << " <Name>" << xmlSafeString(stateParameter->name, true) << "</Name>\n";
-
- if (stateParameter->symbol != nullptr && stateParameter->symbol[0] != '\0')
- parameter << " <Symbol>" << xmlSafeString(stateParameter->symbol, true) << "</Symbol>\n";
-
- parameter << " <Value>" << stateParameter->value << "</Value>\n";
-
- if (stateParameter->midiCC > 0)
- {
- parameter << " <MidiCC>" << stateParameter->midiCC << "</MidiCC>\n";
- parameter << " <MidiChannel>" << stateParameter->midiChannel+1 << "</MidiChannel>\n";
- }
-
- parameter << " </Parameter>\n";
-
- content << parameter;
- }
-
- if (saveState.currentProgramIndex >= 0 && saveState.currentProgramName != nullptr)
- {
- // ignore 'default' program
- #ifdef __USE_GNU
- if ((saveState.currentProgramIndex > 0 || strcasecmp(saveState.currentProgramName, "default") != 0))
- #else
- if ((saveState.currentProgramIndex > 0 || std::strcmp(saveState.currentProgramName, "Default") != 0))
- #endif
- {
- String program("\n");
- program << " <CurrentProgramIndex>" << saveState.currentProgramIndex+1 << "</CurrentProgramIndex>\n";
- program << " <CurrentProgramName>" << xmlSafeString(saveState.currentProgramName, true) << "</CurrentProgramName>\n";
-
- content << program;
- }
- }
-
- if (saveState.currentMidiBank >= 0 && saveState.currentMidiProgram >= 0)
- {
- String midiProgram("\n");
- midiProgram << " <CurrentMidiBank>" << saveState.currentMidiBank+1 << "</CurrentMidiBank>\n";
- midiProgram << " <CurrentMidiProgram>" << saveState.currentMidiProgram+1 << "</CurrentMidiProgram>\n";
-
- content << midiProgram;
- }
-
- for (StateCustomDataItenerator it = saveState.customData.begin(); it.valid(); it.next())
- {
- StateCustomData* const stateCustomData(*it);
-
- String customData("\n"" <CustomData>\n");
- customData << " <Type>" << xmlSafeString(stateCustomData->type, true) << "</Type>\n";
- customData << " <Key>" << xmlSafeString(stateCustomData->key, true) << "</Key>\n";
-
- if (std::strcmp(stateCustomData->type, CUSTOM_DATA_CHUNK) == 0 || std::strlen(stateCustomData->value) >= 128)
- customData << " <Value>\n" << xmlSafeString(stateCustomData->value, true) << "\n </Value>\n";
- else
- customData << " <Value>" << xmlSafeString(stateCustomData->value, true) << "</Value>\n";
-
- customData << " </CustomData>\n";
-
- content << customData;
- }
-
- if (saveState.chunk != nullptr && saveState.chunk[0] != '\0')
- {
- String chunk("\n"" <Chunk>\n");
- chunk << saveState.chunk << "\n </Chunk>\n";
-
- content << chunk;
- }
-
- content << " </Data>\n";
- }
-
- // -----------------------------------------------------------------------
- #endif
-
- CARLA_BACKEND_END_NAMESPACE
-
- #endif // CARLA_STATE_UTILS_HPP_INCLUDED
|