blob: c943bb283fe7e2e6783aabf5b78777336296c095 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.vibrator;
import android.hardware.vibrator.IVibrator;
import android.os.VibratorInfo;
import android.os.vibrator.RampSegment;
import android.os.vibrator.StepSegment;
import android.os.vibrator.VibrationEffectSegment;
import android.util.MathUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Adapter that converts ramp segments that to a sequence of fixed step segments.
*
* <p>This leaves the list unchanged if the device has compose PWLE capability.
*/
final class RampToStepAdapter implements VibrationEffectAdapters.SegmentsAdapter<VibratorInfo> {
private final int mStepDuration;
RampToStepAdapter(int stepDuration) {
mStepDuration = stepDuration;
}
@Override
public int apply(List<VibrationEffectSegment> segments, int repeatIndex,
VibratorInfo info) {
if (info.hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
// The vibrator have PWLE capability, so keep the segments unchanged.
return repeatIndex;
}
int segmentCount = segments.size();
for (int i = 0; i < segmentCount; i++) {
VibrationEffectSegment segment = segments.get(i);
if (!(segment instanceof RampSegment)) {
continue;
}
List<StepSegment> steps = apply(info, (RampSegment) segment);
segments.remove(i);
segments.addAll(i, steps);
int addedSegments = steps.size() - 1;
if (repeatIndex > i) {
repeatIndex += addedSegments;
}
i += addedSegments;
segmentCount += addedSegments;
}
return repeatIndex;
}
private List<StepSegment> apply(VibratorInfo info, RampSegment ramp) {
if (Float.compare(ramp.getStartAmplitude(), ramp.getEndAmplitude()) == 0) {
// Amplitude is the same, so return a single step to simulate this ramp.
return Arrays.asList(
new StepSegment(ramp.getStartAmplitude(),
fillEmptyFrequency(info, ramp.getStartFrequencyHz()),
(int) ramp.getDuration()));
}
List<StepSegment> steps = new ArrayList<>();
int stepCount = (int) (ramp.getDuration() + mStepDuration - 1) / mStepDuration;
for (int i = 0; i < stepCount - 1; i++) {
float pos = (float) i / stepCount;
// Fill zero frequency values with the device resonant frequency before interpolating.
float startFrequencyHz = fillEmptyFrequency(info, ramp.getStartFrequencyHz());
float endFrequencyHz = fillEmptyFrequency(info, ramp.getEndFrequencyHz());
steps.add(new StepSegment(
MathUtils.lerp(ramp.getStartAmplitude(), ramp.getEndAmplitude(), pos),
MathUtils.lerp(startFrequencyHz, endFrequencyHz, pos),
mStepDuration));
}
int duration = (int) ramp.getDuration() - mStepDuration * (stepCount - 1);
float endFrequencyHz = fillEmptyFrequency(info, ramp.getEndFrequencyHz());
steps.add(new StepSegment(ramp.getEndAmplitude(), endFrequencyHz, duration));
return steps;
}
private static float fillEmptyFrequency(VibratorInfo info, float frequencyHz) {
return frequencyHz == 0 ? info.getResonantFrequencyHz() : frequencyHz;
}
}