Using the Component Manager with Audio Units

An Audio Unit is packaged as a component. Extensive documentation and example code exists for the Component Manager, but a brief introduction is provided here to provide some orientation.

There are several Component Manager calls that the user of an Audio Unit will need to understand. These calls revolve around the simple operations of finding which Audio Units are registered, and opening and closing components.

The Component Manager API is exported by CoreServices.framework and is found in the Components.h header file.


Contents
 Finding Audio Units
 Opening Audio Unit Components
Defined Types
 AudioUnit

Finding Audio Units

Components are essentially pieces of dynamically loaded code. They are installed in the system in several known locations, which are then searched when a user logs in. When this search occurs, any components that are found are registered by the system. There is a background process called coreservicesd that is responsible for this task. Once a Component is registered, it can then be found by any application on the system, opened and used.

Apple's Audio Units are distributed in a CFBundle called CoreAudio.component, which is installed in

/System/Library/Components
Two directories are created specifically for use by third party developers as locations to install their Audio Units. The first directory,
/Library/Audio/Plug-Ins/Components/
represents a directory that the Component Manager will register components for any user that logs in to that machine. The second directory
~/Library/Audio/Plug-Ins/Components/
is a directory that can be created for each user. The ~ symbol is used to indicate the current user's home directory. Components found here are only registered when that particular user logs in.

Using the RegisterComponents calls, an application can also register components installed in any location for use by that application only.

Components are described in this structure:

    struct ComponentDescription {
        OSType              componentType;
        OSType              componentSubType;
        OSType              componentManufacturer;
        unsigned long       componentFlags;
        unsigned long       componentFlagsMask;
    };
A component will define all three of the OSType fields with a non-zero value. For instance:
    desc.componentType = 'aufx';
    desc.componentSubType = 'dely';
    desc.componentManufacturer = 'appl';
is the component identifier for Apple's Delay Audio Unit. These three fields together must define a system-wide, unique key . If two or more components are found with the same identifiers, then the Component with the higher version number (in the Component's resource) will be the one that is registered. Apple keeps a registry of component ID's that third parties should register for a unique manufacturer ID. Apple also reserves usage of these fields where they are defined by all lower case characters.

For instance, the following could be defined by a 3rd party:

    desc.componentType = 'aufx';
    desc.componentSubType = 'dely';
    desc.componentManufacturer = 'ACME';
Here, ACME Inc. has defined an Audio Unit that is an effect unit, and more specifically it is also a delay unit, that could, at least according to ACME's intention, be seen as an equivalent, and therefore substitutable, version of Apple's delay unit.
    desc.componentType = 'aufx';
    desc.componentSubType = 'Dely';
    desc.componentManufacturer = 'ACME';
With the usage of the 'Dely' subtype, this equivalence is no longer asserted, but the Audio Unit is still of course considered to be an effect unit.

Given these specifications, components can be found using known values for these fields of the ComponentDescription. A value of zero for any field will tell the Component Manager to match any value for that field.

Firstly, an application can count the number of components that are currently registered with the system. Thus, to find out how many Audio Unit Effect components are currently available:

    ComponentDescription desc;
    desc.componentType = kAudioUnitType_Effect; //'aufx'
    desc.componentSubType = 0;
    desc.componentManufacturer = 0;
    desc.componentFlags = 0;
    desc.componentFlagsMask = 0;
    
    printf ("Found %ld Effect Audio Units\n", CountComponents(&desc));
