gearmulator

Emulation of classic VA synths of the late 90s/2000s that are based on Motorola 56300 family DSPs
Log | Files | Refs | Submodules | README | LICENSE

commit ffa7efb89b8cc8315fac8c54e1b699cc2f2fcb50
parent 172d2f157e1138ea00107d6bfc38d14ea32b7c2e
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date:   Thu,  9 May 2024 16:35:22 +0200

support value list definition by value/string pairs

Diffstat:
Msource/jucePluginLib/parameterbinding.cpp | 62++++++++++++++++++++++++--------------------------------------
Msource/jucePluginLib/parameterdescriptions.cpp | 102++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Msource/jucePluginLib/parameterdescriptions.h | 3+++
Msource/jucePluginLib/parametervaluelist.cpp | 20++++++++++++++++++++
Msource/jucePluginLib/parametervaluelist.h | 5+++++
5 files changed, 132 insertions(+), 60 deletions(-)

diff --git a/source/jucePluginLib/parameterbinding.cpp b/source/jucePluginLib/parameterbinding.cpp @@ -103,48 +103,34 @@ namespace pluginLib std::vector<Entry> sortedValues; - const auto& allValues = v->getAllValueStrings(); - uint8_t i = 0; - for (const auto& vs : allValues) + const auto& desc = v->getDescription(); + const auto& valueList = desc.valueList; + + if(valueList.order.empty()) { - if(vs.isNotEmpty()) - sortedValues.emplace_back(i, vs.toStdString()); - ++i; + uint8_t i = 0; + const auto& allValues = v->getAllValueStrings(); + for (const auto& vs : allValues) + { + if(vs.isNotEmpty()) + sortedValues.emplace_back(i, vs.toStdString()); + ++i; + } } - /* - std::sort(sortedValues.begin(), sortedValues.end(), [](const Entry& _a, const Entry& _b) + else { - const auto aOff =_a.second == "Off" || _a.second == "-"; - const auto bOff =_b.second == "Off" || _b.second == "-"; - - if(aOff && !bOff) - return true; - - if(!aOff && bOff) - return false; - - auto noDigitsString = [](const std::string& _s) + for(uint32_t i=0; i<valueList.order.size(); ++i) { - std::string s; - s.reserve(_s.size()); - for (const char c : _s) - { - if(isdigit(c)) - break; - s += c; - } - return s; - }; - - const auto a = noDigitsString(_a.second); - const auto b = noDigitsString(_b.second); - - if(a == b) - return _a.first < _b.first; - - return a < b; - }); - */ + const auto value = valueList.orderToValue(i); + if(value == ValueList::InvalidIndex) + continue; + const auto text = valueList.valueToText(value); + if(text.empty()) + continue; + sortedValues.emplace_back(value, text); + } + } + uint32_t count = 0; for (const auto& vs : sortedValues) diff --git a/source/jucePluginLib/parameterdescriptions.cpp b/source/jucePluginLib/parameterdescriptions.cpp @@ -100,6 +100,8 @@ namespace pluginLib if (descsArray == nullptr) return "Parameter descriptions are empty"; + std::stringstream errors; + { const auto& valueLists = json["valuelists"]; @@ -113,33 +115,15 @@ namespace pluginLib for(int i=0; i<entryProps.size(); ++i) { const auto key = std::string(entryProps.getName(i).toString().toUTF8()); - const auto values = entryProps.getValueAt(i).getArray(); - - if(m_valueLists.find(key) != m_valueLists.end()) - return "value list " + key + " is defined twice"; - - if(!values || values->isEmpty()) - return std::string("value list ") + key + " is not a valid array of strings"; - - ValueList vl; - vl.texts.reserve(values->size()); - - for (auto&& value : *values) - { - const auto text = static_cast<std::string>(value.toString().toUTF8()); - - if (vl.textToValueMap.find(text) == vl.textToValueMap.end()) - vl.textToValueMap.insert(std::make_pair(text, static_cast<uint32_t>(vl.texts.size()))); + const auto& values = entryProps.getValueAt(i); - vl.texts.push_back(text); - } + const auto result = parseValueList(key, values); - m_valueLists.insert(std::make_pair(key, vl)); + if(!result.empty()) + errors << "Failed to parse value list " << key << ": " << result << '\n'; } } - std::stringstream errors; - const auto& descs = *descsArray; for (int i = 0; i < descs.size(); ++i) @@ -426,6 +410,80 @@ namespace pluginLib return res; } + + std::string ParameterDescriptions::parseValueList(const std::string& _key, const juce::var& _values) + { + auto valuesArray = _values.getArray(); + + if(m_valueLists.find(_key) != m_valueLists.end()) + return "value list " + _key + " is defined twice"; + + ValueList vl; + + if(valuesArray) + { + if(valuesArray->isEmpty()) + return std::string("value list ") + _key + " is not a valid array of strings"; + + vl.texts.reserve(valuesArray->size()); + + for (auto&& value : *valuesArray) + { + const auto text = static_cast<std::string>(value.toString().toUTF8()); + vl.texts.push_back(text); + } + + for(uint32_t i=0; i<vl.texts.size(); ++i) + vl.order.push_back(i); + } + else + { + const auto valueMap = _values.getDynamicObject(); + if(!valueMap) + { + return std::string("value list ") + _key + " neither contains an array of strings nor a map of values => strings"; + } + + std::set<uint32_t> knownValues; + + for (const auto& it : valueMap->getProperties()) + { + const auto keyName = it.name.toString().toStdString(); + const auto& value = it.value; + + const auto key = std::strtol(keyName.c_str(), nullptr, 10); + + if(key < 0) + { + return std::string("Invalid parameter index '") + keyName + " in value list '" + _key + "', values must be >= 0"; + } + if(knownValues.find(key) != knownValues.end()) + { + return std::string("Parameter index '") + keyName + " in value list '" + _key + " has been specified twice"; + } + knownValues.insert(key); + + const auto requiredLength = key+1; + + if(vl.texts.size() < requiredLength) + vl.texts.resize(requiredLength); + + vl.texts[key] = value.toString().toStdString(); + vl.order.push_back(key); + } + } + + for (const auto& text : vl.texts) + { + if (vl.textToValueMap.find(text) == vl.textToValueMap.end()) + vl.textToValueMap.insert(std::make_pair(text, static_cast<uint32_t>(vl.texts.size()))); + } + + m_valueLists.insert(std::make_pair(_key, vl)); + + return {}; + } + void ParameterDescriptions::parseMidiPackets(std::stringstream& _errors, juce::DynamicObject* _packets) { if(!_packets) diff --git a/source/jucePluginLib/parameterdescriptions.h b/source/jucePluginLib/parameterdescriptions.h @@ -51,6 +51,9 @@ namespace pluginLib private: std::string loadJson(const std::string& _jsonString); + + std::string parseValueList(const std::string& _key, const juce::var& _values); + void parseMidiPackets(std::stringstream& _errors, juce::DynamicObject* _packets); void parseMidiPacket(std::stringstream& _errors, const std::string& _key, const juce::var& _value); diff --git a/source/jucePluginLib/parametervaluelist.cpp b/source/jucePluginLib/parametervaluelist.cpp @@ -2,6 +2,11 @@ namespace pluginLib { + namespace + { + std::string g_empty; + } + uint32_t ValueList::textToValue(const std::string& _string) const { const auto it = textToValueMap.find(_string); @@ -16,4 +21,19 @@ namespace pluginLib return texts.back(); return texts[_value]; } + + uint32_t ValueList::orderToValue(const uint32_t _orderIndex) const + { + if(_orderIndex >= order.size()) + return InvalidIndex; + return order[_orderIndex]; + } + + const std::string& ValueList::orderToText(const uint32_t _orderIndex) const + { + const auto value = orderToValue(_orderIndex); + if(value == InvalidIndex) + return g_empty; + return valueToText(value); + } } diff --git a/source/jucePluginLib/parametervaluelist.h b/source/jucePluginLib/parametervaluelist.h @@ -9,10 +9,15 @@ namespace pluginLib { struct ValueList { + static constexpr uint32_t InvalidIndex = 0xffffffff; + uint32_t textToValue(const std::string& _string) const; const std::string& valueToText(const uint32_t _value) const; + uint32_t orderToValue(uint32_t _orderIndex) const; + const std::string& orderToText(uint32_t _orderIndex) const; std::vector<std::string> texts; std::map<std::string, uint32_t> textToValueMap; + std::vector<uint32_t> order; }; }