1 /* 2 * Copyright (C) 2023 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 17 package com.android.server.vibrator; 18 19 import android.hardware.vibrator.IVibrator; 20 import android.os.VibratorInfo; 21 import android.os.vibrator.RampSegment; 22 import android.os.vibrator.VibrationEffectSegment; 23 import android.util.MathUtils; 24 25 import java.util.ArrayList; 26 import java.util.List; 27 28 /** 29 * Adapter that splits segments with longer duration than the device capabilities. 30 * 31 * <p>This transformation replaces large {@link RampSegment} entries by a sequence of smaller 32 * ramp segments that starts and ends at the same amplitudes/frequencies, interpolating the 33 * intermediate values. 34 * 35 * <p>The segments will not be changed if the device doesn't have 36 * {@link IVibrator#CAP_COMPOSE_PWLE_EFFECTS}. 37 */ 38 final class SplitSegmentsAdapter implements VibrationSegmentsAdapter { 39 40 @Override adaptToVibrator(VibratorInfo info, List<VibrationEffectSegment> segments, int repeatIndex)41 public int adaptToVibrator(VibratorInfo info, List<VibrationEffectSegment> segments, 42 int repeatIndex) { 43 if (!info.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) { 44 // The vibrator does not have PWLE capability, so keep the segments unchanged. 45 return repeatIndex; 46 } 47 int maxRampDuration = info.getPwlePrimitiveDurationMax(); 48 if (maxRampDuration <= 0) { 49 // No limit set to PWLE primitive duration. 50 return repeatIndex; 51 } 52 53 int segmentCount = segments.size(); 54 for (int i = 0; i < segmentCount; i++) { 55 if (!(segments.get(i) instanceof RampSegment)) { 56 continue; 57 } 58 RampSegment ramp = (RampSegment) segments.get(i); 59 int splits = ((int) ramp.getDuration() + maxRampDuration - 1) / maxRampDuration; 60 if (splits <= 1) { 61 continue; 62 } 63 segments.remove(i); 64 segments.addAll(i, splitRampSegment(info, ramp, splits)); 65 int addedSegments = splits - 1; 66 if (repeatIndex > i) { 67 repeatIndex += addedSegments; 68 } 69 i += addedSegments; 70 segmentCount += addedSegments; 71 } 72 73 return repeatIndex; 74 } 75 splitRampSegment(VibratorInfo info, RampSegment ramp, int splits)76 private static List<RampSegment> splitRampSegment(VibratorInfo info, RampSegment ramp, 77 int splits) { 78 List<RampSegment> ramps = new ArrayList<>(splits); 79 // Fill zero frequency values with the device resonant frequency before interpolating. 80 float startFrequencyHz = fillEmptyFrequency(info, ramp.getStartFrequencyHz()); 81 float endFrequencyHz = fillEmptyFrequency(info, ramp.getEndFrequencyHz()); 82 long splitDuration = ramp.getDuration() / splits; 83 float previousAmplitude = ramp.getStartAmplitude(); 84 float previousFrequencyHz = startFrequencyHz; 85 long accumulatedDuration = 0; 86 87 for (int i = 1; i < splits; i++) { 88 accumulatedDuration += splitDuration; 89 float durationRatio = (float) accumulatedDuration / ramp.getDuration(); 90 float interpolatedFrequency = 91 MathUtils.lerp(startFrequencyHz, endFrequencyHz, durationRatio); 92 float interpolatedAmplitude = 93 MathUtils.lerp(ramp.getStartAmplitude(), ramp.getEndAmplitude(), durationRatio); 94 RampSegment rampSplit = new RampSegment( 95 previousAmplitude, interpolatedAmplitude, 96 previousFrequencyHz, interpolatedFrequency, 97 (int) splitDuration); 98 ramps.add(rampSplit); 99 previousAmplitude = rampSplit.getEndAmplitude(); 100 previousFrequencyHz = rampSplit.getEndFrequencyHz(); 101 } 102 103 ramps.add(new RampSegment(previousAmplitude, ramp.getEndAmplitude(), previousFrequencyHz, 104 endFrequencyHz, (int) (ramp.getDuration() - accumulatedDuration))); 105 106 return ramps; 107 } 108 fillEmptyFrequency(VibratorInfo info, float frequencyHz)109 private static float fillEmptyFrequency(VibratorInfo info, float frequencyHz) { 110 if (Float.isNaN(info.getResonantFrequencyHz())) { 111 return frequencyHz; 112 } 113 return frequencyHz == 0 ? info.getResonantFrequencyHz() : frequencyHz; 114 } 115 } 116