blob: 706f3d7a56f0076f88111497764fabcbb5ae6fef [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 */
16#include <cstring>
17
Lais Andrade1d885852024-07-30 15:57:23 +010018#include <android_os_vibrator.h>
19
20#include <algorithm>
jiabin06871bb2020-06-30 21:27:52 -070021#include <math.h>
22
23#include <vibrator/ExternalVibrationUtils.h>
24
25namespace android::os {
26
27namespace {
28static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2.0f / 3.0f;
29static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f;
30static constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f;
Lais Andrade1d885852024-07-30 15:57:23 +010031static constexpr float SCALE_GAMMA = 0.65f; // Same as VibrationEffect.SCALE_GAMMA
jiabin06871bb2020-06-30 21:27:52 -070032
Lais Andrade1d885852024-07-30 15:57:23 +010033float getOldHapticScaleGamma(HapticLevel level) {
Ahmad Khalil2cd80352024-02-05 12:15:28 +000034 switch (level) {
35 case HapticLevel::VERY_LOW:
jiabin06871bb2020-06-30 21:27:52 -070036 return 2.0f;
Ahmad Khalil2cd80352024-02-05 12:15:28 +000037 case HapticLevel::LOW:
jiabin06871bb2020-06-30 21:27:52 -070038 return 1.5f;
Ahmad Khalil2cd80352024-02-05 12:15:28 +000039 case HapticLevel::HIGH:
jiabin06871bb2020-06-30 21:27:52 -070040 return 0.5f;
Ahmad Khalil2cd80352024-02-05 12:15:28 +000041 case HapticLevel::VERY_HIGH:
jiabin06871bb2020-06-30 21:27:52 -070042 return 0.25f;
43 default:
44 return 1.0f;
45 }
46}
47
Lais Andrade1d885852024-07-30 15:57:23 +010048float getOldHapticMaxAmplitudeRatio(HapticLevel level) {
Ahmad Khalil2cd80352024-02-05 12:15:28 +000049 switch (level) {
50 case HapticLevel::VERY_LOW:
jiabin06871bb2020-06-30 21:27:52 -070051 return HAPTIC_SCALE_VERY_LOW_RATIO;
Ahmad Khalil2cd80352024-02-05 12:15:28 +000052 case HapticLevel::LOW:
jiabin06871bb2020-06-30 21:27:52 -070053 return HAPTIC_SCALE_LOW_RATIO;
Ahmad Khalil2cd80352024-02-05 12:15:28 +000054 case HapticLevel::NONE:
55 case HapticLevel::HIGH:
56 case HapticLevel::VERY_HIGH:
jiabin06871bb2020-06-30 21:27:52 -070057 return 1.0f;
58 default:
59 return 0.0f;
60 }
61}
62
Lais Andrade1d885852024-07-30 15:57:23 +010063/* Same as VibrationScaler.SCALE_LEVEL_* */
64float getHapticScaleFactor(HapticLevel level) {
65 switch (level) {
66 case HapticLevel::VERY_LOW:
67 return 0.6f;
68 case HapticLevel::LOW:
69 return 0.8f;
70 case HapticLevel::HIGH:
71 return 1.2f;
72 case HapticLevel::VERY_HIGH:
73 return 1.4f;
74 default:
75 return 1.0f;
76 }
77}
78
79float applyOldHapticScale(float value, float gamma, float maxAmplitudeRatio) {
80 float sign = value >= 0 ? 1.0 : -1.0;
81 return powf(fabsf(value / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
82 * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * sign;
83}
84
85float applyNewHapticScale(float value, float scaleFactor) {
86 float scale = powf(scaleFactor, 1.0f / SCALE_GAMMA);
87 if (scaleFactor <= 1) {
88 // Scale down is simply a gamma corrected application of scaleFactor to the intensity.
89 // Scale up requires a different curve to ensure the intensity will not become > 1.
90 return value * scale;
91 }
92
93 float sign = value >= 0 ? 1.0f : -1.0f;
94 float extraScale = powf(scaleFactor, 4.0f - scaleFactor);
95 float x = fabsf(value) * scale * extraScale;
96 float maxX = scale * extraScale; // scaled x for intensity == 1
97
98 float expX = expf(x);
99 float expMaxX = expf(maxX);
100
101 // Using f = tanh as the scale up function so the max value will converge.
102 // a = 1/f(maxX), used to scale f so that a*f(maxX) = 1 (the value will converge to 1).
103 float a = (expMaxX + 1.0f) / (expMaxX - 1.0f);
104 float fx = (expX - 1.0f) / (expX + 1.0f);
105
106 return sign * std::clamp(a * fx, 0.0f, 1.0f);
107}
108
Lais Andraded9451112021-07-02 00:15:33 +0100109void applyHapticScale(float* buffer, size_t length, HapticScale scale) {
Ahmad Khalil2cd80352024-02-05 12:15:28 +0000110 if (scale.isScaleMute()) {
Lais Andraded9451112021-07-02 00:15:33 +0100111 memset(buffer, 0, length * sizeof(float));
112 return;
113 }
Ahmad Khalil2cd80352024-02-05 12:15:28 +0000114 if (scale.isScaleNone()) {
Lais Andraded9451112021-07-02 00:15:33 +0100115 return;
116 }
Ahmad Khalil2cd80352024-02-05 12:15:28 +0000117 HapticLevel hapticLevel = scale.getLevel();
Lais Andrade1d885852024-07-30 15:57:23 +0100118 float scaleFactor = getHapticScaleFactor(hapticLevel);
Ahmad Khalil2cd80352024-02-05 12:15:28 +0000119 float adaptiveScaleFactor = scale.getAdaptiveScaleFactor();
Lais Andrade1d885852024-07-30 15:57:23 +0100120 float oldGamma = getOldHapticScaleGamma(hapticLevel);
121 float oldMaxAmplitudeRatio = getOldHapticMaxAmplitudeRatio(hapticLevel);
Ahmad Khalil2cd80352024-02-05 12:15:28 +0000122
Lais Andraded9451112021-07-02 00:15:33 +0100123 for (size_t i = 0; i < length; i++) {
Ahmad Khalil2cd80352024-02-05 12:15:28 +0000124 if (hapticLevel != HapticLevel::NONE) {
Lais Andrade1d885852024-07-30 15:57:23 +0100125 if (android_os_vibrator_fix_audio_coupled_haptics_scaling()) {
126 buffer[i] = applyNewHapticScale(buffer[i], scaleFactor);
127 } else {
128 buffer[i] = applyOldHapticScale(buffer[i], oldGamma, oldMaxAmplitudeRatio);
129 }
Ahmad Khalil2cd80352024-02-05 12:15:28 +0000130 }
131
132 if (adaptiveScaleFactor != 1.0f) {
133 buffer[i] *= adaptiveScaleFactor;
134 }
Lais Andraded9451112021-07-02 00:15:33 +0100135 }
136}
137
138void clipHapticData(float* buffer, size_t length, float limit) {
139 if (isnan(limit) || limit == 0) {
140 return;
141 }
142 limit = fabsf(limit);
143 for (size_t i = 0; i < length; i++) {
144 float sign = buffer[i] >= 0 ? 1.0 : -1.0;
145 if (fabsf(buffer[i]) > limit) {
146 buffer[i] = limit * sign;
147 }
148 }
149}
150
jiabin06871bb2020-06-30 21:27:52 -0700151} // namespace
152
153bool isValidHapticScale(HapticScale scale) {
Ahmad Khalil2cd80352024-02-05 12:15:28 +0000154 switch (scale.getLevel()) {
155 case HapticLevel::MUTE:
156 case HapticLevel::VERY_LOW:
157 case HapticLevel::LOW:
158 case HapticLevel::NONE:
159 case HapticLevel::HIGH:
160 case HapticLevel::VERY_HIGH:
jiabin06871bb2020-06-30 21:27:52 -0700161 return true;
162 }
163 return false;
164}
165
Lais Andraded9451112021-07-02 00:15:33 +0100166void scaleHapticData(float* buffer, size_t length, HapticScale scale, float limit) {
167 if (isValidHapticScale(scale)) {
168 applyHapticScale(buffer, length, scale);
jiabin06871bb2020-06-30 21:27:52 -0700169 }
Lais Andraded9451112021-07-02 00:15:33 +0100170 clipHapticData(buffer, length, limit);
jiabin06871bb2020-06-30 21:27:52 -0700171}
172
173} // namespace android::os