| @@ -7,6 +7,8 @@ | |||
| Changelist for version 1.46 | |||
| - new class: ScopedTryLock | |||
| - added AudioUnit support to the audio hosting code | |||
| ============================================================================== | |||
| Changelist for version 1.45 | |||
| @@ -233,6 +233,7 @@ const PopupMenu MainHostWindow::getMenuForIndex (int topLevelMenuIndex, const St | |||
| sortTypeMenu.addItem (201, "List plugins in alphabetical order", true, pluginSortMethod == KnownPluginList::sortAlphabetically); | |||
| sortTypeMenu.addItem (202, "List plugins by category", true, pluginSortMethod == KnownPluginList::sortByCategory); | |||
| sortTypeMenu.addItem (203, "List plugins by manufacturer", true, pluginSortMethod == KnownPluginList::sortByManufacturer); | |||
| sortTypeMenu.addItem (204, "List plugins based on the directory structure", true, pluginSortMethod == KnownPluginList::sortByFileSystemLocation); | |||
| menu.addSubMenu ("Plugin menu type", sortTypeMenu); | |||
| menu.addSeparator(); | |||
| @@ -273,6 +274,8 @@ void MainHostWindow::menuItemSelected (int menuItemID, int /*topLevelMenuIndex*/ | |||
| pluginSortMethod = KnownPluginList::sortByCategory; | |||
| else if (menuItemID == 203) | |||
| pluginSortMethod = KnownPluginList::sortByManufacturer; | |||
| else if (menuItemID == 204) | |||
| pluginSortMethod = KnownPluginList::sortByFileSystemLocation; | |||
| ApplicationProperties::getInstance()->getUserSettings() | |||
| ->setValue (T("pluginSortMethod"), (int) pluginSortMethod); | |||
| @@ -887,7 +887,10 @@ void VSTPluginInstance::initialise() | |||
| dispatch (effIdentify, 0, 0, 0, 0); | |||
| { | |||
| // this code would ask the plugin for its name, but so few plugins | |||
| // actually bother implementing this correctly, that it's better to | |||
| // just ignore it and use the file name instead. | |||
| /* { | |||
| char buffer [256]; | |||
| zerostruct (buffer); | |||
| dispatch (effGetEffectName, 0, 0, buffer, 0); | |||
| @@ -896,9 +899,13 @@ void VSTPluginInstance::initialise() | |||
| if (name.isEmpty()) | |||
| name = module->pluginName; | |||
| } | |||
| */ | |||
| dispatch (effSetSampleRate, 0, 0, 0, (float) getSampleRate()); | |||
| dispatch (effSetBlockSize, 0, jmax (32, getBlockSize()), 0, 0); | |||
| if (getSampleRate() > 0) | |||
| dispatch (effSetSampleRate, 0, 0, 0, (float) getSampleRate()); | |||
| if (getBlockSize() > 0) | |||
| dispatch (effSetBlockSize, 0, jmax (32, getBlockSize()), 0, 0); | |||
| dispatch (effOpen, 0, 0, 0, 0); | |||
| @@ -961,7 +968,7 @@ void VSTPluginInstance::prepareToPlay (double sampleRate_, | |||
| dispatch (effSetSampleRate, 0, 0, 0, (float) sampleRate_); | |||
| dispatch (effSetBlockSize, 0, jmax (16, samplesPerBlockExpected), 0, 0); | |||
| tempBuffer.setSize (effect->numOutputs, samplesPerBlockExpected); | |||
| tempBuffer.setSize (jmax (1, effect->numOutputs), samplesPerBlockExpected); | |||
| if (! isPowerOn) | |||
| setPower (true); | |||
| @@ -2266,7 +2273,7 @@ static VstIntPtr handleGeneralCallback (VstInt32 opcode, VstInt32 index, VstInt3 | |||
| return 1; | |||
| case audioMasterGetVendorVersion: | |||
| return 1; | |||
| return 0x0101; | |||
| case audioMasterGetVendorString: | |||
| case audioMasterGetProductString: | |||
| JUCEApplication::getInstance() | |||
| @@ -199,6 +199,8 @@ public: | |||
| diff = first->category.compareLexicographically (second->category); | |||
| else if (method == KnownPluginList::sortByManufacturer) | |||
| diff = first->manufacturerName.compareLexicographically (second->manufacturerName); | |||
| else if (method == KnownPluginList::sortByFileSystemLocation) | |||
| diff = first->file.getParentDirectory().getFullPathName().compare (second->file.getParentDirectory().getFullPathName()); | |||
| if (diff == 0) | |||
| diff = first->name.compareLexicographically (second->name); | |||
| @@ -249,6 +251,103 @@ void KnownPluginList::recreateFromXml (const XmlElement& xml) | |||
| //============================================================================== | |||
| const int menuIdBase = 0x324503f4; | |||
| // This is used to turn a bunch of paths into a nested menu structure. | |||
| struct PluginFilesystemTree | |||
| { | |||
| private: | |||
| String folder; | |||
| OwnedArray <PluginFilesystemTree> subFolders; | |||
| Array <PluginDescription*> plugins; | |||
| void addPlugin (PluginDescription* const pd, const String& path) | |||
| { | |||
| if (path.isEmpty()) | |||
| { | |||
| plugins.add (pd); | |||
| } | |||
| else | |||
| { | |||
| const String firstSubFolder (path.upToFirstOccurrenceOf (T("/"), false, false)); | |||
| const String remainingPath (path.fromFirstOccurrenceOf (T("/"), false, false)); | |||
| for (int i = subFolders.size(); --i >= 0;) | |||
| { | |||
| if (subFolders.getUnchecked(i)->folder.equalsIgnoreCase (firstSubFolder)) | |||
| { | |||
| subFolders.getUnchecked(i)->addPlugin (pd, remainingPath); | |||
| return; | |||
| } | |||
| } | |||
| PluginFilesystemTree* const newFolder = new PluginFilesystemTree(); | |||
| newFolder->folder = firstSubFolder; | |||
| subFolders.add (newFolder); | |||
| newFolder->addPlugin (pd, remainingPath); | |||
| } | |||
| } | |||
| // removes any deeply nested folders that don't contain any actual plugins | |||
| void optimise() | |||
| { | |||
| for (int i = subFolders.size(); --i >= 0;) | |||
| { | |||
| PluginFilesystemTree* const sub = subFolders.getUnchecked(i); | |||
| sub->optimise(); | |||
| if (sub->plugins.size() == 0) | |||
| { | |||
| for (int j = 0; j < sub->subFolders.size(); ++j) | |||
| subFolders.add (sub->subFolders.getUnchecked(j)); | |||
| sub->subFolders.clear (false); | |||
| subFolders.remove (i); | |||
| } | |||
| } | |||
| } | |||
| public: | |||
| void buildTree (const Array <PluginDescription*>& allPlugins) | |||
| { | |||
| for (int i = 0; i < allPlugins.size(); ++i) | |||
| { | |||
| String path (allPlugins.getUnchecked(i)->file.getParentDirectory().getFullPathName()); | |||
| if (path.substring (1, 2) == T(":")) | |||
| path = path.substring (2); | |||
| path = path.replaceCharacter (T('\\'), T('/')); | |||
| addPlugin (allPlugins.getUnchecked(i), path); | |||
| } | |||
| optimise(); | |||
| } | |||
| void addToMenu (PopupMenu& m, const OwnedArray <PluginDescription>& allPlugins) const | |||
| { | |||
| int i; | |||
| for (i = 0; i < subFolders.size(); ++i) | |||
| { | |||
| const PluginFilesystemTree* const sub = subFolders.getUnchecked(i); | |||
| PopupMenu subMenu; | |||
| sub->addToMenu (subMenu, allPlugins); | |||
| m.addSubMenu (sub->folder, subMenu); | |||
| } | |||
| for (i = 0; i < plugins.size(); ++i) | |||
| { | |||
| PluginDescription* const plugin = plugins.getUnchecked(i); | |||
| m.addItem (allPlugins.indexOf (plugin) + menuIdBase, | |||
| plugin->name, true, false); | |||
| } | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| void KnownPluginList::addToMenu (PopupMenu& menu, const SortMethod sortMethod) const | |||
| { | |||
| Array <PluginDescription*> sorted; | |||
| @@ -262,7 +361,7 @@ void KnownPluginList::addToMenu (PopupMenu& menu, const SortMethod sortMethod) c | |||
| } | |||
| if (sortMethod == sortByCategory | |||
| || sortMethod == KnownPluginList::sortByManufacturer) | |||
| || sortMethod == sortByManufacturer) | |||
| { | |||
| String lastSubMenuName; | |||
| PopupMenu sub; | |||
| @@ -293,6 +392,12 @@ void KnownPluginList::addToMenu (PopupMenu& menu, const SortMethod sortMethod) c | |||
| if (sub.getNumItems() > 0) | |||
| menu.addSubMenu (lastSubMenuName, sub); | |||
| } | |||
| else if (sortMethod == sortByFileSystemLocation) | |||
| { | |||
| PluginFilesystemTree root; | |||
| root.buildTree (sorted); | |||
| root.addToMenu (menu, types); | |||
| } | |||
| else | |||
| { | |||
| for (int i = 0; i < sorted.size(); ++i) | |||
| @@ -116,7 +116,8 @@ public: | |||
| defaultOrder = 0, | |||
| sortAlphabetically, | |||
| sortByCategory, | |||
| sortByManufacturer | |||
| sortByManufacturer, | |||
| sortByFileSystemLocation | |||
| }; | |||
| //============================================================================== | |||
| @@ -129,6 +129,9 @@ bool PluginDirectoryScanner::scanNextFile (const bool dontRescanIfAlreadyInList) | |||
| // Managed to load without crashing, so remove it from the dead-man's-pedal.. | |||
| crashedPlugins.removeString (file->getFullPathName()); | |||
| setDeadMansPedalFile (crashedPlugins); | |||
| if (typesFound.size() == 0) | |||
| failedFiles.add (file->getFullPathName()); | |||
| } | |||
| ++nextIndex; | |||
| @@ -97,6 +97,10 @@ public: | |||
| */ | |||
| float getProgress() const { return progress; } | |||
| /** This returns a list of all the filenames of things that looked like being | |||
| a plugin file, but which failed to open for some reason. | |||
| */ | |||
| const StringArray& getFailedFiles() const throw() { return failedFiles; } | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| @@ -105,6 +109,7 @@ private: | |||
| KnownPluginList& list; | |||
| OwnedArray <File> filesToScan; | |||
| File deadMansPedalFile; | |||
| StringArray failedFiles; | |||
| int nextIndex; | |||
| float progress; | |||
| @@ -196,11 +196,18 @@ void PluginListComponent::buttonClicked (Button* b) | |||
| } | |||
| else if (r != 0) | |||
| { | |||
| scanFor (AudioPluginFormatManager::getInstance()->getFormat (r - 10)); | |||
| typeToScan = r - 10; | |||
| startTimer (1); | |||
| } | |||
| } | |||
| } | |||
| void PluginListComponent::timerCallback() | |||
| { | |||
| stopTimer(); | |||
| scanFor (AudioPluginFormatManager::getInstance()->getFormat (typeToScan)); | |||
| } | |||
| bool PluginListComponent::isInterestedInFileDrag (const StringArray& /*files*/) | |||
| { | |||
| return true; | |||
| @@ -273,4 +280,17 @@ void PluginListComponent::scanFor (AudioPluginFormat* format) | |||
| progress = scanner.getProgress(); | |||
| } | |||
| if (scanner.getFailedFiles().size() > 0) | |||
| { | |||
| StringArray shortNames; | |||
| for (int i = 0; i < scanner.getFailedFiles().size(); ++i) | |||
| shortNames.add (File (scanner.getFailedFiles()[i]).getFileName()); | |||
| AlertWindow::showMessageBox (AlertWindow::InfoIcon, | |||
| TRANS("Scan complete"), | |||
| TRANS("Note that the following files appeared to be plugin files, but failed to load correctly:\n\n") | |||
| + shortNames.joinIntoString (", ")); | |||
| } | |||
| } | |||
| @@ -44,7 +44,8 @@ | |||
| class PluginListComponent : public Component, | |||
| public ListBoxModel, | |||
| public ChangeListener, | |||
| public ButtonListener | |||
| public ButtonListener, | |||
| public Timer | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| @@ -79,6 +80,8 @@ public: | |||
| void buttonClicked (Button* b); | |||
| /** @internal */ | |||
| void changeListenerCallback (void*); | |||
| /** @internal */ | |||
| void timerCallback(); | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| @@ -89,6 +92,7 @@ private: | |||
| ListBox* listBox; | |||
| TextButton* optionsButton; | |||
| PropertiesFile* propertiesToUse; | |||
| int typeToScan; | |||
| void scanFor (AudioPluginFormat* format); | |||
| }; | |||