New SoundManager Class

Just as with the bitmap font class, I’ve also been making changes to the SoundManager class. This new version has fixes which means that is plays nicely with other sounds on the iPhone. Previously if a phone call came in or an alarm went off whilst the sound engine was being used, on returning to the game the sound would have stopped.

This is now fixed the the sounds will continue to play normally.

I have changed the API a little for playing sounds now as well. It is now possible to specify a sound source in which you would like the sound played. If you have a sound that you want to play over and over again but not simultaniously, then you can specify a single sound source for the sound. Providing -1 as the sourceID causes the sound engine to simply look for the next available sound source.

The other change to the API is that you no longer need to specify the frequency of the sound file you are playing. I have taken the OpenAL support functions that Apple provide in their samples and I’m using that to identify the type of sound file and frequency rather than hard code it.

This makes loading sounds a lot easier as you don’t need to worry about the type i.e. Stereo or Mono or the frequency, the Sound Manager does that for you.

The last thing that is now working well is the 3D sound. You can update the listeners location using the code below

[sharedSoundManager setListenerPosition:Vector2fMake(pixelPositionX, pixelPositionY)];

and set the location of a sound source when playing a sound using

[sharedSoundManager playSoundWithKey:@"closeDoor" gain:1.0f pitch:1.0f location:Vector2fMake(position.x, position.y) shouldLoop:NO sourceID:-1];

You will see in the code above that I’m using -1 for the sourceID which means the sound manager will look for the next available sound source. The location of the sound source is specified in location where I am making a Vector2f using the current position of the sound within the game environment.

To load a sound into the engine, the code needed is

[sharedSoundManager loadSoundWithKey:@"closeDoor" fileName:@"close_door" fileExt:@"wav"];

No need for the frequency :o)
Again, you should be able to replace the old Sound Manager with this new one by dropping it into your project and making the small changes to any code you have that uses the old SoundManager API.

I hope these changes are useful and let me know if you have any questions or comments.

SoundManager Classes »

Mike

Share:
  • Digg
  • del.icio.us
  • Facebook
  • MySpace
  • Reddit
  • StumbleUpon
  • Technorati
  • TwitThis
  • Design Float
  • DZone
  • email
  • Google Bookmarks
  • LinkedIn
  • Scoopeo
  • Tumblr

18 Comments

Anonymous  on August 15th, 2009

Hi there,

i have tried to build the tutorial9 under IphoneOS2.0 . It seems that Xcode doesn’t reconize the AVfoundation stuff… I can build it under 2.2.1 tough.

Is there anyway to overcome this problem under 2.0?

Thanks.

mike  on August 15th, 2009

The AVFoundation framework was not added until 2.2.1 I believe hence the problems you are having. The only way to fix this is to not use any of the AVFoundation classes in the SoundManager.

The straight OpenAL side of things should work fine, so it should just be the background music code that would need to be removed which makes use of the AVAudioPlayer.

Mike

bob  on August 16th, 2009

@Mike
The new sound manager works great! My bullets now shift from one ear to the other, Bomb drops sound like they are falling. Really Nice!!
one note to others be sure you do a Clean All before compiling do it once for the simulator and once for the device.

I had the problem where it worked in the simulator and not on the device. once I did a Clean All for both it worked fine..

mike  on August 16th, 2009

Hi bob, that’s excellent. I’m glad its working well and it sounds as though the 3D sound is working well for your game.

I’m looking forward to adding it to the list of Reader Games :o)

Mike

Dave  on August 18th, 2009

Hey Mike

First let me say that your tutorials are great stuff! I’ve found them very helpful.

I’ve come across something which you may want to incorporate into the sound manager – at the moment, you always set the audio session category to kAudioSessionCategory_AmbientSound, so music is decoded in software. There’s quite a nice performance increase if you do it in hardware, by setting kAudioSessionCategory_SoloAmbientSound.

Threads here: http://www.idevgames.com/forum.....p?p=152299
and here: http://www.idevgames.com/forum.....hp?t=16048

CC  on August 19th, 2009

