Cannot get kAudioUnitSubType_VoiceProcessingIO code working

I’m attempting realtime AEC (Automatic Echo Cancellation) on macOS using kAudioUnitSubType_VoiceProcessingIO.

I’ve got a pure C++ solution working (gist). You can build that from Terminal. It will play a 440Hz sinewave for 5s during which time you may make noises, which will be recorded and saved (with the sinewave subtracted).

However porting this machinery to JUCE is proving an utter nightmare.

I create a fresh empty app from Producer, setting a BundleID (which seems to be needed, see below), and enabling MicInput. Then I overwrite MainComponent.{h,cpp} and create AEC.hpp (gist)

The code builds and runs with no errors, but it doesn’t work. And I get wierd console warnings that don’t google. I’ve dumped output at the bottom of the gist, but the one that catches my attention is:

AUVoiceIO can't set vp bypass state for null bundleID

So many questions.

  • My other JUCE realtime audio project works fine and without spewing this warning. That’s using AudioSource and getNextAudioBlock.

  • The C++ testcase works perfectly without spewing warnings, or requiring any kind of “bundleID” faffing.

  • Why should an audioUnit care about a bundleID? That makes no sense to me.

GPT4o is also stuck. It’s suggested toggling “Sandbox” on, no luck there.

I even discovered my Apple Developer subscription has lapsed, and reactivated it just in case there’s some silent provisioning-related error somewhere.

All said and done, I am as stuck as a duck in a truck.

Would another engineer care to run this testcase and see whether they get the same output as do I? I would be most grateful.

And can anyone explain what is going on?

The bundle ID is a key reference in plugins because its relevant to MacOS/iOS DAW’s, used to enumerate the plugins available, find the one the user wants in their project, and ensure that it is re-instantiated whenever the project is closed/opened. Its intended as the unique bundle identifier that will give the DAW the resources it needs to find the intended plugin.

And, its even more relevant with AUv3, because Plugins are no longer to be distributed as separate “loadable library” style resources, but instead as “application extensions” (.appex files) which can be bundled in any MacOS/iOS application, in order to comply with the architecture of MacOS/iOS’s sandbox techniques - its moving more to a protected IPC model instead of a loaded-library model, where plugins are really their own separated processes with protected inter-process communication methods instead of just loadable function call sites.

Did you also set a unique pluginManufacturerCode?