This is the home of the Destroy FX Audio Unit utilities library


Introduction

Hello. Here you will find information about a library of helpful functions for dealing with Audio Units in macOS, written by Sophia Poirier of Destroy FX. Currently, the library contains code that is useful for these things:


License

This library binary and source code are released under the terms of a modified BSD License. It allows you to still use the library even if your project is not open source, although we still strongly encourage open source music software development. The documentation (what you are looking at right now) is released under the terms of the GNU Free Documentation License, with the exception of the example code snippets (in sections labeled "programming example"), which, like the other source code, are released under the terms of the same modified BSD License.


How to get it

You can download the packaged up distribution. It contains documentation, header files, a pre-built Mach-O library, resource files, and source files. Since I might not always keep that perfectly up to date, you can always find the latest sources in the Destroy FX source repository.

Please note that there are a lot of files in the repository, most of which are not a part of this library. The files that comprise this library are located within the dfx-library directory and are the ones that start with dfx-au-utilities in their names and the ones in the {language-code}.lproj bundles (those are localized resource files, where {language-code} is an ISO 639 language code).


How to use it

First, you are going to want to read the documentation for any part of the library that you are planning to utilize. This section, however, contains some general info about how to incorporate the library into your project.

The simplest way to use the library is to #include "dfx-au-utilities.h" and link against the pre-built static library lib-dfx-au-utilities.a. If you are using any of the AU preset file stuff in the library, you will also need to include the resource files that are in the localized {language-code}.lproj bundles (where {language-code} is an ISO 639 language code). Those bundles contain localized string *.strings files. I have named them in such a way that they should hopefully not clash with the names of anyone else's existing resource files. Note that, if you rename these resource files, the library will not work, unless you also modify the source code to reference the new file names.

If you don't want to use the pre-built library, or if you want to make modifications to the source code, then instead of linking against the pre-built library, you will want to compile and link all of the *.c and *.m source files. There is also a header file named dfx-au-utilities-private.h that contains an interface for the functions that are used by some of the functions defined in the primary interface. If you're interested, you may want to check that stuff out, and perhaps you'll find something useful for your projects, in which case you can also #include "dfx-au-utilities-private.h" in your source code to be able to access and use those functions. I am not going to really document that stuff, though, since I don't consider it to be the primary library interface, and I'd prefer to keep things simple and clean.

This library relies on some code in the AudioToolbox.framework and CoreFoundation.framework system frameworks, so if your project does not already link against those, then you will need to add them to your project.

This library presents a C API. However, whenever possible (and sensible), transactions are done with CoreFoundation types. This means that Objective C developers can easily utilize the code, since CF types are bridgeable between Carbon and Cocoa.


Contact the author

There is a contact section at the bottom of destroyfx.org if you wish to contact Sophia. If you experience any problems with the library or make any interesting modifications, please let me know! If you have a question about any of this stuff, please feel free to ask me; that way I know what is lacking or unclear in the documentation.






Documentation

Finding, loading, and saving AU preset files

CFArray callbacks for Factory Presets arrays

CFArray callbacks for AU Migrate arrays

Getting an AU's plugin name and manufacturer name

Comparing ComponentDescriptions

Posting parameter change notifications






Finding, loading, and saving AU preset files


Why this matters

In macOS, there are standardized locations for Audio Unit preset files:

	~/Library/Audio/Presets/{manufacturer-name}/{plugin-name}
	/Library/Audio/Presets/{manufacturer-name}/{plugin-name}

(where {manufacturer-name} is the first part of the name from the AudioComponent name string and {plugin-name} is the second part of that string)

In order to best take advantage of the benefits that users get from centralized and standardized plugin settings locations, every AU host should should make it easy for users to save to those locations and to access the preset files that are stored in those locations. The user should not have to think about this issue; things should just work smoothly. Otherwise, you can be sure that virtually no users will be going to the trouble of navigating to the proper standard folder, and then they will lose out.

Rather than simply proposing a solution and leaving it up to everyone to implement in their own way, I decided to just provide one that everyone can use. Using this library, you can do things The Right Way™ with little effort. There is a save function that provides a simple means for saving an AU's current state to a preset file in the proper location within the user domain, where user-authored files should go. For finding and opening AU preset files, there is a function that you can call that will return a CFTree of all of the preset files for a given AU in a given file system domain. You can use this to generate a menu of preset files, or whatever other sort of user interface works for your particular application. (This side of things isn't as ready-made as the saving side of things, but that is because I don't want to pretend that I know how everyone's user interface could best present a collection of preset file names to the user.) There is also a function for reading an AU preset file's data and applying it as the new state data for an AU instance.

I really think that it would be a big improvement if every host had behavior like this. Users want an easy way to have their plugin settings accessible in all of their plugin host apps. This gives them that. Users don't want to always worry about where their files are being saved and always be asked to choose a location. This gives them that. It gives a clean, simple system with advantages of interoperability between hosts.


Types

DFXFileSystemDomain


Functions

SaveAUStateToPresetFile
SaveAUStateToPresetFile_Bundle
CustomSaveAUPresetFile
CustomSaveAUPresetFile_Bundle

CFTreeCreateFromAUPresetFilesInDomain
GetCFURLFromFileURLsTreeNode
CopyAUPresetNameFromCFURL
RestoreAUStateFromPresetFile
GetAUComponentDescriptionFromStateData
GetAUComponentDescriptionFromPresetFile
FindPresetsDirForAU
CFURLIsAUPreset


Constants

kDFXFileSystemDomain_Local
kDFXFileSystemDomain_User

To give you a better understanding of the purposes of these functions, here's what I think the ideal host behavior might be, using these functions:

For each instanciated AU, the host offers a menu for preset file stuff. The first item in the menu says "Save Preset File..." When the user chooses that menu item, the host presents some UI to allow the user to specify a name for the preset and then with that calls SaveAUStateToPresetFile.

