/* * 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. */ /* Equalizer implementation */ #include "sles_allinclusive.h" #ifdef ANDROID #include <system/audio_effects/effect_equalizer.h> #endif #define MAX_EQ_PRESETS 3 #if !defined(ANDROID) static const struct EqualizerBand EqualizerBands[MAX_EQ_BANDS] = { {1000, 1500, 2000}, {2000, 3000, 4000}, {4000, 5500, 7000}, {7000, 8000, 9000} }; static const struct EqualizerPreset { const char *mName; SLmillibel mLevels[MAX_EQ_BANDS]; } EqualizerPresets[MAX_EQ_PRESETS] = { {"Default", {0, 0, 0, 0}}, {"Bass", {500, 200, 100, 0}}, {"Treble", {0, 100, 200, 500}} }; #endif #if defined(ANDROID) /** * returns true if this interface is not associated with an initialized Equalizer effect */ static inline bool NO_EQ(IEqualizer* v) { return (v->mEqEffect == 0); } #endif static SLresult IEqualizer_SetEnabled(SLEqualizerItf self, SLboolean enabled) { SL_ENTER_INTERFACE IEqualizer *thiz = (IEqualizer *) self; interface_lock_exclusive(thiz); thiz->mEnabled = (SLboolean) enabled; #if !defined(ANDROID) result = SL_RESULT_SUCCESS; #else if (NO_EQ(thiz)) { result = SL_RESULT_CONTROL_LOST; } else { android::status_t status = thiz->mEqEffect->setEnabled((bool) thiz->mEnabled); result = android_fx_statusToResult(status); } #endif interface_unlock_exclusive(thiz); SL_LEAVE_INTERFACE } static SLresult IEqualizer_IsEnabled(SLEqualizerItf self, SLboolean *pEnabled) { SL_ENTER_INTERFACE if (NULL == pEnabled) { result = SL_RESULT_PARAMETER_INVALID; } else { IEqualizer *thiz = (IEqualizer *) self; interface_lock_exclusive(thiz); #if !defined(ANDROID) SLboolean enabled = thiz->mEnabled; *pEnabled = enabled; result = SL_RESULT_SUCCESS; #else if (NO_EQ(thiz)) { result = SL_RESULT_CONTROL_LOST; } else { *pEnabled = (SLboolean) thiz->mEqEffect->getEnabled(); result = SL_RESULT_SUCCESS; } #endif interface_unlock_exclusive(thiz); } SL_LEAVE_INTERFACE } static SLresult IEqualizer_GetNumberOfBands(SLEqualizerItf self, SLuint16 *pNumBands) { SL_ENTER_INTERFACE if (NULL == pNumBands) { result = SL_RESULT_PARAMETER_INVALID; } else { IEqualizer *thiz = (IEqualizer *) self; // Note: no lock, but OK because it is const *pNumBands = thiz->mNumBands; result = SL_RESULT_SUCCESS; } SL_LEAVE_INTERFACE } static SLresult IEqualizer_GetBandLevelRange(SLEqualizerItf self, SLmillibel *pMin, SLmillibel *pMax) { SL_ENTER_INTERFACE if (NULL == pMin && NULL == pMax) { result = SL_RESULT_PARAMETER_INVALID; } else { IEqualizer *thiz = (IEqualizer *) self; // Note: no lock, but OK because it is const if (NULL != pMin) *pMin = thiz->mBandLevelRangeMin; if (NULL != pMax) *pMax = thiz->mBandLevelRangeMax; result = SL_RESULT_SUCCESS; } SL_LEAVE_INTERFACE } static SLresult IEqualizer_SetBandLevel(SLEqualizerItf self, SLuint16 band, SLmillibel level) { SL_ENTER_INTERFACE IEqualizer *thiz = (IEqualizer *) self; if (!(thiz->mBandLevelRangeMin <= level && level <= thiz->mBandLevelRangeMax) || (band >= thiz->mNumBands)) { result = SL_RESULT_PARAMETER_INVALID; } else { interface_lock_exclusive(thiz); #if !defined(ANDROID) thiz->mLevels[band] = level; thiz->mPreset = SL_EQUALIZER_UNDEFINED; result = SL_RESULT_SUCCESS; #else if (NO_EQ(thiz)) { result = SL_RESULT_CONTROL_LOST; } else { android::status_t status = android_eq_setParam(thiz->mEqEffect, EQ_PARAM_BAND_LEVEL, band, &level); result = android_fx_statusToResult(status); } #endif interface_unlock_exclusive(thiz); } SL_LEAVE_INTERFACE } static SLresult IEqualizer_GetBandLevel(SLEqualizerItf self, SLuint16 band, SLmillibel *pLevel) { SL_ENTER_INTERFACE if (NULL == pLevel) { result = SL_RESULT_PARAMETER_INVALID; } else { IEqualizer *thiz = (IEqualizer *) self; // const, no lock needed if (band >= thiz->mNumBands) { result = SL_RESULT_PARAMETER_INVALID; } else { SLmillibel level = 0; interface_lock_shared(thiz); #if !defined(ANDROID) level = thiz->mLevels[band]; result = SL_RESULT_SUCCESS; #else if (NO_EQ(thiz)) { result = SL_RESULT_CONTROL_LOST; } else { android::status_t status = android_eq_getParam(thiz->mEqEffect, EQ_PARAM_BAND_LEVEL, band, &level); result = android_fx_statusToResult(status); } #endif interface_unlock_shared(thiz); *pLevel = level; } } SL_LEAVE_INTERFACE } static SLresult IEqualizer_GetCenterFreq(SLEqualizerItf self, SLuint16 band, SLmilliHertz *pCenter) { SL_ENTER_INTERFACE if (NULL == pCenter) { result = SL_RESULT_PARAMETER_INVALID; } else { IEqualizer *thiz = (IEqualizer *) self; if (band >= thiz->mNumBands) { result = SL_RESULT_PARAMETER_INVALID; } else { #if !defined(ANDROID) // Note: no lock, but OK because it is const *pCenter = thiz->mBands[band].mCenter; result = SL_RESULT_SUCCESS; #else SLmilliHertz center = 0; interface_lock_shared(thiz); if (NO_EQ(thiz)) { result = SL_RESULT_CONTROL_LOST; } else { android::status_t status = android_eq_getParam(thiz->mEqEffect, EQ_PARAM_CENTER_FREQ, band, ¢er); result = android_fx_statusToResult(status); } interface_unlock_shared(thiz); *pCenter = center; #endif } } SL_LEAVE_INTERFACE } static SLresult IEqualizer_GetBandFreqRange(SLEqualizerItf self, SLuint16 band, SLmilliHertz *pMin, SLmilliHertz *pMax) { SL_ENTER_INTERFACE if (NULL == pMin && NULL == pMax) { result = SL_RESULT_PARAMETER_INVALID; } else { IEqualizer *thiz = (IEqualizer *) self; if (band >= thiz->mNumBands) { result = SL_RESULT_PARAMETER_INVALID; } else { #if !defined(ANDROID) // Note: no lock, but OK because it is const if (NULL != pMin) *pMin = thiz->mBands[band].mMin; if (NULL != pMax) *pMax = thiz->mBands[band].mMax; result = SL_RESULT_SUCCESS; #else SLmilliHertz range[2] = {0, 0}; // SLmilliHertz is SLuint32 interface_lock_shared(thiz); if (NO_EQ(thiz)) { result = SL_RESULT_CONTROL_LOST; } else { android::status_t status = android_eq_getParam(thiz->mEqEffect, EQ_PARAM_BAND_FREQ_RANGE, band, range); result = android_fx_statusToResult(status); } interface_unlock_shared(thiz); if (NULL != pMin) { *pMin = range[0]; } if (NULL != pMax) { *pMax = range[1]; } #endif } } SL_LEAVE_INTERFACE } static SLresult IEqualizer_GetBand(SLEqualizerItf self, SLmilliHertz frequency, SLuint16 *pBand) { SL_ENTER_INTERFACE if (NULL == pBand) { result = SL_RESULT_PARAMETER_INVALID; } else { IEqualizer *thiz = (IEqualizer *) self; #if !defined(ANDROID) // search for band whose center frequency has the closest ratio to 1.0 // assumes bands are unsorted (a pessimistic assumption) // assumes bands can overlap (a pessimistic assumption) // assumes a small number of bands, so no need for a fancier algorithm const struct EqualizerBand *band; float floatFreq = (float) frequency; float bestRatio = 0.0; SLuint16 bestBand = SL_EQUALIZER_UNDEFINED; for (band = thiz->mBands; band < &thiz->mBands[thiz->mNumBands]; ++band) { if (!(band->mMin <= frequency && frequency <= band->mMax)) continue; assert(band->mMin <= band->mCenter && band->mCenter <= band->mMax); assert(band->mCenter != 0); float ratio = frequency <= band->mCenter ? floatFreq / band->mCenter : band->mCenter / floatFreq; if (ratio > bestRatio) { bestRatio = ratio; bestBand = band - thiz->mBands; } } *pBand = bestBand; result = SL_RESULT_SUCCESS; #else uint16_t band = 0; interface_lock_shared(thiz); if (NO_EQ(thiz)) { result = SL_RESULT_CONTROL_LOST; } else { android::status_t status = android_eq_getParam(thiz->mEqEffect, EQ_PARAM_GET_BAND, frequency, &band); result = android_fx_statusToResult(status); } interface_unlock_shared(thiz); *pBand = (SLuint16)band; #endif } SL_LEAVE_INTERFACE } static SLresult IEqualizer_GetCurrentPreset(SLEqualizerItf self, SLuint16 *pPreset) { SL_ENTER_INTERFACE if (NULL == pPreset) { result = SL_RESULT_PARAMETER_INVALID; } else { IEqualizer *thiz = (IEqualizer *) self; interface_lock_shared(thiz); #if !defined(ANDROID) SLuint16 preset = thiz->mPreset; interface_unlock_shared(thiz); *pPreset = preset; result = SL_RESULT_SUCCESS; #else uint16_t preset = 0; if (NO_EQ(thiz)) { result = SL_RESULT_CONTROL_LOST; } else { android::status_t status = android_eq_getParam(thiz->mEqEffect, EQ_PARAM_CUR_PRESET, 0, &preset); result = android_fx_statusToResult(status); } interface_unlock_shared(thiz); *pPreset = (SLuint16) preset; #endif } SL_LEAVE_INTERFACE } static SLresult IEqualizer_UsePreset(SLEqualizerItf self, SLuint16 index) { SL_ENTER_INTERFACE SL_LOGV("Equalizer::UsePreset index=%u", index); IEqualizer *thiz = (IEqualizer *) self; if (index >= thiz->mNumPresets) { result = SL_RESULT_PARAMETER_INVALID; } else { interface_lock_exclusive(thiz); #if !defined(ANDROID) SLuint16 band; for (band = 0; band < thiz->mNumBands; ++band) thiz->mLevels[band] = EqualizerPresets[index].mLevels[band]; thiz->mPreset = index; interface_unlock_exclusive(thiz); result = SL_RESULT_SUCCESS; #else if (NO_EQ(thiz)) { result = SL_RESULT_CONTROL_LOST; } else { android::status_t status = android_eq_setParam(thiz->mEqEffect, EQ_PARAM_CUR_PRESET, 0, &index); result = android_fx_statusToResult(status); } interface_unlock_shared(thiz); #endif } SL_LEAVE_INTERFACE } static SLresult IEqualizer_GetNumberOfPresets(SLEqualizerItf self, SLuint16 *pNumPresets) { SL_ENTER_INTERFACE if (NULL == pNumPresets) { result = SL_RESULT_PARAMETER_INVALID; } else { IEqualizer *thiz = (IEqualizer *) self; // Note: no lock, but OK because it is const *pNumPresets = thiz->mNumPresets; result = SL_RESULT_SUCCESS; } SL_LEAVE_INTERFACE } static SLresult IEqualizer_GetPresetName(SLEqualizerItf self, SLuint16 index, const SLchar **ppName) { SL_ENTER_INTERFACE if (NULL == ppName) { result = SL_RESULT_PARAMETER_INVALID; } else { IEqualizer *thiz = (IEqualizer *) self; #if !defined(ANDROID) if (index >= thiz->mNumPresets) { result = SL_RESULT_PARAMETER_INVALID; } else { *ppName = (SLchar *) thiz->mPresets[index].mName; result = SL_RESULT_SUCCESS; } #else if (index >= thiz->mNumPresets) { result = SL_RESULT_PARAMETER_INVALID; } else { // FIXME query preset name rather than retrieve it from the engine. // In SL ES 1.0.1, the strings must exist for the lifetime of the engine. // Starting in 1.1, this will change and we don't need to hold onto the strings // for so long as they will copied into application space. *ppName = (SLchar *) thiz->mThis->mEngine->mEqPresetNames[index]; result = SL_RESULT_SUCCESS; } #endif } SL_LEAVE_INTERFACE } static const struct SLEqualizerItf_ IEqualizer_Itf = { IEqualizer_SetEnabled, IEqualizer_IsEnabled, IEqualizer_GetNumberOfBands, IEqualizer_GetBandLevelRange, IEqualizer_SetBandLevel, IEqualizer_GetBandLevel, IEqualizer_GetCenterFreq, IEqualizer_GetBandFreqRange, IEqualizer_GetBand, IEqualizer_GetCurrentPreset, IEqualizer_UsePreset, IEqualizer_GetNumberOfPresets, IEqualizer_GetPresetName }; void IEqualizer_init(void *self) { IEqualizer *thiz = (IEqualizer *) self; thiz->mItf = &IEqualizer_Itf; thiz->mEnabled = SL_BOOLEAN_FALSE; thiz->mPreset = SL_EQUALIZER_UNDEFINED; #if 0 < MAX_EQ_BANDS unsigned band; for (band = 0; band < MAX_EQ_BANDS; ++band) thiz->mLevels[band] = 0; #endif // const fields thiz->mNumPresets = 0; thiz->mNumBands = 0; #if !defined(ANDROID) thiz->mBands = EqualizerBands; thiz->mPresets = EqualizerPresets; #endif thiz->mBandLevelRangeMin = 0; thiz->mBandLevelRangeMax = 0; #if defined(ANDROID) memset(&thiz->mEqDescriptor, 0, sizeof(effect_descriptor_t)); // placement new (explicit constructor) (void) new (&thiz->mEqEffect) android::sp<android::AudioEffect>(); #endif } void IEqualizer_deinit(void *self) { #if defined(ANDROID) IEqualizer *thiz = (IEqualizer *) self; // explicit destructor thiz->mEqEffect.~sp(); #endif } bool IEqualizer_Expose(void *self) { #if defined(ANDROID) IEqualizer *thiz = (IEqualizer *) self; if (!android_fx_initEffectDescriptor(SL_IID_EQUALIZER, &thiz->mEqDescriptor)) { SL_LOGE("Equalizer initialization failed"); thiz->mNumPresets = 0; thiz->mNumBands = 0; thiz->mBandLevelRangeMin = 0; thiz->mBandLevelRangeMax = 0; return false; } #endif return true; }