/* * 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. */ /* EffectSend implementation */ #include "sles_allinclusive.h" /** \brief Maps AUX index to OutputMix interface index */ static const unsigned char AUX_to_MPH[AUX_MAX] = { MPH_ENVIRONMENTALREVERB, MPH_PRESETREVERB }; /** \brief This is a private function that validates the effect interface specified by the * application when it calls EnableEffectSend, IsEnabled, SetSendLevel, or GetSendLevel. * For the interface to be valid, it has to satisfy these requirements: * - object is an audio player (MIDI player is not supported yet) * - audio sink is an output mix * - interface was exposed at object creation time or by DynamicInterface::AddInterface * - interface was "gotten" with Object::GetInterface */ static struct EnableLevel *getEnableLevel(IEffectSend *this, const void *pAuxEffect) { // Make sure this effect send is on an audio player, not a MIDI player CAudioPlayer *audioPlayer = (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(this)) ? (CAudioPlayer *) this->mThis : NULL; if (NULL == audioPlayer) { return NULL; } // Get the output mix for this player COutputMix *outputMix = CAudioPlayer_GetOutputMix(audioPlayer); unsigned aux; if (pAuxEffect == &outputMix->mEnvironmentalReverb.mItf) { aux = AUX_ENVIRONMENTALREVERB; } else if (pAuxEffect == &outputMix->mPresetReverb.mItf) { aux = AUX_PRESETREVERB; } else { SL_LOGE("EffectSend on unknown aux effect %p", pAuxEffect); return NULL; } assert(aux < AUX_MAX); // Validate that the application has a valid interface for the effect. The interface must have // been exposed at object creation time or by DynamicInterface::AddInterface, and it also must // have been "gotten" with Object::GetInterface. unsigned MPH = AUX_to_MPH[aux]; int index = MPH_to_OutputMix[MPH]; if (0 > index) { SL_LOGE("EffectSend aux=%u MPH=%u", aux, MPH); return NULL; } unsigned mask = 1 << index; object_lock_shared(&outputMix->mObject); SLuint32 state = outputMix->mObject.mInterfaceStates[index]; mask &= outputMix->mObject.mGottenMask; object_unlock_shared(&outputMix->mObject); switch (state) { case INTERFACE_EXPOSED: case INTERFACE_ADDED: case INTERFACE_SUSPENDED: case INTERFACE_SUSPENDING: case INTERFACE_RESUMING_1: case INTERFACE_RESUMING_2: if (mask) { return &this->mEnableLevels[aux]; } SL_LOGE("EffectSend no GetInterface yet"); break; default: SL_LOGE("EffectSend invalid interface state %lu", state); break; } return NULL; } #if defined(ANDROID) && !defined(USE_BACKPORT) /** \brief This is a private function that translates an Android effect framework status code * to the SL ES result code used in the EnableEffectSend() function of the SLEffectSendItf * interface. */ static SLresult translateEnableFxSendError(android::status_t status) { switch (status) { case android::NO_ERROR: return SL_RESULT_SUCCESS; case android::INVALID_OPERATION: case android::BAD_VALUE: default: SL_LOGE("EffectSend status %u", status); return SL_RESULT_RESOURCE_ERROR; } } #endif static SLresult IEffectSend_EnableEffectSend(SLEffectSendItf self, const void *pAuxEffect, SLboolean enable, SLmillibel initialLevel) { SL_ENTER_INTERFACE if (!((SL_MILLIBEL_MIN <= initialLevel) && (initialLevel <= 0))) { result = SL_RESULT_PARAMETER_INVALID; } else { IEffectSend *this = (IEffectSend *) self; struct EnableLevel *enableLevel = getEnableLevel(this, pAuxEffect); if (NULL == enableLevel) { result = SL_RESULT_PARAMETER_INVALID; } else { interface_lock_exclusive(this); enableLevel->mEnable = SL_BOOLEAN_FALSE != enable; // normalize enableLevel->mSendLevel = initialLevel; #if !defined(ANDROID) || defined(USE_BACKPORT) result = SL_RESULT_SUCCESS; #else // TODO do not repeat querying of CAudioPlayer, done inside getEnableLevel() CAudioPlayer *ap = (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(this)) ? (CAudioPlayer *) this->mThis : NULL; // note that if this was a MIDI player, getEnableLevel would have returned NULL assert(NULL != ap); // check which effect the send is attached to, attach and set level COutputMix *outputMix = CAudioPlayer_GetOutputMix(ap); // the initial send level set here is the total energy on the aux bus, // so it must take into account the player volume level if (pAuxEffect == &outputMix->mPresetReverb.mItf) { result = translateEnableFxSendError(android_fxSend_attach(ap, (bool) enable, outputMix->mPresetReverb.mPresetReverbEffect, initialLevel + ap->mVolume.mLevel)); } else if (pAuxEffect == &outputMix->mEnvironmentalReverb.mItf) { result = translateEnableFxSendError(android_fxSend_attach(ap, (bool) enable, outputMix->mEnvironmentalReverb.mEnvironmentalReverbEffect, initialLevel + ap->mVolume.mLevel)); } else { SL_LOGE("EffectSend unknown aux effect %p", pAuxEffect); result = SL_RESULT_PARAMETER_INVALID; } #endif interface_unlock_exclusive(this); } } SL_LEAVE_INTERFACE } static SLresult IEffectSend_IsEnabled(SLEffectSendItf self, const void *pAuxEffect, SLboolean *pEnable) { SL_ENTER_INTERFACE if (NULL == pEnable) { result = SL_RESULT_PARAMETER_INVALID; } else { IEffectSend *this = (IEffectSend *) self; struct EnableLevel *enableLevel = getEnableLevel(this, pAuxEffect); if (NULL == enableLevel) { *pEnable = SL_BOOLEAN_FALSE; result = SL_RESULT_PARAMETER_INVALID; } else { interface_lock_shared(this); SLboolean enable = enableLevel->mEnable; interface_unlock_shared(this); *pEnable = enable; result = SL_RESULT_SUCCESS; } } SL_LEAVE_INTERFACE } static SLresult IEffectSend_SetDirectLevel(SLEffectSendItf self, SLmillibel directLevel) { SL_ENTER_INTERFACE if (!((SL_MILLIBEL_MIN <= directLevel) && (directLevel <= 0))) { result = SL_RESULT_PARAMETER_INVALID; } else { IEffectSend *this = (IEffectSend *) self; interface_lock_exclusive(this); CAudioPlayer *ap = (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(this)) ? (CAudioPlayer *) this->mThis : NULL; if (NULL != ap) { SLmillibel oldDirectLevel = ap->mDirectLevel; if (oldDirectLevel != directLevel) { ap->mDirectLevel = directLevel; #if defined(ANDROID) ap->mAmplFromDirectLevel = sles_to_android_amplification(directLevel); interface_unlock_exclusive_attributes(this, ATTR_GAIN); #else interface_unlock_exclusive(this); #endif } else { interface_unlock_exclusive(this); } } else { // MIDI player is silently not supported interface_unlock_exclusive(this); } result = SL_RESULT_SUCCESS; } SL_LEAVE_INTERFACE } static SLresult IEffectSend_GetDirectLevel(SLEffectSendItf self, SLmillibel *pDirectLevel) { SL_ENTER_INTERFACE if (NULL == pDirectLevel) { result = SL_RESULT_PARAMETER_INVALID; } else { IEffectSend *this = (IEffectSend *) self; interface_lock_shared(this); CAudioPlayer *ap = (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(this)) ? (CAudioPlayer *) this->mThis : NULL; if (NULL != ap) { *pDirectLevel = ap->mDirectLevel; } else { // MIDI player is silently not supported *pDirectLevel = 0; } interface_unlock_shared(this); result = SL_RESULT_SUCCESS; } SL_LEAVE_INTERFACE } static SLresult IEffectSend_SetSendLevel(SLEffectSendItf self, const void *pAuxEffect, SLmillibel sendLevel) { SL_ENTER_INTERFACE if (!((SL_MILLIBEL_MIN <= sendLevel) && (sendLevel <= 0))) { result = SL_RESULT_PARAMETER_INVALID; } else { IEffectSend *this = (IEffectSend *) self; struct EnableLevel *enableLevel = getEnableLevel(this, pAuxEffect); if (NULL == enableLevel) { result = SL_RESULT_PARAMETER_INVALID; } else { result = SL_RESULT_SUCCESS; // EnableEffectSend is exclusive, so this has to be also interface_lock_exclusive(this); enableLevel->mSendLevel = sendLevel; #if defined(ANDROID) && !defined(USE_BACKPORT) CAudioPlayer *ap = (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(this)) ? (CAudioPlayer *) this->mThis : NULL; if (NULL != ap) { // the send level set here is the total energy on the aux bus, so it must take // into account the player volume level result = android_fxSend_setSendLevel(ap, sendLevel + ap->mVolume.mLevel); } #endif interface_unlock_exclusive(this); } } SL_LEAVE_INTERFACE } static SLresult IEffectSend_GetSendLevel(SLEffectSendItf self, const void *pAuxEffect, SLmillibel *pSendLevel) { SL_ENTER_INTERFACE if (NULL == pSendLevel) { result = SL_RESULT_PARAMETER_INVALID; } else { IEffectSend *this = (IEffectSend *) self; struct EnableLevel *enableLevel = getEnableLevel(this, pAuxEffect); if (NULL == enableLevel) { result = SL_RESULT_PARAMETER_INVALID; } else { interface_lock_shared(this); SLmillibel sendLevel = enableLevel->mSendLevel; interface_unlock_shared(this); *pSendLevel = sendLevel; result = SL_RESULT_SUCCESS; } } SL_LEAVE_INTERFACE } static const struct SLEffectSendItf_ IEffectSend_Itf = { IEffectSend_EnableEffectSend, IEffectSend_IsEnabled, IEffectSend_SetDirectLevel, IEffectSend_GetDirectLevel, IEffectSend_SetSendLevel, IEffectSend_GetSendLevel }; void IEffectSend_init(void *self) { IEffectSend *this = (IEffectSend *) self; this->mItf = &IEffectSend_Itf; struct EnableLevel *enableLevel = this->mEnableLevels; unsigned aux; for (aux = 0; aux < AUX_MAX; ++aux, ++enableLevel) { enableLevel->mEnable = SL_BOOLEAN_FALSE; enableLevel->mSendLevel = SL_MILLIBEL_MIN; } }