The host calls CFTreeCreateFromAUPresetFilesInDomain for each relevant file system domain: kDFXFileSystemDomain_User and kDFXFileSystemDomain_Local. For each one that returns a valid CFTree, the host creates a sub-menu item accordingly: "User presets" and "Local presets". The host populates those sub-menus by walking through the respective trees. For each node in the tree, the host uses GetCFURLFromFileURLsTreeNode to get the CFURL for that tree node. For each file CFURL in the tree, the host uses CopyAUPresetNameFromCFURL to get a CFString to use as the text for that menu item. For each sub-directory CFURL in the tree, the host uses CFURLCopyLastPathComponent to get a CFString to use as the text for that menu item, and makes that menu item another sub-menu for all of the children of that tree node. Now the sub-menus are complete. When the user chooses any one of the preset files from these sub-menus, the host uses RestoreAUStateFromPresetFile to load that file in the AU.

Finally, there is one more item in this menu: "Load Preset File..." When the user chooses that item, the host presents a file open dialog whose initial location is set using FindPresetsDirForAU, and whose selectable file filter relies upon CFURLIsAUPreset, and then uses RestoreAUStateFromPresetFile to load that selected file in the AU.



DFXFileSystemDomain

typedef enum
{
	kDFXFileSystemDomain_Local,
	kDFXFileSystemDomain_User
} DFXFileSystemDomain


explanation

The macOS file system is split into "domains", and the two domains handled in some of this library's functions are "local" and "user". The local domain is for non-system files that are globally shared by all users of a computer (location is relative to / the root of the system hard disk). The user domain is for files encapsulated only within a given user account (location is relative to ~/ the root of the user account directory).



SaveAUStateToPresetFile

OSStatus SaveAUStateToPresetFile(AudioComponentInstance inAUComponentInstance, CFStringRef inAUPresetNameString, CFURLRef* outSavedAUPresetFileURL, Boolean inPromptToReplaceFile)

inAUComponentInstance
The Audio Unit instance whose state data (ClassInfo AU property) you want to write out to a preset file.

inAUPresetNameString
The name to use for the saved AU preset file.

outSavedAUPresetFileURL
Upon successful return, this may point to a CFURLRef that references the file to which the data was saved. Can be NULL.

inPromptToReplaceFile
Whether a dialog will be presented asking the user before replacing the existing file, if a file already exists at the save location.

result
A status code: noErr if the operation was successful, otherwise some appropriate error code. If the user was asked whether to replace an existing file and chose not to, then userCanceledErr is returned. If the AU's state data could not be retrieved, then the error code will be whatever the AU itself or the OS returned. If there was an error writing the data to disk, then the error code will be whatever CFURL error was returned when attempting the operation. If there was a problem finding the standard location for the AU for saving the file, then an appropriate error code will be returned. If any necessary CoreFoundation objects could not be created, then coreFoundationUnknownErr is returned.


explanation

This is the function that you use when the user wants to export a preset file for an AU to disk. It always saves to the "user" file system domain.

You do not need to add the .aupreset file name extension to the preset name. It will be automatically appended for the output file name. But if the you did include the .aupreset extension, that will be noted and nothing extra will be appended.

If the file name is too long, it will be truncated (before appending the .aupreset extension, so the extension is always complete).

The caller is responsible for releasing inAUPresetNameString when done with it. SaveAUStateToPresetFile does not consume a reference to inAUPresetNameString, nor is it retained.

This function sets the value of the kAUPresetNameKey in the saved preset dictionary to name that the user enters as the file name. This is the expected behavior when saving AU preset files.

If you are interested in knowing to which file the AU state data was saved, you can provide a valid pointer to a CFURLRef for the outSavedAUPresetFileURL argument. If you are not interested, pass in NULL for outSavedAUPresetFileURL. But please note that it might be the case that no valid reference is available before this function returns. Therefore, if you do pass in a valid reference to a CFURLRef for the outSavedAUPresetFileURL argument, the you should first null the CFURLRef. If the function returns successfully, you still must check to see if your CFURLRef is still null in order to determine if the CFURL was able to be provided. If the CFURL is not null, then you own a reference to it and are responsible for releasing it when done with it.

Similarly, it may not be possible to get the CFURL of the saved file. Again, if the caller passes a reference to a CFURLRef for the outSavedAUPresetFileURL argument, the caller should first null the CFURLRef, since it might be the case that no valid reference is available before this function returns.

When you call this function, the resources for any dialog are searched for in the main application bundle. If you are hosting an Audio Unit but are not the running application (or you are the Audio Unit itself), you need to call the SaveAUStateToPresetFile_Bundle function instead so that you can specify the bundle where the resources can be found.



SaveAUStateToPresetFile_Bundle

OSStatus SaveAUStateToPresetFile_Bundle(AudioComponentInstance inAUComponentInstance, CFStringRef inAUPresetNameString, CFURLRef* outSavedAUPresetFileURL, Boolean inPromptToReplaceFile, CFBundleRef inBundle)

inAUComponentInstance
The Audio Unit instance whose state data (ClassInfo AU property) you want to write out to a preset file.

inAUPresetNameString
The name to use for the saved AU preset file.

outSavedAUPresetFileURL
Upon successful return, this may point to a CFURLRef that references the file to which the data was saved. Can be NULL.

inPromptToReplaceFile
Whether a dialog will be presented asking the user before replacing the existing file, if a file already exists at the save location.

inBundle
A reference to the bundle where the dialog resources can be found. If NULL, then the main application bundle is used.

result
A status code, the same as with SaveAUStateToPresetFile.


explanation

This is a variant of SaveAUStateToPresetFile that allows you to specify the bundle where the dialog resources are located. You should use this function if you are calling from some plugin or framework or anything other than the running application.


programming example

Here's an example of how an AU View might use this function to load a preset file for its associated Audio Unit:

CFBundleRef const pluginBundle = CFBundleGetBundleWithIdentifier(CFSTR("org.destroyfx.MegaEffect"));
if (pluginBundle != NULL)
{
	CFURLRef savedFileURL = NULL;
	OSStatus const status = SaveAUStateToPresetFile_Bundle(GetEditAudioUnit(), NULL, &savedFileURL, pluginBundle);
	if ((status == noErr) && (savedFileURL != NULL))
	{
		CFShow(savedFileURL);	/* print info about the saved file's URL to stderr */
		CFRelease(savedFileURL);
	}
}



CustomSaveAUPresetFile

OSStatus CustomSaveAUPresetFile(AudioComponentInstance inAUComponentInstance, CFURLRef inAUPresetFileURL, Boolean inPromptToReplaceFile)

inAUComponentInstance
The Audio Unit instance whose state data (ClassInfo AU property) you want to write out to a preset file.