Thanks for the update to the SoundManager. I went ahead and added a stopSoundWithKey: method, since I’m going to need it for my purposes. For what it’s worth, here’s how I implemented it (anything I’m missing?):

- (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];

}

I also added an unloadSoundWithKey: method:

- (void)unloadSoundWithKey:(NSString *)theSoundKey {

//
[self stopSoundWithKey:theSoundKey];

// Get the buffer ID for the sound key
NSNumber *numVal = [soundLibrary objectForKey:theSoundKey];
if(numVal == nil) return;
NSUInteger bufferID = [numVal unsignedIntValue];

// Delete the OpenAL buffer
ALenum error;
alDeleteBuffers(1, &bufferID);

}

Also, I am seeing memory leaks with this SoundManager object. I’m still a bit unsure about how to track down memory leaks, but it appears to me that the leaks are happening down in the guts of OpenAL, not in the SoundManager object. Or am I missing something?

CC  on August 19th, 2009

Thanks for posting this; it’s really helpful to have examples of OpenAL to work from. By looking at your code and your tutorial, I was able to add my own stopSoundWithKey: and unloadSoundWithKey: methods to the SoundManager with little trouble.

One problem, though, is that when I use the sound manager, I see memory leaks. The leaks are happening somewhere under initOpenAL and loadSoundWithKey:. Any idea what’s happening, or how I could plug the leaks? I’m almost certain they have nothing to do with my additions, since the leaks happen even when I only load and play a single sound; my code doesn’t get called.

mike  on August 21st, 2009

I have uploaded a new version of the SoundManager class. I’ve not had a chance to add the stopSoundWithKey code, but you can now remove a sound or music from the sound manager if they are no longer needed.

@CC, thanks for your stop code. That is a much easier approach than I had in mind. I’ll have to check performance if you stop a sound in the middle of a game as querying in OpenAL like in OpenGL does appear to eat cycles. With a max of 32 sound sources I should not think it will be too much of a problem.

@Dave, thanks for the info. I had seen info on the difference between SoloAmbientSound and AmbientSound before but it had always looked as though it was just decoding compressed sound, such as an MP3, through hardware and that it would not make much difference to OpenAL. At the moment the sound manager will allow your iPod music to continue to play when you start the game. If you switch to solo it will stop music when you start the game as its taking the hardware for itself.

What I think I’ll do is allow the user to decide if they want to have their iPod music stop and hear the in game music or not. With 3.0 it would even be cool to provide the user with in game options for choosing music to listen too.

Thanks for the comments guys. Its this kind of info that keeps me learning and the blog being useful. Its much appreciated.

Mike

Dave  on August 21st, 2009

@Mike

Yes, the difference is for compressed sound, so it only applies to the music, not OpenAL. However changing to the hardware decoding for music made quite a difference to performance for me when playing music.

The approach I’ve adopted is to check at startup whether or not the ipod is currently playing audio – if it isn’t, I use AmbientSound and set a music mute flag, so that the user’s ipod music continues to play. If the user then unmutes the music in the game menu, the category is changed to SoloAmbientSound and the ipod music stops.
If not, I use SoloAmbientSound and my game’s music plays from the start.

CC  on August 21st, 2009

@Mike, glad to help.

What about the memory leak I mentioned? Do you see it, too?

You know, I’ve been learning Objective-C, Cocoa, and the iPhone SDK for a good couple of months now, and everything has come together swimmingly for me, with the sole exception of audio (so far, knock on wood). It seems like no matter what I try, I run into problems, either unacceptable latency or memory leaks or restrictions on length of audio.

So, I appreciate you putting up your work and sharing it. I think if we can squash this memory leak, I’ll have a final, workable solution. Thanks.

mike  on August 21st, 2009

