Sunday, April 5, 2009

Sound state (playing, looping) support in SoundEngine.cpp

I have recently compared AVAudioPlayer with SoundEngine with the verdict that short of using AudioQueue, SoundEngine.cpp was better, at least for now.

The problem I faced with it is that it doesn't support query of the sound's states. This is not crucial, but because a game I was working on as a lot of objects falling at the same time, I wanted sounds to play simultaneously. I found it difficult to do if I couldn't find out if a sound is playing to start with.

I saw the same question poping up on some forums, with the usual answer being "use AVAudioPlayer", which is kind of a daft answer. So here is the simple changes I made to the original SoundEngine.h and SoundEngine.cpp to add support for the states.

Add this to the end of SoundEngine.h :

bool  SoundEngine_EffectIsPlaying(UInt32 inEffectID);
bool SoundEngine_EffectIsLooping(UInt32 inEffectID);

This in SoundEngine.cpp, in the SoundEngineEffect class :

ALenum GetState()
{
ALenum state;
alGetSourcei(mSourceID, AL_SOURCE_STATE, &state);
return state;
}
Add this in the same file, but in the OpenALObject class

bool EffectIsPlaying(UInt32 inEffectID)
{
SoundEngineEffect *theEffect = mEffectsMap->Get(inEffectID);
return (theEffect) ? (theEffect->GetState() == AL_PLAYING) : kSoundEngineErrInvalidID;
}
bool EffectIsLooping(UInt32 inEffectID)
{
SoundEngineEffect *theEffect = mEffectsMap->Get(inEffectID);
return (theEffect) ? (theEffect->GetState() == AL_LOOPING) : kSoundEngineErrInvalidID;
}
This is pretty self explanatory. GetState returns other values, like AL_PAUSED or AL_STOPPED, so the same method could be used for the whole range of states.

And finally, in the same file (SoundEngine.cpp), near the end where the Extern stuff is declared :

extern "C"
OSStatus SoundEngine_SetMaxDistance(Float32 inValue)
{
return (sOpenALObject) ? sOpenALObject->SetMaxDistance(inValue) : kSoundEngineErrUnitialized;
}

extern "C"
OSStatus SoundEngine_SetReferenceDistance(Float32 inValue)
{
return (sOpenALObject) ? sOpenALObject->SetReferenceDistance(inValue) : kSoundEngineErrUnitialized;
}

You can then use SoundEngine_EffectIsPlaying() and SoundEngine_EffectIsLooping() to query the sound's states.

Saturday, April 4, 2009

AVAudioPlayer vs SoundEngine.cpp

I have recently converted a iPhone game from SoundEngine.cpp to AVAudioPlayer, and seeing as the subject of one method over the other doesn't seem to be discussed much, here is my take on using either method.

First off, I have to say that aftee the conversion, I went back to SoundEngine.cpp... I've read some suggestions that long sounds (MP3, AAC) should be played with AVAudioPlayer, and short one should go to SoundEngine.cpp (or OpenAL),and this seems correct.

The real advantage of SoundEngine.cpp is that you can dynamically change the pitch/frequency/etc of the sound, allowing to "randomize the playback" and thus reducing sound repetitiveness for the player. This cannot be done in AVAudioPlayer, but on the other hand, you can query the sound to know if it is playing, which is oddly missing from SoundEngine.cpp, and you can even monitor sounds through delegate, which could be useful.

(See here for a solution for SoundEngine.cpp's playback tracking)

So why did I switch to AVAudioPlayer in the first place then? Because Apple doesn't support SoundEngine.cpp, as it's part of a demo and not part of the SDK itself. Unfortunately, even the "This Must Be Solid, It's An Official API" feeling of AVAudioPlayer, I find that SoundEngine gives better results, and it is more customizable.