11namespace tracktion {
inline namespace engine
14static juce::String controllerIDToString (
int id,
int channelid)
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;
26 if (name.isNotEmpty())
27 name =
" (" + name +
")";
52ParameterControlMappings::ParameterControlMappings (Edit& ed) : edit (ed)
56ParameterControlMappings::~ParameterControlMappings()
60ParameterControlMappings* ParameterControlMappings::getCurrentlyFocusedMappings (
Engine& engine)
62 if (
auto ed = engine.getUIBehaviour().getLastFocusedEdit())
63 return &ed->getParameterControlMappings();
69int ParameterControlMappings::addMapping (
int controllerID,
int channel,
const AutomatableParameter::Ptr& param)
73 for (
int i = controllerIDs.size(); --i >= 0;)
75 if (controllerIDs.getUnchecked (i) == controllerID
76 && channelIDs[i] == channel
77 && parameters[i] == param)
83 controllerIDs.add (controllerID);
84 channelIDs.add (channel);
85 parameters.add (param);
86 parameterFullNames.add (param->getFullName());
88 return controllerIDs.size() - 1;
91void ParameterControlMappings::removeMapping (
int index)
95 controllerIDs.remove (index);
96 channelIDs.remove (index);
97 parameters.remove (index);
98 parameterFullNames.remove (index);
100 tellEditAboutChange();
106 checkForDeletedParams();
108 #if JUCE_MODAL_LOOPS_PERMITTED
115 setLearntParam (
false);
118void ParameterControlMappings::handleAsyncUpdate()
120 if (edit.engine.getMidiLearnState().isActive() && edit.getParameterChangeHandler().isParameterPending())
123 auto param = edit.getParameterChangeHandler().getPendingParam (
true);
124 listeningOnRow = parameters.indexOf (param);
126 if (listeningOnRow == -1)
127 listeningOnRow = addMapping (lastControllerID, lastControllerChannel, param);
129 setLearntParam (
false);
130 tellEditAboutChange();
134 setLearntParam (
true);
138void ParameterControlMappings::sendChange (
int controllerID,
float newValue,
int channel)
142 lastControllerID = controllerID;
143 lastControllerChannel = channel;
144 lastControllerValue = newValue;
146 bool learnActive = edit.engine.getMidiLearnState().isActive()
147 && edit.engine.getUIBehaviour().getCurrentlyFocusedEdit() == &edit;
149 if (listeningOnRow >= 0 || learnActive)
150 triggerAsyncUpdate();
154 for (
int index = 0; index < controllerIDs.size(); index++)
156 if (controllerIDs[index] == controllerID && channelIDs[index] == channel)
158 if (
auto p = parameters.getUnchecked (index))
160 jassert (Selectable::isSelectableValid (p.get()));
161 p->midiControllerMoved (newValue);
168bool ParameterControlMappings::getParameterMapping (
AutomatableParameter& param,
int& channel,
int& controllerID)
const
170 auto index = parameters.indexOf (¶m);
174 channel = controllerID = -1;
178 channel = channelIDs[index];
179 controllerID = controllerIDs[index];
183bool ParameterControlMappings::removeParameterMapping (AutomatableParameter& param)
185 auto index = parameters.indexOf (¶m);
190 removeMapping (index);
195void ParameterControlMappings::tellEditAboutChange()
197 SelectionManager::refreshAllPropertyPanels();
200void ParameterControlMappings::checkForDeletedParams()
204 for (
int j = parameters.size(); --j >= 0;)
206 auto param = parameters[j];
208 if ((param !=
nullptr && param->getReferenceCount() <= 1)
209 || (param ==
nullptr && parameterFullNames[j].isNotEmpty()))
212 allParams = edit.getAllAutomatableParams (
true);
216 for (
auto* p : allParams)
218 if (p->getFullName() == parameterFullNames[j])
220 jassert (p->getReferenceCount() > 0);
226 parameters.set (j, param.get());
232void ParameterControlMappings::loadFromEdit()
236 controllerIDs.clear();
239 parameterFullNames.clear();
241 auto state = edit.state.getChildWithName (IDs::CONTROLLERMAPPINGS);
247 for (
int j = 0; j < state.getNumChildren(); ++j)
249 auto map = state.getChild(j);
251 const int id = map[IDs::id];
252 const int channel = map.getProperty (IDs::channel, 1);
253 auto paramName = map[IDs::param].
toString();
254 auto pluginID = EditItemID::fromProperty (map, IDs::pluginID);
259 allParams = edit.getAllAutomatableParams (
true);
261 for (
auto* p : allParams)
263 if (p->getFullName() == paramName)
270 && paramName !=
TRANS(
"Master volume")
271 && paramName !=
TRANS(
"Master pan")
272 && pluginID != p->getOwnerID())
275 jassert (p->getReferenceCount() > 0);
277 controllerIDs.add (
id);
278 channelIDs.add (channel);
280 parameterFullNames.add (paramName);
289void ParameterControlMappings::saveToEdit()
293 checkForDeletedParams();
295 auto um = &edit.getUndoManager();
297 if (controllerIDs.isEmpty())
299 auto state = edit.state.getChildWithName (IDs::CONTROLLERMAPPINGS);
302 edit.state.removeChild (state, um);
306 auto state = edit.state.getOrCreateChildWithName (IDs::CONTROLLERMAPPINGS, um);
308 state.removeAllChildren (um);
309 state.removeAllProperties (um);
311 for (
int i = 0; i < controllerIDs.size(); ++i)
313 if (parameters[i] !=
nullptr && controllerIDs[i] != 0)
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());
321 state.addChild (e, -1, um);
327void ParameterControlMappings::addPluginToMenu (Plugin::Ptr plugin,
juce::PopupMenu& menu,
330 AutomatableParameterTree::TreeNode* node = plugin->getParameterTree().rootNode.get();
331 addPluginToMenu (node, menu, allParams, index, addAllItemIndex);
334void ParameterControlMappings::addPluginToMenu (AutomatableParameterTree::TreeNode* node,
juce::PopupMenu& menu,
337 for (
auto subNode : node->subNodes)
339 if (subNode->type == AutomatableParameterTree::Parameter)
341 AutomatableParameter::Ptr autoParam = subNode->parameter;
344 if (autoParam->isParameterActive())
346 allParams.
add ({ autoParam.get(), index });
349 allParams.
add ({ autoParam.get(), addAllItemIndex });
352 menu.
addItem (index, autoParam->getParameterName().fromLastOccurrenceOf (
" >> ",
false,
false),
353 autoParam->isParameterActive());
357 if (subNode->type == AutomatableParameterTree::Group)
361 int itemID = ++index;
365 addPluginToMenu (subNode, subMenu, allParams, index, itemID);
367 removeAddAllCommandIfTooManyItems (subMenu);
371 menu.
addSubMenu (subNode->getGroupName(), subMenu);
383 addSortedListToMenu (loadPresets, presets, 60000);
384 addSortedListToMenu (deletePresets, presets, 70000);
387 m.addSubMenu (
TRANS(
"Load presets"), loadPresets, loadPresets.
getNumItems() > 0);
392 for (
auto param : parameters)
393 if (param != nullptr)
394 if (auto p = param->getPlugin())
395 plugins.addIfNotAlreadyThere (p);
399 for (
auto f : plugins)
401 if (
auto t = f->getOwnerTrack())
402 pluginNames.
add (t->getName() +
" >> " + f->getName());
404 pluginNames.
add (f->getName());
407 pluginNames.
sort (
true);
410 addSortedListToMenu (savePresets, pluginNames, 50000);
412 m.addSubMenu (
TRANS(
"Save preset"), savePresets, savePresets.
getNumItems() > 0);
413 m.addSubMenu (
TRANS(
"Delete presets"), deletePresets, deletePresets.
getNumItems() > 0);
422 if (
auto volume = edit.getMasterVolumePlugin())
423 addPluginToMenu (volume, masterPluginsSubMenu, allParams, index, 0);
425 for (
auto plugin : edit.getMasterPluginList())
429 int addAllItemIndex = ++index;
430 pluginSubMenu.
addItem (addAllItemIndex,
TRANS(
"Add all parameters"));
433 addPluginToMenu (plugin, pluginSubMenu, allParams, index, addAllItemIndex);
434 removeAddAllCommandIfTooManyItems (pluginSubMenu);
438 masterPluginsSubMenu.
addSubMenu (plugin->getName(), pluginSubMenu);
442 m.addSubMenu (
TRANS(
"Master Plugins"), masterPluginsSubMenu);
445 auto& rackTypes = edit.getRackList();
447 if (rackTypes.size() > 0)
451 for (
int i = 0; i < rackTypes.size(); ++i)
454 auto rackType = rackTypes.getRackType(i);
456 for (
auto plugin : rackType->getPlugins())
460 int addAllItemIndex = ++index;
461 pluginSubMenu.
addItem (addAllItemIndex,
TRANS(
"Add all parameters"));
464 addPluginToMenu (plugin, pluginSubMenu, allParams, index, addAllItemIndex);
465 removeAddAllCommandIfTooManyItems (pluginSubMenu);
469 rackSubMenu.
addSubMenu (plugin->getName(), pluginSubMenu);
473 racksSubMenu.
addSubMenu (rackType->rackName, rackSubMenu);
477 m.addSubMenu (
TRANS(
"Plugin Racks"), racksSubMenu);
486 for (
auto plugin : track->pluginList)
490 int addAllItemIndex = ++index;
491 pluginSubMenu.
addItem (addAllItemIndex,
TRANS(
"Add all parameters"));
494 addPluginToMenu (plugin, pluginSubMenu, allParams, index, addAllItemIndex);
495 removeAddAllCommandIfTooManyItems (pluginSubMenu);
499 tracksSubMenu.
addSubMenu (plugin->getName(), pluginSubMenu);
503 for (
int j = 0; j < track->getNumTrackItems(); j++)
505 if (
auto clip =
dynamic_cast<Clip*
> (track->getTrackItem(j)))
507 if (
auto pluginList = clip->getPluginList())
509 for (
auto plugin : *pluginList)
513 int addAllItemIndex = ++index;
514 pluginSubMenu.
addItem (addAllItemIndex,
TRANS(
"Add all parameters"));
518 addPluginToMenu (plugin, pluginSubMenu, allParams, index, addAllItemIndex);
519 removeAddAllCommandIfTooManyItems (pluginSubMenu);
523 tracksSubMenu.
addSubMenu (plugin->getName(), pluginSubMenu);
530 m.addSubMenu (track->getName(), tracksSubMenu);
536void ParameterControlMappings::showMappingsListForRow (
int row)
540 auto m = buildMenu (allParams);
542 #if JUCE_MODAL_LOOPS_PERMITTED
543 const int r = m.show();
548 if (r >= 50000 && r < 51000)
550 savePreset (r - 50000);
552 else if (r >= 60000 && r < 61000)
554 loadPreset (r - 60000);
556 else if (r >= 70000 && r < 71000)
558 deletePreset (r - 70000);
562 for (
const auto& pair : allParams)
564 if (pair.index == r && pair.param !=
nullptr)
566 while (row >= controllerIDs.size())
568 controllerIDs.add (0);
570 parameters.add (
nullptr);
571 parameterFullNames.add ({});
574 parameters.set (row, pair.param);
575 parameterFullNames.set (row, pair.param->getFullName());
581 tellEditAboutChange();
586void ParameterControlMappings::listenToRow (
int row)
588 listeningOnRow = row;
589 lastControllerID = 0;
590 lastControllerChannel = 0;
595int ParameterControlMappings::getRowBeingListenedTo()
const
597 return listeningOnRow;
604 if (rowNumber == listeningOnRow)
606 if (lastControllerID > 0)
607 return controllerIDToString (lastControllerID, lastControllerChannel)
610 return "(" +
TRANS(
"Move a MIDI controller") +
")";
613 if (controllerIDs[rowNumber] != 0)
614 return controllerIDToString (controllerIDs[rowNumber], channelIDs[rowNumber]);
616 return TRANS(
"Click here to choose a controller");
621 if (
auto p = parameters[rowNumber])
622 return p->getFullName();
624 return TRANS(
"Choose Parameter") +
"...";
627 return { getLeftText(), getRightText() };
630ParameterControlMappings::Mapping ParameterControlMappings::getMappingForRow (
int row)
const
632 return { parameters[row].get(), controllerIDs[row], channelIDs[row] };
635void ParameterControlMappings::setLearntParam (
bool keepListening)
637 if (listeningOnRow >= 0)
641 if (lastControllerID > 0)
643 if (listeningOnRow >= controllerIDs.size())
645 controllerIDs.add ({});
648 parameterFullNames.add ({});
651 controllerIDs.set (listeningOnRow, lastControllerID);
652 channelIDs.set (listeningOnRow, lastControllerChannel);
654 tellEditAboutChange();
660 lastControllerID = 0;
661 lastControllerChannel = 0;
668void ParameterControlMappings::savePreset (
int index)
672 for (
auto param : parameters)
673 if (param != nullptr)
674 if (auto p = param->getPlugin())
675 plugins.addIfNotAlreadyThere (p);
679 for (
int i = 0; i < plugins.
size(); ++i)
685 if (
auto t = f->getOwnerTrack())
686 name = t->getName() +
" >> " + name;
688 pluginNames.
add (name);
692 pluginNames.
sort (
true);
694 #if JUCE_MODAL_LOOPS_PERMITTED
695 auto plugin = plugins[pluginNames[index].getTrailingIntValue()];
698 .createAlertWindow (
TRANS(
"Plugin mapping preset"),
699 TRANS(
"Create a new plugin mapping preset"),
700 {}, {}, {}, juce::AlertWindow::QuestionIcon, 0,
nullptr));
702 w->addTextEditor (
"setName", plugin->getName(),
TRANS(
"Name:"));
706 int res = w->runModalLoop();
707 auto name = w->getTextEditorContents (
"setName");
709 if (res == 0 || name.trim().isEmpty())
716 #if JUCE_MODAL_LOOPS_PERMITTED
718 xml->setAttribute (
"name", name);
719 xml->setAttribute (
"filter", plugin->getName());
721 for (
int i = 0; i < parameters.size(); ++i)
723 if (
auto p = parameters[i])
725 if (p->getPlugin() == plugin)
728 mapping->setAttribute (
"controller", controllerIDs[i]);
729 mapping->setAttribute (
"channel", channelIDs[i]);
730 mapping->setAttribute (
"parameter", p->paramID);
731 xml->addChildElement (mapping);
737 xmlNew.addChildElement (xml);
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));
744 edit.engine.getPropertyStorage().setXmlProperty (SettingID::filterControlMappingPresets, xmlNew);
748void ParameterControlMappings::loadPreset (
int index)
750 if (
auto xml = edit.engine.getPropertyStorage().getXmlProperty (SettingID::filterControlMappingPresets))
752 if (
auto mapping = xml->getChildElement (index))
754 Plugin::Array matchingPlugins;
757 auto plugin = mapping->getStringAttribute (
"filter");
761 matchingPlugins.add (p);
764 if (matchingPlugins.isEmpty())
766 edit.engine.getUIBehaviour().showWarningAlert (
TRANS(
"Not found"),
767 TRANS(
"The plugin was not found"));
771 auto plugin = matchingPlugins.getFirst();
773 if (matchingPlugins.size() > 1)
775 #if JUCE_MODAL_LOOPS_PERMITTED
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()
788 .createAlertWindow (
TRANS(
"Select plugin"),
789 TRANS(
"Select plugin to apply preset to:"),
791 juce::AlertWindow::QuestionIcon, 0,
nullptr));
793 w->addCustomComponent (&cb);
797 int res = w->runModalLoop();
808 if (plugin !=
nullptr)
810 for (
auto item : mapping->getChildIterator())
812 controllerIDs.add (item->getStringAttribute (
"controller").getIntValue());
813 channelIDs.add (item->getStringAttribute (
"channel").getIntValue());
814 parameters.add (plugin->getAutomatableParameterByID (item->getStringAttribute (
"parameter")));
817 tellEditAboutChange();
829 if (
auto xml = edit.engine.getPropertyStorage().getXmlProperty (SettingID::filterControlMappingPresets))
830 for (
auto e : xml->getChildIterator())
831 result.add (e->getStringAttribute (
"name"));
836void ParameterControlMappings::deletePreset (
int index)
838 if (
auto xml = edit.engine.getPropertyStorage().getXmlProperty (SettingID::filterControlMappingPresets))
841 xmlCopy.removeChildElement (xmlCopy.getChildElement (index),
true);
842 edit.engine.getPropertyStorage().setXmlProperty (SettingID::filterControlMappingPresets, xmlCopy);
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)
void sort(bool ignoreCase)
void add(String stringToAdd)
#define TRANS(stringLiteral)
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.