Merge "Use linear interpolator in VibratorInfo bandwidth curve"

This commit is contained in:
Lais Andrade
2022-01-24 20:09:04 +00:00
committed by Android (Google) Code Review
2 changed files with 59 additions and 17 deletions

View File

@@ -573,21 +573,29 @@ public class VibratorInfo implements Parcelable {
* supported frequency range is empty.
*/
public float getMaxAmplitude(float frequencyHz) {
if (isEmpty() || Float.isNaN(frequencyHz)) {
if (isEmpty() || Float.isNaN(frequencyHz) || !mFrequencyRangeHz.contains(frequencyHz)) {
// Unsupported frequency requested, vibrator cannot play at this frequency.
return 0;
}
float position = (frequencyHz - mMinFrequencyHz) / mFrequencyResolutionHz;
int floorIndex = (int) Math.floor(position);
int ceilIndex = (int) Math.ceil(position);
if (floorIndex < 0 || floorIndex >= mMaxAmplitudes.length) {
return 0;
}
if (floorIndex != ceilIndex && ceilIndex < mMaxAmplitudes.length) {
// Value in between two mapped frequency values, use the lowest supported one.
return MathUtils.min(mMaxAmplitudes[floorIndex], mMaxAmplitudes[ceilIndex]);
}
return mMaxAmplitudes[floorIndex];
// Subtract minFrequencyHz to simplify offset calculations.
float mappingFreq = frequencyHz - mMinFrequencyHz;
// Find the bucket to interpolate within.
// Any calculated index should be safe, except exactly equal to max amplitude can be
// one step too high, so constrain it to guarantee safety.
int startIdx = MathUtils.constrain(
/* amount= */ (int) Math.floor(mappingFreq / mFrequencyResolutionHz),
/* low= */ 0, /* high= */ mMaxAmplitudes.length - 1);
int nextIdx = MathUtils.constrain(
/* amount= */ startIdx + 1,
/* low= */ 0, /* high= */ mMaxAmplitudes.length - 1);
// Linearly interpolate the amplitudes based on the frequency range of the bucket.
return MathUtils.constrainedMap(
mMaxAmplitudes[startIdx], mMaxAmplitudes[nextIdx],
startIdx * mFrequencyResolutionHz, nextIdx * mFrequencyResolutionHz,
mappingFreq);
}
/** Returns the raw list of maximum relative output accelerations from the vibrator. */

View File

@@ -19,6 +19,7 @@ package android.os;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import android.hardware.vibrator.Braking;
@@ -144,6 +145,7 @@ public class VibratorInfoTest {
assertTrue(
new VibratorInfo.Builder(TEST_VIBRATOR_ID).build().getFrequencyProfile().isEmpty());
}
@Test
public void testFrequencyProfile_invalidValuesCreatesEmptyProfile() {
// Invalid, contains NaN values or empty array.
@@ -172,6 +174,31 @@ public class VibratorInfoTest {
150, 50, /* frequencyResolutionHz= */ 10, TEST_AMPLITUDE_MAP).isEmpty());
}
@Test
public void testGetFrequencyRangeHz_emptyProfileReturnsNull() {
assertNull(new VibratorInfo.FrequencyProfile(
Float.NaN, 50, 25, TEST_AMPLITUDE_MAP).getFrequencyRangeHz());
assertNull(new VibratorInfo.FrequencyProfile(
150, Float.NaN, 25, TEST_AMPLITUDE_MAP).getFrequencyRangeHz());
assertNull(new VibratorInfo.FrequencyProfile(
150, 50, Float.NaN, TEST_AMPLITUDE_MAP).getFrequencyRangeHz());
assertNull(new VibratorInfo.FrequencyProfile(150, 50, 25, null).getFrequencyRangeHz());
}
@Test
public void testGetFrequencyRangeHz_validProfileReturnsMappedValues() {
VibratorInfo.FrequencyProfile profile = new VibratorInfo.FrequencyProfile(
/* resonantFrequencyHz= */ 150,
/* minFrequencyHz= */ 50,
/* frequencyResolutionHz= */ 25,
/* maxAmplitudes= */ new float[]{
/* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f,
/* 200Hz= */ 0.8f});
assertEquals(50f, profile.getFrequencyRangeHz().getLower(), TEST_TOLERANCE);
assertEquals(200f, profile.getFrequencyRangeHz().getUpper(), TEST_TOLERANCE);
}
@Test
public void testGetMaxAmplitude_emptyProfileReturnsAlwaysZero() {
VibratorInfo.FrequencyProfile profile = EMPTY_FREQUENCY_PROFILE;
@@ -191,7 +218,7 @@ public class VibratorInfoTest {
}
@Test
public void testGetMaxAmplitude_validprofileReturnsMappedValues() {
public void testGetMaxAmplitude_validProfileReturnsMappedValues() {
VibratorInfo.FrequencyProfile profile = new VibratorInfo.FrequencyProfile(
/* resonantFrequencyHz= */ 150,
/* minFrequencyHz= */ 50,
@@ -200,16 +227,23 @@ public class VibratorInfoTest {
/* 50Hz= */ 0.1f, 0.2f, 0.4f, 0.8f, /* 150Hz= */ 1f, 0.9f,
/* 200Hz= */ 0.8f});
// Values in the max amplitudes array should return exact measurement.
assertEquals(1f, profile.getMaxAmplitude(150f), TEST_TOLERANCE);
assertEquals(0.9f, profile.getMaxAmplitude(175f), TEST_TOLERANCE);
assertEquals(0.8f, profile.getMaxAmplitude(125f), TEST_TOLERANCE);
// Min and max frequencies should return exact measurement from array.
assertEquals(0.8f, profile.getMaxAmplitude(200f), TEST_TOLERANCE);
assertEquals(0.1f, profile.getMaxAmplitude(50f), TEST_TOLERANCE);
// 145Hz maps to the max amplitude for 125Hz, which is lower.
assertEquals(0.8f, profile.getMaxAmplitude(145f), TEST_TOLERANCE); // 145Hz
// 185Hz maps to the max amplitude for 200Hz, which is lower.
assertEquals(0.8f, profile.getMaxAmplitude(185f), TEST_TOLERANCE); // 185Hz
// Values outside [50Hz, 200Hz] just return 0.
assertEquals(0f, profile.getMaxAmplitude(49f), TEST_TOLERANCE);
assertEquals(0f, profile.getMaxAmplitude(201f), TEST_TOLERANCE);
// 145Hz maps to linear value between 125Hz and 150Hz max amplitudes 0.8 and 1.
assertEquals(0.96f, profile.getMaxAmplitude(145f), TEST_TOLERANCE);
// 185Hz maps to linear value between 175Hz and 200Hz max amplitudes 0.9 and 0.8.
assertEquals(0.86f, profile.getMaxAmplitude(185f), TEST_TOLERANCE);
}
@Test