• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #undef LOG_TAG
24 #define LOG_TAG "CustomVibrationMatcher"
25 
26 namespace OHOS {
27 namespace Sensors {
28 namespace {
29 const std::map<int32_t, std::vector<int32_t>> TRANSIENT_VIBRATION_INFOS = {
30     {0x28, {0x4d, 0x4d, 0x0b}}, {0x2c, {0x2a, 0x64, 0x07}}, {0x30, {0x44, 0x52, 0x16}},
31     {0x3c, {0x45, 0x34, 0x0a}}, {0x40, {0x2e, 0x43, 0x0a}}, {0x48, {0x51, 0x52, 0x0a}},
32     {0x4c, {0x3a, 0x0c, 0x0f}}, {0x50, {0x64, 0x20, 0x14}}, {0x54, {0x55, 0x34, 0x1c}},
33     {0x5c, {0x32, 0x0c, 0x13}}, {0x60, {0x12, 0x07, 0x0a}}
34 };
35 constexpr int32_t FREQUENCY_MIN = 0;
36 constexpr int32_t FREQUENCY_MAX = 100;
37 constexpr int32_t INTENSITY_MIN = 0;
38 constexpr int32_t INTENSITY_MAX = 100;
39 constexpr int32_t TRANSIENT_GRADE_NUM = 4;
40 constexpr int32_t CONTINUOUS_GRADE_NUM = 8;
41 constexpr int32_t CONTINUOUS_GRADE_MASK = 100;
42 constexpr float ROUND_OFFSET = 0.5;
43 constexpr float TRANSIENT_GRADE_GAIN = 0.25;
44 constexpr float CONTINUOUS_GRADE_SCALE = 100. / 8;
45 constexpr float INTENSITY_WEIGHT = 0.5;
46 constexpr float FREQUENCY_WEIGHT = 0.5;
47 constexpr float WEIGHT_SUM_INIT = 100;
48 constexpr int32_t STOP_WAVEFORM = 0;
49 constexpr int32_t EFFECT_ID_BOUNDARY = 1000;
50 constexpr int32_t DURATION_MAX = 1600;
51 constexpr float CURVE_INTENSITY_SCALE = 100.00;
52 constexpr int32_t SLICE_STEP = 50;
53 constexpr int32_t CONTINUOUS_VIBRATION_DURATION_MIN = 15;
54 }  // namespace
55 
TransformTime(const VibratePackage & package,std::vector<CompositeEffect> & compositeEffects)56 int32_t CustomVibrationMatcher::TransformTime(const VibratePackage &package,
57     std::vector<CompositeEffect> &compositeEffects)
58 {
59     CALL_LOG_ENTER;
60     VibratePattern flatPattern = MixedWaveProcess(package);
61     if (flatPattern.events.empty()) {
62         MISC_HILOGE("The events of pattern is empty");
63         return ERROR;
64     }
65     int32_t frontTime = 0;
66     for (const VibrateEvent &event : flatPattern.events) {
67         TimeEffect timeEffect;
68         timeEffect.delay = event.time - frontTime;
69         timeEffect.time = event.duration;
70         CompositeEffect compositeEffect;
71         compositeEffect.timeEffect = timeEffect;
72         compositeEffects.push_back(compositeEffect);
73         frontTime = event.time;
74     }
75     TimeEffect timeEffect;
76     timeEffect.delay = flatPattern.events.back().duration;
77     timeEffect.time = 0;
78     CompositeEffect compositeEffect;
79     compositeEffect.timeEffect = timeEffect;
80     compositeEffects.push_back(compositeEffect);
81     return SUCCESS;
82 }
83 
TransformEffect(const VibratePackage & package,std::vector<CompositeEffect> & compositeEffects)84 int32_t CustomVibrationMatcher::TransformEffect(const VibratePackage &package,
85     std::vector<CompositeEffect> &compositeEffects)
86 {
87     CALL_LOG_ENTER;
88     VibratePattern flatPattern = MixedWaveProcess(package);
89     if (flatPattern.events.empty()) {
90         MISC_HILOGE("The events of pattern is empty");
91         return ERROR;
92     }
93     int32_t preStartTime = flatPattern.startTime;
94     int32_t preDuration = 0;
95     for (const VibrateEvent &event : flatPattern.events) {
96         if (event.tag == EVENT_TAG_CONTINUOUS) {
97             ProcessContinuousEvent(event, preStartTime, preDuration, compositeEffects);
98         } else if (event.tag == EVENT_TAG_TRANSIENT) {
99             ProcessTransientEvent(event, preStartTime, preDuration, compositeEffects);
100         } else {
101             MISC_HILOGE("Unknown event tag, tag:%{public}d", event.tag);
102             return ERROR;
103         }
104     }
105     PrimitiveEffect primitiveEffect;
106     primitiveEffect.delay = preDuration;
107     primitiveEffect.effectId = STOP_WAVEFORM;
108     CompositeEffect compositeEffect;
109     compositeEffect.primitiveEffect = primitiveEffect;
110     compositeEffects.push_back(compositeEffect);
111     return SUCCESS;
112 }
113 
MixedWaveProcess(const VibratePackage & package)114 VibratePattern CustomVibrationMatcher::MixedWaveProcess(const VibratePackage &package)
115 {
116     VibratePattern outputPattern;
117     std::vector<VibrateEvent> &outputEvents = outputPattern.events;
118     for (const VibratePattern &pattern : package.patterns) {
119         for (VibrateEvent event : pattern.events) {
120             event.time += pattern.startTime;
121             PreProcessEvent(event);
122             if ((outputEvents.empty()) ||
123                 (event.time >= (outputEvents.back().time + outputEvents.back().duration)) ||
124                 (outputEvents.back().tag == EVENT_TAG_TRANSIENT)) {
125                 outputEvents.emplace_back(event);
126             } else {
127                 VibrateEvent &lastEvent = outputEvents.back();
128                 VibrateEvent newEvent = {
129                     .tag = EVENT_TAG_CONTINUOUS,
130                     .time = lastEvent.time,
131                     .duration = std::max(lastEvent.time + lastEvent.duration, event.time + event.duration)
132                         - lastEvent.time,
133                     .intensity = lastEvent.intensity,
134                     .frequency = lastEvent.frequency,
135                     .index = lastEvent.index,
136                     .points = MergeCurve(lastEvent.points, event.points),
137                 };
138                 outputEvents.pop_back();
139                 outputEvents.push_back(newEvent);
140             }
141         }
142     }
143     return outputPattern;
144 }
145 
PreProcessEvent(VibrateEvent & event)146 void CustomVibrationMatcher::PreProcessEvent(VibrateEvent &event)
147 {
148     if (event.points.empty()) {
149         VibrateCurvePoint startPoint = {
150             .time = 0,
151             .intensity = INTENSITY_MAX,
152             .frequency = 0,
153         };
154         event.points.push_back(startPoint);
155         VibrateCurvePoint endPoint = {
156             .time = event.duration,
157             .intensity = INTENSITY_MAX,
158             .frequency = 0,
159         };
160         event.points.push_back(endPoint);
161     }
162     event.duration = std::max(event.duration, CONTINUOUS_VIBRATION_DURATION_MIN);
163     for (VibrateCurvePoint &curvePoint : event.points) {
164         curvePoint.time += event.time;
165         curvePoint.intensity *= (event.intensity / CURVE_INTENSITY_SCALE);
166         curvePoint.intensity = std::max(curvePoint.intensity, INTENSITY_MIN);
167         curvePoint.intensity = std::min(curvePoint.intensity, INTENSITY_MAX);
168         curvePoint.frequency += event.frequency;
169         curvePoint.frequency = std::max(curvePoint.frequency, FREQUENCY_MIN);
170         curvePoint.frequency = std::min(curvePoint.frequency, FREQUENCY_MAX);
171     }
172 }
173 
MergeCurve(const std::vector<VibrateCurvePoint> & curveLeft,const std::vector<VibrateCurvePoint> & curveRight)174 std::vector<VibrateCurvePoint> CustomVibrationMatcher::MergeCurve(const std::vector<VibrateCurvePoint> &curveLeft,
175     const std::vector<VibrateCurvePoint> &curveRight)
176 {
177     int32_t overlapLeft = std::max(curveLeft.front().time, curveRight.front().time);
178     int32_t overlapRight = std::min(curveLeft.back().time, curveRight.back().time);
179     std::vector<VibrateCurvePoint> newCurve;
180     size_t i = 0;
181     size_t j = 0;
182     while (i < curveLeft.size() || j < curveRight.size()) {
183         while (i < curveLeft.size() && ((curveLeft[i].time < overlapLeft) || (curveLeft[i].time > overlapRight) || (j == curveRight.size()))) {
184             newCurve.push_back(curveLeft[i]);
185             ++i;
186         }
187         while (j < curveRight.size() && ((curveRight[j].time < overlapLeft) || (curveRight[j].time > overlapRight) || (i == curveLeft.size()))) {
188             newCurve.push_back(curveRight[j]);
189             ++j;
190         }
191         VibrateCurvePoint newCurvePoint;
192         if (i < curveLeft.size() && j < curveRight.size()) {
193             if (curveLeft[i].time < curveRight[j].time) {
194                 int32_t intensity = Interpolation(curveRight[j - 1].time, curveRight[j].time,
195                     curveRight[j - 1].intensity, curveRight[j].intensity, curveLeft[i].time);
196                 int32_t frequency = Interpolation(curveRight[j - 1].time, curveRight[j].time,
197                     curveRight[j - 1].frequency, curveRight[j].frequency, curveLeft[i].time);
198                 newCurvePoint.time = curveLeft[i].time;
199                 newCurvePoint.intensity = std::max(curveLeft[i].intensity, intensity);
200                 newCurvePoint.frequency = (curveLeft[i].frequency + frequency) / 2;
201                 ++i;
202             } else if (curveLeft[i].time > curveRight[j].time) {
203                 int32_t intensity = Interpolation(curveLeft[i - 1].time, curveLeft[i].time,
204                     curveLeft[i - 1].intensity, curveLeft[i].intensity, curveRight[j].time);
205                 int32_t frequency = Interpolation(curveLeft[i - 1].time, curveLeft[i].time,
206                     curveLeft[i - 1].frequency, curveLeft[i].frequency, curveRight[j].time);
207                 newCurvePoint.time = curveRight[j].time;
208                 newCurvePoint.intensity = std::max(curveRight[j].intensity, intensity);
209                 newCurvePoint.frequency = (curveRight[j].frequency + frequency) / 2;
210                 ++j;
211             } else {
212                 newCurvePoint.time = curveRight[i].time;
213                 newCurvePoint.intensity = std::max(curveLeft[i].intensity, curveRight[j].intensity);
214                 newCurvePoint.frequency = (curveLeft[i].frequency + curveRight[j].frequency) / 2;
215                 ++i;
216                 ++j;
217             }
218             newCurve.push_back(newCurvePoint);
219         }
220     }
221     return newCurve;
222 }
223 
ProcessContinuousEvent(const VibrateEvent & event,int32_t & preStartTime,int32_t & preDuration,std::vector<CompositeEffect> & compositeEffects)224 void CustomVibrationMatcher::ProcessContinuousEvent(const VibrateEvent &event, int32_t &preStartTime,
225     int32_t &preDuration, std::vector<CompositeEffect> &compositeEffects)
226 {
227     if (event.duration < 2 * SLICE_STEP) {
228         VibrateSlice slice = {
229             .time = event.time,
230             .duration = event.duration,
231             .intensity = event.intensity,
232             .frequency = event.frequency,
233         };
234         ProcessContinuousEventSlice(slice, preStartTime, preDuration, compositeEffects);
235         return;
236     }
237     const std::vector<VibrateCurvePoint> &curve = event.points;
238     int32_t endTime = curve.back().time;
239     int32_t curTime = curve.front().time;
240     int32_t curIntensity = curve.front().intensity;
241     int32_t curFrequency = curve.front().frequency;
242     int32_t nextTime = 0;
243     int32_t i = 0;
244     while (curTime < endTime) {
245         int32_t nextIntensity = 0;
246         int32_t nextFrequency = 0;
247         if ((endTime - curTime) >= (2 * SLICE_STEP)) {
248             nextTime = curTime + SLICE_STEP;
249         } else {
250             nextTime = endTime;
251         }
252         while (curve[i].time < nextTime) {
253             ++i;
254         }
255         nextIntensity = Interpolation(curve[i - 1].time, curve[i].time, curve[i - 1].intensity, curve[i].intensity,
256             nextTime);
257         nextFrequency = Interpolation(curve[i - 1].time, curve[i].time, curve[i - 1].frequency, curve[i].frequency,
258             nextTime);
259         VibrateSlice slice = {
260             .time = curTime,
261             .duration = nextTime - curTime,
262             .intensity = (curIntensity + nextIntensity) / 2,
263             .frequency = (curFrequency + nextFrequency) / 2,
264         };
265         ProcessContinuousEventSlice(slice, preStartTime, preDuration, compositeEffects);
266         curTime = nextTime;
267         curIntensity = nextIntensity;
268         curFrequency = nextFrequency;
269     }
270 }
271 
ProcessContinuousEventSlice(const VibrateSlice & slice,int32_t & preStartTime,int32_t & preDuration,std::vector<CompositeEffect> & compositeEffects)272 void CustomVibrationMatcher::ProcessContinuousEventSlice(const VibrateSlice &slice, int32_t &preStartTime,
273     int32_t &preDuration, std::vector<CompositeEffect> &compositeEffects)
274 {
275     int32_t grade = -1;
276     if (slice.intensity == INTENSITY_MAX) {
277         grade = CONTINUOUS_GRADE_NUM - 1;
278     } else {
279         grade = round(slice.intensity / CONTINUOUS_GRADE_SCALE + ROUND_OFFSET) - 1;
280     }
281     if ((!compositeEffects.empty()) && (slice.time == preStartTime + preDuration)) {
282         PrimitiveEffect &prePrimitiveEffect = compositeEffects.back().primitiveEffect;
283         int32_t preEffectId = prePrimitiveEffect.effectId;
284         int32_t preGrade = preEffectId % CONTINUOUS_GRADE_MASK;
285         int32_t mergeDuration = preDuration + slice.duration;
286         if (preEffectId > EFFECT_ID_BOUNDARY && preGrade == grade && mergeDuration < DURATION_MAX) {
287             prePrimitiveEffect.effectId = mergeDuration * CONTINUOUS_GRADE_MASK + grade;
288             preDuration = mergeDuration;
289             return;
290         }
291     }
292     PrimitiveEffect primitiveEffect;
293     primitiveEffect.delay = slice.time - preStartTime;
294     primitiveEffect.effectId = slice.duration * CONTINUOUS_GRADE_MASK + grade;
295     CompositeEffect compositeEffect;
296     compositeEffect.primitiveEffect = primitiveEffect;
297     compositeEffects.push_back(compositeEffect);
298     preStartTime = slice.time;
299     preDuration = slice.duration;
300 }
301 
ProcessTransientEvent(const VibrateEvent & event,int32_t & preStartTime,int32_t & preDuration,std::vector<CompositeEffect> & compositeEffects)302 void CustomVibrationMatcher::ProcessTransientEvent(const VibrateEvent &event, int32_t &preStartTime,
303     int32_t &preDuration, std::vector<CompositeEffect> &compositeEffects)
304 {
305     int32_t matchId = 0;
306     float minWeightSum = WEIGHT_SUM_INIT;
307     for (const auto &transientInfo : TRANSIENT_VIBRATION_INFOS) {
308         int32_t id = transientInfo.first;
309         const std::vector<int32_t> &info = transientInfo.second;
310         float frequencyDistance = std::abs(event.frequency - info[1]);
311         for (int32_t j = 0; j < TRANSIENT_GRADE_NUM; ++j) {
312             float intensityDistance = std::abs(event.intensity - info[0] * (1 - j * TRANSIENT_GRADE_GAIN));
313             float weightSum = INTENSITY_WEIGHT * intensityDistance + FREQUENCY_WEIGHT * frequencyDistance;
314             if (weightSum < minWeightSum) {
315                 minWeightSum = weightSum;
316                 matchId = id + j;
317             }
318         }
319     }
320     PrimitiveEffect primitiveEffect;
321     primitiveEffect.delay = event.time - preStartTime;
322     primitiveEffect.effectId = matchId;
323     CompositeEffect compositeEffect;
324     compositeEffect.primitiveEffect = primitiveEffect;
325     compositeEffects.push_back(compositeEffect);
326     preStartTime = event.time;
327     preDuration = event.duration;
328 }
329 
Interpolation(int32_t x1,int32_t x2,int32_t y1,int32_t y2,int32_t x)330 int32_t CustomVibrationMatcher::Interpolation(int32_t x1, int32_t x2, int32_t y1, int32_t y2, int32_t x)
331 {
332     if (x1 == x2) {
333         return y1;
334     }
335     float delta_y = static_cast<float>(y2 - y1);
336     float delta_x = static_cast<float>(x2 - x1);
337     return y1 + delta_y / delta_x * (x - x1);
338 }
339 }  // namespace Sensors
340 }  // namespace OHOS
341