| @@ -73,6 +73,8 @@ | |||
| #define JUCE_AUDIOUNIT_OBJC_NAME(x) JUCE_JOIN_MACRO (x, AUv3) | |||
| #include <future> | |||
| JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnullability-completeness") | |||
| using namespace juce; | |||
| @@ -1750,7 +1752,7 @@ public: | |||
| { | |||
| JUCE_ASSERT_MESSAGE_THREAD | |||
| if (processorHolder != nullptr) | |||
| if (processorHolder.get() != nullptr) | |||
| JuceAudioUnitv3::removeEditor (getAudioProcessor()); | |||
| } | |||
| @@ -1795,22 +1797,25 @@ public: | |||
| 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() | |||
| { | |||
| if (processorHolder != nullptr) | |||
| if (auto* processor = processorHolder->get()) | |||
| if (auto ptr = processorHolder.get()) | |||
| if (auto* processor = ptr->get()) | |||
| processor->memoryWarningReceived(); | |||
| } | |||
| void viewDidAppear (bool) | |||
| { | |||
| if (processorHolder != nullptr) | |||
| if (processorHolder.get() != nullptr) | |||
| if (AudioProcessorEditor* editor = getAudioProcessor().getActiveEditor()) | |||
| editor->setVisible (true); | |||
| } | |||
| void viewDidDisappear (bool) | |||
| { | |||
| if (processorHolder != nullptr) | |||
| if (processorHolder.get() != nullptr) | |||
| if (AudioProcessorEditor* editor = getAudioProcessor().getActiveEditor()) | |||
| editor->setVisible (false); | |||
| } | |||
| @@ -1846,65 +1851,72 @@ public: | |||
| //============================================================================== | |||
| 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; | |||
| AudioProcessorHolder::Ptr processorHolder = nullptr; | |||
| LockedProcessorHolder processorHolder; | |||
| 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(); } | |||
| }; | |||
| //============================================================================== | |||