Retrieving Component names on OS X
Since examples how to search through the list of components on a system are vastly available on the Internet already, I thought it'd be a good idea to create a tutorial discussing the exact same thing. Actually, since I'm writing tutorials about the OS X audio and MIDI system, I thought I couldn't really omit a simple tutorial about components.
Introduction
There's a great diversity of components on an OS X machine managing various kinds of functions, like picture processing, animation processing and audio processing. Although my tutorials focus on the last kind, this tutorial does not confine its usage to this group.
In this tutorial I'll try to explain how to retrieve a list of the names of components on a given system, and how to use this name to retrieve a single ComponentDescription of the component by its name. I'll use ObjC array types to ease integration with Cocoa applications. Again, this is a newbie tutorial although I assume you know a bit about ObjC programming. We're not using a gui in order not to clutter the code.
Coding
Right! We'll start with the required headers. The audio headers are not required in order to use components, but we're going to select only music device components, so we need the other headers in this example.
#import <Foundation/Foundation.h>
#import <CoreAudio/CoreAudio.h>
#import <AudioToolbox/AudioToolbox.h>
In our best bottom-up traditions, we're first going to define the functions we need and here's the first one, to retrieve a list of components by given criteria. The ComponentDescription argument is supposed to be a partly filled out variable, defining the criteria a component has to apply to:
NSMutableArray *FindComponentsWithType(ComponentDescription *fcd)
{
The function returns an NSMutableArray, and the search also requires some additional variables:
NSMutableArray *res;
ComponentDescription ccd;
Component cmp = NULL;
Handle pName;
char *cName;
int len;
I'll explain the use of these variables below. First of all, let's create the NSMutableArray we're going to return:
res = [NSMutableArray arrayWithCapacity: 10];
Simple enough. The function we'll use to get the name of a component returns the name in a relocatable memory block instead of a C string. Unlike C strings, such a block uses a Pascal like setup, putting the size of the memory block within the memory block handle. We'll have to create a handle now:
pName = NewHandle(0);
The initial size is zero bytes. That's ok, the handle will dynamically resize.
The next step will be looping through the list of applicable components. Apple provides us with a happy'n'dandy function, FindNextComponent. It takes the last retrieved component (or NULL to get the first one) and the partly filled out ComponentDescription, returning the next component matching the description.
If the ComponentDescription is completely blank, FindNextComponent will eventually return every component on the system.
while((cmp = FindNextComponent(cmp, fcd)) != 0) {
Ok! FindNextComponent returned a matching component in cmp. Now we're going to get the name in our pName handle:
GetComponentInfo(cmp, &ccd, pName, NULL, NULL);
GetComponentInfo can do more than just return the name of a component. The two NULLs, if replaced with variable names, can be filled with the component's information and icon. But who would want that?! We're interested in the name! Well we got it alright. However, the name is in this handle thing. Now we're going to transform the handle into a C string, which we can handle. In C.
A memory handle can fly about in memory, so first we have to tell it to stay put:
HLock(pName);
Now that pName is locked and not cruising through heap memory, let the C transformation begin!
cName = *pName;
len = *cName++;
cName[len] = 0;
cName first points to the start of the handle. The first byte in the handle is the size of the handle memory, which len holds as of the second line. cName however now points to the second byte of the handle; the first byte of the handle content. To completely C-stringify the handle, we terminate the memory block with a NULL. So you see, common C style pointer whiggling. Don't know why I even explained this.
cName holds the C string, which we subsequently add to our array:
[res addObject: [NSString stringWithCString: cName]];
HUnlock(pName);
}
Unlock the pName handle, and our loop finishes and iterates once more, until FindNextComponent returns NULL. When done, we must destroy our memory handle and return the created array:
DisposeHandle(pName);
return(res);
}
Our first function is completed! We could now rush over to main() and complete this tutorial but it'd be rather short so we'll add another function, to retrieve a ComponentDescription by name. The function takes a pointer to a ComponentDescription which we'll fill with the desired component, if exists, and the name of the component. The function doesn't touch the supplied ComponentDescription if nothing is found, and returns FALSE or TRUE (go do the math).
Boolean FindComponentWithName(ComponentDescription *cd, NSString *Name)
{
Boolean res = FALSE;
char *name;
ComponentDescription ccd, fcd;
Component cmp = NULL;
Handle pName;
char *cName;
int len;
name = (char *)[Name cString];
bzero(&fcd, sizeof(fcd));
pName = NewHandle(0);
while((cmp = FindNextComponent(cmp, &fcd)) != 0) {
GetComponentInfo(cmp, &ccd, pName, NULL, NULL);
HLock(pName);
cName = *pName;
len = *cName++;
cName[len] = 0;
if(strcmp(name, cName) == 0) {
HUnlock(pName);
DisposeHandle(pName);
cd->componentManufacturer = ccd.componentManufacturer;
cd->componentType = ccd.componentType;
cd->componentSubType = ccd.componentSubType;
cd->componentFlags = 0;
cd->componentFlagsMask = 0;
return(TRUE);
}
HUnlock(pName);
}
DisposeHandle(pName);
return(res);
}
The function looks unsurprisingly like FindComponentsWithType, however in sight of a component with the required name, we quickly copy the description to cd and bail out. I'm sure there's a smarter solution in finding a component by name. If you know one, please mail me. Also, an NSString can do string comparison by itself, so converting our input NSString into a C string which we strcmp() could be written in more pure ObjC.
Now that we're done writing functions, let's start with main():
int main(int argc, char *argv[])
{
NSMutableArray *List;
ComponentDescription cd;
char *name;
NSString *str;
int i, n;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
A few boring declarations. Below, we're going to fill our ComponentDescription, in this case we want to find all components of the Music Device type.
bzero(&cd, sizeof(ComponentDescription));
cd.componentType = kAudioUnitType_MusicDevice;
List = FindComponentsWithType(&cd);
There! The list contains all components registered as a music device. Commenting out the cd.componentType line would've given us all components registered on the system. Now for showing'em:
n = [List count];
printf("---> %d objects found\n", n);
for(i = 0; i < n; i++) {
str = [List objectAtIndex: i];
name = (char *)[str cString];
printf("%d: >%s<\n", i, name);
}
printf("\n");
And since we didn't create the other function for nothing, let's test that one too:
str = [NSString stringWithCString: "Apple: DLSMusicDevice"];
printf("The >%s< unit was ", [str cString]);
if(FindComponentWithName(&cd, str))
printf("found!\n");
else
printf("NOT FOUND!\n");
The Apple: DLSMusicDevice should be around on your system, so the program shouldn't say it's not. Now to end the main function:
[pool release];
return(0);
}
... And compile it:
gcc -o ComponentsExample ComponentsExample.m -framework Foundation -framework CoreServices -lobjc
Done! Hope you found this tutorial a bit informational.
Download the sourcecode of the example: ComponentsExample.m
See other tutorials: OS X Tutorials.
If you have any comments, please mail me at QdK@quickdekay.net