inAUPresetFileURL
The file path where you wish to save the AU preset file.

inPromptToReplaceFile
Whether a dialog will be presented asking the user before replacing the existing file, if a file already exists at the save location.

result
A status code: noErr if the operation was successful, otherwise some appropriate error code. If the user was asked whether to replace an existing file and chose not to, then userCanceledErr is returned. If the AU's state data could not be retrieved, then the error code will be whatever the AU itself or the OS returned. If there was an error writing the data to disk, then the error code will be whatever CFURL error was returned when attempting the operation. If any necessary CoreFoundation objects could not be created, then coreFoundationUnknownErr is returned.


explanation

This function can save a preset file for an AU to a non-default location on disk.

You do not need to add the .aupreset file name extension to the preset name. It will be automatically appended for the output file name. But if the you did include the .aupreset extension, that will be noted and nothing extra will be appended.

If the file name is too long, it will be truncated (before appending the .aupreset extension, so the extension is always complete).

The caller is responsible for releasing inAUPresetNameString when done with it. CustomSaveAUPresetFile does not consume a reference to inAUPresetNameString, nor is it retained.

This function sets the value of the kAUPresetNameKey in the saved preset dictionary to name that the user enters as the file name. This is the expected behavior when saving AU preset files.

When you call this function, the resources for any dialog are searched for in the main application bundle. If you are hosting an Audio Unit but are not the running application (or you are the Audio Unit itself), you need to call the CustomSaveAUPresetFile_Bundle function instead so that you can specify the bundle where the resources can be found.



CustomSaveAUPresetFile_Bundle

OSStatus CustomSaveAUPresetFile_Bundle(AudioComponentInstance inAUComponentInstance, CFURLRef inAUPresetFileURL, Boolean inPromptToReplaceFile, CFBundleRef inBundle)

inAUComponentInstance
The Audio Unit instance whose state data (ClassInfo AU property) you want to write out to a preset file.

inAUPresetFileURL
The file path where you wish to save the AU preset file.

inPromptToReplaceFile
Whether a dialog will be presented asking the user before replacing the existing file, if a file already exists at the save location.

inBundle
A reference to the bundle where the dialog resources can be found. If NULL, then the main application bundle is used.

result
A status code, the same as with CustomSaveAUPresetFile.


explanation

This is a variant of CustomSaveAUPresetFile that allows you to specify the bundle where the dialog resources are located. You should use this function if you are calling from some plugin or framework or anything other than the running application.



CFTreeCreateFromAUPresetFilesInDomain

CFTreeRef CFTreeCreateFromAUPresetFilesInDomain(AudioComponent inAUComponent, DFXFileSystemDomain inFileSystemDomain)

inAUComponent
The AU Component whose preset files you want to find.

inFileSystemDomain
The file system domain constant for the domain that you want to search in for preset files.

result
A CFTree with the complete hierarchy of preset files and sub-directories for the given AU and domain. The root tree is for the root directory containing the preset files. If no preset files are found or if an error occurs, the result will be NULL. The info field of each tree node's CFTreeContext is a CFURLRef for the preset file or sub-directory.


explanation

This is the function that you use when you want to find out what AU preset files exist on the user's system. In order to find everything in the user's presets collection, you will want to do this for each possible file system domain: kDFXFileSystemDomain_User and kDFXFileSystemDomain_Local.

The caller owns a reference to the CFTree. You should CFRelease it when you are done. When you do that, all child trees and contained CFURLs will be released. The CFURLRef in each tree node is valid for the lifespan of the returned CFTree object. If you are going to release the tree, you should first retain any CFURLs that you still want to be able to access.

If you want an easy way to fetch the CFURL for a given tree node, you can use GetCFURLFromFileURLsTreeNode.

If any sub-directory contains no preset files, then it gets pruned from the tree before this function returns, so you don't need to check for empty branches.

If you want to know whether a given tree node is a reference to a preset file or a sub-directory of preset files, simply check to see if it has any children with CFTreeGetChildCount. If the return count value is greater than zero, then that means that the tree node references a sub-directory; otherwise, it references a file.

If you want to get a nicely displayable name string for a given preset file CFURL (like for the text in a menu entry, without its parent directory path prefixed and without the .aupreset file name extension), you can use CopyAUPresetNameFromCFURL. If you want to do the same thing for a sub-directory CFURL, simply use the CFURLCopyLastPathComponent function (which is part of the regular CoreFoundation API).

