Audio Unit v3 - Multiple instances of same AU extension

Dear audio developers,


I am currently investigating Audio Unit v3 - App extension mechanism in OS X context.

I managed to build OSXFilterDemoAppExtension from https://developer.apple.com/library/content/samplecode/AudioUnitV3Example/Introduction/Intro.html


Using Logic 10.2.4, I was able to instantiate the FilterDemoAppExtension on my audio tracks.

This was working fine right out-of-the-box. 🙂

Once instantiated I could see the new process created which confirms my app runs in its own sandboxed process.


Problems started when I tried to add & remove multiple instances of the same app extension on other tracks, as per the following sequence:


1 - Add FilterDemoAppExtension on track1 insert.

A new dedicated process is created.

2 - Add FilterDemoAppExtension on track2 insert.

No other process created, both app extension seem to run in the same process successfully.

3 - Remove one of the instance.

I cannot see the dedicated process running anymore, and the remaining instance view becomes black and unresponsive...


As I am not really familiar today with App Extension and Sandboxing principles I wonder:

- is there any option I should use to force creating a dedicated process for each instance created ?

- should I use a reference counted mechanism to avoid process termination on first desctruction of one of the instance ?

- could it be a Logic bug I am falling into there ?

- or am I missing something else ?


Thank you for your help,

Cheers

Accepted Reply

Thanks for the clarification - yes indeed this is the case and there's no way to force a new extension process for each instance of the same audio unit being instantiated multiple times. So, indeed if your audio unit dies it will take out the other instances - but host survival is the main benefit which didn't exist previously. There are performance considerations in this design and were touched upon in the WWDC discussion of Audio Unit Extensions you may want to take a look at if you haven't seen it already.


Hope that's helpful.

Replies

I have an open Logic X bug regarding deallocation I've reproduced with 10.2.4 which is still being analyzed.

Hello,


Thank you for this feedback.

Further investigations allowed me to identify that the AU extension process is actually crashing on deallocation.


Here is the associated callstack:

(lldb) bt
* thread #1: tid = 0x3afcd, 0x00007fff8cfb028e AppKit`-[NSApplication _crashOnException:] + 109, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
  * frame #0: 0x00007fff8cfb028e AppKit`-[NSApplication _crashOnException:] + 109
    frame #1: 0x00007fff8cfb01d5 AppKit`-[NSApplication reportException:] + 320
    frame #2: 0x00007fff8cc470a7 AppKit`-[NSApplication run] + 1160
    frame #3: 0x00007fff8cc11a8a AppKit`NSApplicationMain + 1237
    frame #4: 0x00007fffa425d8b3 libxpc.dylib`_xpc_objc_main + 775
    frame #5: 0x00007fffa425c2d0 libxpc.dylib`xpc_main + 494
    frame #6: 0x00007fff90a157bb Foundation`-[NSXPCListener resume] + 165
    frame #7: 0x00007fff9f512433 PlugInKit`-[PKService run] + 865
    frame #8: 0x00007fff9f511fa0 PlugInKit`+[PKService main] + 55
    frame #9: 0x00007fff9f512457 PlugInKit`+[PKService _defaultRun:arguments:] + 17
    frame #10: 0x00007fff90bb795c Foundation`NSExtensionMain + 51
    frame #11: 0x00007fffa3ffa255 libdyld.dylib`start + 1
    frame #12: 0x00007fffa3ffa255 libdyld.dylib`start + 1


And relevant console logs:

10:54:51.297947 +0200      Logic Pro X      extension connection was interrupted
10:54:51.392913 +0200      Logic Pro X      304: Extension request interrupted! (AU likely crashed. 0x60800032ac80 0x7f897a588690)
10:55:00.567469 +0200      Logic Pro X      TIC TCP Conn Cancel [1:0x600000186660]
10:55:00.567535 +0200      Logic Pro X      TIC TCP Conn Destroyed [1:0x600000186660]


Would you please confirm we are talking about the same issue ?


But while evaluating AUv3, it makes me wonder the following questions:


AppExtension/Sandboxing sounds like a good feature to isolate a plug-in in a separate process.

But if all instances of the same AppExtension share the same process, it means that a plugin instance that crashed will impact other instances as well.

In my situation it also sometimes makes my Logic app to crash as well because communication with other instances is broken.

So at the end it seems it just breaks the Sandbox feature that should prevent my host app to crash if my plugin crash...


For this reason, I think it is key for AUv3 that each instance of a given AppExtension runs into its own process.

Is it possible somehow ? I started walk through Sandbox documentation but I still cannot find the answer...


Thank you very much for your help,

Best regards

This is what I'm tracking, happens on deallocation and appears to be Logic specific. Regarding in/out of process instantiation, I think you're referring to the AudioComponentInstantiationOptions. On macOS a host may request in-process loading of an AUv3.


[27866]: AudioUnitViewController Deallocated
[27866]: AUAudioUnit Deallocated

[27866]: An instance 0x7fc68963c030 of class MyAudioUnit was deallocated while key value observers were still registered with it. Current observation info: <NSKeyValueObservationInfo 0x7fc689414cb0> (
<NSKeyValueObservance 0x7fc689555100: Observer: 0x7fc6894019d0, Key path: parameterTree, Options: <New: NO, Old: NO, Prior: NO> Context: 0x0, Property: 0x7fc68955f2f0>
<NSKeyValueObservance 0x7fc689562300: Observer: 0x7fc6894019d0, Key path: allParameterValues, Options: <New: NO, Old: NO, Prior: NO> Context: 0x0, Property: 0x7fc689561390>
)

