Skip to content

Commit

Permalink
AudioUnitManager: Wrap AudioUnitManager is shared pointer
Browse files Browse the repository at this point in the history
This fixes the issue described in
#13887 (comment)
  • Loading branch information
fwcd committed Nov 26, 2024
1 parent e6fd1fc commit 10140c9
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 35 deletions.
2 changes: 1 addition & 1 deletion src/effects/backends/audiounit/audiouniteffectprocessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class AudioUnitEffectProcessor final : public EffectProcessorImpl<AudioUnitEffec
const GroupFeatureState& groupFeatures) override;

private:
AudioUnitManager m_manager;
AudioUnitManagerPointer m_manager;

QList<EngineEffectParameterPointer> m_parameters;
QList<AudioUnitParameterValue> m_lastValues;
Expand Down
8 changes: 4 additions & 4 deletions src/effects/backends/audiounit/audiouniteffectprocessor.mm
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@

AudioUnitEffectProcessor::AudioUnitEffectProcessor(
AVAudioUnitComponent* _Nullable component)
: m_manager(component) {
: m_manager(AudioUnitManager::create(component)) {
}

void AudioUnitEffectProcessor::loadEngineEffectParameters(
Expand All @@ -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";
Expand All @@ -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());
Expand Down Expand Up @@ -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 ||
Expand Down
21 changes: 16 additions & 5 deletions src/effects/backends/audiounit/audiounitmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#import <AudioToolbox/AudioToolbox.h>
#import <CoreAudioTypes/CoreAudioTypes.h>

#include <QSharedPointer>
#include <QString>

enum AudioUnitInstantiationType {
Expand All @@ -12,17 +13,22 @@ enum AudioUnitInstantiationType {
AsyncOutOfProcess,
};

class AudioUnitManager;
typedef QSharedPointer<AudioUnitManager> 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
Expand All @@ -41,8 +47,13 @@ class AudioUnitManager {
std::atomic<bool> 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);
};
55 changes: 33 additions & 22 deletions src/effects/backends/audiounit/audiounitmanager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,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<AudioUnitManager>(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() {
Expand Down Expand Up @@ -70,7 +78,9 @@
}

void AudioUnitManager::instantiateAudioUnitAsync(
AVAudioUnitComponent* _Nonnull component, bool inProcess) {
AudioUnitManagerPointer manager,
AVAudioUnitComponent* _Nonnull component,
bool inProcess) {
auto options = kAudioComponentInstantiation_LoadOutOfProcess;

if (inProcess) {
Expand All @@ -83,33 +93,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) {
Expand Down
6 changes: 3 additions & 3 deletions src/effects/backends/audiounit/audiounitmanifest.mm
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,18 @@
setAuthor(QString::fromNSString([component manufacturerName]));

// Instantiate audio unit (out-of-process) to load parameters
AudioUnitManager manager{component};
AudioUnitManagerPointer manager = AudioUnitManager::create(component);

const int TIMEOUT_MS = 2000;
if (!manager.waitForAudioUnit(TIMEOUT_MS)) {
if (!manager->waitForAudioUnit(TIMEOUT_MS)) {
qWarning() << name() << "took more than" << TIMEOUT_MS
<< "ms to initialize, skipping manifest initialization "
"for this effect. This means this effect will not "
"display any parameters and likely not be useful!";
return;
}

AudioUnit audioUnit = manager.getAudioUnit();
AudioUnit audioUnit = manager->getAudioUnit();

if (audioUnit) {
// Fetch number of parameters
Expand Down

0 comments on commit 10140c9

Please sign in to comment.