Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 136ca7e3 authored by Lais Andrade's avatar Lais Andrade
Browse files

Use linear interpolator in VibratorInfo bandwidth curve

Change VibratorInfo.FrequencyProfile to use a linear interpolation for
the max amplitude supported by frequency values between two measurement
entries in the HAL bandwidth returned.

Bug: 203785430
Test: VibratorInfoTest
Change-Id: I56e998df40698be87a713ca9013380c0e938e165
parent cb3718bf
Loading
Loading
Loading
Loading
+20 −12
Original line number Diff line number Diff line
@@ -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. */
+39 −5
Original line number Diff line number Diff line
@@ -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