| /* | |
| * Copyright (C) 2010 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| /** \file CEngine.c Engine class */ | |
| #include "sles_allinclusive.h" | |
| #ifdef ANDROID | |
| #include <binder/IServiceManager.h> | |
| #include <utils/StrongPointer.h> | |
| #include <audiomanager/AudioManager.h> | |
| #include <audiomanager/IAudioManager.h> | |
| #endif | |
| /* This implementation supports at most one engine, identical for both OpenSL ES and OpenMAX AL */ | |
| CEngine *theOneTrueEngine = NULL; | |
| pthread_mutex_t theOneTrueMutex = PTHREAD_MUTEX_INITIALIZER; | |
| unsigned theOneTrueRefCount = 0; | |
| // incremented by slCreateEngine or xaCreateEngine, decremented by Object::Destroy on engine | |
| /** \brief Called by dlopen when .so is loaded */ | |
| __attribute__((constructor)) static void onDlOpen(void) | |
| { | |
| } | |
| /** \brief Called by dlclose when .so is unloaded */ | |
| __attribute__((destructor)) static void onDlClose(void) | |
| { | |
| // a memory barrier would be sufficient, but the mutex is easier | |
| (void) pthread_mutex_lock(&theOneTrueMutex); | |
| if ((NULL != theOneTrueEngine) || (0 < theOneTrueRefCount)) { | |
| SL_LOGE("Object::Destroy omitted for engine %p", theOneTrueEngine); | |
| } | |
| (void) pthread_mutex_unlock(&theOneTrueMutex); | |
| } | |
| /** \brief Hook called by Object::Realize when an engine is realized */ | |
| SLresult CEngine_Realize(void *self, SLboolean async) | |
| { | |
| CEngine *thiz = (CEngine *) self; | |
| SLresult result; | |
| #ifndef ANDROID | |
| // create the sync thread | |
| int err = pthread_create(&thiz->mSyncThread, (const pthread_attr_t *) NULL, sync_start, thiz); | |
| result = err_to_result(err); | |
| if (SL_RESULT_SUCCESS != result) | |
| return result; | |
| #endif | |
| // initialize the thread pool for asynchronous operations | |
| result = ThreadPool_init(&thiz->mThreadPool, 0, 0); | |
| if (SL_RESULT_SUCCESS != result) { | |
| thiz->mEngine.mShutdown = SL_BOOLEAN_TRUE; | |
| (void) pthread_join(thiz->mSyncThread, (void **) NULL); | |
| return result; | |
| } | |
| #ifdef ANDROID | |
| // use checkService() to avoid blocking if audio service is not up yet | |
| android::sp<android::IBinder> binder = | |
| android::defaultServiceManager()->checkService(android::String16("audio")); | |
| if (binder == 0) { | |
| SL_LOGE("CEngine_Realize: binding to audio service failed, service up?"); | |
| } else { | |
| thiz->mAudioManager = android::interface_cast<android::IAudioManager>(binder); | |
| } | |
| #endif | |
| #ifdef USE_SDL | |
| SDL_open(&thiz->mEngine); | |
| #endif | |
| return SL_RESULT_SUCCESS; | |
| } | |
| /** \brief Hook called by Object::Resume when an engine is resumed */ | |
| SLresult CEngine_Resume(void *self, SLboolean async) | |
| { | |
| return SL_RESULT_SUCCESS; | |
| } | |
| /** \brief Hook called by Object::Destroy when an engine is destroyed */ | |
| void CEngine_Destroy(void *self) | |
| { | |
| CEngine *thiz = (CEngine *) self; | |
| // Verify that there are no extant objects | |
| unsigned instanceCount = thiz->mEngine.mInstanceCount; | |
| unsigned instanceMask = thiz->mEngine.mInstanceMask; | |
| if ((0 < instanceCount) || (0 != instanceMask)) { | |
| SL_LOGE("Object::Destroy(%p) for engine ignored; %u total active objects", | |
| thiz, instanceCount); | |
| while (0 != instanceMask) { | |
| unsigned i = ctz(instanceMask); | |
| assert(MAX_INSTANCE > i); | |
| SL_LOGE("Object::Destroy(%p) for engine ignored; active object ID %u at %p", | |
| thiz, i + 1, thiz->mEngine.mInstances[i]); | |
| instanceMask &= ~(1 << i); | |
| } | |
| } | |
| // If engine was created but not realized, there will be no sync thread yet | |
| pthread_t zero; | |
| memset(&zero, 0, sizeof(pthread_t)); | |
| if (0 != memcmp(&zero, &thiz->mSyncThread, sizeof(pthread_t))) { | |
| // Announce to the sync thread that engine is shutting down; it polls so should see it soon | |
| thiz->mEngine.mShutdown = SL_BOOLEAN_TRUE; | |
| // Wait for the sync thread to acknowledge the shutdown | |
| while (!thiz->mEngine.mShutdownAck) { | |
| object_cond_wait(&thiz->mObject); | |
| } | |
| // The sync thread should have exited by now, so collect it by joining | |
| (void) pthread_join(thiz->mSyncThread, (void **) NULL); | |
| } | |
| // Shutdown the thread pool used for asynchronous operations (there should not be any) | |
| ThreadPool_deinit(&thiz->mThreadPool); | |
| #if defined(ANDROID) | |
| if (thiz->mAudioManager != 0) { | |
| thiz->mAudioManager.clear(); | |
| } | |
| // free equalizer preset names | |
| if (NULL != thiz->mEqPresetNames) { | |
| for (unsigned i = 0; i < thiz->mEqNumPresets; ++i) { | |
| if (NULL != thiz->mEqPresetNames[i]) { | |
| delete[] thiz->mEqPresetNames[i]; | |
| thiz->mEqPresetNames[i] = NULL; | |
| } | |
| } | |
| delete[] thiz->mEqPresetNames; | |
| thiz->mEqPresetNames = NULL; | |
| } | |
| thiz->mEqNumPresets = 0; | |
| #endif | |
| #ifdef USE_SDL | |
| SDL_close(); | |
| #endif | |
| } | |
| /** \brief Hook called by Object::Destroy before an engine is about to be destroyed */ | |
| predestroy_t CEngine_PreDestroy(void *self) | |
| { | |
| predestroy_t ret; | |
| (void) pthread_mutex_lock(&theOneTrueMutex); | |
| assert(self == theOneTrueEngine); | |
| switch (theOneTrueRefCount) { | |
| case 0: | |
| assert(false); | |
| ret = predestroy_error; | |
| break; | |
| case 1: | |
| ret = predestroy_ok; | |
| break; | |
| default: | |
| --theOneTrueRefCount; | |
| ret = predestroy_again; | |
| break; | |
| } | |
| (void) pthread_mutex_unlock(&theOneTrueMutex); | |
| return ret; | |
| } | |
| /** \brief Called by IObject::Destroy after engine is destroyed. The parameter refers to the | |
| * previous engine, which is now undefined memory. | |
| */ | |
| void CEngine_Destroyed(CEngine *self) | |
| { | |
| int ok; | |
| ok = pthread_mutex_lock(&theOneTrueMutex); | |
| assert(0 == ok); | |
| assert(self == theOneTrueEngine); | |
| theOneTrueEngine = NULL; | |
| assert(1 == theOneTrueRefCount); | |
| theOneTrueRefCount = 0; | |
| ok = pthread_mutex_unlock(&theOneTrueMutex); | |
| assert(0 == ok); | |
| } |