This function checks each file found to determine if it is an AU preset type file, based on its file name extension (.aupreset). It also examines the data in the file to determine whether it is valid AU state data for the specified AU (i.e. the identifying values must match the AU's AudioComponentDescription, which is determined by using ComponentAndDescriptionMatch_Loosely). Any files that don't pass either of these examinations are excluded from the tree. There should not ever be preset files for the wrong AU in the standard locations, but just in case there are, they will be excluded.


programming example

Here's an example recursive function that walks through the CFTree hierarchy, looks at each node and determines if it's a file or sub-directory, gets the CFURLRef for each node, and uses CFShow to print a description of the CFURL to stderr:

void ShowTree(CFTreeRef inTree)
{
	if (inTree == NULL)
	{
		fprintf(stderr, "tree is null!\n");
	}
	else
	{
		if (CFTreeGetChildCount(inTree) > 0)
		{
			fprintf(stderr, "this tree node represents a sub-directory of preset files\n");
		}
		else
		{
			fprintf(stderr, "this tree node represents a preset file\n");
		}
		CFURLRef const treeItemURL = GetCFURLFromFileURLsTreeNode(inTree);
		CFShow(treeItemURL);
		CFTreeRef const child = CFTreeGetFirstChild(inTree);
		if (child != NULL)
		{
			ShowTree(child);
		}
		CFTreeRef const next = CFTreeGetNextSibling(inTree);
		if (next != NULL)
		{
			ShowTree(next);
		}
	}
}

And here's an example of using the above function to find all of the AU preset files in each valid file system domain, for a given AU Component:

fprintf(stderr, "\n\tlooking in the user domain for preset files...\n");
CFTreeRef auPresetsTree = CFTreeCreateFromAUPresetFilesInDomain(someAUComponent, kDFXFileSystemDomain_User);
ShowTree(auPresetsTree);
if (auPresetsTree != NULL)
{
	CFRelease(auPresetsTree);
}

fprintf(stderr, "\n\tlooking in the local domain for preset files...\n");
auPresetsTree = CFTreeCreateFromAUPresetFilesInDomain(someAUComponent, kDFXFileSystemDomain_Local);
ShowTree(auPresetsTree);
if (auPresetsTree != NULL)
{
	CFRelease(auPresetsTree);
}



GetCFURLFromFileURLsTreeNode

CFURLRef GetCFURLFromFileURLsTreeNode(CFTreeRef inTree)

inTree
A CFTreeRef that holds a CFURLRef in the info field of its CFTreeContext.

result
The CFURLRef that is contained in the input tree, or NULL if something goes wrong.


explanation

You use this function when you want to access one of the files or sub-directories in the tree that is returned by CFTreeCreateFromAUPresetFilesInDomain. The file or sub-directory CFURL reference is contained in the info field of each tree node's CFTreeContext structure. Since there's no very quick and convenient way to access that tree data pointer, I wrote this function to make it easy.

This function simply returns the pointer value without retaining the CFURL. The tree owns a reference to the CFURL. If you need to keep a reference to the CFURL beyond the life of its owner tree, then you are responsible for retaining it and then releasing it when you are done.


programming example

See RestoreAUStateFromPresetFile for a usage example.



CopyAUPresetNameFromCFURL

CFStringRef CopyAUPresetNameFromCFURL(CFURLRef inAUPresetFileURL)

inAUPresetFileURL
A CFURLRef representing an AU preset file.

result
A CFStringRef that has the name of the file represented by the input file without the .aupreset file name extension. If anything goes wrong, the result is NULL. If the input CFURL does not represent a file with an extension, then I'm not sure what happens, whatever happens when you do that with CFURLCreateCopyDeletingPathExtension.


explanation

This is a convenience function for getting a more user-friendly display text of an AU preset file from a CFURL reference to the file. It removes the parent directory path and the .aupreset file name extension, returning only the name of the file without file name extension. You will probably want to use this function when generating a user interface to the preset files in the user's collection.

The caller owns a reference to the returned CFString and is responsible for releasing it when done.


programming example

Here's an example that iterates through every sibling in one level of a preset files tree and uses the readable name from CopyAUPresetNameFromCFURL to set the text of each item in a menu:

CFIndex const numChildren = CFTreeGetChildCount(parentTree);
SetControlMaximum(yourMenuControl, numChildren);
CFTreeRef presetFileTreeNode = CFTreeGetFirstChild(parentTree);
for (CFIndex i = 0; i < numChildren; i++)
{
	CFURLRef const theFile = GetCFURLFromFileURLsTreeNode(presetFileTreeNode);
	CFStringRef itemName = NULL;
	/* it's a sub-directory of preset files */
	if (CFTreeGetChildCount(presetFileTreeNode) > 0)
	{
		itemName = CFURLCopyLastPathComponent(theFile);
	}
	/* it's a preset file */
	else
	{
		itemName = CopyAUPresetNameFromCFURL(theFile);
	}
	if (itemName != NULL)
	{
		SetMenuItemTextWithCFString(yourMenu, i + 1, itemName);
		CFRelease(itemName);
	}
	presetFileTreeNode = CFTreeGetNextSibling(presetFileTreeNode);
}



RestoreAUStateFromPresetFile

OSStatus RestoreAUStateFromPresetFile(AudioComponentInstance inAUComponentInstance, CFURLRef inAUPresetFileURL)

inAUComponentInstance
The Audio Unit instance whose state data (ClassInfo AU property) you want to restore from a preset file on disk.

inAUPresetFileURL
A CFURLRef that references the AU preset file that contains the AU state data that you would like to restore.

result
A status code: noErr if the operation was successful, otherwise some appropriate error code. If the AU's state data could not be restored, then the error code will be whatever the AU itself or the OS returned. If there was an error reading the data from disk, then the error code will be whatever CFURL error was returned when attempting the operation. If the file's data was not valid XML data that could be translated into a CFPropertyList, then coreFoundationUnknownErr or whatever error code was encountered during that operation is returned.


explanation

This is the function that you would use to get the data from an AU preset file and set that as the current state of an Audio Unit. For example, you might call this after you have used CFTreeCreateFromAUPresetFilesInDomain to get a tree of the user's preset files collection, presented that collection to the user, and then the user indicated that she wanted to load one of those presets.

The caller is responsible for releasing inAUPresetFileURL when done with it. RestoreAUStateFromPresetFile does not consume a reference to inAUPresetFileURL, nor is it retained.


programming example

CFTreeRef const auPresetsTree = CFTreeCreateFromAUPresetFilesInDomain(AudioComponentInstanceGetComponent(someAUInstance), kDFXFileSystemDomain_User);
/* ...do something here to present the tree to the user... */
/* ...the user chooses a file... */
/* ...get the tree node corresponding to that file... */
CFURLRef const theFile = GetCFURLFromFileURLsTreeNode(presetTreeNode);
OSStatus const status = RestoreAUStateFromPresetFile(someAUInstance, theFile);



GetAUComponentDescriptionFromStateData

OSStatus GetAUComponentDescriptionFromStateData(CFPropertyListRef inAUStateData, AudioComponentDescription* outComponentDescription)

inAUStateData
A CFPropertyListRef containing an AU state data dictionary.

outComponentDescription
Upon successful return, this structure's type, sub-type, and manufacturer ID values will be set to those found in the AU preset file's data (and the other values in the structure will be set to zero).

result
A status code: noErr if the operation was successful, otherwise some appropriate error code. Possible error code is kAudioUnitErr_InvalidPropertyValue if the dictionary is not in the expected format or is missing any of the required keys, or if the dictionary's value for kAUPresetVersionKey is unrecognized.


explanation

AU state data is stored in the form of a CFDictionary. The dictionary is expected to contain values for the type, sub-type, and manufacturer IDs of the specific AU that created the data. This is in order to specify which AU is able read the data. This function collects those identifying values from an AU state data dictionary into an AudioComponentDescription, providing a simple way to identify the data's creator.

This function is used internally by GetAUComponentDescriptionFromPresetFile to process the data contained in an AU preset file. Since it may also be useful in other contexts, I made it one of the public functions of this library.



GetAUComponentDescriptionFromPresetFile

OSStatus GetAUComponentDescriptionFromPresetFile(CFURLRef inAUPresetFileURL, AudioComponentDescription* outComponentDescription)

inAUPresetFileURL
A CFURLRef representing an AU preset file.

outComponentDescription
Upon successful return, this structure's type, sub-type, and manufacturer ID values will be set to those found in the AU preset file's data (and the other values in the structure will be set to zero).

result
A status code: noErr if the operation was successful, otherwise some appropriate error code. Possible error codes are those returned by CreatePropertyListFromXMLFile or GetAUComponentDescriptionFromStateData.


explanation

This is a variant of GetAUComponentDescriptionFromStateData that allows you to start with a file reference to an AU preset file and then retrieve the identifying values of the contained AU state data's creator. It is a convenience function that takes care of reading the XML data from the AU preset file, and then from there does the same thing as GetAUComponentDescriptionFromStateData.

This function is used internally by CFTreeCreateFromAUPresetFilesInDomain when it validates an AU preset file's data to see if its identifying values match those of the specified AU. Since it may also be useful in other contexts, I made it one of the public functions of this library.



FindPresetsDirForAU

CFURLRef FindPresetsDirForAU(AudioComponent inAUComponent, DFXFileSystemDomain inFileSystemDomain, Boolean inCreateDir)

inAUComponent
The AU Component whose preset files location you seek.

inFileSystemDomain
The file system domain constant for the domain that you want to search in for preset files.

inCreateDir
Whether any path directory components should be created if not already present.

result
A directory URL: a CFURL of the correct directory for preset files for the particular AU, or null if any error occurred.


explanation

Based upon the specific AU component provided and the requested file system domain, this function will return the correct location for preset files for that AU within the given domain. If that directory path does not already exist, requesting to create any directories necessary to complete the path will attempt to do that. Without specifying creation, it is possible that the returned path does not actually fully exist.



CFURLIsAUPreset

Boolean CFURLIsAUPreset(CFURLRef inURL)

inURL
Any CFURLRef.

result
A boolean answer: TRUE if the CFURL is an AU preset file, otherwise FALSE.


explanation

This function determines whether the CFURL represents a file and has the filename extension expected of AU preset files.






CFArray callbacks for Factory Presets arrays


Why this matters

CFArrays use a set of callbacks that define what happens to their contained data items when you retain the array, release the array, ask to compare two items in the array, or ask for a description of an item in the array. When the data items that an array holds are all CF types, there are standard CF ways to handle all of these situations and a convenient pre-made global CFArray callbacks structure that gives you that behavior (kCFTypeArrayCallBacks). In the AU API, though, you need to make CFArrays of non-CF type data when you support the kAudioUnitProperty_FactoryPresets property. So for those arrays, you need to define custom callbacks that will properly handle pointers to AUPreset structures as their data items. The lifespan of the array may be greater than the lifespan of the plugin and any of its AUPreset data, so you must ensure proper memory management of the AUPreset data items when the CFArray is retained and released. That is what my callbacks do, which can be used via the kCFAUPresetArrayCallBacks constant.

Using these callbacks also requires the use of an object that can encompass an AUPreset structure but also hold the necessary extra data to mimic CFType behavior. That is the purpose of the CFAUPresetRef type. It is an opaque type and so there are functions for handling it correctly. You must use the Factory Preset array callbacks in this library only with this data type.


Types

CFAUPresetRef


Functions

CFAUPresetCreate
CFAUPresetRetain
CFAUPresetRelease


Constants

kCFAUPresetArrayCallBacks



CFAUPresetRef

typedef struct CFAUPreset const* CFAUPresetRef


explanation

CFAUPreset is an opaque type that contains an AUPreset structure. It is a wrapper-type object designed to be able to mimic CoreFoundation object behavior for the AUPreset structure.

Please note that CFAUPreset is not a CFType, and therefore CFAUPresetRef is not compatible with any common CoreFoundation functions (like CFRetain, CFRelease, etc.). The functions contained in this library are the only ones that you can use with a CFAUPresetRef.

A CFAUPresetRef can be cast to AUPreset*, but the same is not true for the reverse.



CFAUPresetCreate

CFAUPresetRef CFAUPresetCreate(CFAllocatorRef inAllocator, SInt32 inPresetNumber, CFStringRef inPresetName)

inAllocator
The allocator to use to allocate the memory via CFAllocatorAllocate.

inPresetNumber
The value that you want set as the presetNumber value in the AUPreset.

inPresetName
The string that you want used as the presetName value in the AUPreset.

result
A reference to a newly allocated CFAUPreset object containing an AUPreset with its fields set to the values as passed into this function.


explanation

This function will allocate an instance of a CFAUPreset object and initialize the value fields of its contained AUPreset structure according to the corresponding arguments of the function.

The caller owns a reference to the returned CFAUPresetRef and is responsible for releasing it when done.


programming example

Here's an example implementation of an AUBase-derived AU's GetPresets() method:

OSStatus MegaEffect::GetPresets(CFArrayRef* outData) const
{
	// simply a query to see if the property is supported
	if (!outData)
	{
		return noErr;
	}

	CFMutableArrayRef const presetsArray = CFArrayCreateMutable(kCFAllocatorDefault, 2, &kCFAUPresetArrayCallBacks);
	if (!presetsArray)
	{
		*outData = nullptr;
		return coreFoundationUnknownErr;
	}

	if (CFAUPresetRef const preset = CFAUPresetCreate(kCFAllocatorDefault, 1, CFSTR("a preset")))
	{
		CFArrayAppendValue(presetsArray, preset);
		CFAUPresetRelease(preset);
	}

	if (CFAUPresetRef const preset = CFAUPresetCreate(kCFAllocatorDefault, 2, CFSTR("another preset")))
	{
		CFArrayAppendValue(presetsArray, preset);
		CFAUPresetRelease(preset);
	}

	*outData = reinterpret_cast<CFArrayRef>(presetsArray);
	return noErr;
}



CFAUPresetRetain

CFAUPresetRef CFAUPresetRetain(CFAUPresetRef inPreset)

inPreset
A reference to a CFAUPreset object to be retained.

result
A reference to the CFAUPreset object that was retained.


explanation

This function mimics the behavior of CFRetain, but specifically for the CFAUPresetRef type and not any CFTypeRef.



CFAUPresetRelease

void CFAUPresetRelease(CFAUPresetRef inPreset)

inPreset
A reference to a CFAUPreset object to be released.


explanation

This function mimics the behavior of CFRelease, but specifically for the CFAUPresetRef type and not any CFTypeRef.


programming example

See CFAUPresetCreate for a relevant example.



kCFAUPresetArrayCallBacks

CFArrayCallBacks const kCFAUPresetArrayCallBacks


explanation

This CFArrayCallbacks structure is initialized (when your code is loaded) to contain pointers to callbacks that will correctly handle retaining, releasing, comparing, and describing CFArrays that contain CFAUPresetRef instances as their data items. You can pass this for the CFArrayCallbacks* argument when creating arrays with CFArrayCreate or CFArrayCreateMutable.


programming example

See CFAUPresetCreate for a relevant example.






CFArray callbacks for AU Migrate arrays


Why this matters

CFArrays use a set of callbacks that define what happens to their contained data items when you retain the array, release the array, ask to compare two items in the array, or ask for a description of an item in the array. When the data items that an array holds are all CF types, there are standard CF ways to handle all of these situations and a convenient pre-made global CFArray callbacks structure that gives you that behavior (kCFTypeArrayCallBacks). In the AU API, though, you need to make CFArrays of non-CF type data when you support the kAudioUnitMigrateProperty_FromPlugin property. So for those arrays, you need to define custom callbacks that will properly handle pointers to AudioUnitOtherPluginDesc structures as their data items. The lifespan of the array may be greater than the lifespan of the plugin and any of its AudioUnitOtherPluginDesc data, so you must ensure proper memory management of the AudioUnitOtherPluginDesc data items when the CFArray is retained and released. That is what my callbacks do, which can be used via the kCFAUOtherPluginDescArrayCallBacks constant.

Using these callbacks also requires the use of an object that can encompass an AudioUnitOtherPluginDesc structure but also hold the necessary extra data to mimic CFType behavior. That is the purpose of the CFAUOtherPluginDescRef type. It is an opaque type and so there are functions for handling it correctly. You must use the AU Migrate array callbacks in this library only with this data type.


Types

CFAUOtherPluginDescRef


Functions

CFAUOtherPluginDescCreate
CFAUOtherPluginDescCreateVST
CFAUOtherPluginDescCreateMAS
CFAUOtherPluginDescRetain
CFAUOtherPluginDescRelease


Constants

kCFAUOtherPluginDescArrayCallBacks



CFAUOtherPluginDescRef

typedef struct CFAUOtherPluginDesc const* CFAUOtherPluginDescRef


explanation

CFAUOtherPluginDesc is an opaque type that contains an AudioUnitOtherPluginDesc structure. It is a wrapper-type object designed to be able to mimic CoreFoundation object behavior for the AudioUnitOtherPluginDesc structure.

Please note that CFAUOtherPluginDesc is not a CFType, and therefore CFAUOtherPluginDescRef is not compatible with any common CoreFoundation functions (like CFRetain, CFRelease, etc.). The functions contained in this library are the only ones that you can use with a CFAUOtherPluginDescRef.

A CFAUOtherPluginDescRef can be cast to AudioUnitOtherPluginDesc*, but the same is not true for the reverse.



CFAUOtherPluginDescCreate

CFAUOtherPluginDescRef CFAUOtherPluginDescCreate(CFAllocatorRef inAllocator, UInt32 inFormat, OSType inTypeID, OSType inSubTypeID, OSType inManufacturerID)

inAllocator
The allocator to use to allocate the memory via CFAllocatorAllocate.

inFormat
The value that you want set as the format value in the AudioUnitOtherPluginDesc.

inTypeID
The value that you want set as the mType value in the AudioUnitOtherPluginDesc.

inSubTypeID
The value that you want set as the mSubType value in the AudioUnitOtherPluginDesc.

inManufacturerID
The value that you want set as the mManufacturer value in the AudioUnitOtherPluginDesc.

result
A reference to a newly allocated CFAUOtherPluginDesc object containing an AudioUnitOtherPluginDesc with its fields set to the values as passed into this function.


explanation

This function will allocate an instance of a CFAUOtherPluginDesc object and initialize the value fields of its contained AudioUnitOtherPluginDesc structure according to the corresponding arguments of the function.

The caller owns a reference to the returned CFAUOtherPluginDescRef and is responsible for releasing it when done.


programming example

See CFAUOtherPluginDescCreateVST for a closely related example.



CFAUOtherPluginDescCreateVST

CFAUOtherPluginDescRef CFAUOtherPluginDescCreateVST(CFAllocatorRef inAllocator, OSType inUniqueID)

inAllocator
The allocator to use to allocate the memory via CFAllocatorAllocate.

inUniqueID
The compatible VST plugin's "unique ID" value.

result
A reference to a newly allocated CFAUOtherPluginDesc object containing an AudioUnitOtherPluginDesc with its fields set correctly for specifying a VST plugin with the identifying value as passed into this function.


explanation

This function will allocate an instance of a CFAUOtherPluginDesc object and initialize the value fields of its contained AudioUnitOtherPluginDesc structure according to specifying a VST plugin the corresponding argument of the function.

The caller owns a reference to the returned CFAUOtherPluginDescRef and is responsible for releasing it when done.

This function is a convenience wrapper for CFAUOtherPluginDescCreate.


programming example

Here's an example implementation the GetProperty method, specifically overridden for the kAudioUnitMigrateProperty_FromPlugin property, for an AUEffectBase-derived AU that has VST and MAS counterpart versions with importable settings:

OSStatus MegaEffect::GetProperty(AudioUnitPropertyID inPropertyID, 
					AudioUnitScope inScope, AudioUnitElement inElement, 
					void* outData)
{
	if (inPropertyID == kAudioUnitMigrateProperty_FromPlugin)
	{
		CFMutableArrayRef const descsArray = CFArrayCreateMutable(kCFAllocatorDefault, 2, &kCFAUOtherPluginDescArrayCallBacks);
		if (!descsArray)
		{
			return coreFoundationUnknownErr;
		}

		if (CFAUOtherPluginDescRef const otherPluginMigrateDesc = CFAUOtherPluginDescCreateVST(kCFAllocatorDefault, 'Plug'))
		{
			CFArrayAppendValue(descsArray, otherPluginMigrateDesc);
			CFAUOtherPluginDescRelease(otherPluginMigrateDesc);
		}

		if (CFAUOtherPluginDescRef const otherPluginMigrateDesc = CFAUOtherPluginDescCreateMAS(kCFAllocatorDefault, 'Plug', 3, 'Acme'))
		{
			CFArrayAppendValue(descsArray, otherPluginMigrateDesc);
			CFAUOtherPluginDescRelease(otherPluginMigrateDesc);
		}

		std::memcpy(outData, &descsArray, sizeof(descsArray));
		return noErr;
	}

	return AUEffectBase::GetProperty(inPropertyID, inScope, inElement, outData);
}



CFAUOtherPluginDescCreateMAS

CFAUOtherPluginDescRef CFAUOtherPluginDescCreateMAS(CFAllocatorRef inAllocator, OSType inEffectID, OSType inVariantID, OSType inManufacturerID)

inAllocator
The allocator to use to allocate the memory via CFAllocatorAllocate.

inEffectID
The compatible MAS plugin's masEffectID value.

inVariantID
The compatible MAS plugin's masVariantID value.

inManufacturerID
The compatible MAS plugin's masManufacturer value.

result
A reference to a newly allocated CFAUOtherPluginDesc object containing an AudioUnitOtherPluginDesc with its fields set correctly for specifying a MAS plugin with the identifying values as passed into this function.


explanation

This function will allocate an instance of a CFAUOtherPluginDesc object and initialize the value fields of its contained AudioUnitOtherPluginDesc structure according to specifying a MAS plugin the corresponding arguments of the function.

The caller owns a reference to the returned CFAUOtherPluginDescRef and is responsible for releasing it when done.

This function is a convenience wrapper for CFAUOtherPluginDescCreate.


programming example

See CFAUOtherPluginDescCreateVST for a relevant example.



CFAUOtherPluginDescRetain

CFAUOtherPluginDescRef CFAUOtherPluginDescRetain(CFAUOtherPluginDescRef inDesc)

inPreset
A reference to a CFAUOtherPluginDesc object to be retained.

result
A reference to the CFAUOtherPluginDesc object that was retained.


explanation

This function mimics the behavior of CFRetain, but specifically for the CFAUOtherPluginDescRef type and not any CFTypeRef.



CFAUOtherPluginDescRelease

void CFAUOtherPluginDescRelease(CFAUOtherPluginDescRef inDesc)

inPreset
A reference to a CFAUOtherPluginDesc object to be released.


explanation

This function mimics the behavior of CFRelease, but specifically for the CFAUOtherPluginDescRef type and not any CFTypeRef.


programming example

See CFAUOtherPluginDescCreateVST for a relevant example.



kCFAUOtherPluginDescArrayCallBacks

CFArrayCallBacks const kCFAUOtherPluginDescArrayCallBacks


explanation

This CFArrayCallbacks structure is initialized (when your code is loaded) to contain pointers to callbacks that will correctly handle retaining, releasing, comparing, and describing CFArrays that contain CFAUOtherPluginDescRef instances as their data items. You can pass this for the CFArrayCallbacks* argument when creating arrays with CFArrayCreate or CFArrayCreateMutable.


programming example

See CFAUOtherPluginDescCreateVST for a relevant example.






Getting an AU's plugin name and manufacturer name


Why this matters

Audio Unit Component names are formatted in this certain way: "Manufacturer Name: Plugin Name". Very often, AU hosts want to parse out just the plugin name or just the manufacturer name to display to the user. So that is what these functions do. It's a common thing to need to do, and a pretty simple thing, but still I see no reason why every host author should need to write their own parsing code redundantly, so I'm sharing mine. Plus I needed to write this code for the AU preset file handling stuff anyway.

Please note that the "manufacturer: plugin" name format is only an Audio Unit thing, so these functions are not useful for other types of Components.


Functions

CopyAUNameAndManufacturerStrings



CopyAUNameAndManufacturerStrings

OSStatus CopyAUNameAndManufacturerStrings(AudioComponent inAUComponent, CFStringRef* outNameString, CFStringRef* outManufacturerString)

inAUComponent
The AU Component whose plugin name and/or manufacturer name you want.

outNameString
Upon successful return, the CFStringRef will reference a CFString representation of the Audio Unit's specific plugin name. Can be NULL.

outManufacturerString
Upon successful return, the CFStringRef will reference a CFString representation of the Audio Unit's manufacturer name. Can be NULL.

result
A status code: noErr if the operation was successful, otherwise some appropriate error code. If there was any problem creating the CFStrings, coreFoundationUnknownErr is returned. If getting the AudioComponent name info fails, the error code is the one returned by AudioComponentCopyName. If the AudioComponent name string is not in the proper AU colon-delimited format, internalComponentErr is returned.


explanation

If you only want the plugin name or only want the manufacturer name, you can send a valid pointer to a CFStringRef for the one that you want and NULL for the other one. You cannot, however, send NULL for both. Doing that will result in a paramErr return value.

The caller owns a reference to each output CFString and is responsible for releasing them when done.


programming example

Here's an example of getting the name strings for an AU and outputting them to stderr:

CFStringRef nameString = NULL, manufacturerString = NULL;
OSStatus const error = CopyAUNameAndManufacturerStrings(someAUComponent, &nameString, &manufacturerString);
if (error == noErr)
{
	fprintf(stderr, "the name of this plugin is:  ");
	CFShow(nameString);

	fprintf(stderr, "the name of this plugin's manufacturer is:  ");
	CFShow(manufacturerString);

	CFRelease(nameString);
	CFRelease(manufacturerString);
}






Comparing ComponentDescriptions


Why this matters

An AudioComponentDescription is a struct that contains values that together uniquely identify an AudioComponent (an Audio Unit is a type of AudioComponent). It is what you use to programatically describe a specific AudioComponent. The struct contains five values, but only three of them are actually used for the purposes of uniquely identifying the AU. Occasionally it is useful to compare ComponentDescriptions to see if they match, so these are just some convenience functions that make that simple, comparing on the basis of the three identifying values: the type, sub-type, and manufacturer IDs. For example, CFTreeCreateFromAUPresetFilesInDomain uses these functions to compare the AudioComponentDescription that GetAUComponentDescriptionFromPresetFile supplies to that of a particular AudioComponent.

These functions also come in "loosely" varieties which match only the sub-type and manufacturer codes (ignoring the type code). The reason for this is that Apple changed the expectations of settings data compatibility in August 2005 (with the release of the CoreAudio SDK version 1.4.2) to allow for compatibility between settings that don't match the type code of a given AU. This allows for you to make different varieties of AUs that are essentially the same plugin in most senses, but different in their type categories (e.g. you may have an offline version and a realtime effect version of some effect). These would share the same sub-type and manufacturer codes and would be expected to be able to read each other's settings data interchangeably.


Functions

ComponentDescriptionsMatch
ComponentDescriptionsMatch_Loosely
ComponentAndDescriptionMatch
ComponentAndDescriptionMatch_Loosely



ComponentDescriptionsMatch

Boolean ComponentDescriptionsMatch(AudioComponentDescription const* inComponentDescription1, AudioComponentDescription const* inComponentDescription2)

inComponentDescription1
A pointer to one of the ComponentDescriptions that you want to compare.

inComponentDescription2
A pointer to the AudioComponentDescription that you want to compare to inComponentDescription1.

result
The result is TRUE if both ComponentDescriptions match, and FALSE otherwise.


explanation

This function compares two ComponentDescriptions to see if they match based on their identifying values.



ComponentDescriptionsMatch_Loosely

Boolean ComponentDescriptionsMatch_Loosely(AudioComponentDescription const* inComponentDescription1, AudioComponentDescription const* inComponentDescription2)

inComponentDescription1
A pointer to one of the ComponentDescriptions that you want to compare.

inComponentDescription2
A pointer to the AudioComponentDescription that you want to compare to inComponentDescription1.

result
The result is TRUE if the sub-type and manufacturer codes of both ComponentDescriptions match, and FALSE otherwise.


explanation

This function is the same as ComponentDescriptionsMatch except that it ignores the type codes when comparing.



ComponentAndDescriptionMatch

Boolean ComponentAndDescriptionMatch(AudioComponent inComponent, AudioComponentDescription const* inComponentDescription))