Thus, to find out how many Audio Unit Effect that are provided by Apple that are currently available:
    ComponentDescription desc;
    desc.componentType = kAudioUnitType_Effect; //'aufx'
    desc.componentSubType = 0;
    desc.componentManufacturer = kAudioUnitManufacturer_Apple; //'appl'
    desc.componentFlags = 0;
    desc.componentFlagsMask = 0;
    
    printf ("Found %ld Apple Effect Units\", CountComponents(&desc));

To find out what components are currently available on the system, the FindNextComponent call is used. FindNextComponent starts with a Component from which to start looking, and NULL can be specified to indicate to start with the first one found. The same usage of the zero wild card is supported in this call.

Thus, to find all of the Audio Unit effect components currently available:

    ComponentDescription desc;
    desc.componentType = kAudioUnitType_Effect; //'aufx'
    desc.componentSubType = 0;
    desc.componentManufacturer = 0;
    desc.componentFlags = 0;
    desc.componentFlagsMask = 0;
    
    Component theAUComponent = FindNextComponent (NULL, &desc);
    while (theAUComponent != NULL)
    {
            // now we need to get the information on the found component
            // swap this to big-endian for display purposes
        ComponentDescription found;
        GetComponentInfo (theAUComponent, &found, 0, 0, 0);

		OSType bigEndianValue = CFSwapInt32HostToBig(found.componentType);
		printf ("%4.4s - ", (char*)&bigEndianValue);
		bigEndianValue = CFSwapInt32HostToBig(found.componentSubType);
		printf ("%4.4s - ", (char*)&bigEndianValue);
		bigEndianValue = CFSwapInt32HostToBig(found.componentManufacturer);
		printf ("%4.4s", (char*)&bigEndianValue);		
            
        theAUComponent = FindNextComponent (theAUComponent, &desc);
    }

By providing the previously found AUComponent to FindNextComponent, the above code will find the next component that is registered after the one that is supplied to this call. Eventually, of course, the code will find no more components, and the return value of FindNextComponent will be NULL.

Opening Audio Unit Components

A Component is used to allocate instances. The Component also provides an API that can be used with that instance of the Component. Typically, the ComponentInstance is typedef'd to an identifier. It is the instances of course that are of most interest to us, as this is where the code is that will do something.

AudioUnit

typedef ComponentInstance AudioUnit;
An AudioUnit is defined to be of type ComponentInstance.

The API that can be used with an Audio Unit is chiefly declared in AudioUnit/AUComponent.h.

Creating a ComponentInstance is done through a call to the Component to open it. The act of opening a Component creates an instance of the component in question, and as many instances of a component as needed can be created by a program. When a program is finished with a particular instance of a component, then it should close it. This will destroy the component instance, and the instance is required to free any of the resources that it has allocated for its own usage.

There are several calls that can be used to create an instance of a component. We will look at one of these.

    OSErr OpenAComponent(Component aComponent, ComponentInstance *ci);
Thus, to open Apple's delay Audio Unit:
    ComponentDescription desc;
    desc.componentType = kAudioUnitType_Effect; //'aufx'
    desc.componentSubType = kAudioUnitSubType_Delay;
    desc.componentManufacturer = kAudioUnitManufacturer_Apple; //'appl'
    desc.componentFlags = 0;
    desc.componentFlagsMask = 0;
    
    Component auComp = FindNextComponent (NULL, &desc);
    if (auComp == NULL) {
        fprintf (stderr,  "didn't find the component\n");
        return;
    }
    
    AudioUnit delayUnit;
    OSErr result = OpenAComponent (auComp, &delayUnit);
    if (result) {
        fprintf (stderr, "Error:%d opening AU\n", result);
        return;
    }
    // OK Now we have the AudioUnit
    // Do something with it
    
    // When we're finished with it
    CloseComponent (delayUnit);

A ComponentInstance (in this case an AudioUnit) can also be used in any of the places that a Component is used. Thus, after we have the Audio Unit above, we could use it like this:

    desc.componentManufacturer = 0;
    
    Component theAUComponent = FindNextComponent ((Component)delayUnit, &desc);

This would return a non-null value if there are any other components around that have the same type and subType. It represents a way to find if there are any other components of that kind available.

The following call can tell you how many particular instances of a component are open at any particular time:

    extern long CountComponentInstances(Component aComponent);

Finally, as components are dynamically loaded, they also provide a means to extend their functionality over time. This is done through declaring a new API that Components of a particular type should implement and there are ways to query a Component to see if it implements a particular Component selector.

The Component API is implemented through an association of a reserved integer value with a particular Component API call. This is called a Component Selector, and the selectors that are reserved for usage with Audio Units by Apple are described in the AUComponent.h and AUNTComponent.h header files.

In the cases where a Component does not implement all of the interfaces that are specified by the designer of the componentType, a call to an unimplemented Component API will (not crash!) return a badComponentSelector result code. Similarly, if a call is consequently made on a ComponentInstance that has previously been closed, the badComponentInstance result code will be returned.