| /* | |
| * 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 *thiz, const void *pAuxEffect) | |
| { | |
| // Make sure this effect send is on an audio player, not a MIDI player | |
| CAudioPlayer *audioPlayer = (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(thiz)) ? | |
| (CAudioPlayer *) thiz->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 &thiz->mEnableLevels[aux]; | |
| } | |
| SL_LOGE("EffectSend no GetInterface yet"); | |
| break; | |
| default: | |
| SL_LOGE("EffectSend invalid interface state %u", state); | |
| break; | |
| } | |
| return NULL; | |
| } | |
| #if defined(ANDROID) | |
| /** \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))) { | |
| // comparison (SL_MILLIBEL_MIN <= initialLevel) is always true due to range of SLmillibel | |
| if (!(initialLevel <= 0)) { | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } else { | |
| IEffectSend *thiz = (IEffectSend *) self; | |
| struct EnableLevel *enableLevel = getEnableLevel(thiz, pAuxEffect); | |
| if (NULL == enableLevel) { | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } else { | |
| interface_lock_exclusive(thiz); | |
| enableLevel->mEnable = SL_BOOLEAN_FALSE != enable; // normalize | |
| enableLevel->mSendLevel = initialLevel; | |
| #if !defined(ANDROID) | |
| result = SL_RESULT_SUCCESS; | |
| #else | |
| // TODO do not repeat querying of CAudioPlayer, done inside getEnableLevel() | |
| CAudioPlayer *ap = (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(thiz)) ? | |
| (CAudioPlayer *) thiz->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(thiz); | |
| } | |
| } | |
| 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 *thiz = (IEffectSend *) self; | |
| struct EnableLevel *enableLevel = getEnableLevel(thiz, pAuxEffect); | |
| if (NULL == enableLevel) { | |
| *pEnable = SL_BOOLEAN_FALSE; | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } else { | |
| interface_lock_shared(thiz); | |
| SLboolean enable = enableLevel->mEnable; | |
| interface_unlock_shared(thiz); | |
| *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))) { | |
| // comparison (SL_MILLIBEL_MIN <= directLevel) is always true due to range of SLmillibel | |
| if (!(directLevel <= 0)) { | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } else { | |
| IEffectSend *thiz = (IEffectSend *) self; | |
| interface_lock_exclusive(thiz); | |
| CAudioPlayer *ap = (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(thiz)) ? | |
| (CAudioPlayer *) thiz->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(thiz, ATTR_GAIN); | |
| #else | |
| interface_unlock_exclusive(thiz); | |
| #endif | |
| } else { | |
| interface_unlock_exclusive(thiz); | |
| } | |
| } else { | |
| // MIDI player is silently not supported | |
| interface_unlock_exclusive(thiz); | |
| } | |
| 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 *thiz = (IEffectSend *) self; | |
| interface_lock_shared(thiz); | |
| CAudioPlayer *ap = (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(thiz)) ? | |
| (CAudioPlayer *) thiz->mThis : NULL; | |
| if (NULL != ap) { | |
| *pDirectLevel = ap->mDirectLevel; | |
| } else { | |
| // MIDI player is silently not supported | |
| *pDirectLevel = 0; | |
| } | |
| interface_unlock_shared(thiz); | |
| 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))) { | |
| // comparison (SL_MILLIBEL_MIN <= sendLevel) is always true due to range of SLmillibel | |
| if (!(sendLevel <= 0)) { | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } else { | |
| IEffectSend *thiz = (IEffectSend *) self; | |
| struct EnableLevel *enableLevel = getEnableLevel(thiz, 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(thiz); | |
| enableLevel->mSendLevel = sendLevel; | |
| #if defined(ANDROID) | |
| CAudioPlayer *ap = (SL_OBJECTID_AUDIOPLAYER == InterfaceToObjectID(thiz)) ? | |
| (CAudioPlayer *) thiz->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(thiz); | |
| } | |
| } | |
| 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 *thiz = (IEffectSend *) self; | |
| struct EnableLevel *enableLevel = getEnableLevel(thiz, pAuxEffect); | |
| if (NULL == enableLevel) { | |
| result = SL_RESULT_PARAMETER_INVALID; | |
| } else { | |
| interface_lock_shared(thiz); | |
| SLmillibel sendLevel = enableLevel->mSendLevel; | |
| interface_unlock_shared(thiz); | |
| *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 *thiz = (IEffectSend *) self; | |
| thiz->mItf = &IEffectSend_Itf; | |
| struct EnableLevel *enableLevel = thiz->mEnableLevels; | |
| unsigned aux; | |
| for (aux = 0; aux < AUX_MAX; ++aux, ++enableLevel) { | |
| enableLevel->mEnable = SL_BOOLEAN_FALSE; | |
| enableLevel->mSendLevel = SL_MILLIBEL_MIN; | |
| } | |
| } |