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 |
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/ComponentsTwo 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
.
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.
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.