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 #include <map>
20
21 #include "sensors_errors.h"
22
23 namespace OHOS {
24 namespace Sensors {
25 namespace {
26 const std::map<int32_t, std::vector<int32_t>> TRANSIENT_VIBRATION_INFOS = {
27 {0x28, {0x4d, 0x4d, 0x0b}}, {0x2c, {0x2a, 0x64, 0x07}}, {0x30, {0x44, 0x52, 0x16}},
28 {0x3c, {0x45, 0x34, 0x0a}}, {0x40, {0x2e, 0x43, 0x0a}}, {0x48, {0x51, 0x52, 0x0a}},
29 {0x4c, {0x3a, 0x0c, 0x0f}}, {0x50, {0x64, 0x20, 0x14}}, {0x54, {0x55, 0x34, 0x1c}},
30 {0x5c, {0x32, 0x0c, 0x13}}, {0x60, {0x12, 0x07, 0x0a}}
31 };
32 constexpr int32_t INTENSITY_MAX = 100;
33 constexpr int32_t TRANSIENT_GRADE_NUM = 4;
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 TRANSIENT_GRADE_GAIN = 0.25;
38 constexpr float CONTINUOUS_GRADE_SCALE = 100. / 8;
39 constexpr float INTENSITY_WEIGHT = 0.5;
40 constexpr float FREQUENCY_WEIGHT = 0.5;
41 constexpr float WEIGHT_SUM_INIT = 100;
42 constexpr int32_t STOP_WAVEFORM = 0;
43 constexpr int32_t EFFECT_ID_BOUNDARY = 1000;
44 constexpr int32_t DURATION_MAX = 1600;
45 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, MISC_LOG_DOMAIN, "CustomVibrationMatcher" };
46 } // namespace
47
TransformEffect(const std::set<VibrateEvent> & vibrateSet,std::vector<CompositeEffect> & compositeEffects)48 int32_t CustomVibrationMatcher::TransformEffect(const std::set<VibrateEvent> &vibrateSet,
49 std::vector<CompositeEffect> &compositeEffects)
50 {
51 CALL_LOG_ENTER;
52 int32_t preStartTime = 0;
53 int32_t preDuration = 0;
54 for (const auto &event : vibrateSet) {
55 if ((preDuration != 0) && (event.startTime < preStartTime + preDuration)) {
56 MISC_HILOGE("Vibration events overlap");
57 return ERROR;
58 }
59 if (event.tag == EVENT_TAG_CONTINUOUS) {
60 ProcessContinuousEvent(event, preStartTime, preDuration, compositeEffects);
61 } else if (event.tag == EVENT_TAG_TRANSIENT) {
62 ProcessTransientEvent(event, preStartTime, preDuration, compositeEffects);
63 } else {
64 MISC_HILOGE("Unknown event tag, tag:%{public}d", event.tag);
65 return ERROR;
66 }
67 }
68 PrimitiveEffect primitiveEffect;
69 primitiveEffect.delay = preDuration;
70 primitiveEffect.effectId = STOP_WAVEFORM;
71 CompositeEffect compositeEffect;
72 compositeEffect.primitiveEffect = primitiveEffect;
73 compositeEffects.push_back(compositeEffect);
74 return SUCCESS;
75 }
76
ProcessContinuousEvent(const VibrateEvent & event,int32_t & preStartTime,int32_t & preDuration,std::vector<CompositeEffect> & compositeEffects)77 void CustomVibrationMatcher::ProcessContinuousEvent(const VibrateEvent &event, int32_t &preStartTime,
78 int32_t &preDuration, std::vector<CompositeEffect> &compositeEffects)
79 {
80 int32_t grade = -1;
81 if (event.intensity == INTENSITY_MAX) {
82 grade = CONTINUOUS_GRADE_NUM - 1;
83 } else {
84 grade = round(event.intensity / CONTINUOUS_GRADE_SCALE + ROUND_OFFSET) - 1;
85 }
86 if ((!compositeEffects.empty()) && (event.startTime == preStartTime + preDuration)) {
87 PrimitiveEffect& prePrimitiveEffect = compositeEffects.back().primitiveEffect;
88 int32_t preEffectId = prePrimitiveEffect.effectId;
89 int32_t preGrade = preEffectId % CONTINUOUS_GRADE_MASK;
90 int32_t mergeDuration = preDuration + event.duration;
91 if (preEffectId > EFFECT_ID_BOUNDARY && preGrade == grade && mergeDuration < DURATION_MAX) {
92 prePrimitiveEffect.effectId = mergeDuration * CONTINUOUS_GRADE_MASK + grade;
93 preDuration = mergeDuration;
94 return;
95 }
96 }
97 PrimitiveEffect primitiveEffect;
98 primitiveEffect.delay = event.startTime - preStartTime;
99 primitiveEffect.effectId = event.duration * CONTINUOUS_GRADE_MASK + grade;
100 CompositeEffect compositeEffect;
101 compositeEffect.primitiveEffect = primitiveEffect;
102 compositeEffects.push_back(compositeEffect);
103 preStartTime = event.startTime;
104 preDuration = event.duration;
105 }
106
ProcessTransientEvent(const VibrateEvent & event,int32_t & preStartTime,int32_t & preDuration,std::vector<CompositeEffect> & compositeEffects)107 void CustomVibrationMatcher::ProcessTransientEvent(const VibrateEvent &event, int32_t &preStartTime,
108 int32_t &preDuration, std::vector<CompositeEffect> &compositeEffects)
109 {
110 int32_t matchId = 0;
111 float minWeightSum = WEIGHT_SUM_INIT;
112 for (const auto &transientInfo : TRANSIENT_VIBRATION_INFOS) {
113 int32_t id = transientInfo.first;
114 const std::vector<int32_t> &info = transientInfo.second;
115 float frequencyDistance = std::abs(event.frequency - info[1]);
116 for (int32_t j = 0; j < TRANSIENT_GRADE_NUM; ++j) {
117 float intensityDistance = std::abs(event.intensity - info[0] * (1 - j * TRANSIENT_GRADE_GAIN));
118 float weightSum = INTENSITY_WEIGHT * intensityDistance + FREQUENCY_WEIGHT * frequencyDistance;
119 if (weightSum < minWeightSum) {
120 minWeightSum = weightSum;
121 matchId = id + j;
122 }
123 }
124 }
125 PrimitiveEffect primitiveEffect;
126 primitiveEffect.delay = event.startTime - preStartTime;
127 primitiveEffect.effectId = matchId;
128 CompositeEffect compositeEffect;
129 compositeEffect.primitiveEffect = primitiveEffect;
130 compositeEffects.push_back(compositeEffect);
131 preStartTime = event.startTime;
132 preDuration = event.duration;
133 }
134 } // namespace Sensors
135 } // namespace OHOS