| @@ -1354,14 +1354,17 @@ private: | |||
| jassertfalse; | |||
| } | |||
| [paramTree.get() setImplementorValueObserver: paramObserver]; | |||
| [paramTree.get() setImplementorValueProvider: paramProvider]; | |||
| [paramTree.get() setImplementorStringFromValueCallback: stringFromValueProvider]; | |||
| [paramTree.get() setImplementorValueFromStringCallback: valueFromStringProvider]; | |||
| [paramTree.get() setImplementorValueObserver: ^(AUParameter* param, AUValue value) { this->valueChangedFromHost (param, value); }]; | |||
| [paramTree.get() setImplementorValueProvider: ^(AUParameter* param) { return this->getValue (param); }]; | |||
| [paramTree.get() setImplementorStringFromValueCallback: ^(AUParameter* param, const AUValue* value) { return this->stringFromValue (param, value); }]; | |||
| [paramTree.get() setImplementorValueFromStringCallback: ^(AUParameter* param, NSString* str) { return this->valueFromString (param, str); }]; | |||
| if (getAudioProcessor().hasEditor()) | |||
| { | |||
| editorObserverToken = ObserverPtr ([paramTree.get() tokenByAddingParameterObserver: editorParamObserver], | |||
| editorObserverToken = ObserverPtr ([paramTree.get() tokenByAddingParameterObserver: ^(AUParameterAddress, AUValue) | |||
| { | |||
| // this will have already been handled by valueChangedFromHost | |||
| }], | |||
| ObserverDestructor { paramTree.get() }); | |||
| } | |||
| } | |||
| @@ -1630,11 +1633,6 @@ private: | |||
| return 0; | |||
| } | |||
| void valueChangedForObserver (AUParameterAddress, AUValue) | |||
| { | |||
| // this will have already been handled by valueChangedFromHost | |||
| } | |||
| NSString* stringFromValue (AUParameter* param, const AUValue* value) | |||
| { | |||
| String text; | |||
| @@ -1738,11 +1736,6 @@ private: | |||
| CoreAudioTimeConversions timeConversions; | |||
| std::unique_ptr<AUAudioUnitBusArray, NSObjectDeleter> inputBusses, outputBusses; | |||
| ObjCBlock<AUImplementorValueObserver> paramObserver = CreateObjCBlock (this, &JuceAudioUnitv3::valueChangedFromHost); | |||
| ObjCBlock<AUImplementorValueProvider> paramProvider = CreateObjCBlock (this, &JuceAudioUnitv3::getValue); | |||
| ObjCBlock<AUImplementorStringFromValueCallback> stringFromValueProvider = CreateObjCBlock (this, &JuceAudioUnitv3::stringFromValue); | |||
| ObjCBlock<AUImplementorValueFromStringCallback> valueFromStringProvider = CreateObjCBlock (this, &JuceAudioUnitv3::valueFromString); | |||
| #if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS | |||
| std::map<AUParameterAddress, int> indexForAddress; | |||
| #endif | |||
| @@ -1752,7 +1745,6 @@ private: | |||
| // to avoid recursion on parameter changes, we need to add an | |||
| // editor observer to do the parameter changes | |||
| std::unique_ptr<AUParameterTree, NSObjectDeleter> paramTree; | |||
| ObjCBlock<AUParameterObserver> editorParamObserver = CreateObjCBlock (this, &JuceAudioUnitv3::valueChangedForObserver); | |||
| ObserverPtr editorObserverToken; | |||
| std::unique_ptr<NSMutableArray<NSNumber*>, NSObjectDeleter> channelCapabilities; | |||
| @@ -506,40 +506,16 @@ using AudioUnitCreationCallback = std::function<void (AudioUnit, OSStatus)>; | |||
| static void createAudioUnit (VersionedAudioComponent versionedComponent, AudioUnitCreationCallback callback) | |||
| { | |||
| struct AUAsyncInitializationCallback | |||
| { | |||
| typedef void (^AUCompletionCallbackBlock)(AudioComponentInstance, OSStatus); | |||
| explicit AUAsyncInitializationCallback (AudioUnitCreationCallback inOriginalCallback) | |||
| : originalCallback (std::move (inOriginalCallback)) | |||
| { | |||
| block = CreateObjCBlock (this, &AUAsyncInitializationCallback::completion); | |||
| } | |||
| AUCompletionCallbackBlock getBlock() noexcept { return block; } | |||
| void completion (AudioComponentInstance audioUnit, OSStatus err) | |||
| { | |||
| originalCallback (audioUnit, err); | |||
| delete this; | |||
| } | |||
| double sampleRate; | |||
| int framesPerBuffer; | |||
| AudioUnitCreationCallback originalCallback; | |||
| ObjCBlock<AUCompletionCallbackBlock> block; | |||
| }; | |||
| auto callbackBlock = new AUAsyncInitializationCallback (std::move (callback)); | |||
| if (versionedComponent.isAUv3) | |||
| { | |||
| if (@available (macOS 10.11, *)) | |||
| { | |||
| AudioComponentInstantiate (versionedComponent.audioComponent, kAudioComponentInstantiation_LoadOutOfProcess, | |||
| callbackBlock->getBlock()); | |||
| AudioComponentInstantiate (versionedComponent.audioComponent, | |||
| kAudioComponentInstantiation_LoadOutOfProcess, | |||
| ^(AudioComponentInstance audioUnit, OSStatus err) | |||
| { | |||
| callback (audioUnit, err); | |||
| }); | |||
| return; | |||
| } | |||
| @@ -547,7 +523,7 @@ static void createAudioUnit (VersionedAudioComponent versionedComponent, AudioUn | |||
| AudioComponentInstance audioUnit; | |||
| auto err = AudioComponentInstanceNew (versionedComponent.audioComponent, &audioUnit); | |||
| callbackBlock->completion (err != noErr ? nullptr : audioUnit, err); | |||
| callback (err != noErr ? nullptr : audioUnit, err); | |||
| } | |||
| struct AudioComponentResult | |||
| @@ -2615,9 +2591,6 @@ public: | |||
| { | |||
| addAndMakeVisible (wrapper); | |||
| viewControllerCallback = | |||
| CreateObjCBlock (this, &AudioUnitPluginWindowCocoa::requestViewControllerCallback); | |||
| setOpaque (true); | |||
| setVisible (true); | |||
| setSize (100, 100); | |||
| @@ -2673,7 +2646,6 @@ private: | |||
| AudioUnitFormatHelpers::AutoResizingNSViewComponent wrapper; | |||
| typedef void (^ViewControllerCallbackBlock)(AUViewControllerBase *); | |||
| ObjCBlock<ViewControllerCallbackBlock> viewControllerCallback; | |||
| bool waitingForViewCallback = false; | |||
| @@ -2727,12 +2699,9 @@ private: | |||
| && dataSize == sizeof (ViewControllerCallbackBlock)) | |||
| { | |||
| waitingForViewCallback = true; | |||
| ViewControllerCallbackBlock callback; | |||
| callback = viewControllerCallback; | |||
| ViewControllerCallbackBlock* info = &callback; | |||
| auto callback = ^(AUViewControllerBase* controller) { this->requestViewControllerCallback (controller); }; | |||
| if (noErr == AudioUnitSetProperty (plugin.audioUnit, kAudioUnitProperty_RequestViewController, kAudioUnitScope_Global, 0, info, dataSize)) | |||
| if (noErr == AudioUnitSetProperty (plugin.audioUnit, kAudioUnitProperty_RequestViewController, kAudioUnitScope_Global, 0, &callback, dataSize)) | |||
| return true; | |||
| waitingForViewCallback = false; | |||
| @@ -504,29 +504,59 @@ auto createObjCBlockImpl (Class* object, Fn func, Signature<Result (Params...)>) | |||
| } | |||
| } // namespace detail | |||
| /* Creates an Obj-C block automatically from a member function. */ | |||
| template <typename Class, typename MemberFunc> | |||
| auto CreateObjCBlock (Class* object, MemberFunc fn) | |||
| { | |||
| return detail::createObjCBlockImpl (object, fn, detail::getSignature (fn)); | |||
| } | |||
| /* Automatically copies and releases a block, a bit like a smart pointer for an Obj-C block. | |||
| This is helpful to automatically manage the lifetime of blocks, e.g. if you need to keep a block | |||
| around to be used later. This is the case in the AudioUnit API, where the host may provide a | |||
| musicalContextBlock that can be called by the plugin during rendering. Copying blocks isn't | |||
| realtime-safe, so the plugin must cache the block before rendering. | |||
| If you're just creating blocks to pass them directly to an Obj-C API, you probably won't need to | |||
| use this type. | |||
| */ | |||
| template <typename BlockType> | |||
| class ObjCBlock | |||
| { | |||
| public: | |||
| ObjCBlock() { block = nullptr; } | |||
| template <typename R, class C, typename... P> | |||
| ObjCBlock (C* _this, R (C::*fn)(P...)) : block (CreateObjCBlock (_this, fn)) {} | |||
| ObjCBlock (BlockType b) : block ([b copy]) {} | |||
| ObjCBlock& operator= (const BlockType& other) { if (block != nullptr) { [block release]; } block = [other copy]; return *this; } | |||
| bool operator== (const void* ptr) const { return ((const void*) block == ptr); } | |||
| bool operator!= (const void* ptr) const { return ((const void*) block != ptr); } | |||
| ~ObjCBlock() { if (block != nullptr) [block release]; } | |||
| ObjCBlock() = default; | |||
| ObjCBlock (BlockType b) | |||
| : block ([b copy]) {} | |||
| ObjCBlock (const ObjCBlock& other) | |||
| : block (other.block != nullptr ? [other.block copy] : nullptr) {} | |||
| ObjCBlock& operator= (const BlockType& other) | |||
| { | |||
| ObjCBlock { other }.swap (*this); | |||
| return *this; | |||
| } | |||
| ~ObjCBlock() noexcept | |||
| { | |||
| if (block != nullptr) | |||
| [block release]; | |||
| } | |||
| bool operator== (BlockType ptr) const { return block == ptr; } | |||
| bool operator!= (BlockType ptr) const { return block != ptr; } | |||
| operator BlockType() const { return block; } | |||
| void swap (ObjCBlock& other) noexcept | |||
| { | |||
| std::swap (other.block, block); | |||
| } | |||
| private: | |||
| BlockType block; | |||
| BlockType block = nullptr; | |||
| }; | |||
| //============================================================================== | |||
| @@ -189,7 +189,11 @@ public: | |||
| if (ref == nullptr) | |||
| return; | |||
| [ref->panel beginWithCompletionHandler: CreateObjCBlock (ref.getComponent(), &Native::finished)]; | |||
| [ref->panel beginWithCompletionHandler: ^(NSInteger result) | |||
| { | |||
| if (auto* ptr = ref.getComponent()) | |||
| ptr->finished (result); | |||
| }]; | |||
| if (ref->preview != nullptr) | |||
| ref->preview->toFront (true); | |||