[AUv3] Factory-created instances retain a null AUAudioUnit pointer
Summary
The standard AUv3 factory path creates the C++ wrapper with a null Objective-C AUAudioUnit reference and does not appear to update that reference after the actual audio unit instance has been created.
As a result, the wrapper may continue sending messages to nil for operations that depend on the real AUAudioUnit instance.
Severity
P0 — Blocking
This affects the standard AUv3 extension instantiation path.
Affected area
modules/yup_audio_plugin_client/auv3/yup_audio_plugin_client_AUv3.mm
Current behaviour
The factory path constructs the C++ wrapper before the Objective-C audio unit exists:
auto* cpp = new AudioPluginProcessorAUv3 (nil, desc, 0, error);
The wrapper is initialized with nil, after which the Objective-C AUAudioUnit instance is created and associated with the wrapper.
The wrapper's stored AUAudioUnit reference does not appear to be replaced with the newly created instance before initialization-dependent methods are used.
Potentially affected calls include:
[au maximumFramesToRender]
[au musicalContextBlock]
[au transportStateBlock]
[au scheduleMIDIEventBlock]
Impact
Depending on the code path, this may cause:
- resource allocation to use invalid defaults;
- MIDI output scheduling to be unavailable;
- musical context and transport state to be unavailable;
- incorrect maximum render-frame handling;
- an AUv3 instance that initializes only partially.
Objective-C messages sent to nil do not necessarily crash, which can make this issue difficult to detect while still leaving the audio unit in an invalid state.
Steps to reproduce
- Build a YUP plugin as an AUv3 extension.
- Instantiate it through the standard
AUAudioUnitFactory path.
- Add assertions or logging around the wrapper's stored
AUAudioUnit reference.
- Allocate render resources.
- Query transport, musical context, maximum render frames, or MIDI scheduling.
Actual result
The C++ wrapper may still hold a null AUAudioUnit reference after the real audio unit has been created.
Expected result
The C++ wrapper must receive the final AUAudioUnit instance before any initialization that depends on it.
Initialization should run once, after both sides of the bridge are fully connected.
Suggested fix
Separate construction from attachment and initialization:
auto* cpp = new AudioPluginProcessorAUv3 (desc, options, error);
auto* audioUnit = [[YUPAudioUnit alloc] initWithComponentDescription:desc
options:options
error:error];
cpp->attachAudioUnit(audioUnit);
YUPAudioUnit::setThis(audioUnit, cpp);
if (!cpp->initialize())
{
// Return an error and destroy the partially created instance.
}
Also consider:
- asserting that
au != nil in all methods that require it;
- preventing
initialize() from being called more than once;
- making incomplete initialization fail explicitly.
Acceptance criteria
[AUv3] Factory-created instances retain a null
AUAudioUnitpointerSummary
The standard AUv3 factory path creates the C++ wrapper with a null Objective-C
AUAudioUnitreference and does not appear to update that reference after the actual audio unit instance has been created.As a result, the wrapper may continue sending messages to
nilfor operations that depend on the realAUAudioUnitinstance.Severity
P0 — Blocking
This affects the standard AUv3 extension instantiation path.
Affected area
modules/yup_audio_plugin_client/auv3/yup_audio_plugin_client_AUv3.mmCurrent behaviour
The factory path constructs the C++ wrapper before the Objective-C audio unit exists:
The wrapper is initialized with
nil, after which the Objective-CAUAudioUnitinstance is created and associated with the wrapper.The wrapper's stored
AUAudioUnitreference does not appear to be replaced with the newly created instance before initialization-dependent methods are used.Potentially affected calls include:
Impact
Depending on the code path, this may cause:
Objective-C messages sent to
nildo not necessarily crash, which can make this issue difficult to detect while still leaving the audio unit in an invalid state.Steps to reproduce
AUAudioUnitFactorypath.AUAudioUnitreference.Actual result
The C++ wrapper may still hold a null
AUAudioUnitreference after the real audio unit has been created.Expected result
The C++ wrapper must receive the final
AUAudioUnitinstance before any initialization that depends on it.Initialization should run once, after both sides of the bridge are fully connected.
Suggested fix
Separate construction from attachment and initialization:
Also consider:
au != nilin all methods that require it;initialize()from being called more than once;Acceptance criteria
AUAudioUnitinstance before initialization.maximumFramesToRenderis read from the correct instance.AUAudioUnitreference is non-null.