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

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_ParameterControlMappings.cpp
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
14static juce::String controllerIDToString (int id, int channelid)
15{
16 auto channel = " [" + juce::String (channelid) + "]";
17
18 if (id >= 0x40000) return TRANS("Channel Pressure Controller") + "# " + channel;
19 if (id >= 0x30000) return "RPN #" + juce::String (id & 0x7fff) + channel;
20 if (id >= 0x20000) return "NRPN #" + juce::String (id & 0x7fff) + channel;
21
22 if (id >= 0x10000)
23 {
24 auto name = TRANS(juce::MidiMessage::getControllerName (id & 0x7f));
25
26 if (name.isNotEmpty())
27 name = " (" + name + ")";
28
29 return TRANS("Controller") + " #" + juce::String (id & 0x7f) + name + channel;
30 }
31
32 return {};
33}
34
35static void removeAddAllCommandIfTooManyItems (juce::PopupMenu& menu)
36{
37 // if there are more than 200 parameters, strip off the initial two items ("add all" and separator)
38 if (menu.getNumItems() > 201)
39 {
41 int n = 0;
42
43 for (juce::PopupMenu::MenuItemIterator i (menu); i.next();)
44 if (++n >= 2)
45 m2.addItem (i.getItem());
46
47 menu = m2;
48 }
49}
50
51//==============================================================================
52ParameterControlMappings::ParameterControlMappings (Edit& ed) : edit (ed)
53{
54}
55
56ParameterControlMappings::~ParameterControlMappings()
57{
58}
59
60ParameterControlMappings* ParameterControlMappings::getCurrentlyFocusedMappings (Engine& engine)
61{
62 if (auto ed = engine.getUIBehaviour().getLastFocusedEdit())
63 return &ed->getParameterControlMappings();
64
65 return {};
66}
67
68//==============================================================================
69int ParameterControlMappings::addMapping (int controllerID, int channel, const AutomatableParameter::Ptr& param)
70{
71 const juce::ScopedLock sl (lock);
72
73 for (int i = controllerIDs.size(); --i >= 0;)
74 {
75 if (controllerIDs.getUnchecked (i) == controllerID
76 && channelIDs[i] == channel
77 && parameters[i] == param)
78 {
79 return i;
80 }
81 }
82
83 controllerIDs.add (controllerID);
84 channelIDs.add (channel);
85 parameters.add (param);
86 parameterFullNames.add (param->getFullName());
87
88 return controllerIDs.size() - 1;
89}
90
91void ParameterControlMappings::removeMapping (int index)
92{
93 const juce::ScopedLock sl (lock);
94
95 controllerIDs.remove (index);
96 channelIDs.remove (index);
97 parameters.remove (index);
98 parameterFullNames.remove (index);
99
100 tellEditAboutChange();
101}
102
103void ParameterControlMappings::showMappingsEditor (juce::DialogWindow::LaunchOptions& o)
104{
105 listenToRow (-1);
106 checkForDeletedParams();
107
108 #if JUCE_MODAL_LOOPS_PERMITTED
109 o.runModal();
110 #else
112 #endif
113
114 listenToRow (-1);
115 setLearntParam (false);
116}
117
118void ParameterControlMappings::handleAsyncUpdate()
119{
120 if (edit.engine.getMidiLearnState().isActive() && edit.getParameterChangeHandler().isParameterPending())
121 {
122 const MidiLearnState::ScopedChangeCaller changeCaller (edit.engine.getMidiLearnState(), MidiLearnState::added);
123 auto param = edit.getParameterChangeHandler().getPendingParam (true);
124 listeningOnRow = parameters.indexOf (param);
125
126 if (listeningOnRow == -1)
127 listeningOnRow = addMapping (lastControllerID, lastControllerChannel, param);
128
129 setLearntParam (false);
130 tellEditAboutChange();
131 }
132 else if (juce::isPositiveAndBelow (listeningOnRow, controllerIDs.size() + 1))
133 {
134 setLearntParam (true);
135 }
136}
137
138void ParameterControlMappings::sendChange (int controllerID, float newValue, int channel)
139{
140 const juce::ScopedLock sl (lock);
141
142 lastControllerID = controllerID;
143 lastControllerChannel = channel;
144 lastControllerValue = newValue;
145
146 bool learnActive = edit.engine.getMidiLearnState().isActive()
147 && edit.engine.getUIBehaviour().getCurrentlyFocusedEdit() == &edit;
148
149 if (listeningOnRow >= 0 || learnActive)
150 triggerAsyncUpdate();
151
152 if (! learnActive)
153 {
154 for (int index = 0; index < controllerIDs.size(); index++)
155 {
156 if (controllerIDs[index] == controllerID && channelIDs[index] == channel)
157 {
158 if (auto p = parameters.getUnchecked (index))
159 {
160 jassert (Selectable::isSelectableValid (p.get()));
161 p->midiControllerMoved (newValue);
162 }
163 }
164 }
165 }
166}
167
168bool ParameterControlMappings::getParameterMapping (AutomatableParameter& param, int& channel, int& controllerID) const
169{
170 auto index = parameters.indexOf (&param);
171
172 if (index < 0)
173 {
174 channel = controllerID = -1;
175 return false;
176 }
177
178 channel = channelIDs[index];
179 controllerID = controllerIDs[index];
180 return true;
181}
182
183bool ParameterControlMappings::removeParameterMapping (AutomatableParameter& param)
184{
185 auto index = parameters.indexOf (&param);
186
187 if (index < 0)
188 return false;
189
190 removeMapping (index);
191 return true;
192}
193
194//==============================================================================
195void ParameterControlMappings::tellEditAboutChange()
196{
197 SelectionManager::refreshAllPropertyPanels();
198}
199
200void ParameterControlMappings::checkForDeletedParams()
201{
203
204 for (int j = parameters.size(); --j >= 0;)
205 {
206 auto param = parameters[j];
207
208 if ((param != nullptr && param->getReferenceCount() <= 1)
209 || (param == nullptr && parameterFullNames[j].isNotEmpty()))
210 {
211 if (allParams.isEmpty())
212 allParams = edit.getAllAutomatableParams (true);
213
214 param = nullptr;
215
216 for (auto* p : allParams)
217 {
218 if (p->getFullName() == parameterFullNames[j])
219 {
220 jassert (p->getReferenceCount() > 0);
221 param = p;
222 break;
223 }
224 }
225
226 parameters.set (j, param.get());
227 }
228 }
229}
230
231//==============================================================================
232void ParameterControlMappings::loadFromEdit()
233{
234 const juce::ScopedLock sl (lock);
235
236 controllerIDs.clear();
237 channelIDs.clear();
238 parameters.clear();
239 parameterFullNames.clear();
240
241 auto state = edit.state.getChildWithName (IDs::CONTROLLERMAPPINGS);
242
243 if (state.isValid())
244 {
246
247 for (int j = 0; j < state.getNumChildren(); ++j)
248 {
249 auto map = state.getChild(j);
250
251 const int id = map[IDs::id];
252 const int channel = map.getProperty (IDs::channel, 1); // Won't exist pre 2.1.0.4, default to 1
253 auto paramName = map[IDs::param].toString();
254 auto pluginID = EditItemID::fromProperty (map, IDs::pluginID);
255
256 if (id != 0)
257 {
258 if (allParams.isEmpty())
259 allParams = edit.getAllAutomatableParams (true);
260
261 for (auto* p : allParams)
262 {
263 if (p->getFullName() == paramName)
264 {
265 // we want to check the pluginID against id of parent of the parameter
266 // to make sure it gets assigned to the correct plugins parameter
267 // if then pluginID doesn't exists (pre 2.0.1.5) then all we can do
268 // is match by name
269 if (pluginID.isValid()
270 && paramName != TRANS("Master volume")
271 && paramName != TRANS("Master pan")
272 && pluginID != p->getOwnerID())
273 continue;
274
275 jassert (p->getReferenceCount() > 0);
276
277 controllerIDs.add (id);
278 channelIDs.add (channel);
279 parameters.add (p);
280 parameterFullNames.add (paramName);
281 break;
282 }
283 }
284 }
285 }
286 }
287}
288
289void ParameterControlMappings::saveToEdit()
290{
291 const juce::ScopedLock sl (lock);
292
293 checkForDeletedParams();
294
295 auto um = &edit.getUndoManager();
296
297 if (controllerIDs.isEmpty())
298 {
299 auto state = edit.state.getChildWithName (IDs::CONTROLLERMAPPINGS);
300
301 if (state.isValid())
302 edit.state.removeChild (state, um);
303 }
304 else
305 {
306 auto state = edit.state.getOrCreateChildWithName (IDs::CONTROLLERMAPPINGS, um);
307
308 state.removeAllChildren (um);
309 state.removeAllProperties (um);
310
311 for (int i = 0; i < controllerIDs.size(); ++i)
312 {
313 if (parameters[i] != nullptr && controllerIDs[i] != 0)
314 {
315 auto e = createValueTree (IDs::MAP,
316 IDs::id, controllerIDs[i],
317 IDs::channel, channelIDs[i],
318 IDs::param, parameters[i]->getFullName(),
319 IDs::pluginID, parameters[i]->getOwnerID());
320
321 state.addChild (e, -1, um);
322 }
323 }
324 }
325}
326
327void ParameterControlMappings::addPluginToMenu (Plugin::Ptr plugin, juce::PopupMenu& menu,
328 juce::Array<ParameterAndIndex>& allParams, int& index, int addAllItemIndex)
329{
330 AutomatableParameterTree::TreeNode* node = plugin->getParameterTree().rootNode.get();
331 addPluginToMenu (node, menu, allParams, index, addAllItemIndex);
332}
333
334void ParameterControlMappings::addPluginToMenu (AutomatableParameterTree::TreeNode* node, juce::PopupMenu& menu,
335 juce::Array<ParameterAndIndex>& allParams, int& index, int addAllItemIndex)
336{
337 for (auto subNode : node->subNodes)
338 {
339 if (subNode->type == AutomatableParameterTree::Parameter)
340 {
341 AutomatableParameter::Ptr autoParam = subNode->parameter;
342 index++;
343
344 if (autoParam->isParameterActive())
345 {
346 allParams.add ({ autoParam.get(), index });
347
348 if (addAllItemIndex)
349 allParams.add ({ autoParam.get(), addAllItemIndex });
350 }
351
352 menu.addItem (index, autoParam->getParameterName().fromLastOccurrenceOf (" >> ", false, false),
353 autoParam->isParameterActive());
354
355 }
356
357 if (subNode->type == AutomatableParameterTree::Group)
358 {
359 juce::PopupMenu subMenu;
360
361 int itemID = ++index;
362 subMenu.addItem (itemID, TRANS("Add all parameters"));
363 subMenu.addSeparator();
364
365 addPluginToMenu (subNode, subMenu, allParams, index, itemID);
366
367 removeAddAllCommandIfTooManyItems (subMenu);
368
369 // only add the submenu if something is in it, not counting the add all parameters
370 if (subMenu.getNumItems() > 1)
371 menu.addSubMenu (subNode->getGroupName(), subMenu);
372 }
373 }
374}
375
376juce::PopupMenu ParameterControlMappings::buildMenu (juce::Array<ParameterAndIndex>& allParams)
377{
379
380 juce::StringArray presets (getPresets());
381
382 juce::PopupMenu loadPresets, deletePresets;
383 addSortedListToMenu (loadPresets, presets, 60000);
384 addSortedListToMenu (deletePresets, presets, 70000);
385
387 m.addSubMenu (TRANS("Load presets"), loadPresets, loadPresets.getNumItems() > 0);
388
389 // save preset
390 juce::Array<Plugin*> plugins;
391
392 for (auto param : parameters)
393 if (param != nullptr)
394 if (auto p = param->getPlugin())
395 plugins.addIfNotAlreadyThere (p);
396
397 juce::StringArray pluginNames;
398
399 for (auto f : plugins)
400 {
401 if (auto t = f->getOwnerTrack())
402 pluginNames.add (t->getName() + " >> " + f->getName());
403 else
404 pluginNames.add (f->getName());
405 }
406
407 pluginNames.sort (true);
408
409 juce::PopupMenu savePresets;
410 addSortedListToMenu (savePresets, pluginNames, 50000);
411
412 m.addSubMenu (TRANS("Save preset"), savePresets, savePresets.getNumItems() > 0);
413 m.addSubMenu (TRANS("Delete presets"), deletePresets, deletePresets.getNumItems() > 0);
414
415 m.addSeparator();
416
417 // Build the menu for the main parameters
418 juce::PopupMenu masterPluginsSubMenu;
419
420 int index = 0;
421
422 if (auto volume = edit.getMasterVolumePlugin())
423 addPluginToMenu (volume, masterPluginsSubMenu, allParams, index, 0);
424
425 for (auto plugin : edit.getMasterPluginList())
426 {
427 juce::PopupMenu pluginSubMenu;
428
429 int addAllItemIndex = ++index;
430 pluginSubMenu.addItem (addAllItemIndex, TRANS("Add all parameters"));
431 pluginSubMenu.addSeparator();
432
433 addPluginToMenu (plugin, pluginSubMenu, allParams, index, addAllItemIndex);
434 removeAddAllCommandIfTooManyItems (pluginSubMenu);
435
436 // only add the submenu if something is in it, not counting the add all parameters
437 if (pluginSubMenu.getNumItems() > 1)
438 masterPluginsSubMenu.addSubMenu (plugin->getName(), pluginSubMenu);
439 }
440
441 if (masterPluginsSubMenu.getNumItems())
442 m.addSubMenu (TRANS("Master Plugins"), masterPluginsSubMenu);
443
444 // Build the menu for the rack parameters
445 auto& rackTypes = edit.getRackList();
446
447 if (rackTypes.size() > 0)
448 {
449 juce::PopupMenu racksSubMenu;
450
451 for (int i = 0; i < rackTypes.size(); ++i)
452 {
453 juce::PopupMenu rackSubMenu;
454 auto rackType = rackTypes.getRackType(i);
455
456 for (auto plugin : rackType->getPlugins())
457 {
458 juce::PopupMenu pluginSubMenu;
459
460 int addAllItemIndex = ++index;
461 pluginSubMenu.addItem (addAllItemIndex, TRANS("Add all parameters"));
462 pluginSubMenu.addSeparator();
463
464 addPluginToMenu (plugin, pluginSubMenu, allParams, index, addAllItemIndex);
465 removeAddAllCommandIfTooManyItems (pluginSubMenu);
466
467 // only add the submenu if something is in it, not counting the add all parameters
468 if (pluginSubMenu.getNumItems() > 1)
469 rackSubMenu.addSubMenu (plugin->getName(), pluginSubMenu);
470 }
471
472 if (rackSubMenu.getNumItems() > 0)
473 racksSubMenu.addSubMenu (rackType->rackName, rackSubMenu);
474 }
475
476 if (racksSubMenu.getNumItems())
477 m.addSubMenu (TRANS("Plugin Racks"), racksSubMenu);
478 }
479
480 // Build the menu for each track
481 for (auto track : getAllTracks (edit))
482 {
483 juce::PopupMenu tracksSubMenu;
484
485 // Build a menu for each plugin in the plugin panel
486 for (auto plugin : track->pluginList)
487 {
488 juce::PopupMenu pluginSubMenu;
489
490 int addAllItemIndex = ++index;
491 pluginSubMenu.addItem (addAllItemIndex, TRANS("Add all parameters"));
492 pluginSubMenu.addSeparator();
493
494 addPluginToMenu (plugin, pluginSubMenu, allParams, index, addAllItemIndex);
495 removeAddAllCommandIfTooManyItems (pluginSubMenu);
496
497 // only add the submenu if something is in it, not counting the add all parameters
498 if (pluginSubMenu.getNumItems() > 1)
499 tracksSubMenu.addSubMenu (plugin->getName(), pluginSubMenu);
500 }
501
502 // Build a menu for each plugin on a clip
503 for (int j = 0; j < track->getNumTrackItems(); j++)
504 {
505 if (auto clip = dynamic_cast<Clip*> (track->getTrackItem(j)))
506 {
507 if (auto pluginList = clip->getPluginList())
508 {
509 for (auto plugin : *pluginList)
510 {
511 juce::PopupMenu pluginSubMenu;
512
513 int addAllItemIndex = ++index;
514 pluginSubMenu.addItem (addAllItemIndex, TRANS("Add all parameters"));
515 pluginSubMenu.addSeparator();
516
517 // add all the parameters to the submenu.
518 addPluginToMenu (plugin, pluginSubMenu, allParams, index, addAllItemIndex);
519 removeAddAllCommandIfTooManyItems (pluginSubMenu);
520
521 // only add the submenu if something is in it, not counting the add all parameters
522 if (pluginSubMenu.getNumItems() > 1)
523 tracksSubMenu.addSubMenu (plugin->getName(), pluginSubMenu);
524 }
525 }
526 }
527 }
528
529 if (tracksSubMenu.getNumItems())
530 m.addSubMenu (track->getName(), tracksSubMenu);
531 }
532
533 return m;
534}
535
536void ParameterControlMappings::showMappingsListForRow (int row)
537{
540 auto m = buildMenu (allParams);
541
542 #if JUCE_MODAL_LOOPS_PERMITTED
543 const int r = m.show();
544 #else
545 const int r = 0;
546 #endif
547
548 if (r >= 50000 && r < 51000)
549 {
550 savePreset (r - 50000);
551 }
552 else if (r >= 60000 && r < 61000)
553 {
554 loadPreset (r - 60000);
555 }
556 else if (r >= 70000 && r < 71000)
557 {
558 deletePreset (r - 70000);
559 }
560 else if (r != 0)
561 {
562 for (const auto& pair : allParams)
563 {
564 if (pair.index == r && pair.param != nullptr)
565 {
566 while (row >= controllerIDs.size())
567 {
568 controllerIDs.add (0);
569 channelIDs.add (0);
570 parameters.add (nullptr);
571 parameterFullNames.add ({});
572 }
573
574 parameters.set (row, pair.param);
575 parameterFullNames.set (row, pair.param->getFullName());
576
577 ++row;
578 }
579 }
580
581 tellEditAboutChange();
582 sendChangeMessage();
583 }
584}
585
586void ParameterControlMappings::listenToRow (int row)
587{
588 listeningOnRow = row;
589 lastControllerID = 0;
590 lastControllerChannel = 0;
591
592 sendChangeMessage();
593}
594
595int ParameterControlMappings::getRowBeingListenedTo() const
596{
597 return listeningOnRow;
598}
599
600std::pair<juce::String, juce::String> ParameterControlMappings::getTextForRow (int rowNumber) const
601{
602 auto getLeftText = [&] () -> juce::String
603 {
604 if (rowNumber == listeningOnRow)
605 {
606 if (lastControllerID > 0)
607 return controllerIDToString (lastControllerID, lastControllerChannel)
608 + ": " + juce::String (juce::roundToInt (lastControllerValue * 100.0f)) + "%";
609
610 return "(" + TRANS("Move a MIDI controller") + ")";
611 }
612
613 if (controllerIDs[rowNumber] != 0)
614 return controllerIDToString (controllerIDs[rowNumber], channelIDs[rowNumber]);
615
616 return TRANS("Click here to choose a controller");
617 };
618
619 auto getRightText = [&] () -> juce::String
620 {
621 if (auto p = parameters[rowNumber])
622 return p->getFullName();
623
624 return TRANS("Choose Parameter") + "...";
625 };
626
627 return { getLeftText(), getRightText() };
628}
629
630ParameterControlMappings::Mapping ParameterControlMappings::getMappingForRow (int row) const
631{
632 return { parameters[row].get(), controllerIDs[row], channelIDs[row] };
633}
634
635void ParameterControlMappings::setLearntParam (bool keepListening)
636{
637 if (listeningOnRow >= 0)
638 {
640
641 if (lastControllerID > 0)
642 {
643 if (listeningOnRow >= controllerIDs.size())
644 {
645 controllerIDs.add ({});
646 channelIDs.add ({});
647 parameters.add ({});
648 parameterFullNames.add ({});
649 }
650
651 controllerIDs.set (listeningOnRow, lastControllerID);
652 channelIDs.set (listeningOnRow, lastControllerChannel);
653
654 tellEditAboutChange();
655 }
656
657 if (! keepListening)
658 {
659 listeningOnRow = -1;
660 lastControllerID = 0;
661 lastControllerChannel = 0;
662 }
663
664 sendChangeMessage();
665 }
666}
667
668void ParameterControlMappings::savePreset (int index)
669{
670 juce::Array<Plugin*> plugins;
671
672 for (auto param : parameters)
673 if (param != nullptr)
674 if (auto p = param->getPlugin())
675 plugins.addIfNotAlreadyThere (p);
676
677 juce::StringArray pluginNames;
678
679 for (int i = 0; i < plugins.size(); ++i)
680 {
681 if (auto* f = plugins.getUnchecked(i))
682 {
683 auto name = f->getName() + "|" + juce::String (i);
684
685 if (auto t = f->getOwnerTrack())
686 name = t->getName() + " >> " + name;
687
688 pluginNames.add (name);
689 }
690 }
691
692 pluginNames.sort (true);
693
694 #if JUCE_MODAL_LOOPS_PERMITTED
695 auto plugin = plugins[pluginNames[index].getTrailingIntValue()];
696
698 .createAlertWindow (TRANS("Plugin mapping preset"),
699 TRANS("Create a new plugin mapping preset"),
700 {}, {}, {}, juce::AlertWindow::QuestionIcon, 0, nullptr));
701
702 w->addTextEditor ("setName", plugin->getName(), TRANS("Name:"));
703 w->addButton (TRANS("OK"), 1, juce::KeyPress (juce::KeyPress::returnKey));
704 w->addButton (TRANS("Cancel"), 0, juce::KeyPress (juce::KeyPress::escapeKey));
705
706 int res = w->runModalLoop();
707 auto name = w->getTextEditorContents ("setName");
708
709 if (res == 0 || name.trim().isEmpty())
710 return;
711 #else
712 juce::ignoreUnused (index);
713 return;
714 #endif
715
716 #if JUCE_MODAL_LOOPS_PERMITTED
717 auto xml = new juce::XmlElement ("filter");
718 xml->setAttribute ("name", name);
719 xml->setAttribute ("filter", plugin->getName());
720
721 for (int i = 0; i < parameters.size(); ++i)
722 {
723 if (auto p = parameters[i])
724 {
725 if (p->getPlugin() == plugin)
726 {
727 auto mapping = new juce::XmlElement ("mapping");
728 mapping->setAttribute ("controller", controllerIDs[i]);
729 mapping->setAttribute ("channel", channelIDs[i]);
730 mapping->setAttribute ("parameter", p->paramID);
731 xml->addChildElement (mapping);
732 }
733 }
734 }
735
736 juce::XmlElement xmlNew ("FILTERMAPPINGPRESETS");
737 xmlNew.addChildElement (xml);
738
739 if (auto xmlOld = edit.engine.getPropertyStorage().getXmlProperty (SettingID::filterControlMappingPresets))
740 for (auto n : xmlOld->getChildIterator())
741 if (n->getStringAttribute ("name") != name)
742 xmlNew.addChildElement (new juce::XmlElement (*n));
743
744 edit.engine.getPropertyStorage().setXmlProperty (SettingID::filterControlMappingPresets, xmlNew);
745 #endif
746}
747
748void ParameterControlMappings::loadPreset (int index)
749{
750 if (auto xml = edit.engine.getPropertyStorage().getXmlProperty (SettingID::filterControlMappingPresets))
751 {
752 if (auto mapping = xml->getChildElement (index))
753 {
754 Plugin::Array matchingPlugins;
755
756 {
757 auto plugin = mapping->getStringAttribute ("filter");
758
759 for (auto p : getAllPlugins (edit, true))
760 if (plugin == p->getName())
761 matchingPlugins.add (p);
762 }
763
764 if (matchingPlugins.isEmpty())
765 {
766 edit.engine.getUIBehaviour().showWarningAlert (TRANS("Not found"),
767 TRANS("The plugin was not found"));
768 }
769 else
770 {
771 auto plugin = matchingPlugins.getFirst();
772
773 if (matchingPlugins.size() > 1)
774 {
775 #if JUCE_MODAL_LOOPS_PERMITTED
777 cb.setSize (200,20);
778
779 for (int i = 0; i < matchingPlugins.size(); ++i)
780 if (auto p = matchingPlugins.getUnchecked (i))
781 cb.addItem (p->getOwnerTrack() ? p->getOwnerTrack()->getName() + " >> " + p->getName()
782 : p->getName(),
783 i + 1);
784
785 cb.setSelectedId(1);
786
788 .createAlertWindow (TRANS("Select plugin"),
789 TRANS("Select plugin to apply preset to:"),
790 {}, {}, {},
791 juce::AlertWindow::QuestionIcon, 0, nullptr));
792
793 w->addCustomComponent (&cb);
794 w->addButton (TRANS("OK"), 1, juce::KeyPress (juce::KeyPress::returnKey));
795 w->addButton (TRANS("Cancel"), 0, juce::KeyPress (juce::KeyPress::escapeKey));
796
797 int res = w->runModalLoop();
798
799 if (res == 1)
800 plugin = matchingPlugins[cb.getSelectedId() - 1];
801 else
802 plugin = nullptr;
803 #else
804 plugin = nullptr;
805 #endif
806 }
807
808 if (plugin != nullptr)
809 {
810 for (auto item : mapping->getChildIterator())
811 {
812 controllerIDs.add (item->getStringAttribute ("controller").getIntValue());
813 channelIDs.add (item->getStringAttribute ("channel").getIntValue());
814 parameters.add (plugin->getAutomatableParameterByID (item->getStringAttribute ("parameter")));
815 }
816
817 tellEditAboutChange();
818 sendChangeMessage();
819 }
820 }
821 }
822 }
823}
824
825juce::StringArray ParameterControlMappings::getPresets() const
826{
827 juce::StringArray result;
828
829 if (auto xml = edit.engine.getPropertyStorage().getXmlProperty (SettingID::filterControlMappingPresets))
830 for (auto e : xml->getChildIterator())
831 result.add (e->getStringAttribute ("name"));
832
833 return result;
834}
835
836void ParameterControlMappings::deletePreset (int index)
837{
838 if (auto xml = edit.engine.getPropertyStorage().getXmlProperty (SettingID::filterControlMappingPresets))
839 {
840 juce::XmlElement xmlCopy (*xml);
841 xmlCopy.removeChildElement (xmlCopy.getChildElement (index), true);
842 edit.engine.getPropertyStorage().setXmlProperty (SettingID::filterControlMappingPresets, xmlCopy);
843 }
844}
845
846}} // namespace tracktion { inline namespace engine
ElementType getUnchecked(int index) const
bool isEmpty() const noexcept
int size() const noexcept
void add(const ElementType &newElement)
void setSelectedId(int newItemId, NotificationType notification=sendNotificationAsync)
int getSelectedId() const noexcept
void addItem(const String &newItemText, int newItemId)
void setSize(int newWidth, int newHeight)
bool isValid() const noexcept
const String & toString() const noexcept
static const int escapeKey
static const int returnKey
static LookAndFeel & getDefaultLookAndFeel() noexcept
static const char * getControllerName(int controllerNumber)
int getNumItems() const noexcept
void addSubMenu(String subMenuName, PopupMenu subMenu, bool isEnabled=true)
void addItem(Item newItem)
void sort(bool ignoreCase)
void add(String stringToAdd)
#define TRANS(stringLiteral)
#define jassert(expression)
void ignoreUnused(Types &&...) noexcept
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
int roundToInt(const FloatType value) noexcept
juce::String getName(LaunchQType t)
Retuns the name of a LaunchQType for display purposes.
juce::Array< Track * > getAllTracks(const Edit &edit)
Returns all the tracks in an Edit.
Plugin::Array getAllPlugins(const Edit &edit, bool includeMasterVolume)
Returns all the plugins in a given Edit.
#define CRASH_TRACER
This macro adds the current location to a stack which gets logged if a crash happens.