[27866]: (
  0   CoreFoundation                      0x00007fff93d534f2 __exceptionPreprocess + 178
  1   libobjc.A.dylib                     0x00007fff89a5bf7e objc_exception_throw + 48
  2   CoreFoundation                      0x00007fff93dba4bd +[NSException raise:format:] + 205
  3   Foundation                          0x00007fff972b3f80 NSKVODeallocate + 294
  4   AudioToolbox                        0x00007fff944f474f -[AURemoteExtensionContext dealloc] + 160
  5   Foundation                          0x00007fff97264329 -[_NSXPCConnectionExportedObjectTable setExportedObject:forProxyNumber:] + 103
  6   Foundation                          0x00007fff972751ed message_handler + 446
  7   libxpc.dylib                        0x00007fff9982756e _xpc_connection_call_event_handler + 35
  8   libxpc.dylib                        0x00007fff99825a0a _xpc_connection_mach_event + 1209
  9   libdispatch.dylib                   0x00007fff8861f596 _dispatch_client_callout4 + 9
  10  libdispatch.dylib                   0x00007fff8862341b _dispatch_mach_cancel_invoke + 56
  11  libdispatch.dylib                   0x00007fff8861e44c _dispatch_mach_invoke + 972
  12  libdispatch.dylib                   0x00007fff8861c200 _dispatch_queue_drain + 1207
  13  libdispatch.dylib                   0x00007fff88622707 _dispatch_queue_invoke + 549
  14  libdispatch.dylib                   0x00007fff8861ad53 _dispatch_root_queue_drain + 538
  15  libdispatch.dylib                   0x00007fff8861ab00 _dispatch_worker_thread3 + 91
  16  libsystem_pthread.dylib             0x00007fff8dab74de _pthread_wqthread + 1129
  17  libsystem_pthread.dylib             0x00007fff8dab5341 start_wqthread + 13
)

Hello theanalogkid,


Thank you very much for your feedback. I am happy to know this Logic issue is being analyzed.

Concerning process instantiation, let me clarify my request:

I am aware of In/Out host process instantiation, thanks to:

  • kAudioComponentInstantiation_LoadInProcess
  • kAudioComponentInstantiation_LoadOutOfProcess


My request only concerns

kAudioComponentInstantiation_LoadOutOfProcess


I noticed that when I load 2 instances of the same Audio Unit app extension, both are running in the same Process (but not the same as the host, as expected).

As a consequence, if instance A crashes the app extension process, instance B cannot run anymore.

Thus I am wondering: can we force a new process creation for each instance of the same AU App Extension ?


Best regards,

Joris

Thanks for the clarification - yes indeed this is the case and there's no way to force a new extension process for each instance of the same audio unit being instantiated multiple times. So, indeed if your audio unit dies it will take out the other instances - but host survival is the main benefit which didn't exist previously. There are performance considerations in this design and were touched upon in the WWDC discussion of Audio Unit Extensions you may want to take a look at if you haven't seen it already.


Hope that's helpful.

Hello theanalogkid,


Thank you very much for this confirmation.

This is helpful to me.

There are performance considerations in this design and were touched upon in the WWDC discussion of Audio Unit Extensions you may want to take a look at if you haven't seen it already.

Yes I am already using this material. Thank you for the tip. 😉

Best regards,

Joris

Whoa! This is critical info that I see no reference to anywhere in documentation. If one looks at App Extension Programming Guide Audio Unit is the only one that will really be used multipult times in a host, so this is probably why this hasn't come up more. I gotta say there is no documentation anywhere that make it clear that multiple instances of an extension in a single host will share the same process. Actually the WWDC video you reference sounds a lot like multiple instances will be created: "we saw how they get loaded by the system into separate extension service processes." When I heard that I actually heard, "every instance gets its own process". I now understand that is not the case. I think there needs to be a note about this in documentation.


Another point: The whole part about performace considerations. Would loading a process for each instance of a audio unit change the preformace vs all instances in the host being in the same process. There really isnt communication between the instances, only between host and audio unit. Right? Or is there optimization under the hood that is bundling inter-proccess compunication into one "call"? That would be awsome! Seems unlikely. To clarify, If I place two instances of the same AU in an AVAudioEngine inline. So inputUnit -> AUInstance1 -> AUInstance2->ouputUnit. I would have to plan on 80μs instead of 40μs even tho they are in the same process.

For my intention is the question "why does crash the AUv3 plugin" ???


if you code the au plug with dsp algorithm and so on - sure it can crash sometimes - but its on you to fix the issue that crashed the instance...

it is also none practical to publish or distribute software that can crash sometimes - there a lot of reasons why....


so its on you to fixe these issues to go stable with the plugin...


i have tested your problems, but i can see no probs in the hirachie of the sandbox and / or the process as well, when the instances run in the same process - while it is the same process (with or without different parameters or properties)...


So what i want to tell you - if you got crashes on one instance, other instances will also be able to crash - in conjunction of time, a time periode or in conjunction with your DSP algorithm...



Of course it is posibble to make any instance of a plug to its own process as well - but the AUv3 is (for me) in Beta - time will go over to solve any problems... :-)

Hello, there are currently many issues for MIDI FX Plugins and Audio Unit v3:
  • MIDI FX Plugins do not receive the host sample rate, there is no way to inherrit or react to sample rate changes made in Logic Pro X. (This affects severly plugins that calculate midi delta using the event.sampleTime)

  • MIDI FX Plugins that are instantiated as multiple instances on 2 or more tracks, the midi on other instances than selected track are passed and can be seen in plugin instance, however they are not played back later on instrument. This is a serious bug as If anybody instantiate Midi FX Plugin v3 on 2 or more instances, it breaks the playback of these tracks.