blob: 4757bdbc6bd05dac9c315359d4daea312949e39c [file] [log] [blame]
jiabin06871bb2020-06-30 21:27:52 -07001/*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Lais Andrade045376e2024-07-24 15:35:07 +010016#define LOG_TAG "ExternalVibrationUtils"
17
jiabin06871bb2020-06-30 21:27:52 -070018#include <cstring>
19
Lais Andrade780b4622024-08-30 15:55:27 +010020#include <android-base/parsedouble.h>
21#include <android-base/properties.h>
22#include <android-base/thread_annotations.h>
23
Lais Andradefd0e14c2024-07-30 15:57:23 +010024#include <android_os_vibrator.h>
25
26#include <algorithm>
jiabin06871bb2020-06-30 21:27:52 -070027#include <math.h>
28
Lais Andrade045376e2024-07-24 15:35:07 +010029#include <log/log.h>
jiabin06871bb2020-06-30 21:27:52 -070030#include <vibrator/ExternalVibrationUtils.h>
31
32namespace android::os {
33
34namespace {
35static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2.0f / 3.0f;
36static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f;
37static constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f;
Lais Andradefd0e14c2024-07-30 15:57:23 +010038static constexpr float SCALE_GAMMA = 0.65f; // Same as VibrationEffect.SCALE_GAMMA
Lais Andrade045376e2024-07-24 15:35:07 +010039static constexpr float SCALE_LEVEL_GAIN = 1.4f; // Same as VibrationConfig.DEFAULT_SCALE_LEVEL_GAIN
jiabin06871bb2020-06-30 21:27:52 -070040
Lais Andrade780b4622024-08-30 15:55:27 +010041float getScaleLevelGain() {
42 static std::mutex gMutex;
43 static float gScaleLevelGain GUARDED_BY(gMutex) = 0;
44 std::lock_guard lock(gMutex);
45 if (gScaleLevelGain != 0) {
46 return gScaleLevelGain;
47 }
48 float scaleGain;
49 std::string value = ::android::base::GetProperty("vendor.vibrator.scale.level.gain", "");
50 if (value.empty() || !::android::base::ParseFloat(value, &scaleGain) || scaleGain <= 1) {
51 scaleGain = SCALE_LEVEL_GAIN;
52 }
53 return gScaleLevelGain = scaleGain;
54}
55
Lais Andradefd0e14c2024-07-30 15:57:23 +010056float getOldHapticScaleGamma(HapticLevel level) {
Ahmad Khalil2cd80352024-02-05 12:15:28 +000057 switch (level) {
58 case HapticLevel::VERY_LOW:
jiabin06871bb2020-06-30 21:27:52 -070059 return 2.0f;
Ahmad Khalil2cd80352024-02-05 12:15:28 +000060 case HapticLevel::LOW:
jiabin06871bb2020-06-30 21:27:52 -070061 return 1.5f;
Ahmad Khalil2cd80352024-02-05 12:15:28 +000062 case HapticLevel::HIGH:
jiabin06871bb2020-06-30 21:27:52 -070063 return 0.5f;
Ahmad Khalil2cd80352024-02-05 12:15:28 +000064 case HapticLevel::VERY_HIGH:
jiabin06871bb2020-06-30 21:27:52 -070065 return 0.25f;
66 default:
67 return 1.0f;
68 }
69}
70
Lais Andradefd0e14c2024-07-30 15:57:23 +010071float getOldHapticMaxAmplitudeRatio(HapticLevel level) {
Ahmad Khalil2cd80352024-02-05 12:15:28 +000072 switch (level) {
73 case HapticLevel::VERY_LOW:
jiabin06871bb2020-06-30 21:27:52 -070074 return HAPTIC_SCALE_VERY_LOW_RATIO;
Ahmad Khalil2cd80352024-02-05 12:15:28 +000075 case HapticLevel::LOW:
jiabin06871bb2020-06-30 21:27:52 -070076 return HAPTIC_SCALE_LOW_RATIO;
Ahmad Khalil2cd80352024-02-05 12:15:28 +000077 case HapticLevel::NONE:
78 case HapticLevel::HIGH:
79 case HapticLevel::VERY_HIGH:
jiabin06871bb2020-06-30 21:27:52 -070080 return 1.0f;
81 default:
82 return 0.0f;
83 }
84}
85
Lais Andrade045376e2024-07-24 15:35:07 +010086/* Same as VibrationScaler.getScaleFactor */
87float getHapticScaleFactor(HapticScale scale) {
88 if (android_os_vibrator_haptics_scale_v2_enabled()) {
89 if (scale.getScaleFactor() >= 0) {
90 // ExternalVibratorService provided the scale factor, use it.
91 return scale.getScaleFactor();
92 }
93
94 HapticLevel level = scale.getLevel();
95 switch (level) {
96 case HapticLevel::MUTE:
97 return 0.0f;
98 case HapticLevel::NONE:
99 return 1.0f;
100 default:
Lais Andrade780b4622024-08-30 15:55:27 +0100101 float scaleLevelGain = getScaleLevelGain();
102 float scaleFactor = powf(scaleLevelGain, static_cast<int32_t>(level));
Lais Andrade045376e2024-07-24 15:35:07 +0100103 if (scaleFactor <= 0) {
104 ALOGE("Invalid scale factor %.2f for level %d, using fallback to 1.0",
105 scaleFactor, static_cast<int32_t>(level));
106 scaleFactor = 1.0f;
107 }
108 return scaleFactor;
109 }
110 }
111 // Same as VibrationScaler.SCALE_FACTOR_*
112 switch (scale.getLevel()) {
113 case HapticLevel::MUTE:
114 return 0.0f;
Lais Andradefd0e14c2024-07-30 15:57:23 +0100115 case HapticLevel::VERY_LOW:
116 return 0.6f;
117 case HapticLevel::LOW:
118 return 0.8f;
119 case HapticLevel::HIGH:
120 return 1.2f;
121 case HapticLevel::VERY_HIGH:
122 return 1.4f;
123 default:
124 return 1.0f;
125 }
126}
127
128float applyOldHapticScale(float value, float gamma, float maxAmplitudeRatio) {
129 float sign = value >= 0 ? 1.0 : -1.0;
130 return powf(fabsf(value / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
131 * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign;
132}
133
134float applyNewHapticScale(float value, float scaleFactor) {
Lais Andrade045376e2024-07-24 15:35:07 +0100135 if (android_os_vibrator_haptics_scale_v2_enabled()) {
136 if (scaleFactor <= 1 || value == 0) {
137 return value * scaleFactor;
138 } else {
139 // Using S * x / (1 + (S - 1) * x^2) as the scale up function to converge to 1.0.
140 return (value * scaleFactor) / (1 + (scaleFactor - 1) * value * value);
141 }
142 }
Lais Andradefd0e14c2024-07-30 15:57:23 +0100143 float scale = powf(scaleFactor, 1.0f / SCALE_GAMMA);
144 if (scaleFactor <= 1) {
145 // Scale down is simply a gamma corrected application of scaleFactor to the intensity.
146 // Scale up requires a different curve to ensure the intensity will not become > 1.
147 return value * scale;
148 }
149
150 float sign = value >= 0 ? 1.0f : -1.0f;
151 float extraScale = powf(scaleFactor, 4.0f - scaleFactor);
152 float x = fabsf(value) * scale * extraScale;
153 float maxX = scale * extraScale; // scaled x for intensity == 1
154
155 float expX = expf(x);
156 float expMaxX = expf(maxX);
157
158 // Using f = tanh as the scale up function so the max value will converge.
159 // a = 1/f(maxX), used to scale f so that a*f(maxX) = 1 (the value will converge to 1).
160 float a = (expMaxX + 1.0f) / (expMaxX - 1.0f);
161 float fx = (expX - 1.0f) / (expX + 1.0f);
162
163 return sign * std::clamp(a * fx, 0.0f, 1.0f);
164}
165
Lais Andraded9451112021-07-02 00:15:33 +0100166void applyHapticScale(float* buffer, size_t length, HapticScale scale) {
Ahmad Khalil2cd80352024-02-05 12:15:28 +0000167 if (scale.isScaleMute()) {
Lais Andraded9451112021-07-02 00:15:33 +0100168 memset(buffer, 0, length * sizeof(float));
169 return;
170 }
Ahmad Khalil2cd80352024-02-05 12:15:28 +0000171 if (scale.isScaleNone()) {
Lais Andraded9451112021-07-02 00:15:33 +0100172 return;
173 }
Ahmad Khalil2cd80352024-02-05 12:15:28 +0000174 HapticLevel hapticLevel = scale.getLevel();
Lais Andrade045376e2024-07-24 15:35:07 +0100175 float scaleFactor = getHapticScaleFactor(scale);
Ahmad Khalil2cd80352024-02-05 12:15:28 +0000176 float adaptiveScaleFactor = scale.getAdaptiveScaleFactor();
Lais Andradefd0e14c2024-07-30 15:57:23 +0100177 float oldGamma = getOldHapticScaleGamma(hapticLevel);
178 float oldMaxAmplitudeRatio = getOldHapticMaxAmplitudeRatio(hapticLevel);
Ahmad Khalil2cd80352024-02-05 12:15:28 +0000179
Lais Andraded9451112021-07-02 00:15:33 +0100180 for (size_t i = 0; i < length; i++) {
Ahmad Khalil2cd80352024-02-05 12:15:28 +0000181 if (hapticLevel != HapticLevel::NONE) {
Lais Andrade045376e2024-07-24 15:35:07 +0100182 if (android_os_vibrator_fix_audio_coupled_haptics_scaling() ||
183 android_os_vibrator_haptics_scale_v2_enabled()) {
Lais Andradefd0e14c2024-07-30 15:57:23 +0100184 buffer[i] = applyNewHapticScale(buffer[i], scaleFactor);
185 } else {
186 buffer[i] = applyOldHapticScale(buffer[i], oldGamma, oldMaxAmplitudeRatio);
187 }
Ahmad Khalil2cd80352024-02-05 12:15:28 +0000188 }
189
Lais Andradeee09df52024-08-09 17:53:14 +0100190 if (adaptiveScaleFactor >= 0 && adaptiveScaleFactor != 1.0f) {
Ahmad Khalil2cd80352024-02-05 12:15:28 +0000191 buffer[i] *= adaptiveScaleFactor;
192 }
Lais Andraded9451112021-07-02 00:15:33 +0100193 }
194}
195
196void clipHapticData(float* buffer, size_t length, float limit) {
197 if (isnan(limit) || limit == 0) {
198 return;
199 }
200 limit = fabsf(limit);
201 for (size_t i = 0; i < length; i++) {
202 float sign = buffer[i] >= 0 ? 1.0 : -1.0;
203 if (fabsf(buffer[i]) > limit) {
204 buffer[i] = limit * sign;
205 }
206 }
207}
208
jiabin06871bb2020-06-30 21:27:52 -0700209} // namespace
210
211bool isValidHapticScale(HapticScale scale) {
Ahmad Khalil2cd80352024-02-05 12:15:28 +0000212 switch (scale.getLevel()) {
213 case HapticLevel::MUTE:
214 case HapticLevel::VERY_LOW:
215 case HapticLevel::LOW:
216 case HapticLevel::NONE:
217 case HapticLevel::HIGH:
218 case HapticLevel::VERY_HIGH:
jiabin06871bb2020-06-30 21:27:52 -0700219 return true;
220 }
221 return false;
222}
223
Lais Andraded9451112021-07-02 00:15:33 +0100224void scaleHapticData(float* buffer, size_t length, HapticScale scale, float limit) {
225 if (isValidHapticScale(scale)) {
226 applyHapticScale(buffer, length, scale);
jiabin06871bb2020-06-30 21:27:52 -0700227 }
Lais Andraded9451112021-07-02 00:15:33 +0100228 clipHapticData(buffer, length, limit);
jiabin06871bb2020-06-30 21:27:52 -0700229}
230
231} // namespace android::os