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.os.VibratorInfo; 20 import android.os.vibrator.RampSegment; 21 import android.os.vibrator.VibrationEffectSegment; 22 import android.util.MathUtils; 23 import android.util.Range; 24 25 import java.util.List; 26 27 /** 28 * Adapter that clips frequency values to the supported range specified by 29 * {@link VibratorInfo.FrequencyProfile}, then clips amplitude values to the max supported one at 30 * each frequency. 31 * 32 * <p>The {@link VibratorInfo.FrequencyProfile} is only applicable to PWLE compositions. This 33 * adapter is only applied to {@link RampSegment} and all other segments will remain unchanged. 34 */ 35 final class ClippingAmplitudeAndFrequencyAdapter implements VibrationSegmentsAdapter { 36 37 @Override adaptToVibrator(VibratorInfo info, List<VibrationEffectSegment> segments, int repeatIndex)38 public int adaptToVibrator(VibratorInfo info, List<VibrationEffectSegment> segments, 39 int repeatIndex) { 40 int segmentCount = segments.size(); 41 for (int i = 0; i < segmentCount; i++) { 42 VibrationEffectSegment segment = segments.get(i); 43 if (segment instanceof RampSegment) { 44 segments.set(i, adaptToVibrator(info, (RampSegment) segment)); 45 } 46 } 47 return repeatIndex; 48 } 49 adaptToVibrator(VibratorInfo info, RampSegment segment)50 private RampSegment adaptToVibrator(VibratorInfo info, RampSegment segment) { 51 float clampedStartFrequency = clampFrequency(info, segment.getStartFrequencyHz()); 52 float clampedEndFrequency = clampFrequency(info, segment.getEndFrequencyHz()); 53 return new RampSegment( 54 clampAmplitude(info, clampedStartFrequency, segment.getStartAmplitude()), 55 clampAmplitude(info, clampedEndFrequency, segment.getEndAmplitude()), 56 clampedStartFrequency, 57 clampedEndFrequency, 58 (int) segment.getDuration()); 59 } 60 clampFrequency(VibratorInfo info, float frequencyHz)61 private float clampFrequency(VibratorInfo info, float frequencyHz) { 62 Range<Float> frequencyRangeHz = info.getFrequencyProfile().getFrequencyRangeHz(); 63 if (frequencyHz == 0 || frequencyRangeHz == null) { 64 return Float.isNaN(info.getResonantFrequencyHz()) ? 0 : info.getResonantFrequencyHz(); 65 } 66 return frequencyRangeHz.clamp(frequencyHz); 67 } 68 clampAmplitude(VibratorInfo info, float frequencyHz, float amplitude)69 private float clampAmplitude(VibratorInfo info, float frequencyHz, float amplitude) { 70 VibratorInfo.FrequencyProfile mapping = info.getFrequencyProfile(); 71 if (mapping.isEmpty()) { 72 // No frequency mapping was specified so leave amplitude unchanged. 73 // The frequency will be clamped to the device's resonant frequency. 74 return amplitude; 75 } 76 return MathUtils.min(amplitude, mapping.getMaxAmplitude(frequencyHz)); 77 } 78 } 79