Krotos Modules 3
Loading...
Searching...
No Matches
ParameterManager.cpp
Go to the documentation of this file.
1namespace krotos
2{
3 const float ParameterManager::DefaultModDepth = 0.2f;
4 const float ParameterManager::OnOffThreshold = 0.5f;
5
6 ParameterManager::ParameterManager(AudioProcessor& processor, int numParams, const String& namePrefix)
7 : m_processor(processor), m_vts(processor, &m_undoManager), m_parentTree(XmlType::Tag::plugin),
8 m_processorTree(XmlType::Tag::params), m_kwidgetTree(XmlType::Tag::kwidgets),
9 m_connectionTree(XmlType::Tag::connections), m_modulationTree(XmlType::Tag::modulations),
10 m_customDataTree(XmlType::Tag::customData), m_numGenericParameters(numParams),
11 m_parameterNamePrefix(namePrefix)
12 {
13 // The number of digits of the max number of parameters, ex. 512 -> numZeros=3 80 -> numZeros=2 etc.
14 // for making the parameter names look nice in the loop.
16 static_cast<int>(std::floor(std::log10(static_cast<double>(m_numGenericParameters - 1)))) + 1;
17
18 m_parentTree.appendChild(m_kwidgetTree, nullptr);
19 m_parentTree.appendChild(m_connectionTree, nullptr);
20 m_parentTree.appendChild(m_processorTree, nullptr);
21 m_parentTree.appendChild(m_modulationTree, nullptr);
22
23 // custom data tree test
24 m_parentTree.appendChild(m_customDataTree, nullptr);
25
27
28 // When using the 2-parameter constructor for AudioProcessorValueTreeState, you have to add
29 // all the parameters and then assign it a valid ValueTree (i.e. one with a name)
30 m_vts.state = m_processorTree;
31
33 }
34
35 Kwidget* ParameterManager::getKwidget(const String& kwidgetID)
36 {
37 Kwidget* kwidget = nullptr;
38 try
39 {
40 kwidget = m_activeKwidgets.at(kwidgetID);
41 }
42 catch (std::out_of_range& e)
43 {
44 DBG("Error: getKwidget failed to find kwidget with given ID: " << kwidgetID << "\n" << e.what());
45 ignoreUnused(e);
46 }
47
48 return kwidget;
49 }
50
51 bool ParameterManager::addKwidget(Kwidget* kwidget, bool useEndParams, bool isChildKwidget)
52 {
53 auto kwidgetID = kwidget->getKwidgetID();
54 // do not allow duplicate kwidgetIDs
55 if (m_activeKwidgets[kwidgetID] != nullptr)
56 {
57 // Duplicate kwidgetID
58 jassertfalse;
59 return false;
60 }
61
62 auto kwidgetTree = createKwidgetTree(kwidget, useEndParams);
63
64 if (!kwidgetTree.isValid())
65 {
66 jassertfalse;
67 return false;
68 }
69
70 // Append the kwidget tree to the kwidgets tree
71 if (!isChildKwidget)
72 m_kwidgetTree.appendChild(kwidgetTree, nullptr);
73
74 m_kwidgetData[kwidget] = kwidgetTree;
75 m_activeKwidgets[kwidget->getKwidgetID()] = kwidget;
76
77 attachParameters(kwidget, kwidgetTree);
78
79 return true;
80 }
81
82 void ParameterManager::removeKwidget(const String& kwidgetID)
83 {
86
87 auto k = getKwidget(kwidgetID);
88
89 k->setAccessible(false);
90
92 m_kwidgetTree.removeChild(m_kwidgetData[k], nullptr);
93 m_kwidgetData.erase(k);
94 m_activeKwidgets.erase(kwidgetID);
95 }
96
98 {
99 StringArray kwidgetsToRemove;
100 for (const auto& kwidget : m_activeKwidgets)
101 kwidgetsToRemove.add(kwidget.first);
102
103 for (const auto& kwidget : kwidgetsToRemove)
104 removeKwidget(kwidget);
105 }
106
107 void ParameterManager::addConnection(const String& sourceID, const String& destinationID)
108 {
109 ValueTree newConnection{
111 {{XmlType::Property::source, sourceID}, {XmlType::Property::destination, destinationID}}};
112
113 m_connectionTree.appendChild(newConnection, getUndoManager());
114 }
115
116 void ParameterManager::removeConnection(const String& sourceID, const String& destID)
117 {
118 for (const auto& connection : m_connectionTree)
119 {
120 if (connection[XmlType::Property::source] == sourceID &&
121 connection[XmlType::Property::destination] == destID)
122 {
123 m_connectionTree.removeChild(connection, getUndoManager());
124 return;
125 }
126 }
127
128 // If you reach this point, that means you tried to remove a connection that didn't exist.
129 jassertfalse;
130 }
131
133 {
134 std::vector<ValueTree> connectionsToRemove;
135
136 for (const auto& c : m_connectionTree)
137 if (c[XmlType::Property::source] == kwidgetID || c[XmlType::Property::destination] == kwidgetID)
138 connectionsToRemove.push_back(c);
139
140 for (const auto& c : connectionsToRemove)
141 m_connectionTree.removeChild(c, getUndoManager());
142 }
143
145
146 bool ParameterManager::connectionExists(const String& sourceID, const String& destID)
147 {
148 for (const auto& connection : m_connectionTree)
149 {
150 if (connection[XmlType::Property::source] == sourceID &&
151 connection[XmlType::Property::destination] == destID)
152 {
153 return true;
154 }
155 }
156 return false;
157 }
158
159 ValueTree ParameterManager::addModulation(const String& src, int modulatorIdx, const String& dest,
160 const String& paramID, float depth, KAttachment::Polarity polarity)
161 {
162 ValueTree vt{Tag::modulation,
163 {{Property::source, src},
164 {Property::modulatorIndex, modulatorIdx},
165 {Property::destination, dest},
166 {Property::param, paramID},
167 {Property::depth, depth},
168 {Property::polarity, polarity}}};
169
170 for (int i = 0; i < m_modulationTree.getNumChildren(); i++)
171 {
172 auto child = m_modulationTree.getChild(i);
173 auto destination = child.getPropertyPointer(Property::destination)->toString();
174 auto param = child.getPropertyPointer(Property::param)->toString();
175 auto source = child.getPropertyPointer(Property::source)->toString();
176
177 if (destination == dest && param == paramID)
178 {
179 // the new kwidget and parameter are the same ones that already exist in the tree.
180 if (source == src)
181 {
182 // the modulation that you are trying to create already exists
183 // we don't need to add this modulation
184 return child;
185 }
186 }
187 }
188
189 m_modulationTree.appendChild(vt, getUndoManager());
190
191 return vt;
192 }
193
194 void ParameterManager::removeModulation(const String& src, int modulatorIdx, const String& dest,
195 const String& paramID)
196 {
197 m_modulationTree.removeChild(getModulationTree(src, modulatorIdx, dest, paramID), getUndoManager());
198 }
199
200 void ParameterManager::removeModulation(ValueTree modulationChildTree)
201 {
202 if (modulationChildTree.isAChildOf(m_modulationTree))
203 m_modulationTree.removeChild(modulationChildTree, getUndoManager());
204 else
205 // Tree is not a child of the modulation tree. Has it been deleted already?
206 jassertfalse;
207 }
208
209 void ParameterManager::setModulationDepth(const String& src, int modulatorIdx, const String& dest,
210 const String& paramID, float depth)
211 {
212 getModulationTree(src, modulatorIdx, dest, paramID).setProperty(Property::depth, depth, getUndoManager());
213 }
214
215 void ParameterManager::setModulationPolarity(const String& src, int modulatorIdx, const String& dest,
216 const String& paramID, KAttachment::Polarity polarity)
217 {
218 getModulationTree(src, modulatorIdx, dest, paramID).setProperty(Property::polarity, polarity, getUndoManager());
219 }
220
222 {
223 std::vector<ValueTree> modulationToRemove;
224
225 for (const auto& m : m_modulationTree)
226 if (m[Property::source] == kwidgetID || m[Property::destination] == kwidgetID)
227 modulationToRemove.push_back(m);
228
229 for (const auto& m : modulationToRemove)
230 m_modulationTree.removeChild(m, getUndoManager());
231 }
232
234
235 void ParameterManager::clearModulationExcept(const String& allowedSource)
236 {
237 Array<ValueTree> nodesToRemove;
238
239 for (auto child : m_modulationTree)
240 {
241 // Check if the source attribute contains the allowed source
242 if (!child[Property::source].toString().contains(allowedSource))
243 {
244 // Add the modulation node to the array for removal
245 nodesToRemove.add(child);
246 }
247 }
248
249 // Remove the nodes after the read iteration
250 for (auto& node : nodesToRemove)
251 {
252 m_modulationTree.removeChild(node, getUndoManager());
253 }
254 }
255
257
259
260 const ValueTree& ParameterManager::getKwidgetsTree() { return m_kwidgetTree; };
261
262 const StringArray ParameterManager::StaticConnectionDestinations = {"Master", "AudioOut1", "AudioOut2", "AudioOut3",
263 "AudioOut4"};
264
265 void ParameterManager::setCurrentState(const ValueTree& newState)
266 {
267 // Update AudioProcessorValueTreeState params
268 m_parentTree.removeChild(m_processorTree, nullptr);
269 m_processorTree = newState.getChildWithName(Tag::params).createCopy();
270 m_parentTree.appendChild(m_processorTree, nullptr);
271 m_vts.replaceState(m_processorTree);
272
273 // Add connections - changes to the m_connectionTree will cause callbacks which add/remove connections as
274 // necessary
275 auto newConnectionTree = newState.getChildWithName(Tag::connections);
276 addNonStaticConnections(newConnectionTree);
277
278 // Add modulation
279 auto newModulationTree = newState.getChildWithName(Tag::modulations);
280 copyModulationTree(newModulationTree);
281
283 }
284
285 void ParameterManager::addNonStaticConnections(juce::ValueTree& newConnectionTree)
286 {
287 // Remove all connections to audio out destinations
288 // Loop though children from last to first so removing a child won't kill indexing
289 for (int i = newConnectionTree.getNumChildren() - 1; i >= 0; i--)
290 {
291 auto child = newConnectionTree.getChild(i);
292 auto destination = child.getPropertyPointer(Property::destination)->toString();
293 // Add any non-static connections to the m_connectionTree
294 if (!isAStaticConnectionDestination(destination))
295 {
296 newConnectionTree.removeChild(
297 child, nullptr); // You must remove the child from the parent before adding it elsewhere
298 m_connectionTree.addChild(child, -1, nullptr);
299 }
300 }
301 }
302
303 AudioProcessor& ParameterManager::getProcessor() { return m_processor; }
304
305 AudioProcessorValueTreeState& ParameterManager::getProcessorState() { return m_vts; }
306
308
310 {
311 struct InitialStateAction : public UndoableAction
312 {
313 bool perform() override { return true; }
314 bool undo() override { return true; }
315 };
316
317 m_undoManager.clearUndoHistory();
318 m_undoManager.beginNewTransaction("Initial State");
319 m_undoManager.perform(new InitialStateAction());
320 }
321
322 void ParameterManager::debugState() const { DBG(m_parentTree.toXmlString()); }
323
324 void ParameterManager::addCustomDataNode(const ValueTree& customDataNode)
325 {
326 m_customDataTree.appendChild(customDataNode, getUndoManager());
327 }
328
330
332 {
333 for (int i = 0; i < m_numGenericParameters; i++)
334 {
335 // For 512 total params, parameterID looks like -> '#000' '#025' '#495' etc.
336 auto name = m_parameterNamePrefix + String(i).paddedLeft('0', m_numDigitsInGenericParamName);
337
338 std::unique_ptr<GenericParameter> p{new GenericParameter(name, name, i)};
339 m_genericParameters.push_back(p.get()); // Save a pointer to the GenericParameter
340 m_vts.createAndAddParameter(
341 std::move(p)); // Give ownership of the parameter to AudioProcessorValueTreeState
342 }
343 }
344
345 std::vector<int> ParameterManager::findGenericParameters(int numParametersToLink, bool useEndParams)
346 {
347 // find a consecutive group of free GenericParameters to link the KParameters to
348 int startIdx = -1;
349 int freeCount = 0;
350 bool success = false;
351
352 if (useEndParams == false)
353 {
354 for (int i = 0; i < m_numGenericParameters; i++)
355 {
356 bool attached = m_genericParameters[static_cast<size_t>(i)]->isLinked();
357
358 if (freeCount == 0)
359 {
360 if (!attached)
361 {
362 startIdx = i;
363 freeCount++;
364 }
365 }
366 else if (freeCount > 0 && freeCount < numParametersToLink)
367 {
368 if (!attached)
369 freeCount++;
370 else
371 freeCount = 0;
372 }
373 else if (freeCount == numParametersToLink)
374 {
375 success = true;
376 break;
377 }
378 }
379 }
380 else
381 {
382 // for child kwidgets assign from the end of the generic parameters
383 for (int i = m_numGenericParameters - 1; i >= 0; i--)
384 {
385 bool attached = m_genericParameters[static_cast<size_t>(i)]->isLinked();
386
387 if (freeCount == 0)
388 {
389 if (!attached)
390 {
391 freeCount++;
392 }
393 }
394 else if (freeCount > 0 && freeCount < numParametersToLink)
395 {
396 if (!attached)
397 freeCount++;
398 else
399 freeCount = 0;
400 }
401 else if (freeCount == numParametersToLink)
402 {
403 startIdx = i + 1;
404 success = true;
405 break;
406 }
407 }
408 }
409
410 if (success)
411 {
412 std::vector<int> paramLocs;
413 for (int i = 0; i < numParametersToLink; i++)
414 {
415 paramLocs.push_back(i + startIdx);
416 }
417 return paramLocs;
418 }
419
420 return {};
421 }
422
423 ValueTree ParameterManager::createKwidgetTree(Kwidget* k, bool useEndParams)
424 {
425 ValueTree kwidgetTree(k->getState());
426 auto paramTree = kwidgetTree.getChildWithName(Tag::params);
427
428 // No parameter attachment necessary if the Kwidget has no parameters.
429 auto numParams = paramTree.getNumChildren();
430 if (numParams == 0)
431 return kwidgetTree;
432
433 // If a Kwidget is added from a saved state, it will link to the same parameters
434 // that it was previously linked to.
435 bool needsAttachment = static_cast<int>(paramTree.getChild(0)[Property::linkIndex]) == -1;
436 if (!needsAttachment)
437 return kwidgetTree;
438
439 // If there are no available parameters to link to, this will return an invalid ValueTree.
440 auto genericParameterLocs = findGenericParameters(numParams, useEndParams);
441 if (genericParameterLocs.empty())
442 return {};
443
444 // Set the linkIndex property for each parameter using the indices obtained in the previous step.
445 auto it = genericParameterLocs.begin();
446 for (auto param : paramTree)
447 param.setProperty(Property::linkIndex, *it++, nullptr);
448
449 return kwidgetTree;
450 }
451
452 String ParameterManager::generateKwidgetID(const String& kwidgetType, const String& idModifier, int typeLimit)
453 {
454 jassert(kwidgetType != "");
455 jassert(typeLimit >= 0);
456
457 auto kwidgetCount = std::distance(m_activeKwidgets.begin(), m_activeKwidgets.end()) + 1;
458
459 // Todo: maxSuffixes appears to be redundant / unused. Leaving in to retain limiter on type count.
460 if (typeLimit <= 0)
461 {
462 typeLimit = kwidgetCount;
463 }
464
465 for (int candidateSuffix = 0; candidateSuffix < typeLimit; candidateSuffix++)
466 {
467 String candidateID = idModifier + kwidgetType + String(candidateSuffix);
468 // Only consider the candidate if it isn't already assigned to a kwidget
469 // We loop round incrementing the suffix until we find a vacant ID
470 if (m_activeKwidgets[candidateID] == nullptr) // == nullptr if not in the map
471 {
472 // Success, this candidateID is not in use by any kwidget in the system,
473 // we return it for use by the caller
474 return candidateID;
475 }
476 // Otherwise we loop round and generate a new candidate
477 }
478 DBG("ParameterManager::generateKwidgetID: Failed to generate a kwidgetID based on input parameters");
479 return ""; // Returning empty string denotes that the operation was not successful
480 }
481
482 bool ParameterManager::registerKwidget(juce::ValueTree& kwidget, StringArray& kwidgetIDArray,
483 bool /*repairEnabled*/)
484 {
485 bool isValid = true;
486 String errorMessage = "";
487
488 auto kwidgetID = kwidget[XmlType::Property::id].toString();
489
490 if (kwidgetIDArray.contains(kwidgetID, true)) // See if it already registered
491 {
492 isValid = false; // Return false as currently we are not attempting to rectify duplicate IDs, only flag them
493 errorMessage += "Duplicate KwidgetID found: " + kwidgetID + ", not automatically repaired";
494 }
495 else
496 {
497 kwidgetIDArray.add(kwidgetID);
498 }
499
500 if (errorMessage != "")
501 {
502 DBG(errorMessage);
503 }
504
505 return isValid;
506 }
507
508 bool ParameterManager::initialiseKwidgetIDUsageTable(const ValueTree& state, bool repairEnabled)
509 {
510 StringArray kwidgetIDArray;
511 bool isValid = true;
512 // Set the entire vector to unallocated
513 kwidgetIDArray.clear();
514
515 auto kwidgetTree = state.getChildWithName(XmlType::Tag::kwidgets);
516 // DBG(kwidgetTree.toXmlString());
517 for (auto kwidget : kwidgetTree) // For every kwidget
518 {
519 isValid &= registerKwidget(kwidget, kwidgetIDArray, repairEnabled);
520 // Dealing with child Kwidgets - not recursive - we have no plans for infinitely nested Kwidgets
521 auto childKwidgetTree = kwidget.getChildWithName(XmlType::Tag::kwidgets);
522 for (auto ckwidget : childKwidgetTree) // For every child kwidget
523 {
524 isValid &= registerKwidget(ckwidget, kwidgetIDArray, repairEnabled);
525 }
526 }
527 return isValid;
528 }
529
530 bool ParameterManager::flagUsedIndexes(juce::ValueTree& kwidget, bool repairEnabled)
531 {
532 bool isValid = true;
533 auto paramTree = kwidget.getChildWithName(Tag::params); // Get the tree of parameters
534 String errorMessage = "";
535 for (auto param : paramTree) // Loop though each parameter
536 {
537 // For every link index in every Kwidget, keep a note of which indexes are used
538 auto idx = static_cast<int>(param[Property::linkIndex]); // Fetch its link index
539 if (idx >= 0 && idx < m_numGenericParameters) // Check within valid range
540 {
541 if (m_linkIndexAllocation[idx] == true) // If it is already true, we have found a duplicate index
542 {
543 isValid = repairEnabled; // This will never become invalid if repair is enabled
544 errorMessage += "Duplicate link index found: " + param[Property::id].toString() +
545 ", linkIndex = " + String(idx);
546
547 if (repairEnabled)
548 {
549 param.setProperty(Property::linkIndex, "-1",
550 nullptr); // Start the repair process by flagging as unallocated
551 errorMessage += " : Set to -1, will be repaired by the Parameter check";
552 }
553 errorMessage += "\n";
554 }
555 else
556 {
557 m_linkIndexAllocation[idx] = true; // Flag the index as used
558 }
559 }
560 }
561
562 if (errorMessage != "")
563 {
564 DBG(errorMessage);
565 }
566
567 return isValid;
568 }
569
570 bool ParameterManager::initialiseLinkIndexUsageTable(const ValueTree& state, bool repairEnabled)
571 {
572 bool isValid = true;
573 // Set the entire vector to unallocated
574 m_linkIndexAllocation.clear();
575 for (int i = 0; i < m_numGenericParameters; i++)
576 {
577 m_linkIndexAllocation.push_back(false);
578 }
579
580 auto kwidgetTree = state.getChildWithName(XmlType::Tag::kwidgets);
581 // DBG(kwidgetTree.toXmlString());
582 for (auto kwidget : kwidgetTree) // For every kwidget
583 {
584 isValid &= flagUsedIndexes(kwidget, repairEnabled);
585 // Dealing with child Kwidgets - not recursive - we have no plans for infinitely nested Kwidgets
586 auto childKwidgetTree = kwidget.getChildWithName(XmlType::Tag::kwidgets);
587 for (auto ckwidget : childKwidgetTree) // For every child kwidget
588 {
589 isValid &= flagUsedIndexes(ckwidget, repairEnabled);
590 }
591 }
592 return isValid;
593 }
594
596 {
597 for (int idx = 0; idx < m_numGenericParameters; idx++)
598 {
599 if (m_linkIndexAllocation[idx] == false)
600 {
601 m_linkIndexAllocation[idx] = true; // Flag that it is now used
602 return idx;
603 }
604 }
605 jassertfalse; // No free indices left
606 return -1; // TODO: deal with no free indexes left situation
607 }
608
609 void ParameterManager::setGenericParameterValue(ValueTree& paramTreeGeneric, int index, var newValue)
610 {
611 auto genericParamID =
612 m_parameterNamePrefix + String(index).paddedLeft('0', m_numDigitsInGenericParamName); // Create the ID
613 auto genericParam =
614 paramTreeGeneric.getChildWithProperty(Property::id, genericParamID); // Retreive the generic param
615 genericParam.setProperty(Property::value, newValue, nullptr);
616 }
617
618 bool ParameterManager::validateKwidgetState(ValueTree& kwidgetTree, ValueTree& paramTreeGeneric,
619 bool repairEnabled = false)
620 {
621 String errorMessage = "";
622 int errorCount = 0;
623 bool isValid = true;
624
625 String kwidgetType = kwidgetTree[XmlType::Property::type].toString();
626 auto kwidgetID = kwidgetTree[XmlType::Property::id].toString();
627 auto kwidget = KwidgetFactory::createKwidget(kwidgetType, kwidgetID);
628
629 // Check that the Kwidget was created
630 if (kwidget != nullptr)
631 {
632 auto paramTree = kwidgetTree.getChildWithName(Tag::params); // Get the incoming tree of parameters
633 for (auto param : paramTree) // Loop though each parameter
634 {
635 auto idx = static_cast<int>(param[Property::linkIndex]); // Fetch its link index
636 auto paramID = param[Property::id].toString(); // Fetch its ID
637
638 // Check link index to see whether it is within valid range
639 if (idx < 0 || idx >= m_numGenericParameters)
640 {
641 errorMessage +=
642 String(++errorCount) + ") Index out of range: " + paramID + ", linkIndex = " + String(idx);
643 if (repairEnabled)
644 {
645 int newIndex = getUnusedLinkIndex();
646 param.setProperty(Property::linkIndex, newIndex,
647 nullptr); // Repair state by setting linkIndex to an unused index
648 auto existingValue = param.getProperty(
649 Property::value); // We will use this to update the newly assigned generic parameter
650 // DBG(existingValue.toString());
651 // Copy the original value of the parameter into the value of the newly linked generic
652 // parameter We don't care if the existingValue is out of range, this will be checked for as
653 // usual further down
654 setGenericParameterValue(paramTreeGeneric, newIndex, existingValue);
655
656 errorMessage += " : Repaired with linkIndex = " + String(newIndex) +
657 " set to original value (= " + existingValue.toString() + ")";
658 }
659 isValid = repairEnabled;
660 errorMessage += "\n";
661 }
662
663 // Check that the parameter is recognised ( ID exists within the Kwidget prototype )
664 auto p = kwidget->getParameter(paramID);
665 if (p == nullptr)
666 {
667 errorMessage += String(++errorCount) + ") Parameter not recognised: " + paramID +
668 ", linkIndex = " + String(idx);
669 /* Temporarily disabled this repair as deleting node here upsets the node iterator and skips
670 * processing the next node leaving unrecognised param in is benign anyway, it will simply be
671 * ignored, cannot exist in kwidget state as param not declared TODO: for completeness remove the
672 * child, and nudge the iterator back 1 somehow*/
673 if (repairEnabled)
674 {
675 // paramTree.removeChild(param, nullptr); // Repair state by removing unwanted parameter
676 errorMessage += " : Not repaired but benign - see comment in code";
677 }
678 isValid = repairEnabled;
679 errorMessage += "\n";
680 }
681 }
682
683 auto& kwidgetParams = kwidget->getParameters();
684 for (auto param : kwidgetParams) // Loop though each parameter in the kwidget - this is the defintition of
685 // the params we actually need
686 {
687 auto id = param->getParameterID(); // This is the name of the parameter
688 auto defaultValue = param->getValue(); // and the value it defaulted to when it was created
689 auto paramChild = paramTree.getChildWithProperty(
690 Property::id, id); // Try to fetch a parameter with the same name from state
691 if (paramChild.isValid()) // If it is valid, we know the parameter exists in state - hooray!
692 {
693 auto value = paramChild.getProperty(Property::value);
694 auto range = param->getNormalisableRange();
695 if (float(value) < range.start || float(value) > range.end)
696 {
697 errorMessage += String(++errorCount) + ") Parameter value out of range: " + id +
698 ", range = " + String(range.start) + " to " + String(range.end) +
699 ", value = " + String(float(value));
700 if (repairEnabled)
701 {
702 paramChild.setProperty(Property::value, defaultValue,
703 nullptr); // Repair state by setting to default value
704
705 // Copy the corrected value of the parameter into the value of its linked generic parameter
706 auto linkIndex = static_cast<int>(paramChild[Property::linkIndex]); // Fetch its link index
707 setGenericParameterValue(paramTreeGeneric, linkIndex, defaultValue);
708
709 errorMessage += " : Repaired with default value (= " + String(defaultValue) + ")";
710 }
711 isValid = repairEnabled;
712 errorMessage += "\n";
713 }
714 }
715 else // The parameter was not found in the state - boohoo !
716 {
717 errorMessage += String(++errorCount) + ") Parameter missing: " + id;
718 if (repairEnabled)
719 {
720 // Create the missing parameter with default value and set the linked generic param to that
721 // value as well
722 auto newParameter = kwidgetTree.getChildWithName(Tag::params)
723 .getChild(0)
724 .createCopy(); // Grab this as a template
725 auto newIndex = getUnusedLinkIndex();
726 newParameter.setProperty(Property::id, param->getParameterID(), nullptr);
727 newParameter.setProperty(Property::linkIndex, newIndex, nullptr);
728 newParameter.setProperty(Property::value, defaultValue, nullptr);
729 paramTree.appendChild(
730 newParameter,
731 nullptr); // Repair state by adding missing parameter with an available link index
732
733 // Set the linked generic parameter with the default value as well
734 setGenericParameterValue(paramTreeGeneric, newIndex, defaultValue);
735
736 errorMessage += " : Repaired with new param and default value = " + String(defaultValue);
737 }
738 isValid = repairEnabled;
739 errorMessage += "\n";
740 }
741 }
742 }
743 else // Could not create Kwidget, this is an unrecoverable error at present
744 {
745 errorMessage +=
746 String(++errorCount) + ") Kwidget type not recognised: " + kwidgetType + " : Not Repaired\n";
747 isValid = false;
748 }
749
750 errorMessage = "Parameter check found " + String(errorCount) + " errors in Kwidget " + kwidgetID + ":\n" +
751 errorMessage;
752
753 if (errorCount > 0)
754 {
755 DBG(errorMessage);
756 }
757
758 return isValid;
759 }
760
761 void ParameterManager::attachParameters(Kwidget* kwidget, const ValueTree& kwidgetTree)
762 {
763 auto paramTree = kwidgetTree.getChildWithName(Tag::params);
764 for (const auto& param : paramTree)
765 {
766 auto paramID = param[Property::id].toString();
767 auto idx = static_cast<int>(param[Property::linkIndex]);
768 auto p = kwidget->getParameter(paramID);
769
770 p->attach(m_genericParameters[static_cast<size_t>(idx)]);
771 }
772 }
773
774 void ParameterManager::detachParameters(Kwidget* kwidget, const ValueTree& kwidgetTree)
775 {
776 auto paramTree = kwidgetTree.getChildWithName(Tag::params);
777 for (const auto& param : paramTree)
778 {
779 auto paramID = param[Property::id].toString();
780 auto p = kwidget->getParameter(paramID);
781
782 p->detach();
783 }
784 }
785
786 ValueTree ParameterManager::getModulationTree(const String& sourceID, int modulatorIdx, const String& destinationID,
787 const String& paramID)
788 {
789 for (const auto& m : m_modulationTree)
790 {
791 if (m[Property::source].toString() == sourceID && int(m[Property::modulatorIndex]) == modulatorIdx &&
792 m[Property::destination].toString() == destinationID && m[Property::param].toString() == paramID)
793 {
794 return m;
795 }
796 }
797
798 // No matching modulation instance.
799 jassertfalse;
800 return m_modulationTree;
801 }
802
804 {
805 // check through the modulation connections and only copy over modulations that don't exist already in the tree.
806 for (int i = 0; i < modTree.getNumChildren(); i++)
807 {
808 auto child = modTree.getChild(i);
809 auto destination = child.getPropertyPointer(Property::destination)->toString();
810 auto param = child.getPropertyPointer(Property::param)->toString();
811 auto source = child.getPropertyPointer(Property::source)->toString();
812
813 bool found = false;
814 for (int j = 0; j < m_modulationTree.getNumChildren(); j++)
815 {
816 auto modulation = m_modulationTree.getChild(j);
817
818 if (source == modulation.getProperty(Property::source).toString() &&
819 param == modulation.getProperty(Property::param).toString())
820 {
821 // this is the same kwidget and parameter
822 // see if the destination also matches
823 if (destination == modulation.getProperty(Property::destination).toString())
824 {
825 // this modulation is already in the tree, don't add a duplicate
826 // but do update its value if the incoming one is different
827 if (modulation.getProperty(Property::depth) != child.getProperty(Property::depth))
828 {
829 modulation.setProperty(Property::depth, child.getProperty(Property::depth), nullptr);
830 }
831 found = true;
832 break;
833 }
834 }
835 }
836
837 if (found == false)
838 {
839 // if we haven't found the modulation copy this one over to the tree
840 ValueTree newModulation(child.getType());
841 newModulation.copyPropertiesAndChildrenFrom(child, nullptr);
842 m_modulationTree.addChild(newModulation, -1, nullptr);
843 }
844 }
845 }
846} // namespace krotos
A subclass of RangedAudioParameter that can act as an alias for another audio parameter,...
Definition GenericParameter.h:15
Polarity
Definition KAttachment.h:120
void attach(GenericParameter *p)
Definition KParameter.cpp:146
void detach()
Definition KParameter.cpp:160
static std::unique_ptr< Kwidget > createKwidget(const String &kwidgetType, const String &kwidgetID)
Definition KwidgetFactory.h:56
Definition Kwidget.h:8
const ValueTree & getState()
Definition Kwidget.cpp:242
KParameter * getParameter(const String &parameterID) const noexcept
Get a raw pointer to a KParameter belonging to this Kwidget's KwidgetProcessor. Useful for low overhe...
Definition Kwidget.cpp:168
const String & getKwidgetID() const
Definition Kwidget.cpp:367
bool flagUsedIndexes(juce::ValueTree &kwidget, bool repairEnabled)
Definition ParameterManager.cpp:530
void removeModulationInvolving(const String &kwidgetID)
Definition ParameterManager.cpp:221
void setGenericParameterValue(ValueTree &paramTreeGeneric, int index, var newValue)
Definition ParameterManager.cpp:609
ValueTree m_kwidgetTree
Definition ParameterManager.h:314
bool registerKwidget(juce::ValueTree &kwidget, StringArray &kwidgetIDArray, bool repairEnabled)
Definition ParameterManager.cpp:482
int m_numDigitsInGenericParamName
Definition ParameterManager.h:324
const ValueTree & getKwidgetsTree()
Definition ParameterManager.cpp:260
void clearModulation()
Definition ParameterManager.cpp:233
ValueTree m_processorTree
Definition ParameterManager.h:313
std::vector< GenericParameter * > m_genericParameters
Definition ParameterManager.h:310
void detachParameters(Kwidget *kwidget, const ValueTree &kwidgetTree)
Definition ParameterManager.cpp:774
bool validateKwidgetState(ValueTree &kwidgetTree, ValueTree &paramTree, bool repairEnabled)
Definition ParameterManager.cpp:618
Kwidget * getKwidget(const String &kwidgetID)
Definition ParameterManager.cpp:35
void removeKwidget(const String &kwidgetID)
Definition ParameterManager.cpp:82
void resetUndoManager()
Definition ParameterManager.cpp:309
void setCurrentState(const ValueTree &newState)
Definition ParameterManager.cpp:265
std::vector< bool > m_linkIndexAllocation
Definition ParameterManager.h:281
ValueTree m_parentTree
Definition ParameterManager.h:312
AudioProcessor & m_processor
Definition ParameterManager.h:307
static const StringArray StaticConnectionDestinations
Contains a list of the names of all the static connection destinations.
Definition ParameterManager.h:278
void clearConnections()
Definition ParameterManager.cpp:144
bool initialiseLinkIndexUsageTable(const ValueTree &state, bool repairEnabled)
Definition ParameterManager.cpp:570
const String m_parameterNamePrefix
Definition ParameterManager.h:323
int getUnusedLinkIndex()
Definition ParameterManager.cpp:595
void setModulationDepth(const String &sourceID, int modulatorIdx, const String &destinationID, const String &paramID, float depth)
Definition ParameterManager.cpp:209
void attachParameters(Kwidget *kwidget, const ValueTree &kwidgetTree)
Definition ParameterManager.cpp:761
UndoManager m_undoManager
Definition ParameterManager.h:308
ValueTree m_connectionTree
Definition ParameterManager.h:315
ValueTree & getState()
Definition ParameterManager.cpp:256
String generateKwidgetID(const String &kwidgetType, const String &idModifier="", int typeInstanceLimit=0)
Definition ParameterManager.cpp:452
std::map< String, Kwidget * > m_activeKwidgets
Definition ParameterManager.h:320
const int m_numGenericParameters
Definition ParameterManager.h:322
void clearModulationExcept(const String &allowedSource)
Definition ParameterManager.cpp:235
void addNonStaticConnections(juce::ValueTree &newConnectionTree)
Definition ParameterManager.cpp:285
void clearKwidgets()
Definition ParameterManager.cpp:97
void addConnection(const String &sourceID, const String &destinationID)
Definition ParameterManager.cpp:107
ValueTree m_modulationTree
Definition ParameterManager.h:316
void removeModulation(const String &sourceID, int modulatorIdx, const String &destinationID, const String &paramID)
Definition ParameterManager.cpp:194
void copyModulationTree(ValueTree modTree)
Definition ParameterManager.cpp:803
void addCustomDataNode(const ValueTree &customDataNode)
Definition ParameterManager.cpp:324
void debugState() const
Definition ParameterManager.cpp:322
ValueTree createKwidgetTree(Kwidget *k, bool useEndParams=false)
Definition ParameterManager.cpp:423
void setModulationPolarity(const String &src, int modulatorIdx, const String &dest, const String &paramID, KAttachment::Polarity polarity)
Definition ParameterManager.cpp:215
AudioProcessor & getProcessor()
Definition ParameterManager.cpp:303
void addGenericParameters()
Definition ParameterManager.cpp:331
bool connectionExists(const String &sourceID, const String &destID)
Definition ParameterManager.cpp:146
std::vector< int > findGenericParameters(int numParametersToLink, bool useEndParams)
Definition ParameterManager.cpp:345
ValueTree addModulation(const String &sourceID, int modulatorIdx, const String &destinationID, const String &paramID, float depth=DefaultModDepth, KAttachment::Polarity=KAttachment::Polarity::Bipolar)
Definition ParameterManager.cpp:159
bool addKwidget(Kwidget *kwidget, bool useEndParams=false, bool isChildKwidget=false)
Add a new Kwidget to the state. This will add the kwidget to the internal state ValueTree and it's pa...
Definition ParameterManager.cpp:51
const ValueTree & getModulationTree()
Definition ParameterManager.cpp:258
bool initialiseKwidgetIDUsageTable(const ValueTree &state, bool repairEnabled)
Definition ParameterManager.cpp:508
const bool isAStaticConnectionDestination(const String &destName)
Returns true if the destName you supplied is the name of a static connection destination.
Definition ParameterManager.h:170
UndoManager * getUndoManager()
Definition ParameterManager.cpp:307
void removeConnection(const String &sourceID, const String &destinationID)
Definition ParameterManager.cpp:116
ValueTree m_customDataTree
Definition ParameterManager.h:317
void removeConnectionsInvolving(const String &kwidgetID)
Definition ParameterManager.cpp:132
AudioProcessorValueTreeState & getProcessorState()
Definition ParameterManager.cpp:305
std::map< Kwidget *, ValueTree > m_kwidgetData
Definition ParameterManager.h:319
static const float OnOffThreshold
The threshold at which a toggle, or "on/off" parameter should change from on to off stage.
Definition ParameterManager.h:45
ValueTree & getCustomDataTree()
Definition ParameterManager.cpp:329
AudioProcessorValueTreeState m_vts
Definition ParameterManager.h:309
static const float DefaultModDepth
Definition ParameterManager.h:40
ParameterManager(AudioProcessor &processor, int numParams=1000, const String &namePrefix="#")
Definition ParameterManager.cpp:6
Definition AirAbsorptionFilter.cpp:2
static const Identifier modulatorIndex
Definition XmlType.h:48
static const Identifier id
Definition XmlType.h:41
static const Identifier destination
Definition XmlType.h:46
static const Identifier value
Definition XmlType.h:42
static const Identifier source
Definition XmlType.h:45
static const Identifier depth
Definition XmlType.h:49
static const Identifier param
Definition XmlType.h:47
static const Identifier polarity
Definition XmlType.h:50
static const Identifier linkIndex
Definition XmlType.h:44
static const Identifier type
Definition XmlType.h:43
Definition XmlType.h:15
static const Identifier connection
Definition XmlType.h:25
static const Identifier kwidgets
Definition XmlType.h:22
static const Identifier params
Definition XmlType.h:18
static const Identifier modulation
Definition XmlType.h:27
static const Identifier connections
Definition XmlType.h:24
static const Identifier modulations
Definition XmlType.h:26
Common names for storing data in a ValueTree.
Definition XmlType.h:7