Merge "Immersive audio APIs for headtracking" into sc-v2-dev am: 7783648723
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15605263 Change-Id: I05979b818cc3519c1bbcb3cfa7b89125dc7a9274
This commit is contained in:
@@ -5409,9 +5409,33 @@ package android.media {
|
||||
|
||||
public class Spatializer {
|
||||
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void addCompatibleAudioDevice(@NonNull android.media.AudioDeviceAttributes);
|
||||
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void addOnHeadTrackingModeChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnHeadTrackingModeChangedListener);
|
||||
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void clearOnHeadToSoundstagePoseUpdatedListener();
|
||||
method @NonNull @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public java.util.List<android.media.AudioDeviceAttributes> getCompatibleAudioDevices();
|
||||
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public int getDesiredHeadTrackingMode();
|
||||
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public int getHeadTrackingMode();
|
||||
method @NonNull @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public java.util.List<java.lang.Integer> getSupportedHeadTrackingModes();
|
||||
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void recenterHeadTracker();
|
||||
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void removeCompatibleAudioDevice(@NonNull android.media.AudioDeviceAttributes);
|
||||
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void removeOnHeadTrackingModeChangedListener(@NonNull android.media.Spatializer.OnHeadTrackingModeChangedListener);
|
||||
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setDesiredHeadTrackingMode(int);
|
||||
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setEnabled(boolean);
|
||||
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setGlobalTransform(@NonNull float[]);
|
||||
method @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public void setOnHeadToSoundstagePoseUpdatedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.Spatializer.OnHeadToSoundstagePoseUpdatedListener);
|
||||
field @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public static final int HEAD_TRACKING_MODE_DISABLED = -1; // 0xffffffff
|
||||
field @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public static final int HEAD_TRACKING_MODE_OTHER = 0; // 0x0
|
||||
field @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public static final int HEAD_TRACKING_MODE_RELATIVE_DEVICE = 2; // 0x2
|
||||
field @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public static final int HEAD_TRACKING_MODE_RELATIVE_WORLD = 1; // 0x1
|
||||
field @RequiresPermission("android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS") public static final int HEAD_TRACKING_MODE_UNSUPPORTED = -2; // 0xfffffffe
|
||||
}
|
||||
|
||||
public static interface Spatializer.OnHeadToSoundstagePoseUpdatedListener {
|
||||
method public void onHeadToSoundstagePoseUpdated(@NonNull android.media.Spatializer, @NonNull float[]);
|
||||
}
|
||||
|
||||
public static interface Spatializer.OnHeadTrackingModeChangedListener {
|
||||
method public void onDesiredHeadTrackingModeChanged(@NonNull android.media.Spatializer, int);
|
||||
method public void onHeadTrackingModeChanged(@NonNull android.media.Spatializer, int);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,6 +36,8 @@ import android.media.IRecordingConfigDispatcher;
|
||||
import android.media.IRingtonePlayer;
|
||||
import android.media.IStrategyPreferredDevicesDispatcher;
|
||||
import android.media.ISpatializerCallback;
|
||||
import android.media.ISpatializerHeadTrackingModeCallback;
|
||||
import android.media.ISpatializerHeadToSoundStagePoseCallback;
|
||||
import android.media.IVolumeController;
|
||||
import android.media.IVolumeController;
|
||||
import android.media.PlayerBase;
|
||||
@@ -410,13 +412,33 @@ interface IAudioService {
|
||||
|
||||
boolean canBeSpatialized(in AudioAttributes aa, in AudioFormat af);
|
||||
|
||||
void registerSpatializerCallback(in ISpatializerCallback callback);
|
||||
void registerSpatializerCallback(in ISpatializerCallback cb);
|
||||
|
||||
void unregisterSpatializerCallback(in ISpatializerCallback callback);
|
||||
void unregisterSpatializerCallback(in ISpatializerCallback cb);
|
||||
|
||||
void registerSpatializerHeadTrackingCallback(in ISpatializerHeadTrackingModeCallback cb);
|
||||
|
||||
void unregisterSpatializerHeadTrackingCallback(in ISpatializerHeadTrackingModeCallback cb);
|
||||
|
||||
void registerHeadToSoundstagePoseCallback(in ISpatializerHeadToSoundStagePoseCallback cb);
|
||||
|
||||
void unregisterHeadToSoundstagePoseCallback(in ISpatializerHeadToSoundStagePoseCallback cb);
|
||||
|
||||
List<AudioDeviceAttributes> getSpatializerCompatibleAudioDevices();
|
||||
|
||||
void addSpatializerCompatibleAudioDevice(in AudioDeviceAttributes ada);
|
||||
|
||||
void removeSpatializerCompatibleAudioDevice(in AudioDeviceAttributes ada);
|
||||
|
||||
void setDesiredHeadTrackingMode(int mode);
|
||||
|
||||
int getDesiredHeadTrackingMode();
|
||||
|
||||
int[] getSupportedHeadTrackingModes();
|
||||
|
||||
int getActualHeadTrackingMode();
|
||||
|
||||
oneway void setSpatializerGlobalTransform(in float[] transform);
|
||||
|
||||
oneway void recenterHeadTracker();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 android.media;
|
||||
|
||||
/**
|
||||
* AIDL for the AudioService to signal Spatializer state changes.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
oneway interface ISpatializerHeadToSoundStagePoseCallback {
|
||||
|
||||
/**
|
||||
* The pose is sent as an array of 6 float values, the first 3 are the translation vector, the
|
||||
* other 3 are the rotation vector.
|
||||
*/
|
||||
void dispatchPoseChanged(in float[] pose);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 android.media;
|
||||
|
||||
/**
|
||||
* AIDL for the AudioService to signal Spatializer head tracking mode changes.
|
||||
*
|
||||
* {@hide}
|
||||
*/
|
||||
oneway interface ISpatializerHeadTrackingModeCallback {
|
||||
|
||||
void dispatchSpatializerActualHeadTrackingModeChanged(int mode);
|
||||
|
||||
void dispatchSpatializerDesiredHeadTrackingModeChanged(int mode);
|
||||
}
|
||||
@@ -49,20 +49,8 @@ public class Spatializer {
|
||||
|
||||
private final @NonNull AudioManager mAm;
|
||||
|
||||
private final Object mStateListenerLock = new Object();
|
||||
|
||||
private static final String TAG = "Spatializer";
|
||||
|
||||
/**
|
||||
* List of listeners for state listener and their associated Executor.
|
||||
* List is lazy-initialized on first registration
|
||||
*/
|
||||
@GuardedBy("mStateListenerLock")
|
||||
private @Nullable ArrayList<StateListenerInfo> mStateListeners;
|
||||
|
||||
@GuardedBy("mStateListenerLock")
|
||||
private SpatializerInfoDispatcherStub mInfoDispatcherStub;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Constructor with AudioManager acting as proxy to AudioService
|
||||
@@ -141,6 +129,75 @@ public class Spatializer {
|
||||
*/
|
||||
public static final int SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL = 1;
|
||||
|
||||
/** @hide */
|
||||
@IntDef(flag = false, value = {
|
||||
HEAD_TRACKING_MODE_UNSUPPORTED,
|
||||
HEAD_TRACKING_MODE_DISABLED,
|
||||
HEAD_TRACKING_MODE_RELATIVE_WORLD,
|
||||
HEAD_TRACKING_MODE_RELATIVE_DEVICE,
|
||||
}) public @interface HeadTrackingMode {};
|
||||
|
||||
/** @hide */
|
||||
@IntDef(flag = false, value = {
|
||||
HEAD_TRACKING_MODE_DISABLED,
|
||||
HEAD_TRACKING_MODE_RELATIVE_WORLD,
|
||||
HEAD_TRACKING_MODE_RELATIVE_DEVICE,
|
||||
}) public @interface HeadTrackingModeSet {};
|
||||
|
||||
/** @hide */
|
||||
@IntDef(flag = false, value = {
|
||||
HEAD_TRACKING_MODE_RELATIVE_WORLD,
|
||||
HEAD_TRACKING_MODE_RELATIVE_DEVICE,
|
||||
}) public @interface HeadTrackingModeSupported {};
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Constant indicating head tracking is not supported by this {@code Spatializer}
|
||||
* @see #getHeadTrackingMode()
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
|
||||
@RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
|
||||
public static final int HEAD_TRACKING_MODE_UNSUPPORTED = -2;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Constant indicating head tracking is disabled on this {@code Spatializer}
|
||||
* @see #getHeadTrackingMode()
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
|
||||
@RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
|
||||
public static final int HEAD_TRACKING_MODE_DISABLED = -1;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Constant indicating head tracking is in a mode whose behavior is unknown. This is not an
|
||||
* error state but represents a customized behavior not defined by this API.
|
||||
* @see #getHeadTrackingMode()
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
|
||||
@RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
|
||||
public static final int HEAD_TRACKING_MODE_OTHER = 0;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Constant indicating head tracking is tracking the user's position / orientation relative to
|
||||
* the world around them
|
||||
* @see #getHeadTrackingMode()
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
|
||||
@RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
|
||||
public static final int HEAD_TRACKING_MODE_RELATIVE_WORLD = 1;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Constant indicating head tracking is tracking the user's position / orientation relative to
|
||||
* the device
|
||||
* @see #getHeadTrackingMode()
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
|
||||
@RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
|
||||
public static final int HEAD_TRACKING_MODE_RELATIVE_DEVICE = 2;
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -176,7 +233,7 @@ public class Spatializer {
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface to be notified of changes to the state of the spatializer.
|
||||
* An interface to be notified of changes to the state of the spatializer effect.
|
||||
*/
|
||||
public interface OnSpatializerStateChangedListener {
|
||||
/**
|
||||
@@ -199,6 +256,58 @@ public class Spatializer {
|
||||
void onSpatializerAvailableChanged(@NonNull Spatializer spat, boolean available);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* An interface to be notified of changes to the head tracking mode, used by the spatializer
|
||||
* effect.
|
||||
* Changes to the mode may come from explicitly setting a different mode
|
||||
* (see {@link #setDesiredHeadTrackingMode(int)}) or a change in system conditions (see
|
||||
* {@link #getHeadTrackingMode()}
|
||||
* @see #addOnHeadTrackingModeChangedListener(Executor, OnHeadTrackingModeChangedListener)
|
||||
* @see #removeOnHeadTrackingModeChangedListener(OnHeadTrackingModeChangedListener)
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
|
||||
public interface OnHeadTrackingModeChangedListener {
|
||||
/**
|
||||
* Called when the actual head tracking mode of the spatializer changed.
|
||||
* @param spatializer the {@code Spatializer} instance whose head tracking mode is changing
|
||||
* @param mode the new head tracking mode
|
||||
*/
|
||||
void onHeadTrackingModeChanged(@NonNull Spatializer spatializer,
|
||||
@HeadTrackingMode int mode);
|
||||
|
||||
/**
|
||||
* Called when the desired head tracking mode of the spatializer changed
|
||||
* @param spatializer the {@code Spatializer} instance whose head tracking mode was set
|
||||
* @param mode the newly set head tracking mode
|
||||
*/
|
||||
void onDesiredHeadTrackingModeChanged(@NonNull Spatializer spatializer,
|
||||
@HeadTrackingModeSet int mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* An interface to be notified of updates to the head to soundstage pose, as represented by the
|
||||
* current head tracking mode.
|
||||
* @see #setOnHeadToSoundstagePoseUpdatedListener(Executor, OnHeadToSoundstagePoseUpdatedListener)
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
|
||||
public interface OnHeadToSoundstagePoseUpdatedListener {
|
||||
/**
|
||||
* Called when the head to soundstage transform is updated
|
||||
* @param spatializer the {@code Spatializer} instance affected by the pose update
|
||||
* @param pose the new pose data representing the transform between the frame
|
||||
* of reference for the current head tracking mode (see
|
||||
* {@link #getHeadTrackingMode()}) and the device being tracked (for
|
||||
* instance a pair of headphones with a head tracker).<br>
|
||||
* The head pose data is represented as an array of six float values, where
|
||||
* the first three values are the translation vector, and the next three
|
||||
* are the rotation vector.
|
||||
*/
|
||||
void onHeadToSoundstagePoseUpdated(@NonNull Spatializer spatializer,
|
||||
@NonNull float[] pose);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether audio of the given {@link AudioFormat}, played with the given
|
||||
* {@link AudioAttributes} can be spatialized.
|
||||
@@ -342,6 +451,17 @@ public class Spatializer {
|
||||
}
|
||||
}
|
||||
|
||||
private final Object mStateListenerLock = new Object();
|
||||
/**
|
||||
* List of listeners for state listener and their associated Executor.
|
||||
* List is lazy-initialized on first registration
|
||||
*/
|
||||
@GuardedBy("mStateListenerLock")
|
||||
private @Nullable ArrayList<StateListenerInfo> mStateListeners;
|
||||
|
||||
@GuardedBy("mStateListenerLock")
|
||||
private @Nullable SpatializerInfoDispatcherStub mInfoDispatcherStub;
|
||||
|
||||
private final class SpatializerInfoDispatcherStub extends ISpatializerCallback.Stub {
|
||||
@Override
|
||||
public void dispatchSpatializerEnabledChanged(boolean enabled) {
|
||||
@@ -423,4 +543,378 @@ public class Spatializer {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Return the current head tracking mode as used by the system.
|
||||
* Note this may differ from the desired head tracking mode. Reasons for the two to differ
|
||||
* include: a head tracking device is not available for the current audio output device,
|
||||
* the transmission conditions between the tracker and device have deteriorated and tracking
|
||||
* has been disabled.
|
||||
* @see #getDesiredHeadTrackingMode()
|
||||
* @return the current head tracking mode
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
|
||||
@RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
|
||||
public @HeadTrackingMode int getHeadTrackingMode() {
|
||||
try {
|
||||
return mAm.getService().getActualHeadTrackingMode();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error calling getActualHeadTrackingMode", e);
|
||||
return HEAD_TRACKING_MODE_UNSUPPORTED;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Return the desired head tracking mode.
|
||||
* Note this may differ from the actual head tracking mode, reflected by
|
||||
* {@link #getHeadTrackingMode()}.
|
||||
* @return the desired head tring mode
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
|
||||
@RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
|
||||
public @HeadTrackingMode int getDesiredHeadTrackingMode() {
|
||||
try {
|
||||
return mAm.getService().getDesiredHeadTrackingMode();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error calling getDesiredHeadTrackingMode", e);
|
||||
return HEAD_TRACKING_MODE_UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Returns the list of supported head tracking modes.
|
||||
* @return the list of modes that can be used in {@link #setDesiredHeadTrackingMode(int)} to
|
||||
* enable head tracking. The list will be empty if {@link #getHeadTrackingMode()}
|
||||
* is {@link #HEAD_TRACKING_MODE_UNSUPPORTED}. Values can be
|
||||
* {@link #HEAD_TRACKING_MODE_OTHER},
|
||||
* {@link #HEAD_TRACKING_MODE_RELATIVE_WORLD} or
|
||||
* {@link #HEAD_TRACKING_MODE_RELATIVE_DEVICE}
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
|
||||
@RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
|
||||
public @NonNull List<Integer> getSupportedHeadTrackingModes() {
|
||||
try {
|
||||
final int[] modes = mAm.getService().getSupportedHeadTrackingModes();
|
||||
final ArrayList<Integer> list = new ArrayList<>(0);
|
||||
for (int mode : modes) {
|
||||
list.add(mode);
|
||||
}
|
||||
return list;
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error calling getSupportedHeadTrackModes", e);
|
||||
return new ArrayList(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Sets the desired head tracking mode.
|
||||
* Note a set desired mode may differ from the actual head tracking mode.
|
||||
* @see #getHeadTrackingMode()
|
||||
* @param mode the desired head tracking mode, one of the values returned by
|
||||
* {@link #getSupportedHeadTrackModes()}, or {@link #HEAD_TRACKING_MODE_DISABLED} to
|
||||
* disable head tracking.
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
|
||||
@RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
|
||||
public void setDesiredHeadTrackingMode(@HeadTrackingModeSet int mode) {
|
||||
try {
|
||||
mAm.getService().setDesiredHeadTrackingMode(mode);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error calling setDesiredHeadTrackingMode to " + mode, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Recenters the head tracking at the current position / orientation.
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
|
||||
@RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
|
||||
public void recenterHeadTracker() {
|
||||
try {
|
||||
mAm.getService().recenterHeadTracker();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error calling recenterHeadTracker", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Adds a listener to be notified of changes to the head tracking mode of the
|
||||
* {@code Spatializer}
|
||||
* @param executor the {@code Executor} handling the callbacks
|
||||
* @param listener the listener to register
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
|
||||
@RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
|
||||
public void addOnHeadTrackingModeChangedListener(
|
||||
@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull OnHeadTrackingModeChangedListener listener) {
|
||||
Objects.requireNonNull(executor);
|
||||
Objects.requireNonNull(listener);
|
||||
synchronized (mHeadTrackingListenerLock) {
|
||||
if (hasListener(listener, mHeadTrackingListeners)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Called addOnHeadTrackingModeChangedListener() "
|
||||
+ "on a previously registered listener");
|
||||
}
|
||||
// lazy initialization of the list of strategy-preferred device listener
|
||||
if (mHeadTrackingListeners == null) {
|
||||
mHeadTrackingListeners = new ArrayList<>();
|
||||
}
|
||||
mHeadTrackingListeners.add(
|
||||
new ListenerInfo<OnHeadTrackingModeChangedListener>(listener, executor));
|
||||
if (mHeadTrackingListeners.size() == 1) {
|
||||
// register binder for callbacks
|
||||
if (mHeadTrackingDispatcherStub == null) {
|
||||
mHeadTrackingDispatcherStub =
|
||||
new SpatializerHeadTrackingDispatcherStub();
|
||||
}
|
||||
try {
|
||||
mAm.getService().registerSpatializerHeadTrackingCallback(
|
||||
mHeadTrackingDispatcherStub);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Removes a previously added listener for changes to the head tracking mode of the
|
||||
* {@code Spatializer}.
|
||||
* @param listener the listener to unregister
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
|
||||
@RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
|
||||
public void removeOnHeadTrackingModeChangedListener(
|
||||
@NonNull OnHeadTrackingModeChangedListener listener) {
|
||||
Objects.requireNonNull(listener);
|
||||
synchronized (mHeadTrackingListenerLock) {
|
||||
if (!removeListener(listener, mHeadTrackingListeners)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Called removeOnHeadTrackingModeChangedListener() "
|
||||
+ "on an unregistered listener");
|
||||
}
|
||||
if (mHeadTrackingListeners.size() == 0) {
|
||||
// unregister binder for callbacks
|
||||
try {
|
||||
mAm.getService().unregisterSpatializerHeadTrackingCallback(
|
||||
mHeadTrackingDispatcherStub);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
} finally {
|
||||
mHeadTrackingDispatcherStub = null;
|
||||
mHeadTrackingListeners = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Set the listener to receive head to soundstage pose updates.
|
||||
* @param executor the {@code Executor} handling the callbacks
|
||||
* @param listener the listener to register
|
||||
* @see #clearOnHeadToSoundstagePoseUpdatedListener()
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
|
||||
@RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
|
||||
public void setOnHeadToSoundstagePoseUpdatedListener(
|
||||
@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull OnHeadToSoundstagePoseUpdatedListener listener) {
|
||||
Objects.requireNonNull(executor);
|
||||
Objects.requireNonNull(listener);
|
||||
synchronized (mPoseListenerLock) {
|
||||
if (mPoseListener != null) {
|
||||
throw new IllegalStateException("Trying to overwrite existing listener");
|
||||
}
|
||||
mPoseListener =
|
||||
new ListenerInfo<OnHeadToSoundstagePoseUpdatedListener>(listener, executor);
|
||||
mPoseDispatcher = new SpatializerPoseDispatcherStub();
|
||||
try {
|
||||
mAm.getService().registerHeadToSoundstagePoseCallback(mPoseDispatcher);
|
||||
} catch (RemoteException e) {
|
||||
mPoseListener = null;
|
||||
mPoseDispatcher = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Clears the listener for head to soundstage pose updates
|
||||
* @see #setOnHeadToSoundstagePoseUpdatedListener(Executor, OnHeadToSoundstagePoseUpdatedListener)
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
|
||||
@RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
|
||||
public void clearOnHeadToSoundstagePoseUpdatedListener() {
|
||||
synchronized (mPoseListenerLock) {
|
||||
if (mPoseDispatcher == null) {
|
||||
throw (new IllegalStateException("No listener to clear"));
|
||||
}
|
||||
try {
|
||||
mAm.getService().unregisterHeadToSoundstagePoseCallback(mPoseDispatcher);
|
||||
} catch (RemoteException e) { }
|
||||
mPoseListener = null;
|
||||
mPoseDispatcher = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* Sets an additional transform over the soundstage.
|
||||
* The transform represents the pose of the soundstage, relative
|
||||
* to either the device (in {@link #HEAD_TRACKING_MODE_RELATIVE_DEVICE} mode), the world (in
|
||||
* {@link #HEAD_TRACKING_MODE_RELATIVE_WORLD}) or the listener’s head (in
|
||||
* {@link #HEAD_TRACKING_MODE_DISABLED} mode).
|
||||
* @param transform an array of 6 float values, the first 3 are the translation vector, the
|
||||
* other 3 are the rotation vector.
|
||||
*/
|
||||
@SystemApi(client = SystemApi.Client.PRIVILEGED_APPS)
|
||||
@RequiresPermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
|
||||
public void setGlobalTransform(@NonNull float[] transform) {
|
||||
if (Objects.requireNonNull(transform).length != 6) {
|
||||
throw new IllegalArgumentException("transform array must be of size 6, was "
|
||||
+ transform.length);
|
||||
}
|
||||
try {
|
||||
mAm.getService().setSpatializerGlobalTransform(transform);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error calling setGlobalTransform", e);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// callback helper definitions
|
||||
|
||||
private static class ListenerInfo<T> {
|
||||
final @NonNull T mListener;
|
||||
final @NonNull Executor mExecutor;
|
||||
|
||||
ListenerInfo(T listener, Executor exe) {
|
||||
mListener = listener;
|
||||
mExecutor = exe;
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> ListenerInfo<T> getListenerInfo(
|
||||
T listener, ArrayList<ListenerInfo<T>> listeners) {
|
||||
if (listeners == null) {
|
||||
return null;
|
||||
}
|
||||
for (ListenerInfo<T> info : listeners) {
|
||||
if (info.mListener == listener) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static <T> boolean hasListener(T listener, ArrayList<ListenerInfo<T>> listeners) {
|
||||
return getListenerInfo(listener, listeners) != null;
|
||||
}
|
||||
|
||||
private static <T> boolean removeListener(T listener, ArrayList<ListenerInfo<T>> listeners) {
|
||||
final ListenerInfo<T> infoToRemove = getListenerInfo(listener, listeners);
|
||||
if (infoToRemove != null) {
|
||||
listeners.remove(infoToRemove);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// head tracking callback management and stub
|
||||
|
||||
private final Object mHeadTrackingListenerLock = new Object();
|
||||
/**
|
||||
* List of listeners for head tracking mode listener and their associated Executor.
|
||||
* List is lazy-initialized on first registration
|
||||
*/
|
||||
@GuardedBy("mHeadTrackingListenerLock")
|
||||
private @Nullable ArrayList<ListenerInfo<OnHeadTrackingModeChangedListener>>
|
||||
mHeadTrackingListeners;
|
||||
|
||||
@GuardedBy("mHeadTrackingListenerLock")
|
||||
private @Nullable SpatializerHeadTrackingDispatcherStub mHeadTrackingDispatcherStub;
|
||||
|
||||
private final class SpatializerHeadTrackingDispatcherStub
|
||||
extends ISpatializerHeadTrackingModeCallback.Stub {
|
||||
@Override
|
||||
public void dispatchSpatializerActualHeadTrackingModeChanged(int mode) {
|
||||
// make a shallow copy of listeners so callback is not executed under lock
|
||||
final ArrayList<ListenerInfo<OnHeadTrackingModeChangedListener>> headTrackingListeners;
|
||||
synchronized (mHeadTrackingListenerLock) {
|
||||
if (mHeadTrackingListeners == null || mHeadTrackingListeners.size() == 0) {
|
||||
return;
|
||||
}
|
||||
headTrackingListeners = (ArrayList<ListenerInfo<OnHeadTrackingModeChangedListener>>)
|
||||
mHeadTrackingListeners.clone();
|
||||
}
|
||||
try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
|
||||
for (ListenerInfo<OnHeadTrackingModeChangedListener> info : headTrackingListeners) {
|
||||
info.mExecutor.execute(() -> info.mListener
|
||||
.onHeadTrackingModeChanged(Spatializer.this, mode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchSpatializerDesiredHeadTrackingModeChanged(int mode) {
|
||||
// make a shallow copy of listeners so callback is not executed under lock
|
||||
final ArrayList<ListenerInfo<OnHeadTrackingModeChangedListener>> headTrackingListeners;
|
||||
synchronized (mHeadTrackingListenerLock) {
|
||||
if (mHeadTrackingListeners == null || mHeadTrackingListeners.size() == 0) {
|
||||
return;
|
||||
}
|
||||
headTrackingListeners = (ArrayList<ListenerInfo<OnHeadTrackingModeChangedListener>>)
|
||||
mHeadTrackingListeners.clone();
|
||||
}
|
||||
try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
|
||||
for (ListenerInfo<OnHeadTrackingModeChangedListener> info : headTrackingListeners) {
|
||||
info.mExecutor.execute(() -> info.mListener
|
||||
.onDesiredHeadTrackingModeChanged(Spatializer.this, mode));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// head pose callback management and stub
|
||||
private final Object mPoseListenerLock = new Object();
|
||||
/**
|
||||
* Listener for head to soundstage updates
|
||||
*/
|
||||
@GuardedBy("mPoseListenerLock")
|
||||
private @Nullable ListenerInfo<OnHeadToSoundstagePoseUpdatedListener> mPoseListener;
|
||||
@GuardedBy("mPoseListenerLock")
|
||||
private @Nullable SpatializerPoseDispatcherStub mPoseDispatcher;
|
||||
|
||||
private final class SpatializerPoseDispatcherStub
|
||||
extends ISpatializerHeadToSoundStagePoseCallback.Stub {
|
||||
|
||||
@Override
|
||||
public void dispatchPoseChanged(float[] pose) {
|
||||
// make a copy of ref to listener so callback is not executed under lock
|
||||
final ListenerInfo<OnHeadToSoundstagePoseUpdatedListener> listener;
|
||||
synchronized (mPoseListenerLock) {
|
||||
listener = mPoseListener;
|
||||
}
|
||||
if (listener == null) {
|
||||
return;
|
||||
}
|
||||
try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
|
||||
listener.mExecutor.execute(() -> listener.mListener
|
||||
.onHeadToSoundstagePoseUpdated(Spatializer.this, pose));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,8 @@ import android.media.IPlaybackConfigDispatcher;
|
||||
import android.media.IRecordingConfigDispatcher;
|
||||
import android.media.IRingtonePlayer;
|
||||
import android.media.ISpatializerCallback;
|
||||
import android.media.ISpatializerHeadToSoundStagePoseCallback;
|
||||
import android.media.ISpatializerHeadTrackingModeCallback;
|
||||
import android.media.IStrategyPreferredDevicesDispatcher;
|
||||
import android.media.IVolumeController;
|
||||
import android.media.MediaMetrics;
|
||||
@@ -8354,16 +8356,48 @@ public class AudioService extends IAudioService.Stub
|
||||
|
||||
/** @see Spatializer.SpatializerInfoDispatcherStub */
|
||||
public void registerSpatializerCallback(
|
||||
@NonNull ISpatializerCallback dispatcher) {
|
||||
Objects.requireNonNull(dispatcher);
|
||||
mSpatializerHelper.registerStateCallback(dispatcher);
|
||||
@NonNull ISpatializerCallback cb) {
|
||||
Objects.requireNonNull(cb);
|
||||
mSpatializerHelper.registerStateCallback(cb);
|
||||
}
|
||||
|
||||
/** @see Spatializer.SpatializerInfoDispatcherStub */
|
||||
public void unregisterSpatializerCallback(
|
||||
@NonNull ISpatializerCallback dispatcher) {
|
||||
Objects.requireNonNull(dispatcher);
|
||||
mSpatializerHelper.unregisterStateCallback(dispatcher);
|
||||
@NonNull ISpatializerCallback cb) {
|
||||
Objects.requireNonNull(cb);
|
||||
mSpatializerHelper.unregisterStateCallback(cb);
|
||||
}
|
||||
|
||||
/** @see Spatializer#SpatializerHeadTrackingDispatcherStub */
|
||||
public void registerSpatializerHeadTrackingCallback(
|
||||
@NonNull ISpatializerHeadTrackingModeCallback cb) {
|
||||
enforceModifyDefaultAudioEffectsPermission();
|
||||
Objects.requireNonNull(cb);
|
||||
mSpatializerHelper.registerHeadTrackingModeCallback(cb);
|
||||
}
|
||||
|
||||
/** @see Spatializer#SpatializerHeadTrackingDispatcherStub */
|
||||
public void unregisterSpatializerHeadTrackingCallback(
|
||||
@NonNull ISpatializerHeadTrackingModeCallback cb) {
|
||||
enforceModifyDefaultAudioEffectsPermission();
|
||||
Objects.requireNonNull(cb);
|
||||
mSpatializerHelper.unregisterHeadTrackingModeCallback(cb);
|
||||
}
|
||||
|
||||
/** @see Spatializer#setOnHeadToSoundstagePoseUpdatedListener */
|
||||
public void registerHeadToSoundstagePoseCallback(
|
||||
@NonNull ISpatializerHeadToSoundStagePoseCallback cb) {
|
||||
enforceModifyDefaultAudioEffectsPermission();
|
||||
Objects.requireNonNull(cb);
|
||||
mSpatializerHelper.registerHeadToSoundstagePoseCallback(cb);
|
||||
}
|
||||
|
||||
/** @see Spatializer#clearOnHeadToSoundstagePoseUpdatedListener */
|
||||
public void unregisterHeadToSoundstagePoseCallback(
|
||||
@NonNull ISpatializerHeadToSoundStagePoseCallback cb) {
|
||||
enforceModifyDefaultAudioEffectsPermission();
|
||||
Objects.requireNonNull(cb);
|
||||
mSpatializerHelper.unregisterHeadToSoundstagePoseCallback(cb);
|
||||
}
|
||||
|
||||
/** @see Spatializer#getSpatializerCompatibleAudioDevices() */
|
||||
@@ -8386,6 +8420,51 @@ public class AudioService extends IAudioService.Stub
|
||||
mSpatializerHelper.removeCompatibleAudioDevice(ada);
|
||||
}
|
||||
|
||||
/** @see Spatializer#getSupportedHeadTrackingModes() */
|
||||
public int[] getSupportedHeadTrackingModes() {
|
||||
enforceModifyDefaultAudioEffectsPermission();
|
||||
return mSpatializerHelper.getSupportedHeadTrackingModes();
|
||||
}
|
||||
|
||||
/** @see Spatializer#getHeadTrackingMode() */
|
||||
public int getActualHeadTrackingMode() {
|
||||
enforceModifyDefaultAudioEffectsPermission();
|
||||
return mSpatializerHelper.getActualHeadTrackingMode();
|
||||
}
|
||||
|
||||
/** @see Spatializer#getDesiredHeadTrackingMode() */
|
||||
public int getDesiredHeadTrackingMode() {
|
||||
enforceModifyDefaultAudioEffectsPermission();
|
||||
return mSpatializerHelper.getDesiredHeadTrackingMode();
|
||||
}
|
||||
|
||||
/** @see Spatializer#setGlobalTransform */
|
||||
public void setSpatializerGlobalTransform(@NonNull float[] transform) {
|
||||
enforceModifyDefaultAudioEffectsPermission();
|
||||
Objects.requireNonNull(transform);
|
||||
mSpatializerHelper.setGlobalTransform(transform);
|
||||
}
|
||||
|
||||
/** @see Spatializer#recenterHeadTracker() */
|
||||
public void recenterHeadTracker() {
|
||||
enforceModifyDefaultAudioEffectsPermission();
|
||||
mSpatializerHelper.recenterHeadTracker();
|
||||
}
|
||||
|
||||
/** @see Spatializer#setDesiredHeadTrackingMode */
|
||||
public void setDesiredHeadTrackingMode(@Spatializer.HeadTrackingModeSet int mode) {
|
||||
enforceModifyDefaultAudioEffectsPermission();
|
||||
switch(mode) {
|
||||
case Spatializer.HEAD_TRACKING_MODE_DISABLED:
|
||||
case Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD:
|
||||
case Spatializer.HEAD_TRACKING_MODE_RELATIVE_DEVICE:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
mSpatializerHelper.setDesiredHeadTrackingMode(mode);
|
||||
}
|
||||
|
||||
//==========================================================================================
|
||||
private boolean readCameraSoundForced() {
|
||||
return SystemProperties.getBoolean("audio.camerasound.force", false) ||
|
||||
|
||||
@@ -25,7 +25,10 @@ import android.media.AudioSystem;
|
||||
import android.media.INativeSpatializerCallback;
|
||||
import android.media.ISpatializer;
|
||||
import android.media.ISpatializerCallback;
|
||||
import android.media.ISpatializerHeadToSoundStagePoseCallback;
|
||||
import android.media.ISpatializerHeadTrackingModeCallback;
|
||||
import android.media.Spatializer;
|
||||
import android.media.SpatializerHeadTrackingMode;
|
||||
import android.os.RemoteCallbackList;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
@@ -64,6 +67,8 @@ public class SpatializerHelper {
|
||||
/** 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 int mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
|
||||
private int mDesiredHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
|
||||
private @Nullable ISpatializer mSpat;
|
||||
private @Nullable SpatializerCallback mSpatCallback;
|
||||
|
||||
@@ -150,6 +155,7 @@ public class SpatializerHelper {
|
||||
mState = STATE_UNINITIALIZED;
|
||||
mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
|
||||
mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
|
||||
mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
|
||||
init();
|
||||
setFeatureEnabled(featureEnabled);
|
||||
}
|
||||
@@ -189,6 +195,15 @@ public class SpatializerHelper {
|
||||
|
||||
public void onHeadTrackingModeChanged(byte mode) {
|
||||
logd("SpatializerCallback.onHeadTrackingModeChanged mode:" + mode);
|
||||
int oldMode, newMode;
|
||||
synchronized (this) {
|
||||
oldMode = mActualHeadTrackingMode;
|
||||
mActualHeadTrackingMode = headTrackingModeTypeToSpatializerInt(mode);
|
||||
newMode = mActualHeadTrackingMode;
|
||||
}
|
||||
if (oldMode != newMode) {
|
||||
dispatchActualHeadTrackingMode(newMode);
|
||||
}
|
||||
}
|
||||
|
||||
public void onHeadToSoundStagePoseUpdated(float[] headToStage) {
|
||||
@@ -196,6 +211,11 @@ public class SpatializerHelper {
|
||||
Log.e(TAG, "SpatializerCallback.onHeadToStagePoseUpdated null transform");
|
||||
return;
|
||||
}
|
||||
if (headToStage.length != 6) {
|
||||
Log.e(TAG, "SpatializerCallback.onHeadToStagePoseUpdated invalid transform length"
|
||||
+ headToStage.length);
|
||||
return;
|
||||
}
|
||||
if (DEBUG) {
|
||||
// 6 values * (4 digits + 1 dot + 2 brackets) = 42 characters
|
||||
StringBuilder t = new StringBuilder(42);
|
||||
@@ -204,6 +224,7 @@ public class SpatializerHelper {
|
||||
}
|
||||
logd("SpatializerCallback.onHeadToStagePoseUpdated headToStage:" + t);
|
||||
}
|
||||
dispatchPoseUpdate(headToStage);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -472,4 +493,239 @@ public class SpatializerHelper {
|
||||
logd("canBeSpatialized returning " + able);
|
||||
return able;
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
// head tracking
|
||||
final RemoteCallbackList<ISpatializerHeadTrackingModeCallback> mHeadTrackingModeCallbacks =
|
||||
new RemoteCallbackList<ISpatializerHeadTrackingModeCallback>();
|
||||
|
||||
synchronized void registerHeadTrackingModeCallback(
|
||||
@NonNull ISpatializerHeadTrackingModeCallback callback) {
|
||||
mHeadTrackingModeCallbacks.register(callback);
|
||||
}
|
||||
|
||||
synchronized void unregisterHeadTrackingModeCallback(
|
||||
@NonNull ISpatializerHeadTrackingModeCallback callback) {
|
||||
mHeadTrackingModeCallbacks.unregister(callback);
|
||||
}
|
||||
|
||||
synchronized int[] getSupportedHeadTrackingModes() {
|
||||
switch (mState) {
|
||||
case STATE_UNINITIALIZED:
|
||||
return new int[0];
|
||||
case STATE_NOT_SUPPORTED:
|
||||
// return an empty list when Spatializer functionality is not supported
|
||||
// because the list of head tracking modes you can set is actually empty
|
||||
// as defined in {@link Spatializer#getSupportedHeadTrackingModes()}
|
||||
return new int[0];
|
||||
case STATE_ENABLED_UNAVAILABLE:
|
||||
case STATE_DISABLED_UNAVAILABLE:
|
||||
case STATE_DISABLED_AVAILABLE:
|
||||
case STATE_ENABLED_AVAILABLE:
|
||||
if (mSpat == null) {
|
||||
return new int[0];
|
||||
}
|
||||
break;
|
||||
}
|
||||
// mSpat != null
|
||||
try {
|
||||
final byte[] values = mSpat.getSupportedHeadTrackingModes();
|
||||
ArrayList<Integer> list = new ArrayList<>(0);
|
||||
for (byte value : values) {
|
||||
switch (value) {
|
||||
case SpatializerHeadTrackingMode.OTHER:
|
||||
case SpatializerHeadTrackingMode.DISABLED:
|
||||
// not expected here, skip
|
||||
break;
|
||||
case SpatializerHeadTrackingMode.RELATIVE_WORLD:
|
||||
case SpatializerHeadTrackingMode.RELATIVE_SCREEN:
|
||||
list.add(headTrackingModeTypeToSpatializerInt(value));
|
||||
break;
|
||||
default:
|
||||
Log.e(TAG, "Unexpected head tracking mode:" + value,
|
||||
new IllegalArgumentException("invalid mode"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
int[] modes = new int[list.size()];
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
modes[i] = list.get(i);
|
||||
}
|
||||
return modes;
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error calling getSupportedHeadTrackingModes", e);
|
||||
return new int[] { Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED };
|
||||
}
|
||||
}
|
||||
|
||||
synchronized int getActualHeadTrackingMode() {
|
||||
switch (mState) {
|
||||
case STATE_UNINITIALIZED:
|
||||
return Spatializer.HEAD_TRACKING_MODE_DISABLED;
|
||||
case STATE_NOT_SUPPORTED:
|
||||
return Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
|
||||
case STATE_ENABLED_UNAVAILABLE:
|
||||
case STATE_DISABLED_UNAVAILABLE:
|
||||
case STATE_DISABLED_AVAILABLE:
|
||||
case STATE_ENABLED_AVAILABLE:
|
||||
if (mSpat == null) {
|
||||
return Spatializer.HEAD_TRACKING_MODE_DISABLED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// mSpat != null
|
||||
try {
|
||||
return headTrackingModeTypeToSpatializerInt(mSpat.getActualHeadTrackingMode());
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error calling getActualHeadTrackingMode", e);
|
||||
return Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
synchronized int getDesiredHeadTrackingMode() {
|
||||
return mDesiredHeadTrackingMode;
|
||||
}
|
||||
|
||||
synchronized void setGlobalTransform(@NonNull float[] transform) {
|
||||
if (transform.length != 6) {
|
||||
throw new IllegalArgumentException("invalid array size" + transform.length);
|
||||
}
|
||||
if (!checkSpatForHeadTracking("setGlobalTransform")) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mSpat.setGlobalTransform(transform);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error calling setGlobalTransform", e);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void recenterHeadTracker() {
|
||||
if (!checkSpatForHeadTracking("recenterHeadTracker")) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
mSpat.recenterHeadTracker();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error calling recenterHeadTracker", e);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void setDesiredHeadTrackingMode(@Spatializer.HeadTrackingModeSet int mode) {
|
||||
if (!checkSpatForHeadTracking("setDesiredHeadTrackingMode")) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (mode != mDesiredHeadTrackingMode) {
|
||||
mSpat.setDesiredHeadTrackingMode(spatializerIntToHeadTrackingModeType(mode));
|
||||
mDesiredHeadTrackingMode = mode;
|
||||
dispatchDesiredHeadTrackingMode(mode);
|
||||
}
|
||||
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error calling setDesiredHeadTrackingMode", e);
|
||||
}
|
||||
}
|
||||
|
||||
private int headTrackingModeTypeToSpatializerInt(byte mode) {
|
||||
switch (mode) {
|
||||
case SpatializerHeadTrackingMode.OTHER:
|
||||
return Spatializer.HEAD_TRACKING_MODE_OTHER;
|
||||
case SpatializerHeadTrackingMode.DISABLED:
|
||||
return Spatializer.HEAD_TRACKING_MODE_DISABLED;
|
||||
case SpatializerHeadTrackingMode.RELATIVE_WORLD:
|
||||
return Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD;
|
||||
case SpatializerHeadTrackingMode.RELATIVE_SCREEN:
|
||||
return Spatializer.HEAD_TRACKING_MODE_RELATIVE_DEVICE;
|
||||
default:
|
||||
throw(new IllegalArgumentException("Unexpected head tracking mode:" + mode));
|
||||
}
|
||||
}
|
||||
|
||||
private byte spatializerIntToHeadTrackingModeType(int sdkMode) {
|
||||
switch (sdkMode) {
|
||||
case Spatializer.HEAD_TRACKING_MODE_OTHER:
|
||||
return SpatializerHeadTrackingMode.OTHER;
|
||||
case Spatializer.HEAD_TRACKING_MODE_DISABLED:
|
||||
return SpatializerHeadTrackingMode.DISABLED;
|
||||
case Spatializer.HEAD_TRACKING_MODE_RELATIVE_WORLD:
|
||||
return SpatializerHeadTrackingMode.RELATIVE_WORLD;
|
||||
case Spatializer.HEAD_TRACKING_MODE_RELATIVE_DEVICE:
|
||||
return SpatializerHeadTrackingMode.RELATIVE_SCREEN;
|
||||
default:
|
||||
throw(new IllegalArgumentException("Unexpected head tracking mode:" + sdkMode));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkSpatForHeadTracking(String funcName) {
|
||||
switch (mState) {
|
||||
case STATE_UNINITIALIZED:
|
||||
case STATE_NOT_SUPPORTED:
|
||||
return false;
|
||||
case STATE_ENABLED_UNAVAILABLE:
|
||||
case STATE_DISABLED_UNAVAILABLE:
|
||||
case STATE_DISABLED_AVAILABLE:
|
||||
case STATE_ENABLED_AVAILABLE:
|
||||
if (mSpat == null) {
|
||||
throw (new IllegalStateException(
|
||||
"null Spatializer when calling " + funcName));
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void dispatchActualHeadTrackingMode(int newMode) {
|
||||
final int nbCallbacks = mHeadTrackingModeCallbacks.beginBroadcast();
|
||||
for (int i = 0; i < nbCallbacks; i++) {
|
||||
try {
|
||||
mHeadTrackingModeCallbacks.getBroadcastItem(i)
|
||||
.dispatchSpatializerActualHeadTrackingModeChanged(newMode);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error in dispatchSpatializerActualHeadTrackingModeChanged", e);
|
||||
}
|
||||
}
|
||||
mHeadTrackingModeCallbacks.finishBroadcast();
|
||||
}
|
||||
|
||||
private void dispatchDesiredHeadTrackingMode(int newMode) {
|
||||
final int nbCallbacks = mHeadTrackingModeCallbacks.beginBroadcast();
|
||||
for (int i = 0; i < nbCallbacks; i++) {
|
||||
try {
|
||||
mHeadTrackingModeCallbacks.getBroadcastItem(i)
|
||||
.dispatchSpatializerDesiredHeadTrackingModeChanged(newMode);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error in dispatchSpatializerDesiredHeadTrackingModeChanged", e);
|
||||
}
|
||||
}
|
||||
mHeadTrackingModeCallbacks.finishBroadcast();
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
// head pose
|
||||
final RemoteCallbackList<ISpatializerHeadToSoundStagePoseCallback> mHeadPoseCallbacks =
|
||||
new RemoteCallbackList<ISpatializerHeadToSoundStagePoseCallback>();
|
||||
|
||||
synchronized void registerHeadToSoundstagePoseCallback(
|
||||
@NonNull ISpatializerHeadToSoundStagePoseCallback callback) {
|
||||
mHeadPoseCallbacks.register(callback);
|
||||
}
|
||||
|
||||
synchronized void unregisterHeadToSoundstagePoseCallback(
|
||||
@NonNull ISpatializerHeadToSoundStagePoseCallback callback) {
|
||||
mHeadPoseCallbacks.unregister(callback);
|
||||
}
|
||||
|
||||
private void dispatchPoseUpdate(float[] pose) {
|
||||
final int nbCallbacks = mHeadPoseCallbacks.beginBroadcast();
|
||||
for (int i = 0; i < nbCallbacks; i++) {
|
||||
try {
|
||||
mHeadPoseCallbacks.getBroadcastItem(i)
|
||||
.dispatchPoseChanged(pose);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error in dispatchPoseChanged", e);
|
||||
}
|
||||
}
|
||||
mHeadPoseCallbacks.finishBroadcast();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user