| @@ -7,6 +7,8 @@ | |||||
| Changelist for version 1.46 | Changelist for version 1.46 | ||||
| - new class: ScopedTryLock | - new class: ScopedTryLock | ||||
| - added AudioUnit support to the audio hosting code | |||||
| ============================================================================== | ============================================================================== | ||||
| Changelist for version 1.45 | 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 (201, "List plugins in alphabetical order", true, pluginSortMethod == KnownPluginList::sortAlphabetically); | ||||
| sortTypeMenu.addItem (202, "List plugins by category", true, pluginSortMethod == KnownPluginList::sortByCategory); | sortTypeMenu.addItem (202, "List plugins by category", true, pluginSortMethod == KnownPluginList::sortByCategory); | ||||
| sortTypeMenu.addItem (203, "List plugins by manufacturer", true, pluginSortMethod == KnownPluginList::sortByManufacturer); | 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.addSubMenu ("Plugin menu type", sortTypeMenu); | ||||
| menu.addSeparator(); | menu.addSeparator(); | ||||
| @@ -273,6 +274,8 @@ void MainHostWindow::menuItemSelected (int menuItemID, int /*topLevelMenuIndex*/ | |||||
| pluginSortMethod = KnownPluginList::sortByCategory; | pluginSortMethod = KnownPluginList::sortByCategory; | ||||
| else if (menuItemID == 203) | else if (menuItemID == 203) | ||||
| pluginSortMethod = KnownPluginList::sortByManufacturer; | pluginSortMethod = KnownPluginList::sortByManufacturer; | ||||
| else if (menuItemID == 204) | |||||
| pluginSortMethod = KnownPluginList::sortByFileSystemLocation; | |||||
| ApplicationProperties::getInstance()->getUserSettings() | ApplicationProperties::getInstance()->getUserSettings() | ||||
| ->setValue (T("pluginSortMethod"), (int) pluginSortMethod); | ->setValue (T("pluginSortMethod"), (int) pluginSortMethod); | ||||
| @@ -887,7 +887,10 @@ void VSTPluginInstance::initialise() | |||||
| dispatch (effIdentify, 0, 0, 0, 0); | 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]; | char buffer [256]; | ||||
| zerostruct (buffer); | zerostruct (buffer); | ||||
| dispatch (effGetEffectName, 0, 0, buffer, 0); | dispatch (effGetEffectName, 0, 0, buffer, 0); | ||||
| @@ -896,9 +899,13 @@ void VSTPluginInstance::initialise() | |||||
| if (name.isEmpty()) | if (name.isEmpty()) | ||||
| name = module->pluginName; | 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); | dispatch (effOpen, 0, 0, 0, 0); | ||||
| @@ -961,7 +968,7 @@ void VSTPluginInstance::prepareToPlay (double sampleRate_, | |||||
| dispatch (effSetSampleRate, 0, 0, 0, (float) sampleRate_); | dispatch (effSetSampleRate, 0, 0, 0, (float) sampleRate_); | ||||
| dispatch (effSetBlockSize, 0, jmax (16, samplesPerBlockExpected), 0, 0); | dispatch (effSetBlockSize, 0, jmax (16, samplesPerBlockExpected), 0, 0); | ||||
| tempBuffer.setSize (effect->numOutputs, samplesPerBlockExpected); | |||||
| tempBuffer.setSize (jmax (1, effect->numOutputs), samplesPerBlockExpected); | |||||
| if (! isPowerOn) | if (! isPowerOn) | ||||
| setPower (true); | setPower (true); | ||||
| @@ -2266,7 +2273,7 @@ static VstIntPtr handleGeneralCallback (VstInt32 opcode, VstInt32 index, VstInt3 | |||||
| return 1; | return 1; | ||||
| case audioMasterGetVendorVersion: | case audioMasterGetVendorVersion: | ||||
| return 1; | |||||
| return 0x0101; | |||||
| case audioMasterGetVendorString: | case audioMasterGetVendorString: | ||||
| case audioMasterGetProductString: | case audioMasterGetProductString: | ||||
| JUCEApplication::getInstance() | JUCEApplication::getInstance() | ||||
| @@ -199,6 +199,8 @@ public: | |||||
| diff = first->category.compareLexicographically (second->category); | diff = first->category.compareLexicographically (second->category); | ||||
| else if (method == KnownPluginList::sortByManufacturer) | else if (method == KnownPluginList::sortByManufacturer) | ||||
| diff = first->manufacturerName.compareLexicographically (second->manufacturerName); | diff = first->manufacturerName.compareLexicographically (second->manufacturerName); | ||||
| else if (method == KnownPluginList::sortByFileSystemLocation) | |||||
| diff = first->file.getParentDirectory().getFullPathName().compare (second->file.getParentDirectory().getFullPathName()); | |||||
| if (diff == 0) | if (diff == 0) | ||||
| diff = first->name.compareLexicographically (second->name); | diff = first->name.compareLexicographically (second->name); | ||||
| @@ -249,6 +251,103 @@ void KnownPluginList::recreateFromXml (const XmlElement& xml) | |||||
| //============================================================================== | //============================================================================== | ||||
| const int menuIdBase = 0x324503f4; | 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 | void KnownPluginList::addToMenu (PopupMenu& menu, const SortMethod sortMethod) const | ||||
| { | { | ||||
| Array <PluginDescription*> sorted; | Array <PluginDescription*> sorted; | ||||
| @@ -262,7 +361,7 @@ void KnownPluginList::addToMenu (PopupMenu& menu, const SortMethod sortMethod) c | |||||
| } | } | ||||
| if (sortMethod == sortByCategory | if (sortMethod == sortByCategory | ||||
| || sortMethod == KnownPluginList::sortByManufacturer) | |||||
| || sortMethod == sortByManufacturer) | |||||
| { | { | ||||
| String lastSubMenuName; | String lastSubMenuName; | ||||
| PopupMenu sub; | PopupMenu sub; | ||||
| @@ -293,6 +392,12 @@ void KnownPluginList::addToMenu (PopupMenu& menu, const SortMethod sortMethod) c | |||||
| if (sub.getNumItems() > 0) | if (sub.getNumItems() > 0) | ||||
| menu.addSubMenu (lastSubMenuName, sub); | menu.addSubMenu (lastSubMenuName, sub); | ||||
| } | } | ||||
| else if (sortMethod == sortByFileSystemLocation) | |||||
| { | |||||
| PluginFilesystemTree root; | |||||
| root.buildTree (sorted); | |||||
| root.addToMenu (menu, types); | |||||
| } | |||||
| else | else | ||||
| { | { | ||||
| for (int i = 0; i < sorted.size(); ++i) | for (int i = 0; i < sorted.size(); ++i) | ||||
| @@ -116,7 +116,8 @@ public: | |||||
| defaultOrder = 0, | defaultOrder = 0, | ||||
| sortAlphabetically, | sortAlphabetically, | ||||
| sortByCategory, | 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.. | // Managed to load without crashing, so remove it from the dead-man's-pedal.. | ||||
| crashedPlugins.removeString (file->getFullPathName()); | crashedPlugins.removeString (file->getFullPathName()); | ||||
| setDeadMansPedalFile (crashedPlugins); | setDeadMansPedalFile (crashedPlugins); | ||||
| if (typesFound.size() == 0) | |||||
| failedFiles.add (file->getFullPathName()); | |||||
| } | } | ||||
| ++nextIndex; | ++nextIndex; | ||||
| @@ -97,6 +97,10 @@ public: | |||||
| */ | */ | ||||
| float getProgress() const { return progress; } | 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 | juce_UseDebuggingNewOperator | ||||
| @@ -105,6 +109,7 @@ private: | |||||
| KnownPluginList& list; | KnownPluginList& list; | ||||
| OwnedArray <File> filesToScan; | OwnedArray <File> filesToScan; | ||||
| File deadMansPedalFile; | File deadMansPedalFile; | ||||
| StringArray failedFiles; | |||||
| int nextIndex; | int nextIndex; | ||||
| float progress; | float progress; | ||||
| @@ -196,11 +196,18 @@ void PluginListComponent::buttonClicked (Button* b) | |||||
| } | } | ||||
| else if (r != 0) | 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*/) | bool PluginListComponent::isInterestedInFileDrag (const StringArray& /*files*/) | ||||
| { | { | ||||
| return true; | return true; | ||||
| @@ -273,4 +280,17 @@ void PluginListComponent::scanFor (AudioPluginFormat* format) | |||||
| progress = scanner.getProgress(); | 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, | class PluginListComponent : public Component, | ||||
| public ListBoxModel, | public ListBoxModel, | ||||
| public ChangeListener, | public ChangeListener, | ||||
| public ButtonListener | |||||
| public ButtonListener, | |||||
| public Timer | |||||
| { | { | ||||
| public: | public: | ||||
| //============================================================================== | //============================================================================== | ||||
| @@ -79,6 +80,8 @@ public: | |||||
| void buttonClicked (Button* b); | void buttonClicked (Button* b); | ||||
| /** @internal */ | /** @internal */ | ||||
| void changeListenerCallback (void*); | void changeListenerCallback (void*); | ||||
| /** @internal */ | |||||
| void timerCallback(); | |||||
| //============================================================================== | //============================================================================== | ||||
| juce_UseDebuggingNewOperator | juce_UseDebuggingNewOperator | ||||
| @@ -89,6 +92,7 @@ private: | |||||
| ListBox* listBox; | ListBox* listBox; | ||||
| TextButton* optionsButton; | TextButton* optionsButton; | ||||
| PropertiesFile* propertiesToUse; | PropertiesFile* propertiesToUse; | ||||
| int typeToScan; | |||||
| void scanFor (AudioPluginFormat* format); | void scanFor (AudioPluginFormat* format); | ||||
| }; | }; | ||||