Hi CC, I cant say I’ve seen any problems with memory leaks using the SoundManager class. That said, I’ve not been looking at leaks lately as I’ve been working performance issues with the iPhone 3G. iPod 2nd Gen, iPhone 3Gs fantastic, 60fps, not a blip. iPhone 3G with the same code and its been running like a dog :o(

Anyway, enough about that, I’ll take a look in instruments when I get home later to see if I am getting any leaks as well.

What size/length of sounds are you using in OpenAL and for music. I’m only loading up a few small wav files at the moment which could be why I am not seeing any leaks. I’m also not releasing any sounds at the moment either.

Let me know what you are doing and I’ll check it out my end.

Mike

CC  on August 21st, 2009

In removeSoundWithKey:, you have:

glDeleteBuffers(1, &bufferID);

I think that should be:

alDeleteBuffers(1, &bufferID);

Regarding the memory leak, I just created a clean project and added the SoundManager class to it. By merely calling:

SoundManager *sm = [SoundManager sharedSoundManager];

…without loading or playing any sounds, I get two memory leaks.

Both are apparently leaked when we call alcMakeContextCurrent in initOpenAL. I don’t see anything wrong with that, but then, I am completely new to OpenAL.

If I then try to load a sound, I end up with 10 *more* leaks.

mike  on August 21st, 2009

@CC, ouch. Its well known that there are leaks deep down inside apples own frameworks that we can’t do much about and it could be we have found some. I will though check this out tonight and see if anything is related to how I’m doing things.

Thanks for the info.

Mike

mike  on August 22nd, 2009

@CC, I ran my game through instruments last night. I saw a couple of leaks right at the start which looked to be down inside the frameworks themselves, but after than there were no leaks reported and object allocation was stable.

If you want to take a screen shot of instruments showing the leaks and email them to me or if there is code you can share I’ll take a look.

Mike

Early  on August 27th, 2009

Hi Mike, I don’t know that I am understanding the sourceID completely.

everything works fine when I use -1 but if I try assigning a source id I don’t get any sound.

if(_player.moving == YES){
	[_sharedSoundManager playSoundWithKey:@"Walking" gain:2.0f pitch:1.0f location:Vector2fMake(_player.tileX * 50, _player.tileY * 50) shouldLoop:NO sourceID:5];
}

is there something else I have to do to assign a sourceID?

A Person  on August 29th, 2009

Sorry about that i hit enter and it sent it.

@Anyone who knows trigrometery well

With my Render Manager complete i have been working on a custom rotate function with some google research i found this

NewX = x*cos(angle) - y*sin(angle)
NewY = x*sin(angle) + y*cos(angle)

x = NewX
y = NewY

i have tried this with my own version however it doesent seem to work

basically when it draws a triangle appears at (0, 240) and some coordinates are negative

here is the code

float verts[8] = {vertices[0].tl_x, vertices[0].tl_y, vertices[0].tr_x, vertices[0].tr_y, vertices[0].bl_x, vertices[0].bl_y, vertices[0].br_x, vertices[0].br_y};
float destination[8];
destination[0] = verts[0]*cos(45) - verts[1]*sin(45);
destination[1] = verts[0]*sin(45) + verts[1]*cos(45);
destination[2] = verts[2]*cos(45) - verts[3]*sin(45);
destination[3] = verts[2]*sin(45) + verts[3]*cos(45);
destination[4] = verts[4];
destination[5] = verts[5];
destination[6] = verts[6]*cos(45) - verts[7]*sin(45);
destination[7] = verts[6]*sin(45) + verts[7]*cos(45);

I know it is inefficent right now but i’ll fix that later this is just a test

I also know mike has a custom opengl stack thing but i don’t want to figure out how to change it to except Quad2*

CC  on September 1st, 2009

Howdy. I was just wondering if you were planning on updating the Sound Manager with stopSound and removeSound functionality. I’m still struggling off and on with audio issues, and I’m sure others are, as well, given how much confusion there is out there about iPhone audio.

(Also, just a suggestion, it would be helpful to include in your downloadable package the support files the SoundManager references (OpenAL support and Singleton synthesis) for people who just want the Sound Manager stuff, especially since there are more than one similarly-named OpenAL support files floating around.)

Thanks again for sharing your work. I wouldn’t be as far along as I am without it.

Sound helpers at Under The Bridge  on October 21st, 2009

[...] New SoundManager Class is an OpenAL wrapper that apparently has 3D sound working well — which we actually haven’t encountered a need for yet, but as soon as we do run into something like that, this is definitely what we’ll start with! [...]

Leave a Comment