Apple’s Core Audio API is very powerful. It is also not easy. With Great Power comes Great Heartburn when the documentation is out of date and/or scattered. Sometimes the needs of the few outweigh the needs of the many as in the case of conference talks. At WWDC 2011, session 411 was named “Music in iOS and Lion” (see the Resources). If you watch the presentation, you wil get a taste of what is possible. Unfortunately they were as stingy with code examples as Webern was with notes. (The goal of conference presentations is to show off, and not to educate the viewers. Not picking on these guys, but this has been true of every presentation I’ve ever seen).
So, here is a first step. It is a complete project that runs and produces sound. Many things are missing – on purpose – which will be added in subsequent blog posts. The point is to get the main point across in the simplest manner possible.
Core Audio is a C API. Currently there are no official Objective-C bindings. So, you need to mix your Objective-C code with C code. In more advanced code you will be using many Core Foundation types too, so get comfortable with this intermixing. You will also use classes such as AUGraph from the AudioToolbox framework. So, in your project setup, (under build phases in XCode 4+) you need to link to the AudioToolbox framework and the CoreAudio framework. You do not link to the AudioUnit framework here. If you do, you will get an error.
Most CA (Core Audio) API functions return an OSStatus value. This is a 32 bit integer.
OSStatus NewAUGraph ( AUGraph *outGraph ); |
The function to set up your graph is NewAUGraph. Here is one way to handle the result.
1 2 3 4 5 6 | AUGraph outGraph; OSStatus result = noErr; result = NewAUGraph ( &outGraph ); if(result != noErr) { // handle it } |
Chris Adamson (see the Resources) wrote a function that many programmers use to display the status as a string if possible. If not he prints the digit. This is a bit like the old original Mac toaster that would pop up a dialog stating “Error -151 Occurred”. So, I’ve added a switch statement that checks the value against well known error constants.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | void CheckError(OSStatus error, const char *operation) { if (error == noErr) return; char str[20]; // see if it appears to be a 4-char-code *(UInt32 *)(str + 1) = CFSwapInt32HostToBig(error); if (isprint(str[1]) && isprint(str[2]) && isprint(str[3]) && isprint(str[4])) { str[0] = str[5] = '''; str[6] = ' |