inComponent
An AudioComponent whose AudioComponentDescription you want to compare to another AudioComponentDescription.

inComponentDescription
The AudioComponentDescription that you want to compare to inComponent's AudioComponentDescription.

result
The result is TRUE if inComponent's AudioComponentDescription matches inComponentDescription, and FALSE otherwise.


explanation

If you are starting with an AudioComponent that you want to compare to some AudioComponentDescription, then this function will do the work of querying the AudioComponent's AudioComponentDescription and then comparing those two ComponentDescriptions to see if they match. If querying the AudioComponentDescription of inComponent's fails, then the function will return FALSE.

This function is a convenience wrapper for ComponentDescriptionsMatch.



ComponentAndDescriptionMatch_Loosely

Boolean ComponentAndDescriptionMatch_Loosely(AudioComponent inComponent, AudioComponentDescription const* inComponentDescription)

inComponent
An AudioComponent whose AudioComponentDescription you want to compare to another AudioComponentDescription.

inComponentDescription
The AudioComponentDescription that you want to compare to inComponent's AudioComponentDescription.

result
The result is TRUE if inComponent's sub-type and manufacturer codes match inComponentDescription's, and FALSE otherwise.


explanation

This function is the same as ComponentAndDescriptionMatch except that it ignores the type codes when comparing.






