--- /dev/null
+/*
+ * 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;
+ }
+}