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