Posting parameter change notifications


Why this matters

Sometimes a change occurs to an Audio Unit's parameter of which other interested parties might not be aware. For example, an AU might do something in a certain context that changes a parameter value internally (like mapping a MIDI CC event to a parameter change), and maybe the GUI or some other user interface might want to be notified so that it can update itself to reflect that change. These functions provide easy ways to do that. AU has a nice system for sending parameter change notifications to any arbitrary number of parameter listeners, and these functions are just a wrappers for that part of the AU API. I found myself writing the same little six line function over and over again in every AU that I developed, so I figured I would just make one reusable function for them all. So these functions are just for convenience, they don't provide any extra functionality.


Functions

AUParameterChange_TellListeners_ScopeElement
AUParameterChange_TellListeners



AUParameterChange_TellListeners_ScopeElement

void AUParameterChange_TellListeners_ScopeElement(AudioComponentInstance inAUComponentInstance, AudioUnitParameterID inParameterID, AudioUnitScope inScope, AudioUnitElement inElement)

inAUComponentInstance
The Audio Unit instance whose parameter value has changed.

inParameterID
The ID of the changed parameter.

inScope
The scope of the changed parameter.

inElement
The element of the changed parameter.


explanation

This function is a convenience wrapper of AUParameterListenerNotify, which is part of the regular Audio Unit API. When you use AUParameterListenerNotify, you need to have an AudioUnitParameter structure, initialize all of its values, and then call AUParameterListenerNotify. This function just lets you do that in one line rather than six.


