/* * Copyright (C) 2014 Haruki Hasegawa * * 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. */ package com.h6ah4i.android.media.standard.audiofx; import android.media.audiofx.Equalizer; import android.util.Log; import com.h6ah4i.android.media.audiofx.IEqualizer; import com.h6ah4i.android.media.utils.AudioEffectSettingsConverter; import com.h6ah4i.android.media.utils.DefaultEqualizerPresets; import com.h6ah4i.android.media.utils.EqualizerBandInfoCorrector; public class StandardEqualizer extends StandardAudioEffect implements IEqualizer { private static final String TAG = "StandardEqualizer"; private IEqualizer.OnParameterChangeListener mUserOnParameterChangeListener; private android.media.audiofx.Equalizer.OnParameterChangeListener mOnParameterChangeListener = new android.media.audiofx.Equalizer.OnParameterChangeListener() { @Override public void onParameterChange( android.media.audiofx.Equalizer effect, int status, int param1, int param2, int value) { StandardEqualizer.this.onParameterChange(effect, status, param1, param2, value); } }; public StandardEqualizer(int priority, int audioSession) throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException, RuntimeException { super(new Equalizer(priority, audioSession)); getEqualizer().setParameterListener(mOnParameterChangeListener); initializeForCompat(); } /** * Get underlying Equalizer instance. * * @return underlying Equalizer instance. */ public Equalizer getEqualizer() { return (Equalizer) super.getAudioEffect(); } @Override public void release() { super.release(); mOnParameterChangeListener = null; mUserOnParameterChangeListener = null; } @Override public void setProperties(IEqualizer.Settings settings) throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { verifySettings(settings); checkIsNotReleased("setProperties()"); if (mIsWorkaroundHTCAndGalaxyS4Enabled) { workaroundGalaxyS4SetProperties(settings); } else if (mIsCyanogenModWorkaroundEnabled) { workaroundCyanogenModSetProperties(settings); } else { getEqualizer().setProperties(AudioEffectSettingsConverter.convert(settings)); } } @Override public IEqualizer.Settings getProperties() throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { checkIsNotReleased("getProperties()"); if (mIsWorkaroundHTCAndGalaxyS4Enabled) { return workaroundGalaxyS4GetPropertiesCompat(); } else if (mIsCyanogenModWorkaroundEnabled) { return workaroundCyanogenModGetPropertiesCompat(); } else { return AudioEffectSettingsConverter.convert(getEqualizer().getProperties()); } } @Override public void setParameterListener(IEqualizer.OnParameterChangeListener listener) { checkIsNotReleased("setParameterListener()"); mUserOnParameterChangeListener = listener; } /* package */void onParameterChange( android.media.audiofx.Equalizer effect, int status, int param1, int param2, int value) { IEqualizer.OnParameterChangeListener listener = null; listener = mUserOnParameterChangeListener; if (listener != null) { listener.onParameterChange(this, status, param1, param2, value); } } // === Fix unwanted behaviors === private static final short DEFAULT_PRESET = 0; private short mNumBands; private short mNumPresets; private short mBandLevelMin; private short mBandLevelMax; private int[][] mBandFreqRange; private int[] mBandCenterFreq; private int[][] mCorrectedBandFreqRange; private int[] mCorrectedCenterFreq; // for usePreset() doesn't work devices (Galaxy S4) private boolean mIsWorkaroundHTCAndGalaxyS4Enabled; // for setProperties() doesn't work devices (CyangenMod) private boolean mIsCyanogenModWorkaroundEnabled; private short[] mWorkaroundBandLevels; private short mWorkaroundCurPreset; private boolean mIsNegativeBandLevelWorkaroundEnabled; private void initializeForCompat() { final int MIN = 0; final int MAX = 1; final Equalizer eq = getEqualizer(); mNumBands = eq.getNumberOfBands(); mNumPresets = eq.getNumberOfPresets(); short[] levelRange = eq.getBandLevelRange(); mBandLevelMin = levelRange[0]; mBandLevelMax = levelRange[1]; mBandFreqRange = new int[mNumBands][2]; mBandCenterFreq = new int[mNumBands]; for (short band = 0; band < mNumBands; band++) { int center = eq.getCenterFreq(band); int[] range = eq.getBandFreqRange(band); mBandFreqRange[band][MIN] = range[MIN]; mBandFreqRange[band][MAX] = range[MAX]; mBandCenterFreq[band] = center; } // correct center freq. & band freq. range int[] correctedCenterFreq = new int[mNumBands]; int[][] correctedBandFreqRange = new int[mNumBands][2]; if (EqualizerBandInfoCorrector.correct( mNumBands, mBandCenterFreq, mBandFreqRange, correctedCenterFreq, correctedBandFreqRange)) { mCorrectedCenterFreq = correctedCenterFreq; mCorrectedBandFreqRange = correctedBandFreqRange; } initializePresetWorkarounds(); } private void initializePresetWorkarounds() { // set to default preset // (NOTE: default preset of Galaxy S4 is "3") final Equalizer eq = getEqualizer(); eq.usePreset(DEFAULT_PRESET); if (mNumBands == 5) { short[] bandLevels = new short[mNumBands]; for (short band = 0; band < mNumBands; band++) { bandLevels[band] = eq.getBandLevel(band); } IEqualizer.Settings defSettings = DefaultEqualizerPresets.PRESET_NORMAL; if (!((bandLevels[0] == defSettings.bandLevels[0]) && (bandLevels[1] == defSettings.bandLevels[1]) && (bandLevels[2] == defSettings.bandLevels[2]) && (bandLevels[3] == defSettings.bandLevels[3]) && (bandLevels[4] == defSettings.bandLevels[4]))) { // check usePreset() method doesn't work properly... Log.d(TAG, "Use workaround version of Equalizer.usePreset() method"); mIsWorkaroundHTCAndGalaxyS4Enabled = true; } if (!mIsWorkaroundHTCAndGalaxyS4Enabled && mNumPresets >= 2) { try { eq.usePreset((short) 1); eq.usePreset(DEFAULT_PRESET); } catch (IllegalArgumentException e) { // HTC phones (HTC Evo 3D, Evo 4G LTE) raises // IllegalArgumentException mIsWorkaroundHTCAndGalaxyS4Enabled = true; } } } else if (mNumBands == 6) { // CyanogenMod (based) custom ROMs // Check setBandLevel() and getBandLevel() works properly // (If level is negative, +1 incremented value will be returned) // ex.) // set: -2, get: -1 // set: -1, get: 0 // set: 0, get: 0 // set: 1, get: 1 // set: 2, get: 2 final short origBandLevel = eq.getBandLevel((short) 0); eq.setBandLevel((short) 0, (short) -1); mIsNegativeBandLevelWorkaroundEnabled = (eq.getBandLevel((short) 0) == 0); eq.setBandLevel((short) 0, origBandLevel); // Check setProperties() works properly android.media.audiofx.Equalizer.Settings settings = eq.getProperties(); try { settings.curPreset = DEFAULT_PRESET; eq.setProperties(settings); } catch (IllegalArgumentException e) { mIsCyanogenModWorkaroundEnabled = true; } } if (mIsWorkaroundHTCAndGalaxyS4Enabled) { mNumPresets = DefaultEqualizerPresets.NUM_PRESETS; mWorkaroundBandLevels = new short[mNumBands]; workaroundGalaxyS4UsePreset(DEFAULT_PRESET); } else if (mIsCyanogenModWorkaroundEnabled) { mWorkaroundBandLevels = new short[mNumBands]; workaroundCyanogenModUsePreset(DEFAULT_PRESET); } } private void workaroundGalaxyS4UsePreset(short preset) { if (preset != PRESET_UNDEFINED) { workaroundGalaxyS4SetProperties(DefaultEqualizerPresets.getPreset(preset)); } } private void workaroundCyanogenModUsePreset(short preset) { if (preset != PRESET_UNDEFINED) { final Equalizer eq = getEqualizer(); eq.usePreset(preset); for (short band = 0; band < mNumBands; band++) { mWorkaroundBandLevels[band] = eq.getBandLevel(band); } mWorkaroundCurPreset = preset; } } private String workaroundGalaxyS4GetPesetName(short preset) { if (preset >= 0 && preset < mNumPresets) { return DefaultEqualizerPresets.getName(preset); } else { return ""; } } private short workaroundGetBandLevel(short band) { return mWorkaroundBandLevels[band]; } private void workaroundGalaxyS4SetBandLevel(short band, short level) { getEqualizer().setBandLevel(band, level); mWorkaroundBandLevels[band] = level; mWorkaroundCurPreset = PRESET_UNDEFINED; } private void workaroundCyanogenModSetBandLevel(short band, short level) { if (mIsNegativeBandLevelWorkaroundEnabled) { if (level < 0) { level -= (short) 1; } } getEqualizer().setBandLevel(band, level); mWorkaroundBandLevels[band] = level; mWorkaroundCurPreset = PRESET_UNDEFINED; } private short workaroundGetCurrentPreset() { return mWorkaroundCurPreset; } private void workaroundGalaxyS4SetProperties(IEqualizer.Settings settings) { final Equalizer eq = getEqualizer(); if (settings.curPreset != PRESET_UNDEFINED) { // NOTE: if curPreset has valid preset no., // bandLevels values are not used. settings = DefaultEqualizerPresets.getPreset(settings.curPreset); } // apply if (settings.curPreset != PRESET_UNDEFINED) { try { eq.usePreset(settings.curPreset); } catch (IllegalArgumentException e) { // HTC devices raises IllegalArgumentException } } for (short band = 0; band < settings.numBands; band++) { eq.setBandLevel(band, settings.bandLevels[band]); } mWorkaroundCurPreset = settings.curPreset; System.arraycopy(settings.bandLevels, 0, mWorkaroundBandLevels, 0, mNumBands); } private void workaroundCyanogenModSetProperties(IEqualizer.Settings settings) { // apply if (settings.curPreset != PRESET_UNDEFINED) { final Equalizer eq = getEqualizer(); eq.usePreset(settings.curPreset); mWorkaroundCurPreset = settings.curPreset; for (short band = 0; band < settings.numBands; band++) { mWorkaroundBandLevels[band] = eq.getBandLevel(band); } } else { for (short band = 0; band < settings.numBands; band++) { workaroundCyanogenModSetBandLevel(band, settings.bandLevels[band]); } mWorkaroundCurPreset = settings.curPreset; System.arraycopy(settings.bandLevels, 0, mWorkaroundBandLevels, 0, mNumBands); } } private IEqualizer.Settings workaroundGalaxyS4GetPropertiesCompat() { IEqualizer.Settings settings = new IEqualizer.Settings(); settings.curPreset = mWorkaroundCurPreset; settings.numBands = mNumBands; settings.bandLevels = mWorkaroundBandLevels.clone(); return settings; } private IEqualizer.Settings workaroundCyanogenModGetPropertiesCompat() { final Equalizer eq = getEqualizer(); IEqualizer.Settings settings = new IEqualizer.Settings(); settings.curPreset = mWorkaroundCurPreset; settings.numBands = mNumBands; settings.bandLevels = new short[mNumBands]; for (short band = 0; band < mNumBands; band++) { settings.bandLevels[band] = eq.getBandLevel(band); } return settings; } private static int[] getBandFreqRangeCompat(int[][] bandFreqRange, short band) { int[] range = new int[2]; range[0] = bandFreqRange[band][0]; range[1] = bandFreqRange[band][1]; return range; } private static short getBandCompat(short numBands, int[][] range, int frequency) { final int MIN = 0; final int MAX = 1; if (frequency < range[0][MIN]) return (short) (-1); if (frequency > range[numBands - 1][MAX]) return (short) (-1); for (short band = 0; band < numBands; band++) { if ((frequency >= range[band][MIN]) && (frequency <= range[band][MAX])) { return band; } } return (short) (-1); } private static int getCenterFreqCompat(int[] centerFreq, short band) { return centerFreq[band]; } @Override public short getNumberOfBands() throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { checkIsNotReleased("getNumberOfBands()"); return getEqualizer().getNumberOfBands(); } @Override public short getNumberOfPresets() throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { checkIsNotReleased("getNumberOfPresets()"); if (mIsWorkaroundHTCAndGalaxyS4Enabled) { return mNumPresets; } else { return getEqualizer().getNumberOfPresets(); } } @Override public void usePreset(short preset) throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { verifyPresetRange(preset); checkIsNotReleased("usePreset()"); if (mIsWorkaroundHTCAndGalaxyS4Enabled) { workaroundGalaxyS4UsePreset(preset); } else if (mIsCyanogenModWorkaroundEnabled) { workaroundCyanogenModUsePreset(preset); } else { getEqualizer().usePreset(preset); } } @Override public String getPresetName(short preset) { checkIsNotReleased("getPresetName()"); if (mIsWorkaroundHTCAndGalaxyS4Enabled) { return workaroundGalaxyS4GetPesetName(preset); } else { return getEqualizer().getPresetName(preset); } } @Override public short getBandLevel(short band) throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { verifyBandRange(band); checkIsNotReleased("getBandLevel()"); if (mIsWorkaroundHTCAndGalaxyS4Enabled) { return workaroundGetBandLevel(band); } else { return getEqualizer().getBandLevel(band); } } @Override public short[] getBandLevelRange() throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { checkIsNotReleased("getBandLevelRange()"); return getEqualizer().getBandLevelRange(); } @Override public void setBandLevel(short band, short level) throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { verifyBandRange(band); verifyBandLevelRange(level); checkIsNotReleased("setBandLevel()"); if (mIsWorkaroundHTCAndGalaxyS4Enabled) { workaroundGalaxyS4SetBandLevel(band, level); } else if (mIsNegativeBandLevelWorkaroundEnabled) { workaroundCyanogenModSetBandLevel(band, level); } else { getEqualizer().setBandLevel(band, level); } } @Override public short getBand(int frequency) throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { // NOTE: // Use getBandCompat() method instead of super.getBand(frequency), // because the original implementation returns wrong values. checkIsNotReleased("getBand()"); if (mCorrectedBandFreqRange != null) { return getBandCompat(mNumBands, mCorrectedBandFreqRange, frequency); } else { return getEqualizer().getBand(frequency); } } @Override public int[] getBandFreqRange(short band) throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { // NOTE: // Use getBandFreqRangeCompat() method instead of // super.getBand(frequency), // because the original implementation returns wrong values. verifyBandRange(band); checkIsNotReleased("getBandFreqRange()"); if (mCorrectedBandFreqRange != null) { return getBandFreqRangeCompat(mCorrectedBandFreqRange, band); } else { return getEqualizer().getBandFreqRange(band); } } @Override public int getCenterFreq(short band) throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { verifyBandRange(band); checkIsNotReleased("getCenterFreq()"); if (mCorrectedCenterFreq != null) { return getCenterFreqCompat(mCorrectedCenterFreq, band); } else { return getEqualizer().getCenterFreq(band); } } @Override public short getCurrentPreset() throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { short preset; checkIsNotReleased("getCurrentPreset()"); if (mIsWorkaroundHTCAndGalaxyS4Enabled || mIsCyanogenModWorkaroundEnabled) { preset = workaroundGetCurrentPreset(); } else { preset = getEqualizer().getCurrentPreset(); } if (!(preset >= 0 && preset < mNumPresets)) { // Fix return value (Galaxy S4 may returns (mNumPresets + 1)) preset = PRESET_UNDEFINED; } return preset; } private void verifyBandLevelRange(short level) { if (!(level >= mBandLevelMin && level <= mBandLevelMax)) throw new IllegalArgumentException("bad parameter value: level = " + level); } private void verifyBandRange(short band) { if (!(band >= 0 && band < mNumBands)) throw new IllegalArgumentException("bad parameter value: band = " + band); } private void verifyPresetRange(short preset) { if (!((preset >= 0 && preset < mNumPresets) || (preset == PRESET_UNDEFINED))) throw new IllegalArgumentException("bad parameter value: preset = " + preset); } private void verifySettings(IEqualizer.Settings settings) { if (settings == null) throw new IllegalArgumentException("The parameter 'settings' is null"); if (settings.bandLevels == null) { throw new IllegalArgumentException("settings invalid property: bandLevels is null"); } if (settings.numBands != settings.bandLevels.length || settings.numBands != mNumBands) { throw new IllegalArgumentException("settings invalid band count: " + settings.numBands); } for (short i = 0; i < mNumBands; i++) { verifyBandLevelRange(settings.bandLevels[i]); } verifyPresetRange(settings.curPreset); } // ============================== }