/* * Copyright (C) 2010-2011 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. */ package com.android.musicfx; import android.content.Context; import android.content.SharedPreferences; import android.media.MediaPlayer; import android.media.audiofx.AudioEffect; import android.media.audiofx.BassBoost; import android.media.audiofx.Equalizer; import android.media.audiofx.PresetReverb; import android.media.audiofx.Virtualizer; import android.util.Log; import java.util.Arrays; import java.util.concurrent.ConcurrentHashMap; /** * The Common class defines constants to be used by the control panels. */ public class ControlPanelEffect { private final static String TAG = "MusicFXControlPanelEffect"; /** * Audio session priority */ private static final int PRIORITY = 0; /** * The control mode specifies if control panel updates effects and preferences or only * preferences. */ static enum ControlMode { /** * Control panel updates effects and preferences. Applicable when audio session is delivered * by user. */ CONTROL_EFFECTS, /** * Control panel only updates preferences. Applicable when there was no audio or invalid * session provided by user. */ CONTROL_PREFERENCES } static enum Key { global_enabled, virt_enabled, virt_strength, virt_type, bb_enabled, bb_strength, te_enabled, te_strength, avl_enabled, lm_enabled, lm_strength, eq_enabled, eq_num_bands, eq_level_range, eq_center_freq, eq_band_level, eq_num_presets, eq_preset_name, eq_preset_user_band_level, eq_preset_user_band_level_default, eq_preset_opensl_es_band_level, eq_preset_ci_extreme_band_level, eq_current_preset, pr_enabled, pr_current_preset } // Effect/audio session Mappings /** * Hashmap initial capacity */ private static final int HASHMAP_INITIAL_CAPACITY = 16; /** * Hashmap load factor */ private static final float HASHMAP_LOAD_FACTOR = 0.75f; /** * ConcurrentHashMap concurrency level */ private static final int HASHMAP_CONCURRENCY_LEVEL = 2; /** * Map containing the Virtualizer audio session, effect mappings. */ private static final ConcurrentHashMap<Integer, Virtualizer> mVirtualizerInstances = new ConcurrentHashMap<Integer, Virtualizer>( HASHMAP_INITIAL_CAPACITY, HASHMAP_LOAD_FACTOR, HASHMAP_CONCURRENCY_LEVEL); /** * Map containing the BB audio session, effect mappings. */ private static final ConcurrentHashMap<Integer, BassBoost> mBassBoostInstances = new ConcurrentHashMap<Integer, BassBoost>( HASHMAP_INITIAL_CAPACITY, HASHMAP_LOAD_FACTOR, HASHMAP_CONCURRENCY_LEVEL); /** * Map containing the EQ audio session, effect mappings. */ private static final ConcurrentHashMap<Integer, Equalizer> mEQInstances = new ConcurrentHashMap<Integer, Equalizer>( HASHMAP_INITIAL_CAPACITY, HASHMAP_LOAD_FACTOR, HASHMAP_CONCURRENCY_LEVEL); /** * Map containing the PR audio session, effect mappings. */ private static final ConcurrentHashMap<Integer, PresetReverb> mPresetReverbInstances = new ConcurrentHashMap<Integer, PresetReverb>( HASHMAP_INITIAL_CAPACITY, HASHMAP_LOAD_FACTOR, HASHMAP_CONCURRENCY_LEVEL); /** * Map containing the package name, audio session mappings. */ private static final ConcurrentHashMap<String, Integer> mPackageSessions = new ConcurrentHashMap<String, Integer>( HASHMAP_INITIAL_CAPACITY, HASHMAP_LOAD_FACTOR, HASHMAP_CONCURRENCY_LEVEL); // Defaults final static boolean GLOBAL_ENABLED_DEFAULT = false; private final static boolean VIRTUALIZER_ENABLED_DEFAULT = true; private final static int VIRTUALIZER_STRENGTH_DEFAULT = 1000; private final static boolean BASS_BOOST_ENABLED_DEFAULT = true; private final static int BASS_BOOST_STRENGTH_DEFAULT = 667; private final static boolean PRESET_REVERB_ENABLED_DEFAULT = false; private final static int PRESET_REVERB_CURRENT_PRESET_DEFAULT = 0; // None // EQ defaults private final static boolean EQUALIZER_ENABLED_DEFAULT = true; private final static String EQUALIZER_PRESET_NAME_DEFAULT = "Preset"; private final static short EQUALIZER_NUMBER_BANDS_DEFAULT = 5; private final static short EQUALIZER_NUMBER_PRESETS_DEFAULT = 0; private final static short[] EQUALIZER_BAND_LEVEL_RANGE_DEFAULT = { -1500, 1500 }; private final static int[] EQUALIZER_CENTER_FREQ_DEFAULT = { 60000, 230000, 910000, 3600000, 14000000 }; private final static short[] EQUALIZER_PRESET_CIEXTREME_BAND_LEVEL = { 0, 800, 400, 100, 1000 }; private final static short[] EQUALIZER_PRESET_USER_BAND_LEVEL_DEFAULT = { 0, 0, 0, 0, 0 }; private final static short[][] EQUALIZER_PRESET_OPENSL_ES_BAND_LEVEL_DEFAULT = new short[EQUALIZER_NUMBER_PRESETS_DEFAULT][EQUALIZER_NUMBER_BANDS_DEFAULT]; // EQ effect properties which are invariable over all EQ effects sessions private static short[] mEQBandLevelRange = EQUALIZER_BAND_LEVEL_RANGE_DEFAULT; private static short mEQNumBands = EQUALIZER_NUMBER_BANDS_DEFAULT; private static int[] mEQCenterFreq = EQUALIZER_CENTER_FREQ_DEFAULT; private static short mEQNumPresets = EQUALIZER_NUMBER_PRESETS_DEFAULT; private static short[][] mEQPresetOpenSLESBandLevel = EQUALIZER_PRESET_OPENSL_ES_BAND_LEVEL_DEFAULT; private static String[] mEQPresetNames; private static boolean mIsEQInitialized = false; private final static Object mEQInitLock = new Object(); /** * Default int argument used in methods to see that the arg is a dummy. Used for method * overloading. */ private final static int DUMMY_ARGUMENT = -1; /** * Inits effects preferences for the given context and package name in the control panel. If * preferences for the given package name don't exist, they are created and initialized. * * @param context * @param packageName * @param audioSession * System wide unique audio session identifier. */ public static void initEffectsPreferences(final Context context, final String packageName, final int audioSession) { final SharedPreferences prefs = context.getSharedPreferences(packageName, Context.MODE_PRIVATE); final SharedPreferences.Editor editor = prefs.edit(); final ControlMode controlMode = getControlMode(audioSession); // init preferences try { // init global on/off switch final boolean isGlobalEnabled = prefs.getBoolean(Key.global_enabled.toString(), GLOBAL_ENABLED_DEFAULT); editor.putBoolean(Key.global_enabled.toString(), isGlobalEnabled); Log.v(TAG, "isGlobalEnabled = " + isGlobalEnabled); // Virtualizer final boolean isVIEnabled = prefs.getBoolean(Key.virt_enabled.toString(), VIRTUALIZER_ENABLED_DEFAULT); final int vIStrength = prefs.getInt(Key.virt_strength.toString(), VIRTUALIZER_STRENGTH_DEFAULT); editor.putBoolean(Key.virt_enabled.toString(), isVIEnabled); editor.putInt(Key.virt_strength.toString(), vIStrength); // BassBoost final boolean isBBEnabled = prefs.getBoolean(Key.bb_enabled.toString(), BASS_BOOST_ENABLED_DEFAULT); final int bBStrength = prefs.getInt(Key.bb_strength.toString(), BASS_BOOST_STRENGTH_DEFAULT); editor.putBoolean(Key.bb_enabled.toString(), isBBEnabled); editor.putInt(Key.bb_strength.toString(), bBStrength); // Equalizer synchronized (mEQInitLock) { // If EQ is not initialized already create "dummy" audio session created by // MediaPlayer and create effect on it to retrieve the invariable EQ properties if (!mIsEQInitialized) { final MediaPlayer mediaPlayer = new MediaPlayer(); final int session = mediaPlayer.getAudioSessionId(); Equalizer equalizerEffect = null; try { Log.d(TAG, "Creating dummy EQ effect on session " + session); equalizerEffect = new Equalizer(PRIORITY, session); mEQBandLevelRange = equalizerEffect.getBandLevelRange(); mEQNumBands = equalizerEffect.getNumberOfBands(); mEQCenterFreq = new int[mEQNumBands]; for (short band = 0; band < mEQNumBands; band++) { mEQCenterFreq[band] = equalizerEffect.getCenterFreq(band); } mEQNumPresets = equalizerEffect.getNumberOfPresets(); mEQPresetNames = new String[mEQNumPresets]; mEQPresetOpenSLESBandLevel = new short[mEQNumPresets][mEQNumBands]; for (short preset = 0; preset < mEQNumPresets; preset++) { mEQPresetNames[preset] = equalizerEffect.getPresetName(preset); equalizerEffect.usePreset(preset); for (short band = 0; band < mEQNumBands; band++) { mEQPresetOpenSLESBandLevel[preset][band] = equalizerEffect .getBandLevel(band); } } mIsEQInitialized = true; } catch (final IllegalStateException e) { Log.e(TAG, "Equalizer: " + e); } catch (final IllegalArgumentException e) { Log.e(TAG, "Equalizer: " + e); } catch (final UnsupportedOperationException e) { Log.e(TAG, "Equalizer: " + e); } catch (final RuntimeException e) { Log.e(TAG, "Equalizer: " + e); } finally { if (equalizerEffect != null) { Log.d(TAG, "Releasing dummy EQ effect"); equalizerEffect.release(); } mediaPlayer.release(); // When there was a failure set some good defaults if (!mIsEQInitialized) { mEQPresetOpenSLESBandLevel = new short[mEQNumPresets][mEQNumBands]; for (short preset = 0; preset < mEQNumPresets; preset++) { // Init preset names to a dummy name mEQPresetNames[preset] = prefs.getString( Key.eq_preset_name.toString() + preset, EQUALIZER_PRESET_NAME_DEFAULT + preset); if (preset < EQUALIZER_PRESET_OPENSL_ES_BAND_LEVEL_DEFAULT.length) { mEQPresetOpenSLESBandLevel[preset] = Arrays.copyOf( EQUALIZER_PRESET_OPENSL_ES_BAND_LEVEL_DEFAULT[preset], mEQNumBands); } } } } } editor.putInt(Key.eq_level_range.toString() + 0, mEQBandLevelRange[0]); editor.putInt(Key.eq_level_range.toString() + 1, mEQBandLevelRange[1]); editor.putInt(Key.eq_num_bands.toString(), mEQNumBands); editor.putInt(Key.eq_num_presets.toString(), mEQNumPresets); // Resetting the EQ arrays depending on the real # bands with defaults if // band < default size else 0 by copying default arrays over new ones final short[] eQPresetCIExtremeBandLevel = Arrays.copyOf( EQUALIZER_PRESET_CIEXTREME_BAND_LEVEL, mEQNumBands); final short[] eQPresetUserBandLevelDefault = Arrays.copyOf( EQUALIZER_PRESET_USER_BAND_LEVEL_DEFAULT, mEQNumBands); // If no preset prefs set use CI EXTREME (= numPresets) final short eQPreset = (short) prefs.getInt(Key.eq_current_preset.toString(), mEQNumPresets); editor.putInt(Key.eq_current_preset.toString(), eQPreset); final short[] bandLevel = new short[mEQNumBands]; for (short band = 0; band < mEQNumBands; band++) { if (controlMode == ControlMode.CONTROL_PREFERENCES) { if (eQPreset < mEQNumPresets) { // OpenSL ES effect presets bandLevel[band] = mEQPresetOpenSLESBandLevel[eQPreset][band]; } else if (eQPreset == mEQNumPresets) { // CI EXTREME bandLevel[band] = eQPresetCIExtremeBandLevel[band]; } else { // User bandLevel[band] = (short) prefs.getInt( Key.eq_preset_user_band_level.toString() + band, eQPresetUserBandLevelDefault[band]); } editor.putInt(Key.eq_band_level.toString() + band, bandLevel[band]); } editor.putInt(Key.eq_center_freq.toString() + band, mEQCenterFreq[band]); editor.putInt(Key.eq_preset_ci_extreme_band_level.toString() + band, eQPresetCIExtremeBandLevel[band]); editor.putInt(Key.eq_preset_user_band_level_default.toString() + band, eQPresetUserBandLevelDefault[band]); } for (short preset = 0; preset < mEQNumPresets; preset++) { editor.putString(Key.eq_preset_name.toString() + preset, mEQPresetNames[preset]); for (short band = 0; band < mEQNumBands; band++) { editor.putInt(Key.eq_preset_opensl_es_band_level.toString() + preset + "_" + band, mEQPresetOpenSLESBandLevel[preset][band]); } } } final boolean isEQEnabled = prefs.getBoolean(Key.eq_enabled.toString(), EQUALIZER_ENABLED_DEFAULT); editor.putBoolean(Key.eq_enabled.toString(), isEQEnabled); // Preset reverb final boolean isEnabledPR = prefs.getBoolean(Key.pr_enabled.toString(), PRESET_REVERB_ENABLED_DEFAULT); final short presetPR = (short) prefs.getInt(Key.pr_current_preset.toString(), PRESET_REVERB_CURRENT_PRESET_DEFAULT); editor.putBoolean(Key.pr_enabled.toString(), isEnabledPR); editor.putInt(Key.pr_current_preset.toString(), presetPR); editor.commit(); } catch (final RuntimeException e) { Log.e(TAG, "initEffectsPreferences: processingEnabled: " + e); } } /** * Gets the effect control mode based on the given audio session in the control panel. Control * mode defines if the control panel is controlling effects and/or preferences * * @param audioSession * System wide unique audio session identifier. * @return effect control mode */ public static ControlMode getControlMode(final int audioSession) { if (audioSession == AudioEffect.ERROR_BAD_VALUE) { return ControlMode.CONTROL_PREFERENCES; } return ControlMode.CONTROL_EFFECTS; } /** * Sets boolean parameter to value for given key * * @param context * @param packageName * @param audioSession * System wide unique audio session identifier. * @param key * @param value */ public static void setParameterBoolean(final Context context, final String packageName, final int audioSession, final Key key, final boolean value) { try { final SharedPreferences prefs = context.getSharedPreferences(packageName, Context.MODE_PRIVATE); final ControlMode controlMode = getControlMode(audioSession); boolean enabled = value; // Global on/off if (key == Key.global_enabled) { boolean processingEnabled = false; if (value == true) { // enable all with respect to preferences if (controlMode == ControlMode.CONTROL_EFFECTS) { final Virtualizer virtualizerEffect = getVirtualizerEffect(audioSession); if (virtualizerEffect != null) { virtualizerEffect.setEnabled(prefs.getBoolean( Key.virt_enabled.toString(), VIRTUALIZER_ENABLED_DEFAULT)); final int vIStrength = prefs.getInt(Key.virt_strength.toString(), VIRTUALIZER_STRENGTH_DEFAULT); setParameterInt(context, packageName, audioSession, Key.virt_strength, vIStrength); } final BassBoost bassBoostEffect = getBassBoostEffect(audioSession); if (bassBoostEffect != null) { bassBoostEffect.setEnabled(prefs.getBoolean(Key.bb_enabled.toString(), BASS_BOOST_ENABLED_DEFAULT)); final int bBStrength = prefs.getInt(Key.bb_strength.toString(), BASS_BOOST_STRENGTH_DEFAULT); setParameterInt(context, packageName, audioSession, Key.bb_strength, bBStrength); } final Equalizer equalizerEffect = getEqualizerEffect(audioSession); if (equalizerEffect != null) { equalizerEffect.setEnabled(prefs.getBoolean(Key.eq_enabled.toString(), EQUALIZER_ENABLED_DEFAULT)); final int[] bandLevels = getParameterIntArray(context, packageName, audioSession, Key.eq_band_level); final int len = bandLevels.length; for (short band = 0; band < len; band++) { final int level = bandLevels[band]; setParameterInt(context, packageName, audioSession, Key.eq_band_level, level, band); } } // XXX: Preset Reverb not used for the moment, so commented out the effect // creation to not use MIPS // final PresetReverb presetReverbEffect = // getPresetReverbEffect(audioSession); // if (presetReverbEffect != null) { // presetReverbEffect.setEnabled(prefs.getBoolean( // Key.pr_enabled.toString(), PRESET_REVERB_ENABLED_DEFAULT)); // } } processingEnabled = true; Log.v(TAG, "processingEnabled=" + processingEnabled); } else { // disable all if (controlMode == ControlMode.CONTROL_EFFECTS) { final Virtualizer virtualizerEffect = getVirtualizerEffectNoCreate(audioSession); if (virtualizerEffect != null) { mVirtualizerInstances.remove(audioSession, virtualizerEffect); virtualizerEffect.setEnabled(false); virtualizerEffect.release(); } final BassBoost bassBoostEffect = getBassBoostEffectNoCreate(audioSession); if (bassBoostEffect != null) { mBassBoostInstances.remove(audioSession, bassBoostEffect); bassBoostEffect.setEnabled(false); bassBoostEffect.release(); } final Equalizer equalizerEffect = getEqualizerEffectNoCreate(audioSession); if (equalizerEffect != null) { mEQInstances.remove(audioSession, equalizerEffect); equalizerEffect.setEnabled(false); equalizerEffect.release(); } // XXX: Preset Reverb not used for the moment, so commented out the effect // creation to not use MIPS // final PresetReverb presetReverbEffect = // getPresetReverbEffect(audioSession); // if (presetReverbEffect != null) { // presetReverbEffect.setEnabled(false); // } } processingEnabled = false; Log.v(TAG, "processingEnabled=" + processingEnabled); } enabled = processingEnabled; } else if (controlMode == ControlMode.CONTROL_EFFECTS) { final boolean isGlobalEnabled = prefs.getBoolean(Key.global_enabled.toString(), GLOBAL_ENABLED_DEFAULT); if (isGlobalEnabled == true) { // Set effect parameters switch (key) { case global_enabled: // Global, already handled, to get out error free break; // Virtualizer case virt_enabled: final Virtualizer virtualizerEffect = getVirtualizerEffect(audioSession); if (virtualizerEffect != null) { virtualizerEffect.setEnabled(value); enabled = virtualizerEffect.getEnabled(); } break; // BassBoost case bb_enabled: final BassBoost bassBoostEffect = getBassBoostEffect(audioSession); if (bassBoostEffect != null) { bassBoostEffect.setEnabled(value); enabled = bassBoostEffect.getEnabled(); } break; // Equalizer case eq_enabled: final Equalizer equalizerEffect = getEqualizerEffect(audioSession); if (equalizerEffect != null) { equalizerEffect.setEnabled(value); enabled = equalizerEffect.getEnabled(); } break; // PresetReverb case pr_enabled: // XXX: Preset Reverb not used for the moment, so commented out the effect // creation to not use MIPS // final PresetReverb presetReverbEffect = // getPresetReverbEffect(audioSession); // if (presetReverbEffect != null) { // presetReverbEffect.setEnabled(value); // enabled = presetReverbEffect.getEnabled(); // } break; default: Log.e(TAG, "Unknown/unsupported key " + key); return; } } } // Set preferences final SharedPreferences.Editor editor = prefs.edit(); editor.putBoolean(key.toString(), enabled); editor.commit(); } catch (final RuntimeException e) { Log.e(TAG, "setParameterBoolean: " + key + "; " + value + "; " + e); } } /** * Gets boolean parameter for given key * * @param context * @param packageName * @param audioSession * System wide unique audio session identifier. * @param key * @return parameter value */ public static Boolean getParameterBoolean(final Context context, final String packageName, final int audioSession, final Key key) { final SharedPreferences prefs = context.getSharedPreferences(packageName, Context.MODE_PRIVATE); boolean value = false; try { value = prefs.getBoolean(key.toString(), value); } catch (final RuntimeException e) { Log.e(TAG, "getParameterBoolean: " + key + "; " + value + "; " + e); } return value; } /** * Sets int parameter for given key and value arg0, arg1 * * @param context * @param packageName * @param audioSession * System wide unique audio session identifier. * @param key * @param arg0 * @param arg1 */ public static void setParameterInt(final Context context, final String packageName, final int audioSession, final Key key, final int arg0, final int arg1) { String strKey = key.toString(); int value = arg0; try { final SharedPreferences prefs = context.getSharedPreferences(packageName, Context.MODE_PRIVATE); final SharedPreferences.Editor editor = prefs.edit(); final ControlMode controlMode = getControlMode(audioSession); // Set effect parameters if (controlMode == ControlMode.CONTROL_EFFECTS) { switch (key) { // Virtualizer case virt_strength: { final Virtualizer virtualizerEffect = getVirtualizerEffect(audioSession); if (virtualizerEffect != null) { virtualizerEffect.setStrength((short) value); value = virtualizerEffect.getRoundedStrength(); } break; } // BassBoost case bb_strength: { final BassBoost bassBoostEffect = getBassBoostEffect(audioSession); if (bassBoostEffect != null) { bassBoostEffect.setStrength((short) value); value = bassBoostEffect.getRoundedStrength(); } break; } // Equalizer case eq_band_level: { if (arg1 == DUMMY_ARGUMENT) { throw new IllegalArgumentException("Dummy arg passed."); } final short band = (short) arg1; strKey = strKey + band; final Equalizer equalizerEffect = getEqualizerEffect(audioSession); if (equalizerEffect != null) { equalizerEffect.setBandLevel(band, (short) value); value = equalizerEffect.getBandLevel(band); // save band level in User preset editor.putInt(Key.eq_preset_user_band_level.toString() + band, value); } break; } case eq_current_preset: { final Equalizer equalizerEffect = getEqualizerEffect(audioSession); if (equalizerEffect != null) { final short preset = (short) value; final int numBands = prefs.getInt(Key.eq_num_bands.toString(), EQUALIZER_NUMBER_BANDS_DEFAULT); final int numPresets = prefs.getInt(Key.eq_num_presets.toString(), EQUALIZER_NUMBER_PRESETS_DEFAULT); if (preset < numPresets) { // OpenSL ES EQ Effect presets equalizerEffect.usePreset(preset); value = equalizerEffect.getCurrentPreset(); } else { final short[] eQPresetCIExtremeBandLevelDefault = Arrays.copyOf( EQUALIZER_PRESET_CIEXTREME_BAND_LEVEL, numBands); final short[] eQPresetUserBandLevelDefault = Arrays.copyOf( EQUALIZER_PRESET_USER_BAND_LEVEL_DEFAULT, numBands); // Set the band levels manually for custom presets for (short band = 0; band < numBands; band++) { short bandLevel = 0; if (preset == numPresets) { // CI EXTREME bandLevel = (short) prefs.getInt( Key.eq_preset_ci_extreme_band_level.toString() + band, eQPresetCIExtremeBandLevelDefault[band]); } else { // User bandLevel = (short) prefs.getInt( Key.eq_preset_user_band_level.toString() + band, eQPresetUserBandLevelDefault[band]); } equalizerEffect.setBandLevel(band, bandLevel); } } // update band levels for (short band = 0; band < numBands; band++) { final short level = equalizerEffect.getBandLevel(band); editor.putInt(Key.eq_band_level.toString() + band, level); } } break; } case eq_preset_user_band_level: // Fall through case eq_preset_user_band_level_default: // Fall through case eq_preset_ci_extreme_band_level: { if (arg1 == DUMMY_ARGUMENT) { throw new IllegalArgumentException("Dummy arg passed."); } final short band = (short) arg1; strKey = strKey + band; break; } case pr_current_preset: // XXX: Preset Reverb not used for the moment, so commented out the effect // creation to not use MIPS // final PresetReverb presetReverbEffect = getPresetReverbEffect(audioSession); // if (presetReverbEffect != null) { // presetReverbEffect.setPreset((short) value); // value = presetReverbEffect.getPreset(); // } break; default: Log.e(TAG, "setParameterInt: Unknown/unsupported key " + key); return; } } else { switch (key) { // Virtualizer case virt_strength: // Do nothing break; case virt_type: // Do nothing break; // BassBoost case bb_strength: // Do nothing break; // Equalizer case eq_band_level: { if (arg1 == DUMMY_ARGUMENT) { throw new IllegalArgumentException("Dummy arg passed."); } final short band = (short) arg1; strKey = strKey + band; editor.putInt(Key.eq_preset_user_band_level.toString() + band, value); break; } case eq_current_preset: { final short preset = (short) value; final int numBands = prefs.getInt(Key.eq_num_bands.toString(), EQUALIZER_NUMBER_BANDS_DEFAULT); final int numPresets = prefs.getInt(Key.eq_num_presets.toString(), EQUALIZER_NUMBER_PRESETS_DEFAULT); final short[][] eQPresetOpenSLESBandLevelDefault = Arrays.copyOf( EQUALIZER_PRESET_OPENSL_ES_BAND_LEVEL_DEFAULT, numBands); final short[] eQPresetCIExtremeBandLevelDefault = Arrays.copyOf( EQUALIZER_PRESET_CIEXTREME_BAND_LEVEL, numBands); final short[] eQPresetUserBandLevelDefault = Arrays.copyOf( EQUALIZER_PRESET_USER_BAND_LEVEL_DEFAULT, numBands); for (short band = 0; band < numBands; band++) { short bandLevel = 0; if (preset < numPresets) { // OpenSL ES EQ Effect presets bandLevel = (short) prefs.getInt( Key.eq_preset_opensl_es_band_level.toString() + preset + "_" + band, eQPresetOpenSLESBandLevelDefault[preset][band]); } else if (preset == numPresets) { // CI EXTREME bandLevel = (short) prefs.getInt( Key.eq_preset_ci_extreme_band_level.toString() + band, eQPresetCIExtremeBandLevelDefault[band]); } else { // User bandLevel = (short) prefs.getInt( Key.eq_preset_user_band_level.toString() + band, eQPresetUserBandLevelDefault[band]); } editor.putInt(Key.eq_band_level.toString() + band, bandLevel); } break; } case eq_preset_user_band_level: // Fall through case eq_preset_user_band_level_default: // Fall through case eq_preset_ci_extreme_band_level: { if (arg1 == DUMMY_ARGUMENT) { throw new IllegalArgumentException("Dummy arg passed."); } final short band = (short) arg1; strKey = strKey + band; break; } case pr_current_preset: // Do nothing break; default: Log.e(TAG, "setParameterInt: Unknown/unsupported key " + key); return; } } // Set preferences editor.putInt(strKey, value); editor.apply(); } catch (final RuntimeException e) { Log.e(TAG, "setParameterInt: " + key + "; " + arg0 + "; " + arg1 + "; " + e); } } /** * Sets int parameter for given key and value arg * * @param context * @param packageName * @param audioSession * System wide unique audio session identifier. * @param key * @param arg */ public static void setParameterInt(final Context context, final String packageName, final int audioSession, final Key key, final int arg) { setParameterInt(context, packageName, audioSession, key, arg, DUMMY_ARGUMENT); } /** * Gets int parameter given key * * @param context * @param packageName * @param audioSession * System wide unique audio session identifier. * @param key * @return parameter value */ public static int getParameterInt(final Context context, final String packageName, final int audioSession, final String key) { int value = 0; try { final SharedPreferences prefs = context.getSharedPreferences(packageName, Context.MODE_PRIVATE); value = prefs.getInt(key, value); } catch (final RuntimeException e) { Log.e(TAG, "getParameterInt: " + key + "; " + e); } return value; } /** * Gets int parameter given key * * @param context * @param packageName * @param audioSession * System wide unique audio session identifier. * @param key * @return parameter value */ public static int getParameterInt(final Context context, final String packageName, final int audioSession, final Key key) { return getParameterInt(context, packageName, audioSession, key.toString()); } /** * Gets int parameter given key and arg * * @param context * @param packageName * @param audioSession * System wide unique audio session identifier. * @param audioSession * @param key * @param arg * @return parameter value */ public static int getParameterInt(final Context context, final String packageName, final int audioSession, final Key key, final int arg) { return getParameterInt(context, packageName, audioSession, key.toString() + arg); } /** * Gets int parameter given key, arg0 and arg1 * * @param context * @param packageName * @param audioSession * System wide unique audio session identifier. * @param audioSession * @param key * @param arg0 * @param arg1 * @return parameter value */ public static int getParameterInt(final Context context, final String packageName, final int audioSession, final Key key, final int arg0, final int arg1) { return getParameterInt(context, packageName, audioSession, key.toString() + arg0 + "_" + arg1); } /** * Gets integer array parameter given key. Returns null if not found. * * @param context * @param packageName * @param audioSession * System wide unique audio session identifier. * @param key * @return parameter value array */ public static int[] getParameterIntArray(final Context context, final String packageName, final int audioSession, final Key key) { final SharedPreferences prefs = context.getSharedPreferences(packageName, Context.MODE_PRIVATE); int[] intArray = null; try { // Get effect parameters switch (key) { case eq_level_range: { intArray = new int[2]; break; } case eq_center_freq: // Fall through case eq_band_level: // Fall through case eq_preset_user_band_level: // Fall through case eq_preset_user_band_level_default: // Fall through case eq_preset_ci_extreme_band_level: { final int numBands = prefs.getInt(Key.eq_num_bands.toString(), 0); intArray = new int[numBands]; break; } default: Log.e(TAG, "getParameterIntArray: Unknown/unsupported key " + key); return null; } for (int i = 0; i < intArray.length; i++) { intArray[i] = prefs.getInt(key.toString() + i, 0); } } catch (final RuntimeException e) { Log.e(TAG, "getParameterIntArray: " + key + "; " + e); } return intArray; } /** * Gets string parameter given key. Returns empty string if not found. * * @param context * @param packageName * @param audioSession * System wide unique audio session identifier. * @param key * @return parameter value */ public static String getParameterString(final Context context, final String packageName, final int audioSession, final String key) { String value = ""; try { final SharedPreferences prefs = context.getSharedPreferences(packageName, Context.MODE_PRIVATE); // Get effect parameters value = prefs.getString(key, value); } catch (final RuntimeException e) { Log.e(TAG, "getParameterString: " + key + "; " + e); } return value; } /** * Gets string parameter given key. * * @param context * @param packageName * @param audioSession * System wide unique audio session identifier. * @param key * @return parameter value */ public static String getParameterString(final Context context, final String packageName, final int audioSession, final Key key) { return getParameterString(context, packageName, audioSession, key.toString()); } /** * Gets string parameter given key and arg. * * @param context * @param packageName * @param audioSession * System wide unique audio session identifier. * @param args * @return parameter value */ public static String getParameterString(final Context context, final String packageName, final int audioSession, final Key key, final int arg) { return getParameterString(context, packageName, audioSession, key.toString() + arg); } /** * Opens/initializes the effects session for the given audio session with preferences linked to * the given package name and context. * * @param context * @param packageName * @param audioSession * System wide unique audio session identifier. */ public static void openSession(final Context context, final String packageName, final int audioSession) { Log.v(TAG, "openSession(" + context + ", " + packageName + ", " + audioSession + ")"); final String methodTag = "openSession: "; // init preferences final SharedPreferences prefs = context.getSharedPreferences(packageName, Context.MODE_PRIVATE); final SharedPreferences.Editor editor = prefs.edit(); final boolean isGlobalEnabled = prefs.getBoolean(Key.global_enabled.toString(), GLOBAL_ENABLED_DEFAULT); editor.putBoolean(Key.global_enabled.toString(), isGlobalEnabled); if (!isGlobalEnabled) { return; } // Manage audioSession information // Retrieve AudioSession Id from map boolean isExistingAudioSession = false; try { final Integer currentAudioSession = mPackageSessions.putIfAbsent(packageName, audioSession); if (currentAudioSession != null) { // Compare with passed argument if (currentAudioSession == audioSession) { // FIXME: Normally, we should exit the function here // BUT: we have to take care of the virtualizer because of // a bug in the Android Effects Framework // editor.commit(); // return; isExistingAudioSession = true; } else { closeSession(context, packageName, currentAudioSession); } } } catch (final NullPointerException e) { Log.e(TAG, methodTag + e); editor.commit(); return; } // Because the audioSession is new, get effects & settings from shared preferences // Virtualizer // create effect final Virtualizer virtualizerEffect = getVirtualizerEffect(audioSession); { final String errorTag = methodTag + "Virtualizer error: "; try { // read parameters final boolean isEnabled = prefs.getBoolean(Key.virt_enabled.toString(), VIRTUALIZER_ENABLED_DEFAULT); final int strength = prefs.getInt(Key.virt_strength.toString(), VIRTUALIZER_STRENGTH_DEFAULT); // init settings Virtualizer.Settings settings = new Virtualizer.Settings("Virtualizer;strength=" + strength); virtualizerEffect.setProperties(settings); // set parameters if (isGlobalEnabled == true) { virtualizerEffect.setEnabled(isEnabled); } else { virtualizerEffect.setEnabled(false); } // get parameters settings = virtualizerEffect.getProperties(); Log.v(TAG, "Parameters: " + settings.toString() + ";enabled=" + isEnabled); // update preferences editor.putBoolean(Key.virt_enabled.toString(), isEnabled); editor.putInt(Key.virt_strength.toString(), settings.strength); } catch (final RuntimeException e) { Log.e(TAG, errorTag + e); } } // In case of an existing audio session // Exit after the virtualizer has been re-enabled if (isExistingAudioSession) { editor.commit(); return; } // BassBoost // create effect final BassBoost bassBoostEffect = getBassBoostEffect(audioSession); { final String errorTag = methodTag + "BassBoost error: "; try { // read parameters final boolean isEnabled = prefs.getBoolean(Key.bb_enabled.toString(), BASS_BOOST_ENABLED_DEFAULT); final int strength = prefs.getInt(Key.bb_strength.toString(), BASS_BOOST_STRENGTH_DEFAULT); // init settings BassBoost.Settings settings = new BassBoost.Settings("BassBoost;strength=" + strength); bassBoostEffect.setProperties(settings); // set parameters if (isGlobalEnabled == true) { bassBoostEffect.setEnabled(isEnabled); } else { bassBoostEffect.setEnabled(false); } // get parameters settings = bassBoostEffect.getProperties(); Log.v(TAG, "Parameters: " + settings.toString() + ";enabled=" + isEnabled); // update preferences editor.putBoolean(Key.bb_enabled.toString(), isEnabled); editor.putInt(Key.bb_strength.toString(), settings.strength); } catch (final RuntimeException e) { Log.e(TAG, errorTag + e); } } // Equalizer // create effect final Equalizer equalizerEffect = getEqualizerEffect(audioSession); { final String errorTag = methodTag + "Equalizer error: "; try { final short eQNumBands; final short[] bandLevel; final int[] eQCenterFreq; final short eQNumPresets; final String[] eQPresetNames; short eQPreset; synchronized (mEQInitLock) { // read parameters mEQBandLevelRange = equalizerEffect.getBandLevelRange(); mEQNumBands = equalizerEffect.getNumberOfBands(); mEQCenterFreq = new int[mEQNumBands]; mEQNumPresets = equalizerEffect.getNumberOfPresets(); mEQPresetNames = new String[mEQNumPresets]; for (short preset = 0; preset < mEQNumPresets; preset++) { mEQPresetNames[preset] = equalizerEffect.getPresetName(preset); editor.putString(Key.eq_preset_name.toString() + preset, mEQPresetNames[preset]); } editor.putInt(Key.eq_level_range.toString() + 0, mEQBandLevelRange[0]); editor.putInt(Key.eq_level_range.toString() + 1, mEQBandLevelRange[1]); editor.putInt(Key.eq_num_bands.toString(), mEQNumBands); editor.putInt(Key.eq_num_presets.toString(), mEQNumPresets); // Resetting the EQ arrays depending on the real # bands with defaults if band < // default size else 0 by copying default arrays over new ones final short[] eQPresetCIExtremeBandLevel = Arrays.copyOf( EQUALIZER_PRESET_CIEXTREME_BAND_LEVEL, mEQNumBands); final short[] eQPresetUserBandLevelDefault = Arrays.copyOf( EQUALIZER_PRESET_USER_BAND_LEVEL_DEFAULT, mEQNumBands); // If no preset prefs set use CI EXTREME (= numPresets) eQPreset = (short) prefs .getInt(Key.eq_current_preset.toString(), mEQNumPresets); if (eQPreset < mEQNumPresets) { // OpenSL ES effect presets equalizerEffect.usePreset(eQPreset); eQPreset = equalizerEffect.getCurrentPreset(); } else { for (short band = 0; band < mEQNumBands; band++) { short level = 0; if (eQPreset == mEQNumPresets) { // CI EXTREME level = eQPresetCIExtremeBandLevel[band]; } else { // User level = (short) prefs.getInt( Key.eq_preset_user_band_level.toString() + band, eQPresetUserBandLevelDefault[band]); } equalizerEffect.setBandLevel(band, level); } } editor.putInt(Key.eq_current_preset.toString(), eQPreset); bandLevel = new short[mEQNumBands]; for (short band = 0; band < mEQNumBands; band++) { mEQCenterFreq[band] = equalizerEffect.getCenterFreq(band); bandLevel[band] = equalizerEffect.getBandLevel(band); editor.putInt(Key.eq_band_level.toString() + band, bandLevel[band]); editor.putInt(Key.eq_center_freq.toString() + band, mEQCenterFreq[band]); editor.putInt(Key.eq_preset_ci_extreme_band_level.toString() + band, eQPresetCIExtremeBandLevel[band]); editor.putInt(Key.eq_preset_user_band_level_default.toString() + band, eQPresetUserBandLevelDefault[band]); } eQNumBands = mEQNumBands; eQCenterFreq = mEQCenterFreq; eQNumPresets = mEQNumPresets; eQPresetNames = mEQPresetNames; } final boolean isEnabled = prefs.getBoolean(Key.eq_enabled.toString(), EQUALIZER_ENABLED_DEFAULT); editor.putBoolean(Key.eq_enabled.toString(), isEnabled); if (isGlobalEnabled == true) { equalizerEffect.setEnabled(isEnabled); } else { equalizerEffect.setEnabled(false); } // dump Log.v(TAG, "Parameters: Equalizer"); Log.v(TAG, "bands=" + eQNumBands); String str = "levels="; for (short band = 0; band < eQNumBands; band++) { str = str + bandLevel[band] + "; "; } Log.v(TAG, str); str = "center="; for (short band = 0; band < eQNumBands; band++) { str = str + eQCenterFreq[band] + "; "; } Log.v(TAG, str); str = "presets="; for (short preset = 0; preset < eQNumPresets; preset++) { str = str + eQPresetNames[preset] + "; "; } Log.v(TAG, str); Log.v(TAG, "current=" + eQPreset); } catch (final RuntimeException e) { Log.e(TAG, errorTag + e); } } // XXX: Preset Reverb not used for the moment, so commented out the effect creation to not // use MIPS left in the code for (future) reference. // Preset reverb // create effect // final PresetReverb presetReverbEffect = getPresetReverbEffect(audioSession); // { // final String errorTag = methodTag + "PresetReverb error: "; // // try { // // read parameters // final boolean isEnabled = prefs.getBoolean(Key.pr_enabled.toString(), // PRESET_REVERB_ENABLED_DEFAULT); // final short preset = (short) prefs.getInt(Key.pr_current_preset.toString(), // PRESET_REVERB_CURRENT_PRESET_DEFAULT); // // // init settings // PresetReverb.Settings settings = new PresetReverb.Settings("PresetReverb;preset=" // + preset); // // // read/update preferences // presetReverbEffect.setProperties(settings); // // // set parameters // if (isGlobalEnabled == true) { // presetReverbEffect.setEnabled(isEnabled); // } else { // presetReverbEffect.setEnabled(false); // } // // // get parameters // settings = presetReverbEffect.getProperties(); // Log.v(TAG, "Parameters: " + settings.toString() + ";enabled=" + isEnabled); // // // update preferences // editor.putBoolean(Key.pr_enabled.toString(), isEnabled); // editor.putInt(Key.pr_current_preset.toString(), settings.preset); // } catch (final RuntimeException e) { // Log.e(TAG, errorTag + e); // } // } editor.commit(); } /** * Closes the audio session (release effects) for the given session * * @param context * @param packageName * @param audioSession * System wide unique audio session identifier. */ public static void closeSession(final Context context, final String packageName, final int audioSession) { Log.v(TAG, "closeSession(" + context + ", " + packageName + ", " + audioSession + ")"); // PresetReverb final PresetReverb presetReverb = mPresetReverbInstances.remove(audioSession); if (presetReverb != null) { presetReverb.release(); } // Equalizer final Equalizer equalizer = mEQInstances.remove(audioSession); if (equalizer != null) { equalizer.release(); } // BassBoost final BassBoost bassBoost = mBassBoostInstances.remove(audioSession); if (bassBoost != null) { bassBoost.release(); } // Virtualizer final Virtualizer virtualizer = mVirtualizerInstances.remove(audioSession); if (virtualizer != null) { virtualizer.release(); } mPackageSessions.remove(packageName); } /** * Enables or disables all effects (global enable/disable) for a given context, package name and * audio session. It sets/inits the control mode and preferences and then sets the global * enabled parameter. * * @param context * @param packageName * @param audioSession * System wide unique audio session identifier. * @param enabled */ public static void setEnabledAll(final Context context, final String packageName, final int audioSession, final boolean enabled) { initEffectsPreferences(context, packageName, audioSession); setParameterBoolean(context, packageName, audioSession, Key.global_enabled, enabled); } /** * Gets the virtualizer effect for the given audio session. If the effect on the session doesn't * exist yet, create it and add to collection. * * @param audioSession * System wide unique audio session identifier. * @return virtualizerEffect */ private static Virtualizer getVirtualizerEffectNoCreate(final int audioSession) { return mVirtualizerInstances.get(audioSession); } private static Virtualizer getVirtualizerEffect(final int audioSession) { Virtualizer virtualizerEffect = getVirtualizerEffectNoCreate(audioSession); if (virtualizerEffect == null) { try { final Virtualizer newVirtualizerEffect = new Virtualizer(PRIORITY, audioSession); virtualizerEffect = mVirtualizerInstances.putIfAbsent(audioSession, newVirtualizerEffect); if (virtualizerEffect == null) { // put succeeded, use new value virtualizerEffect = newVirtualizerEffect; } } catch (final IllegalArgumentException e) { Log.e(TAG, "Virtualizer: " + e); } catch (final UnsupportedOperationException e) { Log.e(TAG, "Virtualizer: " + e); } catch (final RuntimeException e) { Log.e(TAG, "Virtualizer: " + e); } } return virtualizerEffect; } /** * Gets the bass boost effect for the given audio session. If the effect on the session doesn't * exist yet, create it and add to collection. * * @param audioSession * System wide unique audio session identifier. * @return bassBoostEffect */ private static BassBoost getBassBoostEffectNoCreate(final int audioSession) { return mBassBoostInstances.get(audioSession); } private static BassBoost getBassBoostEffect(final int audioSession) { BassBoost bassBoostEffect = getBassBoostEffectNoCreate(audioSession); if (bassBoostEffect == null) { try { final BassBoost newBassBoostEffect = new BassBoost(PRIORITY, audioSession); bassBoostEffect = mBassBoostInstances.putIfAbsent(audioSession, newBassBoostEffect); if (bassBoostEffect == null) { // put succeeded, use new value bassBoostEffect = newBassBoostEffect; } } catch (final IllegalArgumentException e) { Log.e(TAG, "BassBoost: " + e); } catch (final UnsupportedOperationException e) { Log.e(TAG, "BassBoost: " + e); } catch (final RuntimeException e) { Log.e(TAG, "BassBoost: " + e); } } return bassBoostEffect; } /** * Gets the equalizer effect for the given audio session. If the effect on the session doesn't * exist yet, create it and add to collection. * * @param audioSession * System wide unique audio session identifier. * @return equalizerEffect */ private static Equalizer getEqualizerEffectNoCreate(final int audioSession) { return mEQInstances.get(audioSession); } private static Equalizer getEqualizerEffect(final int audioSession) { Equalizer equalizerEffect = getEqualizerEffectNoCreate(audioSession); if (equalizerEffect == null) { try { final Equalizer newEqualizerEffect = new Equalizer(PRIORITY, audioSession); equalizerEffect = mEQInstances.putIfAbsent(audioSession, newEqualizerEffect); if (equalizerEffect == null) { // put succeeded, use new value equalizerEffect = newEqualizerEffect; } } catch (final IllegalArgumentException e) { Log.e(TAG, "Equalizer: " + e); } catch (final UnsupportedOperationException e) { Log.e(TAG, "Equalizer: " + e); } catch (final RuntimeException e) { Log.e(TAG, "Equalizer: " + e); } } return equalizerEffect; } // XXX: Preset Reverb not used for the moment, so commented out the effect creation to not // use MIPS // /** // * Gets the preset reverb effect for the given audio session. If the effect on the session // * doesn't exist yet, create it and add to collection. // * // * @param audioSession // * System wide unique audio session identifier. // * @return presetReverbEffect // */ // private static PresetReverb getPresetReverbEffect(final int audioSession) { // PresetReverb presetReverbEffect = mPresetReverbInstances.get(audioSession); // if (presetReverbEffect == null) { // try { // final PresetReverb newPresetReverbEffect = new PresetReverb(PRIORITY, audioSession); // presetReverbEffect = mPresetReverbInstances.putIfAbsent(audioSession, // newPresetReverbEffect); // if (presetReverbEffect == null) { // // put succeeded, use new value // presetReverbEffect = newPresetReverbEffect; // } // } catch (final IllegalArgumentException e) { // Log.e(TAG, "PresetReverb: " + e); // } catch (final UnsupportedOperationException e) { // Log.e(TAG, "PresetReverb: " + e); // } catch (final RuntimeException e) { // Log.e(TAG, "PresetReverb: " + e); // } // } // return presetReverbEffect; // } }