1*ec779b8eSAndroid Build Coastguard Worker /*
2*ec779b8eSAndroid Build Coastguard Worker * Copyright 2021, The Android Open Source Project
3*ec779b8eSAndroid Build Coastguard Worker *
4*ec779b8eSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*ec779b8eSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*ec779b8eSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*ec779b8eSAndroid Build Coastguard Worker *
8*ec779b8eSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*ec779b8eSAndroid Build Coastguard Worker *
10*ec779b8eSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*ec779b8eSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*ec779b8eSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*ec779b8eSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*ec779b8eSAndroid Build Coastguard Worker * limitations under the License.
15*ec779b8eSAndroid Build Coastguard Worker */
16*ec779b8eSAndroid Build Coastguard Worker
17*ec779b8eSAndroid Build Coastguard Worker #include "SpatializerPoseController.h"
18*ec779b8eSAndroid Build Coastguard Worker #include <android-base/stringprintf.h>
19*ec779b8eSAndroid Build Coastguard Worker #include <chrono>
20*ec779b8eSAndroid Build Coastguard Worker #include <cstdint>
21*ec779b8eSAndroid Build Coastguard Worker #include <string>
22*ec779b8eSAndroid Build Coastguard Worker
23*ec779b8eSAndroid Build Coastguard Worker #define LOG_TAG "SpatializerPoseController"
24*ec779b8eSAndroid Build Coastguard Worker //#define LOG_NDEBUG 0
25*ec779b8eSAndroid Build Coastguard Worker #include <audio_utils/mutex.h>
26*ec779b8eSAndroid Build Coastguard Worker #include <cutils/properties.h>
27*ec779b8eSAndroid Build Coastguard Worker #include <sensor/Sensor.h>
28*ec779b8eSAndroid Build Coastguard Worker #include <media/MediaMetricsItem.h>
29*ec779b8eSAndroid Build Coastguard Worker #include <media/QuaternionUtil.h>
30*ec779b8eSAndroid Build Coastguard Worker #include <utils/Log.h>
31*ec779b8eSAndroid Build Coastguard Worker #include <utils/SystemClock.h>
32*ec779b8eSAndroid Build Coastguard Worker
33*ec779b8eSAndroid Build Coastguard Worker namespace android {
34*ec779b8eSAndroid Build Coastguard Worker
35*ec779b8eSAndroid Build Coastguard Worker using media::createHeadTrackingProcessor;
36*ec779b8eSAndroid Build Coastguard Worker using media::HeadTrackingMode;
37*ec779b8eSAndroid Build Coastguard Worker using media::HeadTrackingProcessor;
38*ec779b8eSAndroid Build Coastguard Worker using media::Pose3f;
39*ec779b8eSAndroid Build Coastguard Worker using media::SensorPoseProvider;
40*ec779b8eSAndroid Build Coastguard Worker using media::Twist3f;
41*ec779b8eSAndroid Build Coastguard Worker
42*ec779b8eSAndroid Build Coastguard Worker using namespace std::chrono_literals;
43*ec779b8eSAndroid Build Coastguard Worker
44*ec779b8eSAndroid Build Coastguard Worker namespace {
45*ec779b8eSAndroid Build Coastguard Worker
46*ec779b8eSAndroid Build Coastguard Worker // This is how fast, in m/s, we allow position to shift during rate-limiting.
47*ec779b8eSAndroid Build Coastguard Worker constexpr float kMaxTranslationalVelocity = 2;
48*ec779b8eSAndroid Build Coastguard Worker
49*ec779b8eSAndroid Build Coastguard Worker // This is how fast, in rad/s, we allow rotation angle to shift during rate-limiting.
50*ec779b8eSAndroid Build Coastguard Worker constexpr float kMaxRotationalVelocity = 0.8f;
51*ec779b8eSAndroid Build Coastguard Worker
52*ec779b8eSAndroid Build Coastguard Worker // This is how far into the future we predict the head pose.
53*ec779b8eSAndroid Build Coastguard Worker // The prediction duration should be based on the actual latency from
54*ec779b8eSAndroid Build Coastguard Worker // head-tracker to audio output, though setting the prediction duration too
55*ec779b8eSAndroid Build Coastguard Worker // high may result in higher prediction errors when the head accelerates or
56*ec779b8eSAndroid Build Coastguard Worker // decelerates (changes velocity).
57*ec779b8eSAndroid Build Coastguard Worker //
58*ec779b8eSAndroid Build Coastguard Worker // The head tracking predictor will do a best effort to achieve the requested
59*ec779b8eSAndroid Build Coastguard Worker // prediction duration. If the duration is too far in the future based on
60*ec779b8eSAndroid Build Coastguard Worker // current sensor variance, the predictor may internally restrict duration to what
61*ec779b8eSAndroid Build Coastguard Worker // is achievable with reasonable confidence as the "best prediction".
62*ec779b8eSAndroid Build Coastguard Worker constexpr auto kPredictionDuration = 120ms;
63*ec779b8eSAndroid Build Coastguard Worker
64*ec779b8eSAndroid Build Coastguard Worker // After not getting a pose sample for this long, we would treat the measurement as stale.
65*ec779b8eSAndroid Build Coastguard Worker // The max connection interval is 50ms, and HT sensor event interval can differ depending on the
66*ec779b8eSAndroid Build Coastguard Worker // sampling rate, scheduling, sensor eventQ FIFO etc. 120 (2 * 50 + 20) ms seems reasonable for now.
67*ec779b8eSAndroid Build Coastguard Worker constexpr auto kFreshnessTimeout = 120ms;
68*ec779b8eSAndroid Build Coastguard Worker
69*ec779b8eSAndroid Build Coastguard Worker // Auto-recenter kicks in after the head has been still for this long.
70*ec779b8eSAndroid Build Coastguard Worker constexpr auto kAutoRecenterWindowDuration = 6s;
71*ec779b8eSAndroid Build Coastguard Worker
72*ec779b8eSAndroid Build Coastguard Worker // Auto-recenter considers head not still if translated by this much (in meters, approx).
73*ec779b8eSAndroid Build Coastguard Worker constexpr float kAutoRecenterTranslationThreshold = 0.1f;
74*ec779b8eSAndroid Build Coastguard Worker
75*ec779b8eSAndroid Build Coastguard Worker // Auto-recenter considers head not still if rotated by this much (in radians, approx).
76*ec779b8eSAndroid Build Coastguard Worker constexpr float kAutoRecenterRotationThreshold = 10.5f / 180 * M_PI;
77*ec779b8eSAndroid Build Coastguard Worker
78*ec779b8eSAndroid Build Coastguard Worker // Screen is considered to be unstable (not still) if it has moved significantly within the last
79*ec779b8eSAndroid Build Coastguard Worker // time window of this duration.
80*ec779b8eSAndroid Build Coastguard Worker constexpr auto kScreenStillnessWindowDuration = 750ms;
81*ec779b8eSAndroid Build Coastguard Worker
82*ec779b8eSAndroid Build Coastguard Worker // Screen is considered to have moved significantly if translated by this much (in meter, approx).
83*ec779b8eSAndroid Build Coastguard Worker constexpr float kScreenStillnessTranslationThreshold = 0.1f;
84*ec779b8eSAndroid Build Coastguard Worker
85*ec779b8eSAndroid Build Coastguard Worker // Screen is considered to have moved significantly if rotated by this much (in radians, approx).
86*ec779b8eSAndroid Build Coastguard Worker constexpr float kScreenStillnessRotationThreshold = 15.0f / 180 * M_PI;
87*ec779b8eSAndroid Build Coastguard Worker
88*ec779b8eSAndroid Build Coastguard Worker // Time units for system clock ticks. This is what the Sensor Framework timestamps represent and
89*ec779b8eSAndroid Build Coastguard Worker // what we use for pose filtering.
90*ec779b8eSAndroid Build Coastguard Worker using Ticks = std::chrono::nanoseconds;
91*ec779b8eSAndroid Build Coastguard Worker
92*ec779b8eSAndroid Build Coastguard Worker // How many ticks in a second.
93*ec779b8eSAndroid Build Coastguard Worker constexpr auto kTicksPerSecond = Ticks::period::den;
94*ec779b8eSAndroid Build Coastguard Worker
getSensorMetricsId(int32_t sensorId)95*ec779b8eSAndroid Build Coastguard Worker std::string getSensorMetricsId(int32_t sensorId) {
96*ec779b8eSAndroid Build Coastguard Worker return std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_SENSOR).append(std::to_string(sensorId));
97*ec779b8eSAndroid Build Coastguard Worker }
98*ec779b8eSAndroid Build Coastguard Worker
99*ec779b8eSAndroid Build Coastguard Worker } // namespace
100*ec779b8eSAndroid Build Coastguard Worker
SpatializerPoseController(Listener * listener,std::chrono::microseconds sensorPeriod,std::optional<std::chrono::microseconds> maxUpdatePeriod)101*ec779b8eSAndroid Build Coastguard Worker SpatializerPoseController::SpatializerPoseController(Listener* listener,
102*ec779b8eSAndroid Build Coastguard Worker std::chrono::microseconds sensorPeriod,
103*ec779b8eSAndroid Build Coastguard Worker std::optional<std::chrono::microseconds> maxUpdatePeriod)
104*ec779b8eSAndroid Build Coastguard Worker : mListener(listener),
105*ec779b8eSAndroid Build Coastguard Worker mSensorPeriod(sensorPeriod),
106*ec779b8eSAndroid Build Coastguard Worker mProcessor(createHeadTrackingProcessor(HeadTrackingProcessor::Options{
107*ec779b8eSAndroid Build Coastguard Worker .maxTranslationalVelocity = kMaxTranslationalVelocity / kTicksPerSecond,
108*ec779b8eSAndroid Build Coastguard Worker .maxRotationalVelocity = kMaxRotationalVelocity / kTicksPerSecond,
109*ec779b8eSAndroid Build Coastguard Worker .freshnessTimeout = Ticks(kFreshnessTimeout).count(),
110*ec779b8eSAndroid Build Coastguard Worker .predictionDuration = []() -> float {
111*ec779b8eSAndroid Build Coastguard Worker const int duration_ms =
112*ec779b8eSAndroid Build Coastguard Worker property_get_int32("audio.spatializer.prediction_duration_ms", -1);
113*ec779b8eSAndroid Build Coastguard Worker if (duration_ms >= 0) {
114*ec779b8eSAndroid Build Coastguard Worker return duration_ms * 1'000'000LL;
115*ec779b8eSAndroid Build Coastguard Worker } else {
116*ec779b8eSAndroid Build Coastguard Worker return Ticks(kPredictionDuration).count();
117*ec779b8eSAndroid Build Coastguard Worker }
118*ec779b8eSAndroid Build Coastguard Worker }(),
119*ec779b8eSAndroid Build Coastguard Worker .autoRecenterWindowDuration = Ticks(kAutoRecenterWindowDuration).count(),
120*ec779b8eSAndroid Build Coastguard Worker .autoRecenterTranslationalThreshold = kAutoRecenterTranslationThreshold,
121*ec779b8eSAndroid Build Coastguard Worker .autoRecenterRotationalThreshold = kAutoRecenterRotationThreshold,
122*ec779b8eSAndroid Build Coastguard Worker .screenStillnessWindowDuration = Ticks(kScreenStillnessWindowDuration).count(),
123*ec779b8eSAndroid Build Coastguard Worker .screenStillnessTranslationalThreshold = kScreenStillnessTranslationThreshold,
124*ec779b8eSAndroid Build Coastguard Worker .screenStillnessRotationalThreshold = kScreenStillnessRotationThreshold,
125*ec779b8eSAndroid Build Coastguard Worker })),
126*ec779b8eSAndroid Build Coastguard Worker mPoseProvider(SensorPoseProvider::create("headtracker", this)),
__anon40b8ed630302null127*ec779b8eSAndroid Build Coastguard Worker mThread([this, maxUpdatePeriod] { // It's important that mThread is initialized after
128*ec779b8eSAndroid Build Coastguard Worker // everything else because it runs a member
129*ec779b8eSAndroid Build Coastguard Worker // function that may use any member
130*ec779b8eSAndroid Build Coastguard Worker // of this class.
131*ec779b8eSAndroid Build Coastguard Worker while (true) {
132*ec779b8eSAndroid Build Coastguard Worker Pose3f headToStage;
133*ec779b8eSAndroid Build Coastguard Worker std::optional<HeadTrackingMode> modeIfChanged;
134*ec779b8eSAndroid Build Coastguard Worker {
135*ec779b8eSAndroid Build Coastguard Worker audio_utils::unique_lock ul(mMutex);
136*ec779b8eSAndroid Build Coastguard Worker while (true) {
137*ec779b8eSAndroid Build Coastguard Worker if (mShouldExit) {
138*ec779b8eSAndroid Build Coastguard Worker ALOGV("Exiting thread");
139*ec779b8eSAndroid Build Coastguard Worker return;
140*ec779b8eSAndroid Build Coastguard Worker }
141*ec779b8eSAndroid Build Coastguard Worker if (mShouldCalculate) {
142*ec779b8eSAndroid Build Coastguard Worker std::tie(headToStage, modeIfChanged) = calculate_l();
143*ec779b8eSAndroid Build Coastguard Worker break;
144*ec779b8eSAndroid Build Coastguard Worker }
145*ec779b8eSAndroid Build Coastguard Worker if (maxUpdatePeriod.has_value()) {
146*ec779b8eSAndroid Build Coastguard Worker mCondVar.wait_for(ul, maxUpdatePeriod.value());
147*ec779b8eSAndroid Build Coastguard Worker } else {
148*ec779b8eSAndroid Build Coastguard Worker mCondVar.wait(ul);
149*ec779b8eSAndroid Build Coastguard Worker }
150*ec779b8eSAndroid Build Coastguard Worker }
151*ec779b8eSAndroid Build Coastguard Worker }
152*ec779b8eSAndroid Build Coastguard Worker
153*ec779b8eSAndroid Build Coastguard Worker // Invoke the callbacks outside the lock.
154*ec779b8eSAndroid Build Coastguard Worker mListener->onHeadToStagePose(headToStage);
155*ec779b8eSAndroid Build Coastguard Worker if (modeIfChanged) {
156*ec779b8eSAndroid Build Coastguard Worker mListener->onActualModeChange(modeIfChanged.value());
157*ec779b8eSAndroid Build Coastguard Worker }
158*ec779b8eSAndroid Build Coastguard Worker
159*ec779b8eSAndroid Build Coastguard Worker {
160*ec779b8eSAndroid Build Coastguard Worker std::lock_guard lock(mMutex);
161*ec779b8eSAndroid Build Coastguard Worker if (!mCalculated) {
162*ec779b8eSAndroid Build Coastguard Worker mCalculated = true;
163*ec779b8eSAndroid Build Coastguard Worker mCondVar.notify_all();
164*ec779b8eSAndroid Build Coastguard Worker }
165*ec779b8eSAndroid Build Coastguard Worker mShouldCalculate = false;
166*ec779b8eSAndroid Build Coastguard Worker }
167*ec779b8eSAndroid Build Coastguard Worker }
168*ec779b8eSAndroid Build Coastguard Worker }) {
169*ec779b8eSAndroid Build Coastguard Worker const media::PosePredictorType posePredictorType =
170*ec779b8eSAndroid Build Coastguard Worker (media::PosePredictorType)
171*ec779b8eSAndroid Build Coastguard Worker property_get_int32("audio.spatializer.pose_predictor_type", -1);
172*ec779b8eSAndroid Build Coastguard Worker if (isValidPosePredictorType(posePredictorType)) {
173*ec779b8eSAndroid Build Coastguard Worker mProcessor->setPosePredictorType(posePredictorType);
174*ec779b8eSAndroid Build Coastguard Worker }
175*ec779b8eSAndroid Build Coastguard Worker }
176*ec779b8eSAndroid Build Coastguard Worker
~SpatializerPoseController()177*ec779b8eSAndroid Build Coastguard Worker SpatializerPoseController::~SpatializerPoseController() {
178*ec779b8eSAndroid Build Coastguard Worker {
179*ec779b8eSAndroid Build Coastguard Worker std::lock_guard lock(mMutex);
180*ec779b8eSAndroid Build Coastguard Worker mShouldExit = true;
181*ec779b8eSAndroid Build Coastguard Worker mCondVar.notify_all();
182*ec779b8eSAndroid Build Coastguard Worker }
183*ec779b8eSAndroid Build Coastguard Worker mThread.join();
184*ec779b8eSAndroid Build Coastguard Worker }
185*ec779b8eSAndroid Build Coastguard Worker
setHeadSensor(int32_t sensor)186*ec779b8eSAndroid Build Coastguard Worker void SpatializerPoseController::setHeadSensor(int32_t sensor) {
187*ec779b8eSAndroid Build Coastguard Worker std::lock_guard lock(mMutex);
188*ec779b8eSAndroid Build Coastguard Worker if (sensor == mHeadSensor) return;
189*ec779b8eSAndroid Build Coastguard Worker ALOGV("%s: new sensor:%d mHeadSensor:%d mScreenSensor:%d",
190*ec779b8eSAndroid Build Coastguard Worker __func__, sensor, mHeadSensor, mScreenSensor);
191*ec779b8eSAndroid Build Coastguard Worker
192*ec779b8eSAndroid Build Coastguard Worker // Stop current sensor, if valid and different from the other sensor.
193*ec779b8eSAndroid Build Coastguard Worker if (mHeadSensor != INVALID_SENSOR && mHeadSensor != mScreenSensor) {
194*ec779b8eSAndroid Build Coastguard Worker mPoseProvider->stopSensor(mHeadSensor);
195*ec779b8eSAndroid Build Coastguard Worker mediametrics::LogItem(getSensorMetricsId(mHeadSensor))
196*ec779b8eSAndroid Build Coastguard Worker .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_STOP)
197*ec779b8eSAndroid Build Coastguard Worker .record();
198*ec779b8eSAndroid Build Coastguard Worker }
199*ec779b8eSAndroid Build Coastguard Worker
200*ec779b8eSAndroid Build Coastguard Worker if (sensor != INVALID_SENSOR) {
201*ec779b8eSAndroid Build Coastguard Worker if (sensor != mScreenSensor) {
202*ec779b8eSAndroid Build Coastguard Worker // Start new sensor.
203*ec779b8eSAndroid Build Coastguard Worker mHeadSensor =
204*ec779b8eSAndroid Build Coastguard Worker mPoseProvider->startSensor(sensor, mSensorPeriod) ? sensor : INVALID_SENSOR;
205*ec779b8eSAndroid Build Coastguard Worker if (mHeadSensor != INVALID_SENSOR) {
206*ec779b8eSAndroid Build Coastguard Worker auto sensor = mPoseProvider->getSensorByHandle(mHeadSensor);
207*ec779b8eSAndroid Build Coastguard Worker std::string stringType = sensor ? sensor->getStringType().c_str() : "";
208*ec779b8eSAndroid Build Coastguard Worker mediametrics::LogItem(getSensorMetricsId(mHeadSensor))
209*ec779b8eSAndroid Build Coastguard Worker .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_START)
210*ec779b8eSAndroid Build Coastguard Worker .set(AMEDIAMETRICS_PROP_MODE, AMEDIAMETRICS_PROP_MODE_VALUE_HEAD)
211*ec779b8eSAndroid Build Coastguard Worker .set(AMEDIAMETRICS_PROP_TYPE, stringType)
212*ec779b8eSAndroid Build Coastguard Worker .record();
213*ec779b8eSAndroid Build Coastguard Worker }
214*ec779b8eSAndroid Build Coastguard Worker } else {
215*ec779b8eSAndroid Build Coastguard Worker // Sensor is already enabled.
216*ec779b8eSAndroid Build Coastguard Worker mHeadSensor = mScreenSensor;
217*ec779b8eSAndroid Build Coastguard Worker }
218*ec779b8eSAndroid Build Coastguard Worker } else {
219*ec779b8eSAndroid Build Coastguard Worker mHeadSensor = INVALID_SENSOR;
220*ec779b8eSAndroid Build Coastguard Worker }
221*ec779b8eSAndroid Build Coastguard Worker
222*ec779b8eSAndroid Build Coastguard Worker mProcessor->recenter(true /* recenterHead */, false /* recenterScreen */, __func__);
223*ec779b8eSAndroid Build Coastguard Worker }
224*ec779b8eSAndroid Build Coastguard Worker
setScreenSensor(int32_t sensor)225*ec779b8eSAndroid Build Coastguard Worker void SpatializerPoseController::setScreenSensor(int32_t sensor) {
226*ec779b8eSAndroid Build Coastguard Worker std::lock_guard lock(mMutex);
227*ec779b8eSAndroid Build Coastguard Worker if (sensor == mScreenSensor) return;
228*ec779b8eSAndroid Build Coastguard Worker ALOGV("%s: new sensor:%d mHeadSensor:%d mScreenSensor:%d",
229*ec779b8eSAndroid Build Coastguard Worker __func__, sensor, mHeadSensor, mScreenSensor);
230*ec779b8eSAndroid Build Coastguard Worker
231*ec779b8eSAndroid Build Coastguard Worker // Stop current sensor, if valid and different from the other sensor.
232*ec779b8eSAndroid Build Coastguard Worker if (mScreenSensor != INVALID_SENSOR && mScreenSensor != mHeadSensor) {
233*ec779b8eSAndroid Build Coastguard Worker mPoseProvider->stopSensor(mScreenSensor);
234*ec779b8eSAndroid Build Coastguard Worker mediametrics::LogItem(getSensorMetricsId(mScreenSensor))
235*ec779b8eSAndroid Build Coastguard Worker .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_STOP)
236*ec779b8eSAndroid Build Coastguard Worker .record();
237*ec779b8eSAndroid Build Coastguard Worker }
238*ec779b8eSAndroid Build Coastguard Worker
239*ec779b8eSAndroid Build Coastguard Worker if (sensor != INVALID_SENSOR) {
240*ec779b8eSAndroid Build Coastguard Worker if (sensor != mHeadSensor) {
241*ec779b8eSAndroid Build Coastguard Worker // Start new sensor.
242*ec779b8eSAndroid Build Coastguard Worker mScreenSensor =
243*ec779b8eSAndroid Build Coastguard Worker mPoseProvider->startSensor(sensor, mSensorPeriod) ? sensor : INVALID_SENSOR;
244*ec779b8eSAndroid Build Coastguard Worker auto sensor = mPoseProvider->getSensorByHandle(mScreenSensor);
245*ec779b8eSAndroid Build Coastguard Worker std::string stringType = sensor ? sensor->getStringType().c_str() : "";
246*ec779b8eSAndroid Build Coastguard Worker mediametrics::LogItem(getSensorMetricsId(mScreenSensor))
247*ec779b8eSAndroid Build Coastguard Worker .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_START)
248*ec779b8eSAndroid Build Coastguard Worker .set(AMEDIAMETRICS_PROP_MODE, AMEDIAMETRICS_PROP_MODE_VALUE_SCREEN)
249*ec779b8eSAndroid Build Coastguard Worker .set(AMEDIAMETRICS_PROP_TYPE, stringType)
250*ec779b8eSAndroid Build Coastguard Worker .record();
251*ec779b8eSAndroid Build Coastguard Worker } else {
252*ec779b8eSAndroid Build Coastguard Worker // Sensor is already enabled.
253*ec779b8eSAndroid Build Coastguard Worker mScreenSensor = mHeadSensor;
254*ec779b8eSAndroid Build Coastguard Worker }
255*ec779b8eSAndroid Build Coastguard Worker } else {
256*ec779b8eSAndroid Build Coastguard Worker mScreenSensor = INVALID_SENSOR;
257*ec779b8eSAndroid Build Coastguard Worker }
258*ec779b8eSAndroid Build Coastguard Worker
259*ec779b8eSAndroid Build Coastguard Worker mProcessor->recenter(false /* recenterHead */, true /* recenterScreen */, __func__);
260*ec779b8eSAndroid Build Coastguard Worker }
261*ec779b8eSAndroid Build Coastguard Worker
setDesiredMode(HeadTrackingMode mode)262*ec779b8eSAndroid Build Coastguard Worker void SpatializerPoseController::setDesiredMode(HeadTrackingMode mode) {
263*ec779b8eSAndroid Build Coastguard Worker std::lock_guard lock(mMutex);
264*ec779b8eSAndroid Build Coastguard Worker mProcessor->setDesiredMode(mode);
265*ec779b8eSAndroid Build Coastguard Worker }
266*ec779b8eSAndroid Build Coastguard Worker
setScreenToStagePose(const Pose3f & screenToStage)267*ec779b8eSAndroid Build Coastguard Worker void SpatializerPoseController::setScreenToStagePose(const Pose3f& screenToStage) {
268*ec779b8eSAndroid Build Coastguard Worker std::lock_guard lock(mMutex);
269*ec779b8eSAndroid Build Coastguard Worker mProcessor->setScreenToStagePose(screenToStage);
270*ec779b8eSAndroid Build Coastguard Worker }
271*ec779b8eSAndroid Build Coastguard Worker
setDisplayOrientation(float physicalToLogicalAngle)272*ec779b8eSAndroid Build Coastguard Worker void SpatializerPoseController::setDisplayOrientation(float physicalToLogicalAngle) {
273*ec779b8eSAndroid Build Coastguard Worker std::lock_guard lock(mMutex);
274*ec779b8eSAndroid Build Coastguard Worker mProcessor->setDisplayOrientation(physicalToLogicalAngle);
275*ec779b8eSAndroid Build Coastguard Worker }
276*ec779b8eSAndroid Build Coastguard Worker
calculateAsync()277*ec779b8eSAndroid Build Coastguard Worker void SpatializerPoseController::calculateAsync() {
278*ec779b8eSAndroid Build Coastguard Worker std::lock_guard lock(mMutex);
279*ec779b8eSAndroid Build Coastguard Worker mShouldCalculate = true;
280*ec779b8eSAndroid Build Coastguard Worker mCondVar.notify_all();
281*ec779b8eSAndroid Build Coastguard Worker }
282*ec779b8eSAndroid Build Coastguard Worker
waitUntilCalculated()283*ec779b8eSAndroid Build Coastguard Worker void SpatializerPoseController::waitUntilCalculated() {
284*ec779b8eSAndroid Build Coastguard Worker audio_utils::unique_lock ul(mMutex);
285*ec779b8eSAndroid Build Coastguard Worker while (!mCalculated) {
286*ec779b8eSAndroid Build Coastguard Worker mCondVar.wait(ul);
287*ec779b8eSAndroid Build Coastguard Worker }
288*ec779b8eSAndroid Build Coastguard Worker }
289*ec779b8eSAndroid Build Coastguard Worker
290*ec779b8eSAndroid Build Coastguard Worker std::tuple<media::Pose3f, std::optional<media::HeadTrackingMode>>
calculate_l()291*ec779b8eSAndroid Build Coastguard Worker SpatializerPoseController::calculate_l() {
292*ec779b8eSAndroid Build Coastguard Worker Pose3f headToStage;
293*ec779b8eSAndroid Build Coastguard Worker HeadTrackingMode mode;
294*ec779b8eSAndroid Build Coastguard Worker std::optional<media::HeadTrackingMode> modeIfChanged;
295*ec779b8eSAndroid Build Coastguard Worker
296*ec779b8eSAndroid Build Coastguard Worker mProcessor->calculate(elapsedRealtimeNano());
297*ec779b8eSAndroid Build Coastguard Worker headToStage = mProcessor->getHeadToStagePose();
298*ec779b8eSAndroid Build Coastguard Worker mode = mProcessor->getActualMode();
299*ec779b8eSAndroid Build Coastguard Worker if (!mActualMode.has_value() || mActualMode.value() != mode) {
300*ec779b8eSAndroid Build Coastguard Worker mActualMode = mode;
301*ec779b8eSAndroid Build Coastguard Worker modeIfChanged = mode;
302*ec779b8eSAndroid Build Coastguard Worker }
303*ec779b8eSAndroid Build Coastguard Worker return std::make_tuple(headToStage, modeIfChanged);
304*ec779b8eSAndroid Build Coastguard Worker }
305*ec779b8eSAndroid Build Coastguard Worker
recenter()306*ec779b8eSAndroid Build Coastguard Worker void SpatializerPoseController::recenter() {
307*ec779b8eSAndroid Build Coastguard Worker std::lock_guard lock(mMutex);
308*ec779b8eSAndroid Build Coastguard Worker mProcessor->recenter(true /* recenterHead */, true /* recenterScreen */, __func__);
309*ec779b8eSAndroid Build Coastguard Worker }
310*ec779b8eSAndroid Build Coastguard Worker
onPose(int64_t timestamp,int32_t sensor,const Pose3f & pose,const std::optional<Twist3f> & twist,bool isNewReference)311*ec779b8eSAndroid Build Coastguard Worker void SpatializerPoseController::onPose(int64_t timestamp, int32_t sensor, const Pose3f& pose,
312*ec779b8eSAndroid Build Coastguard Worker const std::optional<Twist3f>& twist, bool isNewReference) {
313*ec779b8eSAndroid Build Coastguard Worker std::lock_guard lock(mMutex);
314*ec779b8eSAndroid Build Coastguard Worker constexpr float NANOS_TO_MILLIS = 1e-6;
315*ec779b8eSAndroid Build Coastguard Worker constexpr float RAD_TO_DEGREE = 180.f / M_PI;
316*ec779b8eSAndroid Build Coastguard Worker
317*ec779b8eSAndroid Build Coastguard Worker const float delayMs = (elapsedRealtimeNano() - timestamp) * NANOS_TO_MILLIS; // CLOCK_BOOTTIME
318*ec779b8eSAndroid Build Coastguard Worker
319*ec779b8eSAndroid Build Coastguard Worker if (sensor == mHeadSensor) {
320*ec779b8eSAndroid Build Coastguard Worker std::vector<float> pryprydt(8); // pitch, roll, yaw, d_pitch, d_roll, d_yaw,
321*ec779b8eSAndroid Build Coastguard Worker // discontinuity, timestamp_delay
322*ec779b8eSAndroid Build Coastguard Worker media::quaternionToAngles(pose.rotation(), &pryprydt[0], &pryprydt[1], &pryprydt[2]);
323*ec779b8eSAndroid Build Coastguard Worker if (twist) {
324*ec779b8eSAndroid Build Coastguard Worker const auto rotationalVelocity = twist->rotationalVelocity();
325*ec779b8eSAndroid Build Coastguard Worker // The rotational velocity is an intrinsic transform (i.e. based on the head
326*ec779b8eSAndroid Build Coastguard Worker // coordinate system, not the world coordinate system). It is a 3 element vector:
327*ec779b8eSAndroid Build Coastguard Worker // axis (d theta / dt).
328*ec779b8eSAndroid Build Coastguard Worker //
329*ec779b8eSAndroid Build Coastguard Worker // We leave rotational velocity relative to the head coordinate system,
330*ec779b8eSAndroid Build Coastguard Worker // as the initial head tracking sensor's world frame is arbitrary.
331*ec779b8eSAndroid Build Coastguard Worker media::quaternionToAngles(media::rotationVectorToQuaternion(rotationalVelocity),
332*ec779b8eSAndroid Build Coastguard Worker &pryprydt[3], &pryprydt[4], &pryprydt[5]);
333*ec779b8eSAndroid Build Coastguard Worker }
334*ec779b8eSAndroid Build Coastguard Worker pryprydt[6] = isNewReference;
335*ec779b8eSAndroid Build Coastguard Worker pryprydt[7] = delayMs;
336*ec779b8eSAndroid Build Coastguard Worker for (size_t i = 0; i < 6; ++i) {
337*ec779b8eSAndroid Build Coastguard Worker // pitch, roll, yaw in degrees, referenced in degrees on the world frame.
338*ec779b8eSAndroid Build Coastguard Worker // d_pitch, d_roll, d_yaw rotational velocity in degrees/s, based on the world frame.
339*ec779b8eSAndroid Build Coastguard Worker pryprydt[i] *= RAD_TO_DEGREE;
340*ec779b8eSAndroid Build Coastguard Worker }
341*ec779b8eSAndroid Build Coastguard Worker mHeadSensorRecorder.record(pryprydt);
342*ec779b8eSAndroid Build Coastguard Worker mHeadSensorDurableRecorder.record(pryprydt);
343*ec779b8eSAndroid Build Coastguard Worker
344*ec779b8eSAndroid Build Coastguard Worker mProcessor->setWorldToHeadPose(timestamp, pose,
345*ec779b8eSAndroid Build Coastguard Worker twist.value_or(Twist3f()) / kTicksPerSecond);
346*ec779b8eSAndroid Build Coastguard Worker if (isNewReference) {
347*ec779b8eSAndroid Build Coastguard Worker mProcessor->recenter(true, false, __func__);
348*ec779b8eSAndroid Build Coastguard Worker }
349*ec779b8eSAndroid Build Coastguard Worker }
350*ec779b8eSAndroid Build Coastguard Worker if (sensor == mScreenSensor) {
351*ec779b8eSAndroid Build Coastguard Worker std::vector<float> pryt{ 0.f, 0.f, 0.f, delayMs}; // pitch, roll, yaw, timestamp_delay
352*ec779b8eSAndroid Build Coastguard Worker media::quaternionToAngles(pose.rotation(), &pryt[0], &pryt[1], &pryt[2]);
353*ec779b8eSAndroid Build Coastguard Worker for (size_t i = 0; i < 3; ++i) {
354*ec779b8eSAndroid Build Coastguard Worker pryt[i] *= RAD_TO_DEGREE;
355*ec779b8eSAndroid Build Coastguard Worker }
356*ec779b8eSAndroid Build Coastguard Worker mScreenSensorRecorder.record(pryt);
357*ec779b8eSAndroid Build Coastguard Worker mScreenSensorDurableRecorder.record(pryt);
358*ec779b8eSAndroid Build Coastguard Worker
359*ec779b8eSAndroid Build Coastguard Worker mProcessor->setWorldToScreenPose(timestamp, pose);
360*ec779b8eSAndroid Build Coastguard Worker if (isNewReference) {
361*ec779b8eSAndroid Build Coastguard Worker mProcessor->recenter(false, true, __func__);
362*ec779b8eSAndroid Build Coastguard Worker }
363*ec779b8eSAndroid Build Coastguard Worker }
364*ec779b8eSAndroid Build Coastguard Worker }
365*ec779b8eSAndroid Build Coastguard Worker
toString(unsigned level) const366*ec779b8eSAndroid Build Coastguard Worker std::string SpatializerPoseController::toString(unsigned level) const NO_THREAD_SAFETY_ANALYSIS {
367*ec779b8eSAndroid Build Coastguard Worker std::string prefixSpace(level, ' ');
368*ec779b8eSAndroid Build Coastguard Worker std::string ss = prefixSpace + "SpatializerPoseController:\n";
369*ec779b8eSAndroid Build Coastguard Worker bool needUnlock = false;
370*ec779b8eSAndroid Build Coastguard Worker
371*ec779b8eSAndroid Build Coastguard Worker prefixSpace += ' ';
372*ec779b8eSAndroid Build Coastguard Worker auto now = std::chrono::steady_clock::now();
373*ec779b8eSAndroid Build Coastguard Worker if (!audio_utils::std_mutex_timed_lock(mMutex, std::chrono::nanoseconds(
374*ec779b8eSAndroid Build Coastguard Worker media::kSpatializerDumpSysTimeOutInSecond).count())) {
375*ec779b8eSAndroid Build Coastguard Worker ss.append(prefixSpace).append("try_lock failed, dumpsys maybe INACCURATE!\n");
376*ec779b8eSAndroid Build Coastguard Worker } else {
377*ec779b8eSAndroid Build Coastguard Worker needUnlock = true;
378*ec779b8eSAndroid Build Coastguard Worker }
379*ec779b8eSAndroid Build Coastguard Worker
380*ec779b8eSAndroid Build Coastguard Worker ss += prefixSpace;
381*ec779b8eSAndroid Build Coastguard Worker if (mHeadSensor == INVALID_SENSOR) {
382*ec779b8eSAndroid Build Coastguard Worker ss += "HeadSensor: INVALID\n";
383*ec779b8eSAndroid Build Coastguard Worker } else {
384*ec779b8eSAndroid Build Coastguard Worker base::StringAppendF(&ss, "HeadSensor: 0x%08x "
385*ec779b8eSAndroid Build Coastguard Worker "(active world-to-head : head-relative velocity) "
386*ec779b8eSAndroid Build Coastguard Worker "[ pitch, roll, yaw : d_pitch, d_roll, d_yaw : disc : delay ] "
387*ec779b8eSAndroid Build Coastguard Worker "(degrees, degrees/s, bool, ms)\n", mHeadSensor);
388*ec779b8eSAndroid Build Coastguard Worker ss.append(prefixSpace)
389*ec779b8eSAndroid Build Coastguard Worker .append(" PerMinuteHistory:\n")
390*ec779b8eSAndroid Build Coastguard Worker .append(mHeadSensorDurableRecorder.toString(level + 3))
391*ec779b8eSAndroid Build Coastguard Worker .append(prefixSpace)
392*ec779b8eSAndroid Build Coastguard Worker .append(" PerSecondHistory:\n")
393*ec779b8eSAndroid Build Coastguard Worker .append(mHeadSensorRecorder.toString(level + 3));
394*ec779b8eSAndroid Build Coastguard Worker }
395*ec779b8eSAndroid Build Coastguard Worker
396*ec779b8eSAndroid Build Coastguard Worker ss += prefixSpace;
397*ec779b8eSAndroid Build Coastguard Worker if (mScreenSensor == INVALID_SENSOR) {
398*ec779b8eSAndroid Build Coastguard Worker ss += "ScreenSensor: INVALID\n";
399*ec779b8eSAndroid Build Coastguard Worker } else {
400*ec779b8eSAndroid Build Coastguard Worker base::StringAppendF(&ss, "ScreenSensor: 0x%08x (active world-to-screen) "
401*ec779b8eSAndroid Build Coastguard Worker "[ pitch, roll, yaw : delay ] "
402*ec779b8eSAndroid Build Coastguard Worker "(degrees, ms)\n", mScreenSensor);
403*ec779b8eSAndroid Build Coastguard Worker ss.append(prefixSpace)
404*ec779b8eSAndroid Build Coastguard Worker .append(" PerMinuteHistory:\n")
405*ec779b8eSAndroid Build Coastguard Worker .append(mScreenSensorDurableRecorder.toString(level + 3))
406*ec779b8eSAndroid Build Coastguard Worker .append(prefixSpace)
407*ec779b8eSAndroid Build Coastguard Worker .append(" PerSecondHistory:\n")
408*ec779b8eSAndroid Build Coastguard Worker .append(mScreenSensorRecorder.toString(level + 3));
409*ec779b8eSAndroid Build Coastguard Worker }
410*ec779b8eSAndroid Build Coastguard Worker
411*ec779b8eSAndroid Build Coastguard Worker ss += prefixSpace;
412*ec779b8eSAndroid Build Coastguard Worker if (mActualMode.has_value()) {
413*ec779b8eSAndroid Build Coastguard Worker base::StringAppendF(&ss, "ActualMode: %s\n", media::toString(mActualMode.value()).c_str());
414*ec779b8eSAndroid Build Coastguard Worker } else {
415*ec779b8eSAndroid Build Coastguard Worker ss += "ActualMode NOTEXIST\n";
416*ec779b8eSAndroid Build Coastguard Worker }
417*ec779b8eSAndroid Build Coastguard Worker
418*ec779b8eSAndroid Build Coastguard Worker if (mProcessor) {
419*ec779b8eSAndroid Build Coastguard Worker ss += mProcessor->toString_l(level + 1);
420*ec779b8eSAndroid Build Coastguard Worker } else {
421*ec779b8eSAndroid Build Coastguard Worker ss.append(prefixSpace.c_str()).append("HeadTrackingProcessor not exist\n");
422*ec779b8eSAndroid Build Coastguard Worker }
423*ec779b8eSAndroid Build Coastguard Worker
424*ec779b8eSAndroid Build Coastguard Worker if (mPoseProvider) {
425*ec779b8eSAndroid Build Coastguard Worker ss += mPoseProvider->toString(level + 1);
426*ec779b8eSAndroid Build Coastguard Worker } else {
427*ec779b8eSAndroid Build Coastguard Worker ss.append(prefixSpace.c_str()).append("SensorPoseProvider not exist\n");
428*ec779b8eSAndroid Build Coastguard Worker }
429*ec779b8eSAndroid Build Coastguard Worker
430*ec779b8eSAndroid Build Coastguard Worker if (needUnlock) {
431*ec779b8eSAndroid Build Coastguard Worker mMutex.unlock();
432*ec779b8eSAndroid Build Coastguard Worker }
433*ec779b8eSAndroid Build Coastguard Worker // TODO: 233092747 add history sensor info with SimpleLog.
434*ec779b8eSAndroid Build Coastguard Worker return ss;
435*ec779b8eSAndroid Build Coastguard Worker }
436*ec779b8eSAndroid Build Coastguard Worker
437*ec779b8eSAndroid Build Coastguard Worker } // namespace android
438