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.
24 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.
Is there a way to use cocosDenshion to start up a background music file at a position other than the start of the song? For instance if I have one track playing and it has played 1:02 minutes, and I want to switch songs but the new one to start at 1:02 minutes (doesnt have to be seamless), is that possible?
Hi Lam,
I try this your tutorial. How to return positive decibel value?
[bg averagePowerForChannel:i] : -160 -> 0
avgPowerForChannel = pow(10, (0.05 * [bg averagePowerForChannel:i]));
avgPowerForChannel -> [0,1].
Can you help me return positive decibel value please ?
Thanks so much !!!
I have updated the original code to work with V0.99.5 of Cocos2d for iPhone.
You can Download a zip of the updated project from here http://www.silverbacktechsol.co.uk/blogresources/DenshionAudioVisualDemo.zip
Hi Lam
I am facing the problem when I remove the delegate and add the delegate again! I get level = 0.0 in avAvgPowerLevelDidChange.
Do I need to do something after removing the delegate and before adding it again?
Thanks
Ahsan
Logically it should be as simple as adding the instance since addDelegate places the instance into a list and that list is used to notify everyone. There shouldn’t really be any other needed steps.
Hi
I just needed to resetMetering after starting the background music.
Something like:
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:fullPath loop:YES];
[[AudioVisualization sharedAV] resetMetering];
[[AudioVisualization sharedAV] addDelegate:self forChannel:0];
Thanks!
Regards
Ahsan
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.
+1
So, is there no way to run the metering code with a song played from the itunes library?
Thanks!
Yeah music played through iTunes does not go through AVAudioPlayer so metering can’t be done.
Black Lotus MTG…
[...]Fun with cocosDenshion and Audio Metering [Update] | Fancy Rat Studios[...]…
Hello…I need somebody who can help me with this…It is possible to make the audio-meter works with music played through iTunes in the ipod (the music stored in the ipod)???…I mean…is there any other possibility to do that??? No just it was made here in this app with AVAudioPlayer…I really need your help with that guys…thank you.
I haven’t looked into it much but I did recall that someone was able to use their ipod library music to create a rhythm game using AVAsset.
Take a look into into that to see if it helps you.
Thank you Lam,I’ll see it…if you find anything else please let me know…thank you again.
Hello !
I tried with an another music (a caf file with same properties as the one with the demo) but some time the values from avg power and peak power goes wild, they’ren’t between 0 and 1 but more like ~ inf.
I’m wondering why ? any idea ?
Thanks !
I haven’t tried with caf files yet though there may be some issue with the format and AVAudioFoundation but nothing that I know of an answer.
Great information :)