iPhone Game Programming – Tutorial 9 – Sound Manager
In this tutorial I create a simple sound manager class that can play back multiple sounds using OpenAL. It also allows you to play background music as well using the AVAudioPlayer class.
The reason for writing my own sound manager was to see how OpenAL works. Commenters on this blog provided some great links, see the “Sound of music… explosions and lasers”, so I decided to work through them and get something working.
This class is simple and provides basic access to playing sounds in OpenAL. It also makes use of all 32 sources OpenAL sources available on the iPhone so that the same sound can be played simultaneously which is great for explosions and laser fire.
One item I have not yet got into the class is 3D sound. OpenAL give you the ability to position the listener in 3D space and to also position each source of sound in 3D space. OpenAL will then work out depending on where the listener is in relation to the sound source what the sound should sound like i.e. volume, pitch etc. The API for this looks simple enough but I’ve not had time to try it out and wanted to get the core class tutorial published. I will take a look at this area and post any changes I make to the class.
A big thanks goes to www.benbritten.com there is a great OpenAL tutorial on there about getting going with the basics needed and its this well written tutorial which got me off the ground, so a real thanks to Ben.
As usual, please post any questions, suggestions, or issues you spot with the code and I hope you enjoy the tutorial.
Mike
117 Comments
Eskema on May 26th, 2009
bob maybe its because the format 22050, or the compression used on the file
bob on May 26th, 2009
@eskema
I don’t think so, I tried it at different rates 44100 and what not and I also tried several different files, same thing. I’m still messing around with it. Have you tried a stereo file ?
mike on May 26th, 2009
Hi bob
I have tried it with stereo and it seemed to work fine. I’ve put up the file I used at http://www.71squared.co.uk/iPhone/laser.caf for you to download and try. Its around 1.3Mb as well as I wanted to see how OpenAL handled large sounds.
This sound effect plays fine but I’d be interested to see if you get the same results. If it works then it could be how the app you are using encodes the file. I generated my sound file in GarageBand, saved as AIFF and then used Audiacity to convert to a WAV, then from a WAV to a CAF using afconvert, so its been through a number of steps and works ok.
Let me know how it goes.
Mike
bob on May 26th, 2009
@Mike
I converted my caf files to wav using a free program called audacity which can be found here http://audacity.sourceforge.net/download/mac
Then I converted them to caf again using afconvert. That worked, It must have something to do with the way the original program I used to save them. I think I might buy QuickTime Pro..
bob on May 26th, 2009
@Mike
Well I fixed the sound by just putting them through afconvert. Without converting them to another format.
I used
afconvert -f caff -d LEI16 ocean.caf ocean.caf that did the trick. I think the program I originally used said it saved as caf format but I bet it didn’t do it correctly. The software I originally used was SOUND STUDIO3
Thanks alot for the Help.
mike on May 26th, 2009
Thanks for the update bob. I use Audiacity as well, its a good little app. I’m glad you got to the bottom of it anyway :o)
Mike
Thomas on May 27th, 2009
Hi
Hopefully you can help me, i’m using your tutorial 9 code as base for at game. It’s a simple pong tennis game just to get the hang of making games for iphone. I have a problem with a texture getting bigger then the actual size when i move it. It’s a bit hard to explain but if the texture is like this, try to emagine this is the pong bat
***
When i in updateScene move the x to fast it’s like it rendered one and a half time so i now is
****
a little wider.
If i in updateScene move it slowly it dosen’t do this.
Hope anyone has clue to what is going on.
mike on May 27th, 2009
Hi Thomas
That’s not one I’ve seen before. The only thing I can think of that would make the texture bigger is if the quad being drawn somehow was bigger as the texture inside it would be scaled up.
If you can send me a sample project with this problem I’d be happy to take a lool, mike@71squared.co.uk.
Mike
Jamie Hill on May 27th, 2009
Blimey… you’re banging these out one after another! …excellent news, I’ve got some catching up to do though ;)
CKJ on May 27th, 2009
Hi,
I don’t see the release of AVAudioPlayer
when i try to do it, i get gdb ;(
can you explain more about memory using about AVAudioPlayer.
Regards,
mike on May 27th, 2009
Hi CKJ, that is a good point. I’ve made some changes to the code which does now release the AVAAudioPlayer instance correctly and I’ve also spotted another bug with the release of the soundbuffers in the dealloc method.
When I get home tonight I’m going to put the changes into the Tutorial 9 project and let people know when its available for download.
Thanks for the feedback.
Mike
thomas on May 27th, 2009
Hi Mike
Just to let you know i found my error so i’m not sending you a email anyway..
thomas on May 27th, 2009
Just a quick question, how do i collision detect two images? I need to know if image one and two collide, i’m using your lession 9 as base so i’m using your image classes.
mike on May 27th, 2009
Hi Thomas, glad you got your problem sorted out. Collision detection is a tutorial I am going to be starting soon. There are many many different ways to do it.
The easiest way to do it is to check if the bounding box of each image intersect. If you create a CGRect with the location and dimensions of the images you can that use a method it has which check if that CGRect intersects with another, which you provide as part of the method call. If they do then you have collided.
If you go a google on collision detection you will find loads of info on it. The Image class at the moment does not have any specific functionality for collision detection. This is something that I will be building into the entity classes I will need. Again, I’m going to be covering this in tutorials to provide more detail.
Hope that helps
Mike
mike on May 27th, 2009
A new version of the Tutorial 9 project has been created which contains a couple of fixes. One which releases the backgroundMusicPlayer and the other which fixes a problem when trying to remove all OpenAL buffers. I was trying to look through a Dictionary as if it were an array DOH!!! :o)
Mike
Chris on May 28th, 2009
Hey Mike, quick question, and this may be a stupid one:
How would you stop background music from playing?
Thanks
Chris
RoberRM on May 28th, 2009
Hi Mike!
I’m not even close to this tutorial yet, but I’ve read your previous post and I have to ask: do you plan on releasing another “quick update” tutorial with this fixes?
You see, I’m following your tutorials, so I’m missing all this changes you introduce in your code but not in the videos. Don’t take me wrong: I’m not asking you to do anything extra (you are doing us a big favor already), this is just a question to let me know if I should check the final code when all the tutorials are finished (because there may be portions of code not included in the tutorials, such as this fixes) or not.
Thank you for your patience! :D
mike on May 28th, 2009
Hi RoberRM, yes, I’m going to do an update vid which will cover the fixes in the Sound Manager and also a couple of changes to the Image class as well :o)
Mike
mike on May 28th, 2009
@chris, there is a method called stopPlayingMusic which can be used. You will need to add that method to the header as well as I’ve spotted that it was missing.
Let me know if you have any issues with that.
Mike
Ant1 on May 28th, 2009
Thank you very much for another fantastic tutorial can’t wait for the next one!
Tom on May 28th, 2009
Hi Mike,
I had a little problem playing music using the AVAudioPlayer. When I ran my application in the simulator it crashed with the following error.
Error loading /Library/QuickTime/DivX Decoder.component/Contents/MacOS/DivX Decoder: dlopen(/Library/QuickTime/DivX Decoder.component/Contents/MacOS/DivX Decoder, 262): Symbol not found: _SCDynamicStoreCopyConsoleUser
You can quite easily fix this problem by moving all incompatible codecs out of the /Library/Quicktime folder to a temp folder for safe keeping.
The error message tells you which codec the iPhone simulator has a problem with. In the above example the erroneous codec is the divx decoder.
Jason on May 29th, 2009
Mike,
I love the sound manager, thanks for all the work on the tutorials.
Hey, I haven’t seen any tutorials or posts, but do you plan on doing something on the best way to handle user input (touches) in your iphone game?
Thanks
-Jason
RoberRM on June 2nd, 2009
Yet another great tutorial, Mike. :D Thank you very much!
I’ve seen that you didn’t get to fix that warning Xcode gives in the initWithContentsOfURL of the AVAudioPlayer. I’ve searched around the web and all I’ve seen is people passing nil instead of &error. I’ve tried other options but my knowledge of this things is quite limited yet (i.e. thanks to you I understood what the & sign meant :D ) and nothing else seemed to remove it.
I’d like to know your thoughts on this.
Once again, thank you for your help. ;)
Róber
lpbaker on June 6th, 2009
These tutorials have been HUGELY helpful, I can’t tell you how grateful I am to you for taking the time to do them and make it all so clear. Although I started writing games in 1980 and have written hundreds of them in multiple languages, trying to get my head around OpenGL was really aggravating – your tutorials have made it all seem so simple! I’ve sent you a small Paypal donation by way of thanks :)
One question; what’s your position on people taking this code and using it in commercial games? As in, if I write an iPhone game using your code and then sell it … ?
Thanks,
Lindsay
mike on June 6th, 2009
Hi Lindsay and thanks for the donation, it is always much appreciated :o)
I’m glad you are finding the tutorials useful. The aim is to make them as simple as I can and not get hung up in the complexity which its always possible to introduce into these topics.
As for the code, it is completely public domain. I should really clearly state that somewhere. If you want to use the code in a commercial game that’s great and you are free to do so. If you do manage to release something it would be good to hear about it as I’d love to see what people have managed to create with some of the code :o)
Good luck and always happy to answer questions, if I can ;o)
Mike
lpbaker on June 7th, 2009
That is hugely generous, Mike! If I do make a commercially successful game (big IF!) then I’ll definitely shoot a nicer donation your way.
I’ve just finished watching Tut#4 so I’ve got a ways to go before I start asking specific questions (especially since this is adapting as you go along and learn more). For now I’m just grateful to see what you’re doing, as it’s helping not only my OpenGL but it’s given me a few “aha” moments with Objective C as well :)
Linki i ciekawostki | appledev.pl on June 7th, 2009
[...] dla iPhone’a. Bardzo ciekawe i bardzo techniczne artykuÅ‚y (jak choć ten o OpenGL). iPhone Game Programming – Tutorial 9 – Sound Manager Tutorial pokazujÄ…cy jak korzystać z OpenAL. Avoiding iPhone App Rejection, Part 2 Kilka [...]
IPhoner on June 8th, 2009
Nice tutorials! I’ve noticed that many proffesional
games are displaying a little movie as intro, it would be cool if you showed us how to play a little movie before getting to the main screen where text and sprites are displayed.
Also, I have jailbreaked my ipod touch so I can easily deploy the compiled/linked result of you tutorials into the ipod touch, but I can’t figure out how to do that…. How can I test the Oglgame.app directly on ipod touch.
Thank you.
Daniel on June 9th, 2009
Hi Mike!
I’m having lots of trouble with my sharedsoundmanager instance.
when i call the line:
sharedSoundManager = [SingletonSoundManager sharedSoundManager];
it makes my app to crash. The odd thing about this code is that
it worked perfect when i tested it yesterday. I don’t know why it does this.
im running my app in the 2.2.1 simulator on my mac mini.
Do you have any ideas what causes this problem?
Thank you for these great tutorials. probably some of the most useful ones i’ve come across on the web:)
Daniel
Daniel on June 9th, 2009
Hi!
sorry about my last post. I solved the problem. it was caused by the soundproperties on my mac mini. now it runs perfectly:D
Thanks again for these great tutorials
Daniel
mike on June 9th, 2009
Hi Daniel
Glad you got it sorted and that your finding the tutorials useful :o)
Mike
Austin on June 9th, 2009
Hi Mike
I’m having trouble with this tutorial. My app keeps crashing and no sound will play. Even when I download the Tutorial 9 Project the app will play no sound and crash. Is there anything obvious I am missing?
Thank you!
Austin
mike on June 9th, 2009
Hi Austin
There is nothing that comes to mind, it should work ok. Daniel above has just posted about a problem he was having with his settings that made his app crash.
Daniel, can you share the changes you made as it could be the same problem Austin is having.
Mike
lpbaker on June 10th, 2009
I also can’t get the Tut9 project to run; here’s the console output:
[Session started at 2009-06-10 11:23:15 +1000.] 2009-06-10 11:23:16.640 OGLGame[3121:20b] AngelCodeFont: Initializing vertex arrays for font 'kerning' with capacity of '95' 2009-06-10 11:23:16.648 OGLGame[3121:20b] Tiled: Tilemap map dimensions are 100x6 2009-06-10 11:23:16.648 OGLGame[3121:20b] Tiled: Tilemap tile dimensions are 50x50 2009-06-10 11:23:16.648 OGLGame[3121:20b] Tiled: Tilemap property 'MyMapProp' found with value 'My Value' 2009-06-10 11:23:16.649 OGLGame[3121:20b] --> TILESET found named: Untitled, width=50, height=50, firstgid=1, spacing=0, id=0 2009-06-10 11:23:16.649 OGLGame[3121:20b] ----> Found source for tileset called 'tiles.png'. 2009-06-10 11:23:16.656 OGLGame[3121:20b] --> LAYER found called: Layer 0, width=100, height=6 2009-06-10 11:23:16.657 OGLGame[3121:20b] ----> Found '600' tile elements 2009-06-10 11:23:16.659 OGLGame[3121:20b] --> LAYER found called: Layer 1, width=100, height=6 2009-06-10 11:23:16.661 OGLGame[3121:20b] ----> Found '600' tile elements 2009-06-10 11:23:16.663 OGLGame[3121:20b] --> Initializing vertex arrays for '70' quads. 2009-06-10 11:23:16.664 OGLGame[3121:20b] Map Property value = 'My Value' [11:23:19.741 ] AQMEIOBase::DoStartIO: timeout [11:23:20.017 ] AQMEDevice::StartIO: AudioOutputUnitStart returned -66681 [11:23:20.017 ] AUIOClient_StartIO failed (-66681) 2009-06-10 11:23:20.036 OGLGame[3121:20b] Error loading /Library/QuickTime/LiveType.component/Contents/MacOS/LiveType: dlopen(/Library/QuickTime/LiveType.component/Contents/MacOS/LiveType, 262): Symbol not found: _SCDynamicStoreCopyConsoleUser Referenced from: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/LangAnalysis.framework/Versions/A/LangAnalysis Expected in: /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.2.1.sdk/System/Library/Frameworks/SystemConfiguration.framework/Versions/A/SystemConfiguration
mike on June 10th, 2009
@lpbaker, thanks for the log output. This is something I have seen before as have some others on the blog. The problem is that there is a codec which is not compatible with the iPhone Simulator. From the log above, the codec which is causing the problem is LiveType.
If you copy the codec causing the problems out of the
/Library/QuickTime
folder to somewhere safe it should sort out the problem.
Let me know how that goes.
Mike
lpbaker on June 10th, 2009
@mike: I took out Livetype.component and tried again, this time it failed on the DivX decoder. Removed that too and the project worked :) Thanks for the quick answer.
Completely unrelated question: since you’ve mentioned it several times, I’ve been checking out Cocos2D, and although it’s a lot to take in initially, it does seem very powerful. I was wondering why you’re writing your own engine from scratch – is it just to learn how? Don’t get me wrong, I’m loving the videos and the project you’re building, I’m just curious!
Thanks,
Lindsay
mike on June 10th, 2009
Excellent, I’m glad that got things working.
As for Cocos2D, it is a great engine. For me, I just have to know how things work :o) I may well use Cocos2D in the future, but I’ve always felt that you get the most out of things like Cocos2D if you understand what is going on under the hood. If you just need to get something out quick then just using the engine is great, but certainly for me and it seems others as well, there is a real interest to understand how things work when using OpenGL.
Sorry for the ramble, but basically I just love learning about this stuff :O)
Mike
lpbaker on June 10th, 2009
@mike: I understand completely – I’m the same :)
Any chance you’ll be doing a tutorial on user input soon? It would be nice to be able to get things moving around the screen based on touches or tilts.
Thanks, Lindsay
Austin on June 10th, 2009
The DivX decoder was my problem too. Thank you Mike and Ipbaker for that!
barry on June 10th, 2009
Hi Mike,
Thanks for creating your Sound Manager tutorial. It’s excellent!
I’d really like to re-download the Tutorial 9 project when any of the following updates occur: 3D sound (to which you refer in your recent blog), a completed “stopSoundWithKey:” method, and/or a solution for the warning that Xcode periodically gives regarding the call to AVAudioPlayer’s “initWithContentsOfURL:error:” method.
Is there a list I can get on to be notified if you post any of these changes?
Thanks,
Barry
RoberRM on June 10th, 2009
@barry: The only solution I’ve found so far is to change the error:@error into error:nil. That eliminates the warning although you won’t be able to check for errors later. That’s what I’ve done because that’s what everybody else appears to be doing, but I don’t think that’s the correct approach.
Anonymous on June 17th, 2009
Mike,
Where did you find your sound effects?
Thanks
mike on June 17th, 2009
Finding sounds is always a pain. I normally just hunt around the internet until I find something I can use. One site I’ve got stuff from is http://www.grsites.com/sounds/
There are others, but I just tend to hunt around rather than have a favorite place.
Mike
dnsolo on June 24th, 2009
Hi Mike,
Thank you so much for all of your amazing tutorials, you have really helped me to understand much more about iPhone development, please keep up the great work!
I have now got your Sound Engine code working on my MacBook but I need to be able to stop the music tracks and also stop sound effects playing, could you please provide method implementations for the ’stopPlayingMusic’ and ’stopSoundWithKey’ method, many thanks.
Also, if you could publish another tutorial about ‘3D sound’ and how to use it effectively in games then that would be brilliant.
Finally, a word of advice to other iPhone developers out there, I’ve experienced problems when recording sounds in AIF format and then changing their file extension to CAF. Sometimes these sounds do not play correctly, i.e. they are heavily distorted.
I now use the ‘AFConvert’ tool to convert all of my AIF sound files to CAF format and I have not experienced any problems with this new approach.
To do this, load up Terminal and enter the following command (changing the sound sample settings and filenames accordingly):
afconvert -f caff -d LEI16@44100 MyAIFfile.aif MyCafFile.caf
Dan
aslong on June 27th, 2009
Mike,
Wanted to let you know while I was following the tutorial I found a memory leak in the loadSoundWithKey method. When your are returning from AudioFileReadBytes failing the outData variable is not released. Maybe showing the code will help to explain this:
What the tutorial had:
// Load the byte data from the file into the data buffer
OSStatus result = noErr;
result = AudioFileReadBytes(fileID, FALSE, 0, &fileSize, outData);
AudioFileClose(fileID);
if (result != 0)
{
NSLog(@"ERROR SoundEngine: Cannot load sound:%@", theFileName);
return;
}
What it should have:
// Load the byte data from the file into the data buffer
OSStatus result = noErr;
result = AudioFileReadBytes(fileID, FALSE, 0, &fileSize, outData);
AudioFileClose(fileID);
if (result != 0)
{
NSLog(@"ERROR SoundEngine: Cannot load sound:%@", theFileName);
if (outData)
{
free(outData);
outData = NULL;
}
return;
}
Its minor and probably would not cause an issue. Just thought I would let you know. Keep up the great work Mike. I look forward to every tutorial.
Andrew
(Sorry for any weird formatting issues)
willc2 on July 5th, 2009
Hi Mike,
Thanks for sharing your knowledge via these tutorials, they’ve been very informative.
For people having sound format troubles like static or distortion, the problem is the endian-ness of the sound files. Amadeus Pro, which I use, will always save .caf files as big-endian, whereas Mike’s SingletonSoundManager requires little-endian data. You can inspect your files in Quicktime Pro to tell the difference. I made an AppleScript droplet app that calls afconvert and put it on my finder sidebar so I could easily fix my files after I finish editing them.
Blog » 71² » iPhone Game Programming – Tutorial 9 – Sound Manager on July 7th, 2009
[...] posted here: 71² » iPhone Game Programming – Tutorial 9 – Sound Manager Tags: jak-korzysta, jako-rozszerzenie, programming, sound-manager, swoich-kabinach, teatrze-dzia, [...]
David on July 19th, 2009
Hi Mike! I was curious to know what would be required to pause the music and return to the spot where it left off. Is it possible to do without writing a special function or would I need a variable to record the time position?
Thank you for all the hard work on this tutorial! I very much appreciate the time and dedication that has been spent on this.
barry on July 19th, 2009
Hi Mike,
Ditto! I’d love to know the answer to David’s question (posted today, on July 19) regarding pausing the music and returning to the spot where it left off. That would be a tremendous help!!
Thanks!
Mike on July 19th, 2009
Hi guys. I away at the moment so don’t have access to my code, but I’m sure that AVAudioplayer has a pause method you can call, I think it is is in a method I put in the sound class as well. If it is an openal sound you want to pause I would need to check.
Mike
barry on July 19th, 2009
Ah yes, Mike, you’re right — AVAudioPlayer has a pause method (and you did add the “pauseMusic” method to Tutorial 10). I haven’t tried it yet, but after pausing, I’m assuming that all we have to do is call AVAudioPlayer’s “play” method to resume.
Thanks Mike!
Mike on July 19th, 2009
@Barry, you got it, play will will resume from where it was paused.
dnsolo on July 20th, 2009
Hi Mike,
I would like to fade out the music rather than just stopping it, is there an easy way of doing this?
Thanks,
Dan
Mike on July 20th, 2009
Hi Dan, there are no api calls to do this for us that I know if, so I would create a new method in the sound manager called something like fadeOutMusic. This method could take the delta from the update method and use that to update the music volume as you can set the volume of the music and openal separately.
You can then use this method to pause the music when the volume gets to 0 so that I’d you had a fade in method the music will carry on from where it left off.
Not got my code with me as I’m on holiday but that should work ok :0)
Mike
Incredible iPhone Game Programming Tutorials With Video | iPhone Development Tutorials and Tips on July 31st, 2009
[...] 7 – Singleton Class iPhone Game Programming – Tutorial 8 – Particle Emitter iPhone Game Programming – Tutorial 9 – Sound Manager iPhone Game Programming – Tutorial 10 – Game [...]
mike on August 1st, 2009
@aslong, sorry for the delay in getting back to you on your comment. You are of course absolutely right about the memory leak. I’ve been making changes to the Sound Manager this week and I’m just posting on them. I’m going to add this fix to that post.
Thanks for spotting this and sharing, it really helps.
Mike
mike on August 1st, 2009
@dnsolo, thanks for the comments and I’ll certainly do something on 3D sound now I’ve got it working in my game :o)
I’ve only covered the basics in what I have written, but it should be enough to get you and others up and running. I’ll put it on the list and get something out ASAP.
Mike
Anonymous on August 2nd, 2009
Sweet. Thanks Mike for a great tutorial!
My question is – How do you stop audio playing
- (void) stopSoundWithKey:(NSString*)theSoundKey {
[?]
}
I assume its got something to do with
alSourceStop(sourceID)
but I don’t know hoe to find the right sourceID
Ant help would be greatly appreciated
Thanks!
Jonathan on August 2nd, 2009
Sorry , I didnt mean to be Anonymous
chrisk on August 5th, 2009
I’m new to iphone development & mac’s .
When Using your sound manager as a reference i’ve come across a odd problem. Some samples seem to crash the sound manger , all sound fx is lost but the music starts to play again after a 2+ second freeze.
I’m using audacity and then afconvert to make samples.. Any idea?
Its 100% a sample data problem but i’m a little confused as to what.
chrisk on August 5th, 2009
Update simulator works fine, device cause’s problem.
Also noticed that your example file photon.caf is report by afconvert at 44khz but you set it as 22050 in code. if you set it as 44khz it play incorrectly.
Chris
chrisk on August 5th, 2009
looks like a problem with this line
alBufferData(bufferID, AL_FORMAT_STEREO16, outData, fileSize, theFrequency);
as im loading a mono16 sample.
its also why your 44khz sample had to be set as 22050hz to play correctly.
Chris
anonymous on August 5th, 2009
awesome tutorial! Watched the whole thing now I’mm going to use it to make some kind of instrument app. Thanks a lot!
anonymous on August 6th, 2009
@Anonymous (or is it @Jonathan),
I solved this by just doing
NSUInteger soundID = [self nextAvailableSource]-1; //Then alSourceStop(soundID);
This way it just returns the last sound added then stops that sound. It only works if you have just one sound or know the order of the songs being added. This worked in my situation. In this case you can get rid of the WithKey:(NSString*)theSoundKey part in the method because it is not implemented.
I actually didn’t know about the alSourceStop call so you helped me out with that :)
Hope this helps.
MarsMan on August 9th, 2009
Your tutorials are freaking awesome! Thank you for generously sharing your newly acquires openAL knowledge with us!
Do you have any plans on adding the ability to dynamically modify sounds while they are playing? Additions like dynamic pitch change would be great for creating doppler effects (for moving cars, spacecraft, etc.) Dynamic volume change would be cool to use for creating release “envelopes” for sounds. For example, you could put a release on a long ray blaster sound which would play the long version of the sound as long as your finger is held on the screen, but quickly fades to zero as you take your finger off the screen).
mike on August 9th, 2009
Hi MarsMan
I’ve got a new version of the SoundManager which has a few bug fixes included that make it play nice when phone calls or alarms interrupt the sounds :o)
I’ve not done anything on dynamic pitch changing etc, but things like doppler are handled for you by OpenAL. You can set not only the location of a sound source, but its direction and speed. Based on the location of the listener, volume and doppler effects are calculate for you. There is a HUGE amount available in OpenAL and it just takes some configuring. Its worth checking out OpenAL docs to see what is possible.
If I get a chance I will post more on sounds, but I will be posting the new SoundManager shortly.
Mike
Jonathan on August 10th, 2009
Thanks! [anonymous on August 6th]
Unfortunately, I need to be able to target specific sounds to stop…as apposed to the last one added.
Is there a way stop the sound by targeting its key
eg… stopSoundWithKey:@”sound”
Thanks!
CC on August 19th, 2009
What about unloading sounds? If we know we don’t need a sound any more, what needs to be done in order to free up that memory?
More importantly, what are the best practices with regard to loading and unloading sets of sounds? For instance, if you have a set of sounds associated with “level one”, another for “level two”, and another for “main menu”, for instance, what’s the best way of swapping out these sets of sounds? Is it better to load all sounds up front during app initialization, as implied by the interface of your sound manager? Seems like that would be a bad idea in a resource-limited environment like the iPhone, but then, maybe the idea is to have no latency later…
Also, in init, don’t you need to release self before returning nil? You do it for the last “return nil” but not where you say “if(!result) return nil;”
mike on August 19th, 2009
Hi CC
Good point about the removal of sounds and music entries. I’ve added this to my latest version of the sound manager which I will upload when I get home.
As for loading the sounds, your of course right about the memory limits. I would say that if there are not that many sounds or they are relatively small, its ok to load them all at startup to remove any latency by creating them later. If they are level based and having them all in memory is not possible, then removing the old sounds and loading the new ones when the level loads before game play starts would be best, again to remove latency during game play.
Good spot on the missing [self release]. I will add this to the calls before I upload when I get home.
Thanks for the comments :o)
Mike
mike on August 19th, 2009
@Jonathan, that is a method I need to finish. I’ve had a stub in there reminding me to do it for a while :o) so I need to get it sorted.
It just requires some changes that allow the sourceID to be held against the sound key when the sound is played allowing us to stop that source if necessary.
The bit I’ve not sorted yet is the ability to know when a sound has finished so that the sourceID can disassociated from the sound key when the sound finishes playing, like a callback. If I don’t then I may end up stopping some other sound which is now using that sound source.
I’ll take a look and get back to you when I’ve come up with something. All suggestions welcome of course :O)
Mike
CC on August 19th, 2009
Thanks for looking into it. :)
For stopping sounds, the thing I did was loop through all the sound sources and see if they are using the buffer of the sound I want to remove. Not sure if this is the right approach, though. Here’s the code I used, and it seems to work (crossing fingers that formatting is preserved…):
- (void)stopSoundWithKey:(NSString*)theSoundKey {
// Get the buffer ID for the sound key
NSNumber *numVal = [soundLibrary objectForKey:theSoundKey];
if(numVal == nil) return;
NSUInteger bufferID = [numVal unsignedIntValue];
// Loop through the OpenAL sources and stop them
// if it is using the wanted buffer.
for(NSNumber *sourceIDVal in soundSources) {
NSUInteger sourceID = [sourceIDVal unsignedIntValue];
NSInteger bufferForSource;
alGetSourcei(sourceID, AL_BUFFER, &bufferForSource);
if (bufferForSource == bufferID) {
ALenum error;
alSourceStop( sourceID );
alSourcei( sourceID, AL_BUFFER, 0);
}
}
// Remove the object from the sound library
[soundLibrary removeObjectForKey:theSoundKey];
}
Jonathan on August 21st, 2009
Everyone he is awesome… very helpful!
@cc
Thanks for the stopping code. Great way of targeting a key and works well.
The only wierd thing I am getting, is an unused variable warning on
ALenum error;
What is this for?
Cheers
CC on August 21st, 2009
Oh. Sorry about that.
The ALenum error; is a leftover of some error-checking code I had in there to NSLog() any errors that come up. Since it was just NSLog()’ing and not doing anything different functionally, I took it out for clarity.
If you want to, you can put the error checking back in with little trouble – just call alError() after each call and log an error message based on the result.
MarsMan on September 7th, 2009
@CC wrote some great code for targeting and stopping a key.
Does anyone have a good method of stopping all playing sounds at once? I don’t see an openAL function which accomplishes this.
mike on September 7th, 2009
@MarsMan, your right that there is no single command to stop all OpenAL sounds. The easiest way would be to loop through all your sources stopping anything playing.
That’s the only way I can think of doing it.
Mike
Simon on September 12th, 2009
No sooner did I think I had it all worked out than this happened. I got the following error message when trying to build the project:
Undefined symbols:
"_AudioFileGetProperty", referenced from:
-[SoundManager audioFileSize:] in SoundManager.o
"_AudioFileReadBytes", referenced from:
-[SoundManager loadSoundWithKey:fileName:fileExt:frequency:] in SoundManager.o
"_AudioFileClose", referenced from:
-[SoundManager loadSoundWithKey:fileName:fileExt:frequency:] in SoundManager.o
"_AudioFileOpenURL", referenced from:
-[SoundManager openAudioFile:] in SoundManager.o
Any ideas what this means?
mike on September 12th, 2009
Looks like it’s a missing framework. Make sure that you have got the AudioToolbox framework added to your project.
Let me know if that does not work.
Mike
Simon on September 12th, 2009
First off thanks for the quick response. Second you were spot on.
Thanks again
Pascal on September 22nd, 2009
Hi Mike at first i would like to say thanks for your great Tutorials.
I saw that you still have no solution for the warning inside the allocating for the AVAudioPlayer.
I hope I can fix this problem with you.
The warning says “distinct Object-C type”. If you click on “Jump to Definition” for “initWithContentsOfURL:error:” you will see that there is no explicit definition for this.
AFAIK: The type of an [Class alloc] is id, that’s why the compiler doesn’t know which “initWithConentsOfURL:error:” he is supposed to use.
This new code will explicit cast the AVAudioPlayer class and fix the warning.
backgroundMusicPlayer = [(AVAudioPlayer *)[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error];
(I didn’t now the right syntax for posting code, but still hope this works ;)
Please tell me if I’am not 100% correct with my Opinion ;)
Regards
Pascal
mike on September 22nd, 2009
Hi Pascal
Thanks for your post. I’m currently using Xcode 3.2 and SDK 3.1 and I don’t get any errors when compiling the sound manager which contains the allocation of the audio player. Which version of the SDK are you using?
Mike
Pascal on September 22nd, 2009
Hi Mike
I currently using Xcode 3.1.3 and SDK 3.0 I downloaded the Project File and compiled this for SDK 2.2 and for SDK 3.0 . But I didn’t get any error I only get the warning which is also shown in your Tutorial Video at time 41:00 in Line 289.
I will download the new Version of XCode and the new SDK to test. But my post will fix the problem for the Tutorial ;)
Regards
Pascal
gN0Me on September 28th, 2009
Hi, Mike!
I have a problem with using Sound Manager for MP3 and AIFF files. I try to change value of frequency and format of buffer but everytime I got only noise. The same file in CAF of WAV format plays fine. Could you help me? Thanks.
BTW: I used Audacity to convert files.
gN0Me
iPhone Game Programming – Tutorial 9 – Sound Manager | 71² – The … | IPhoneMate on September 29th, 2009
[...] the original post: iPhone Game Programming – Tutorial 9 – Sound Manager | 71² – The … Share and [...]
Marsman on October 11th, 2009
Mike,
I thought you might like to see your SoundManager class on steroids. My app is currently awaiting Apples approval.
I get a few pops and clicks (some faintly audible in the YouTube demo). I’m sure it’s because I am taxing the limits of openAL and using long 44k samples.
B.T.W., if this app sells I will be sending you a gift!
jimbob1971 on October 15th, 2009
Apologies if you covered this in the tutorial – I’m on the early ones and just whizzed through, but where does the music track come from? It sounds like an old C64/Amiga game, but I can’t remember which. Thanks!
mike on October 15th, 2009
Hi jimbob1971, the sound track is a Bomb The Base track called Megablast. It was used in the Bitmap Brothers shooter called Xenon II – Megablast on the Commodore Amiga amongst other platforms :o)
Mike
Remi on October 28th, 2009
Just needed to say thanks!
Saved me from fighting with openAL for at least a couple of days.
Thorsten on November 2nd, 2009
Hi Mike,
thank you for your great tutorials. They gave me a brilliant starting point after struggling with iPhone development about half a year!
But I also have a question: Using the SoundManager class my little app doesn’t play Sounds after receiving calls or timer events. Where can I start to find a solution for this?
Thank you,
Thorsten
mike on November 2nd, 2009
Hi Thorsten
I’m glad that the tutorials have been useful. There is a fix for your sound problem. It is to do with recognising when an interruption has occurred such as an alarm or phone call and then dealing with it correctly.
I am going to create a forum entry in the Sound Tutorial topic which shows how to fix the problem. I may even post my latest sound engine which already has the necessary changes.
Check back soon and the info should be there.
Mike
Joe on November 2nd, 2009
Mike,
I present a modalview to allow the user to select and play music. When I dismiss the controller touches in the game loop are no longer detected. Any ideas?
Thanks!
Andrew on December 8th, 2009
Hey Mike
Thanks for the sound engine in tutorial 9 its been an unbelievably great assest in making my game for my masters.
One thing i do ask though is how do i make the audio fade out and fade in rather than pressing start game and it just stops??
I will also say you sound engine works great alongside the cocos2d package nicely!! If you could help me with the question it would certainly add a nice touch to my game.
Thanks again and keep up the good work
Andrew :)
jeff951 on December 15th, 2009
Hi Mike
I am currently working on a iPhone game and have especially found your tutorials very helpful they go though everything very well. But I was wondering in regards to this tutorial here do you know if there is a issue with 3.1 as i have the code that you have and it does not work and when i tried to run your code in 3.1 it would not work either. Any help you can give on this would be great!
Jeffrey
jeff951 on December 15th, 2009
Hey figured out that issue i was testing sound effects on the simulator which does not work.OOps.
Now i am trying to set up scroll bars to change the volume of my background tracks i have on that has sound effects that loops and one the is music when i change the volume with that method it only changes the volume of the music not the other track. Any ideas?
RRdaS on December 31st, 2009
Hi Mike. Thank you for your great tutorials!
I didn’t realize when you changed the routines, but if you uncomment the line in EAGLView.m:
[playerShip1 renderAtPoint:CGPointMake(140, 240) centerOfImage:YES];
It will cause a program crash, and this line of code was working in the past.
In my understanding, using getSubImageAtPoint is a good way to have an independent “enemy” with the same texture.
Should I consider getSubImageAtPoint obsolete in your routines?
Thanks.
RRdaS
iphone developer on January 4th, 2010
Mike,
Awesome tutorial! Only thing is I tried applying this to my latest project which is a game that is similar to an old school pong but with a different theme (im going to keep it a secret for now until its release, ill keep you posted).
I had some trouble getting OpenAL sound but eventually figured it out. Definitely sounds better when the user has headphones on. I set it up so you can hear sound from different ears based on where the ball hits. Anyways, definitely good knowledge here!
Thanks.
Barry on January 8th, 2010
Hi Mike,
Thanks for the Sound Manager tutorial. Great stuff! I’m using a slightly older version of your Sound Manager. However, I need to be able to unload sounds when I no longer need them. I have about 100 64K .MP3 files, and I only need to play one of them at a time; but my app is crashing after I have loaded about 40 of them. I’m assuming that it’s a memory problem, so I wanted to call the “removeSoundWithKey:” method (which you provided in SM) before I load each new sound file; but that method causes the app to crash. I noticed that this method is no longer in your latest version of SM. For your quick reference, I’ve included the code for the “removeSoundWithKey” method (un-modified). Is there an easy fix for this method, or should I be using another approach (or a different file format)?
Please advise ASAP. Thank you very much for your help.
Barry
********************
- (void)removeSoundWithKey:(NSString*)aSoundKey {
// Find the buffer which has been linked to the sound key provided
NSNumber *numVal = [soundLibrary objectForKey:aSoundKey];
// If the key is not found log it and finish
if(numVal == nil) {
NSLog(@”WARNING – SoundManager: No sound with key ‘%@’ was found so cannot be removed”, aSoundKey);
return;
}
// Get the buffer number form the sound library so that the sound buffer can be released
NSUInteger bufferID = [numVal unsignedIntValue];
glDeleteBuffers(1, &bufferID);
[soundLibrary removeObjectForKey:aSoundKey];
if(DEBUG) NSLog(@”INFO – SoundManager: Removed sound with key ‘%@’”, aSoundKey);
}
Barry on January 8th, 2010
Hi Mike,
Well, sometimes the answer is so easy that you don’t even notice it. I’m now using the AVAudioPlayer method s.t. when I load sounds, only the file paths are loaded (not the entire sound file). Problem solved.
BTW: If you do have a quick fix/idea for the “removeSoundWithKey:” method, I’d still love to know it.
Thanks,
Barry
mike on January 8th, 2010
Hi Barry
I’ve checked my current sound manager code and its using exactly the same code you posted to remove a sound. I’m using it in SLQ to remove sounds when you leave the main game to same memory and its working ok. I also tested it when removing a sound key that does not exist and that works ok as well.
Do you have more info on the crash you are getting. Is it EXC_BAD_ACCESS for example?
Mike
Jonathan on January 10th, 2010
Hi, Mike (and other 71squared Sound people)
I also am having trouble with “removeSoundWithKey” … At first it seems to do what it is meant to do – it removes the sound and I can’t access it again unless I reload it. BUT, when I test my apps dealloc’s with ‘Instruments’ it doesn’t show any deallocation. It doesn’t seem to free up the memory.
My app is a level based app, which loads and removes sounds for each individual level. Since removing sounds doesn’t seem to be releasing the memory my app eventually crashes with this message:
————————————-
Program received signal: “0”.
warning: check_safe_call: could not restore current frame
kill
quit
————————————-
In a desperate attempt to find a solution, I have even tried using your “shutdownSoundManager”
…But I don’t know how to force it to start again.
Any ideas?
Thank you very much for your help.
Jonathan on January 10th, 2010
Hi, its’s Jonathan again.
I have created a ultra simple, striped back version of your sound engine. It has a view with a loadSound, playSound and removeSound button.
You can download it here :
http://www.tuistudios.com/iphoneDev/
If you run it through an objectAlloc Instrument, you should notice that it doesn’t seem to release what it loaded. This eventually crashes my app.
Am I doing something wrong?
Does your removeSoundwithKey have these problems?
(Note: I used a ‘unloadSoundWithKey’ instead of ‘removeSound’ because the later doesn’t stop a sound if it is looping – as in this case)
Thank again.
mike on January 10th, 2010
Hi Jonathan, I’m sat working on the book at the moment so I’ll check this out now and get back to you shortly :o)
The next chapter I’m starting is on the sound engine so this is interesting to me as well.
Mike
mike on January 10th, 2010
Hi Jonathan
OK, this is odd. There were problems in the removeSoundWithKey method. An error was reported every time alDeleteBuffers was called which meant the buffer was not being destroyed.
I have re-written that method so that it now unbinds the buffer from any source that have played it or are playing it. Unbinding like this causes the alDeleteBuffers to work. It checked out the OpenAL dev guide and it does say in there that a buffer will not be deleted if it is still bound to a source, so I should have spotted that before.
One other odd thing I came across is that if a sound is playing in a source, you can query the source for the buffer which is playing. If a sound has played in a source and finished, the query will give you 0, even though you still have to unbind the buffer from that source else the delete buffer will fail.
I got round this by unbinding the buffer from all sources not playing anything, and from any sources playing that do have that buffer number returned. That side of things looks to be working ok.
Having done all that, I still see an allocation increase when removing and then loading a sound in your test app.
I’m stumped a little now. If the OpenAL calls are now working, then I’m not sure why allocations are not being removed. It could be caching that OpenAL is doing and it will eventually remove the data, but I’ve not read anything that says it works that way.
I’ve put the new sound manager class up at the following link. Take a look and see if that helps with your crashing.
If anyone else has any comments feel free to jump in :o)
Jonathan on January 11th, 2010
HI, Mike
Thanks for the response.
Yes, unfortunately, it still crashes my app.
I have uploaded the test app with your new code in it – SoundEngine_02.zip
http://www.tuistudios.com/iphoneDev/
I will keep researching.
Does anyone else have sounds releasing ok?
Thanks
Jonathan on January 11th, 2010
Well, I have worked something out!?
I haven’t solved it but I have managed to ‘free’ the buffer for a single sound. Which proves that it indeed can be done. I can see it being dealloc’ed in ‘Instruments’.
The problem is I can’t get it to work for multiple sounds. It only seems to remove the buffer for the last sound loaded.
Here is what I did :
——————————————
Soundmanager.h –
ALvoid *bufferData;
Soundmanager.m –
I removed: ALvoid *data;
I changed everything “data” to “bufferData”
Then I added a timer to trigger the below removeSoundWithKey
…
alSourcei(currentSourceID, AL_BUFFER, 0);
NSTimer *freeBuf = [NSTimer timerWithTimeInterval:0.2 target:self
selector:@selector(freeBuffer) userInfo: nil repeats: NO];
[[NSRunLoop currentRunLoop] addTimer:freeBuf forMode:NSDefaultRunLoopMode];
…
-(void)freeBuffer {
free(bufferData);
}
—————————————–
So, yeah…please dont think I am posting this because I am proud of it. I am actually embarrassed at how crap this is. But I thought I would post it in case it could be a clue to you as to what is going on with the buffers.
“free(bufferData);” (or the “data” loaded in loadSoundWithKey) does actually work. But my crap set up only deletes the most recently loaded.
Cheers
Barry on January 12th, 2010
Hi Mike,
Sorry for my delayed response (I’ve been unable to do further testing until now).
Thanks for the updated “removeSoundWithKey” method. But for some reason, it still crashes my app after calling the routine 50 or 60 times. I’m assuming that memory is maxing out due to the sounds not being released (?)
When it crashes, the console displays the following error:
“Program received signal: “0″. warning: check_safe_call: could not restore current frame.”
Thanks,
Barry
mike on January 18th, 2010
Hi Barry, I managed to find the memory leak in the sound manager class :o)
You may have already found it, but if not the problem was in the loadSoundWithKey method. I was setting data to be populated with the sound data from the sound file using
data = MyGetOpenALAudioData(fileURL, &size, &format, &freq);
and then was never freeing that memory again. I needed to add
if (data)
free(data);
after performing the alBufferDataStaticProc and also before
data = NULL;
Doing this stops the allocations rising and frees the memory correctly.
I hope this helps you as I feel real dumb for not spotting it sooner.
Let me know how you get on.
Mike
Jonathan on January 19th, 2010
Thank you Mike for keeping us in the loop!
However, I still can’t seem to get it to work.
The “free (data):” seems to clear the buffers before I can play them – So when I go to play the sound, the app crashes.
I have uploaded that simple sound engine app again with your adjustments.
http://www.tuistudios.com/iphonedev
I really don’t want to take too much of your time but could you please check to see if I have free’d the data in the right spot. I can’t seem to work it out?
Or are you able to re-upload your Sound Engine.
Thank you in advance.
mike on January 19th, 2010
Hi Jonathan
I’ve downloaded your project and the free is in the right place. The sound engine works fine for me without making any changes at all, on both the sim and the real device. The music plays, I can then remove it, load it and play it again. The same sound engine is also working ok in SLQ with those changes.
The only other change I would make in the loadSoundWithKey method is to change alBufferDataStaticProc to alBufferData. It works ok in your project if you use either, but alBufferData means OpenAL is responsible for the buffer data once it is loaded, alBufferDataStaticProc means we are responsible for the data which unless you need to control memory very very closely is not needed and is more hassle than its worth. I made the mistake of using that in my original sound engine but found that info out since.
When your app crashes what error are you getting and where.
I’ve posted my current sound engine at http://www.71squared.com/iPhone/SoundClasses.zip
Mike
Jonathan on January 19th, 2010
Hi, Mike and thank you for your quick response.
Below are the steps in the crash of the app.
1- The app loads and everything is fine
2- I press “play” and there is no sound
3- The “play” button hangs on highlighted state.
4 -Then the iPhone simulator drops behind all open apps, making Xcode active.
5- there is no errors in the console.
6- The debugger shows the app stops on all the stuff to do with playing:
alSourcePlay (sourceID);
0×0003654a jmp 0×36557
0×0003654c mov (%edx),%eax
7- If I force it to continue I get a “EXC_BAD_ACCESS” error
Having said all that, I changed alBufferDataStaticProc to alBufferData, as you suggested.
Which does seem to make everything work. That’s great, but it does seem strange I can’t use
alBufferDataStaticProc and you can?
Thanks for your help!
Marsman on January 19th, 2010
I experienced the same results as Jonathan when implementing the free(data) code. Sound trigger button hangs in the hi-lighted state. Sound does not play.
I am using xCode 3.2.1
Barry on January 20th, 2010
Nice catch, Mike! The changes you suggested for the loadSoundWithKey method worked perfectly. Thanks so much!
Barry
mike on January 20th, 2010
Hi Marsman/Jonathan
Have you tried the sound manager with the free(data) and also having changed alBufferDataStaticProc to just alBufferData. The static version means you control the buffer data so freeing the data will free the buffer contents giving you all sorts of problems. alBufferData means OpenAL controls the buffer and has its own copy, letting you free up the data.
Let me know
Mike
Jonathan on January 21st, 2010
Hi, Mike.
Yes. The alBufferData works. Thank you very much.
Jonathan.
Kev on February 25th, 2010
I want to be able to break up the conversion of long mp3s and display % complete.
Is there a way to append more data to an existing buffer along the lines of (in alBufferDataStaticProc) doing something like this (but with some sort of append call instead of starting again)?
if (proc)
{
int chunkSize = 100;
int chunk = size / chunkSize;
int offset = 0;
for (int i=0; i<chunkSize; ++i)
{
/*
call to update graphics with % complete
*/
proc(bid, format, data + offset, chunk, freq);
offset += chunk;
}
}
Obviously this just leave the last 1% of the track in the buffer.
Thanks,
Kev
Kev on February 25th, 2010
As a follow up to my post above: I am finding that when trying to play long MP3s the delay in getting the sound ready results in problems with the touch interface and accelerometers. By breaking up the conversion it prevents these problems (presumably by letting the OS get a look in frequently enough).
PS. Briliant article!





bob on May 26th, 2009
@Mike,
Hi Mike, found a problem. The sound manager will play mono sounds but not stereo. I loaded your ‘laser sound’ into an editor and it is mono. I converted it to stereo and you get a lot of noise with the sound. I thought it was sound file but I don’t think it is. Any idea’s?