Audio Units v3 OS x: Instantiating custom audio units

I'm working on writing a custom audio unit using the v3 APIs, and I'm having trouble getting them to instantiate correctly under some circumstances:


I can instantiate my Audio Unit if I include its view controller and AUAudioUnit subclass within the associated app (either directly or by linking them in from a framework) and then registering it using AUAudioUnit.registerSubclass(asComponentDescription:, name:, version:), but I haven't been able to get it to work by installing the .appex file anywhere. The system just doesn't find it. I have tried putting the .appex in ~/Library/Audio/Plug-Ins/Components, and /Library/Audio/Plug-Ins/Components but it never shows up in the list returned by AVAudioUnitComponentManager.sharedAudioUnitComponentManager().componentsMatchingDescription(). I created a minimal test project using the templates provided by XCode for an Audio Unit Extension, and I've been careful to be sure the Info.plist file for the extension has been filled in correctly.


Has anyone else successfully created a v3 audio unit and had it recognized by the system? If so, how?


Also, for v3 audio units with custom UIs, does one override requestViewControllerWithCompletionHandler()? This seems to be the only way to instantiate a custom view controller. The FilterDemo from AudioUnitV3Example instantiates a custom UI in the embedding view controller, which seems totally wrong if one wants to package the audio unit for use elsewhere in the system.


Thanks.

Replies

You don't install the .appex bundle separately like you would an Audio Unit Plug-In or Component Manager based component. App Extensions don't work this way and you must use an app to contain and deliver your extension. Each extension is a separate binary that runs independent of the app used to deliver it but the app is the delivery mechanism as is required.


I suggest taking a look at the following resources for some general information about App Extensions as well as some specifics about Audio Unit Extensions.


App Extension Programming Guide

App Extension Types - Audio Unit

WWDC Session 508 - Audio Unit Extensions

AudioUnitV3Example


requestViewControllerWithCompletionHandler: is implemented in the framework and asynchronously requests the audio unit's view controller. Host apps call this to obtain an audio unit's view for embedding. You can see how this works in the AUv3Host sample in the HostViewController file. It's recommended that v3 Audio Unit Extensions always provide a custom view.

Thanks for the quick response.


I read through all of the documentation, and now I understand how deployment and distribution of Audio Unit App Extensions is supposed to work, however I still haven't managed to get it to work! In particular:


It says in the docs, "Xcode registers a built app extension for the duration of the debugging session on OS X. This means that if you want to install the development version of your extension on OS X you need to use the Finder to copy it from the build location to a location such as the Applications folder."


However, if I specify, for example, the AUv3Host example app as the host to run for debugging my extension, the host app still doesn't find my audio unit during that debug session. Nor does copying my containing app into the Applications folder help. Nor does the extension show up in the Extensions tab of System Preferences where it says I need to enable it first. In case it matters I have signed both the extension and the containing app with my developer identity.


Any idea what I'm doing wrong here?


Thanks.

OK, I'm getting a little frustrated here!


I have tried creating lots of variant test projects, attempting to follow the documentation exactly, and nothing works completely.


First Scenario: containing app with Audio Unit Extension created entirely from Xcode templates

  1. Created a new app.
  2. Added an Audio Unit Extension target to it
  3. Set up Scheme to run the Audio Unit Extension in the OSXAUv3Host demo app.

Result: OSXAUv3Host demo app does not find the extension.


Second Scenario: used containing app to debug AU Extension

  1. Added code to app's ViewController viewDidLoad method to instantiate my audio unit:
  2. var desc = AudioComponentDescription()
    
    desc.componentType = kAudioUnitType_Effect
    desc.componentSubType = UTGetOSTypeFromString("YaAt")
    desc.componentManufacturer = UTGetOSTypeFromString("dlby")
    desc.componentFlags = 0
    desc.componentFlagsMask = 0
    
    AUAudioUnit.instantiateWithComponentDescription(desc, options: [])
    {[weak self] au, error in
        guard let localAudioUnit = au else { return }
        guard let strongSelf = self else { return }
        strongSelf.audioUnit = localAudioUnit
    }
  3. Set Xcode to run the containing app when debugging the extension

Result: AUAudioUnit.instantiateWithComponentDescription() calls the closure with a au=nil and an error set. In other words, the system doesn't find the extension despite the fact that the documentation says that Xcode was supposed to have registered it for the debug session.


Third Scenario: refactor the code, putting the audio unit extension code into a framework so it can be shared with the containing app, and then adding code just before line 9 above to explicitly register the audio unit:

AUAudioUnit.registerSubclass(MyAudioUnit.self , asComponentDescription: desc, name: "Dolby: YetAnotherAUTestExtension", version: UInt32.max)

Result: AUAudioUnit.instantiateWithComponentDescription() calls the closure with a valid audio unit.


Fourth Scenario: attempt to instantiate the AU's UI:

