Saturday, March 15, 2014

MIDI Controllable AU Project Setup in Xcode 5.

My first task in learning audio programming was setup of an AudioUnit project. Now is 2014 and my Mac is running Maverics so I've decided to create "Lion and later" unit with generic UI. 
With this consideration I wanted to include minimum needed to compile working plugin omitting backward compatibility additions.
As starting point for this I used sample projects from Apple and this great tutorial from Sample &  Hold.


Project

Usual "File > New > Project..." sequence opened the project setup wizard where I selected "OSX > Framework & Library > Bundle" template and clicked next. On the next screen I gave the name "TheMIDIFXPlugin" to the project. The rest remained unchanged including Cocoa as base framework.


Frameworks

I included the following three libraries (and removed all the rest):
  • AudioToolbox.framework
  • AudioUnit.framework
  • CoreServices.framework 
Also added corresponding imports in Prefix file:
  • #include <AudioToolbox/AudioToolbox.h>
  • #include <AudioUnit/AudioUnit.h>
  • #include <CoreServices/CoreServices.h>

Core Audio Classes

The latest version of Core Audio Utility Classes I found was this one. It contains the essential base for Audio Unit development as well as various utility classes. 

AudioUnits/AUPublic/AUBase
contains very basic codebase for any type of AU. I added the entire folder except AUResources.r,  AUDispatch header and cpp files since for AUPlugin model corresponding AUPluginDispatch files used.

AudioUnits/AUPublic/OtherBases 
contains bases for different types of effect plugins whereas AudioUnits/AUPublic/AUInstrumentBase is foundation for instrument units. Which classes you need from these folders depends on which kind of AU you're making. My intention was to create effect unit controllable by MIDI. After checking Apple docs for the advice on what to include I added AUMIDIEffectBase and its ancestors: AUMIDIBase and AUEffectBase.

That's the basics. The rest I figured out later by compiling the project and looking into the errors navigator. Here is the resulting list.

From AudioUnits/AUPublic/Utility:
  • AUBaseHelper.h
  • AUBaseHelper.cpp
  • AUBuffer.h
  • AUBuffer.cpp
  • AUSilentTimeout.h

From AudioUnits/PublicUtility:
  • CAAtomic.h
  • CAAtomicStack.h
  • CAAudioChannelLayout.cpp
  • CAAudioChannelLayout.h
  • CAAutoDisposer.h
  • CADebugMacros.cpp
  • CADebugMacros.h
  • CADebugPrintf.cpp
  • CADebugPrintf.h
  • CAException.h
  • CAHostTimeBase.cpp
  • CAHostTimeBase.h
  • CAMath.h
  • CAReferenceCounted.h
  • CAStreamBasicDescription.cpp
  • CAStreamBasicDescription.h
  • CAThreadSafeList.h
  • CAVectorUnit.cpp
  • CAVectorUnit.h
  • CAVectorUnitTypes.h
  • CAXException.cpp
  • CAXException.h

The Code

My basic header and implementation files were almost empty. The only thing to mention here is because I was creating MIDI-controlled effect I passed AUMIDIEffectFactory as the first parameter for AUDIOCOMPONENT_ENTRY macro. AUMIDIEffectFactory extends basic method lookup selectors list with two MIDI  related selectors - for MIDI event and SysEx so plugin can actually
handle  MIDI events.


Info.plist

This part is well described in tutorials and famous tech note 2276 so just brief mention for completeness.

  • manufacturer - should be four-letter code with at least one capital letter.
  • type - four-letter code describing type of the plugin. I put here one of the codes provided by Apple. Namely - "aumf".  Possible types with comprehensive explanation are in AUComponent header in AudioUnit.framework and in the Apple docs.
  • subtype - any four-letter code can be used here. I set "mfxp".
  • factoryFunction - the function itself is generated by AUDIOCOMPONENT_ENTRY macro defined in ComponentBase header (the one for ML and later). It accepts component class name as the second parameter and uses it to construct name of factory function by adding "Factory" word. So for my main class "TheMIDIFXPlugin" the factory function name is TheMIDIFXPluginFactory.
  • description - "Empty MIDI FX Plugin" for this project.
  • name - the name as it will appear in host applications. "The MIDI FX Plugin" for this project.
  • version - the only field with type Number. I set 0xFFFFFFFF - thanks once again for the explanation to Sample & Hold.


Build Settings

The only changes I did in Build Settings tab were.

  • Set -bundle as a value  "Linking -> Other Linker Flags"
  • Set component as a value for Packaging->Wrapper Extension.
  • As my AudioUnit only works with AUPlugin model I added corresponding  CA_USE_AUDIO_PLUGIN_ONLY=1 Preprocessor Macros for both Debug And Release in "Apple LLVM 5.0 - Preprocessing" section.
  • Ensured Precompile Prefix Header was set to "YES".

Build Phases

Besides frameworks, mentioned above I added Copy Files phase with Destination: Absolute Path,  path to components folder: /Users/[user name]/Library/Audio/Plug-Ins/Components and TheMIDIFXPlugin.component in the list of files to copy.

Warnings and Errors

After eliminating all the errors related to including necessary files I still had one error in AUMIDIEffectBase::ComponentEntryDispatch. It could not find corresponding method in  AUEffectBase. The latter inherits this method from ComponentBase, and there we can see that this method is not needed for AUPlugin model and excluded from compilation  by CA_USE_AUDIO_PLUGIN_ONLY macro.
I did the same for AUMIDIEffectBase::ComponentEntryDispatch by extending #if !TARGET_OS_IPHONE condition with  && !CA_USE_AUDIO_PLUGIN_ONLY.

Test

Eventually I built and validated the component with auval.
# auval -v aumf mfxp Acme
After successful validation I also opened Logic to check that it actually recognises plugin as MIDI-controled one. And it does! Cool!



That's it. :)
I uploaded the project on Github.

No comments:

Post a Comment