tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_VSTXML.h
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2024
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7
8 Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
9*/
10
11namespace tracktion { inline namespace engine
12{
13
14class VSTXML
15{
16public:
17 static VSTXML* createFor (const juce::XmlElement& xml)
18 {
19 if (xml.hasTagName ("VSTParametersStructure"))
20 return new VSTXML (xml);
21
22 if (const auto* x = xml.getChildByName ("VSTParametersStructure"))
23 return new VSTXML (*x);
24
25 return {};
26 }
27
28 //=============================================================================
29 struct Group;
30
31 struct Base
32 {
33 Base() noexcept {}
34 virtual ~Base() {}
35
36 Group* parent = nullptr;
37 };
38
39 struct Param : public Base
40 {
41 int paramID = 0;
42 juce::String expr, name, label;
43 juce::StringArray shortNames;
44 juce::String type;
45 int numberOfStates = 0;
46 float defaultValue = 0.0f;
47 };
48
49 struct Group : public Base
50 {
51 juce::String name;
52 juce::OwnedArray<Base> paramTree;
53 };
54
55 struct Range
56 {
57 Range() noexcept {}
58 Range (const juce::String& s) { set (s); }
59
60 void set (const juce::String& s)
61 {
62 inclusiveLow = s.startsWithChar ('[');
63 inclusiveHigh = s.endsWithChar (']');
64
65 auto str = s.removeCharacters ("[]");
66
67 low = str.upToFirstOccurrenceOf (",", false, false).getFloatValue();
68 high = str.fromLastOccurrenceOf (",", false, false).getFloatValue();
69 }
70
71 bool contains (float f) const noexcept
72 {
73 return (inclusiveLow ? (f >= low) : (f > low))
74 && (inclusiveHigh ? (f <= high) : (f < high));
75 }
76
77 float low = 0;
78 float high = 0;
79
80 bool inclusiveLow = false;
81 bool inclusiveHigh = false;
82 };
83
84 struct Entry
85 {
86 juce::String name;
87 Range range;
88 };
89
90 struct ValueType
91 {
92 juce::String name, label;
94 };
95
96 struct Template
97 {
98 juce::String name;
100 };
101
102 const Param* getParamForID (int paramID, const Group* grp) const
103 {
104 for (auto item : (grp != nullptr ? grp->paramTree : paramTree))
105 {
106 if (auto param = dynamic_cast<const Param*> (item))
107 if (param->paramID == paramID)
108 return param;
109
110 if (auto group = dynamic_cast<const Group*> (item))
111 if (auto res = getParamForID (paramID, group))
112 return res;
113 }
114
115 return {};
116 }
117
118 const ValueType* getValueType (const juce::String& name) const
119 {
120 for (auto v : valueTypes)
121 if (v->name == name)
122 return v;
123
124 return {};
125 }
126
127 juce::OwnedArray<Base> paramTree;
130
131private:
132 VSTXML (const juce::XmlElement& xml)
133 {
134 for (auto item : xml.getChildIterator())
135 {
136 if (item->hasTagName ("Param")) parseParam (*item, nullptr, nullptr);
137 else if (item->hasTagName ("ValueType")) parseValueType (*item);
138 else if (item->hasTagName ("Template")) parseTemplate (*item);
139 else if (item->hasTagName ("Group")) parseGroup (*item, nullptr);
140 }
141 }
142
143 void parseParam (const juce::XmlElement& item, Group* group, Template* temp)
144 {
145 auto param = new Param();
146
147 if (temp != nullptr)
148 param->expr = item.getStringAttribute ("id");
149 else
150 param->paramID = item.getIntAttribute ("id");
151
152 param->name = item.getStringAttribute ("name");
153 param->label = item.getStringAttribute ("label");
154 param->type = item.getStringAttribute ("type");
155 param->numberOfStates = item.getIntAttribute ("numberOfStates");
156 param->defaultValue = (float) item.getDoubleAttribute ("defaultValue");
157
158 param->shortNames.addTokens (item.getStringAttribute ("shortName"), ",", {});
159 param->shortNames.trim();
160 param->shortNames.removeEmptyStrings();
161
162 if (group != nullptr)
163 {
164 group->paramTree.add (param);
165 param->parent = group;
166 }
167 else if (temp != nullptr)
168 {
169 temp->params.add (param);
170 }
171 else
172 {
173 paramTree.add (param);
174 }
175 }
176
177 void parseValueType (const juce::XmlElement& item)
178 {
179 auto vt = new ValueType();
180 valueTypes.add (vt);
181
182 vt->name = item.getStringAttribute ("name");
183 vt->label = item.getStringAttribute ("label");
184
185 int curEntry = 0;
186 const int numEntries = item.getNumChildElements();
187
188 for (auto entryXml : item.getChildWithTagNameIterator ("Entry"))
189 {
190 auto entry = new Entry();
191 entry->name = entryXml->getStringAttribute ("name");
192
193 if (entryXml->hasAttribute ("value"))
194 {
195 entry->range.set (entryXml->getStringAttribute ("value"));
196 }
197 else
198 {
199 entry->range.low = curEntry / (float) numEntries;
200 entry->range.high = (curEntry + 1) / (float) numEntries;
201
202 entry->range.inclusiveLow = true;
203 entry->range.inclusiveHigh = (curEntry == numEntries - 1);
204 }
205
206 vt->entries.add (entry);
207 ++curEntry;
208 }
209 }
210
211 void parseTemplate (const juce::XmlElement& item)
212 {
213 auto temp = new Template();
214 templates.add (temp);
215 temp->name = item.getStringAttribute ("name");
216
217 for (auto param : item.getChildIterator())
218 parseParam (*param, nullptr, temp);
219 }
220
221 void parseGroup (const juce::XmlElement& item, Group* parentGroup)
222 {
223 auto group = new Group();
224
225 if (parentGroup)
226 {
227 parentGroup->paramTree.add (group);
228 group->parent = parentGroup;
229 }
230 else
231 {
232 paramTree.add (group);
233 }
234
235 group->name = item.getStringAttribute ("name");
236
237 if (item.hasAttribute ("template"))
238 {
239 juce::StringArray variables;
240 variables.addTokens (item.getStringAttribute ("values"), ";", {});
241 variables.trim();
242
243 for (auto temp : templates)
244 {
245 if (temp->name == item.getStringAttribute ("template"))
246 {
247 for (int i = 0; i < temp->params.size(); ++i)
248 {
249 auto param = new Param();
250 group->paramTree.add (param);
251
252 param->parent = group;
253 param->paramID = evaluate (temp->params[i]->expr, variables);
254 param->defaultValue = temp->params[i]->defaultValue;
255 param->label = temp->params[i]->label;
256 param->name = temp->params[i]->name;
257 param->numberOfStates = temp->params[i]->numberOfStates;
258 param->shortNames = temp->params[i]->shortNames;
259 param->type = temp->params[i]->type;
260 }
261 }
262 }
263 }
264 else
265 {
266 for (auto subItem : item.getChildIterator())
267 {
268 if (subItem->hasTagName ("Param")) parseParam (*subItem, group, nullptr);
269 else if (subItem->hasTagName ("Group")) parseGroup (*subItem, group);
270 }
271 }
272 }
273
274 int evaluate (juce::String expr, const juce::StringArray& variables) const
275 {
276 juce::StringArray names;
277 juce::Array<int> vals;
278
279 for (auto& v : variables)
280 {
281 if (v.contains ("="))
282 {
283 names.add (v.upToFirstOccurrenceOf ("=", false, false));
284 vals.add (v.fromFirstOccurrenceOf ("=", false, false).getIntValue());
285 }
286 }
287
288 for (int i = 0; i < names.size(); ++i)
289 {
290 for (;;)
291 {
292 const int idx = expr.indexOfWholeWord (names[i]);
293 if (idx < 0)
294 break;
295
296 expr = expr.replaceSection (idx, names[i].length(), juce::String (vals[i]));
297 }
298 }
299
300 expr = expr.retainCharacters ("01234567890-+")
301 .replace ("+", " + ")
302 .replace ("-", " - ");
303
304 juce::StringArray tokens;
305 tokens.addTokens (expr, " ", {});
306
307 bool add = true;
308 int val = 0;
309
310 for (const auto& s : tokens)
311 {
312 if (s == "+")
313 {
314 add = true;
315 }
316 else if (s == "-")
317 {
318 add = false;
319 }
320 else
321 {
322 if (add)
323 val += s.getIntValue();
324 else
325 val -= s.getIntValue();
326 }
327 }
328
329 return val;
330 }
331};
332
333}} // namespace tracktion { inline namespace engine
void add(const ElementType &newElement)
ObjectClass * add(ObjectClass *newObject)
int size() const noexcept
void add(String stringToAdd)
int addTokens(StringRef stringToTokenise, bool preserveQuotedStrings)
bool endsWithChar(juce_wchar character) const noexcept
bool startsWithChar(juce_wchar character) const noexcept
String removeCharacters(StringRef charactersToRemove) const
String retainCharacters(StringRef charactersToRetain) const
int indexOfWholeWord(StringRef wordToLookFor) const noexcept
String replace(StringRef stringToReplace, StringRef stringToInsertInstead, bool ignoreCase=false) const
String replaceSection(int startIndex, int numCharactersToReplace, StringRef stringToInsert) const
int getNumChildElements() const noexcept
XmlElement * getChildByName(StringRef tagNameToLookFor) const noexcept
double getDoubleAttribute(StringRef attributeName, double defaultReturnValue=0.0) const
bool hasAttribute(StringRef attributeName) const noexcept
bool hasTagName(StringRef possibleTagName) const noexcept
int getIntAttribute(StringRef attributeName, int defaultReturnValue=0) const
const String & getStringAttribute(StringRef attributeName) const noexcept
typedef float