Fun with cocosDenshion and Audio Metering [Update]
For those of you that have gotten a chance to use CocosDenshion, it is the sound engine written by Steve Oldmeadow.
It’s great since loading audio effects and music for us was quite a task beforehand.
Here’s a little demo of what we are going to make in this article shown running on the iPhone simulator.
Well today what we’ll look at is the CDAudioManager class of the CocosDenshion package itself and see if we can produce some neat effects to spice up our games.
Let’s understand CDAudioManager a bit by looking at the comments in the source.
/** CDAudioManager is a wrapper around AVAudioPlayer. CDAudioManager is basically a thin wrapper around an AVAudioPlayer object used for playing background music and a CDSoundEngine object used for playing sound effects. It manages the audio session for you deals with audio session interruption. It is fairly low level and it is expected you have some understanding of the underlying technologies. For example, for many use cases regarding background music it is expected you will work directly with the backgroundMusic AVAudioPlayer which is exposed as a property. Requirements: - Firmware: OS 2.2 or greater - Files: CDAudioManager.*, CocosDenshion.* - Frameworks: OpenAL, AudioToolbox, AVFoundation @since v0.8 */
Since it’s a wrapper around AVAudioPlayer we have some functionality for obtaining data that you can use for playback-level metering.
The methods to use for level metering are:
- the meteringEnabled property
- -(void)updateMeters
- -(float)averagePowerForChannel:(NSUInteger)channelNumber
- -(float)peakPowerForChannel:(NSUInteger)channelNumber
Since we’re just quickly setting up some background audio, let’s do this as simple as possible by using SimpleAudioEngine.
Accessing CDAudioManager
SimpleAudioEngine (as of this post) won’t give us direct access to the audio manager for our background music so let’s quickly add a get method to return the audio manager.
SimpleAudioEngine.m
-(CDAudioManager*)audioManager
{
return am;
}
Update: The alternative for accessing is through the sharedInstance of audio manager and I’ve now reworked the code to use that instead. So now you don’t need to modify SimpleAudioEngine anymore.
We needed access to CDAudioManager so we can get access to AVAudioPlayer to access metering methods. We do this below:
Updated for performance by weak referencing the AVAudioPlayer instance into a class variable.
audioPlayer_ = [[SimpleAudioEngine sharedEngine] audioManager].backgroundMusic; audioPlayer_.meteringEnabled = YES;
Well we have enabled metering but what we now need to do is call updateMeters every time we need
to get meter level values. Cocos2D has functionality for scheduling timers so we set that up and call updateMeters.
-(void)tick:(ccTime) dt
{
[audioPlayer_ updateMeters];
}
While still in the loop where we update our meter, we can now get values for the average and peak power of our background music.
-(void)tick:(ccTime) dt
{
[audioPlayer_ updateMeters];
double peakPowerForChannel = 0.f, avgPowerForChannel = 0.f;
for(ushort i = 0; i < audioPlayer_.numberOfChannels; ++i){
// convert the -160 to 0 dB to [0..1] range
peakPowerForChannel = pow(10, (0.05 * [audioPlayer_ peakPowerForChannel:i]));
avgPowerForChannel = pow(10, (0.05 * [audioPlayer_ averagePowerForChannel:i]));
filteredPeak_[i] = filterSmooth_ * peakPowerForChannel + (1.0 - filterSmooth_) * filteredPeak_[i];
filteredAverage_[i] = filterSmooth_ * avgPowerForChannel + (1.0 - filterSmooth_) * filteredAverage_[i];
}
}
What I’ve done is set all this up in the class AudioVisualization which will handle all the background audio metering for you. You access the class through the sharedAV method which returns a shared singleton instance.
All you need to do is add your delegate with the method:
-(void)addDelegate:(id<AudioVisualizationProtocol>)delegate forChannel:(ushort)channel;
The forChannel argument specifies the channel you want to meter for your background music. If it is mono then just set it to 0.
Now you just need to implement one of the AudioVisualizationProtocol’s to receive level values for your delegate.
-(void)avAvgPowerLevelDidChange:(float)level channel:(ushort)aChannel;
That’s pretty much it! Hope you find this as cool as I did when I figured it out.
Just download the example demo file below to try it out.
This demo uses the fast, easy and open source cocos2d framework.
CocosDenshion is the sound engine for the cocos2d framework if you didn’t know that from reading this whole post =/.

You can leave a response, or trackback from your own site.
8 Conversations
Converse

I plan to make a demo video to help show what the demo code can do, stay tuned.
[...] can read the article here: Fun with CocosDenshion and audio metering And you can download the test project from here: [...]
Nicely written and very useful! Thanks for the tutorial.
[...] The full tutorial and code can be found here: Fun with cocosDenshion and Audio Metering [...]
[...] Fun with cocosDenshion and Audio Metering [Update] | Fancy Rat Studios (tags: music iphone animation programming tutorials opensource) [...]
[...] Fun with cocosDenshion and Audio Metering [Update] | Fancy Rat Studios [...]
Is there away to select a sound from the iTunes music library rather than embedded song?
As of right now the way to play music off of the devices library is through the MPMediaPickerController. It’s quite an easy class to setup and get music running. Unfortunately the class does not connect itself with the AVAudioFoundation class which is what we’re using to run the metering code in this post but it if that’s not what you’re looking for then that is the best solution.