| @@ -73,6 +73,8 @@ | |||||
| #define JUCE_AUDIOUNIT_OBJC_NAME(x) JUCE_JOIN_MACRO (x, AUv3) | #define JUCE_AUDIOUNIT_OBJC_NAME(x) JUCE_JOIN_MACRO (x, AUv3) | ||||
| #include <future> | |||||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnullability-completeness") | JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnullability-completeness") | ||||
| using namespace juce; | using namespace juce; | ||||
| @@ -1750,7 +1752,7 @@ public: | |||||
| { | { | ||||
| JUCE_ASSERT_MESSAGE_THREAD | JUCE_ASSERT_MESSAGE_THREAD | ||||
| if (processorHolder != nullptr) | |||||
| if (processorHolder.get() != nullptr) | |||||
| JuceAudioUnitv3::removeEditor (getAudioProcessor()); | JuceAudioUnitv3::removeEditor (getAudioProcessor()); | ||||
| } | } | ||||
| @@ -1795,22 +1797,25 @@ public: | |||||
| void viewDidLayoutSubviews() | void viewDidLayoutSubviews() | ||||
| { | { | ||||
| if (processorHolder != nullptr && [myself view] != nullptr) | |||||
| if (auto holder = processorHolder.get()) | |||||
| { | { | ||||
| if (AudioProcessorEditor* editor = getAudioProcessor().getActiveEditor()) | |||||
| if ([myself view] != nullptr) | |||||
| { | { | ||||
| if (processorHolder->viewConfiguration != nullptr) | |||||
| editor->hostMIDIControllerIsAvailable (processorHolder->viewConfiguration->hostHasMIDIController); | |||||
| if (AudioProcessorEditor* editor = getAudioProcessor().getActiveEditor()) | |||||
| { | |||||
| if (holder->viewConfiguration != nullptr) | |||||
| editor->hostMIDIControllerIsAvailable (holder->viewConfiguration->hostHasMIDIController); | |||||
| editor->setBounds (convertToRectInt ([[myself view] bounds])); | |||||
| editor->setBounds (convertToRectInt ([[myself view] bounds])); | |||||
| if (JUCE_IOS_MAC_VIEW* peerView = [[[myself view] subviews] objectAtIndex: 0]) | |||||
| { | |||||
| #if JUCE_IOS | |||||
| [peerView setNeedsDisplay]; | |||||
| #else | |||||
| [peerView setNeedsDisplay: YES]; | |||||
| #endif | |||||
| if (JUCE_IOS_MAC_VIEW* peerView = [[[myself view] subviews] objectAtIndex: 0]) | |||||
| { | |||||
| #if JUCE_IOS | |||||
| [peerView setNeedsDisplay]; | |||||
| #else | |||||
| [peerView setNeedsDisplay: YES]; | |||||
| #endif | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -1818,21 +1823,21 @@ public: | |||||
| void didReceiveMemoryWarning() | void didReceiveMemoryWarning() | ||||
| { | { | ||||
| if (processorHolder != nullptr) | |||||
| if (auto* processor = processorHolder->get()) | |||||
| if (auto ptr = processorHolder.get()) | |||||
| if (auto* processor = ptr->get()) | |||||
| processor->memoryWarningReceived(); | processor->memoryWarningReceived(); | ||||
| } | } | ||||
| void viewDidAppear (bool) | void viewDidAppear (bool) | ||||
| { | { | ||||
| if (processorHolder != nullptr) | |||||
| if (processorHolder.get() != nullptr) | |||||
| if (AudioProcessorEditor* editor = getAudioProcessor().getActiveEditor()) | if (AudioProcessorEditor* editor = getAudioProcessor().getActiveEditor()) | ||||
| editor->setVisible (true); | editor->setVisible (true); | ||||
| } | } | ||||
| void viewDidDisappear (bool) | void viewDidDisappear (bool) | ||||
| { | { | ||||
| if (processorHolder != nullptr) | |||||
| if (processorHolder.get() != nullptr) | |||||
| if (AudioProcessorEditor* editor = getAudioProcessor().getActiveEditor()) | if (AudioProcessorEditor* editor = getAudioProcessor().getActiveEditor()) | ||||
| editor->setVisible (false); | editor->setVisible (false); | ||||
| } | } | ||||
| @@ -1846,65 +1851,72 @@ public: | |||||
| //============================================================================== | //============================================================================== | ||||
| AUAudioUnit* createAudioUnit (const AudioComponentDescription& descr, NSError** error) | AUAudioUnit* createAudioUnit (const AudioComponentDescription& descr, NSError** error) | ||||
| { | { | ||||
| AUAudioUnit* retval = nil; | |||||
| const auto holder = [&] | |||||
| { | |||||
| if (auto initialisedHolder = processorHolder.get()) | |||||
| return initialisedHolder; | |||||
| if (! MessageManager::getInstance()->isThisTheMessageThread()) | |||||
| waitForExecutionOnMainThread ([this] { [myself view]; }); | |||||
| return processorHolder.get(); | |||||
| }(); | |||||
| if (holder == nullptr) | |||||
| return nullptr; | |||||
| return (new JuceAudioUnitv3 (holder, descr, 0, error))->getAudioUnit(); | |||||
| } | |||||
| private: | |||||
| template <typename Callback> | |||||
| static void waitForExecutionOnMainThread (Callback&& callback) | |||||
| { | |||||
| if (MessageManager::getInstance()->isThisTheMessageThread()) | |||||
| { | { | ||||
| WaitableEvent creationEvent; | |||||
| callback(); | |||||
| return; | |||||
| } | |||||
| // AUv3 headers say that we may block this thread and that the message thread is guaranteed | |||||
| // to be unblocked | |||||
| struct AUCreator : public CallbackMessage | |||||
| { | |||||
| JuceAUViewController& owner; | |||||
| AudioComponentDescription pDescr; | |||||
| NSError** pError; | |||||
| AUAudioUnit*& outAU; | |||||
| WaitableEvent& e; | |||||
| AUCreator (JuceAUViewController& parent, const AudioComponentDescription& paramDescr, NSError** paramError, | |||||
| AUAudioUnit*& outputAU, WaitableEvent& event) | |||||
| : owner (parent), pDescr (paramDescr), pError (paramError), outAU (outputAU), e (event) | |||||
| {} | |||||
| void messageCallback() override | |||||
| { | |||||
| outAU = owner.createAudioUnitOnMessageThread (pDescr, pError); | |||||
| e.signal(); | |||||
| } | |||||
| }; | |||||
| std::promise<void> promise; | |||||
| MessageManager::callAsync ([&] | |||||
| { | |||||
| callback(); | |||||
| promise.set_value(); | |||||
| }); | |||||
| (new AUCreator (*this, descr, error, retval, creationEvent))->post(); | |||||
| creationEvent.wait (-1); | |||||
| promise.get_future().get(); | |||||
| } | |||||
| // There's a chance that createAudioUnit will be called from a background | |||||
| // thread while the processorHolder is being updated on the main thread. | |||||
| class LockedProcessorHolder | |||||
| { | |||||
| public: | |||||
| AudioProcessorHolder::Ptr get() const | |||||
| { | |||||
| const ScopedLock lock (mutex); | |||||
| return holder; | |||||
| } | } | ||||
| else | |||||
| LockedProcessorHolder& operator= (const AudioProcessorHolder::Ptr& other) | |||||
| { | { | ||||
| retval = createAudioUnitOnMessageThread (descr, error); | |||||
| const ScopedLock lock (mutex); | |||||
| holder = other; | |||||
| return *this; | |||||
| } | } | ||||
| return [retval autorelease]; | |||||
| } | |||||
| private: | |||||
| mutable CriticalSection mutex; | |||||
| AudioProcessorHolder::Ptr holder; | |||||
| }; | |||||
| private: | |||||
| //============================================================================== | //============================================================================== | ||||
| AUViewController<AUAudioUnitFactory>* myself; | AUViewController<AUAudioUnitFactory>* myself; | ||||
| AudioProcessorHolder::Ptr processorHolder = nullptr; | |||||
| LockedProcessorHolder processorHolder; | |||||
| Rectangle<int> preferredSize { 1, 1 }; | Rectangle<int> preferredSize { 1, 1 }; | ||||
| //============================================================================== | //============================================================================== | ||||
| AUAudioUnit* createAudioUnitOnMessageThread (const AudioComponentDescription& descr, NSError** error) | |||||
| { | |||||
| JUCE_ASSERT_MESSAGE_THREAD | |||||
| [myself view]; // this will call [view load] and ensure that the AudioProcessor has been instantiated | |||||
| if (processorHolder == nullptr) | |||||
| return nullptr; | |||||
| return (new JuceAudioUnitv3 (processorHolder, descr, 0, error))->getAudioUnit(); | |||||
| } | |||||
| AudioProcessor& getAudioProcessor() const noexcept { return **processorHolder; } | |||||
| AudioProcessor& getAudioProcessor() const noexcept { return **processorHolder.get(); } | |||||
| }; | }; | ||||
| //============================================================================== | //============================================================================== | ||||