diff options
author | Steve Kondik <steve@cyngn.com> | 2016-03-22 11:21:43 -0700 |
---|---|---|
committer | Steve Kondik <steve@cyngn.com> | 2016-03-24 09:13:14 -0700 |
commit | 70f9b012d9200faf044395570f7fea9b06b0e306 (patch) | |
tree | e40e477c18de50c0cc956c81db695e55a911e5f3 /src | |
parent | 6aa40a7e3785d4f573f8d51f2c3c438434f4cee2 (diff) | |
download | android_packages_apps_AudioFX-70f9b012d9200faf044395570f7fea9b06b0e306.tar.gz android_packages_apps_AudioFX-70f9b012d9200faf044395570f7fea9b06b0e306.tar.bz2 android_packages_apps_AudioFX-70f9b012d9200faf044395570f7fea9b06b0e306.zip |
audiofx: Fix a lot of state management issues
* Centralized control of effects under the backend code
* Implemented caches for all effects
* Simplified effect backend detection
* Fixed multiple instantiation of effects on the same session
Change-Id: I1bb97fb4e34cf3893208e827fb8981f872daf39b
Diffstat (limited to 'src')
-rw-r--r-- | src/com/cyngn/audiofx/activity/MasterConfigControl.java | 17 | ||||
-rw-r--r-- | src/com/cyngn/audiofx/backends/AndroidEffects.java | 79 | ||||
-rw-r--r-- | src/com/cyngn/audiofx/backends/EffectSet.java | 53 | ||||
-rw-r--r-- | src/com/cyngn/audiofx/backends/EffectSetWithAndroidEq.java | 47 | ||||
-rw-r--r-- | src/com/cyngn/audiofx/backends/EffectsFactory.java | 66 | ||||
-rw-r--r-- | src/com/cyngn/audiofx/knobs/KnobCommander.java | 4 | ||||
-rw-r--r-- | src/com/cyngn/audiofx/service/AudioFxService.java | 31 |
7 files changed, 185 insertions, 112 deletions
diff --git a/src/com/cyngn/audiofx/activity/MasterConfigControl.java b/src/com/cyngn/audiofx/activity/MasterConfigControl.java index ec36613..fc67dbc 100644 --- a/src/com/cyngn/audiofx/activity/MasterConfigControl.java +++ b/src/com/cyngn/audiofx/activity/MasterConfigControl.java @@ -49,8 +49,6 @@ public class MasterConfigControl { private final Context mContext; - private boolean mHasMaxxAudio; - private AudioFxService.LocalBinder mService; private ServiceConnection mServiceConnection; private int mServiceRefCount = 0; @@ -79,14 +77,9 @@ public class MasterConfigControl { mCallbacks = new StateCallbacks(this); mEqManager = new EqualizerManager(context, this); - - mHasMaxxAudio = getGlobalPrefs() - .getBoolean(Constants.AUDIOFX_GLOBAL_HAS_MAXXAUDIO, false); } public void onResetDefaults() { - mHasMaxxAudio = getGlobalPrefs() - .getBoolean(Constants.AUDIOFX_GLOBAL_HAS_MAXXAUDIO, false); mEqManager.applyDefaults(); } @@ -284,13 +277,21 @@ public class MasterConfigControl { } public boolean hasMaxxAudio() { - return mHasMaxxAudio; + return getGlobalPrefs().getBoolean(Constants.AUDIOFX_GLOBAL_HAS_MAXXAUDIO, false); } public boolean getMaxxVolumeEnabled() { return getPrefs().getBoolean(Constants.DEVICE_AUDIOFX_MAXXVOLUME_ENABLE, false); } + public boolean hasBassBoost() { + return getGlobalPrefs().getBoolean(Constants.AUDIOFX_GLOBAL_HAS_BASSBOOST, false); + } + + public boolean hasVirtualizer() { + return getGlobalPrefs().getBoolean(Constants.AUDIOFX_GLOBAL_HAS_VIRTUALIZER, false); + } + public void setMaxxVolumeEnabled(boolean enable) { getPrefs().edit().putBoolean(Constants.DEVICE_AUDIOFX_MAXXVOLUME_ENABLE, enable).apply(); updateService(AudioFxService.VOLUME_BOOST_CHANGED); diff --git a/src/com/cyngn/audiofx/backends/AndroidEffects.java b/src/com/cyngn/audiofx/backends/AndroidEffects.java index 88927a3..7e2811c 100644 --- a/src/com/cyngn/audiofx/backends/AndroidEffects.java +++ b/src/com/cyngn/audiofx/backends/AndroidEffects.java @@ -1,31 +1,33 @@ package com.cyngn.audiofx.backends; +import android.media.audiofx.AudioEffect; import android.media.audiofx.BassBoost; import android.media.audiofx.PresetReverb; import android.media.audiofx.Virtualizer; +import android.util.Log; +import android.util.SparseArray; /** * EffectSet which comprises standard Android effects */ class AndroidEffects extends EffectSetWithAndroidEq { + private final SparseArray<Short> mCache = new SparseArray<Short>(); + /** * Session-specific bassboost */ private final BassBoost mBassBoost; - private boolean mBassBoostEnabled = false; /** * Session-specific virtualizer */ private final Virtualizer mVirtualizer; - private boolean mVirtualizerEnabled = false; /** * Session-specific reverb */ private final PresetReverb mPresetReverb; - private boolean mPresetReverbEnabled = false; public AndroidEffects(int sessionId) { super(sessionId); @@ -34,6 +36,8 @@ class AndroidEffects extends EffectSetWithAndroidEq { mBassBoost = new BassBoost(1000, sessionId); mVirtualizer = new Virtualizer(1000, sessionId); mPresetReverb = new PresetReverb(1000, sessionId); + + addEffects(mBassBoost, mVirtualizer, mPresetReverb); } catch (Exception e) { release(); throw e; @@ -52,59 +56,72 @@ class AndroidEffects extends EffectSetWithAndroidEq { @Override public void enableBassBoost(boolean enable) { - mBassBoost.setEnabled(enable); - mBassBoostEnabled = enable; + if (enable == mBassBoost.getEnabled()) { + return; + } + try { + mBassBoost.setEnabled(enable); + } catch (Exception e) { + Log.e(TAG, "Unable to " + (enable ? "enable" : "disable") + " bass boost!", e); + } } @Override public void setBassBoostStrength(short strength) { - if (mBassBoostEnabled) { - mBassBoost.setStrength(strength); - } + setParameterSafe(mBassBoost, BassBoost.PARAM_STRENGTH, strength); } @Override public void enableVirtualizer(boolean enable) { - mVirtualizer.setEnabled(enable); - mVirtualizerEnabled = enable; + if (enable == mVirtualizer.getEnabled()) { + return; + } + try { + mVirtualizer.setEnabled(enable); + } catch (Exception e) { + Log.e(TAG, "Unable to " + (enable ? "enable" : "disable") + " virtualizer!", e); + } } @Override public void setVirtualizerStrength(short strength) { - if (mVirtualizerEnabled) { - mVirtualizer.setStrength(strength); - } + setParameterSafe(mVirtualizer, Virtualizer.PARAM_STRENGTH, strength); } @Override public void enableReverb(boolean enable) { - mPresetReverb.setEnabled(enable); - mPresetReverbEnabled = enable; + if (enable == mPresetReverb.getEnabled()) { + return; + } + try { + mPresetReverb.setEnabled(enable); + } catch (Exception e) { + Log.e(TAG, "Unable to " + (enable ? "enable" : "disable") + " preset reverb!", e); + } } @Override public void setReverbPreset(short preset) { - if (mPresetReverbEnabled) { - mPresetReverb.setPreset(preset); - } + setParameterSafe(mPresetReverb, PresetReverb.PARAM_PRESET, preset); } @Override - public void release() { - super.release(); - if (mPresetReverb != null) { - mPresetReverb.release(); + public int getBrand() { + return EffectsFactory.ANDROID; + } + + private synchronized void setParameterSafe(AudioEffect e, int p, short v) { + if (mCache.indexOfKey(p) >= 0 && v == mCache.get(p)) { + return; } - if (mBassBoost != null) { - mBassBoost.release(); + if (!e.hasControl()) { + return; } - if (mVirtualizer != null) { - mVirtualizer.release(); + try { + e.setParameter(p, v); + mCache.put(p, v); + } catch (Exception ex) { + Log.e(TAG, "Failed to set param " + p + " for effect " + e.getDescriptor().name, ex); } } - - @Override - public int getBrand() { - return EffectsFactory.ANDROID; - } } diff --git a/src/com/cyngn/audiofx/backends/EffectSet.java b/src/com/cyngn/audiofx/backends/EffectSet.java index b31a2b8..248dc01 100644 --- a/src/com/cyngn/audiofx/backends/EffectSet.java +++ b/src/com/cyngn/audiofx/backends/EffectSet.java @@ -1,9 +1,10 @@ package com.cyngn.audiofx.backends; -import android.content.Context; -import android.media.audiofx.Equalizer; +import android.media.audiofx.AudioEffect; +import android.util.Log; -import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; /** * Helper class representing the full complement of effects attached to one @@ -11,7 +12,11 @@ import java.util.List; */ public abstract class EffectSet { - private final int mSessionId; + protected static final String TAG = "AudioFx-EffectSet"; + + protected final int mSessionId; + + protected final ArrayList<AudioEffect> mEffects = new ArrayList<AudioEffect>(); public EffectSet(int sessionId) { mSessionId = sessionId; @@ -40,7 +45,7 @@ public abstract class EffectSet { /** * @param levels in decibels */ - public void setEqualizerLevelsDecibels(float[] levels) { } + public abstract void setEqualizerLevelsDecibels(float[] levels); public abstract short getNumEqualizerBands(); @@ -106,18 +111,38 @@ public abstract class EffectSet { return; } - public abstract void release(); + public synchronized void release() { + for (AudioEffect e : mEffects) { + Log.d(TAG, "releasing effect: " + e.getDescriptor().name); + try { + e.release(); + } catch (Exception ex) { + Log.e(TAG, ex.getMessage(), ex); + } + } + mEffects.clear(); + } - public void disableAll() { - enableBassBoost(false); - enableVirtualizer(false); - enableEqualizer(false); - enableReverb(false); - enableTrebleBoost(false); - enableVolumeBoost(false); + public boolean isActive() { + return mEffects.size() > 0; } public abstract int getBrand(); - public void setGlobalEnabled(boolean globalEnabled) { } + public void setGlobalEnabled(boolean globalEnabled) { + if (globalEnabled) { + return; + } + for (AudioEffect e : mEffects) { + try { + e.setEnabled(false); + } catch (Exception ex) { + Log.e(TAG, ex.getMessage(), ex); + } + } + } + + protected void addEffects(AudioEffect... effects) { + mEffects.addAll(Arrays.asList(effects)); + } } diff --git a/src/com/cyngn/audiofx/backends/EffectSetWithAndroidEq.java b/src/com/cyngn/audiofx/backends/EffectSetWithAndroidEq.java index 5b0cd30..b1248b2 100644 --- a/src/com/cyngn/audiofx/backends/EffectSetWithAndroidEq.java +++ b/src/com/cyngn/audiofx/backends/EffectSetWithAndroidEq.java @@ -1,6 +1,9 @@ package com.cyngn.audiofx.backends; import android.media.audiofx.Equalizer; +import android.util.Log; +import android.util.SparseArray; + import com.cyngn.audiofx.eq.EqUtils; /** @@ -15,16 +18,14 @@ public abstract class EffectSetWithAndroidEq extends EffectSet { private short mEqNumPresets = -1; private short mEqNumBands = -1; - /* - * Take lots of care to not poke values that don't need - * to be poked- this can cause audible pops. - */ - private boolean mEqualizerEnabled = false; + private final SparseArray<Short> mLevelCache = new SparseArray<Short>(); public EffectSetWithAndroidEq(int sessionId) { super(sessionId); try { mEqualizer = new Equalizer(1000, sessionId); + + addEffects(mEqualizer); } catch (Exception e) { release(); throw e; @@ -33,19 +34,21 @@ public abstract class EffectSetWithAndroidEq extends EffectSet { public void enableEqualizer(boolean enable) { - if (enable != mEqualizerEnabled) { - mEqualizerEnabled = enable; + if (enable == mEqualizer.getEnabled()) { + return; + } + try { mEqualizer.setEnabled(enable); + } catch (Exception e) { + Log.e(TAG, "enableEqualizer failed! enable=" + enable + " sessionId=" + mSessionId, e); } } @Override public void setEqualizerLevelsDecibels(float[] levels) { - if (mEqualizerEnabled) { - final short[] equalizerLevels = EqUtils.convertDecibelsToMillibelsInShorts(levels); - for (short i = 0; i < equalizerLevels.length; i++) { - mEqualizer.setBandLevel(i, equalizerLevels[i]); - } + final short[] equalizerLevels = EqUtils.convertDecibelsToMillibelsInShorts(levels); + for (short i = 0; i < equalizerLevels.length; i++) { + setBandLevelSafe(i, equalizerLevels[i]); } } @@ -58,9 +61,7 @@ public abstract class EffectSetWithAndroidEq extends EffectSet { @Override public void setEqualizerBandLevel(short band, float level) { - if (mEqualizerEnabled) { - mEqualizer.setBandLevel(band, (short) level); - } + setBandLevelSafe(band, (short)level); } public int getEqualizerBandLevel(short band) { @@ -89,10 +90,18 @@ public abstract class EffectSetWithAndroidEq extends EffectSet { return mEqualizer.getCenterFreq(band); } - @Override - public void release() { - if (mEqualizer != null) { - mEqualizer.release(); + private synchronized void setBandLevelSafe(short band, short level) { + if (!mEqualizer.hasControl()) { + return; + } + if (mLevelCache.indexOfKey((int)band) >= 0 && level == mLevelCache.get((int)band)) { + return; + } + try { + mEqualizer.setBandLevel(band, level); + mLevelCache.put((int)band, level); + } catch (Exception e) { + Log.e(TAG, "Unable to set eq band=" + band + " level=" + level, e); } } } diff --git a/src/com/cyngn/audiofx/backends/EffectsFactory.java b/src/com/cyngn/audiofx/backends/EffectsFactory.java index d37f255..6534e83 100644 --- a/src/com/cyngn/audiofx/backends/EffectsFactory.java +++ b/src/com/cyngn/audiofx/backends/EffectsFactory.java @@ -1,8 +1,7 @@ package com.cyngn.audiofx.backends; import android.content.Context; -import android.content.SharedPreferences; -import com.cyngn.audiofx.Constants; +import android.util.Log; import java.io.File; @@ -14,43 +13,60 @@ import java.io.File; */ public class EffectsFactory { - public static final int ANDROID = 0; - public static final int MAXXAUDIO = 1; - public static final int DTS = 2; + private static final String TAG = "AudioFx-EffectsFactory"; - public static EffectSet createEffectSet(Context context, int sessionId) { - final SharedPreferences prefs = Constants.getGlobalPrefs(context); + public static final int ANDROID = 1; + public static final int MAXXAUDIO = 2; + public static final int DTS = 3; - // dts? - final boolean hasDts = prefs.getBoolean(Constants.AUDIOFX_GLOBAL_HAS_DTS, hasDts()); - if (hasDts) { - return new DtsEffects(context, sessionId); - } - // maxx audio? if it's the very first time we try to init this, the pref won't exist - // and so we assume we have it, because it will get destroyed and cleaned up and regular - // effects will be returned - boolean hasMaxxAudio = prefs.getBoolean(Constants.AUDIOFX_GLOBAL_HAS_MAXXAUDIO, true); + public static EffectSet createEffectSet(Context context, int sessionId) { - if (hasMaxxAudio) { - // try MaxxAudio next, this will throw an exception if unavailable - MaxxAudioEffects fx = null; + EffectSet effects = null; + int brand = getBrand(); + + // dts? + if (brand == DTS) { try { - fx = new MaxxAudioEffects(sessionId); + effects = new DtsEffects(context, sessionId); } catch (Exception e) { - fx = null; + Log.e(TAG, "Unable to create DTS effects!", e); + effects = null; } - // good to go! - if (fx != null) { - return fx; + } else if (brand == MAXXAUDIO) { + // try MaxxAudio next, this will throw an exception if unavailable + try { + effects = new MaxxAudioEffects(sessionId); + } catch (Exception e) { + Log.e(TAG, "Unable to create MaxxAudio effects!", e); + effects = null; } } - return new AndroidEffects(sessionId); + if (effects == null) { + // if this throws, we're screwed, don't bother to recover. these + // are the standard effects that every android device must have, + // and if they don't exist we have bigger problems. + effects = new AndroidEffects(sessionId); + } + + return effects; } public static boolean hasDts() { return new File("***REMOVED***").exists(); } + public static boolean hasMaxxAudio() { + return new File("***REMOVED***").exists(); + } + + public static int getBrand() { + if (hasDts()) { + return DTS; + } else if (hasMaxxAudio()) { + return MAXXAUDIO; + } + return ANDROID; + } } diff --git a/src/com/cyngn/audiofx/knobs/KnobCommander.java b/src/com/cyngn/audiofx/knobs/KnobCommander.java index 657e78d..b045c16 100644 --- a/src/com/cyngn/audiofx/knobs/KnobCommander.java +++ b/src/com/cyngn/audiofx/knobs/KnobCommander.java @@ -62,7 +62,7 @@ public class KnobCommander { } public boolean hasBassBoost() { - return mConfig.getGlobalPrefs().getBoolean(Constants.AUDIOFX_GLOBAL_HAS_BASSBOOST, false); + return mConfig.hasBassBoost(); } public boolean hasTreble() { @@ -70,7 +70,7 @@ public class KnobCommander { } public boolean hasVirtualizer() { - return mConfig.getGlobalPrefs().getBoolean(Constants.AUDIOFX_GLOBAL_HAS_VIRTUALIZER, false); + return mConfig.hasVirtualizer(); } public boolean isBassEffectEnabled() { diff --git a/src/com/cyngn/audiofx/service/AudioFxService.java b/src/com/cyngn/audiofx/service/AudioFxService.java index d4c595e..3e0b959 100644 --- a/src/com/cyngn/audiofx/service/AudioFxService.java +++ b/src/com/cyngn/audiofx/service/AudioFxService.java @@ -259,21 +259,25 @@ public class AudioFxService extends Service { * msg.obj = sessionId */ sessionId = (Integer) msg.obj; - mHandler.removeMessages(MSG_REMOVE_SESSION, sessionId); - mHandler.removeMessages(MSG_UPDATE_FOR_SESSION, sessionId); - try { - session = EffectsFactory.createEffectSet(getApplicationContext(), sessionId); - } catch (Exception e) { - Log.e(TAG, "couldn't create effects for session id: " + sessionId, e); + if (sessionId == 0) { break; } synchronized (mAudioSessionsL) { + mHandler.removeMessages(MSG_REMOVE_SESSION, sessionId); + mHandler.removeMessages(MSG_UPDATE_FOR_SESSION, sessionId); if (mAudioSessionsL.indexOfKey(sessionId) < 0) { + try { + session = EffectsFactory.createEffectSet(getApplicationContext(), sessionId); + } catch (Exception e) { + Log.e(TAG, "couldn't create effects for session id: " + sessionId, + e); + break; + } mAudioSessionsL.put(sessionId, session); if (DEBUG) Log.w(TAG, "added new EffectSet for sessionId=" + sessionId); - Message.obtain(mHandler, MSG_UPDATE_FOR_SESSION, ALL_CHANGED, 0, - sessionId).sendToTarget(); } + mHandler.sendMessageAtFrontOfQueue(Message.obtain(mHandler, MSG_UPDATE_FOR_SESSION, + ALL_CHANGED, 0, sessionId)); } break; @@ -282,12 +286,14 @@ public class AudioFxService extends Service { * msg.obj = sessionId */ sessionId = (Integer) msg.obj; - mHandler.removeMessages(MSG_ADD_SESSION, sessionId); - mHandler.removeMessages(MSG_UPDATE_FOR_SESSION, sessionId); + if (sessionId == 0) { + break; + } synchronized (mAudioSessionsL) { + mHandler.removeMessages(MSG_UPDATE_FOR_SESSION, sessionId); if (mAudioSessionsL.indexOfKey(sessionId) > -1) { final EffectSet effectSet = mAudioSessionsL.removeReturnOld(sessionId); - if (effectSet != null) { + if (effectSet != null && effectSet.isActive()) { effectSet.release(); if (DEBUG) Log.w(TAG, "removed and released sessionId=" + sessionId); } @@ -303,8 +309,8 @@ public class AudioFxService extends Service { if (DEBUG) Log.i(TAG, "Updating to configuration: " + mode); // cancel updates for other effects, let them go through on the last call - mHandler.removeMessages(MSG_UPDATE_FOR_SESSION); synchronized (mAudioSessionsL) { + mHandler.removeMessages(MSG_UPDATE_FOR_SESSION); final int N = mAudioSessionsL.size(); for (int i = 0; i < N; i++) { final int sessionIdKey = mAudioSessionsL.keyAt(i); @@ -738,7 +744,6 @@ public class AudioFxService extends Service { } editor.putString(EQUALIZER_PRESET_NAMES, presetNames.toString()); - editor.putBoolean(AUDIOFX_GLOBAL_HAS_VIRTUALIZER, temp.hasVirtualizer()); editor.putBoolean(AUDIOFX_GLOBAL_HAS_BASSBOOST, temp.hasBassBoost()); editor.putBoolean(AUDIOFX_GLOBAL_HAS_MAXXAUDIO, temp.getBrand() == EffectsFactory.MAXXAUDIO); |