#include "SoundSystemSL.h" #include #include #include "../../util/Mth.h" #include "../../world/level/tile/Tile.h" #include "../../world/phys/Vec3.h" #include "../../client/sound/Sound.h" #include "../log.h" // Only one engine can be created at once. You CAN (if you really want) // start two games at once, then it will crash without objEngine being static. /*static*/ SLObjectItf SoundSystemSL::objEngine = 0; typedef struct t_context { SLObjectItf obj; Mutex* mutex; } t_context; Mutex SoundSystemSL::toRemoveMutex; std::vector SoundSystemSL::toRemove; SoundSystemSL::SoundSystemSL() : available(true), listener(NULL), numBuffersPlaying(0) { init(); } SoundSystemSL::~SoundSystemSL() { toRemoveMutex.unlock(); for (SoundList::iterator it = playingBuffers.begin(); it != playingBuffers.end(); ++it) (**it)->Destroy(*it); (*objOutput)->Destroy(objOutput); if (SoundSystemSL::objEngine != 0) { (*SoundSystemSL::objEngine)->Destroy(SoundSystemSL::objEngine); SoundSystemSL::objEngine = 0; } } void SoundSystemSL::init() { SoundSystemSL::toRemove.clear(); SoundSystemSL::toRemove.reserve(MAX_BUFFERS_PLAYING); toRemoveCopy.resize(MAX_BUFFERS_PLAYING); SLresult res; const int MAX_NUMBER_INTERFACES = 2; SLboolean required[MAX_NUMBER_INTERFACES]; SLInterfaceID iidArray[MAX_NUMBER_INTERFACES]; SLEngineOption EngineOption[] = {(SLuint32) SL_ENGINEOPTION_THREADSAFE, (SLuint32) SL_BOOLEAN_TRUE}; /* Create OpenSL ES (destroy first if needed)*/ if (SoundSystemSL::objEngine != 0) (*SoundSystemSL::objEngine)->Destroy(SoundSystemSL::objEngine); res = slCreateEngine( &SoundSystemSL::objEngine, 1, EngineOption, 0, NULL, NULL); checkErr(res); /* Realizing the SL Engine in synchronous mode. */ res = (*SoundSystemSL::objEngine)->Realize(SoundSystemSL::objEngine, SL_BOOLEAN_FALSE); if (checkErr(res)) { available = false; return; } (*SoundSystemSL::objEngine)->GetInterface(SoundSystemSL::objEngine, SL_IID_ENGINE, (void*)&engEngine); checkErr(res); /* Create Output Mix object to be used by player - no interfaces required */ res = (*engEngine)->CreateOutputMix(engEngine, &objOutput, 0, iidArray, required); checkErr(res); /* Realizing the Output Mix object in synchronous mode. */ res = (*objOutput)->Realize(objOutput, SL_BOOLEAN_FALSE); checkErr(res); } void SoundSystemSL::destroy() {} void SoundSystemSL::setListenerPos( float x, float y, float z ) { if (!listener) { listenerPos = Vec3(x, y, z); return; } SLVec3D pos = {(SLint32)(1000.0f * x), (SLint32)(1000.0f * y), (SLint32)(1000.0f * z)}; SLresult res = (*listener)->SetLocationCartesian(listener, &pos); checkErr(res); } void SoundSystemSL::setListenerAngle( float deg ) { if (!listener) return; SLresult res = (*listener)->SetOrientationAngles(listener, deg*1000.0f, 0, 0); checkErr(res); } void SoundSystemSL::playAt( const SoundDesc& sound, float x, float y, float z, float volume, float pitch ) { removeStoppedSounds(); if (numBuffersPlaying >= MAX_BUFFERS_PLAYING) return; /* Setup the data source structure for the player */ SLDataLocator_AndroidSimpleBufferQueue uri = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2}; SLDataFormat_PCM mime = { SL_DATAFORMAT_PCM, (SLuint32)sound.channels, (SLuint32)(sound.frameRate * 1000), (SLuint32)(sound.byteWidth << 3), (SLuint32)(sound.byteWidth << 3), sound.channels==1? SL_SPEAKER_FRONT_CENTER : SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, SL_BYTEORDER_LITTLEENDIAN }; SLDataSource audioSource = {&uri, &mime}; SLDataLocator_OutputMix locator_outputmix; SLDataSink audioSink; SLObjectItf player; SLPlayItf playItf; //SL3DLocationItf locationItf; SLVolumeItf volumeItf; /* Setup the data sink structure */ locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; locator_outputmix.outputMix = objOutput; audioSink.pLocator = (void *)&locator_outputmix; audioSink.pFormat = NULL; /* Buffer queue-able */ static SLboolean required[2]; static SLInterfaceID iidArray[2]; required[0] = SL_BOOLEAN_TRUE; iidArray[0] = SL_IID_BUFFERQUEUE; required[1] = SL_BOOLEAN_TRUE; iidArray[1] = SL_IID_VOLUME; /* Create the 3D player */ SLresult res = (*engEngine)->CreateAudioPlayer(engEngine, &player, &audioSource, &audioSink, 2, iidArray, required); //printf("SL: Created audio player\n"); checkErr(res); /* Realizing the player in synchronous mode. */ res = (*player)->Realize(player, SL_BOOLEAN_FALSE); //LOGI("SL: Realize audio player\n"); checkErr(res); /* Get the play and volume interfaces */ res = (*player)->GetInterface(player, SL_IID_PLAY, (void*)&playItf); //LOGI("SL: Get Player interface\n"); checkErr(res); res = (*player)->GetInterface(player, SL_IID_VOLUME, (void*)&volumeItf); //LOGI("SL: Get Player interface\n"); checkErr(res); SLmillibel maxVolume; res = (*volumeItf)->GetMaxVolumeLevel(volumeItf, &maxVolume); SLmillibel mbelVolume = maxVolume - 2000 * (1-volume);//Mth::lerp(SL_MILLIBEL_MIN, maxVolume, 0.95f + 0.05f*volume); LOGI("min: %d, max: %d, current: %d (%f)\n", SL_MILLIBEL_MIN, maxVolume, mbelVolume, volume); res = (*volumeItf)->SetVolumeLevel(volumeItf, mbelVolume); checkErr(res); SLAndroidSimpleBufferQueueItf buffer1; res = (*player)->GetInterface(player, SL_IID_BUFFERQUEUE, &buffer1); checkErr(res); //t_context* context = new t_context(); //{ player, &toRemoveMutex }; //context->obj = player; //context->mutex = &toRemoveMutex; res = (*buffer1)->RegisterCallback(buffer1, SoundSystemSL::removePlayer, (void*)player); checkErr(res); res = (*buffer1)->Enqueue(buffer1, sound.frames, sound.size); checkErr(res); /* Start playing the 3D source */ res = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PLAYING ); //LOGI("SL: Set play state\n"); checkErr(res); playingBuffers.push_back(player); ++numBuffersPlaying; } bool SoundSystemSL::checkErr( SLresult res ) { if ( res != SL_RESULT_SUCCESS ) { LOGI("OpenSL error: %d\n", res); return true; } return false; } /*static*/ void SoundSystemSL::removeStoppedSounds() { toRemoveMutex.lock(); const int numBuffersToRemove = toRemove.size(); for (int i = 0; i < numBuffersToRemove; ++i) toRemoveCopy[i] = toRemove[i]; SoundSystemSL::toRemove.clear(); toRemoveMutex.unlock(); for (int i = 0; i < numBuffersToRemove; ++i) { SLObjectItf obj = toRemoveCopy[i]; SoundList::iterator it = playingBuffers.begin(); while (it != playingBuffers.end()) { if (*it == obj) { playingBuffers.erase(it); break; } ++it; } (*obj)->Destroy(obj); --numBuffersPlaying; } } void SoundSystemSL::removePlayer( SLAndroidSimpleBufferQueueItf bq, void *context_ ) { //t_context* context = (t_context*) context_; //context->mutex->lock(); //SoundSystemSL::toRemove.push_back( context->obj ); //context->mutex->unlock(); //delete context; SoundSystemSL::toRemoveMutex.lock(); SoundSystemSL::toRemove.push_back( (SLObjectItf) context_ ); SoundSystemSL::toRemoveMutex.unlock(); }