programming example

See AUParameterChange_TellListeners for a relevant example.



AUParameterChange_TellListeners

void AUParameterChange_TellListeners(AudioComponentInstance inAUComponentInstance, AudioUnitParameterID inParameterID)

inAUComponentInstance
The Audio Unit instance whose parameter value has changed.

inParameterID
The ID of the changed parameter.


explanation

This function is a convenience wrapper of a convenience wrapper. It simply calls AUParameterChange_TellListeners_ScopeElement passing kAudioUnitScope_Global for the scope value and 0 for the element value. Since I find that, most of the time, my AUs' parameters are in the global scope and element 0, I rarely ever need specify other scopes or elements, so I made this simplified function.


programming example

Here's an example of sending change notifications for every parameter right after a factory preset has been loaded:

OSStatus MegaEffect::NewFactoryPresetSet(AUPreset const& inNewFactoryPreset)
{
	// ...do something to load the factory preset settings...

	for (UInt32 i = 0; i < yourNumParameters; i++)
	{
		AUParameterChange_TellListeners(GetComponentInstance(), yourParameterList[i]);
	}

	return noErr;
}






Change log

November 29th 2020:

May 8th 2008:

August 18th 2006:

September 9th 2005:

August 12th 2005:

March 6th 2005:

June 14th 2004:

September 23rd 2003:

September 18th 2003: