summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Eric Laurent <elaurent@google.com> 2021-09-14 09:45:29 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2021-09-14 09:45:29 +0000
commit0917c8c9672242999abba7001aa0e785d293d782 (patch)
tree3b6efe7fefb0b8a7dbce7cb2e42e406e3c66e90d
parenta58a7d6a4322e7f116083a81490e6035e1905632 (diff)
parent838913c584d0f3a94d35c378f1869a279a4e216a (diff)
Merge "AudioService: initialize Spatializer, track routing changes" into sc-v2-dev
-rw-r--r--media/java/android/media/AudioManager.java3
-rw-r--r--media/java/android/media/Spatializer.java32
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java51
-rw-r--r--services/core/java/com/android/server/audio/AudioSystemAdapter.java23
-rw-r--r--services/core/java/com/android/server/audio/SpatializerHelper.java387
5 files changed, 462 insertions, 34 deletions
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 5db9ddfac870..ee75a8d6f4b2 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2443,8 +2443,7 @@ public class AudioManager {
/**
* Return a handle to the optional platform's {@link Spatializer}
- * @return {@code null} if spatialization is not supported, the {@code Spatializer} instance
- * otherwise.
+ * @return the {@code Spatializer} instance.
*/
public @Nullable Spatializer getSpatializer() {
int level = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
diff --git a/media/java/android/media/Spatializer.java b/media/java/android/media/Spatializer.java
index 3ed8b58959a1..c64bf2c60117 100644
--- a/media/java/android/media/Spatializer.java
+++ b/media/java/android/media/Spatializer.java
@@ -114,6 +114,7 @@ public class Spatializer {
/** @hide */
@IntDef(flag = false, value = {
+ SPATIALIZER_IMMERSIVE_LEVEL_OTHER,
SPATIALIZER_IMMERSIVE_LEVEL_NONE,
SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL,
})
@@ -122,21 +123,46 @@ public class Spatializer {
/**
* @hide
+ * Constant indicating the {@code Spatializer} on this device supports a spatialization
+ * mode that differs from the ones available at this SDK level.
+ * @see #getImmersiveAudioLevel()
+ */
+ public static final int SPATIALIZER_IMMERSIVE_LEVEL_OTHER = -1;
+
+ /**
+ * @hide
* Constant indicating there are no spatialization capabilities supported on this device.
- * @see AudioManager#getImmersiveAudioLevel()
+ * @see #getImmersiveAudioLevel()
*/
public static final int SPATIALIZER_IMMERSIVE_LEVEL_NONE = 0;
/**
* @hide
- * Constant indicating the {@link Spatializer} on this device supports multichannel
+ * Constant indicating the {@code Spatializer} on this device supports multichannel
* spatialization.
- * @see AudioManager#getImmersiveAudioLevel()
+ * @see #getImmersiveAudioLevel()
*/
public static final int SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL = 1;
/**
* @hide
+ * Return the level of support for the spatialization feature on this device.
+ * This level of support is independent of whether the {@code Spatializer} is currently
+ * enabled or available and will not change over time.
+ * @return the level of spatialization support
+ * @see #isEnabled()
+ * @see #isAvailable()
+ */
+ public @ImmersiveAudioLevel int getImmersiveAudioLevel() {
+ int level = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ try {
+ level = mAm.getService().getSpatializerImmersiveAudioLevel();
+ } catch (Exception e) { /* using NONE */ }
+ return level;
+ }
+
+ /**
+ * @hide
* Enables / disables the spatializer effect.
* Changing the enabled state will trigger the public
* {@link OnSpatializerStateChangedListener#onSpatializerEnabledChanged(Spatializer, boolean)}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index b9b90c0f64ea..f2ba42c6ceea 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -200,7 +200,8 @@ import java.util.stream.Collectors;
*/
public class AudioService extends IAudioService.Stub
implements AccessibilityManager.TouchExplorationStateChangeListener,
- AccessibilityManager.AccessibilityServicesStateChangeListener {
+ AccessibilityManager.AccessibilityServicesStateChangeListener,
+ AudioSystemAdapter.OnRoutingUpdatedListener {
private static final String TAG = "AS.AudioService";
@@ -314,12 +315,14 @@ public class AudioService extends IAudioService.Stub
private static final int MSG_SET_A2DP_DEV_CONNECTION_STATE = 38;
private static final int MSG_A2DP_DEV_CONFIG_CHANGE = 39;
private static final int MSG_DISPATCH_AUDIO_MODE = 40;
+ private static final int MSG_ROUTING_UPDATED = 41;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
private static final int MSG_DISABLE_AUDIO_FOR_UID = 100;
private static final int MSG_INIT_STREAMS_VOLUMES = 101;
+ private static final int MSG_INIT_SPATIALIZER = 102;
// end of messages handled under wakelock
// retry delay in case of failure to indicate system ready to AudioFlinger
@@ -869,6 +872,8 @@ public class AudioService extends IAudioService.Stub
mSfxHelper = new SoundEffectsHelper(mContext);
+ mSpatializerHelper = new SpatializerHelper(this, mAudioSystem);
+
mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
mHasVibrator = mVibrator == null ? false : mVibrator.hasVibrator();
@@ -1033,6 +1038,9 @@ public class AudioService extends IAudioService.Stub
// done with service initialization, continue additional work in our Handler thread
queueMsgUnderWakeLock(mAudioHandler, MSG_INIT_STREAMS_VOLUMES,
0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */);
+ queueMsgUnderWakeLock(mAudioHandler, MSG_INIT_SPATIALIZER,
+ 0 /* arg1 */, 0 /* arg2 */, null /* obj */, 0 /* delay */);
+
}
/**
@@ -1222,6 +1230,22 @@ public class AudioService extends IAudioService.Stub
updateVibratorInfos();
}
+ //-----------------------------------------------------------------
+ // routing monitoring from AudioSystemAdapter
+ @Override
+ public void onRoutingUpdatedFromNative() {
+ sendMsg(mAudioHandler,
+ MSG_ROUTING_UPDATED,
+ SENDMSG_REPLACE, 0, 0, null,
+ /*delay*/ 0);
+ }
+
+ void monitorRoutingChanges(boolean enabled) {
+ mAudioSystem.setRoutingListener(enabled ? this : null);
+ }
+
+
+ //-----------------------------------------------------------------
RoleObserver mRoleObserver;
class RoleObserver implements OnRoleHoldersChangedListener {
@@ -1406,6 +1430,9 @@ public class AudioService extends IAudioService.Stub
}
}
+ // TODO check property if feature enabled
+ mSpatializerHelper.reset(/* featureEnabled */ true);
+
onIndicateSystemReady();
// indicate the end of reconfiguration phase to audio HAL
AudioSystem.setParameters("restarting=false");
@@ -7539,6 +7566,13 @@ public class AudioService extends IAudioService.Stub
mAudioEventWakeLock.release();
break;
+ case MSG_INIT_SPATIALIZER:
+ mSpatializerHelper.init();
+ // TODO read property to see if enabled
+ mSpatializerHelper.setFeatureEnabled(true);
+ mAudioEventWakeLock.release();
+ break;
+
case MSG_CHECK_MUSIC_ACTIVE:
onCheckMusicActive((String) msg.obj);
break;
@@ -7671,6 +7705,10 @@ public class AudioService extends IAudioService.Stub
case MSG_DISPATCH_AUDIO_MODE:
dispatchMode(msg.arg1);
break;
+
+ case MSG_ROUTING_UPDATED:
+ mSpatializerHelper.onRoutingUpdated();
+ break;
}
}
}
@@ -8239,7 +8277,7 @@ public class AudioService extends IAudioService.Stub
}
//==========================================================================================
- private final SpatializerHelper mSpatializerHelper = new SpatializerHelper();
+ private final @NonNull SpatializerHelper mSpatializerHelper;
private void enforceModifyDefaultAudioEffectsPermission() {
if (mContext.checkCallingOrSelfPermission(
@@ -8249,9 +8287,12 @@ public class AudioService extends IAudioService.Stub
}
}
- /** @see AudioManager#getSpatializerImmersiveAudioLevel() */
+ /**
+ * Returns the immersive audio level that the platform is capable of
+ * @see Spatializer#getImmersiveAudioLevel()
+ */
public int getSpatializerImmersiveAudioLevel() {
- return mSpatializerHelper.getImmersiveAudioLevel();
+ return mSpatializerHelper.getCapableImmersiveAudioLevel();
}
/** @see Spatializer#isEnabled() */
@@ -8267,7 +8308,7 @@ public class AudioService extends IAudioService.Stub
/** @see Spatializer#setSpatializerEnabled(boolean) */
public void setSpatializerEnabled(boolean enabled) {
enforceModifyDefaultAudioEffectsPermission();
- mSpatializerHelper.setEnabled(enabled);
+ mSpatializerHelper.setFeatureEnabled(enabled);
}
/** @see Spatializer#canBeSpatialized() */
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index 6d567807f357..ac212eee21e6 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -17,6 +17,7 @@
package com.android.server.audio;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioSystem;
@@ -24,6 +25,8 @@ import android.media.audiopolicy.AudioMix;
import android.os.SystemClock;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -59,6 +62,9 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback {
private ConcurrentHashMap<AudioAttributes, ArrayList<AudioDeviceAttributes>>
mDevicesForAttrCache;
private int[] mMethodCacheHit;
+ private static final Object sRoutingListenerLock = new Object();
+ @GuardedBy("sRoutingListenerLock")
+ private static @Nullable OnRoutingUpdatedListener sRoutingListener;
/**
* should be false except when trying to debug caching errors. When true, the value retrieved
@@ -76,6 +82,23 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback {
Log.d(TAG, "---- onRoutingUpdated (from native) ----------");
}
invalidateRoutingCache();
+ final OnRoutingUpdatedListener listener;
+ synchronized (sRoutingListenerLock) {
+ listener = sRoutingListener;
+ }
+ if (listener != null) {
+ listener.onRoutingUpdatedFromNative();
+ }
+ }
+
+ interface OnRoutingUpdatedListener {
+ void onRoutingUpdatedFromNative();
+ }
+
+ static void setRoutingListener(@Nullable OnRoutingUpdatedListener listener) {
+ synchronized (sRoutingListenerLock) {
+ sRoutingListener = listener;
+ }
}
/**
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 708d9e10ad73..2ca100c91043 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -17,9 +17,13 @@
package com.android.server.audio;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioFormat;
+import android.media.AudioSystem;
+import android.media.INativeSpatializerCallback;
+import android.media.ISpatializer;
import android.media.ISpatializerCallback;
import android.media.Spatializer;
import android.os.RemoteCallbackList;
@@ -28,6 +32,7 @@ import android.util.Log;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
/**
* A helper class to manage Spatializer related functionality
@@ -35,12 +40,167 @@ import java.util.List;
public class SpatializerHelper {
private static final String TAG = "AS.SpatializerHelper";
+ private static final boolean DEBUG = true;
+
+ private static void logd(String s) {
+ if (DEBUG) {
+ Log.i(TAG, s);
+ }
+ }
+
+ private final @NonNull AudioSystemAdapter mASA;
+ private final @NonNull AudioService mAudioService;
+
+ //------------------------------------------------------------
+ // Spatializer state machine
+ private static final int STATE_UNINITIALIZED = 0;
+ private static final int STATE_NOT_SUPPORTED = 1;
+ private static final int STATE_DISABLED_UNAVAILABLE = 3;
+ private static final int STATE_ENABLED_UNAVAILABLE = 4;
+ private static final int STATE_ENABLED_AVAILABLE = 5;
+ private static final int STATE_DISABLED_AVAILABLE = 6;
+ private int mState = STATE_UNINITIALIZED;
+
+ /** current level as reported by native Spatializer in callback */
+ private int mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ private int mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ private @Nullable ISpatializer mSpat;
+ private @Nullable SpatializerCallback mSpatCallback;
+
+ // default attributes and format that determine basic availability of spatialization
+ private static final AudioAttributes DEFAULT_ATTRIBUTES = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_MEDIA)
+ .build();
+ private static final AudioFormat DEFAULT_FORMAT = new AudioFormat.Builder()
+ .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+ .setSampleRate(48000)
+ .setChannelMask(AudioFormat.CHANNEL_OUT_5POINT1)
+ .build();
+ // device array to store the routing for the default attributes and format, size 1 because
+ // media is never expected to be duplicated
+ private static final AudioDeviceAttributes[] ROUTING_DEVICES = new AudioDeviceAttributes[1];
//---------------------------------------------------------------
// audio device compatibility / enabled
private final ArrayList<AudioDeviceAttributes> mCompatibleAudioDevices = new ArrayList<>(0);
+ //------------------------------------------------------
+ // initialization
+ SpatializerHelper(@NonNull AudioService mother, @NonNull AudioSystemAdapter asa) {
+ mAudioService = mother;
+ mASA = asa;
+ }
+
+ synchronized void init() {
+ Log.i(TAG, "Initializing");
+ if (mState != STATE_UNINITIALIZED) {
+ throw new IllegalStateException(("init() called in state:" + mState));
+ }
+ // is there a spatializer?
+ mSpatCallback = new SpatializerCallback();
+ final ISpatializer spat = AudioSystem.getSpatializer(mSpatCallback);
+ if (spat == null) {
+ Log.i(TAG, "init(): No Spatializer found");
+ mState = STATE_NOT_SUPPORTED;
+ return;
+ }
+ // capabilities of spatializer?
+ try {
+ byte[] levels = spat.getSupportedLevels();
+ if (levels == null
+ || levels.length == 0
+ || (levels.length == 1
+ && levels[0] == Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE)) {
+ Log.e(TAG, "Spatializer is useless");
+ mState = STATE_NOT_SUPPORTED;
+ return;
+ }
+ for (byte level : levels) {
+ logd("found support for level: " + level);
+ if (level == Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL) {
+ logd("Setting Spatializer to LEVEL_MULTICHANNEL");
+ mCapableSpatLevel = level;
+ break;
+ }
+ }
+ } catch (RemoteException e) { /* capable level remains at NONE*/ }
+ if (mCapableSpatLevel == Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE) {
+ mState = STATE_NOT_SUPPORTED;
+ return;
+ }
+ mState = STATE_DISABLED_UNAVAILABLE;
+ // note at this point mSpat is still not instantiated
+ }
+
+ /**
+ * Like init() but resets the state and spatializer levels
+ * @param featureEnabled
+ */
+ synchronized void reset(boolean featureEnabled) {
+ Log.i(TAG, "Resetting");
+ mState = STATE_UNINITIALIZED;
+ mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ init();
+ setFeatureEnabled(featureEnabled);
+ }
+
+ //------------------------------------------------------
+ // routing monitoring
+ void onRoutingUpdated() {
+ switch (mState) {
+ case STATE_UNINITIALIZED:
+ case STATE_NOT_SUPPORTED:
+ return;
+ case STATE_DISABLED_UNAVAILABLE:
+ case STATE_ENABLED_UNAVAILABLE:
+ case STATE_ENABLED_AVAILABLE:
+ case STATE_DISABLED_AVAILABLE:
+ break;
+ }
+ mASA.getDevicesForAttributes(DEFAULT_ATTRIBUTES).toArray(ROUTING_DEVICES);
+ final boolean able =
+ AudioSystem.canBeSpatialized(DEFAULT_ATTRIBUTES, DEFAULT_FORMAT, ROUTING_DEVICES);
+ logd("onRoutingUpdated: can spatialize media 5.1:" + able
+ + " on device:" + ROUTING_DEVICES[0]);
+ setDispatchAvailableState(able);
+ }
+
+ //------------------------------------------------------
+ // spatializer callback from native
+ private final class SpatializerCallback extends INativeSpatializerCallback.Stub {
+
+ public void onLevelChanged(byte level) {
+ logd("SpatializerCallback.onLevelChanged level:" + level);
+ synchronized (SpatializerHelper.this) {
+ mSpatLevel = level;
+ }
+ // TODO use reported spat level to change state
+ }
+
+ public void onHeadTrackingModeChanged(byte mode) {
+ logd("SpatializerCallback.onHeadTrackingModeChanged mode:" + mode);
+ }
+
+ public void onHeadToSoundStagePoseUpdated(float[] headToStage) {
+ if (headToStage == null) {
+ Log.e(TAG, "SpatializerCallback.onHeadToStagePoseUpdated null transform");
+ return;
+ }
+ if (DEBUG) {
+ // 6 values * (4 digits + 1 dot + 2 brackets) = 42 characters
+ StringBuilder t = new StringBuilder(42);
+ for (float val : headToStage) {
+ t.append("[").append(String.format(Locale.ENGLISH, "%.3f", val)).append("]");
+ }
+ logd("SpatializerCallback.onHeadToStagePoseUpdated headToStage:" + t);
+ }
+ }
+ };
+
+ //------------------------------------------------------
+ // compatible devices
/**
* @return a shallow copy of the list of compatible audio devices
*/
@@ -59,37 +219,72 @@ public class SpatializerHelper {
}
//------------------------------------------------------
- // enabled state
-
- // global state of feature
- boolean mFeatureEnabled = false;
- // initialized state, checked after each audio_server start
- boolean mInitialized = false;
+ // states
synchronized boolean isEnabled() {
- return mFeatureEnabled;
+ switch (mState) {
+ case STATE_UNINITIALIZED:
+ case STATE_NOT_SUPPORTED:
+ case STATE_DISABLED_UNAVAILABLE:
+ case STATE_DISABLED_AVAILABLE:
+ return false;
+ case STATE_ENABLED_UNAVAILABLE:
+ case STATE_ENABLED_AVAILABLE:
+ default:
+ return true;
+ }
}
synchronized boolean isAvailable() {
- if (!mInitialized) {
- return false;
+ switch (mState) {
+ case STATE_UNINITIALIZED:
+ case STATE_NOT_SUPPORTED:
+ case STATE_ENABLED_UNAVAILABLE:
+ case STATE_DISABLED_UNAVAILABLE:
+ return false;
+ case STATE_DISABLED_AVAILABLE:
+ case STATE_ENABLED_AVAILABLE:
+ default:
+ return true;
}
- // TODO check device compatibility
- // ...
- return true;
}
- synchronized void setEnabled(boolean enabled) {
- final boolean oldState = mFeatureEnabled;
- mFeatureEnabled = enabled;
- if (oldState != enabled) {
- dispatchEnabledState();
+ synchronized void setFeatureEnabled(boolean enabled) {
+ switch (mState) {
+ case STATE_UNINITIALIZED:
+ if (enabled) {
+ throw(new IllegalStateException("Can't enable when uninitialized"));
+ }
+ return;
+ case STATE_NOT_SUPPORTED:
+ if (enabled) {
+ Log.e(TAG, "Can't enable when unsupported");
+ }
+ return;
+ case STATE_DISABLED_UNAVAILABLE:
+ case STATE_DISABLED_AVAILABLE:
+ if (enabled) {
+ createSpat();
+ break;
+ } else {
+ // already in disabled state
+ return;
+ }
+ case STATE_ENABLED_UNAVAILABLE:
+ case STATE_ENABLED_AVAILABLE:
+ if (!enabled) {
+ releaseSpat();
+ break;
+ } else {
+ // already in enabled state
+ return;
+ }
}
+ setDispatchFeatureEnabledState(enabled);
}
- public int getImmersiveAudioLevel() {
- // TODO replace placeholder code with actual effect discovery
- return Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ synchronized int getCapableImmersiveAudioLevel() {
+ return mCapableSpatLevel;
}
final RemoteCallbackList<ISpatializerCallback> mStateCallbacks =
@@ -105,24 +300,168 @@ public class SpatializerHelper {
mStateCallbacks.unregister(callback);
}
- private synchronized void dispatchEnabledState() {
+ /**
+ * precondition: mState = STATE_*
+ * isFeatureEnabled() != featureEnabled
+ * @param featureEnabled
+ */
+ private synchronized void setDispatchFeatureEnabledState(boolean featureEnabled) {
+ if (featureEnabled) {
+ switch (mState) {
+ case STATE_DISABLED_UNAVAILABLE:
+ mState = STATE_ENABLED_UNAVAILABLE;
+ break;
+ case STATE_DISABLED_AVAILABLE:
+ mState = STATE_ENABLED_AVAILABLE;
+ break;
+ default:
+ throw(new IllegalStateException("Invalid mState:" + mState
+ + " for enabled true"));
+ }
+ } else {
+ switch (mState) {
+ case STATE_ENABLED_UNAVAILABLE:
+ mState = STATE_DISABLED_UNAVAILABLE;
+ break;
+ case STATE_ENABLED_AVAILABLE:
+ mState = STATE_DISABLED_AVAILABLE;
+ break;
+ default:
+ throw (new IllegalStateException("Invalid mState:" + mState
+ + " for enabled false"));
+ }
+ }
final int nbCallbacks = mStateCallbacks.beginBroadcast();
for (int i = 0; i < nbCallbacks; i++) {
try {
mStateCallbacks.getBroadcastItem(i)
- .dispatchSpatializerEnabledChanged(mFeatureEnabled);
+ .dispatchSpatializerEnabledChanged(featureEnabled);
} catch (RemoteException e) {
Log.e(TAG, "Error in dispatchSpatializerEnabledChanged", e);
}
}
mStateCallbacks.finishBroadcast();
+ // TODO persist enabled state
+ }
+
+ private synchronized void setDispatchAvailableState(boolean available) {
+ switch (mState) {
+ case STATE_UNINITIALIZED:
+ case STATE_NOT_SUPPORTED:
+ throw(new IllegalStateException(
+ "Should not update available state in state:" + mState));
+ case STATE_DISABLED_UNAVAILABLE:
+ if (available) {
+ mState = STATE_DISABLED_AVAILABLE;
+ break;
+ } else {
+ // already in unavailable state
+ return;
+ }
+ case STATE_ENABLED_UNAVAILABLE:
+ if (available) {
+ mState = STATE_ENABLED_AVAILABLE;
+ break;
+ } else {
+ // already in unavailable state
+ return;
+ }
+ case STATE_DISABLED_AVAILABLE:
+ if (available) {
+ // already in available state
+ return;
+ } else {
+ mState = STATE_DISABLED_UNAVAILABLE;
+ break;
+ }
+ case STATE_ENABLED_AVAILABLE:
+ if (available) {
+ // already in available state
+ return;
+ } else {
+ mState = STATE_ENABLED_UNAVAILABLE;
+ break;
+ }
+ }
+ final int nbCallbacks = mStateCallbacks.beginBroadcast();
+ for (int i = 0; i < nbCallbacks; i++) {
+ try {
+ mStateCallbacks.getBroadcastItem(i)
+ .dispatchSpatializerAvailableChanged(available);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error in dispatchSpatializerEnabledChanged", e);
+ }
+ }
+ mStateCallbacks.finishBroadcast();
+ }
+
+ //------------------------------------------------------
+ // native Spatializer management
+
+ /**
+ * precondition: mState == STATE_DISABLED_*
+ */
+ private void createSpat() {
+ if (mSpat == null) {
+ mSpatCallback = new SpatializerCallback();
+ mSpat = AudioSystem.getSpatializer(mSpatCallback);
+ try {
+ mSpat.setLevel((byte) Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Can't set spatializer level", e);
+ mState = STATE_NOT_SUPPORTED;
+ mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
+ }
+ }
+ }
+
+ /**
+ * precondition: mState == STATE_ENABLED_*
+ */
+ private void releaseSpat() {
+ if (mSpat != null) {
+ mSpatCallback = null;
+ try {
+ mSpat.release();
+ mSpat = null;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Can't set release spatializer cleanly", e);
+ }
+ }
}
//------------------------------------------------------
// virtualization capabilities
synchronized boolean canBeSpatialized(
@NonNull AudioAttributes attributes, @NonNull AudioFormat format) {
- // TODO hook up to spatializer effect for query
- return false;
+ logd("canBeSpatialized usage:" + attributes.getUsage()
+ + " format:" + format.toLogFriendlyString());
+ switch (mState) {
+ case STATE_UNINITIALIZED:
+ case STATE_NOT_SUPPORTED:
+ case STATE_ENABLED_UNAVAILABLE:
+ case STATE_DISABLED_UNAVAILABLE:
+ logd("canBeSpatialized false due to state:" + mState);
+ return false;
+ case STATE_DISABLED_AVAILABLE:
+ case STATE_ENABLED_AVAILABLE:
+ break;
+ }
+
+ // filter on AudioAttributes usage
+ switch (attributes.getUsage()) {
+ case AudioAttributes.USAGE_MEDIA:
+ case AudioAttributes.USAGE_GAME:
+ break;
+ default:
+ logd("canBeSpatialized false due to usage:" + attributes.getUsage());
+ return false;
+ }
+ AudioDeviceAttributes[] devices =
+ // going through adapter to take advantage of routing cache
+ (AudioDeviceAttributes[]) mASA.getDevicesForAttributes(attributes).toArray();
+ final boolean able = AudioSystem.canBeSpatialized(attributes, format, devices);
+ logd("canBeSpatialized returning " + able);
+ return able;
}
}