JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_KnownPluginList.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28
31
33{
34 ScopedLock lock (typesArrayLock);
35
36 if (! types.isEmpty())
37 {
38 types.clear();
40 }
41}
42
44{
45 ScopedLock lock (typesArrayLock);
46 return types.size();
47}
48
50{
51 ScopedLock lock (typesArrayLock);
52 return types;
53}
54
56{
58
59 for (auto& d : getTypes())
60 if (d.pluginFormatName == format.getName())
61 result.add (d);
62
63 return result;
64}
65
67{
68 ScopedLock lock (typesArrayLock);
69
70 for (auto& desc : types)
71 if (desc.fileOrIdentifier == fileOrIdentifier)
72 return std::make_unique<PluginDescription> (desc);
73
74 return {};
75}
76
78{
79 ScopedLock lock (typesArrayLock);
80
81 for (auto& desc : types)
82 if (desc.matchesIdentifierString (identifierString))
83 return std::make_unique<PluginDescription> (desc);
84
85 return {};
86}
87
89{
90 {
91 ScopedLock lock (typesArrayLock);
92
93 for (auto& desc : types)
94 {
95 if (desc.isDuplicateOf (type))
96 {
97 // strange - found a duplicate plugin with different info..
98 jassert (desc.name == type.name);
99 jassert (desc.isInstrument == type.isInstrument);
100
101 desc = type;
102 return false;
103 }
104 }
105
106 types.insert (0, type);
107 }
108
110 return true;
111}
112
114{
115 {
116 ScopedLock lock (typesArrayLock);
117
118 for (int i = types.size(); --i >= 0;)
119 if (types.getUnchecked (i).isDuplicateOf (type))
120 types.remove (i);
121 }
122
124}
125
126bool KnownPluginList::isListingUpToDate (const String& fileOrIdentifier,
128{
129 if (getTypeForFile (fileOrIdentifier) == nullptr)
130 return false;
131
132 ScopedLock lock (typesArrayLock);
133
134 for (auto& d : types)
135 if (d.fileOrIdentifier == fileOrIdentifier && formatToUse.pluginNeedsRescanning (d))
136 return false;
137
138 return true;
139}
140
142{
143 if (scanner != newScanner)
144 scanner = std::move (newScanner);
145}
146
147bool KnownPluginList::scanAndAddFile (const String& fileOrIdentifier,
148 const bool dontRescanIfAlreadyInList,
150 AudioPluginFormat& format)
151{
152 const ScopedLock sl (scanLock);
153
155 && getTypeForFile (fileOrIdentifier) != nullptr)
156 {
157 bool needsRescanning = false;
158
159 ScopedLock lock (typesArrayLock);
160
161 for (auto& d : types)
162 {
163 if (d.fileOrIdentifier == fileOrIdentifier && d.pluginFormatName == format.getName())
164 {
165 if (format.pluginNeedsRescanning (d))
166 needsRescanning = true;
167 else
168 typesFound.add (new PluginDescription (d));
169 }
170 }
171
172 if (! needsRescanning)
173 return false;
174 }
175
176 if (blacklist.contains (fileOrIdentifier))
177 return false;
178
180
181 {
182 const ScopedUnlock sl2 (scanLock);
183
184 if (scanner != nullptr)
185 {
186 if (! scanner->findPluginTypesFor (format, found, fileOrIdentifier))
187 addToBlacklist (fileOrIdentifier);
188 }
189 else
190 {
191 format.findAllTypesForFile (found, fileOrIdentifier);
192 }
193 }
194
195 for (auto* desc : found)
196 {
197 if (desc == nullptr)
198 {
200 continue;
201 }
202
203 addType (*desc);
204 typesFound.add (new PluginDescription (*desc));
205 }
206
207 return ! found.isEmpty();
208}
209
211 const StringArray& files,
213{
214 for (const auto& filenameOrID : files)
215 {
216 bool found = false;
217
218 for (auto format : formatManager.getFormats())
219 {
220 if (format->fileMightContainThisPluginType (filenameOrID)
221 && scanAndAddFile (filenameOrID, true, typesFound, *format))
222 {
223 found = true;
224 break;
225 }
226 }
227
228 if (! found)
229 {
230 File f (filenameOrID);
231
232 if (f.isDirectory())
233 {
234 StringArray s;
235
237 s.add (subFile.getFullPathName());
238
239 scanAndAddDragAndDroppedFiles (formatManager, s, typesFound);
240 }
241 }
242 }
243
244 scanFinished();
245}
246
248{
249 if (scanner != nullptr)
250 scanner->scanFinished();
251}
252
254{
255 return blacklist;
256}
257
259{
260 if (! blacklist.contains (pluginID))
261 {
262 blacklist.add (pluginID);
264 }
265}
266
268{
269 const int index = blacklist.indexOf (pluginID);
270
271 if (index >= 0)
272 {
273 blacklist.remove (index);
275 }
276}
277
279{
280 if (blacklist.size() > 0)
281 {
282 blacklist.clear();
284 }
285}
286
287//==============================================================================
289{
290 PluginSorter (KnownPluginList::SortMethod sortMethod, bool forwards) noexcept
291 : method (sortMethod), direction (forwards ? 1 : -1) {}
292
293 bool operator() (const PluginDescription& first, const PluginDescription& second) const
294 {
295 int diff = 0;
296
297 switch (method)
298 {
299 case KnownPluginList::sortByCategory: diff = first.category.compareNatural (second.category, false); break;
300 case KnownPluginList::sortByManufacturer: diff = first.manufacturerName.compareNatural (second.manufacturerName, false); break;
301 case KnownPluginList::sortByFormat: diff = first.pluginFormatName.compare (second.pluginFormatName); break;
302 case KnownPluginList::sortByFileSystemLocation: diff = lastPathPart (first.fileOrIdentifier).compare (lastPathPart (second.fileOrIdentifier)); break;
303 case KnownPluginList::sortByInfoUpdateTime: diff = compare (first.lastInfoUpdateTime, second.lastInfoUpdateTime); break;
304 case KnownPluginList::sortAlphabetically:
305 case KnownPluginList::defaultOrder:
306 default: break;
307 }
308
309 if (diff == 0)
310 diff = first.name.compareNatural (second.name, false);
311
312 return diff * direction < 0;
313 }
314
315private:
316 static String lastPathPart (const String& path)
317 {
318 return path.replaceCharacter ('\\', '/').upToLastOccurrenceOf ("/", false, false);
319 }
320
321 static int compare (Time a, Time b) noexcept
322 {
323 if (a < b) return -1;
324 if (b < a) return 1;
325
326 return 0;
327 }
328
330 int direction;
331};
332
333void KnownPluginList::sort (const SortMethod method, bool forwards)
334{
335 if (method != defaultOrder)
336 {
338
339 {
340 ScopedLock lock (typesArrayLock);
341
342 oldOrder.addArray (types);
343 std::stable_sort (types.begin(), types.end(), PluginSorter (method, forwards));
344 newOrder.addArray (types);
345 }
346
347 auto hasOrderChanged = [&]
348 {
349 for (int i = 0; i < oldOrder.size(); ++i)
350 if (! oldOrder[i].isDuplicateOf (newOrder[i]))
351 return true;
352
353 return false;
354 }();
355
356 if (hasOrderChanged)
358 }
359}
360
361//==============================================================================
363{
364 auto e = std::make_unique<XmlElement> ("KNOWNPLUGINS");
365
366 {
367 ScopedLock lock (typesArrayLock);
368
369 for (int i = types.size(); --i >= 0;)
370 e->prependChildElement (types.getUnchecked (i).createXml().release());
371 }
372
373 for (auto& b : blacklist)
374 e->createNewChildElement ("BLACKLISTED")->setAttribute ("id", b);
375
376 return e;
377}
378
380{
381 clear();
383
384 if (xml.hasTagName ("KNOWNPLUGINS"))
385 {
386 for (auto* e : xml.getChildIterator())
387 {
389
390 if (e->hasTagName ("BLACKLISTED"))
391 blacklist.add (e->getStringAttribute ("id"));
392 else if (info.loadFromXml (*e))
393 addType (info);
394 }
395 }
396}
397
398//==============================================================================
400{
401 enum { menuIdBase = 0x324503f4 };
402
403 static void buildTreeByFolder (KnownPluginList::PluginTree& tree, const Array<PluginDescription>& allPlugins)
404 {
405 for (auto& pd : allPlugins)
406 {
407 auto path = pd.fileOrIdentifier.replaceCharacter ('\\', '/')
408 .upToLastOccurrenceOf ("/", false, false);
409
410 if (path.substring (1, 2) == ":")
411 path = path.substring (2);
412
413 addPlugin (tree, pd, path);
414 }
415
416 optimiseFolders (tree, false);
417 }
418
419 static void optimiseFolders (KnownPluginList::PluginTree& tree, bool concatenateName)
420 {
421 for (int i = tree.subFolders.size(); --i >= 0;)
422 {
423 auto& sub = *tree.subFolders.getUnchecked (i);
424 optimiseFolders (sub, concatenateName || (tree.subFolders.size() > 1));
425
426 if (sub.plugins.isEmpty())
427 {
428 for (auto* s : sub.subFolders)
429 {
430 if (concatenateName)
431 s->folder = sub.folder + "/" + s->folder;
432
433 tree.subFolders.add (s);
434 }
435
436 sub.subFolders.clear (false);
437 tree.subFolders.remove (i);
438 }
439 }
440 }
441
442 static void buildTreeByCategory (KnownPluginList::PluginTree& tree,
445 {
447 auto current = std::make_unique<KnownPluginList::PluginTree>();
448
449 for (auto& pd : sorted)
450 {
451 auto thisType = (sortMethod == KnownPluginList::sortByCategory ? pd.category
452 : pd.manufacturerName);
453
454 if (! thisType.containsNonWhitespaceChars())
455 thisType = "Other";
456
457 if (! thisType.equalsIgnoreCase (lastType))
458 {
459 if (current->plugins.size() + current->subFolders.size() > 0)
460 {
461 current->folder = lastType;
462 tree.subFolders.add (std::move (current));
463 current = std::make_unique<KnownPluginList::PluginTree>();
464 }
465
467 }
468
469 current->plugins.add (pd);
470 }
471
472 if (current->plugins.size() + current->subFolders.size() > 0)
473 {
474 current->folder = lastType;
475 tree.subFolders.add (std::move (current));
476 }
477 }
478
479 static void addPlugin (KnownPluginList::PluginTree& tree, PluginDescription pd, String path)
480 {
481 #if JUCE_MAC
482 if (path.containsChar (':'))
483 path = path.fromFirstOccurrenceOf (":", false, false); // avoid the special AU formatting nonsense on Mac..
484 #endif
485
486 if (path.isEmpty())
487 {
488 tree.plugins.add (pd);
489 }
490 else
491 {
492 auto firstSubFolder = path.upToFirstOccurrenceOf ("/", false, false);
493 auto remainingPath = path.fromFirstOccurrenceOf ("/", false, false);
494
495 for (int i = tree.subFolders.size(); --i >= 0;)
496 {
497 auto& subFolder = *tree.subFolders.getUnchecked (i);
498
499 if (subFolder.folder.equalsIgnoreCase (firstSubFolder))
500 {
501 addPlugin (subFolder, pd, remainingPath);
502 return;
503 }
504 }
505
507 newFolder->folder = firstSubFolder;
508 tree.subFolders.add (newFolder);
509 addPlugin (*newFolder, pd, remainingPath);
510 }
511 }
512
513 static bool containsDuplicateNames (const Array<PluginDescription>& plugins, const String& name)
514 {
515 int matches = 0;
516
517 for (auto& p : plugins)
518 if (p.name == name && ++matches > 1)
519 return true;
520
521 return false;
522 }
523
524 static bool addToMenu (const KnownPluginList::PluginTree& tree, PopupMenu& m,
527 {
528 bool isTicked = false;
529
530 for (auto* sub : tree.subFolders)
531 {
532 PopupMenu subMenu;
533 auto isItemTicked = addToMenu (*sub, subMenu, allPlugins, currentlyTickedPluginID);
534 isTicked = isTicked || isItemTicked;
535
536 m.addSubMenu (sub->folder, subMenu, true, nullptr, isItemTicked, 0);
537 }
538
539 auto getPluginMenuIndex = [&] (const PluginDescription& d)
540 {
541 int i = 0;
542
543 for (auto& p : allPlugins)
544 {
545 if (p.isDuplicateOf (d))
546 return i + menuIdBase;
547
548 ++i;
549 }
550
551 return 0;
552 };
553
554 for (auto& plugin : tree.plugins)
555 {
556 auto name = plugin.name;
557
558 if (containsDuplicateNames (tree.plugins, name))
559 name << " (" << plugin.pluginFormatName << ')';
560
561 auto isItemTicked = plugin.matchesIdentifierString (currentlyTickedPluginID);
562 isTicked = isTicked || isItemTicked;
563
565 }
566
567 return isTicked;
568 }
569};
570
572{
574 sorted.addArray (types);
575
576 std::stable_sort (sorted.begin(), sorted.end(), PluginSorter (sortMethod, true));
577
578 auto tree = std::make_unique<PluginTree>();
579
580 if (sortMethod == sortByCategory || sortMethod == sortByManufacturer || sortMethod == sortByFormat)
581 {
582 PluginTreeUtils::buildTreeByCategory (*tree, sorted, sortMethod);
583 }
584 else if (sortMethod == sortByFileSystemLocation)
585 {
586 PluginTreeUtils::buildTreeByFolder (*tree, sorted);
587 }
588 else
589 {
590 for (auto& p : sorted)
591 tree->plugins.add (p);
592 }
593
594 return tree;
595}
596
597//==============================================================================
600{
601 auto tree = createTree (types, sortMethod);
602 PluginTreeUtils::addToMenu (*tree, menu, types, currentlyTickedPluginID);
603}
604
606{
607 auto i = menuResultCode - PluginTreeUtils::menuIdBase;
608 return isPositiveAndBelow (i, types.size()) ? i : -1;
609}
610
611//==============================================================================
612KnownPluginList::CustomScanner::CustomScanner() {}
613KnownPluginList::CustomScanner::~CustomScanner() {}
614
616
618{
620 return job->shouldExit();
621
622 return false;
623}
624
625//==============================================================================
627{
629}
630
632{
634}
635
637{
638 return createTree (getTypes(), sortMethod);
639}
640
641
642} // namespace juce
Holds a resizable array of primitive or copy-by-value objects.
Definition juce_Array.h:56
void addArray(const Type *elementsToAdd, int numElementsToAdd)
Adds elements from an array to the end of this array.
Definition juce_Array.h:583
int size() const noexcept
Returns the current number of elements in the array.
Definition juce_Array.h:215
void add(const ElementType &newElement)
Appends a new element at the end of the array.
Definition juce_Array.h:418
This maintains a list of known AudioPluginFormats.
Array< AudioPluginFormat * > getFormats() const
Returns a list of all the registered formats.
The base class for a type of plugin format, such as VST, AudioUnit, LADSPA, etc.
void sendChangeMessage()
Causes an asynchronous change message to be sent to all the registered listeners.
Represents a local file or directory.
Definition juce_File.h:45
bool isDirectory() const
Checks whether the file is a directory that exists.
Array< File > findChildFiles(int whatToLookFor, bool searchRecursively, const String &wildCardPattern="*", FollowSymlinks followSymlinks=FollowSymlinks::yes) const
Searches this directory for files matching a wildcard pattern.
@ findFilesAndDirectories
Use this flag to indicate that you want to find both files and directories.
Definition juce_File.h:567
Automatically locks and unlocks a mutex object.
Automatically unlocks and re-locks a mutex object.
bool shouldExit() const noexcept
Returns true if the current scan should be abandoned.
virtual void scanFinished()
Called when a scan has finished, to allow clean-up of resources.
bool isListingUpToDate(const String &possiblePluginFileOrIdentifier, AudioPluginFormat &formatToUse) const
Returns true if the specified file is already known about and if it hasn't been modified since our en...
void clearBlacklistedFiles()
Clears all the blacklisted files.
std::unique_ptr< PluginDescription > getTypeForIdentifierString(const String &identifierString) const
Looks for a type in the list which matches a plugin type ID.
void recreateFromXml(const XmlElement &xml)
Recreates the state of this list from its stored XML format.
SortMethod
Sort methods used to change the order of the plugins in the list.
void sort(SortMethod method, bool forwards)
Sorts the list.
void clear()
Clears the list.
int getNumTypes() const noexcept
Returns the number of types currently in the list.
bool scanAndAddFile(const String &possiblePluginFileOrIdentifier, bool dontRescanIfAlreadyInList, OwnedArray< PluginDescription > &typesFound, AudioPluginFormat &formatToUse)
Looks for all types that can be loaded from a given file, and adds them to the list.
std::unique_ptr< PluginDescription > getTypeForFile(const String &fileOrIdentifier) const
Looks for a type in the list which comes from this file.
std::unique_ptr< XmlElement > createXml() const
Creates some XML that can be used to store the state of this list.
void removeType(const PluginDescription &type)
Removes a type.
const StringArray & getBlacklistedFiles() const
Returns the list of blacklisted files.
void removeFromBlacklist(const String &pluginID)
Removes a plugin ID from the black-list.
void scanAndAddDragAndDroppedFiles(AudioPluginFormatManager &formatManager, const StringArray &filenames, OwnedArray< PluginDescription > &typesFound)
Scans and adds a bunch of files that might have been dragged-and-dropped.
Array< PluginDescription > getTypesForFormat(AudioPluginFormat &) const
Returns the subset of plugin types for a given format.
void scanFinished()
Tells a custom scanner that a scan has finished, and it can release any resources.
static int getIndexChosenByMenu(const Array< PluginDescription > &types, int menuResultCode)
Converts a menu item index that has been chosen into its index in the list.
static void addToMenu(PopupMenu &menu, const Array< PluginDescription > &types, SortMethod sortMethod, const String &currentlyTickedPluginID={})
Adds the plug-in types to a popup menu so that the user can select one.
void addToBlacklist(const String &pluginID)
Adds a plugin ID to the black-list.
KnownPluginList()
Creates an empty list.
static std::unique_ptr< PluginTree > createTree(const Array< PluginDescription > &types, SortMethod sortMethod)
Creates a PluginTree object representing the list of plug-ins.
bool addType(const PluginDescription &type)
Adds a type manually from its description.
Array< PluginDescription > getTypes() const
Returns a copy of the current list.
void setCustomScanner(std::unique_ptr< CustomScanner > newScanner)
Supplies a custom scanner to be used in future scans.
~KnownPluginList() override
Destructor.
A structure that recursively holds a tree of plugins.
An array designed for holding objects.
bool isEmpty() const noexcept
Returns true if the array is empty, false otherwise.
A small class to represent some facts about a particular type of plug-in.
String pluginFormatName
The plug-in format, e.g.
bool loadFromXml(const XmlElement &xml)
Reloads the info in this structure from an XML record that was previously saved with createXML().
bool isInstrument
True if the plug-in identifies itself as a synthesiser.
String category
A category, such as "Dynamics", "Reverbs", etc.
String name
The name of the plug-in.
String manufacturerName
The manufacturer.
String fileOrIdentifier
Either the file containing the plug-in module, or some other unique way of identifying it.
Time lastInfoUpdateTime
The last time that this information was updated.
Creates and displays a popup-menu.
void addSubMenu(String subMenuName, PopupMenu subMenu, bool isEnabled=true)
Appends a sub-menu.
void addItem(Item newItem)
Adds an item to the menu.
A special array for holding a list of strings.
int indexOf(StringRef stringToLookFor, bool ignoreCase=false, int startIndex=0) const
Searches for a string in the array.
bool contains(StringRef stringToLookFor, bool ignoreCase=false) const
Searches for a string in the array.
void clear()
Removes all elements from the array.
int size() const noexcept
Returns the number of strings in the array.
void add(String stringToAdd)
Appends a string at the end of the array.
void remove(int index)
Removes a string from the array.
The JUCE String class!
Definition juce_String.h:53
String upToFirstOccurrenceOf(StringRef substringToEndWith, bool includeSubStringInResult, bool ignoreCase) const
Returns the start of this string, up to the first occurrence of a substring.
int compareNatural(StringRef other, bool isCaseSensitive=false) const noexcept
Compares two strings, taking into account textual characteristics like numbers and spaces.
bool isEmpty() const noexcept
Returns true if the string contains no characters.
bool containsChar(juce_wchar character) const noexcept
Tests whether the string contains a particular character.
String upToLastOccurrenceOf(StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const
Returns the start of this string, up to the last occurrence of a substring.
String replaceCharacter(juce_wchar characterToReplace, juce_wchar characterToInsertInstead) const
Returns a string with all occurrences of a character replaced with a different one.
int compare(const String &other) const noexcept
Case-sensitive comparison with another string.
String fromFirstOccurrenceOf(StringRef substringToStartFrom, bool includeSubStringInResult, bool ignoreCase) const
Returns a section of the string starting from a given substring.
static ThreadPoolJob * getCurrentThreadPoolJob()
If the calling thread is being invoked inside a runJob() method, this will return the ThreadPoolJob t...
Holds an absolute date and time.
Definition juce_Time.h:37
Used to build a tree of elements representing an XML document.
bool hasTagName(StringRef possibleTagName) const noexcept
Tests whether this element has a particular tag name.
Iterator< GetNextElement > getChildIterator() const
Allows iterating the children of an XmlElement using range-for syntax.
#define jassert(expression)
Platform-independent assertion macro.
#define jassertfalse
This will always cause an assertion failure.
JUCE Namespace.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
bool isPositiveAndBelow(Type1 valueToTest, Type2 upperLimit) noexcept
Returns true if a value is at least zero, and also below a specified upper limit.
T stable_sort(T... args)