From 45a52352985cae83fa420f6e9745145c146c2305 Mon Sep 17 00:00:00 2001 From: reuk Date: Tue, 26 Apr 2022 13:38:36 +0100 Subject: [PATCH] AUv3: Fix race on factoryPresets Some AUv3 presets crash when querying the set of presets in Loopy Pro. The issue seems to be because `addPresets` may end up being called concurrently with the host's queries. --- .../AU/juce_AUv3_Wrapper.mm | 56 +++++++++++++------ 1 file changed, 40 insertions(+), 16 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 db7350b1d0..5eac26c055 100644 --- a/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm +++ b/modules/juce_audio_plugin_client/AU/juce_AUv3_Wrapper.mm @@ -133,7 +133,7 @@ public: //============================================================================== virtual AUAudioUnitPreset* getCurrentPreset() = 0; - virtual void setCurrentPreset(AUAudioUnitPreset*) = 0; + virtual void setCurrentPreset (AUAudioUnitPreset*) = 0; virtual NSArray* getFactoryPresets() = 0; virtual NSDictionary* getFullState() @@ -517,22 +517,12 @@ public: //============================================================================== AUAudioUnitPreset* getCurrentPreset() override { - const int n = static_cast ([factoryPresets.get() count]); - const int idx = static_cast (getAudioProcessor().getCurrentProgram()); - - if (idx < n) - return [factoryPresets.get() objectAtIndex:static_cast (idx)]; - - return nullptr; + return factoryPresets.getAtIndex (getAudioProcessor().getCurrentProgram()); } void setCurrentPreset (AUAudioUnitPreset* preset) override { - const int n = static_cast ([factoryPresets.get() count]); - const int idx = static_cast ([preset number]); - - if (isPositiveAndBelow (idx, n)) - getAudioProcessor().setCurrentProgram (idx); + getAudioProcessor().setCurrentProgram (static_cast ([preset number])); } NSArray* getFactoryPresets() override @@ -1185,6 +1175,38 @@ private: juce::AudioBuffer scratchBuffer; }; + class FactoryPresets + { + public: + using Presets = std::unique_ptr, NSObjectDeleter>; + + void set (Presets newPresets) + { + std::lock_guard lock (mutex); + std::swap (presets, newPresets); + } + + NSArray* get() const + { + std::lock_guard lock (mutex); + return presets.get(); + } + + AUAudioUnitPreset* getAtIndex (int index) const + { + std::lock_guard lock (mutex); + + if (index < (int) [presets.get() count]) + return [presets.get() objectAtIndex: (unsigned int) index]; + + return nullptr; + } + + private: + Presets presets; + mutable std::mutex mutex; + }; + //============================================================================== void addAudioUnitBusses (bool isInput) { @@ -1428,7 +1450,7 @@ private: void addPresets() { - factoryPresets.reset ([[NSMutableArray alloc] init]); + FactoryPresets::Presets newPresets { [[NSMutableArray alloc] init] }; const int n = getAudioProcessor().getNumPrograms(); @@ -1440,8 +1462,10 @@ private: [preset.get() setName: juceStringToNS (name)]; [preset.get() setNumber: static_cast (idx)]; - [factoryPresets.get() addObject: preset.get()]; + [newPresets.get() addObject: preset.get()]; } + + factoryPresets.set (std::move (newPresets)); } //============================================================================== @@ -1804,7 +1828,7 @@ private: std::unique_ptr paramTree; std::unique_ptr, NSObjectDeleter> overviewParams, channelCapabilities; - std::unique_ptr, NSObjectDeleter> factoryPresets; + FactoryPresets factoryPresets; ObjCBlock internalRenderBlock;