Merge "Immersive audio APIs for headtracking" into sc-v2-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
7783648723
@@ -5375,9 +5375,33 @@ package android.media {
|
|||||||
|
|
||||||
public class Spatializer {
|
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 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 @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 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 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.IRingtonePlayer;
|
||||||
import android.media.IStrategyPreferredDevicesDispatcher;
|
import android.media.IStrategyPreferredDevicesDispatcher;
|
||||||
import android.media.ISpatializerCallback;
|
import android.media.ISpatializerCallback;
|
||||||
|
import android.media.ISpatializerHeadTrackingModeCallback;
|
||||||
|
import android.media.ISpatializerHeadToSoundStagePoseCallback;
|
||||||
import android.media.IVolumeController;
|
import android.media.IVolumeController;
|
||||||
import android.media.IVolumeController;
|
import android.media.IVolumeController;
|
||||||
import android.media.PlayerBase;
|
import android.media.PlayerBase;
|
||||||
@@ -405,13 +407,33 @@ interface IAudioService {
|
|||||||
|
|
||||||
boolean canBeSpatialized(in AudioAttributes aa, in AudioFormat af);
|
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();
|
List<AudioDeviceAttributes> getSpatializerCompatibleAudioDevices();
|
||||||
|
|
||||||
void addSpatializerCompatibleAudioDevice(in AudioDeviceAttributes ada);
|
void addSpatializerCompatibleAudioDevice(in AudioDeviceAttributes ada);
|
||||||
|
|
||||||
void removeSpatializerCompatibleAudioDevice(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 @NonNull AudioManager mAm;
|
||||||
|
|
||||||
private final Object mStateListenerLock = new Object();
|
|
||||||
|
|
||||||
private static final String TAG = "Spatializer";
|
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
|
* @hide
|
||||||
* Constructor with AudioManager acting as proxy to AudioService
|
* Constructor with AudioManager acting as proxy to AudioService
|
||||||
@@ -141,6 +129,75 @@ public class Spatializer {
|
|||||||
*/
|
*/
|
||||||
public static final int SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL = 1;
|
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.
|
* 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
|
* 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 {
|
public interface OnSpatializerStateChangedListener {
|
||||||
/**
|
/**
|
||||||
@@ -199,6 +256,58 @@ public class Spatializer {
|
|||||||
void onSpatializerAvailableChanged(@NonNull Spatializer spat, boolean available);
|
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
|
* Returns whether audio of the given {@link AudioFormat}, played with the given
|
||||||
* {@link AudioAttributes} can be spatialized.
|
* {@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 {
|
private final class SpatializerInfoDispatcherStub extends ISpatializerCallback.Stub {
|
||||||
@Override
|
@Override
|
||||||
public void dispatchSpatializerEnabledChanged(boolean enabled) {
|
public void dispatchSpatializerEnabledChanged(boolean enabled) {
|
||||||
@@ -423,4 +543,378 @@ public class Spatializer {
|
|||||||
}
|
}
|
||||||
return false;
|
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.IRecordingConfigDispatcher;
|
||||||
import android.media.IRingtonePlayer;
|
import android.media.IRingtonePlayer;
|
||||||
import android.media.ISpatializerCallback;
|
import android.media.ISpatializerCallback;
|
||||||
|
import android.media.ISpatializerHeadToSoundStagePoseCallback;
|
||||||
|
import android.media.ISpatializerHeadTrackingModeCallback;
|
||||||
import android.media.IStrategyPreferredDevicesDispatcher;
|
import android.media.IStrategyPreferredDevicesDispatcher;
|
||||||
import android.media.IVolumeController;
|
import android.media.IVolumeController;
|
||||||
import android.media.MediaMetrics;
|
import android.media.MediaMetrics;
|
||||||
@@ -8322,16 +8324,48 @@ public class AudioService extends IAudioService.Stub
|
|||||||
|
|
||||||
/** @see Spatializer.SpatializerInfoDispatcherStub */
|
/** @see Spatializer.SpatializerInfoDispatcherStub */
|
||||||
public void registerSpatializerCallback(
|
public void registerSpatializerCallback(
|
||||||
@NonNull ISpatializerCallback dispatcher) {
|
@NonNull ISpatializerCallback cb) {
|
||||||
Objects.requireNonNull(dispatcher);
|
Objects.requireNonNull(cb);
|
||||||
mSpatializerHelper.registerStateCallback(dispatcher);
|
mSpatializerHelper.registerStateCallback(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @see Spatializer.SpatializerInfoDispatcherStub */
|
/** @see Spatializer.SpatializerInfoDispatcherStub */
|
||||||
public void unregisterSpatializerCallback(
|
public void unregisterSpatializerCallback(
|
||||||
@NonNull ISpatializerCallback dispatcher) {
|
@NonNull ISpatializerCallback cb) {
|
||||||
Objects.requireNonNull(dispatcher);
|
Objects.requireNonNull(cb);
|
||||||
mSpatializerHelper.unregisterStateCallback(dispatcher);
|
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() */
|
/** @see Spatializer#getSpatializerCompatibleAudioDevices() */
|
||||||
@@ -8354,6 +8388,51 @@ public class AudioService extends IAudioService.Stub
|
|||||||
mSpatializerHelper.removeCompatibleAudioDevice(ada);
|
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() {
|
private boolean readCameraSoundForced() {
|
||||||
return SystemProperties.getBoolean("audio.camerasound.force", false) ||
|
return SystemProperties.getBoolean("audio.camerasound.force", false) ||
|
||||||
|
|||||||
@@ -25,7 +25,10 @@ import android.media.AudioSystem;
|
|||||||
import android.media.INativeSpatializerCallback;
|
import android.media.INativeSpatializerCallback;
|
||||||
import android.media.ISpatializer;
|
import android.media.ISpatializer;
|
||||||
import android.media.ISpatializerCallback;
|
import android.media.ISpatializerCallback;
|
||||||
|
import android.media.ISpatializerHeadToSoundStagePoseCallback;
|
||||||
|
import android.media.ISpatializerHeadTrackingModeCallback;
|
||||||
import android.media.Spatializer;
|
import android.media.Spatializer;
|
||||||
|
import android.media.SpatializerHeadTrackingMode;
|
||||||
import android.os.RemoteCallbackList;
|
import android.os.RemoteCallbackList;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@@ -64,6 +67,8 @@ public class SpatializerHelper {
|
|||||||
/** current level as reported by native Spatializer in callback */
|
/** current level as reported by native Spatializer in callback */
|
||||||
private int mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
|
private int mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
|
||||||
private int mCapableSpatLevel = 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 ISpatializer mSpat;
|
||||||
private @Nullable SpatializerCallback mSpatCallback;
|
private @Nullable SpatializerCallback mSpatCallback;
|
||||||
|
|
||||||
@@ -150,6 +155,7 @@ public class SpatializerHelper {
|
|||||||
mState = STATE_UNINITIALIZED;
|
mState = STATE_UNINITIALIZED;
|
||||||
mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
|
mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
|
||||||
mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
|
mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
|
||||||
|
mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED;
|
||||||
init();
|
init();
|
||||||
setFeatureEnabled(featureEnabled);
|
setFeatureEnabled(featureEnabled);
|
||||||
}
|
}
|
||||||
@@ -189,6 +195,15 @@ public class SpatializerHelper {
|
|||||||
|
|
||||||
public void onHeadTrackingModeChanged(byte mode) {
|
public void onHeadTrackingModeChanged(byte mode) {
|
||||||
logd("SpatializerCallback.onHeadTrackingModeChanged mode:" + 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) {
|
public void onHeadToSoundStagePoseUpdated(float[] headToStage) {
|
||||||
@@ -196,6 +211,11 @@ public class SpatializerHelper {
|
|||||||
Log.e(TAG, "SpatializerCallback.onHeadToStagePoseUpdated null transform");
|
Log.e(TAG, "SpatializerCallback.onHeadToStagePoseUpdated null transform");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (headToStage.length != 6) {
|
||||||
|
Log.e(TAG, "SpatializerCallback.onHeadToStagePoseUpdated invalid transform length"
|
||||||
|
+ headToStage.length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
// 6 values * (4 digits + 1 dot + 2 brackets) = 42 characters
|
// 6 values * (4 digits + 1 dot + 2 brackets) = 42 characters
|
||||||
StringBuilder t = new StringBuilder(42);
|
StringBuilder t = new StringBuilder(42);
|
||||||
@@ -204,6 +224,7 @@ public class SpatializerHelper {
|
|||||||
}
|
}
|
||||||
logd("SpatializerCallback.onHeadToStagePoseUpdated headToStage:" + t);
|
logd("SpatializerCallback.onHeadToStagePoseUpdated headToStage:" + t);
|
||||||
}
|
}
|
||||||
|
dispatchPoseUpdate(headToStage);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -472,4 +493,239 @@ public class SpatializerHelper {
|
|||||||
logd("canBeSpatialized returning " + able);
|
logd("canBeSpatialized returning " + able);
|
||||||
return 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