Some of the best apps on the iPhone so far are about audio: capturing it, processing it, playing it, and even synthesizing it. But how are some of these apps even possible? In this session, we'll deep into the darkest depths of Core Audio, the iPhone's enormously powerful and often challenging audio API. You'll learn the tricks of the Core Audio that aren't obvious from the docs, and the essential techniques you'll need to shake your users' headphones.
6. "Easy" and "CoreAudio" can't be used in the
same sentence. CoreAudio is very powerful,
very complex, and under-documented. Be
prepared for a steep learning curve, APIs with
millions of tiny little pieces, and puzzling things
out from sample code rather than reading
high-level documentation.
–Jens Alfke, coreaudio-api list
Feb 9, 2009
8. • Problem domain is hard
• Performance is hard
• Low latency is hard
• Reusability is hard
9. • Doing without would suck
• Slowness would suck
• Latency would suck
• Non-reusability would suck
10. Last Exit Before Toll
• Alternatives to Core Audio:
• Media Player: plays audio from iPod
library
• AV Framework: Obj-C API for file
playback and recording
14. CA Style
• Procedural C
• Work with buffers of samples
• Most functions return an OSStatus
• You must check this, every time
• Heavily callback-oriented
• Heavily property-oriented
15. Example: Audio Session
• iPhone-only API
• Inspect and set audio hardware and
software properties
• Declare interaction with other audio
processes on the device (e.g., iPod)
35. CA style recap
• Lots of getting and setting properties
• Always check the return OSStatus
• Asychronous callbacks to C functions
• Look up function templates in docs
• “Context” or “user info” pointer can be
an Obj-C object
42. Engines Utilities
Audio Queue Open AL
Audio Units
43. Engines Utilities
AV Fndtn
Audio Queue Open AL
Audio Units
44. Engines Utilities
AV Fndtn
Audio Queue Open AL
Audio Units Audio File
45. Engines Utilities
AV Fndtn
Audio Queue Open AL Audio Conversion
Audio Units Audio File
46. Engines Utilities
AV Fndtn Ext Audio File
Audio Queue Open AL Audio Conversion
Audio Units Audio File
47. Engines Utilities
Audio File Stream
AV Fndtn Ext Audio File
Audio Queue Open AL Audio Conversion
Audio Units Audio File
48. Engines Utilities
Audio Session
Audio File Stream
AV Fndtn Ext Audio File
Audio Queue Open AL Audio Conversion
Audio Units Audio File
49. iPhone Audio Engines
• Media Player
• AV Foundation
• Audio Queue Services
• OpenAL
• Audio Unit Services
50. Media Player
• Objective-C class MPMusicPlayerController
— Plays music from iPod library
• play, pause, stop. Properties for
currentPlaybackTime, volume, etc.
• No access to decoded samples or files
51. AV Framework
• Objective-C classes: AVAudioPlayer,
AVAudioRecorder, AVAudioSession
• Play from / record to files
• Handles compressed formats
• play, record, pause, stop. volume,
currentTime properties
• No access to decoded samples
52. Audio Queue
• C functions for playback, recording
• Callback-driven: you fill or process audio
buffers periodically
• Plays compressed formats
• Latency is negotiable
53. OpenAL
• C API intended for gaming, designed to
resemble OpenGL
• Attach audio to “sources” in 3D space
• Listener hears sound from source relative
to their own position
• Uncompressed (PCM) only
• Can stream to a source (push model)
54. Audio Units
• Lowest publicly-accessed part of Core
Audio
• C API for audio processing
• Uncompressed (PCM) only, no floats
• Extremely low latency
• OpenAL and Queue implemented atop
units
63. AU “pull” model
• Each unit in the chain pulls audio data from
some source
• Another unit
• A callback to your code
• Last unit is typically an I/O unit
67. I/O Unit Effect I/O Unit
(input) Unit (output)
68. I/O Unit Effect I/O Unit
(input) Unit (output)
Caveat: Not entirely accurate, for reasons to be
explained soon…
69. Units on iPhone OS
• I/O Units
• “Remote” I/O: Abstraction around
harware in/out
• Voice Processing I/O: Same as RemoteIO,
but with echo supression for VOIP
• Generic I/O: No hardware access; use for
software audio rendering
70. Units on iPhone OS
• Mixer Units
• Multichannel Mixer: Mixes multiple input
streams into one output stream
• 3D Mixer: Mixes inputs with more
control over panning, resampling. Used by
OpenAL
71. Units on iPhone OS
• Effect units
• iPod Equalizer: Provides same features as
iPod playback equalizer
• Converter units
• Converter: Converts between flavors of
PCM, but not compressed formats
72. Missing Units
• Unit types absent from iPhone OS
• Generators
• Music devices (MIDI) and effects
• Many units found on Mac absent on iPhone
• Filters, reverb, compressor, varispeed,
other effects
73. RemoteIO
• Your app’s most direct access to audio
input/output hardware
• Used at the end of unit chains that play
audio
• Used at the beginning of chains that
capture audio
74. RemoteIO Buses
• Bus 0 is for H/W output
• Headphones, speakers, etc.
• Bus 1 is for H/W input
• Headset mic, phone mic, etc.
91. Discovering a Unit
AudioUnit remoteIOUnit = NULL;
AudioComponent rioComponent = AudioComponentFindNext
(NULL, &audioCompDesc);
OSErr setupErr = AudioComponentInstanceNew
(rioComponent, &remoteIOUnit);
NSAssert (setupErr == noErr,
@"Couldn't get RIO unit instance");
• If there can be more than one match,
iterate over AudioComponentFindNext,
passing in last match you found
94. 3. Set stream
properties
• Define an AudioStreamBasicDescription
• Set it as the
kAudioUnitProperty_StreamFormat
• You will screw this up at least once
95. The ASBD
• Description of the common traits of an
entire audio stream
• Bitrate, channel count, format, format-
specific flags, etc.
• Not all fields apply to all formats
• On iPhone, Audio Units always work with
LPCM
97. Set RemoteIO’s stream
properties
• Declares format you want to read from
input bus, and write to output bus
• Set the kAudioUnitProperty_StreamFormat
property
• On RemoteIO’s output scope for bus 1
• On RemoteIO’s input scope for bus 0
106. Recap
• We created the Remote I/O unit
• Enabled input and output
• Created an ASBD to describe stream
format and set it on bus 1 output and bus 0
input
• Connected bus 1 output to bus 0 input
• Initialized and started the unit
111. Pulling audio
• A unit’s pull can be done one of several
ways:
• By being connected directly to another
audio unit
• By registering a “render callback” to
provide samples
• This is the key to all things cool…
112. Setting a render
callback
• Instead of connecting the audio units
kAudioUnitProperty_MakeConnection…
• Set the property
kAudioUnitProperty_SetRenderCallback
• Provide a function pointer to your own
function, and a “user info” object
• Any samples you provide get played
113. Render Callback steps
1. Enable recording via Audio Session
2. Get the Remote I/O unit
3. Set stream format
4. Set up callback function
5. Let ’er rip
You’ve already done
all these
114. Creating a render
callback
• Define a struct containing whatever data
your callback function will need
• Set up an AUCallbackStruct
• Set it as the
kAudioUnitProperty_SetRenderCallback
on bus 0
• Implement the callback function
115. Create your user data /
context
typedef struct {
! AudioUnit rioUnit;
} EffectState;
//...
EffectState effectState;
123. Almost there
• You now have access to raw samples, in
your render callback, by way of the ioData
pointer
• Anything you care to do with those
samples can now be played out to
hardware
125. iPhone Audio Effects
• Generally need to be performed in render
callbacks
• On Mac OS X, you can create custom units
to encapsulate effects
• This was supposed to be in iPhone OS 3,
but doesn’t actually work
• Try AUPlugin.h See how far you get.
127. Gain effect
• Get gain value of 0.0 to 1.0 from UISlider
• Multiply each sample by this value
128. Render considerations
• Render callbacks are on a real-time thread,
with a hard deadline to return
• If you miss the deadline you get silence
• Render code must be highly performant
129. Bad Ideas for Callbacks
• File or network I/O
• Blocking threads
• Heavy use of Obj-C messaging
• malloc()
• Basically anything that’s potentially slow, of
indeterminate duration, and/or blocks
130. Good ideas for
Callbacks
• Pass a struct, rather than an Obj-C object,
as the user info object for your callback
• Let other threads do work for you and
leave their work somewhere that the
callback can just read it
• Note possible race conditions
131. Slider value in struct
typedef struct {
! AudioUnit rioUnit;
! float slider1Value;
} EffectState;
// set up callback state object
effectState.rioUnit = remoteIOUnit;
effectState.slider1Value = [slider1 value];
// set callback method
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = MyAURenderCallback;
callbackStruct.inputProcRefCon = &effectState;
-(IBAction) handleSlider1ValueChanged {
! effectState.slider1Value = [slider1 value];
}
132. Read Slider Value in
Callback
EffectState *effectState = (EffectState*) inRefCon;
AudioUnit rioUnit = effectState->rioUnit;
float gain = effectState->slider1Value;
// Call AudioUnitRender() as before to copy samples
// into ioData
149. Config mixer unit
• Set the stream property on its inputs
• Set the render callback or connection
properties on its inputs
• Bus 0 is RobotVoiceRenderCallback
• What shall we mix with?
150. Playing audio files in
units
• Can’t do I/O in render callback
• If compressed, we’d also need to convert
to PCM
• Have a separate thread read/convert, then
place PCM data in a ring buffer
• See CARingBuffer (C++) in /Developer/
Extras/PublicUtility
169. Takeaways
• Core Audio is hard
• Core Audio lets you do things that are
freaking awesome
170. Since you’ll need help
• coreaudio-api at lists.apple.com
• Apple developers post here every day
• stackoverflow.com
• devforums.apple.com
• Look for Michael Tyson’s post on Remote
I/O unit