summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSteve Kondik <steve@cyngn.com>2016-03-22 11:21:43 -0700
committerSteve Kondik <steve@cyngn.com>2016-03-24 09:13:14 -0700
commit70f9b012d9200faf044395570f7fea9b06b0e306 (patch)
treee40e477c18de50c0cc956c81db695e55a911e5f3 /src
parent6aa40a7e3785d4f573f8d51f2c3c438434f4cee2 (diff)
downloadandroid_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.java17
-rw-r--r--src/com/cyngn/audiofx/backends/AndroidEffects.java79
-rw-r--r--src/com/cyngn/audiofx/backends/EffectSet.java53
-rw-r--r--src/com/cyngn/audiofx/backends/EffectSetWithAndroidEq.java47
-rw-r--r--src/com/cyngn/audiofx/backends/EffectsFactory.java66
-rw-r--r--src/com/cyngn/audiofx/knobs/KnobCommander.java4
-rw-r--r--src/com/cyngn/audiofx/service/AudioFxService.java31
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);