I'm implementing audio playback into my game engine using OpenSL ES on Android. So far I'm supporting Android file descriptor data locators (AndroidFD) only. The problem is, that the audio playback won't always work if I'm using wrapper classes for OpenSL ES.
The wrappers:
AudioEngine.cpp
AudioEngine::AudioEngine()
: _engine(NULL),
_engineInterface(NULL),
_outputMix(NULL)
{
createEngine();
createOutputMix();
}
AudioEngine::~AudioEngine()
{
(*_outputMix)->Destroy(_outputMix);
(*_engine)->Destroy(_engine);
}
Sound* AudioEngine::loadSound(const String& filename) const
{
return NEW Sound(_engineInterface, _outputMix, filename);
}
// Private
void AudioEngine::createEngine()
{
Uint32 result = slCreateEngine(&_engine, 0u, NULL, 0u, NULL, NULL);
CHECK_OPENSLES_ERROR(result, "Platform::AudioEngine (OpenSLES)",
"Failed to create the engine");
result = (*_engine)->Realize(_engine, SL_BOOLEAN_FALSE);
CHECK_OPENSLES_ERROR(result, "Platform::AudioEngine (OpenSLES)",
"Failed to initialise the engine");
result = (*_engine)->GetInterface(_engine, SL_IID_ENGINE, &_engineInterface);
CHECK_OPENSLES_ERROR(result, "Platform::AudioEngine (OpenSLES)",
"Failed to get the engine interface");
}
void AudioEngine::createOutputMix()
{
Uint32 result = (*_engineInterface)->CreateOutputMix(_engineInterface, &_outputMix, 0u,
NULL, NULL);
CHECK_OPENSLES_ERROR(result, "Platform::AudioEngine (OpenSLES)",
"Failed to create the output mix");
result = (*_outputMix)->Realize(_outputMix, SL_BOOLEAN_FALSE);
CHECK_OPENSLES_ERROR(result, "Platform::AudioEngine (OpenSLES)",
"Failed to initialise the output mix");
}
Sound.cpp
// Public
Sound::Sound(SLEngineItf engineInterface, SLObjectItf outputMix, const String& filename)
: _engineInterface(engineInterface),
_outputMix(outputMix),
_player(NULL),
_playerInterface(NULL)
{
// Member (SLDataFormat_MIME)
_dataSourceFormat.formatType = SL_DATAFORMAT_MIME;
_dataSourceFormat.mimeType = NULL;
_dataSourceFormat.containerType = SL_CONTAINERTYPE_UNSPECIFIED;
// Member (SLDataLocator_AndroidFD)
_dataSourceLocator.locatorType = SL_DATALOCATOR_ANDROIDFD;
AAssetManager* assetManager = Application::assetManager();
AAsset* asset = AAssetManager_open(assetManager, filename.data(), AASSET_MODE_UNKNOWN);
_dataSourceLocator.fd = AAsset_openFileDescriptor(asset,
reinterpret_cast<off_t*>(&_dataSourceLocator.offset),
reinterpret_cast<off_t*>(&_dataSourceLocator.length));
AAsset_close(asset);
createPlayer();
}
Sound::~Sound()
{
(*_player)->Destroy(_player);
}
void Sound::play() const
{
const Uint32 result = (*_playerInterface)->SetPlayState(_playerInterface,
SL_PLAYSTATE_PLAYING);
CHECK_OPENSLES_ERROR(result, "Platform::Sound (OpenSLES)",
"Failed to set the play state");
}
// Private
void Sound::createPlayer()
{
SLDataSource dataSource =
{
&_dataSourceLocator,
&_dataSourceFormat
};
SLDataLocator_OutputMix outputMixLocator =
{
SL_DATALOCATOR_OUTPUTMIX,
_outputMix
};
SLDataSink dataSink =
{
&outputMixLocator,
NULL
};
Uint32 result = (*_engineInterface)->CreateAudioPlayer(_engineInterface, &_player,
&dataSource, &dataSink, 0u, NULL, NULL);
CHECK_OPENSLES_ERROR(result, "Platform::Sound (OpenSLES)",
"Failed to create a player");
result = (*_player)->Realize(_player, SL_BOOLEAN_FALSE);
CHECK_OPENSLES_ERROR(result, "Platform::Sound (OpenSLES)",
"Failed to initialise the player");
result = (*_player)->GetInterface(_player, SL_IID_PLAY, &_playerInterface);
CHECK_OPENSLES_ERROR(result, "Platform::SoundInstance (OpenSLES)",
"Failed to get the player interface");
}
Game.cpp
Game::initialise()
{
_audioEngine = NEW AudioEngine();
_sound = _audioEngine->loadSound("music.ogg");
_sound->play();
}
When the application starts, the sound won't always play. If I write all OpenSL ES code above inside Game::initialise(), the sound will always play. I can't figure out why the same code won't work inside the wrapper classes.
I ran the application on Samsung Galaxy S III (GT-I9305), Android 4.2.2, CyanogenMod 10.1.3-i9305