From e1772515b47c88308b3f38f3e234639abe72a042 Mon Sep 17 00:00:00 2001 From: jules Date: Wed, 3 Dec 2008 14:35:34 +0000 Subject: [PATCH] --- .../juce_mac_CarbonViewWrapperComponent.h | 20 +- .../juce_mac_MainMenu.mm | 11 +- .../juce_mac_NSViewComponentPeer.mm | 4 +- .../src/host/InternalFilters.h | 6 +- .../AudioUnit/juce_AudioUnitWrapper.mm | 14 +- juce_amalgamated.cpp | 1236 +++++++++------ juce_amalgamated.h | 61 +- .../juce_AudioThumbnail.cpp | 2 +- .../formats/juce_AudioUnitPluginFormat.cpp | 1320 ----------------- .../formats/juce_AudioUnitPluginFormat.h | 6 +- .../formats/juce_AudioUnitPluginFormat.mm | 238 ++- .../formats/juce_DirectXPluginFormat.h | 5 +- .../plugins/formats/juce_LADSPAPluginFormat.h | 5 +- .../plugins/formats/juce_VSTPluginFormat.cpp | 54 +- .../plugins/formats/juce_VSTPluginFormat.h | 8 +- .../audio/plugins/juce_AudioPluginFormat.h | 15 +- .../plugins/juce_AudioPluginFormatManager.cpp | 8 +- .../audio/plugins/juce_KnownPluginList.cpp | 82 +- .../audio/plugins/juce_KnownPluginList.h | 9 +- .../audio/plugins/juce_PluginDescription.h | 8 +- .../plugins/juce_PluginDirectoryScanner.cpp | 65 +- .../plugins/juce_PluginDirectoryScanner.h | 8 +- .../plugins/juce_PluginListComponent.cpp | 2 +- .../gui/components/juce_Desktop.h | 4 +- .../gui/components/menus/juce_MenuBarModel.h | 6 +- .../mouse/juce_DragAndDropContainer.cpp | 8 +- 26 files changed, 1232 insertions(+), 1973 deletions(-) delete mode 100644 src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.cpp diff --git a/build/macosx/platform_specific_code/juce_mac_CarbonViewWrapperComponent.h b/build/macosx/platform_specific_code/juce_mac_CarbonViewWrapperComponent.h index 7b80418303..f8094b0de7 100644 --- a/build/macosx/platform_specific_code/juce_mac_CarbonViewWrapperComponent.h +++ b/build/macosx/platform_specific_code/juce_mac_CarbonViewWrapperComponent.h @@ -29,7 +29,13 @@ ============================================================================== */ -/** Creates a floating carbon window that can be used to hold a carbon UI. +#ifndef __JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_JUCEHEADER__ +#define __JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_JUCEHEADER__ + + +//============================================================================== +/** + Creates a floating carbon window that can be used to hold a carbon UI. This is a handy class that's designed to be inlined where needed, e.g. in the audio plugin hosting code. @@ -106,6 +112,8 @@ public: setOurSizeToEmbeddedViewSize(); setEmbeddedWindowToOurSize(); + + creationTime = Time::getCurrentTime(); } } @@ -203,6 +211,11 @@ public: void timerCallback() { setOurSizeToEmbeddedViewSize(); + + // To avoid strange overpainting problems when the UI is first opened, we'll + // repaint it a few times during the first second that it's on-screen.. + if ((Time::getCurrentTime() - creationTime).inMilliseconds() < 1000) + HIViewSetNeedsDisplay (embeddedView, true); } OSStatus carbonEventHandler (EventHandlerCallRef nextHandlerRef, @@ -222,6 +235,8 @@ public: SetEventParameter (event, kEventParamClickActivation, typeClickActivationResult, sizeof (ClickActivationResult), &howToHandleClick); + + HIViewSetNeedsDisplay (embeddedView, true); } break; } @@ -239,6 +254,9 @@ protected: WindowRef wrapperWindow; HIViewRef embeddedView; bool recursiveResize; + Time creationTime; EventHandlerRef eventHandlerRef; }; + +#endif // __JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_JUCEHEADER__ diff --git a/build/macosx/platform_specific_code/juce_mac_MainMenu.mm b/build/macosx/platform_specific_code/juce_mac_MainMenu.mm index 5276ace1d0..26bf9419c1 100644 --- a/build/macosx/platform_specific_code/juce_mac_MainMenu.mm +++ b/build/macosx/platform_specific_code/juce_mac_MainMenu.mm @@ -181,11 +181,14 @@ public: MenuBarModel* currentModel; - void addMenuItem (PopupMenu::MenuItemIterator& iter, NSMenu* menuToAddTo, + void addMenuItem (PopupMenu::MenuItemIterator& iter, NSMenu* menuToAddTo, const int topLevelMenuId, const int topLevelIndex) { NSString* text = juceStringToNS (iter.itemName.upToFirstOccurrenceOf (T(""), false, true)); + if (text == 0) + text = @""; + if (iter.isSeparator) { [menuToAddTo addItem: [NSMenuItem separatorItem]]; @@ -320,7 +323,7 @@ END_JUCE_NAMESPACE BEGIN_JUCE_NAMESPACE //============================================================================== -static NSMenu* createStandardAppMenu (NSMenu* menu, const String& appName, +static NSMenu* createStandardAppMenu (NSMenu* menu, const String& appName, const PopupMenu* extraItems) { if (extraItems != 0 && JuceMainMenuHandler::instance != 0 && extraItems->getNumItems() > 0) @@ -329,7 +332,7 @@ static NSMenu* createStandardAppMenu (NSMenu* menu, const String& appName, while (iter.next()) JuceMainMenuHandler::instance->addMenuItem (iter, menu, 0, -1); - + [menu addItem: [NSMenuItem separatorItem]]; } @@ -415,7 +418,7 @@ void MenuBarModel::setMacMainMenu (MenuBarModel* newMenuBarModel, delete JuceMainMenuHandler::instance; jassert (JuceMainMenuHandler::instance == 0); // should be zeroed in the destructor jassert (extraAppleMenuItems == 0); // you can't specify some extra items without also supplying a model - + extraAppleMenuItems = 0; } else diff --git a/build/macosx/platform_specific_code/juce_mac_NSViewComponentPeer.mm b/build/macosx/platform_specific_code/juce_mac_NSViewComponentPeer.mm index 55bd29b03d..f71ef8cb99 100644 --- a/build/macosx/platform_specific_code/juce_mac_NSViewComponentPeer.mm +++ b/build/macosx/platform_specific_code/juce_mac_NSViewComponentPeer.mm @@ -1347,8 +1347,8 @@ void NSViewComponentPeer::redirectMovedOrResized() //============================================================================== void juce_setKioskComponent (Component* kioskModeComponent, bool enableOrDisable) { - // Very annoyingly, this function has to use the old SetSystemUIMode function, - // which is in Carbon.framework. But, because there's no Cocoa equivalent, it + // Very annoyingly, this function has to use the old SetSystemUIMode function, + // which is in Carbon.framework. But, because there's no Cocoa equivalent, it // is apparently still available in 64-bit apps.. if (enableOrDisable) { diff --git a/extras/audio plugin host/src/host/InternalFilters.h b/extras/audio plugin host/src/host/InternalFilters.h index 6d3c489107..aaab2d41e6 100644 --- a/extras/audio plugin host/src/host/InternalFilters.h +++ b/extras/audio plugin host/src/host/InternalFilters.h @@ -62,10 +62,12 @@ public: //============================================================================== const String getName() const { return "Internal"; } - bool fileMightContainThisPluginType (const File&) { return false; } + bool fileMightContainThisPluginType (const String&) { return false; } const FileSearchPath getDefaultLocationsToSearch() { return FileSearchPath(); } - void findAllTypesForFile (OwnedArray &, const File&) {} + void findAllTypesForFile (OwnedArray &, const String&) {} bool doesPluginStillExist (const PluginDescription&) { return true; } + const String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) { return fileOrIdentifier; } + const StringArray searchPathsForPlugins (const FileSearchPath&, const bool) { return StringArray(); } AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); //============================================================================== diff --git a/extras/audio plugins/wrapper/formats/AudioUnit/juce_AudioUnitWrapper.mm b/extras/audio plugins/wrapper/formats/AudioUnit/juce_AudioUnitWrapper.mm index 068fc444c1..1b74622606 100644 --- a/extras/audio plugins/wrapper/formats/AudioUnit/juce_AudioUnitWrapper.mm +++ b/extras/audio plugins/wrapper/formats/AudioUnit/juce_AudioUnitWrapper.mm @@ -779,8 +779,8 @@ protected: UInt8 inData1, UInt8 inData2, #if defined(MAC_OS_X_VERSION_10_5) - UInt32 inStartFrame -#else + UInt32 inStartFrame) +#else long inStartFrame) #endif { @@ -796,6 +796,14 @@ protected: return noErr; } + OSStatus HandleSysEx (const UInt8* inData, UInt32 inLength) + { +#if JucePlugin_WantsMidiInput + midiEvents.addEvent (inData, inLength, 0); +#endif + return noErr; + } + //============================================================================== ComponentResult GetPresets (CFArrayRef* outData) const { @@ -952,7 +960,7 @@ private: - (NSString*) description { - return [NSString stringWithString: @JucePlugin_Name]; + return [NSString stringWithString: @JucePlugin_Name]; } - (NSView*) uiViewForAudioUnit: (AudioUnit) inAudioUnit diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index aa45f6470f..3d45489599 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -18605,7 +18605,7 @@ void AudioThumbnail::drawChannel (Graphics& g, const float vscale = verticalZoomFactor * h / 256.0f; const Rectangle clip (g.getClipBounds()); - const int skipLeft = clip.getX() - x; + const int skipLeft = jlimit (0, w, clip.getX() - x); w -= skipLeft; x += skipLeft; @@ -26401,14 +26401,14 @@ void AudioPluginFormatManager::addDefaultFormats() } #endif -#if JUCE_PLUGINHOST_VST - formats.add (new VSTPluginFormat()); -#endif - #if JUCE_PLUGINHOST_AU && JUCE_MAC formats.add (new AudioUnitPluginFormat()); #endif +#if JUCE_PLUGINHOST_VST + formats.add (new VSTPluginFormat()); +#endif + #if JUCE_PLUGINHOST_DX && JUCE_WIN32 formats.add (new DirectXPluginFormat()); #endif @@ -26506,10 +26506,10 @@ void KnownPluginList::clear() } } -PluginDescription* KnownPluginList::getTypeForFile (const File& file) const throw() +PluginDescription* KnownPluginList::getTypeForFile (const String& fileOrIdentifier) const throw() { for (int i = 0; i < types.size(); ++i) - if (types.getUnchecked(i)->fileOrIdentifier == file.getFullPathName()) + if (types.getUnchecked(i)->fileOrIdentifier == fileOrIdentifier) return types.getUnchecked(i); return 0; @@ -26550,17 +26550,33 @@ void KnownPluginList::removeType (const int index) throw() sendChangeMessage (this); } -bool KnownPluginList::isListingUpToDate (const File& possiblePluginFile) const throw() +static Time getFileModTime (const String& fileOrIdentifier) throw() +{ + if (fileOrIdentifier.startsWithChar (T('/')) + || fileOrIdentifier[1] == T(':')) + { + return File (fileOrIdentifier).getLastModificationTime(); + } + + return Time (0); +} + +static bool timesAreDifferent (const Time& t1, const Time& t2) throw() +{ + return t1 != t2 || t1 == Time (0); +} + +bool KnownPluginList::isListingUpToDate (const String& fileOrIdentifier) const throw() { - if (getTypeForFile (possiblePluginFile) == 0) + if (getTypeForFile (fileOrIdentifier) == 0) return false; for (int i = types.size(); --i >= 0;) { const PluginDescription* const d = types.getUnchecked(i); - if (d->fileOrIdentifier == possiblePluginFile.getFullPathName() - && d->lastFileModTime != possiblePluginFile.getLastModificationTime()) + if (d->fileOrIdentifier == fileOrIdentifier + && timesAreDifferent (d->lastFileModTime, getFileModTime (fileOrIdentifier))) { return false; } @@ -26569,14 +26585,15 @@ bool KnownPluginList::isListingUpToDate (const File& possiblePluginFile) const t return true; } -bool KnownPluginList::scanAndAddFile (const File& possiblePluginFile, +bool KnownPluginList::scanAndAddFile (const String& fileOrIdentifier, const bool dontRescanIfAlreadyInList, - OwnedArray & typesFound) + OwnedArray & typesFound, + AudioPluginFormat& format) { bool addedOne = false; if (dontRescanIfAlreadyInList - && getTypeForFile (possiblePluginFile) != 0) + && getTypeForFile (fileOrIdentifier) != 0) { bool needsRescanning = false; @@ -26584,9 +26601,9 @@ bool KnownPluginList::scanAndAddFile (const File& possiblePluginFile, { const PluginDescription* const d = types.getUnchecked(i); - if (d->fileOrIdentifier == possiblePluginFile.getFullPathName()) + if (d->fileOrIdentifier == fileOrIdentifier) { - if (d->lastFileModTime != possiblePluginFile.getLastModificationTime()) + if (timesAreDifferent (d->lastFileModTime, getFileModTime (fileOrIdentifier))) needsRescanning = true; else typesFound.add (new PluginDescription (*d)); @@ -26597,23 +26614,18 @@ bool KnownPluginList::scanAndAddFile (const File& possiblePluginFile, return false; } - for (int i = 0; i < AudioPluginFormatManager::getInstance()->getNumFormats(); ++i) - { - AudioPluginFormat* const format = AudioPluginFormatManager::getInstance()->getFormat (i); - - OwnedArray found; - format->findAllTypesForFile (found, possiblePluginFile); + OwnedArray found; + format.findAllTypesForFile (found, fileOrIdentifier); - for (int i = 0; i < found.size(); ++i) - { - PluginDescription* const desc = found.getUnchecked(i); - jassert (desc != 0); + for (int i = 0; i < found.size(); ++i) + { + PluginDescription* const desc = found.getUnchecked(i); + jassert (desc != 0); - if (addType (*desc)) - addedOne = true; + if (addType (*desc)) + addedOne = true; - typesFound.add (new PluginDescription (*desc)); - } + typesFound.add (new PluginDescription (*desc)); } return addedOne; @@ -26624,10 +26636,20 @@ void KnownPluginList::scanAndAddDragAndDroppedFiles (const StringArray& files, { for (int i = 0; i < files.size(); ++i) { - const File f (files [i]); + bool loaded = false; + + for (int j = 0; j < AudioPluginFormatManager::getInstance()->getNumFormats(); ++j) + { + AudioPluginFormat* const format = AudioPluginFormatManager::getInstance()->getFormat (j); + + if (scanAndAddFile (files[i], true, typesFound, *format)) + loaded = true; + } - if (! scanAndAddFile (f, true, typesFound)) + if (! loaded) { + const File f (files[i]); + if (f.isDirectory()) { StringArray s; @@ -26798,7 +26820,13 @@ public: PopupMenu subMenu; sub->addToMenu (subMenu, allPlugins); + +#if JUCE_MAC + // avoid the special AU formatting nonsense on Mac.. + m.addSubMenu (sub->folder.fromFirstOccurrenceOf (T(":"), false, false), subMenu); +#else m.addSubMenu (sub->folder, subMenu); +#endif } for (i = 0; i < plugins.size(); ++i) @@ -27003,8 +27031,7 @@ PluginDirectoryScanner::PluginDirectoryScanner (KnownPluginList& listToAddTo, { directoriesToSearch.removeRedundantPaths(); - for (int j = 0; j < directoriesToSearch.getNumPaths(); ++j) - recursiveFileSearch (directoriesToSearch [j], recursive); + filesOrIdentifiersToScan = format.searchPathsForPlugins (directoriesToSearch, recursive); // If any plugins have crashed recently when being loaded, move them to the // end of the list to give the others a chance to load correctly.. @@ -27012,33 +27039,11 @@ PluginDirectoryScanner::PluginDirectoryScanner (KnownPluginList& listToAddTo, for (int i = 0; i < crashedPlugins.size(); ++i) { - const File f (crashedPlugins[i]); + const String f = crashedPlugins[i]; - for (int j = filesToScan.size(); --j >= 0;) - if (f == *filesToScan.getUnchecked (j)) - filesToScan.move (j, -1); - } -} - -void PluginDirectoryScanner::recursiveFileSearch (const File& dir, const bool recursive) -{ - // avoid allowing the dir iterator to be recursive, because we want to avoid letting it delve inside - // .component or .vst directories. - DirectoryIterator iter (dir, false, "*", File::findFilesAndDirectories); - - while (iter.next()) - { - const File f (iter.getFile()); - bool isPlugin = false; - - if (format.fileMightContainThisPluginType (f)) - { - isPlugin = true; - filesToScan.add (new File (f)); - } - - if (recursive && (! isPlugin) && f.isDirectory()) - recursiveFileSearch (f, true); + for (int j = filesOrIdentifiersToScan.size(); --j >= 0;) + if (f == filesOrIdentifiersToScan[j]) + filesOrIdentifiersToScan.move (j, -1); } } @@ -27046,49 +27051,45 @@ PluginDirectoryScanner::~PluginDirectoryScanner() { } -const File PluginDirectoryScanner::getNextPluginFileThatWillBeScanned() const throw() +const String PluginDirectoryScanner::getNextPluginFileThatWillBeScanned() const throw() { - File* const file = filesToScan [nextIndex]; - - if (file != 0) - return *file; - - return File::nonexistent; + return format.getNameOfPluginFromIdentifier (filesOrIdentifiersToScan [nextIndex]); } bool PluginDirectoryScanner::scanNextFile (const bool dontRescanIfAlreadyInList) { - File* const file = filesToScan [nextIndex]; + String file (filesOrIdentifiersToScan [nextIndex]); - if (file != 0) + if (file.isNotEmpty()) { - if (! list.isListingUpToDate (*file)) + if (! list.isListingUpToDate (file)) { OwnedArray typesFound; // Add this plugin to the end of the dead-man's pedal list in case it crashes... StringArray crashedPlugins (getDeadMansPedalFile()); - crashedPlugins.removeString (file->getFullPathName()); - crashedPlugins.add (file->getFullPathName()); + crashedPlugins.removeString (file); + crashedPlugins.add (file); setDeadMansPedalFile (crashedPlugins); - list.scanAndAddFile (*file, + list.scanAndAddFile (file, dontRescanIfAlreadyInList, - typesFound); + typesFound, + format); // Managed to load without crashing, so remove it from the dead-man's-pedal.. - crashedPlugins.removeString (file->getFullPathName()); + crashedPlugins.removeString (file); setDeadMansPedalFile (crashedPlugins); if (typesFound.size() == 0) - failedFiles.add (file->getFullPathName()); + failedFiles.add (file); } ++nextIndex; - progress = nextIndex / (float) filesToScan.size(); + progress = nextIndex / (float) filesOrIdentifiersToScan.size(); } - return nextIndex < filesToScan.size(); + return nextIndex < filesOrIdentifiersToScan.size(); } const StringArray PluginDirectoryScanner::getDeadMansPedalFile() throw() @@ -27352,7 +27353,7 @@ void PluginListComponent::scanFor (AudioPluginFormat* format) for (;;) { aw.setMessage (TRANS("Testing:\n\n") - + scanner.getNextPluginFileThatWillBeScanned().getFileName()); + + scanner.getNextPluginFileThatWillBeScanned()); MessageManager::getInstance()->runDispatchLoopUntil (20); @@ -27382,7 +27383,7 @@ void PluginListComponent::scanFor (AudioPluginFormat* format) END_JUCE_NAMESPACE /********* End of inlined file: juce_PluginListComponent.cpp *********/ -/********* Start of inlined file: juce_AudioUnitPluginFormat.cpp *********/ +/********* Start of inlined file: juce_AudioUnitPluginFormat.mm *********/ /********* Start of inlined file: juce_Config.h *********/ #ifndef __JUCE_CONFIG_JUCEHEADER__ @@ -27580,18 +27581,22 @@ END_JUCE_NAMESPACE #if JUCE_PLUGINHOST_AU && (! (defined (LINUX) || defined (_WIN32))) -#include -#include +#include +#include + +#if JUCE_MAC && JUCE_32BIT + #define JUCE_SUPPORT_CARBON 1 +#endif + +#if JUCE_SUPPORT_CARBON +#include #include +#endif BEGIN_JUCE_NAMESPACE #if JUCE_MAC -extern void juce_callAnyTimersSynchronously(); -extern bool juce_isHIViewCreatedByJuce (HIViewRef view); -extern bool juce_isWindowCreatedByJuce (WindowRef window); - #if MACOS_10_3_OR_EARLIER #define kAudioUnitType_Generator 'augn' #endif @@ -27607,9 +27612,366 @@ extern bool juce_isWindowCreatedByJuce (WindowRef window); #define log(a) #endif +#if JUCE_SUPPORT_CARBON + +/********* Start of inlined file: juce_mac_CarbonViewWrapperComponent.h *********/ +#ifndef __JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_JUCEHEADER__ +#define __JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_JUCEHEADER__ + +/** + Creates a floating carbon window that can be used to hold a carbon UI. + + This is a handy class that's designed to be inlined where needed, e.g. + in the audio plugin hosting code. +*/ +class CarbonViewWrapperComponent : public Component, + public ComponentMovementWatcher, + public Timer +{ +public: + CarbonViewWrapperComponent() + : ComponentMovementWatcher (this), + wrapperWindow (0), + embeddedView (0), + recursiveResize (false) + { + } + + virtual ~CarbonViewWrapperComponent() + { + jassert (embeddedView == 0); // must call deleteWindow() in the subclass's destructor! + } + + virtual HIViewRef attachView (WindowRef windowRef, HIViewRef rootView) = 0; + virtual void removeView (HIViewRef embeddedView) = 0; + virtual void mouseDown (int x, int y) {} + virtual void paint() {} + + virtual bool getEmbeddedViewSize (int& w, int& h) + { + if (embeddedView == 0) + return false; + + HIRect bounds; + HIViewGetBounds (embeddedView, &bounds); + w = jmax (1, roundFloatToInt (bounds.size.width)); + h = jmax (1, roundFloatToInt (bounds.size.height)); + return true; + } + + void createWindow() + { + if (wrapperWindow == 0) + { + Rect r; + r.left = getScreenX(); + r.top = getScreenY(); + r.right = r.left + getWidth(); + r.bottom = r.top + getHeight(); + + CreateNewWindow (kDocumentWindowClass, + (WindowAttributes) (kWindowStandardHandlerAttribute | kWindowCompositingAttribute + | kWindowNoShadowAttribute | kWindowNoTitleBarAttribute), + &r, &wrapperWindow); + + jassert (wrapperWindow != 0); + if (wrapperWindow == 0) + return; + + NSWindow* carbonWindow = [[NSWindow alloc] initWithWindowRef: wrapperWindow]; + NSWindow* ownerWindow = [((NSView*) getWindowHandle()) window]; + + [ownerWindow addChildWindow: carbonWindow + ordered: NSWindowAbove]; + + embeddedView = attachView (wrapperWindow, HIViewGetRoot (wrapperWindow)); + + EventTypeSpec windowEventTypes[] = { { kEventClassWindow, kEventWindowGetClickActivation }, + { kEventClassWindow, kEventWindowHandleDeactivate } }; + + EventHandlerUPP upp = NewEventHandlerUPP (carbonEventCallback); + InstallWindowEventHandler (wrapperWindow, upp, + sizeof (windowEventTypes) / sizeof (EventTypeSpec), + windowEventTypes, this, &eventHandlerRef); + + setOurSizeToEmbeddedViewSize(); + setEmbeddedWindowToOurSize(); + + creationTime = Time::getCurrentTime(); + } + } + + void deleteWindow() + { + removeView (embeddedView); + embeddedView = 0; + + if (wrapperWindow != 0) + { + RemoveEventHandler (eventHandlerRef); + DisposeWindow (wrapperWindow); + wrapperWindow = 0; + } + } + + void setOurSizeToEmbeddedViewSize() + { + int w, h; + if (getEmbeddedViewSize (w, h)) + { + if (w != getWidth() || h != getHeight()) + { + startTimer (50); + + setSize (w, h); + if (getParentComponent() != 0) + getParentComponent()->setSize (w, h); + } + else + { + startTimer (jlimit (50, 500, getTimerInterval() + 20)); + } + } + else + { + stopTimer(); + } + } + + void setEmbeddedWindowToOurSize() + { + if (! recursiveResize) + { + recursiveResize = true; + + if (embeddedView != 0) + { + HIRect r; + r.origin.x = 0; + r.origin.y = 0; + r.size.width = (float) getWidth(); + r.size.height = (float) getHeight(); + HIViewSetFrame (embeddedView, &r); + } + + if (wrapperWindow != 0) + { + Rect wr; + wr.left = getScreenX(); + wr.top = getScreenY(); + wr.right = wr.left + getWidth(); + wr.bottom = wr.top + getHeight(); + + SetWindowBounds (wrapperWindow, kWindowContentRgn, &wr); + ShowWindow (wrapperWindow); + } + + recursiveResize = false; + } + } + + void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) + { + setEmbeddedWindowToOurSize(); + } + + void componentPeerChanged() + { + deleteWindow(); + createWindow(); + } + + void componentVisibilityChanged (Component&) + { + if (isShowing()) + createWindow(); + else + deleteWindow(); + + setEmbeddedWindowToOurSize(); + } + + void timerCallback() + { + setOurSizeToEmbeddedViewSize(); + + // To avoid strange overpainting problems when the UI is first opened, we'll + // repaint it a few times during the first second that it's on-screen.. + if ((Time::getCurrentTime() - creationTime).inMilliseconds() < 1000) + HIViewSetNeedsDisplay (embeddedView, true); + } + + OSStatus carbonEventHandler (EventHandlerCallRef nextHandlerRef, + EventRef event) + { + switch (GetEventKind (event)) + { + case kEventWindowHandleDeactivate: + ActivateWindow (wrapperWindow, TRUE); + break; + + case kEventWindowGetClickActivation: + { + getTopLevelComponent()->toFront (false); + + ClickActivationResult howToHandleClick = kActivateAndHandleClick; + + SetEventParameter (event, kEventParamClickActivation, typeClickActivationResult, + sizeof (ClickActivationResult), &howToHandleClick); + + HIViewSetNeedsDisplay (embeddedView, true); + } + break; + } + + return noErr; + } + + static pascal OSStatus carbonEventCallback (EventHandlerCallRef nextHandlerRef, + EventRef event, void* userData) + { + return ((CarbonViewWrapperComponent*) userData)->carbonEventHandler (nextHandlerRef, event); + } + +protected: + WindowRef wrapperWindow; + HIViewRef embeddedView; + bool recursiveResize; + Time creationTime; + + EventHandlerRef eventHandlerRef; +}; + +#endif // __JUCE_MAC_CARBONVIEWWRAPPERCOMPONENT_JUCEHEADER__ +/********* End of inlined file: juce_mac_CarbonViewWrapperComponent.h *********/ + +#endif + static int insideCallback = 0; -class AudioUnitPluginWindow; +static const String osTypeToString (OSType type) throw() +{ + char s[4]; + s[0] = (char) (((uint32) type) >> 24); + s[1] = (char) (((uint32) type) >> 16); + s[2] = (char) (((uint32) type) >> 8); + s[3] = (char) ((uint32) type); + return String (s, 4); +} + +static OSType stringToOSType (const String& s1) throw() +{ + const String s (s1 + " "); + + return (((OSType) (unsigned char) s[0]) << 24) + | (((OSType) (unsigned char) s[1]) << 16) + | (((OSType) (unsigned char) s[2]) << 8) + | ((OSType) (unsigned char) s[3]); +} + +static const tchar* auIdentifierPrefix = T("AudioUnit:"); + +static const String createAUPluginIdentifier (const ComponentDescription& desc) +{ + jassert (osTypeToString ('abcd') == T("abcd")); // agh, must have got the endianness wrong.. + jassert (stringToOSType ("abcd") == (OSType) 'abcd'); // ditto + + String s (auIdentifierPrefix); + + if (desc.componentType == kAudioUnitType_MusicDevice) + s << "Synths/"; + else if (desc.componentType == kAudioUnitType_MusicEffect + || desc.componentType == kAudioUnitType_Effect) + s << "Effects/"; + else if (desc.componentType == kAudioUnitType_Generator) + s << "Generators/"; + else if (desc.componentType == kAudioUnitType_Panner) + s << "Panners/"; + + s << osTypeToString (desc.componentType) + << T(",") + << osTypeToString (desc.componentSubType) + << T(",") + << osTypeToString (desc.componentManufacturer); + + return s; +} + +static void getAUDetails (ComponentRecord* comp, String& name, String& manufacturer) +{ + Handle componentNameHandle = NewHandle (sizeof (void*)); + Handle componentInfoHandle = NewHandle (sizeof (void*)); + + if (componentNameHandle != 0 && componentInfoHandle != 0) + { + ComponentDescription desc; + + if (GetComponentInfo (comp, &desc, componentNameHandle, componentInfoHandle, 0) == noErr) + { + ConstStr255Param nameString = (ConstStr255Param) (*componentNameHandle); + ConstStr255Param infoString = (ConstStr255Param) (*componentInfoHandle); + + if (nameString != 0 && nameString[0] != 0) + { + const String all ((const char*) nameString + 1, nameString[0]); +DBG ("name: "+ all); + + manufacturer = all.upToFirstOccurrenceOf (T(":"), false, false).trim(); + name = all.fromFirstOccurrenceOf (T(":"), false, false).trim(); + } + + if (infoString != 0 && infoString[0] != 0) + { + const String all ((const char*) infoString + 1, infoString[0]); +DBG ("info: " + all); + } + + if (name.isEmpty()) + name = ""; + } + + DisposeHandle (componentNameHandle); + DisposeHandle (componentInfoHandle); + } +} + +static bool getComponentDescFromIdentifier (const String& fileOrIdentifier, ComponentDescription& desc, + String& name, String& version, String& manufacturer) +{ + zerostruct (desc); + + if (fileOrIdentifier.startsWithIgnoreCase (auIdentifierPrefix)) + { + String s (fileOrIdentifier.substring (jmax (fileOrIdentifier.lastIndexOfChar (T(':')), + fileOrIdentifier.lastIndexOfChar (T('/'))) + 1)); + + StringArray tokens; + tokens.addTokens (s, T(","), 0); + tokens.trim(); + tokens.removeEmptyStrings(); + + if (tokens.size() == 3) + { + desc.componentType = stringToOSType (tokens[0]); + desc.componentSubType = stringToOSType (tokens[1]); + desc.componentManufacturer = stringToOSType (tokens[2]); + + ComponentRecord* comp = FindNextComponent (0, &desc); + + if (comp != 0) + { + getAUDetails (comp, name, manufacturer); + + return true; + } + } + } + + return false; +} + +class AudioUnitPluginWindowCarbon; +class AudioUnitPluginWindowCocoa; class AudioUnitPluginInstance : public AudioPluginInstance { @@ -27622,11 +27984,11 @@ public: void fillInPluginDescription (PluginDescription& desc) const { desc.name = pluginName; - desc.file = file; + desc.fileOrIdentifier = createAUPluginIdentifier (componentDesc); desc.uid = ((int) componentDesc.componentType) ^ ((int) componentDesc.componentSubType) ^ ((int) componentDesc.componentManufacturer); - desc.lastFileModTime = file.getLastModificationTime(); + desc.lastFileModTime = 0; desc.pluginFormatName = "AudioUnit"; desc.category = getCategory(); desc.manufacturerName = manufacturer; @@ -27676,12 +28038,13 @@ public: juce_UseDebuggingNewOperator private: - friend class AudioUnitPluginWindow; + friend class AudioUnitPluginWindowCarbon; + friend class AudioUnitPluginWindowCocoa; friend class AudioUnitPluginFormat; ComponentDescription componentDesc; String pluginName, manufacturer, version; - File file; + String fileOrIdentifier; CriticalSection lock; bool initialised, wantsMidiMessages, wasPlaying; @@ -27692,7 +28055,7 @@ private: AudioUnit audioUnit; Array parameterIds; - bool getComponentDescFromFile (const File& file); + bool getComponentDescFromFile (const String& fileOrIdentifier); void initialise(); OSStatus renderGetInput (AudioUnitRenderActionFlags* ioActionFlags, @@ -27771,11 +28134,11 @@ private: const String getCategory() const; - AudioUnitPluginInstance (const File& file); + AudioUnitPluginInstance (const String& fileOrIdentifier); }; -AudioUnitPluginInstance::AudioUnitPluginInstance (const File& file_) - : file (file_), +AudioUnitPluginInstance::AudioUnitPluginInstance (const String& fileOrIdentifier) + : fileOrIdentifier (fileOrIdentifier), initialised (false), wantsMidiMessages (false), audioUnit (0), @@ -27786,9 +28149,9 @@ AudioUnitPluginInstance::AudioUnitPluginInstance (const File& file_) { ++insideCallback; - log (T("Opening AU: ") + file.getFullPathName()); + log (T("Opening AU: ") + fileOrIdentifier); - if (getComponentDescFromFile (file)) + if (getComponentDescFromFile (fileOrIdentifier)) { ComponentRecord* const comp = FindNextComponent (0, &componentDesc); @@ -27827,15 +28190,18 @@ AudioUnitPluginInstance::~AudioUnitPluginInstance() juce_free (outputBufferList); } -bool AudioUnitPluginInstance::getComponentDescFromFile (const File& file) +bool AudioUnitPluginInstance::getComponentDescFromFile (const String& fileOrIdentifier) { zerostruct (componentDesc); + if (getComponentDescFromIdentifier (fileOrIdentifier, componentDesc, pluginName, version, manufacturer)) + return true; + + const File file (fileOrIdentifier); if (! file.hasFileExtension (T(".component"))) return false; - const String filename (file.getFullPathName()); - const char* const utf8 = filename.toUTF8(); + const char* const utf8 = fileOrIdentifier.toUTF8(); CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, strlen (utf8), file.isDirectory()); if (url != 0) @@ -28173,9 +28539,10 @@ OSStatus AudioUnitPluginInstance::getTransportState (Boolean* outIsPlaying, *outIsPlaying = result.isPlaying; if (outTransportStateChanged != 0) + { *outTransportStateChanged = result.isPlaying != wasPlaying; - - wasPlaying = result.isPlaying; + wasPlaying = result.isPlaying; + } if (outCurrentSampleInTimeLine != 0) *outCurrentSampleInTimeLine = roundDoubleToInt (result.timeInSeconds * getSampleRate()); @@ -28215,148 +28582,166 @@ OSStatus AudioUnitPluginInstance::getTransportState (Boolean* outIsPlaying, static VoidArray activeWindows; -class AudioUnitPluginWindow : public AudioProcessorEditor, - public Timer +class AudioUnitPluginWindowCocoa : public AudioProcessorEditor { public: - - AudioUnitPluginWindow (AudioUnitPluginInstance& plugin_) + AudioUnitPluginWindowCocoa (AudioUnitPluginInstance& plugin_) : AudioProcessorEditor (&plugin_), plugin (plugin_), - isOpen (false), - pluginWantsKeys (false), - wasShowing (false), - recursiveResize (false), - viewComponent (0), - pluginViewRef (0) + wrapper (0) { - movementWatcher = new CompMovementWatcher (this); + addAndMakeVisible (wrapper = new NSViewComponent()); activeWindows.add (this); setOpaque (true); setVisible (true); - setSize (1, 1); + setSize (100, 100); - ComponentDescription viewList [16]; - UInt32 viewListSize = sizeof (viewList); - AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global, - 0, &viewList, &viewListSize); - - componentRecord = FindNextComponent (0, &viewList[0]); + createView(); } - ~AudioUnitPluginWindow() + ~AudioUnitPluginWindowCocoa() { - deleteAndZero (movementWatcher); - - closePluginWindow(); + const bool wasValid = isValid(); + wrapper->setView (0); activeWindows.removeValue (this); - plugin.editorBeingDeleted (this); + + if (wasValid) + plugin.editorBeingDeleted (this); + + delete wrapper; } - bool isValid() const throw() { return componentRecord != 0; } + bool isValid() const { return wrapper->getView() != 0; } - void componentMovedOrResized() + void paint (Graphics& g) { - if (recursiveResize) - return; + g.fillAll (Colours::white); + } - Component* const topComp = getTopLevelComponent(); + void resized() + { + wrapper->setSize (getWidth(), getHeight()); + } - if (topComp->getPeer() != 0) +private: + AudioUnitPluginInstance& plugin; + NSViewComponent* wrapper; + + bool createView() + { + UInt32 dataSize = 0; + Boolean isWritable = false; + if (AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global, + 0, &dataSize, &isWritable) != noErr + || dataSize == 0) { - int x = 0, y = 0; - relativePositionToOtherComponent (topComp, x, y); + return false; + } - recursiveResize = true; + if (AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global, + 0, &dataSize, &isWritable) != noErr) + { + return false; + } - if (pluginViewRef != 0) + NSView* pluginView = 0; + AudioUnitCocoaViewInfo* info = (AudioUnitCocoaViewInfo*) juce_calloc (dataSize); + + if (AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global, + 0, info, &dataSize) == noErr) + { + NSString* viewClassName = (NSString*) (info->mCocoaAUViewClass[0]); + NSString* path = (NSString*) CFURLCopyPath (info->mCocoaAUViewBundleLocation); + NSBundle* viewBundle = [NSBundle bundleWithPath: [path autorelease]]; + Class viewClass = [viewBundle classNamed: viewClassName]; + + if ([viewClass conformsToProtocol: @protocol (AUCocoaUIBase)] + && [viewClass instancesRespondToSelector: @selector (interfaceVersion)] + && [viewClass instancesRespondToSelector: @selector (uiViewForAudioUnit: withSize:)]) { - HIRect r; - r.origin.x = (float) x; - r.origin.y = (float) y; - r.size.width = (float) getWidth(); - r.size.height = (float) getHeight(); - HIViewSetFrame (pluginViewRef, &r); + id factory = [[[viewClass alloc] init] autorelease]; + pluginView = [factory uiViewForAudioUnit: plugin.audioUnit + withSize: NSMakeSize (getWidth(), getHeight())]; } - recursiveResize = false; + for (int i = (dataSize - sizeof (CFURLRef)) / sizeof (CFStringRef); --i >= 0;) + { + CFRelease (info->mCocoaAUViewClass[i]); + CFRelease (info->mCocoaAUViewBundleLocation); + } } + + juce_free (info); + wrapper->setView (pluginView); + + if (pluginView != 0) + setSize ([pluginView frame].size.width, + [pluginView frame].size.height); + + return pluginView != 0; } +}; + +#if JUCE_SUPPORT_CARBON + +class AudioUnitPluginWindowCarbon : public AudioProcessorEditor +{ +public: - void componentVisibilityChanged() + AudioUnitPluginWindowCarbon (AudioUnitPluginInstance& plugin_) + : AudioProcessorEditor (&plugin_), + plugin (plugin_), + viewComponent (0) { - const bool isShowingNow = isShowing(); + addAndMakeVisible (innerWrapper = new InnerWrapperComponent (this)); - if (wasShowing != isShowingNow) - { - wasShowing = isShowingNow; + activeWindows.add (this); - if (isShowingNow) - openPluginWindow(); - else - closePluginWindow(); - } + setOpaque (true); + setVisible (true); + setSize (400, 300); - componentMovedOrResized(); - } + ComponentDescription viewList [16]; + UInt32 viewListSize = sizeof (viewList); + AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global, + 0, &viewList, &viewListSize); - void componentPeerChanged() - { - closePluginWindow(); - openPluginWindow(); + componentRecord = FindNextComponent (0, &viewList[0]); } - void timerCallback() + ~AudioUnitPluginWindowCarbon() { - if (pluginViewRef != 0) - { - HIRect bounds; - HIViewGetBounds (pluginViewRef, &bounds); - const int w = jmax (32, (int) bounds.size.width); - const int h = jmax (32, (int) bounds.size.height); + deleteAndZero (innerWrapper); - if (w != getWidth() || h != getHeight()) - { - setSize (w, h); - startTimer (50); - } - else - { - startTimer (jlimit (50, 500, getTimerInterval() + 20)); - } - } + activeWindows.removeValue (this); + + if (isValid()) + plugin.editorBeingDeleted (this); } - bool keyStateChanged() + bool isValid() const throw() { return componentRecord != 0; } + + void paint (Graphics& g) { - return pluginWantsKeys; + g.fillAll (Colours::black); } - bool keyPressed (const KeyPress&) + void resized() { - return pluginWantsKeys; + innerWrapper->setSize (getWidth(), getHeight()); } - void paint (Graphics& g) + bool keyStateChanged() { - if (isOpen) - { - ComponentPeer* const peer = getPeer(); + return false; + } - if (peer != 0) - { - peer->addMaskedRegion (getScreenX() - peer->getScreenX(), - getScreenY() - peer->getScreenY(), - getWidth(), getHeight()); - } - } - else - { - g.fillAll (Colours::black); - } + bool keyPressed (const KeyPress&) + { + return false; } void broughtToFront() @@ -28365,101 +28750,104 @@ public: activeWindows.add (this); } - juce_UseDebuggingNewOperator + AudioUnit getAudioUnit() const { return plugin.audioUnit; } -private: - AudioUnitPluginInstance& plugin; - bool isOpen, wasShowing, recursiveResize; - bool pluginWantsKeys; - - ComponentRecord* componentRecord; - AudioUnitCarbonView viewComponent; - HIViewRef pluginViewRef; - - void openPluginWindow() + AudioUnitCarbonView getViewComponent() { - if (isOpen || getWindowHandle() == 0 || componentRecord == 0) - return; + if (viewComponent == 0 && componentRecord != 0) + viewComponent = (AudioUnitCarbonView) OpenComponent (componentRecord); - log (T("Opening AU GUI: ") + plugin.getName()); - isOpen = true; - - pluginWantsKeys = true; //xxx any way to find this out? Does it matter? - - viewComponent = (AudioUnitCarbonView) OpenComponent (componentRecord); + return viewComponent; + } + void closeViewComponent() + { if (viewComponent != 0) { - Float32Point pos = { getScreenX() - getTopLevelComponent()->getScreenX(), - getScreenY() - getTopLevelComponent()->getScreenY() }; - Float32Point size = { 250, 200 }; - - AudioUnitCarbonViewCreate (viewComponent, - plugin.audioUnit, - (WindowRef) getWindowHandle(), - HIViewGetRoot ((WindowRef) getWindowHandle()), - &pos, &size, - (ControlRef*) &pluginViewRef); + CloseComponent (viewComponent); + viewComponent = 0; } - - timerCallback(); // to set our comp to the right size - repaint(); } - void closePluginWindow() - { - stopTimer(); - - if (isOpen) - { - log (T("Closing AU GUI: ") + plugin.getName()); - isOpen = false; - - if (viewComponent != 0) - CloseComponent (viewComponent); + juce_UseDebuggingNewOperator - pluginViewRef = 0; - } - } +private: + AudioUnitPluginInstance& plugin; + ComponentRecord* componentRecord; + AudioUnitCarbonView viewComponent; - class CompMovementWatcher : public ComponentMovementWatcher + class InnerWrapperComponent : public CarbonViewWrapperComponent { public: - CompMovementWatcher (AudioUnitPluginWindow* const owner_) - : ComponentMovementWatcher (owner_), - owner (owner_) + InnerWrapperComponent (AudioUnitPluginWindowCarbon* const owner_) + : owner (owner_) { } - void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) + ~InnerWrapperComponent() { - owner->componentMovedOrResized(); + deleteWindow(); } - void componentPeerChanged() + HIViewRef attachView (WindowRef windowRef, HIViewRef rootView) { - owner->componentPeerChanged(); + log (T("Opening AU GUI: ") + owner->plugin.getName()); + + AudioUnitCarbonView viewComponent = owner->getViewComponent(); + + if (viewComponent == 0) + return 0; + + Float32Point pos = { 0, 0 }; + Float32Point size = { 250, 200 }; + + HIViewRef pluginView = 0; + + AudioUnitCarbonViewCreate (viewComponent, + owner->getAudioUnit(), + windowRef, + rootView, + &pos, + &size, + (ControlRef*) &pluginView); + + return pluginView; } - void componentVisibilityChanged (Component&) + void removeView (HIViewRef) { - owner->componentVisibilityChanged(); + log (T("Closing AU GUI: ") + owner->plugin.getName()); + + owner->closeViewComponent(); } private: - AudioUnitPluginWindow* const owner; + AudioUnitPluginWindowCarbon* const owner; }; - CompMovementWatcher* movementWatcher; + friend class InnerWrapperComponent; + InnerWrapperComponent* innerWrapper; }; +#endif + AudioProcessorEditor* AudioUnitPluginInstance::createEditor() { - AudioUnitPluginWindow* w = new AudioUnitPluginWindow (*this); + AudioProcessorEditor* w = new AudioUnitPluginWindowCocoa (*this); - if (! w->isValid()) + if (! ((AudioUnitPluginWindowCocoa*) w)->isValid()) deleteAndZero (w); +#if JUCE_SUPPORT_CARBON + if (w == 0) + { + w = new AudioUnitPluginWindowCarbon (*this); + + if (! ((AudioUnitPluginWindowCarbon*) w)->isValid()) + deleteAndZero (w); + } +#endif + return w; } @@ -28746,13 +29134,13 @@ AudioUnitPluginFormat::~AudioUnitPluginFormat() } void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray & results, - const File& file) + const String& fileOrIdentifier) { - if (! fileMightContainThisPluginType (file)) + if (! fileMightContainThisPluginType (fileOrIdentifier)) return; PluginDescription desc; - desc.file = file; + desc.fileOrIdentifier = fileOrIdentifier; desc.uid = 0; AudioUnitPluginInstance* instance = dynamic_cast (createInstanceFromDescription (desc)); @@ -28777,9 +29165,9 @@ AudioPluginInstance* AudioUnitPluginFormat::createInstanceFromDescription (const { AudioUnitPluginInstance* result = 0; - if (fileMightContainThisPluginType (desc.file)) + if (fileMightContainThisPluginType (desc.fileOrIdentifier)) { - result = new AudioUnitPluginInstance (desc.file); + result = new AudioUnitPluginInstance (desc.fileOrIdentifier); if (result->audioUnit != 0) { @@ -28794,12 +29182,65 @@ AudioPluginInstance* AudioUnitPluginFormat::createInstanceFromDescription (const return result; } -bool AudioUnitPluginFormat::fileMightContainThisPluginType (const File& f) +const StringArray AudioUnitPluginFormat::searchPathsForPlugins (const FileSearchPath& /*directoriesToSearch*/, + const bool /*recursive*/) { + StringArray result; + ComponentRecord* comp = 0; + ComponentDescription desc; + zerostruct (desc); + + for (;;) + { + zerostruct (desc); + comp = FindNextComponent (comp, &desc); + + if (comp == 0) + break; + + GetComponentInfo (comp, &desc, 0, 0, 0); + + if (desc.componentType == kAudioUnitType_MusicDevice + || desc.componentType == kAudioUnitType_MusicEffect + || desc.componentType == kAudioUnitType_Effect + || desc.componentType == kAudioUnitType_Generator + || desc.componentType == kAudioUnitType_Panner) + { + const String s (createAUPluginIdentifier (desc)); + DBG (s); + result.add (s); + } + } + + return result; +} + +bool AudioUnitPluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier) +{ + ComponentDescription desc; + + String name, version, manufacturer; + if (getComponentDescFromIdentifier (fileOrIdentifier, desc, name, version, manufacturer)) + return FindNextComponent (0, &desc) != 0; + + const File f (fileOrIdentifier); + return f.hasFileExtension (T(".component")) && f.isDirectory(); } +const String AudioUnitPluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier) +{ + ComponentDescription desc; + String name, version, manufacturer; + getComponentDescFromIdentifier (fileOrIdentifier, desc, name, version, manufacturer); + + if (name.isEmpty()) + name = fileOrIdentifier; + + return name; +} + bool AudioUnitPluginFormat::doesPluginStillExist (const PluginDescription& desc) { return File (desc.fileOrIdentifier).exists(); @@ -28807,7 +29248,7 @@ bool AudioUnitPluginFormat::doesPluginStillExist (const PluginDescription& desc) const FileSearchPath AudioUnitPluginFormat::getDefaultLocationsToSearch() { - return FileSearchPath ("~/Library/Audio/Plug-Ins/Components;/Library/Audio/Plug-Ins/Components"); + return FileSearchPath ("(Default AudioUnit locations)"); } #endif @@ -28817,7 +29258,7 @@ END_JUCE_NAMESPACE #undef log #endif -/********* End of inlined file: juce_AudioUnitPluginFormat.cpp *********/ +/********* End of inlined file: juce_AudioUnitPluginFormat.mm *********/ /********* Start of inlined file: juce_VSTPluginFormat.mm *********/ // This file just wraps juce_VSTPluginFormat.cpp in an objective-C wrapper @@ -29055,221 +29496,6 @@ BEGIN_JUCE_NAMESPACE #if JUCE_MAC -/********* Start of inlined file: juce_mac_CarbonViewWrapperComponent.h *********/ -/** Creates a floating carbon window that can be used to hold a carbon UI. - - This is a handy class that's designed to be inlined where needed, e.g. - in the audio plugin hosting code. -*/ -class CarbonViewWrapperComponent : public Component, - public ComponentMovementWatcher, - public Timer -{ -public: - CarbonViewWrapperComponent() - : ComponentMovementWatcher (this), - wrapperWindow (0), - embeddedView (0), - recursiveResize (false) - { - } - - virtual ~CarbonViewWrapperComponent() - { - jassert (embeddedView == 0); // must call deleteWindow() in the subclass's destructor! - } - - virtual HIViewRef attachView (WindowRef windowRef, HIViewRef rootView) = 0; - virtual void removeView (HIViewRef embeddedView) = 0; - virtual void mouseDown (int x, int y) {} - virtual void paint() {} - - virtual bool getEmbeddedViewSize (int& w, int& h) - { - if (embeddedView == 0) - return false; - - HIRect bounds; - HIViewGetBounds (embeddedView, &bounds); - w = jmax (1, roundFloatToInt (bounds.size.width)); - h = jmax (1, roundFloatToInt (bounds.size.height)); - return true; - } - - void createWindow() - { - if (wrapperWindow == 0) - { - Rect r; - r.left = getScreenX(); - r.top = getScreenY(); - r.right = r.left + getWidth(); - r.bottom = r.top + getHeight(); - - CreateNewWindow (kDocumentWindowClass, - (WindowAttributes) (kWindowStandardHandlerAttribute | kWindowCompositingAttribute - | kWindowNoShadowAttribute | kWindowNoTitleBarAttribute), - &r, &wrapperWindow); - - jassert (wrapperWindow != 0); - if (wrapperWindow == 0) - return; - - NSWindow* carbonWindow = [[NSWindow alloc] initWithWindowRef: wrapperWindow]; - NSWindow* ownerWindow = [((NSView*) getWindowHandle()) window]; - - [ownerWindow addChildWindow: carbonWindow - ordered: NSWindowAbove]; - - embeddedView = attachView (wrapperWindow, HIViewGetRoot (wrapperWindow)); - - EventTypeSpec windowEventTypes[] = { { kEventClassWindow, kEventWindowGetClickActivation }, - { kEventClassWindow, kEventWindowHandleDeactivate } }; - - EventHandlerUPP upp = NewEventHandlerUPP (carbonEventCallback); - InstallWindowEventHandler (wrapperWindow, upp, - sizeof (windowEventTypes) / sizeof (EventTypeSpec), - windowEventTypes, this, &eventHandlerRef); - - setOurSizeToEmbeddedViewSize(); - setEmbeddedWindowToOurSize(); - } - } - - void deleteWindow() - { - removeView (embeddedView); - embeddedView = 0; - - if (wrapperWindow != 0) - { - RemoveEventHandler (eventHandlerRef); - DisposeWindow (wrapperWindow); - wrapperWindow = 0; - } - } - - void setOurSizeToEmbeddedViewSize() - { - int w, h; - if (getEmbeddedViewSize (w, h)) - { - if (w != getWidth() || h != getHeight()) - { - startTimer (50); - - setSize (w, h); - if (getParentComponent() != 0) - getParentComponent()->setSize (w, h); - } - else - { - startTimer (jlimit (50, 500, getTimerInterval() + 20)); - } - } - else - { - stopTimer(); - } - } - - void setEmbeddedWindowToOurSize() - { - if (! recursiveResize) - { - recursiveResize = true; - - if (embeddedView != 0) - { - HIRect r; - r.origin.x = 0; - r.origin.y = 0; - r.size.width = (float) getWidth(); - r.size.height = (float) getHeight(); - HIViewSetFrame (embeddedView, &r); - } - - if (wrapperWindow != 0) - { - Rect wr; - wr.left = getScreenX(); - wr.top = getScreenY(); - wr.right = wr.left + getWidth(); - wr.bottom = wr.top + getHeight(); - - SetWindowBounds (wrapperWindow, kWindowContentRgn, &wr); - ShowWindow (wrapperWindow); - } - - recursiveResize = false; - } - } - - void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) - { - setEmbeddedWindowToOurSize(); - } - - void componentPeerChanged() - { - deleteWindow(); - createWindow(); - } - - void componentVisibilityChanged (Component&) - { - if (isShowing()) - createWindow(); - else - deleteWindow(); - - setEmbeddedWindowToOurSize(); - } - - void timerCallback() - { - setOurSizeToEmbeddedViewSize(); - } - - OSStatus carbonEventHandler (EventHandlerCallRef nextHandlerRef, - EventRef event) - { - switch (GetEventKind (event)) - { - case kEventWindowHandleDeactivate: - ActivateWindow (wrapperWindow, TRUE); - break; - - case kEventWindowGetClickActivation: - { - getTopLevelComponent()->toFront (false); - - ClickActivationResult howToHandleClick = kActivateAndHandleClick; - - SetEventParameter (event, kEventParamClickActivation, typeClickActivationResult, - sizeof (ClickActivationResult), &howToHandleClick); - } - break; - } - - return noErr; - } - - static pascal OSStatus carbonEventCallback (EventHandlerCallRef nextHandlerRef, - EventRef event, void* userData) - { - return ((CarbonViewWrapperComponent*) userData)->carbonEventHandler (nextHandlerRef, event); - } - -protected: - WindowRef wrapperWindow; - HIViewRef embeddedView; - bool recursiveResize; - - EventHandlerRef eventHandlerRef; -}; -/********* End of inlined file: juce_mac_CarbonViewWrapperComponent.h *********/ - #endif #undef PRAGMA_ALIGN_SUPPORTED @@ -32021,13 +32247,13 @@ VSTPluginFormat::~VSTPluginFormat() } void VSTPluginFormat::findAllTypesForFile (OwnedArray & results, - const File& file) + const String& fileOrIdentifier) { - if (! fileMightContainThisPluginType (file)) + if (! fileMightContainThisPluginType (fileOrIdentifier)) return; PluginDescription desc; - desc.fileOrIdentifier = file.getFullPathName(); + desc.fileOrIdentifier = fileOrIdentifier; desc.uid = 0; VSTPluginInstance* instance = dynamic_cast (createInstanceFromDescription (desc)); @@ -32105,10 +32331,10 @@ AudioPluginInstance* VSTPluginFormat::createInstanceFromDescription (const Plugi { VSTPluginInstance* result = 0; - File file (desc.fileOrIdentifier); - - if (fileMightContainThisPluginType (file)) + if (fileMightContainThisPluginType (desc.fileOrIdentifier)) { + File file (desc.fileOrIdentifier); + const File previousWorkingDirectory (File::getCurrentWorkingDirectory()); file.getParentDirectory().setAsCurrentWorkingDirectory(); @@ -32137,8 +32363,10 @@ AudioPluginInstance* VSTPluginFormat::createInstanceFromDescription (const Plugi return result; } -bool VSTPluginFormat::fileMightContainThisPluginType (const File& f) +bool VSTPluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier) { + const File f (fileOrIdentifier); + #if JUCE_MAC if (f.isDirectory() && f.hasFileExtension (T(".vst"))) return true; @@ -32170,11 +32398,48 @@ bool VSTPluginFormat::fileMightContainThisPluginType (const File& f) #endif } +const String VSTPluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier) +{ + return fileOrIdentifier; +} + bool VSTPluginFormat::doesPluginStillExist (const PluginDescription& desc) { return File (desc.fileOrIdentifier).exists(); } +const StringArray VSTPluginFormat::searchPathsForPlugins (const FileSearchPath& directoriesToSearch, const bool recursive) +{ + StringArray results; + + for (int j = 0; j < directoriesToSearch.getNumPaths(); ++j) + recursiveFileSearch (results, directoriesToSearch [j], recursive); + + return results; +} + +void VSTPluginFormat::recursiveFileSearch (StringArray& results, const File& dir, const bool recursive) +{ + // avoid allowing the dir iterator to be recursive, because we want to avoid letting it delve inside + // .component or .vst directories. + DirectoryIterator iter (dir, false, "*", File::findFilesAndDirectories); + + while (iter.next()) + { + const File f (iter.getFile()); + bool isPlugin = false; + + if (fileMightContainThisPluginType (f.getFullPathName())) + { + isPlugin = true; + results.add (f.getFullPathName()); + } + + if (recursive && (! isPlugin) && f.isDirectory()) + recursiveFileSearch (results, f, true); + } +} + const FileSearchPath VSTPluginFormat::getDefaultLocationsToSearch() { #if JUCE_MAC @@ -266567,6 +266832,9 @@ public: { NSString* text = juceStringToNS (iter.itemName.upToFirstOccurrenceOf (T(""), false, true)); + if (text == 0) + text = @""; + if (iter.isSeparator) { [menuToAddTo addItem: [NSMenuItem separatorItem]]; diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 8c6bfff3b1..7d01680eb4 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -27836,8 +27836,8 @@ public: /** Either the file containing the plugin module, or some other unique way of identifying it. - E.g. for an AU, this would be the component ID, because not all AUs actually - live in a file... + E.g. for an AU, this would be an ID string that the component manager + could use to retrieve the plugin. For a VST, it's the file path. */ String fileOrIdentifier; @@ -27965,7 +27965,7 @@ public: subtypes, so in that case, each subtype is returned as a separate object. */ virtual void findAllTypesForFile (OwnedArray & results, - const File& file) = 0; + const String& fileOrIdentifier) = 0; /** Tries to recreate a type from a previously generated PluginDescription. @@ -27979,7 +27979,11 @@ public: This is for searching for potential files, so it shouldn't actually try to load the plugin or do anything time-consuming. */ - virtual bool fileMightContainThisPluginType (const File& file) = 0; + virtual bool fileMightContainThisPluginType (const String& fileOrIdentifier) = 0; + + /** Returns a readable version of the name of the plugin that this identifier refers to. + */ + virtual const String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) = 0; /** Checks whether this plugin could possibly be loaded. @@ -27988,6 +27992,14 @@ public: */ virtual bool doesPluginStillExist (const PluginDescription& desc) = 0; + /** Searches a suggested set of directories for any plugins in this format. + + The path might be ignored, e.g. by AUs, which are found by the OS rather + than manually. + */ + virtual const StringArray searchPathsForPlugins (const FileSearchPath& directoriesToSearch, + const bool recursive) = 0; + /** Returns the typical places to look for this kind of plugin. Note that if this returns no paths, it means that the format can't be scanned-for @@ -28541,7 +28553,7 @@ public: /** Looks for a type in the list which comes from this file. */ - PluginDescription* getTypeForFile (const File& file) const throw(); + PluginDescription* getTypeForFile (const String& fileOrIdentifier) const throw(); /** Looks for a type in the list which matches a plugin type ID. @@ -28568,14 +28580,15 @@ public: file (even if it was already known and hasn't been re-scanned) get returned in the array. */ - bool scanAndAddFile (const File& possiblePluginFile, + bool scanAndAddFile (const String& possiblePluginFileOrIdentifier, const bool dontRescanIfAlreadyInList, - OwnedArray & typesFound); + OwnedArray & typesFound, + AudioPluginFormat& formatToUse); /** Returns true if the specified file is already known about and if it hasn't been modified since our entry was created. */ - bool isListingUpToDate (const File& possiblePluginFile) const throw(); + bool isListingUpToDate (const String& possiblePluginFileOrIdentifier) const throw(); /** Scans and adds a bunch of files that might have been dragged-and-dropped. @@ -34678,9 +34691,11 @@ public: ~AudioUnitPluginFormat(); const String getName() const { return "AudioUnit"; } - void findAllTypesForFile (OwnedArray & results, const File& file); + void findAllTypesForFile (OwnedArray & results, const String& fileOrIdentifier); AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); - bool fileMightContainThisPluginType (const File& file); + bool fileMightContainThisPluginType (const String& fileOrIdentifier); + const String getNameOfPluginFromIdentifier (const String& fileOrIdentifier); + const StringArray searchPathsForPlugins (const FileSearchPath& directoriesToSearch, const bool recursive); bool doesPluginStillExist (const PluginDescription& desc); const FileSearchPath getDefaultLocationsToSearch(); @@ -34718,9 +34733,10 @@ public: ~DirectXPluginFormat(); const String getName() const { return "DirectX"; } - void findAllTypesForFile (OwnedArray & results, const File& file); + void findAllTypesForFile (OwnedArray & results, const String& fileOrIdentifier); AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); - bool fileMightContainThisPluginType (const File& file); + bool fileMightContainThisPluginType (const String& fileOrIdentifier); + const String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) { return fileOrIdentifier; } const FileSearchPath getDefaultLocationsToSearch(); juce_UseDebuggingNewOperator @@ -34757,9 +34773,10 @@ public: ~LADSPAPluginFormat(); const String getName() const { return "LADSPA"; } - void findAllTypesForFile (OwnedArray & results, const File& file); + void findAllTypesForFile (OwnedArray & results, const String& fileOrIdentifier); AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); - bool fileMightContainThisPluginType (const File& file); + bool fileMightContainThisPluginType (const String& fileOrIdentifier); + const String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) { return fileOrIdentifier; } const FileSearchPath getDefaultLocationsToSearch(); juce_UseDebuggingNewOperator @@ -34794,9 +34811,11 @@ public: ~VSTPluginFormat(); const String getName() const { return "VST"; } - void findAllTypesForFile (OwnedArray & results, const File& file); + void findAllTypesForFile (OwnedArray & results, const String& fileOrIdentifier); AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); - bool fileMightContainThisPluginType (const File& file); + bool fileMightContainThisPluginType (const String& fileOrIdentifier); + const String getNameOfPluginFromIdentifier (const String& fileOrIdentifier); + const StringArray searchPathsForPlugins (const FileSearchPath& directoriesToSearch, const bool recursive); bool doesPluginStillExist (const PluginDescription& desc); const FileSearchPath getDefaultLocationsToSearch(); @@ -34805,6 +34824,8 @@ public: private: VSTPluginFormat (const VSTPluginFormat&); const VSTPluginFormat& operator= (const VSTPluginFormat&); + + void recursiveFileSearch (StringArray& results, const File& dir, const bool recursive); }; #endif @@ -34884,12 +34905,13 @@ public: */ bool scanNextFile (const bool dontRescanIfAlreadyInList); - /** Returns the file that will be scanned during the next call to scanNextFile(). + /** Returns the description of the plugin that will be scanned during the next + call to scanNextFile(). This is handy if you want to show the user which file is currently getting scanned. */ - const File getNextPluginFileThatWillBeScanned() const throw(); + const String getNextPluginFileThatWillBeScanned() const throw(); /** Returns the estimated progress, between 0 and 1. */ @@ -34905,13 +34927,12 @@ public: private: KnownPluginList& list; AudioPluginFormat& format; - OwnedArray filesToScan; + StringArray filesOrIdentifiersToScan; File deadMansPedalFile; StringArray failedFiles; int nextIndex; float progress; - void recursiveFileSearch (const File& dir, const bool recursive); const StringArray getDeadMansPedalFile() throw(); void setDeadMansPedalFile (const StringArray& newContents) throw(); diff --git a/src/juce_appframework/audio/audio_file_formats/juce_AudioThumbnail.cpp b/src/juce_appframework/audio/audio_file_formats/juce_AudioThumbnail.cpp index fea5822acb..eb464f1ba8 100644 --- a/src/juce_appframework/audio/audio_file_formats/juce_AudioThumbnail.cpp +++ b/src/juce_appframework/audio/audio_file_formats/juce_AudioThumbnail.cpp @@ -510,7 +510,7 @@ void AudioThumbnail::drawChannel (Graphics& g, const float vscale = verticalZoomFactor * h / 256.0f; const Rectangle clip (g.getClipBounds()); - const int skipLeft = clip.getX() - x; + const int skipLeft = jlimit (0, w, clip.getX() - x); w -= skipLeft; x += skipLeft; diff --git a/src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.cpp b/src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.cpp deleted file mode 100644 index aac479768e..0000000000 --- a/src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.cpp +++ /dev/null @@ -1,1320 +0,0 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-7 by Raw Material Software ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the - GNU General Public License, as published by the Free Software Foundation; - either version 2 of the License, or (at your option) any later version. - - JUCE is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with JUCE; if not, visit www.gnu.org/licenses or write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA - - ------------------------------------------------------------------------------ - - If you'd like to release a closed-source product which uses JUCE, commercial - licenses are also available: visit www.rawmaterialsoftware.com/juce for - more information. - - ============================================================================== -*/ - -#include "../../../../../juce_Config.h" - -#if JUCE_PLUGINHOST_AU && (! (defined (LINUX) || defined (_WIN32))) - -#include -#include -#include - -#include "../../../../juce_core/basics/juce_StandardHeader.h" - -BEGIN_JUCE_NAMESPACE - -#include "juce_AudioUnitPluginFormat.h" -#include "../juce_PluginDescription.h" -#include "../../../../juce_core/threads/juce_ScopedLock.h" -#include "../../../../juce_appframework/events/juce_Timer.h" -#include "../../../../juce_core/misc/juce_PlatformUtilities.h" -#include "../../../../juce_appframework/gui/components/layout/juce_ComponentMovementWatcher.h" - -#if JUCE_MAC - -extern void juce_callAnyTimersSynchronously(); -extern bool juce_isHIViewCreatedByJuce (HIViewRef view); -extern bool juce_isWindowCreatedByJuce (WindowRef window); - -#if MACOS_10_3_OR_EARLIER - #define kAudioUnitType_Generator 'augn' -#endif - -// Change this to disable logging of various activities -#ifndef AU_LOGGING - #define AU_LOGGING 1 -#endif - -#if AU_LOGGING - #define log(a) Logger::writeToLog(a); -#else - #define log(a) -#endif - -static int insideCallback = 0; - -//============================================================================== -class AudioUnitPluginWindow; - -//============================================================================== -class AudioUnitPluginInstance : public AudioPluginInstance -{ -public: - //============================================================================== - ~AudioUnitPluginInstance(); - - //============================================================================== - // AudioPluginInstance methods: - - void fillInPluginDescription (PluginDescription& desc) const - { - desc.name = pluginName; - desc.file = file; - desc.uid = ((int) componentDesc.componentType) - ^ ((int) componentDesc.componentSubType) - ^ ((int) componentDesc.componentManufacturer); - desc.lastFileModTime = file.getLastModificationTime(); - desc.pluginFormatName = "AudioUnit"; - desc.category = getCategory(); - desc.manufacturerName = manufacturer; - desc.version = version; - desc.numInputChannels = getNumInputChannels(); - desc.numOutputChannels = getNumOutputChannels(); - desc.isInstrument = (componentDesc.componentType == kAudioUnitType_MusicDevice); - } - - const String getName() const { return pluginName; } - bool acceptsMidi() const { return wantsMidiMessages; } - bool producesMidi() const { return false; } - - //============================================================================== - // AudioProcessor methods: - - void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); - void releaseResources(); - void processBlock (AudioSampleBuffer& buffer, - MidiBuffer& midiMessages); - - AudioProcessorEditor* createEditor(); - - const String getInputChannelName (const int index) const; - bool isInputChannelStereoPair (int index) const; - - const String getOutputChannelName (const int index) const; - bool isOutputChannelStereoPair (int index) const; - - //============================================================================== - int getNumParameters(); - float getParameter (int index); - void setParameter (int index, float newValue); - const String getParameterName (int index); - const String getParameterText (int index); - bool isParameterAutomatable (int index) const; - - //============================================================================== - int getNumPrograms(); - int getCurrentProgram(); - void setCurrentProgram (int index); - const String getProgramName (int index); - void changeProgramName (int index, const String& newName); - - //============================================================================== - void getStateInformation (MemoryBlock& destData); - void getCurrentProgramStateInformation (MemoryBlock& destData); - void setStateInformation (const void* data, int sizeInBytes); - void setCurrentProgramStateInformation (const void* data, int sizeInBytes); - - //============================================================================== - juce_UseDebuggingNewOperator - -private: - friend class AudioUnitPluginWindow; - friend class AudioUnitPluginFormat; - - ComponentDescription componentDesc; - String pluginName, manufacturer, version; - File file; - CriticalSection lock; - bool initialised, wantsMidiMessages, wasPlaying; - - AudioBufferList* outputBufferList; - AudioTimeStamp timeStamp; - AudioSampleBuffer* currentBuffer; - - AudioUnit audioUnit; - Array parameterIds; - - //============================================================================== - bool getComponentDescFromFile (const File& file); - void initialise(); - - //============================================================================== - OSStatus renderGetInput (AudioUnitRenderActionFlags* ioActionFlags, - const AudioTimeStamp* inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList* ioData) const; - - static OSStatus renderGetInputCallback (void* inRefCon, - AudioUnitRenderActionFlags* ioActionFlags, - const AudioTimeStamp* inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList* ioData) - { - return ((AudioUnitPluginInstance*) inRefCon) - ->renderGetInput (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); - } - - OSStatus getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const; - OSStatus getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat, Float32* outTimeSig_Numerator, - UInt32* outTimeSig_Denominator, Float64* outCurrentMeasureDownBeat) const; - OSStatus getTransportState (Boolean* outIsPlaying, Boolean* outTransportStateChanged, - Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling, - Float64* outCycleStartBeat, Float64* outCycleEndBeat); - - static OSStatus getBeatAndTempoCallback (void* inHostUserData, Float64* outCurrentBeat, Float64* outCurrentTempo) - { - return ((AudioUnitPluginInstance*) inHostUserData)->getBeatAndTempo (outCurrentBeat, outCurrentTempo); - } - - static OSStatus getMusicalTimeLocationCallback (void* inHostUserData, UInt32* outDeltaSampleOffsetToNextBeat, - Float32* outTimeSig_Numerator, UInt32* outTimeSig_Denominator, - Float64* outCurrentMeasureDownBeat) - { - return ((AudioUnitPluginInstance*) inHostUserData) - ->getMusicalTimeLocation (outDeltaSampleOffsetToNextBeat, outTimeSig_Numerator, - outTimeSig_Denominator, outCurrentMeasureDownBeat); - } - - static OSStatus getTransportStateCallback (void* inHostUserData, Boolean* outIsPlaying, Boolean* outTransportStateChanged, - Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling, - Float64* outCycleStartBeat, Float64* outCycleEndBeat) - { - return ((AudioUnitPluginInstance*) inHostUserData) - ->getTransportState (outIsPlaying, outTransportStateChanged, - outCurrentSampleInTimeLine, outIsCycling, - outCycleStartBeat, outCycleEndBeat); - } - - //============================================================================== - void getNumChannels (int& numIns, int& numOuts) - { - numIns = 0; - numOuts = 0; - - AUChannelInfo supportedChannels [128]; - UInt32 supportedChannelsSize = sizeof (supportedChannels); - - if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global, - 0, supportedChannels, &supportedChannelsSize) == noErr - && supportedChannelsSize > 0) - { - for (int i = 0; i < supportedChannelsSize / sizeof (AUChannelInfo); ++i) - { - numIns = jmax (numIns, supportedChannels[i].inChannels); - numOuts = jmax (numOuts, supportedChannels[i].outChannels); - } - } - else - { - // (this really means the plugin will take any number of ins/outs as long - // as they are the same) - numIns = numOuts = 2; - } - } - - const String getCategory() const; - - //============================================================================== - AudioUnitPluginInstance (const File& file); -}; - -//============================================================================== -AudioUnitPluginInstance::AudioUnitPluginInstance (const File& file_) - : file (file_), - initialised (false), - wantsMidiMessages (false), - audioUnit (0), - outputBufferList (0), - currentBuffer (0) -{ - try - { - ++insideCallback; - - log (T("Opening AU: ") + file.getFullPathName()); - - if (getComponentDescFromFile (file)) - { - ComponentRecord* const comp = FindNextComponent (0, &componentDesc); - - if (comp != 0) - { - audioUnit = (AudioUnit) OpenComponent (comp); - - wantsMidiMessages = componentDesc.componentType == kAudioUnitType_MusicDevice - || componentDesc.componentType == kAudioUnitType_MusicEffect; - } - } - - --insideCallback; - } - catch (...) - { - --insideCallback; - } -} - -AudioUnitPluginInstance::~AudioUnitPluginInstance() -{ - { - const ScopedLock sl (lock); - - jassert (insideCallback == 0); - - if (audioUnit != 0) - { - AudioUnitUninitialize (audioUnit); - CloseComponent (audioUnit); - audioUnit = 0; - } - } - - juce_free (outputBufferList); -} - -bool AudioUnitPluginInstance::getComponentDescFromFile (const File& file) -{ - zerostruct (componentDesc); - - if (! file.hasFileExtension (T(".component"))) - return false; - - const String filename (file.getFullPathName()); - const char* const utf8 = filename.toUTF8(); - CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, - strlen (utf8), file.isDirectory()); - if (url != 0) - { - CFBundleRef bundleRef = CFBundleCreate (kCFAllocatorDefault, url); - CFRelease (url); - - if (bundleRef != 0) - { - CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName")); - - if (name != 0 && CFGetTypeID (name) == CFStringGetTypeID()) - pluginName = PlatformUtilities::cfStringToJuceString ((CFStringRef) name); - - if (pluginName.isEmpty()) - pluginName = file.getFileNameWithoutExtension(); - - CFTypeRef versionString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleVersion")); - - if (versionString != 0 && CFGetTypeID (versionString) == CFStringGetTypeID()) - version = PlatformUtilities::cfStringToJuceString ((CFStringRef) versionString); - - CFTypeRef manuString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleGetInfoString")); - - if (manuString != 0 && CFGetTypeID (manuString) == CFStringGetTypeID()) - manufacturer = PlatformUtilities::cfStringToJuceString ((CFStringRef) manuString); - - short resFileId = CFBundleOpenBundleResourceMap (bundleRef); - UseResFile (resFileId); - - for (int i = 1; i <= Count1Resources ('thng'); ++i) - { - Handle h = Get1IndResource ('thng', i); - - if (h != 0) - { - HLock (h); - const uint32* const types = (const uint32*) *h; - - if (types[0] == kAudioUnitType_MusicDevice - || types[0] == kAudioUnitType_MusicEffect - || types[0] == kAudioUnitType_Effect - || types[0] == kAudioUnitType_Generator - || types[0] == kAudioUnitType_Panner) - { - componentDesc.componentType = types[0]; - componentDesc.componentSubType = types[1]; - componentDesc.componentManufacturer = types[2]; - break; - } - - HUnlock (h); - ReleaseResource (h); - } - } - - CFBundleCloseBundleResourceMap (bundleRef, resFileId); - CFRelease (bundleRef); - } - } - - return componentDesc.componentType != 0 && componentDesc.componentSubType != 0; -} - -//============================================================================== -void AudioUnitPluginInstance::initialise() -{ - if (initialised || audioUnit == 0) - return; - - log (T("Initialising AU: ") + pluginName); - - parameterIds.clear(); - - { - UInt32 paramListSize = 0; - AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, - 0, 0, ¶mListSize); - - if (paramListSize > 0) - { - parameterIds.insertMultiple (0, 0, paramListSize / sizeof (int)); - - AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, - 0, ¶meterIds.getReference(0), ¶mListSize); - } - } - - { - AURenderCallbackStruct info; - zerostruct (info); - info.inputProcRefCon = this; - info.inputProc = renderGetInputCallback; - - AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, - 0, &info, sizeof (info)); - } - - { - HostCallbackInfo info; - zerostruct (info); - info.hostUserData = this; - info.beatAndTempoProc = getBeatAndTempoCallback; - info.musicalTimeLocationProc = getMusicalTimeLocationCallback; - info.transportStateProc = getTransportStateCallback; - - AudioUnitSetProperty (audioUnit, kAudioUnitProperty_HostCallbacks, kAudioUnitScope_Global, - 0, &info, sizeof (info)); - } - - int numIns, numOuts; - getNumChannels (numIns, numOuts); - setPlayConfigDetails (numIns, numOuts, 0, 0); - - initialised = AudioUnitInitialize (audioUnit) == noErr; - - setLatencySamples (0); -} - - -//============================================================================== -void AudioUnitPluginInstance::prepareToPlay (double sampleRate_, - int samplesPerBlockExpected) -{ - initialise(); - - if (initialised) - { - int numIns, numOuts; - getNumChannels (numIns, numOuts); - - setPlayConfigDetails (numIns, numOuts, sampleRate_, samplesPerBlockExpected); - - Float64 latencySecs = 0.0; - UInt32 latencySize = sizeof (latencySecs); - AudioUnitGetProperty (audioUnit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, - 0, &latencySecs, &latencySize); - - setLatencySamples (roundDoubleToInt (latencySecs * sampleRate_)); - - AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0); - AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0); - AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0); - - AudioStreamBasicDescription stream; - zerostruct (stream); - stream.mSampleRate = sampleRate_; - stream.mFormatID = kAudioFormatLinearPCM; - stream.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; - stream.mFramesPerPacket = 1; - stream.mBytesPerPacket = 4; - stream.mBytesPerFrame = 4; - stream.mBitsPerChannel = 32; - stream.mChannelsPerFrame = numIns; - - OSStatus err = AudioUnitSetProperty (audioUnit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, - 0, &stream, sizeof (stream)); - - stream.mChannelsPerFrame = numOuts; - - err = AudioUnitSetProperty (audioUnit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Output, - 0, &stream, sizeof (stream)); - - juce_free (outputBufferList); - outputBufferList = (AudioBufferList*) juce_calloc (sizeof (AudioBufferList) + sizeof (AudioBuffer) * (numOuts + 1)); - outputBufferList->mNumberBuffers = numOuts; - - for (int i = numOuts; --i >= 0;) - outputBufferList->mBuffers[i].mNumberChannels = 1; - - zerostruct (timeStamp); - timeStamp.mSampleTime = 0; - timeStamp.mHostTime = AudioGetCurrentHostTime(); - timeStamp.mFlags = kAudioTimeStampSampleTimeValid | kAudioTimeStampHostTimeValid; - - currentBuffer = 0; - wasPlaying = false; - } -} - -void AudioUnitPluginInstance::releaseResources() -{ - if (initialised) - { - AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0); - AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0); - AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0); - - juce_free (outputBufferList); - outputBufferList = 0; - currentBuffer = 0; - } -} - -OSStatus AudioUnitPluginInstance::renderGetInput (AudioUnitRenderActionFlags* ioActionFlags, - const AudioTimeStamp* inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList* ioData) const -{ - if (inBusNumber == 0 - && currentBuffer != 0) - { - jassert (inNumberFrames == currentBuffer->getNumSamples()); // if this ever happens, might need to add extra handling - - for (int i = 0; i < ioData->mNumberBuffers; ++i) - { - if (i < currentBuffer->getNumChannels()) - { - memcpy (ioData->mBuffers[i].mData, - currentBuffer->getSampleData (i, 0), - sizeof (float) * inNumberFrames); - } - else - { - zeromem (ioData->mBuffers[i].mData, sizeof (float) * inNumberFrames); - } - } - } - - return noErr; -} - -void AudioUnitPluginInstance::processBlock (AudioSampleBuffer& buffer, - MidiBuffer& midiMessages) -{ - const int numSamples = buffer.getNumSamples(); - - if (initialised) - { - AudioUnitRenderActionFlags flags = 0; - - timeStamp.mHostTime = AudioGetCurrentHostTime(); - - for (int i = getNumOutputChannels(); --i >= 0;) - { - outputBufferList->mBuffers[i].mDataByteSize = sizeof (float) * numSamples; - outputBufferList->mBuffers[i].mData = buffer.getSampleData (i, 0); - } - - currentBuffer = &buffer; - - if (wantsMidiMessages) - { - const uint8* midiEventData; - int midiEventSize, midiEventPosition; - MidiBuffer::Iterator i (midiMessages); - - while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition)) - { - if (midiEventSize <= 3) - MusicDeviceMIDIEvent (audioUnit, - midiEventData[0], midiEventData[1], midiEventData[2], - midiEventPosition); - else - MusicDeviceSysEx (audioUnit, midiEventData, midiEventSize); - } - - midiMessages.clear(); - } - - AudioUnitRender (audioUnit, &flags, &timeStamp, - 0, numSamples, outputBufferList); - - timeStamp.mSampleTime += numSamples; - } - else - { - // Not initialised, so just bypass.. - for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i) - buffer.clear (i, 0, buffer.getNumSamples()); - } -} - -//============================================================================== -OSStatus AudioUnitPluginInstance::getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const -{ - AudioPlayHead* const ph = getPlayHead(); - AudioPlayHead::CurrentPositionInfo result; - - if (ph != 0 && ph->getCurrentPosition (result)) - { - *outCurrentBeat = result.ppqPosition; - *outCurrentTempo = result.bpm; - } - else - { - *outCurrentBeat = 0; - *outCurrentTempo = 120.0; - } - - return noErr; -} - -OSStatus AudioUnitPluginInstance::getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat, - Float32* outTimeSig_Numerator, - UInt32* outTimeSig_Denominator, - Float64* outCurrentMeasureDownBeat) const -{ - AudioPlayHead* const ph = getPlayHead(); - AudioPlayHead::CurrentPositionInfo result; - - if (ph != 0 && ph->getCurrentPosition (result)) - { - *outTimeSig_Numerator = result.timeSigNumerator; - *outTimeSig_Denominator = result.timeSigDenominator; - - *outDeltaSampleOffsetToNextBeat = 0; //xxx - *outCurrentMeasureDownBeat = result.ppqPositionOfLastBarStart; //xxx wrong - } - else - { - *outDeltaSampleOffsetToNextBeat = 0; - *outTimeSig_Numerator = 4; - *outTimeSig_Denominator = 4; - *outCurrentMeasureDownBeat = 0; - } - - return noErr; -} - -OSStatus AudioUnitPluginInstance::getTransportState (Boolean* outIsPlaying, - Boolean* outTransportStateChanged, - Float64* outCurrentSampleInTimeLine, - Boolean* outIsCycling, - Float64* outCycleStartBeat, - Float64* outCycleEndBeat) -{ - AudioPlayHead* const ph = getPlayHead(); - AudioPlayHead::CurrentPositionInfo result; - - if (ph != 0 && ph->getCurrentPosition (result)) - { - if (outIsPlaying != 0) - *outIsPlaying = result.isPlaying; - - if (outTransportStateChanged != 0) - *outTransportStateChanged = result.isPlaying != wasPlaying; - - wasPlaying = result.isPlaying; - - if (outCurrentSampleInTimeLine != 0) - *outCurrentSampleInTimeLine = roundDoubleToInt (result.timeInSeconds * getSampleRate()); - - if (outIsCycling != 0) - *outIsCycling = false; - - if (outCycleStartBeat != 0) - *outCycleStartBeat = 0; - - if (outCycleEndBeat != 0) - *outCycleEndBeat = 0; - } - else - { - if (outIsPlaying != 0) - *outIsPlaying = false; - - if (outTransportStateChanged != 0) - *outTransportStateChanged = false; - - if (outCurrentSampleInTimeLine != 0) - *outCurrentSampleInTimeLine = 0; - - if (outIsCycling != 0) - *outIsCycling = false; - - if (outCycleStartBeat != 0) - *outCycleStartBeat = 0; - - if (outCycleEndBeat != 0) - *outCycleEndBeat = 0; - } - - return noErr; -} - - -//============================================================================== -static VoidArray activeWindows; - -//============================================================================== -class AudioUnitPluginWindow : public AudioProcessorEditor, - public Timer -{ -public: - //============================================================================== - AudioUnitPluginWindow (AudioUnitPluginInstance& plugin_) - : AudioProcessorEditor (&plugin_), - plugin (plugin_), - isOpen (false), - pluginWantsKeys (false), - wasShowing (false), - recursiveResize (false), - viewComponent (0), - pluginViewRef (0) - { - movementWatcher = new CompMovementWatcher (this); - - activeWindows.add (this); - - setOpaque (true); - setVisible (true); - setSize (1, 1); - - ComponentDescription viewList [16]; - UInt32 viewListSize = sizeof (viewList); - AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global, - 0, &viewList, &viewListSize); - - componentRecord = FindNextComponent (0, &viewList[0]); - } - - ~AudioUnitPluginWindow() - { - deleteAndZero (movementWatcher); - - closePluginWindow(); - - activeWindows.removeValue (this); - plugin.editorBeingDeleted (this); - } - - bool isValid() const throw() { return componentRecord != 0; } - - //============================================================================== - void componentMovedOrResized() - { - if (recursiveResize) - return; - - Component* const topComp = getTopLevelComponent(); - - if (topComp->getPeer() != 0) - { - int x = 0, y = 0; - relativePositionToOtherComponent (topComp, x, y); - - recursiveResize = true; - - if (pluginViewRef != 0) - { - HIRect r; - r.origin.x = (float) x; - r.origin.y = (float) y; - r.size.width = (float) getWidth(); - r.size.height = (float) getHeight(); - HIViewSetFrame (pluginViewRef, &r); - } - - recursiveResize = false; - } - } - - void componentVisibilityChanged() - { - const bool isShowingNow = isShowing(); - - if (wasShowing != isShowingNow) - { - wasShowing = isShowingNow; - - if (isShowingNow) - openPluginWindow(); - else - closePluginWindow(); - } - - componentMovedOrResized(); - } - - void componentPeerChanged() - { - closePluginWindow(); - openPluginWindow(); - } - - void timerCallback() - { - if (pluginViewRef != 0) - { - HIRect bounds; - HIViewGetBounds (pluginViewRef, &bounds); - const int w = jmax (32, (int) bounds.size.width); - const int h = jmax (32, (int) bounds.size.height); - - if (w != getWidth() || h != getHeight()) - { - setSize (w, h); - startTimer (50); - } - else - { - startTimer (jlimit (50, 500, getTimerInterval() + 20)); - } - } - } - - //============================================================================== - bool keyStateChanged() - { - return pluginWantsKeys; - } - - bool keyPressed (const KeyPress&) - { - return pluginWantsKeys; - } - - //============================================================================== - void paint (Graphics& g) - { - if (isOpen) - { - ComponentPeer* const peer = getPeer(); - - if (peer != 0) - { - peer->addMaskedRegion (getScreenX() - peer->getScreenX(), - getScreenY() - peer->getScreenY(), - getWidth(), getHeight()); - } - } - else - { - g.fillAll (Colours::black); - } - } - - //============================================================================== - void broughtToFront() - { - activeWindows.removeValue (this); - activeWindows.add (this); - } - - //============================================================================== - juce_UseDebuggingNewOperator - -private: - AudioUnitPluginInstance& plugin; - bool isOpen, wasShowing, recursiveResize; - bool pluginWantsKeys; - - ComponentRecord* componentRecord; - AudioUnitCarbonView viewComponent; - HIViewRef pluginViewRef; - - //============================================================================== - void openPluginWindow() - { - if (isOpen || getWindowHandle() == 0 || componentRecord == 0) - return; - - log (T("Opening AU GUI: ") + plugin.getName()); - isOpen = true; - - pluginWantsKeys = true; //xxx any way to find this out? Does it matter? - - viewComponent = (AudioUnitCarbonView) OpenComponent (componentRecord); - - if (viewComponent != 0) - { - Float32Point pos = { getScreenX() - getTopLevelComponent()->getScreenX(), - getScreenY() - getTopLevelComponent()->getScreenY() }; - Float32Point size = { 250, 200 }; - - AudioUnitCarbonViewCreate (viewComponent, - plugin.audioUnit, - (WindowRef) getWindowHandle(), - HIViewGetRoot ((WindowRef) getWindowHandle()), - &pos, &size, - (ControlRef*) &pluginViewRef); - } - - timerCallback(); // to set our comp to the right size - repaint(); - } - - //============================================================================== - void closePluginWindow() - { - stopTimer(); - - if (isOpen) - { - log (T("Closing AU GUI: ") + plugin.getName()); - isOpen = false; - - if (viewComponent != 0) - CloseComponent (viewComponent); - - pluginViewRef = 0; - } - } - - //============================================================================== - class CompMovementWatcher : public ComponentMovementWatcher - { - public: - CompMovementWatcher (AudioUnitPluginWindow* const owner_) - : ComponentMovementWatcher (owner_), - owner (owner_) - { - } - - //============================================================================== - void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) - { - owner->componentMovedOrResized(); - } - - void componentPeerChanged() - { - owner->componentPeerChanged(); - } - - void componentVisibilityChanged (Component&) - { - owner->componentVisibilityChanged(); - } - - private: - AudioUnitPluginWindow* const owner; - }; - - CompMovementWatcher* movementWatcher; -}; - -//============================================================================== -AudioProcessorEditor* AudioUnitPluginInstance::createEditor() -{ - AudioUnitPluginWindow* w = new AudioUnitPluginWindow (*this); - - if (! w->isValid()) - deleteAndZero (w); - - return w; -} - - -//============================================================================== -const String AudioUnitPluginInstance::getCategory() const -{ - const char* result = 0; - - switch (componentDesc.componentType) - { - case kAudioUnitType_Effect: - case kAudioUnitType_MusicEffect: - result = "Effect"; - break; - case kAudioUnitType_MusicDevice: - result = "Synth"; - break; - case kAudioUnitType_Generator: - result = "Generator"; - break; - case kAudioUnitType_Panner: - result = "Panner"; - break; - default: - break; - } - - return result; -} - -//============================================================================== -int AudioUnitPluginInstance::getNumParameters() -{ - return parameterIds.size(); -} - -float AudioUnitPluginInstance::getParameter (int index) -{ - const ScopedLock sl (lock); - - Float32 value = 0.0f; - - if (audioUnit != 0 && ((unsigned int) index) < (unsigned int) parameterIds.size()) - { - AudioUnitGetParameter (audioUnit, - (UInt32) parameterIds.getUnchecked (index), - kAudioUnitScope_Global, 0, - &value); - } - - return value; -} - -void AudioUnitPluginInstance::setParameter (int index, float newValue) -{ - const ScopedLock sl (lock); - - if (audioUnit != 0 && ((unsigned int) index) < (unsigned int) parameterIds.size()) - { - AudioUnitSetParameter (audioUnit, - (UInt32) parameterIds.getUnchecked (index), - kAudioUnitScope_Global, 0, - newValue, 0); - } -} - -const String AudioUnitPluginInstance::getParameterName (int index) -{ - AudioUnitParameterInfo info; - zerostruct (info); - UInt32 sz = sizeof (info); - - String name; - - if (AudioUnitGetProperty (audioUnit, - kAudioUnitProperty_ParameterInfo, - kAudioUnitScope_Global, - parameterIds [index], &info, &sz) == noErr) - { - if ((info.flags & kAudioUnitParameterFlag_HasCFNameString) != 0) - name = PlatformUtilities::cfStringToJuceString (info.cfNameString); - else - name = String (info.name, sizeof (info.name)); - } - - return name; -} - -const String AudioUnitPluginInstance::getParameterText (int index) -{ - return String (getParameter (index)); -} - -bool AudioUnitPluginInstance::isParameterAutomatable (int index) const -{ - AudioUnitParameterInfo info; - UInt32 sz = sizeof (info); - - if (AudioUnitGetProperty (audioUnit, - kAudioUnitProperty_ParameterInfo, - kAudioUnitScope_Global, - parameterIds [index], &info, &sz) == noErr) - { - return (info.flags & kAudioUnitParameterFlag_NonRealTime) == 0; - } - - return true; -} - -//============================================================================== -int AudioUnitPluginInstance::getNumPrograms() -{ - CFArrayRef presets; - UInt32 sz = sizeof (CFArrayRef); - int num = 0; - - if (AudioUnitGetProperty (audioUnit, - kAudioUnitProperty_FactoryPresets, - kAudioUnitScope_Global, - 0, &presets, &sz) == noErr) - { - num = (int) CFArrayGetCount (presets); - CFRelease (presets); - } - - return num; -} - -int AudioUnitPluginInstance::getCurrentProgram() -{ - AUPreset current; - current.presetNumber = 0; - UInt32 sz = sizeof (AUPreset); - - AudioUnitGetProperty (audioUnit, - kAudioUnitProperty_FactoryPresets, - kAudioUnitScope_Global, - 0, ¤t, &sz); - - return current.presetNumber; -} - -void AudioUnitPluginInstance::setCurrentProgram (int newIndex) -{ - AUPreset current; - current.presetNumber = newIndex; - current.presetName = 0; - - AudioUnitSetProperty (audioUnit, - kAudioUnitProperty_FactoryPresets, - kAudioUnitScope_Global, - 0, ¤t, sizeof (AUPreset)); -} - -const String AudioUnitPluginInstance::getProgramName (int index) -{ - String s; - CFArrayRef presets; - UInt32 sz = sizeof (CFArrayRef); - - if (AudioUnitGetProperty (audioUnit, - kAudioUnitProperty_FactoryPresets, - kAudioUnitScope_Global, - 0, &presets, &sz) == noErr) - { - for (CFIndex i = 0; i < CFArrayGetCount (presets); ++i) - { - const AUPreset* p = (const AUPreset*) CFArrayGetValueAtIndex (presets, i); - - if (p != 0 && p->presetNumber == index) - { - s = PlatformUtilities::cfStringToJuceString (p->presetName); - break; - } - } - - CFRelease (presets); - } - - return s; -} - -void AudioUnitPluginInstance::changeProgramName (int index, const String& newName) -{ - jassertfalse // xxx not implemented! -} - -//============================================================================== -const String AudioUnitPluginInstance::getInputChannelName (const int index) const -{ - if (((unsigned int) index) < (unsigned int) getNumInputChannels()) - return T("Input ") + String (index + 1); - - return String::empty; -} - -bool AudioUnitPluginInstance::isInputChannelStereoPair (int index) const -{ - if (((unsigned int) index) >= (unsigned int) getNumInputChannels()) - return false; - - - return true; -} - -const String AudioUnitPluginInstance::getOutputChannelName (const int index) const -{ - if (((unsigned int) index) < (unsigned int) getNumOutputChannels()) - return T("Output ") + String (index + 1); - - return String::empty; -} - -bool AudioUnitPluginInstance::isOutputChannelStereoPair (int index) const -{ - if (((unsigned int) index) >= (unsigned int) getNumOutputChannels()) - return false; - - return true; -} - -//============================================================================== -void AudioUnitPluginInstance::getStateInformation (MemoryBlock& destData) -{ - getCurrentProgramStateInformation (destData); -} - -void AudioUnitPluginInstance::getCurrentProgramStateInformation (MemoryBlock& destData) -{ - CFPropertyListRef propertyList = 0; - UInt32 sz = sizeof (CFPropertyListRef); - - if (AudioUnitGetProperty (audioUnit, - kAudioUnitProperty_ClassInfo, - kAudioUnitScope_Global, - 0, &propertyList, &sz) == noErr) - { - CFWriteStreamRef stream = CFWriteStreamCreateWithAllocatedBuffers (kCFAllocatorDefault, kCFAllocatorDefault); - CFWriteStreamOpen (stream); - - CFIndex bytesWritten = CFPropertyListWriteToStream (propertyList, stream, kCFPropertyListBinaryFormat_v1_0, 0); - CFWriteStreamClose (stream); - - CFDataRef data = (CFDataRef) CFWriteStreamCopyProperty (stream, kCFStreamPropertyDataWritten); - - destData.setSize (bytesWritten); - destData.copyFrom (CFDataGetBytePtr (data), 0, destData.getSize()); - CFRelease (data); - - CFRelease (stream); - CFRelease (propertyList); - } -} - -void AudioUnitPluginInstance::setStateInformation (const void* data, int sizeInBytes) -{ - setCurrentProgramStateInformation (data, sizeInBytes); -} - -void AudioUnitPluginInstance::setCurrentProgramStateInformation (const void* data, int sizeInBytes) -{ - CFReadStreamRef stream = CFReadStreamCreateWithBytesNoCopy (kCFAllocatorDefault, - (const UInt8*) data, - sizeInBytes, - kCFAllocatorNull); - CFReadStreamOpen (stream); - - CFPropertyListFormat format = kCFPropertyListBinaryFormat_v1_0; - CFPropertyListRef propertyList = CFPropertyListCreateFromStream (kCFAllocatorDefault, - stream, - 0, - kCFPropertyListImmutable, - &format, - 0); - CFRelease (stream); - - if (propertyList != 0) - AudioUnitSetProperty (audioUnit, - kAudioUnitProperty_ClassInfo, - kAudioUnitScope_Global, - 0, &propertyList, sizeof (propertyList)); -} - -//============================================================================== -//============================================================================== -AudioUnitPluginFormat::AudioUnitPluginFormat() -{ -} - -AudioUnitPluginFormat::~AudioUnitPluginFormat() -{ -} - -void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray & results, - const File& file) -{ - if (! fileMightContainThisPluginType (file)) - return; - - PluginDescription desc; - desc.file = file; - desc.uid = 0; - - AudioUnitPluginInstance* instance = dynamic_cast (createInstanceFromDescription (desc)); - - if (instance == 0) - return; - - try - { - instance->fillInPluginDescription (desc); - results.add (new PluginDescription (desc)); - } - catch (...) - { - // crashed while loading... - } - - deleteAndZero (instance); -} - -AudioPluginInstance* AudioUnitPluginFormat::createInstanceFromDescription (const PluginDescription& desc) -{ - AudioUnitPluginInstance* result = 0; - - if (fileMightContainThisPluginType (desc.file)) - { - result = new AudioUnitPluginInstance (desc.file); - - if (result->audioUnit != 0) - { - result->initialise(); - } - else - { - deleteAndZero (result); - } - } - - return result; -} - -bool AudioUnitPluginFormat::fileMightContainThisPluginType (const File& f) -{ - return f.hasFileExtension (T(".component")) - && f.isDirectory(); -} - -bool AudioUnitPluginFormat::doesPluginStillExist (const PluginDescription& desc) -{ - return File (desc.fileOrIdentifier).exists(); -} - -const FileSearchPath AudioUnitPluginFormat::getDefaultLocationsToSearch() -{ - return FileSearchPath ("~/Library/Audio/Plug-Ins/Components;/Library/Audio/Plug-Ins/Components"); -} - -#endif - -END_JUCE_NAMESPACE - -#undef log - -#endif diff --git a/src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.h b/src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.h index 812a6b4a39..d7a12fad6d 100644 --- a/src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.h +++ b/src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.h @@ -49,9 +49,11 @@ public: //============================================================================== const String getName() const { return "AudioUnit"; } - void findAllTypesForFile (OwnedArray & results, const File& file); + void findAllTypesForFile (OwnedArray & results, const String& fileOrIdentifier); AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); - bool fileMightContainThisPluginType (const File& file); + bool fileMightContainThisPluginType (const String& fileOrIdentifier); + const String getNameOfPluginFromIdentifier (const String& fileOrIdentifier); + const StringArray searchPathsForPlugins (const FileSearchPath& directoriesToSearch, const bool recursive); bool doesPluginStillExist (const PluginDescription& desc); const FileSearchPath getDefaultLocationsToSearch(); diff --git a/src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.mm b/src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.mm index 0a4ee134d0..3482a5c1cd 100644 --- a/src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.mm +++ b/src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.mm @@ -81,6 +81,129 @@ BEGIN_JUCE_NAMESPACE static int insideCallback = 0; +//============================================================================== +static const String osTypeToString (OSType type) throw() +{ + char s[4]; + s[0] = (char) (((uint32) type) >> 24); + s[1] = (char) (((uint32) type) >> 16); + s[2] = (char) (((uint32) type) >> 8); + s[3] = (char) ((uint32) type); + return String (s, 4); +} + +static OSType stringToOSType (const String& s1) throw() +{ + const String s (s1 + " "); + + return (((OSType) (unsigned char) s[0]) << 24) + | (((OSType) (unsigned char) s[1]) << 16) + | (((OSType) (unsigned char) s[2]) << 8) + | ((OSType) (unsigned char) s[3]); +} + +static const tchar* auIdentifierPrefix = T("AudioUnit:"); + +static const String createAUPluginIdentifier (const ComponentDescription& desc) +{ + jassert (osTypeToString ('abcd') == T("abcd")); // agh, must have got the endianness wrong.. + jassert (stringToOSType ("abcd") == (OSType) 'abcd'); // ditto + + String s (auIdentifierPrefix); + + if (desc.componentType == kAudioUnitType_MusicDevice) + s << "Synths/"; + else if (desc.componentType == kAudioUnitType_MusicEffect + || desc.componentType == kAudioUnitType_Effect) + s << "Effects/"; + else if (desc.componentType == kAudioUnitType_Generator) + s << "Generators/"; + else if (desc.componentType == kAudioUnitType_Panner) + s << "Panners/"; + + s << osTypeToString (desc.componentType) + << T(",") + << osTypeToString (desc.componentSubType) + << T(",") + << osTypeToString (desc.componentManufacturer); + + return s; +} + +static void getAUDetails (ComponentRecord* comp, String& name, String& manufacturer) +{ + Handle componentNameHandle = NewHandle (sizeof (void*)); + Handle componentInfoHandle = NewHandle (sizeof (void*)); + + if (componentNameHandle != 0 && componentInfoHandle != 0) + { + ComponentDescription desc; + + if (GetComponentInfo (comp, &desc, componentNameHandle, componentInfoHandle, 0) == noErr) + { + ConstStr255Param nameString = (ConstStr255Param) (*componentNameHandle); + ConstStr255Param infoString = (ConstStr255Param) (*componentInfoHandle); + + if (nameString != 0 && nameString[0] != 0) + { + const String all ((const char*) nameString + 1, nameString[0]); +DBG ("name: "+ all); + + manufacturer = all.upToFirstOccurrenceOf (T(":"), false, false).trim(); + name = all.fromFirstOccurrenceOf (T(":"), false, false).trim(); + } + + if (infoString != 0 && infoString[0] != 0) + { + const String all ((const char*) infoString + 1, infoString[0]); +DBG ("info: " + all); + } + + if (name.isEmpty()) + name = ""; + } + + DisposeHandle (componentNameHandle); + DisposeHandle (componentInfoHandle); + } +} + +static bool getComponentDescFromIdentifier (const String& fileOrIdentifier, ComponentDescription& desc, + String& name, String& version, String& manufacturer) +{ + zerostruct (desc); + + if (fileOrIdentifier.startsWithIgnoreCase (auIdentifierPrefix)) + { + String s (fileOrIdentifier.substring (jmax (fileOrIdentifier.lastIndexOfChar (T(':')), + fileOrIdentifier.lastIndexOfChar (T('/'))) + 1)); + + StringArray tokens; + tokens.addTokens (s, T(","), 0); + tokens.trim(); + tokens.removeEmptyStrings(); + + if (tokens.size() == 3) + { + desc.componentType = stringToOSType (tokens[0]); + desc.componentSubType = stringToOSType (tokens[1]); + desc.componentManufacturer = stringToOSType (tokens[2]); + + ComponentRecord* comp = FindNextComponent (0, &desc); + + if (comp != 0) + { + getAUDetails (comp, name, manufacturer); + + return true; + } + } + } + + return false; +} + + //============================================================================== class AudioUnitPluginWindowCarbon; class AudioUnitPluginWindowCocoa; @@ -98,11 +221,11 @@ public: void fillInPluginDescription (PluginDescription& desc) const { desc.name = pluginName; - desc.file = file; + desc.fileOrIdentifier = createAUPluginIdentifier (componentDesc); desc.uid = ((int) componentDesc.componentType) ^ ((int) componentDesc.componentSubType) ^ ((int) componentDesc.componentManufacturer); - desc.lastFileModTime = file.getLastModificationTime(); + desc.lastFileModTime = 0; desc.pluginFormatName = "AudioUnit"; desc.category = getCategory(); desc.manufacturerName = manufacturer; @@ -163,7 +286,7 @@ private: ComponentDescription componentDesc; String pluginName, manufacturer, version; - File file; + String fileOrIdentifier; CriticalSection lock; bool initialised, wantsMidiMessages, wasPlaying; @@ -175,7 +298,7 @@ private: Array parameterIds; //============================================================================== - bool getComponentDescFromFile (const File& file); + bool getComponentDescFromFile (const String& fileOrIdentifier); void initialise(); //============================================================================== @@ -257,12 +380,12 @@ private: const String getCategory() const; //============================================================================== - AudioUnitPluginInstance (const File& file); + AudioUnitPluginInstance (const String& fileOrIdentifier); }; //============================================================================== -AudioUnitPluginInstance::AudioUnitPluginInstance (const File& file_) - : file (file_), +AudioUnitPluginInstance::AudioUnitPluginInstance (const String& fileOrIdentifier) + : fileOrIdentifier (fileOrIdentifier), initialised (false), wantsMidiMessages (false), audioUnit (0), @@ -273,9 +396,9 @@ AudioUnitPluginInstance::AudioUnitPluginInstance (const File& file_) { ++insideCallback; - log (T("Opening AU: ") + file.getFullPathName()); + log (T("Opening AU: ") + fileOrIdentifier); - if (getComponentDescFromFile (file)) + if (getComponentDescFromFile (fileOrIdentifier)) { ComponentRecord* const comp = FindNextComponent (0, &componentDesc); @@ -314,15 +437,18 @@ AudioUnitPluginInstance::~AudioUnitPluginInstance() juce_free (outputBufferList); } -bool AudioUnitPluginInstance::getComponentDescFromFile (const File& file) +bool AudioUnitPluginInstance::getComponentDescFromFile (const String& fileOrIdentifier) { zerostruct (componentDesc); + if (getComponentDescFromIdentifier (fileOrIdentifier, componentDesc, pluginName, version, manufacturer)) + return true; + + const File file (fileOrIdentifier); if (! file.hasFileExtension (T(".component"))) return false; - const String filename (file.getFullPathName()); - const char* const utf8 = filename.toUTF8(); + const char* const utf8 = fileOrIdentifier.toUTF8(); CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, strlen (utf8), file.isDirectory()); if (url != 0) @@ -730,15 +856,24 @@ public: ~AudioUnitPluginWindowCocoa() { + const bool wasValid = isValid(); + wrapper->setView (0); activeWindows.removeValue (this); - if (isValid()) + + if (wasValid) plugin.editorBeingDeleted (this); + delete wrapper; } bool isValid() const { return wrapper->getView() != 0; } + void paint (Graphics& g) + { + g.fillAll (Colours::white); + } + void resized() { wrapper->setSize (getWidth(), getHeight()); @@ -794,6 +929,11 @@ private: juce_free (info); wrapper->setView (pluginView); + + if (pluginView != 0) + setSize ([pluginView frame].size.width, + [pluginView frame].size.height); + return pluginView != 0; } }; @@ -1264,13 +1404,13 @@ AudioUnitPluginFormat::~AudioUnitPluginFormat() } void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray & results, - const File& file) + const String& fileOrIdentifier) { - if (! fileMightContainThisPluginType (file)) + if (! fileMightContainThisPluginType (fileOrIdentifier)) return; PluginDescription desc; - desc.file = file; + desc.fileOrIdentifier = fileOrIdentifier; desc.uid = 0; AudioUnitPluginInstance* instance = dynamic_cast (createInstanceFromDescription (desc)); @@ -1295,9 +1435,9 @@ AudioPluginInstance* AudioUnitPluginFormat::createInstanceFromDescription (const { AudioUnitPluginInstance* result = 0; - if (fileMightContainThisPluginType (desc.file)) + if (fileMightContainThisPluginType (desc.fileOrIdentifier)) { - result = new AudioUnitPluginInstance (desc.file); + result = new AudioUnitPluginInstance (desc.fileOrIdentifier); if (result->audioUnit != 0) { @@ -1312,15 +1452,73 @@ AudioPluginInstance* AudioUnitPluginFormat::createInstanceFromDescription (const return result; } -bool AudioUnitPluginFormat::fileMightContainThisPluginType (const File& f) +const StringArray AudioUnitPluginFormat::searchPathsForPlugins (const FileSearchPath& /*directoriesToSearch*/, + const bool /*recursive*/) { + StringArray result; + ComponentRecord* comp = 0; + ComponentDescription desc; + zerostruct (desc); + + for (;;) + { + zerostruct (desc); + comp = FindNextComponent (comp, &desc); + + if (comp == 0) + break; + + GetComponentInfo (comp, &desc, 0, 0, 0); + + if (desc.componentType == kAudioUnitType_MusicDevice + || desc.componentType == kAudioUnitType_MusicEffect + || desc.componentType == kAudioUnitType_Effect + || desc.componentType == kAudioUnitType_Generator + || desc.componentType == kAudioUnitType_Panner) + { + const String s (createAUPluginIdentifier (desc)); + DBG (s); + result.add (s); + } + } + + return result; +} + +bool AudioUnitPluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier) +{ + ComponentDescription desc; + + String name, version, manufacturer; + if (getComponentDescFromIdentifier (fileOrIdentifier, desc, name, version, manufacturer)) + return FindNextComponent (0, &desc) != 0; + + const File f (fileOrIdentifier); + return f.hasFileExtension (T(".component")) && f.isDirectory(); } +const String AudioUnitPluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier) +{ + ComponentDescription desc; + String name, version, manufacturer; + getComponentDescFromIdentifier (fileOrIdentifier, desc, name, version, manufacturer); + + if (name.isEmpty()) + name = fileOrIdentifier; + + return name; +} + +bool AudioUnitPluginFormat::doesPluginStillExist (const PluginDescription& desc) +{ + return File (desc.fileOrIdentifier).exists(); +} + const FileSearchPath AudioUnitPluginFormat::getDefaultLocationsToSearch() { - return FileSearchPath ("~/Library/Audio/Plug-Ins/Components;/Library/Audio/Plug-Ins/Components"); + return FileSearchPath ("(Default AudioUnit locations)"); } #endif diff --git a/src/juce_appframework/audio/plugins/formats/juce_DirectXPluginFormat.h b/src/juce_appframework/audio/plugins/formats/juce_DirectXPluginFormat.h index 9210cbbb4e..35de3a1375 100644 --- a/src/juce_appframework/audio/plugins/formats/juce_DirectXPluginFormat.h +++ b/src/juce_appframework/audio/plugins/formats/juce_DirectXPluginFormat.h @@ -53,9 +53,10 @@ public: //============================================================================== const String getName() const { return "DirectX"; } - void findAllTypesForFile (OwnedArray & results, const File& file); + void findAllTypesForFile (OwnedArray & results, const String& fileOrIdentifier); AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); - bool fileMightContainThisPluginType (const File& file); + bool fileMightContainThisPluginType (const String& fileOrIdentifier); + const String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) { return fileOrIdentifier; } const FileSearchPath getDefaultLocationsToSearch(); //============================================================================== diff --git a/src/juce_appframework/audio/plugins/formats/juce_LADSPAPluginFormat.h b/src/juce_appframework/audio/plugins/formats/juce_LADSPAPluginFormat.h index def44f2758..59c8b424fe 100644 --- a/src/juce_appframework/audio/plugins/formats/juce_LADSPAPluginFormat.h +++ b/src/juce_appframework/audio/plugins/formats/juce_LADSPAPluginFormat.h @@ -53,9 +53,10 @@ public: //============================================================================== const String getName() const { return "LADSPA"; } - void findAllTypesForFile (OwnedArray & results, const File& file); + void findAllTypesForFile (OwnedArray & results, const String& fileOrIdentifier); AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); - bool fileMightContainThisPluginType (const File& file); + bool fileMightContainThisPluginType (const String& fileOrIdentifier); + const String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) { return fileOrIdentifier; } const FileSearchPath getDefaultLocationsToSearch(); //============================================================================== diff --git a/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.cpp b/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.cpp index c1a172c481..5d526996f5 100644 --- a/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.cpp +++ b/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.cpp @@ -2892,13 +2892,13 @@ VSTPluginFormat::~VSTPluginFormat() } void VSTPluginFormat::findAllTypesForFile (OwnedArray & results, - const File& file) + const String& fileOrIdentifier) { - if (! fileMightContainThisPluginType (file)) + if (! fileMightContainThisPluginType (fileOrIdentifier)) return; PluginDescription desc; - desc.fileOrIdentifier = file.getFullPathName(); + desc.fileOrIdentifier = fileOrIdentifier; desc.uid = 0; VSTPluginInstance* instance = dynamic_cast (createInstanceFromDescription (desc)); @@ -2976,10 +2976,10 @@ AudioPluginInstance* VSTPluginFormat::createInstanceFromDescription (const Plugi { VSTPluginInstance* result = 0; - File file (desc.fileOrIdentifier); - - if (fileMightContainThisPluginType (file)) + if (fileMightContainThisPluginType (desc.fileOrIdentifier)) { + File file (desc.fileOrIdentifier); + const File previousWorkingDirectory (File::getCurrentWorkingDirectory()); file.getParentDirectory().setAsCurrentWorkingDirectory(); @@ -3008,8 +3008,10 @@ AudioPluginInstance* VSTPluginFormat::createInstanceFromDescription (const Plugi return result; } -bool VSTPluginFormat::fileMightContainThisPluginType (const File& f) +bool VSTPluginFormat::fileMightContainThisPluginType (const String& fileOrIdentifier) { + const File f (fileOrIdentifier); + #if JUCE_MAC if (f.isDirectory() && f.hasFileExtension (T(".vst"))) return true; @@ -3041,11 +3043,48 @@ bool VSTPluginFormat::fileMightContainThisPluginType (const File& f) #endif } +const String VSTPluginFormat::getNameOfPluginFromIdentifier (const String& fileOrIdentifier) +{ + return fileOrIdentifier; +} + bool VSTPluginFormat::doesPluginStillExist (const PluginDescription& desc) { return File (desc.fileOrIdentifier).exists(); } +const StringArray VSTPluginFormat::searchPathsForPlugins (const FileSearchPath& directoriesToSearch, const bool recursive) +{ + StringArray results; + + for (int j = 0; j < directoriesToSearch.getNumPaths(); ++j) + recursiveFileSearch (results, directoriesToSearch [j], recursive); + + return results; +} + +void VSTPluginFormat::recursiveFileSearch (StringArray& results, const File& dir, const bool recursive) +{ + // avoid allowing the dir iterator to be recursive, because we want to avoid letting it delve inside + // .component or .vst directories. + DirectoryIterator iter (dir, false, "*", File::findFilesAndDirectories); + + while (iter.next()) + { + const File f (iter.getFile()); + bool isPlugin = false; + + if (fileMightContainThisPluginType (f.getFullPathName())) + { + isPlugin = true; + results.add (f.getFullPathName()); + } + + if (recursive && (! isPlugin) && f.isDirectory()) + recursiveFileSearch (results, f, true); + } +} + const FileSearchPath VSTPluginFormat::getDefaultLocationsToSearch() { #if JUCE_MAC @@ -3059,6 +3098,7 @@ const FileSearchPath VSTPluginFormat::getDefaultLocationsToSearch() #endif } + END_JUCE_NAMESPACE #endif diff --git a/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.h b/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.h index e1e247b612..0a7009c408 100644 --- a/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.h +++ b/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.h @@ -50,9 +50,11 @@ public: //============================================================================== const String getName() const { return "VST"; } - void findAllTypesForFile (OwnedArray & results, const File& file); + void findAllTypesForFile (OwnedArray & results, const String& fileOrIdentifier); AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); - bool fileMightContainThisPluginType (const File& file); + bool fileMightContainThisPluginType (const String& fileOrIdentifier); + const String getNameOfPluginFromIdentifier (const String& fileOrIdentifier); + const StringArray searchPathsForPlugins (const FileSearchPath& directoriesToSearch, const bool recursive); bool doesPluginStillExist (const PluginDescription& desc); const FileSearchPath getDefaultLocationsToSearch(); @@ -62,6 +64,8 @@ public: private: VSTPluginFormat (const VSTPluginFormat&); const VSTPluginFormat& operator= (const VSTPluginFormat&); + + void recursiveFileSearch (StringArray& results, const File& dir, const bool recursive); }; diff --git a/src/juce_appframework/audio/plugins/juce_AudioPluginFormat.h b/src/juce_appframework/audio/plugins/juce_AudioPluginFormat.h index c520e9a2fb..57a7561122 100644 --- a/src/juce_appframework/audio/plugins/juce_AudioPluginFormat.h +++ b/src/juce_appframework/audio/plugins/juce_AudioPluginFormat.h @@ -68,7 +68,7 @@ public: subtypes, so in that case, each subtype is returned as a separate object. */ virtual void findAllTypesForFile (OwnedArray & results, - const File& file) = 0; + const String& fileOrIdentifier) = 0; /** Tries to recreate a type from a previously generated PluginDescription. @@ -82,7 +82,11 @@ public: This is for searching for potential files, so it shouldn't actually try to load the plugin or do anything time-consuming. */ - virtual bool fileMightContainThisPluginType (const File& file) = 0; + virtual bool fileMightContainThisPluginType (const String& fileOrIdentifier) = 0; + + /** Returns a readable version of the name of the plugin that this identifier refers to. + */ + virtual const String getNameOfPluginFromIdentifier (const String& fileOrIdentifier) = 0; /** Checks whether this plugin could possibly be loaded. @@ -91,6 +95,13 @@ public: */ virtual bool doesPluginStillExist (const PluginDescription& desc) = 0; + /** Searches a suggested set of directories for any plugins in this format. + + The path might be ignored, e.g. by AUs, which are found by the OS rather + than manually. + */ + virtual const StringArray searchPathsForPlugins (const FileSearchPath& directoriesToSearch, + const bool recursive) = 0; /** Returns the typical places to look for this kind of plugin. diff --git a/src/juce_appframework/audio/plugins/juce_AudioPluginFormatManager.cpp b/src/juce_appframework/audio/plugins/juce_AudioPluginFormatManager.cpp index c2b1bb79df..d99be65473 100644 --- a/src/juce_appframework/audio/plugins/juce_AudioPluginFormatManager.cpp +++ b/src/juce_appframework/audio/plugins/juce_AudioPluginFormatManager.cpp @@ -79,14 +79,14 @@ void AudioPluginFormatManager::addDefaultFormats() } #endif -#if JUCE_PLUGINHOST_VST - formats.add (new VSTPluginFormat()); -#endif - #if JUCE_PLUGINHOST_AU && JUCE_MAC formats.add (new AudioUnitPluginFormat()); #endif +#if JUCE_PLUGINHOST_VST + formats.add (new VSTPluginFormat()); +#endif + #if JUCE_PLUGINHOST_DX && JUCE_WIN32 formats.add (new DirectXPluginFormat()); #endif diff --git a/src/juce_appframework/audio/plugins/juce_KnownPluginList.cpp b/src/juce_appframework/audio/plugins/juce_KnownPluginList.cpp index 7874b67493..7409285a3c 100644 --- a/src/juce_appframework/audio/plugins/juce_KnownPluginList.cpp +++ b/src/juce_appframework/audio/plugins/juce_KnownPluginList.cpp @@ -55,10 +55,10 @@ void KnownPluginList::clear() } } -PluginDescription* KnownPluginList::getTypeForFile (const File& file) const throw() +PluginDescription* KnownPluginList::getTypeForFile (const String& fileOrIdentifier) const throw() { for (int i = 0; i < types.size(); ++i) - if (types.getUnchecked(i)->fileOrIdentifier == file.getFullPathName()) + if (types.getUnchecked(i)->fileOrIdentifier == fileOrIdentifier) return types.getUnchecked(i); return 0; @@ -99,17 +99,33 @@ void KnownPluginList::removeType (const int index) throw() sendChangeMessage (this); } -bool KnownPluginList::isListingUpToDate (const File& possiblePluginFile) const throw() +static Time getFileModTime (const String& fileOrIdentifier) throw() { - if (getTypeForFile (possiblePluginFile) == 0) + if (fileOrIdentifier.startsWithChar (T('/')) + || fileOrIdentifier[1] == T(':')) + { + return File (fileOrIdentifier).getLastModificationTime(); + } + + return Time (0); +} + +static bool timesAreDifferent (const Time& t1, const Time& t2) throw() +{ + return t1 != t2 || t1 == Time (0); +} + +bool KnownPluginList::isListingUpToDate (const String& fileOrIdentifier) const throw() +{ + if (getTypeForFile (fileOrIdentifier) == 0) return false; for (int i = types.size(); --i >= 0;) { const PluginDescription* const d = types.getUnchecked(i); - if (d->fileOrIdentifier == possiblePluginFile.getFullPathName() - && d->lastFileModTime != possiblePluginFile.getLastModificationTime()) + if (d->fileOrIdentifier == fileOrIdentifier + && timesAreDifferent (d->lastFileModTime, getFileModTime (fileOrIdentifier))) { return false; } @@ -118,14 +134,15 @@ bool KnownPluginList::isListingUpToDate (const File& possiblePluginFile) const t return true; } -bool KnownPluginList::scanAndAddFile (const File& possiblePluginFile, +bool KnownPluginList::scanAndAddFile (const String& fileOrIdentifier, const bool dontRescanIfAlreadyInList, - OwnedArray & typesFound) + OwnedArray & typesFound, + AudioPluginFormat& format) { bool addedOne = false; if (dontRescanIfAlreadyInList - && getTypeForFile (possiblePluginFile) != 0) + && getTypeForFile (fileOrIdentifier) != 0) { bool needsRescanning = false; @@ -133,9 +150,9 @@ bool KnownPluginList::scanAndAddFile (const File& possiblePluginFile, { const PluginDescription* const d = types.getUnchecked(i); - if (d->fileOrIdentifier == possiblePluginFile.getFullPathName()) + if (d->fileOrIdentifier == fileOrIdentifier) { - if (d->lastFileModTime != possiblePluginFile.getLastModificationTime()) + if (timesAreDifferent (d->lastFileModTime, getFileModTime (fileOrIdentifier))) needsRescanning = true; else typesFound.add (new PluginDescription (*d)); @@ -146,23 +163,18 @@ bool KnownPluginList::scanAndAddFile (const File& possiblePluginFile, return false; } - for (int i = 0; i < AudioPluginFormatManager::getInstance()->getNumFormats(); ++i) - { - AudioPluginFormat* const format = AudioPluginFormatManager::getInstance()->getFormat (i); - - OwnedArray found; - format->findAllTypesForFile (found, possiblePluginFile); + OwnedArray found; + format.findAllTypesForFile (found, fileOrIdentifier); - for (int i = 0; i < found.size(); ++i) - { - PluginDescription* const desc = found.getUnchecked(i); - jassert (desc != 0); + for (int i = 0; i < found.size(); ++i) + { + PluginDescription* const desc = found.getUnchecked(i); + jassert (desc != 0); - if (addType (*desc)) - addedOne = true; + if (addType (*desc)) + addedOne = true; - typesFound.add (new PluginDescription (*desc)); - } + typesFound.add (new PluginDescription (*desc)); } return addedOne; @@ -173,10 +185,20 @@ void KnownPluginList::scanAndAddDragAndDroppedFiles (const StringArray& files, { for (int i = 0; i < files.size(); ++i) { - const File f (files [i]); + bool loaded = false; + + for (int j = 0; j < AudioPluginFormatManager::getInstance()->getNumFormats(); ++j) + { + AudioPluginFormat* const format = AudioPluginFormatManager::getInstance()->getFormat (j); + + if (scanAndAddFile (files[i], true, typesFound, *format)) + loaded = true; + } - if (! scanAndAddFile (f, true, typesFound)) + if (! loaded) { + const File f (files[i]); + if (f.isDirectory()) { StringArray s; @@ -350,7 +372,13 @@ public: PopupMenu subMenu; sub->addToMenu (subMenu, allPlugins); + +#if JUCE_MAC + // avoid the special AU formatting nonsense on Mac.. + m.addSubMenu (sub->folder.fromFirstOccurrenceOf (T(":"), false, false), subMenu); +#else m.addSubMenu (sub->folder, subMenu); +#endif } for (i = 0; i < plugins.size(); ++i) diff --git a/src/juce_appframework/audio/plugins/juce_KnownPluginList.h b/src/juce_appframework/audio/plugins/juce_KnownPluginList.h index b4c11c8453..9d33e530ec 100644 --- a/src/juce_appframework/audio/plugins/juce_KnownPluginList.h +++ b/src/juce_appframework/audio/plugins/juce_KnownPluginList.h @@ -74,7 +74,7 @@ public: /** Looks for a type in the list which comes from this file. */ - PluginDescription* getTypeForFile (const File& file) const throw(); + PluginDescription* getTypeForFile (const String& fileOrIdentifier) const throw(); /** Looks for a type in the list which matches a plugin type ID. @@ -101,14 +101,15 @@ public: file (even if it was already known and hasn't been re-scanned) get returned in the array. */ - bool scanAndAddFile (const File& possiblePluginFile, + bool scanAndAddFile (const String& possiblePluginFileOrIdentifier, const bool dontRescanIfAlreadyInList, - OwnedArray & typesFound); + OwnedArray & typesFound, + AudioPluginFormat& formatToUse); /** Returns true if the specified file is already known about and if it hasn't been modified since our entry was created. */ - bool isListingUpToDate (const File& possiblePluginFile) const throw(); + bool isListingUpToDate (const String& possiblePluginFileOrIdentifier) const throw(); /** Scans and adds a bunch of files that might have been dragged-and-dropped. diff --git a/src/juce_appframework/audio/plugins/juce_PluginDescription.h b/src/juce_appframework/audio/plugins/juce_PluginDescription.h index 43227c0358..1ea32fd61c 100644 --- a/src/juce_appframework/audio/plugins/juce_PluginDescription.h +++ b/src/juce_appframework/audio/plugins/juce_PluginDescription.h @@ -75,10 +75,10 @@ public: String version; /** Either the file containing the plugin module, or some other unique way - of identifying it. - - E.g. for an AU, this would be the component ID, because not all AUs actually - live in a file... + of identifying it. + + E.g. for an AU, this would be an ID string that the component manager + could use to retrieve the plugin. For a VST, it's the file path. */ String fileOrIdentifier; diff --git a/src/juce_appframework/audio/plugins/juce_PluginDirectoryScanner.cpp b/src/juce_appframework/audio/plugins/juce_PluginDirectoryScanner.cpp index 476d5cf124..7a668caa69 100644 --- a/src/juce_appframework/audio/plugins/juce_PluginDirectoryScanner.cpp +++ b/src/juce_appframework/audio/plugins/juce_PluginDirectoryScanner.cpp @@ -52,8 +52,7 @@ PluginDirectoryScanner::PluginDirectoryScanner (KnownPluginList& listToAddTo, { directoriesToSearch.removeRedundantPaths(); - for (int j = 0; j < directoriesToSearch.getNumPaths(); ++j) - recursiveFileSearch (directoriesToSearch [j], recursive); + filesOrIdentifiersToScan = format.searchPathsForPlugins (directoriesToSearch, recursive); // If any plugins have crashed recently when being loaded, move them to the // end of the list to give the others a chance to load correctly.. @@ -61,33 +60,11 @@ PluginDirectoryScanner::PluginDirectoryScanner (KnownPluginList& listToAddTo, for (int i = 0; i < crashedPlugins.size(); ++i) { - const File f (crashedPlugins[i]); + const String f = crashedPlugins[i]; - for (int j = filesToScan.size(); --j >= 0;) - if (f == *filesToScan.getUnchecked (j)) - filesToScan.move (j, -1); - } -} - -void PluginDirectoryScanner::recursiveFileSearch (const File& dir, const bool recursive) -{ - // avoid allowing the dir iterator to be recursive, because we want to avoid letting it delve inside - // .component or .vst directories. - DirectoryIterator iter (dir, false, "*", File::findFilesAndDirectories); - - while (iter.next()) - { - const File f (iter.getFile()); - bool isPlugin = false; - - if (format.fileMightContainThisPluginType (f)) - { - isPlugin = true; - filesToScan.add (new File (f)); - } - - if (recursive && (! isPlugin) && f.isDirectory()) - recursiveFileSearch (f, true); + for (int j = filesOrIdentifiersToScan.size(); --j >= 0;) + if (f == filesOrIdentifiersToScan[j]) + filesOrIdentifiersToScan.move (j, -1); } } @@ -96,49 +73,45 @@ PluginDirectoryScanner::~PluginDirectoryScanner() } //============================================================================== -const File PluginDirectoryScanner::getNextPluginFileThatWillBeScanned() const throw() +const String PluginDirectoryScanner::getNextPluginFileThatWillBeScanned() const throw() { - File* const file = filesToScan [nextIndex]; - - if (file != 0) - return *file; - - return File::nonexistent; + return format.getNameOfPluginFromIdentifier (filesOrIdentifiersToScan [nextIndex]); } bool PluginDirectoryScanner::scanNextFile (const bool dontRescanIfAlreadyInList) { - File* const file = filesToScan [nextIndex]; + String file (filesOrIdentifiersToScan [nextIndex]); - if (file != 0) + if (file.isNotEmpty()) { - if (! list.isListingUpToDate (*file)) + if (! list.isListingUpToDate (file)) { OwnedArray typesFound; // Add this plugin to the end of the dead-man's pedal list in case it crashes... StringArray crashedPlugins (getDeadMansPedalFile()); - crashedPlugins.removeString (file->getFullPathName()); - crashedPlugins.add (file->getFullPathName()); + crashedPlugins.removeString (file); + crashedPlugins.add (file); setDeadMansPedalFile (crashedPlugins); - list.scanAndAddFile (*file, + list.scanAndAddFile (file, dontRescanIfAlreadyInList, - typesFound); + typesFound, + format); // Managed to load without crashing, so remove it from the dead-man's-pedal.. - crashedPlugins.removeString (file->getFullPathName()); + crashedPlugins.removeString (file); setDeadMansPedalFile (crashedPlugins); if (typesFound.size() == 0) - failedFiles.add (file->getFullPathName()); + failedFiles.add (file); } ++nextIndex; - progress = nextIndex / (float) filesToScan.size(); + progress = nextIndex / (float) filesOrIdentifiersToScan.size(); } - return nextIndex < filesToScan.size(); + return nextIndex < filesOrIdentifiersToScan.size(); } const StringArray PluginDirectoryScanner::getDeadMansPedalFile() throw() diff --git a/src/juce_appframework/audio/plugins/juce_PluginDirectoryScanner.h b/src/juce_appframework/audio/plugins/juce_PluginDirectoryScanner.h index d559707a7c..81498e7cb2 100644 --- a/src/juce_appframework/audio/plugins/juce_PluginDirectoryScanner.h +++ b/src/juce_appframework/audio/plugins/juce_PluginDirectoryScanner.h @@ -89,12 +89,13 @@ public: */ bool scanNextFile (const bool dontRescanIfAlreadyInList); - /** Returns the file that will be scanned during the next call to scanNextFile(). + /** Returns the description of the plugin that will be scanned during the next + call to scanNextFile(). This is handy if you want to show the user which file is currently getting scanned. */ - const File getNextPluginFileThatWillBeScanned() const throw(); + const String getNextPluginFileThatWillBeScanned() const throw(); /** Returns the estimated progress, between 0 and 1. */ @@ -111,13 +112,12 @@ public: private: KnownPluginList& list; AudioPluginFormat& format; - OwnedArray filesToScan; + StringArray filesOrIdentifiersToScan; File deadMansPedalFile; StringArray failedFiles; int nextIndex; float progress; - void recursiveFileSearch (const File& dir, const bool recursive); const StringArray getDeadMansPedalFile() throw(); void setDeadMansPedalFile (const StringArray& newContents) throw(); diff --git a/src/juce_appframework/audio/plugins/juce_PluginListComponent.cpp b/src/juce_appframework/audio/plugins/juce_PluginListComponent.cpp index c515e131f5..9908756559 100644 --- a/src/juce_appframework/audio/plugins/juce_PluginListComponent.cpp +++ b/src/juce_appframework/audio/plugins/juce_PluginListComponent.cpp @@ -279,7 +279,7 @@ void PluginListComponent::scanFor (AudioPluginFormat* format) for (;;) { aw.setMessage (TRANS("Testing:\n\n") - + scanner.getNextPluginFileThatWillBeScanned().getFileName()); + + scanner.getNextPluginFileThatWillBeScanned()); MessageManager::getInstance()->runDispatchLoopUntil (20); diff --git a/src/juce_appframework/gui/components/juce_Desktop.h b/src/juce_appframework/gui/components/juce_Desktop.h index 232fb168ab..9e87d68601 100644 --- a/src/juce_appframework/gui/components/juce_Desktop.h +++ b/src/juce_appframework/gui/components/juce_Desktop.h @@ -181,8 +181,8 @@ public: The component must already be on the desktop for this method to work. It will be resized to completely fill the screen and any extraneous taskbars, menu bars, etc will be hidden. - - To exit kiosk mode, just call setKioskModeComponent (0). When this is called, + + To exit kiosk mode, just call setKioskModeComponent (0). When this is called, the component that's currently being used will be resized back to the size and position it was in before being put into this mode. */ diff --git a/src/juce_appframework/gui/components/menus/juce_MenuBarModel.h b/src/juce_appframework/gui/components/menus/juce_MenuBarModel.h index 7e59bbc976..311b81fd4b 100644 --- a/src/juce_appframework/gui/components/menus/juce_MenuBarModel.h +++ b/src/juce_appframework/gui/components/menus/juce_MenuBarModel.h @@ -149,9 +149,9 @@ public: not to delete a model while it is being used. An optional extra menu can be specified, containing items to add to the top of - the apple menu. (Confusingly, the 'apple' menu isn't the one with a picture of - an apple, it's the one next to it, with your application's name at the top - and the services menu etc on it). When one of these items is selected, the + the apple menu. (Confusingly, the 'apple' menu isn't the one with a picture of + an apple, it's the one next to it, with your application's name at the top + and the services menu etc on it). When one of these items is selected, the menu bar model will be used to invoke it, and in the menuItemSelected() callback the topLevelMenuIndex parameter will be -1. If you pass in an extraAppleMenuItems object then newMenuBarModel must be non-null. diff --git a/src/juce_appframework/gui/components/mouse/juce_DragAndDropContainer.cpp b/src/juce_appframework/gui/components/mouse/juce_DragAndDropContainer.cpp index 21e9918005..3e186225b6 100644 --- a/src/juce_appframework/gui/components/mouse/juce_DragAndDropContainer.cpp +++ b/src/juce_appframework/gui/components/mouse/juce_DragAndDropContainer.cpp @@ -113,7 +113,7 @@ public: { Component* const over = dynamic_cast (currentlyOver); - // (note: use a local copy of the dragDesc member in case the callback runs + // (note: use a local copy of the dragDesc member in case the callback runs // a modal loop and deletes this object before the method completes) const String dragDescLocal (dragDesc); @@ -163,7 +163,7 @@ public: hit = hit->getComponentAt (rx, ry); } - // (note: use a local copy of the dragDesc member in case the callback runs + // (note: use a local copy of the dragDesc member in case the callback runs // a modal loop and deletes this object before the method completes) const String dragDescLocal (dragDesc); @@ -234,7 +234,7 @@ public: if (dropAccepted && ddt != 0) { - // (note: use a local copy of the dragDesc member in case the callback runs + // (note: use a local copy of the dragDesc member in case the callback runs // a modal loop and deletes this object before the method completes) const String dragDescLocal (dragDesc); @@ -247,7 +247,7 @@ public: void updateLocation (const bool canDoExternalDrag, int x, int y) { - // (note: use a local copy of the dragDesc member in case the callback runs + // (note: use a local copy of the dragDesc member in case the callback runs // a modal loop and deletes this object before it returns) const String dragDescLocal (dragDesc);