From 7da615a7a360ed362357ed6bf1329df03835eb88 Mon Sep 17 00:00:00 2001 From: reuk Date: Thu, 2 Mar 2023 13:15:24 +0000 Subject: [PATCH] Objective-C: Tidy up block usages, and document block helpers --- .../AU/juce_AUv3_Wrapper.mm | 24 +++------ .../juce_AudioUnitPluginFormat.mm | 49 ++++--------------- .../juce_core/native/juce_mac_ObjCHelpers.h | 48 ++++++++++++++---- .../native/juce_mac_FileChooser.mm | 6 ++- 4 files changed, 61 insertions(+), 66 deletions(-) diff --git a/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm b/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm index 555ecefb6d..d91d07ae26 100644 --- a/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm +++ b/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm @@ -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 inputBusses, outputBusses; - ObjCBlock paramObserver = CreateObjCBlock (this, &JuceAudioUnitv3::valueChangedFromHost); - ObjCBlock paramProvider = CreateObjCBlock (this, &JuceAudioUnitv3::getValue); - ObjCBlock stringFromValueProvider = CreateObjCBlock (this, &JuceAudioUnitv3::stringFromValue); - ObjCBlock valueFromStringProvider = CreateObjCBlock (this, &JuceAudioUnitv3::valueFromString); - #if ! JUCE_FORCE_USE_LEGACY_PARAM_IDS std::map 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 paramTree; - ObjCBlock editorParamObserver = CreateObjCBlock (this, &JuceAudioUnitv3::valueChangedForObserver); ObserverPtr editorObserverToken; std::unique_ptr, NSObjectDeleter> channelCapabilities; diff --git a/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm b/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm index 0017920ba2..a36ff06a0b 100644 --- a/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm +++ b/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm @@ -506,40 +506,16 @@ using AudioUnitCreationCallback = std::function; 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 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 viewControllerCallback; bool waitingForViewCallback = false; @@ -2727,12 +2699,9 @@ private: && dataSize == sizeof (ViewControllerCallbackBlock)) { waitingForViewCallback = true; - ViewControllerCallbackBlock callback; - callback = viewControllerCallback; + auto callback = ^(AUViewControllerBase* controller) { this->requestViewControllerCallback (controller); }; - ViewControllerCallbackBlock* info = &callback; - - 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; diff --git a/modules/juce_core/native/juce_mac_ObjCHelpers.h b/modules/juce_core/native/juce_mac_ObjCHelpers.h index 7f17c07b95..6571ff7e33 100644 --- a/modules/juce_core/native/juce_mac_ObjCHelpers.h +++ b/modules/juce_core/native/juce_mac_ObjCHelpers.h @@ -504,29 +504,59 @@ auto createObjCBlockImpl (Class* object, Fn func, Signature) } } // namespace detail +/* Creates an Obj-C block automatically from a member function. */ template 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 class ObjCBlock { public: - ObjCBlock() { block = nullptr; } - template - 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; }; //============================================================================== diff --git a/modules/juce_gui_basics/native/juce_mac_FileChooser.mm b/modules/juce_gui_basics/native/juce_mac_FileChooser.mm index 1bd8cd5309..ddb7df440c 100644 --- a/modules/juce_gui_basics/native/juce_mac_FileChooser.mm +++ b/modules/juce_gui_basics/native/juce_mac_FileChooser.mm @@ -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);