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