1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "custom_vibration_matcher.h"
17
18 #include <cmath>
19
20 #include "sensors_errors.h"
21 #include "vibrator_hdi_connection.h"
22
23 #undef LOG_TAG
24 #define LOG_TAG "CustomVibrationMatcher"
25
26 namespace OHOS {
27 namespace Sensors {
28 namespace {
29 constexpr int32_t FREQUENCY_MIN = 0;
30 constexpr int32_t FREQUENCY_MAX = 100;
31 constexpr int32_t INTENSITY_MIN = 0;
32 constexpr int32_t INTENSITY_MAX = 100;
33 constexpr int32_t VIBRATOR_DELAY = 20;
34 constexpr int32_t CONTINUOUS_GRADE_NUM = 8;
35 constexpr int32_t CONTINUOUS_GRADE_MASK = 100;
36 constexpr float ROUND_OFFSET = 0.5;
37 constexpr float CONTINUOUS_GRADE_SCALE = 100. / 8;
38 constexpr float INTENSITY_WEIGHT = 0.5;
39 constexpr float FREQUENCY_WEIGHT = 0.5;
40 constexpr float WEIGHT_SUM_INIT = 100;
41 constexpr int32_t EFFECT_ID_BOUNDARY = 1000;
42 constexpr int32_t DURATION_MAX = 1600;
43 constexpr float CURVE_INTENSITY_SCALE = 100.00;
44 const float EPSILON = 0.00001;
45 constexpr int32_t SLICE_STEP = 50;
46 constexpr int32_t CONTINUOUS_VIBRATION_DURATION_MIN = 15;
47 constexpr int32_t INDEX_MIN_RESTRICT = 1;
48 constexpr int32_t WAVE_INFO_DIMENSION = 3;
49 } // namespace
50
CustomVibrationMatcher()51 CustomVibrationMatcher::CustomVibrationMatcher()
52 {
53 auto &VibratorDevice = VibratorHdiConnection::GetInstance();
54 int32_t ret = VibratorDevice.GetAllWaveInfo(hdfWaveInfos_);
55 if (ret != ERR_OK) {
56 MISC_HILOGE("GetAllWaveInfo failed infoSize:%{public}zu", hdfWaveInfos_.size());
57 return;
58 }
59 if (!hdfWaveInfos_.empty()) {
60 for (const auto &info : hdfWaveInfos_) {
61 MISC_HILOGI("waveId:%{public}d, intensity:%{public}f, frequency:%{public}f, duration:%{public}d",
62 info.waveId, info.intensity, info.frequency, info.duration);
63 }
64 NormalizedWaveInfo();
65 }
66 }
67
NormalizedWaveInfo()68 void CustomVibrationMatcher::NormalizedWaveInfo()
69 {
70 CALL_LOG_ENTER;
71 auto firstIt = hdfWaveInfos_.begin();
72 float maxIntensity = firstIt->intensity;
73 float minFrequency = firstIt->frequency;
74 float maxFrequency = firstIt->frequency;
75 for (const auto &info : hdfWaveInfos_) {
76 maxIntensity = (maxIntensity > info.intensity) ? maxIntensity : info.intensity;
77 minFrequency = (minFrequency < info.frequency) ? minFrequency : info.frequency;
78 maxFrequency = (maxFrequency > info.frequency) ? maxFrequency : info.frequency;
79 }
80
81 float intensityEqualValue = maxIntensity / INTENSITY_MAX;
82 float frequencyEqualValue = (maxFrequency - minFrequency) / FREQUENCY_MAX;
83 if ((std::abs(intensityEqualValue) <= EPSILON) || (std::abs(frequencyEqualValue) <= EPSILON)) {
84 MISC_HILOGE("The equal value of intensity or frequency is zero");
85 return;
86 }
87 for (const auto &info : hdfWaveInfos_) {
88 std::vector<int32_t> normalizedValue;
89 normalizedValue.push_back(static_cast<int32_t>(info.intensity / intensityEqualValue));
90 normalizedValue.push_back(static_cast<int32_t>((info.frequency - minFrequency) / frequencyEqualValue));
91 normalizedValue.push_back(info.duration);
92 waveInfos_[info.waveId] = normalizedValue;
93 }
94 for (const auto &info : waveInfos_) {
95 MISC_HILOGI("waveId:%{public}d, intensity:%{public}d, frequency:%{public}d, duration:%{public}d",
96 info.first, info.second[0], info.second[1], info.second[WAVE_INFO_DIMENSION - 1]);
97 }
98 }
99
GetInstance()100 CustomVibrationMatcher &CustomVibrationMatcher::GetInstance()
101 {
102 static CustomVibrationMatcher instance;
103 return instance;
104 }
105
TransformTime(const VibratePackage & package,std::vector<CompositeEffect> & compositeEffects)106 int32_t CustomVibrationMatcher::TransformTime(const VibratePackage &package,
107 std::vector<CompositeEffect> &compositeEffects)
108 {
109 CALL_LOG_ENTER;
110 VibratePattern flatPattern = MixedWaveProcess(package);
111 if (flatPattern.events.empty()) {
112 MISC_HILOGE("The events of pattern is empty");
113 return ERROR;
114 }
115 int32_t frontTime = 0;
116 for (const VibrateEvent &event : flatPattern.events) {
117 TimeEffect timeEffect;
118 timeEffect.delay = event.time - frontTime;
119 timeEffect.time = event.duration;
120 timeEffect.intensity = event.intensity;
121 timeEffect.frequency = event.frequency;
122 CompositeEffect compositeEffect;
123 compositeEffect.timeEffect = timeEffect;
124 compositeEffects.push_back(compositeEffect);
125 frontTime = event.time;
126 }
127 TimeEffect timeEffect;
128 timeEffect.delay = flatPattern.events.back().duration;
129 timeEffect.time = 0;
130 timeEffect.intensity = 0;
131 timeEffect.frequency = 0;
132 CompositeEffect compositeEffect;
133 compositeEffect.timeEffect = timeEffect;
134 compositeEffects.push_back(compositeEffect);
135 return SUCCESS;
136 }
137
TransformEffect(const VibratePackage & package,std::vector<CompositeEffect> & compositeEffects)138 int32_t CustomVibrationMatcher::TransformEffect(const VibratePackage &package,
139 std::vector<CompositeEffect> &compositeEffects)
140 {
141 CALL_LOG_ENTER;
142 VibratePattern flatPattern = MixedWaveProcess(package);
143 if (flatPattern.events.empty()) {
144 MISC_HILOGE("The events of pattern is empty");
145 return ERROR;
146 }
147 int32_t preStartTime = flatPattern.startTime;
148 int32_t preDuration = 0;
149 for (const VibrateEvent &event : flatPattern.events) {
150 if ((event.tag == EVENT_TAG_CONTINUOUS) || waveInfos_.empty()) {
151 PrimitiveEffect primitiveEffect;
152 primitiveEffect.delay = event.time - preStartTime;
153 primitiveEffect.effectId = event.duration;
154 primitiveEffect.intensity = event.intensity;
155 CompositeEffect compositeEffect;
156 compositeEffect.primitiveEffect = primitiveEffect;
157 compositeEffects.push_back(compositeEffect);
158 preStartTime = event.time;
159 preDuration = event.duration;
160 } else if (event.tag == EVENT_TAG_TRANSIENT) {
161 ProcessTransientEvent(event, preStartTime, preDuration, compositeEffects);
162 } else {
163 MISC_HILOGE("Unknown event tag, tag:%{public}d", event.tag);
164 return ERROR;
165 }
166 }
167 PrimitiveEffect primitiveEffect;
168 primitiveEffect.delay = preDuration;
169 primitiveEffect.effectId = 0;
170 primitiveEffect.intensity = 0;
171 CompositeEffect compositeEffect;
172 compositeEffect.primitiveEffect = primitiveEffect;
173 compositeEffects.push_back(compositeEffect);
174 return SUCCESS;
175 }
176
MixedWaveProcess(const VibratePackage & package)177 VibratePattern CustomVibrationMatcher::MixedWaveProcess(const VibratePackage &package)
178 {
179 VibratePattern outputPattern;
180 std::vector<VibrateEvent> &outputEvents = outputPattern.events;
181 for (const VibratePattern &pattern : package.patterns) {
182 for (VibrateEvent event : pattern.events) {
183 event.time += pattern.startTime;
184 PreProcessEvent(event);
185 if ((outputEvents.empty()) || (outputEvents.back().tag == EVENT_TAG_TRANSIENT)) {
186 outputEvents.emplace_back(event);
187 } else if ((event.time >= (outputEvents.back().time + outputEvents.back().duration))) {
188 int32_t diffTime = event.time - outputEvents.back().time - outputEvents.back().duration;
189 outputEvents.back().duration += ((diffTime < VIBRATOR_DELAY) ? (diffTime - VIBRATOR_DELAY) : 0);
190 outputEvents.back().duration = std::max(outputEvents.back().duration, 0);
191 outputEvents.emplace_back(event);
192 } else {
193 VibrateEvent &lastEvent = outputEvents.back();
194 VibrateEvent newEvent = {
195 .tag = EVENT_TAG_CONTINUOUS,
196 .time = lastEvent.time,
197 .duration = std::max(lastEvent.time + lastEvent.duration, event.time + event.duration)
198 - lastEvent.time,
199 .intensity = lastEvent.intensity,
200 .frequency = lastEvent.frequency,
201 .index = lastEvent.index,
202 .points = MergeCurve(lastEvent.points, event.points),
203 };
204 outputEvents.pop_back();
205 outputEvents.push_back(newEvent);
206 }
207 }
208 }
209 return outputPattern;
210 }
211
PreProcessEvent(VibrateEvent & event)212 void CustomVibrationMatcher::PreProcessEvent(VibrateEvent &event)
213 {
214 if (event.points.empty()) {
215 VibrateCurvePoint startPoint = {
216 .time = 0,
217 .intensity = INTENSITY_MAX,
218 .frequency = 0,
219 };
220 event.points.push_back(startPoint);
221 VibrateCurvePoint endPoint = {
222 .time = event.duration,
223 .intensity = INTENSITY_MAX,
224 .frequency = 0,
225 };
226 event.points.push_back(endPoint);
227 }
228 event.duration = std::max(event.duration, CONTINUOUS_VIBRATION_DURATION_MIN);
229 for (VibrateCurvePoint &curvePoint : event.points) {
230 curvePoint.time += event.time;
231 curvePoint.intensity *= (event.intensity / CURVE_INTENSITY_SCALE);
232 curvePoint.intensity = std::max(curvePoint.intensity, INTENSITY_MIN);
233 curvePoint.intensity = std::min(curvePoint.intensity, INTENSITY_MAX);
234 curvePoint.frequency += event.frequency;
235 curvePoint.frequency = std::max(curvePoint.frequency, FREQUENCY_MIN);
236 curvePoint.frequency = std::min(curvePoint.frequency, FREQUENCY_MAX);
237 }
238 }
239
MergeCurve(const std::vector<VibrateCurvePoint> & curveLeft,const std::vector<VibrateCurvePoint> & curveRight)240 std::vector<VibrateCurvePoint> CustomVibrationMatcher::MergeCurve(const std::vector<VibrateCurvePoint> &curveLeft,
241 const std::vector<VibrateCurvePoint> &curveRight)
242 {
243 if (curveLeft.empty()) {
244 return curveRight;
245 }
246 if (curveRight.empty()) {
247 return curveLeft;
248 }
249 int32_t overlapLeft = std::max(curveLeft.front().time, curveRight.front().time);
250 int32_t overlapRight = std::min(curveLeft.back().time, curveRight.back().time);
251 std::vector<VibrateCurvePoint> newCurve;
252 size_t i = 0;
253 size_t j = 0;
254 while (i < curveLeft.size() || j < curveRight.size()) {
255 while (i < curveLeft.size() && ((curveLeft[i].time < overlapLeft) || (curveLeft[i].time > overlapRight) ||
256 (j == curveRight.size()))) {
257 newCurve.push_back(curveLeft[i]);
258 ++i;
259 }
260 while (j < curveRight.size() && ((curveRight[j].time < overlapLeft) || (curveRight[j].time > overlapRight) ||
261 (i == curveLeft.size()))) {
262 newCurve.push_back(curveRight[j]);
263 ++j;
264 }
265 VibrateCurvePoint newCurvePoint;
266 if (i < curveLeft.size() && j < curveRight.size()) {
267 if ((curveLeft[i].time < curveRight[j].time) && (j > 0)) {
268 int32_t intensity = Interpolation(curveRight[j - 1].time, curveRight[j].time,
269 curveRight[j - 1].intensity, curveRight[j].intensity, curveLeft[i].time);
270 int32_t frequency = Interpolation(curveRight[j - 1].time, curveRight[j].time,
271 curveRight[j - 1].frequency, curveRight[j].frequency, curveLeft[i].time);
272 newCurvePoint.time = curveLeft[i].time;
273 newCurvePoint.intensity = std::max(curveLeft[i].intensity, intensity);
274 newCurvePoint.frequency = (curveLeft[i].frequency + frequency) / 2;
275 ++i;
276 } else if ((curveLeft[i].time > curveRight[j].time) && (i > 0)) {
277 int32_t intensity = Interpolation(curveLeft[i - 1].time, curveLeft[i].time,
278 curveLeft[i - 1].intensity, curveLeft[i].intensity, curveRight[j].time);
279 int32_t frequency = Interpolation(curveLeft[i - 1].time, curveLeft[i].time,
280 curveLeft[i - 1].frequency, curveLeft[i].frequency, curveRight[j].time);
281 newCurvePoint.time = curveRight[j].time;
282 newCurvePoint.intensity = std::max(curveRight[j].intensity, intensity);
283 newCurvePoint.frequency = (curveRight[j].frequency + frequency) / 2;
284 ++j;
285 } else {
286 newCurvePoint.time = curveRight[i].time;
287 newCurvePoint.intensity = std::max(curveLeft[i].intensity, curveRight[j].intensity);
288 newCurvePoint.frequency = (curveLeft[i].frequency + curveRight[j].frequency) / 2;
289 ++i;
290 ++j;
291 }
292 newCurve.push_back(newCurvePoint);
293 }
294 }
295 return newCurve;
296 }
297
ProcessContinuousEvent(const VibrateEvent & event,int32_t & preStartTime,int32_t & preDuration,std::vector<CompositeEffect> & compositeEffects)298 void CustomVibrationMatcher::ProcessContinuousEvent(const VibrateEvent &event, int32_t &preStartTime,
299 int32_t &preDuration, std::vector<CompositeEffect> &compositeEffects)
300 {
301 if (event.duration < 2 * SLICE_STEP) {
302 VibrateSlice slice = {
303 .time = event.time,
304 .duration = event.duration,
305 .intensity = event.intensity,
306 .frequency = event.frequency,
307 };
308 ProcessContinuousEventSlice(slice, preStartTime, preDuration, compositeEffects);
309 return;
310 }
311 const std::vector<VibrateCurvePoint> &curve = event.points;
312 int32_t endTime = curve.back().time;
313 int32_t curTime = curve.front().time;
314 int32_t curIntensity = curve.front().intensity;
315 int32_t curFrequency = curve.front().frequency;
316 int32_t nextTime = 0;
317 int32_t i = 0;
318 while (curTime < endTime) {
319 int32_t nextIntensity = 0;
320 int32_t nextFrequency = 0;
321 if ((endTime - curTime) >= (2 * SLICE_STEP)) {
322 nextTime = curTime + SLICE_STEP;
323 } else {
324 nextTime = endTime;
325 }
326 while (curve[i].time < nextTime) {
327 ++i;
328 }
329 if (i < INDEX_MIN_RESTRICT) {
330 curTime = nextTime;
331 continue;
332 }
333 nextIntensity = Interpolation(curve[i - 1].time, curve[i].time, curve[i - 1].intensity, curve[i].intensity,
334 nextTime);
335 nextFrequency = Interpolation(curve[i - 1].time, curve[i].time, curve[i - 1].frequency, curve[i].frequency,
336 nextTime);
337 VibrateSlice slice = {
338 .time = curTime,
339 .duration = nextTime - curTime,
340 .intensity = (curIntensity + nextIntensity) / 2,
341 .frequency = (curFrequency + nextFrequency) / 2,
342 };
343 ProcessContinuousEventSlice(slice, preStartTime, preDuration, compositeEffects);
344 curTime = nextTime;
345 curIntensity = nextIntensity;
346 curFrequency = nextFrequency;
347 }
348 }
349
ProcessContinuousEventSlice(const VibrateSlice & slice,int32_t & preStartTime,int32_t & preDuration,std::vector<CompositeEffect> & compositeEffects)350 void CustomVibrationMatcher::ProcessContinuousEventSlice(const VibrateSlice &slice, int32_t &preStartTime,
351 int32_t &preDuration, std::vector<CompositeEffect> &compositeEffects)
352 {
353 int32_t grade = -1;
354 if (slice.intensity == INTENSITY_MAX) {
355 grade = CONTINUOUS_GRADE_NUM - 1;
356 } else {
357 grade = round(slice.intensity / CONTINUOUS_GRADE_SCALE + ROUND_OFFSET) - 1;
358 }
359 if ((!compositeEffects.empty()) && (slice.time == preStartTime + preDuration)) {
360 PrimitiveEffect &prePrimitiveEffect = compositeEffects.back().primitiveEffect;
361 int32_t preEffectId = prePrimitiveEffect.effectId;
362 int32_t preGrade = preEffectId % CONTINUOUS_GRADE_MASK;
363 int32_t mergeDuration = preDuration + slice.duration;
364 if (preEffectId > EFFECT_ID_BOUNDARY && preGrade == grade && mergeDuration < DURATION_MAX) {
365 prePrimitiveEffect.effectId = mergeDuration * CONTINUOUS_GRADE_MASK + grade;
366 preDuration = mergeDuration;
367 return;
368 }
369 }
370 PrimitiveEffect primitiveEffect;
371 primitiveEffect.delay = slice.time - preStartTime;
372 primitiveEffect.effectId = slice.duration * CONTINUOUS_GRADE_MASK + grade;
373 CompositeEffect compositeEffect;
374 compositeEffect.primitiveEffect = primitiveEffect;
375 compositeEffects.push_back(compositeEffect);
376 preStartTime = slice.time;
377 preDuration = slice.duration;
378 }
379
ProcessTransientEvent(const VibrateEvent & event,int32_t & preStartTime,int32_t & preDuration,std::vector<CompositeEffect> & compositeEffects)380 void CustomVibrationMatcher::ProcessTransientEvent(const VibrateEvent &event, int32_t &preStartTime,
381 int32_t &preDuration, std::vector<CompositeEffect> &compositeEffects)
382 {
383 int32_t matchId = 0;
384 float minWeightSum = WEIGHT_SUM_INIT;
385 for (const auto &transientInfo : waveInfos_) {
386 int32_t id = transientInfo.first;
387 const std::vector<int32_t> &info = transientInfo.second;
388 float intensityDistance = std::abs(event.intensity - info[0]);
389 float frequencyDistance = std::abs(event.frequency - info[1]);
390 float weightSum = INTENSITY_WEIGHT * intensityDistance + FREQUENCY_WEIGHT * frequencyDistance;
391 if (weightSum < minWeightSum) {
392 minWeightSum = weightSum;
393 matchId = id;
394 }
395 }
396 PrimitiveEffect primitiveEffect;
397 primitiveEffect.delay = event.time - preStartTime;
398 primitiveEffect.effectId = (-matchId);
399 primitiveEffect.intensity = INTENSITY_MAX;
400 CompositeEffect compositeEffect;
401 compositeEffect.primitiveEffect = primitiveEffect;
402 compositeEffects.push_back(compositeEffect);
403 preStartTime = event.time;
404 preDuration = event.duration;
405 }
406
Interpolation(int32_t x1,int32_t x2,int32_t y1,int32_t y2,int32_t x)407 int32_t CustomVibrationMatcher::Interpolation(int32_t x1, int32_t x2, int32_t y1, int32_t y2, int32_t x)
408 {
409 if (x1 == x2) {
410 return y1;
411 }
412 float delta_y = static_cast<float>(y2 - y1);
413 float delta_x = static_cast<float>(x2 - x1);
414 return y1 + delta_y / delta_x * (x - x1);
415 }
416 } // namespace Sensors
417 } // namespace OHOS
418