Browse Source

AUv3: Fix hangs loading multiple AUv3 instances in Logic

tags/2021-05-28
reuk 4 years ago
parent
commit
2a199d5225
No known key found for this signature in database GPG Key ID: 9ADCD339CFC98A11
1 changed files with 74 additions and 62 deletions
  1. +74
    -62
      modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm

+ 74
- 62
modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm View File

@@ -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(); }
};
//==============================================================================


Loading…
Cancel
Save