Added code within the closure to instantiate the UI:

localAudioUnit.requestViewControllerWithCompletionHandler
{vc in
    guard let  viewController = vc else { return }
...

Result: The closure above is called with vc=nil.


I even tried using Apple's own AudioUnitV3Example to test this:

  1. Set Xcode to execute the OSXAUv3Host demo app when running the OSXFilterDemoAppExtension scheme
  2. Ran the OSXFilterDemoAppExtension scheme

Result: Apple's AUV3Host example program cannot find Apple's own FilterDemoAppExtension!


The only thing that seems to work is explicit AU registration, and instantiation of the AU's UI in the host app's view controller, which is the only thing shown as working in Apple's demo code. This is not acceptable, as it prevents actually using v3 audio units as system components that can be plugged into other host apps.


Having invested way too much time so far learning and trying to get the new AU APIs to work, I really don't want to give up and go back to the v2 APIs. Please help me figure out what's going on here.


Thanks.

Xcode should register the extension for you during the debug session and it does work. Here's what I did after downloading the sample - built OSXFilterDemoApp & OSXAUv3Host, then chose OSXFilterDemoAppExtension and modified the scheme changing the executable to AUv3Host.app, checking the debug executable checkbox - set a breakpoint at the top of initWithComponentDescription in FilterDemo.mm and started a debug session.


Once up and running in AUv3Host, pick "AUV3FilterDemo (Demo)" from the list and hit the breakpoint. I'm using Xcode 7.3.1 (7D1014) on OSX 10.11.4.


The other way to register the extension is to run the container app once. So, to do a test completely outside of Xcode, build OSXFilterDemoApp & OSXAUv3Host. Launch OSXFilterDemoApp and quit it, then run OSXAUv3Host and you should see the filter in the list.


To check if a AUv3 is actually registered (good way to check for duplicates as well) use:

pluginkit -mv


The output will look something like:


com.example.apple-samplecode.FilterDemoAppOSX.FilterDemoAppExtensionOSX(1.0) 75F9AC2C-C02E-4498-91FD-2BD06F6E88EA 2016-05-15 09:18:00 +0000 /Users/geddy/Downloads/FilterDemo.app/Contents/PlugIns/FilterDemoAppExtension.appex


Also the Console:


pkd[288]: INSTALLED:com.example.apple-samplecode.FilterDemoAppOSX.FilterDemoAppExtensionOSX com.example.apple-samplecode.FilterDemoAppOSX.FilterDemoAppExtensionOSX(1.0) <__NSConcreteUUID 0x7f84fb9ee850> 75F9AC2C-C02E-4498-91FD-2BD06F6E88EA /Users/geddy/Downloads/FilterDemo.app/Contents/PlugIns/FilterDemoAppExtension.appex


If you're not sure why the .appx isn't being registered use:

syslog -w


Look for errors from pkd or ***


Also note that Code Signing & App Sandbox is required for the OS X .appex, so make sure to set the Identity Signing settings correctly for the OSXFilterDemoAppExtension target in Xcode.

Thanks!


I got it basically working. It turns out that pkd somehow had got its knickers in a twist and wasn't loading any extensions properly. After a reboot it started to behave again. Plus I had to fix the signing stuff which wasn't quite right.


Sometimes my AU extension fails to be instantiated, but this is probably something in my own code. Now I'm back to just squashing my own ordinary run-of-the-mill bugs.

Now I'm starting to get a little desperate! I have a demo next week and haven't yet figured out how to get everything working.


What I need to do is:


1. Take input from a particular hardware input and

2. Connect it to a processing graph running some audio units and ultimately

3 .Send it to a differenthardware output.


The problem of course is that I can't use two different AUHALs in a single graph. So, using the CAPlayThrough code as a reference I first tried to drop the audio units I had already created into the graph that in the demo contains the Varispeed node (which I don't actually need in this case since both my input and output are in the same clock domain). The problem is that my audio units (which now work fine, thanks) are version 3 audio units and I can't seem to plug them into the old code which is all version 2.


So next I went back to my previous codebase which uses the AVAudioEngine. I wrote a connector audio unit that is a "generator", except that what it "generates" actually comes out of a ring buffer using code like that in CAPlayThrough. The problem is there doesn't seem to be any way to get a reference to this audio unit that I can cast to a specific type and call methods on it that let me set its hardware input device from the host program. AVAudioUnit.instantiateWithComponentDescription() produces what looks like a valid object, but the AUAudioUnit returned in the .AUAudioUnit property doesn't seem to be the same object that appears in my audio unit's init() method when I set a breakpoint in the debugger and examine the address of self, and thus I can't cast it to my specific type and call type-specific methods.


How do I either get my version 3 audio units working in an old graph, or call specific methods on the AUAudioUnit subclass within an AVAudioUnit instantiated from a component description?


Thanks.