1 /* 2 * Copyright (C) 2021 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.StepSegment; 23 import android.os.vibrator.VibrationEffectSegment; 24 25 import java.util.ArrayList; 26 import java.util.List; 27 28 /** 29 * Adapter that converts step segments that should be handled as PWLEs to ramp segments. 30 * 31 * <p>Each replaced {@link StepSegment} will be represented by a {@link RampSegment} with same 32 * start and end amplitudes/frequencies, which can then be converted to PWLE compositions. This 33 * adapter leaves the segments unchanged if the device doesn't have the PWLE composition capability. 34 */ 35 final class StepToRampAdapter implements VibrationEffectAdapters.SegmentsAdapter<VibratorInfo> { 36 37 @Override apply(List<VibrationEffectSegment> segments, int repeatIndex, VibratorInfo info)38 public int apply(List<VibrationEffectSegment> segments, int repeatIndex, 39 VibratorInfo info) { 40 if (!info.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) { 41 // The vibrator does not have PWLE capability, so keep the segments unchanged. 42 return repeatIndex; 43 } 44 convertStepsToRamps(segments); 45 repeatIndex = splitLongRampSegments(info, segments, repeatIndex); 46 return repeatIndex; 47 } 48 convertStepsToRamps(List<VibrationEffectSegment> segments)49 private void convertStepsToRamps(List<VibrationEffectSegment> segments) { 50 int segmentCount = segments.size(); 51 // Convert steps that require frequency control to ramps. 52 for (int i = 0; i < segmentCount; i++) { 53 VibrationEffectSegment segment = segments.get(i); 54 if (isStep(segment) && ((StepSegment) segment).getFrequency() != 0) { 55 segments.set(i, convertStepToRamp((StepSegment) segment)); 56 } 57 } 58 // Convert steps that are next to ramps to also become ramps, so they can be composed 59 // together in the same PWLE waveform. 60 for (int i = 0; i < segmentCount; i++) { 61 if (segments.get(i) instanceof RampSegment) { 62 for (int j = i - 1; j >= 0 && isStep(segments.get(j)); j--) { 63 segments.set(j, convertStepToRamp((StepSegment) segments.get(j))); 64 } 65 for (int j = i + 1; j < segmentCount && isStep(segments.get(j)); j++) { 66 segments.set(j, convertStepToRamp((StepSegment) segments.get(j))); 67 } 68 } 69 } 70 } 71 72 /** 73 * Split {@link RampSegment} entries that have duration longer than {@link 74 * VibratorInfo#getPwlePrimitiveDurationMax()}. 75 */ splitLongRampSegments(VibratorInfo info, List<VibrationEffectSegment> segments, int repeatIndex)76 private int splitLongRampSegments(VibratorInfo info, List<VibrationEffectSegment> segments, 77 int repeatIndex) { 78 int maxDuration = info.getPwlePrimitiveDurationMax(); 79 if (maxDuration <= 0) { 80 // No limit set to PWLE primitive duration. 81 return repeatIndex; 82 } 83 84 int segmentCount = segments.size(); 85 for (int i = 0; i < segmentCount; i++) { 86 if (!(segments.get(i) instanceof RampSegment)) { 87 continue; 88 } 89 RampSegment ramp = (RampSegment) segments.get(i); 90 int splits = ((int) ramp.getDuration() + maxDuration - 1) / maxDuration; 91 if (splits <= 1) { 92 continue; 93 } 94 segments.remove(i); 95 segments.addAll(i, splitRampSegment(ramp, splits)); 96 int addedSegments = splits - 1; 97 if (repeatIndex > i) { 98 repeatIndex += addedSegments; 99 } 100 i += addedSegments; 101 segmentCount += addedSegments; 102 } 103 104 return repeatIndex; 105 } 106 convertStepToRamp(StepSegment segment)107 private static RampSegment convertStepToRamp(StepSegment segment) { 108 return new RampSegment(segment.getAmplitude(), segment.getAmplitude(), 109 segment.getFrequency(), segment.getFrequency(), (int) segment.getDuration()); 110 } 111 splitRampSegment(RampSegment ramp, int splits)112 private static List<RampSegment> splitRampSegment(RampSegment ramp, int splits) { 113 List<RampSegment> ramps = new ArrayList<>(splits); 114 long splitDuration = ramp.getDuration() / splits; 115 float previousAmplitude = ramp.getStartAmplitude(); 116 float previousFrequency = ramp.getStartFrequency(); 117 long accumulatedDuration = 0; 118 119 for (int i = 1; i < splits; i++) { 120 accumulatedDuration += splitDuration; 121 RampSegment rampSplit = new RampSegment( 122 previousAmplitude, interpolateAmplitude(ramp, accumulatedDuration), 123 previousFrequency, interpolateFrequency(ramp, accumulatedDuration), 124 (int) splitDuration); 125 ramps.add(rampSplit); 126 previousAmplitude = rampSplit.getEndAmplitude(); 127 previousFrequency = rampSplit.getEndFrequency(); 128 } 129 130 ramps.add(new RampSegment(previousAmplitude, ramp.getEndAmplitude(), previousFrequency, 131 ramp.getEndFrequency(), (int) (ramp.getDuration() - accumulatedDuration))); 132 133 return ramps; 134 } 135 isStep(VibrationEffectSegment segment)136 private static boolean isStep(VibrationEffectSegment segment) { 137 return segment instanceof StepSegment; 138 } 139 interpolateAmplitude(RampSegment ramp, long duration)140 private static float interpolateAmplitude(RampSegment ramp, long duration) { 141 return interpolate(ramp.getStartAmplitude(), ramp.getEndAmplitude(), duration, 142 ramp.getDuration()); 143 } 144 interpolateFrequency(RampSegment ramp, long duration)145 private static float interpolateFrequency(RampSegment ramp, long duration) { 146 return interpolate(ramp.getStartFrequency(), ramp.getEndFrequency(), duration, 147 ramp.getDuration()); 148 } 149 interpolate(float start, float end, long duration, long totalDuration)150 private static float interpolate(float start, float end, long duration, long totalDuration) { 151 float position = (float) duration / totalDuration; 152 return start + position * (end - start); 153 } 154 } 155