From 4bd207d6faa13d7910eb858bdad8cf32a0100b4b Mon Sep 17 00:00:00 2001 From: fwcd Date: Tue, 26 Nov 2024 20:33:20 +0100 Subject: [PATCH] AudioUnitManager: Wrap AudioUnitManager is shared pointer This fixes the issue described in https://github.com/mixxxdj/mixxx/pull/13887#discussion_r1859094714 --- .../audiounit/audiouniteffectprocessor.h | 2 +- .../audiounit/audiouniteffectprocessor.mm | 8 +-- .../backends/audiounit/audiounitmanager.h | 21 +++++-- .../backends/audiounit/audiounitmanager.mm | 55 +++++++++++-------- .../backends/audiounit/audiounitmanifest.mm | 7 ++- 5 files changed, 58 insertions(+), 35 deletions(-) diff --git a/src/effects/backends/audiounit/audiouniteffectprocessor.h b/src/effects/backends/audiounit/audiouniteffectprocessor.h index 3bbf1d0c933d..c11b0e169304 100644 --- a/src/effects/backends/audiounit/audiouniteffectprocessor.h +++ b/src/effects/backends/audiounit/audiouniteffectprocessor.h @@ -55,7 +55,7 @@ class AudioUnitEffectProcessor final : public EffectProcessorImpl m_parameters; QList m_lastValues; diff --git a/src/effects/backends/audiounit/audiouniteffectprocessor.mm b/src/effects/backends/audiounit/audiouniteffectprocessor.mm index 621e119d69e5..daee0ce0a3c7 100644 --- a/src/effects/backends/audiounit/audiouniteffectprocessor.mm +++ b/src/effects/backends/audiounit/audiouniteffectprocessor.mm @@ -142,7 +142,7 @@ AudioUnitEffectProcessor::AudioUnitEffectProcessor( AVAudioUnitComponent* _Nullable component) - : m_manager(component) { + : m_manager(AudioUnitManager::create(component)) { } void AudioUnitEffectProcessor::loadEngineEffectParameters( @@ -157,7 +157,7 @@ const mixxx::EngineParameters& engineParameters, const EffectEnableState, const GroupFeatureState&) { - AudioUnit _Nullable audioUnit = m_manager.getAudioUnit(); + AudioUnit _Nullable audioUnit = m_manager->getAudioUnit(); if (!audioUnit) { qWarning() << "Cannot process channel before Audio Unit is instantiated"; @@ -175,7 +175,7 @@ } void AudioUnitEffectProcessor::syncParameters() { - AudioUnit _Nullable audioUnit = m_manager.getAudioUnit(); + AudioUnit _Nullable audioUnit = m_manager->getAudioUnit(); DEBUG_ASSERT(audioUnit != nil); m_lastValues.reserve(m_parameters.size()); @@ -210,7 +210,7 @@ void AudioUnitEffectProcessor::syncStreamFormat( const mixxx::EngineParameters& parameters) { - AudioUnit _Nullable audioUnit = m_manager.getAudioUnit(); + AudioUnit _Nullable audioUnit = m_manager->getAudioUnit(); DEBUG_ASSERT(audioUnit != nil); if (parameters.sampleRate() != m_lastSampleRate || diff --git a/src/effects/backends/audiounit/audiounitmanager.h b/src/effects/backends/audiounit/audiounitmanager.h index 3600066ada9c..f17ed6482320 100644 --- a/src/effects/backends/audiounit/audiounitmanager.h +++ b/src/effects/backends/audiounit/audiounitmanager.h @@ -4,6 +4,7 @@ #import #import +#include #include enum AudioUnitInstantiationType { @@ -12,17 +13,22 @@ enum AudioUnitInstantiationType { AsyncOutOfProcess, }; +class AudioUnitManager; +typedef QSharedPointer AudioUnitManagerPointer; + /// A RAII wrapper around an `AudioUnit`. class AudioUnitManager { public: - AudioUnitManager(AVAudioUnitComponent* _Nullable component = nil, - AudioUnitInstantiationType instantiationType = - AudioUnitInstantiationType::AsyncOutOfProcess); ~AudioUnitManager(); AudioUnitManager(const AudioUnitManager&) = delete; AudioUnitManager& operator=(const AudioUnitManager&) = delete; + static AudioUnitManagerPointer create( + AVAudioUnitComponent* _Nullable component = nil, + AudioUnitInstantiationType instantiationType = + AudioUnitInstantiationType::AsyncOutOfProcess); + /// Fetches the audio unit if already instantiated. /// /// Non-blocking and thread-safe, since this method is intended to (also) be @@ -35,8 +41,13 @@ class AudioUnitManager { std::atomic m_isInstantiated; AudioUnit _Nullable m_audioUnit; - void instantiateAudioUnitAsync(AVAudioUnitComponent* _Nonnull component, bool inProcess); - void instantiateAudioUnitSync(AVAudioUnitComponent* _Nonnull component); + AudioUnitManager(AVAudioUnitComponent* _Nullable component); + + static void instantiateAudioUnitAsync(AudioUnitManagerPointer manager, + AVAudioUnitComponent* _Nonnull component, + bool inProcess); + static void instantiateAudioUnitSync(AudioUnitManagerPointer manager, + AVAudioUnitComponent* _Nonnull component); void initializeWith(AudioUnit _Nullable audioUnit); }; diff --git a/src/effects/backends/audiounit/audiounitmanager.mm b/src/effects/backends/audiounit/audiounitmanager.mm index 5757c02f71cd..1922a7a3d703 100644 --- a/src/effects/backends/audiounit/audiounitmanager.mm +++ b/src/effects/backends/audiounit/audiounitmanager.mm @@ -6,28 +6,36 @@ #include "effects/backends/audiounit/audiounitmanager.h" -AudioUnitManager::AudioUnitManager(AVAudioUnitComponent* _Nullable component, - AudioUnitInstantiationType instantiationType) - : m_name(QString::fromNSString([component name])), +AudioUnitManager::AudioUnitManager(AVAudioUnitComponent* _Nullable component) + : m_name(component != nil ? QString::fromNSString([component name]) + : "Unknown"), m_isInstantiated(false) { +} + +AudioUnitManagerPointer AudioUnitManager::create( + AVAudioUnitComponent* _Nullable component, + AudioUnitInstantiationType instantiationType) { + AudioUnitManagerPointer manager = + QSharedPointer(new AudioUnitManager(component)); + // NOTE: The component can be null if the lookup failed in // `AudioUnitBackend::createProcessor`, in which case the effect simply acts // as an identity function on the audio. Same applies when // `AudioUnitManager` is default-initialized. - if (!component) { - return; + if (component) { + switch (instantiationType) { + case Sync: + instantiateAudioUnitSync(manager, component); + break; + case AsyncInProcess: + case AsyncOutOfProcess: + instantiateAudioUnitAsync( + manager, component, instantiationType == AsyncInProcess); + break; + } } - switch (instantiationType) { - case Sync: - instantiateAudioUnitSync(component); - break; - case AsyncInProcess: - case AsyncOutOfProcess: - instantiateAudioUnitAsync( - component, instantiationType == AsyncInProcess); - break; - } + return manager; } AudioUnitManager::~AudioUnitManager() { @@ -49,7 +57,9 @@ } void AudioUnitManager::instantiateAudioUnitAsync( - AVAudioUnitComponent* _Nonnull component, bool inProcess) { + AudioUnitManagerPointer manager, + AVAudioUnitComponent* _Nonnull component, + bool inProcess) { auto options = kAudioComponentInstantiation_LoadOutOfProcess; if (inProcess) { @@ -62,33 +72,34 @@ } // Instantiate the audio unit asynchronously. - qDebug() << "Instantiating Audio Unit" << m_name << "asynchronously"; + qDebug() << "Instantiating Audio Unit" << manager->m_name + << "asynchronously"; // TODO: Fix the weird formatting of blocks // clang-format off AudioComponentInstantiate(component.audioComponent, options, ^(AudioUnit _Nullable audioUnit, OSStatus error) { if (error != noErr) { - qWarning() << "Could not instantiate Audio Unit" << m_name << ":" << error << "(Check https://www.osstatus.com for a description)"; + qWarning() << "Could not instantiate Audio Unit" << manager->m_name << ":" << error << "(Check https://www.osstatus.com for a description)"; return; } - initializeWith(audioUnit); + manager->initializeWith(audioUnit); }); // clang-format on } -void AudioUnitManager::instantiateAudioUnitSync( +void AudioUnitManager::instantiateAudioUnitSync(AudioUnitManagerPointer manager, AVAudioUnitComponent* _Nonnull component) { AudioUnit _Nullable audioUnit = nil; OSStatus error = AudioComponentInstanceNew(component.audioComponent, &audioUnit); if (error != noErr) { - qWarning() << "Audio Unit" << m_name + qWarning() << "Audio Unit" << manager->m_name << "could not be instantiated:" << error << "(Check https://www.osstatus.com for a description)"; } - initializeWith(audioUnit); + manager->initializeWith(audioUnit); } void AudioUnitManager::initializeWith(AudioUnit _Nullable audioUnit) { diff --git a/src/effects/backends/audiounit/audiounitmanifest.mm b/src/effects/backends/audiounit/audiounitmanifest.mm index 1ee8fd2c8f28..ec79fcaaea8a 100644 --- a/src/effects/backends/audiounit/audiounitmanifest.mm +++ b/src/effects/backends/audiounit/audiounitmanifest.mm @@ -18,10 +18,11 @@ setDescription(QString::fromNSString([component typeName])); setAuthor(QString::fromNSString([component manufacturerName])); - // Try instantiating the unit in-process to fetch its properties quickly + // Instantiate audio unit (in-process) to load parameters + AudioUnitManagerPointer manager = AudioUnitManager::create( + component, AudioUnitInstantiationType::Sync); - AudioUnitManager manager{component, AudioUnitInstantiationType::Sync}; - AudioUnit audioUnit = manager.getAudioUnit(); + AudioUnit audioUnit = manager->getAudioUnit(); if (audioUnit) { // Fetch number of parameters