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

Commit 17d0842c authored by Andy Hung's avatar Andy Hung Committed by Android (Google) Code Review
Browse files

Merge "Fix multichannel integer resampling and add tests" into lmp-dev

parents 4730fa07 42b01116
Loading
Loading
Loading
Loading
+10 −26
Original line number Diff line number Diff line
@@ -109,40 +109,25 @@ public:
    }
};

/*
 * Helper template functions for interpolating filter coefficients.
 */

template<typename TC, typename T>
void adjustLerp(T& lerpP __unused)
{
}

template<int32_t, typename T>
void adjustLerp(T& lerpP)
{
    lerpP >>= 16;   // lerpP is 32bit for NEON int32_t, but always 16 bit for non-NEON path
}

template<typename TC, typename TINTERP>
static inline
inline
TC interpolate(TC coef_0, TC coef_1, TINTERP lerp)
{
    return lerp * (coef_1 - coef_0) + coef_0;
}

template<int16_t, uint32_t>
static inline
int16_t interpolate(int16_t coef_0, int16_t coef_1, uint32_t lerp)
{
    return (static_cast<int16_t>(lerp) * ((coef_1-coef_0)<<1)>>16) + coef_0;
template<>
inline
int16_t interpolate<int16_t, uint32_t>(int16_t coef_0, int16_t coef_1, uint32_t lerp)
{   // in some CPU architectures 16b x 16b multiplies are faster.
    return (static_cast<int16_t>(lerp) * static_cast<int16_t>(coef_1 - coef_0) >> 15) + coef_0;
}

template<int32_t, uint32_t>
static inline
int32_t interpolate(int32_t coef_0, int32_t coef_1, uint32_t lerp)
template<>
inline
int32_t interpolate<int32_t, uint32_t>(int32_t coef_0, int32_t coef_1, uint32_t lerp)
{
    return mulAdd(static_cast<int16_t>(lerp), (coef_1-coef_0)<<1, coef_0);
    return (lerp * static_cast<int64_t>(coef_1 - coef_0) >> 31) + coef_0;
}

/* class scope for passing in functions into templates */
@@ -283,7 +268,6 @@ void Process(TO* const out,
        TINTERP lerpP,
        const TO* const volumeLR)
{
    adjustLerp<TC, TINTERP>(lerpP); // coefficient type adjustment for interpolations
    ProcessBase<CHANNELS, STRIDE, InterpCompute>(out, count, coefsP, coefsN, sP, sN, lerpP, volumeLR);
}

+101 −9
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include <math.h>
#include <vector>
#include <utility>
#include <iostream>
#include <cutils/log.h>
#include <gtest/gtest.h>
#include <media/AudioBufferProvider.h>
@@ -153,6 +154,9 @@ double signalEnergy(T *start, T *end, unsigned stride)
    return accum / count;
}

// TI = resampler input type, int16_t or float
// TO = resampler output type, int32_t or float
template <typename TI, typename TO>
void testStopbandDownconversion(size_t channels,
        unsigned inputFreq, unsigned outputFreq,
        unsigned passband, unsigned stopband,
@@ -161,20 +165,21 @@ void testStopbandDownconversion(size_t channels,
    // create the provider
    std::vector<int> inputIncr;
    SignalProvider provider;
    provider.setChirp<int16_t>(channels,
    provider.setChirp<TI>(channels,
            0., inputFreq/2., inputFreq, inputFreq/2000.);
    provider.setIncr(inputIncr);

    // calculate the output size
    size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
    size_t outputFrameSize = channels * sizeof(int32_t);
    size_t outputFrameSize = channels * sizeof(TO);
    size_t outputSize = outputFrameSize * outputFrames;
    outputSize &= ~7;

    // create the resampler
    android::AudioResampler* resampler;

    resampler = android::AudioResampler::create(AUDIO_FORMAT_PCM_16_BIT,
    resampler = android::AudioResampler::create(
            is_same<TI, int16_t>::value ? AUDIO_FORMAT_PCM_16_BIT : AUDIO_FORMAT_PCM_FLOAT,
            channels, outputFreq, quality);
    resampler->setSampleRate(inputFreq);
    resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT,
@@ -186,7 +191,7 @@ void testStopbandDownconversion(size_t channels,
    void* reference = malloc(outputSize);
    resample(channels, reference, outputFrames, refIncr, &provider, resampler);

    int32_t *out = reinterpret_cast<int32_t *>(reference);
    TO *out = reinterpret_cast<TO *>(reference);

    // check signal energy in passband
    const unsigned passbandFrame = passband * outputFreq / 1000.;
@@ -206,10 +211,10 @@ void testStopbandDownconversion(size_t channels,
                provider.getNumFrames(), outputFrames,
                passbandFrame, stopbandFrame, stopbandEnergy, passbandEnergy, dbAtten);
        for (size_t i = 0; i < 10; ++i) {
            printf("%d\n", out[i+passbandFrame*channels]);
            std::cout << out[i+passbandFrame*channels] << std::endl;
        }
        for (size_t i = 0; i < 10; ++i) {
            printf("%d\n", out[i+stopbandFrame*channels]);
            std::cout << out[i+stopbandFrame*channels] << std::endl;
        }
#endif
    }
@@ -292,7 +297,35 @@ TEST(audioflinger_resampler, bufferincrement_interpolatedphase_multi_float) {
 * are properly suppressed.  It uses downsampling because the stopband can be
 * clearly isolated by input frequencies exceeding the output sample rate (nyquist).
 */
TEST(audioflinger_resampler, stopbandresponse) {
TEST(audioflinger_resampler, stopbandresponse_integer) {
    // not all of these may work (old resamplers fail on downsampling)
    static const enum android::AudioResampler::src_quality kQualityArray[] = {
            //android::AudioResampler::LOW_QUALITY,
            //android::AudioResampler::MED_QUALITY,
            //android::AudioResampler::HIGH_QUALITY,
            //android::AudioResampler::VERY_HIGH_QUALITY,
            android::AudioResampler::DYN_LOW_QUALITY,
            android::AudioResampler::DYN_MED_QUALITY,
            android::AudioResampler::DYN_HIGH_QUALITY,
    };

    // in this test we assume a maximum transition band between 12kHz and 20kHz.
    // there must be at least 60dB relative attenuation between stopband and passband.
    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
        testStopbandDownconversion<int16_t, int32_t>(
                2, 48000, 32000, 12000, 20000, kQualityArray[i]);
    }

    // in this test we assume a maximum transition band between 7kHz and 15kHz.
    // there must be at least 60dB relative attenuation between stopband and passband.
    // (the weird ratio triggers interpolative resampling)
    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
        testStopbandDownconversion<int16_t, int32_t>(
                2, 48000, 22101, 7000, 15000, kQualityArray[i]);
    }
}

TEST(audioflinger_resampler, stopbandresponse_integer_multichannel) {
    // not all of these may work (old resamplers fail on downsampling)
    static const enum android::AudioResampler::src_quality kQualityArray[] = {
            //android::AudioResampler::LOW_QUALITY,
@@ -307,13 +340,72 @@ TEST(audioflinger_resampler, stopbandresponse) {
    // in this test we assume a maximum transition band between 12kHz and 20kHz.
    // there must be at least 60dB relative attenuation between stopband and passband.
    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
        testStopbandDownconversion(2, 48000, 32000, 12000, 20000, kQualityArray[i]);
        testStopbandDownconversion<int16_t, int32_t>(
                8, 48000, 32000, 12000, 20000, kQualityArray[i]);
    }

    // in this test we assume a maximum transition band between 7kHz and 15kHz.
    // there must be at least 60dB relative attenuation between stopband and passband.
    // (the weird ratio triggers interpolative resampling)
    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
        testStopbandDownconversion(2, 48000, 22101, 7000, 15000, kQualityArray[i]);
        testStopbandDownconversion<int16_t, int32_t>(
                8, 48000, 22101, 7000, 15000, kQualityArray[i]);
    }
}

TEST(audioflinger_resampler, stopbandresponse_float) {
    // not all of these may work (old resamplers fail on downsampling)
    static const enum android::AudioResampler::src_quality kQualityArray[] = {
            //android::AudioResampler::LOW_QUALITY,
            //android::AudioResampler::MED_QUALITY,
            //android::AudioResampler::HIGH_QUALITY,
            //android::AudioResampler::VERY_HIGH_QUALITY,
            android::AudioResampler::DYN_LOW_QUALITY,
            android::AudioResampler::DYN_MED_QUALITY,
            android::AudioResampler::DYN_HIGH_QUALITY,
    };

    // in this test we assume a maximum transition band between 12kHz and 20kHz.
    // there must be at least 60dB relative attenuation between stopband and passband.
    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
        testStopbandDownconversion<float, float>(
                2, 48000, 32000, 12000, 20000, kQualityArray[i]);
    }

    // in this test we assume a maximum transition band between 7kHz and 15kHz.
    // there must be at least 60dB relative attenuation between stopband and passband.
    // (the weird ratio triggers interpolative resampling)
    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
        testStopbandDownconversion<float, float>(
                2, 48000, 22101, 7000, 15000, kQualityArray[i]);
    }
}

TEST(audioflinger_resampler, stopbandresponse_float_multichannel) {
    // not all of these may work (old resamplers fail on downsampling)
    static const enum android::AudioResampler::src_quality kQualityArray[] = {
            //android::AudioResampler::LOW_QUALITY,
            //android::AudioResampler::MED_QUALITY,
            //android::AudioResampler::HIGH_QUALITY,
            //android::AudioResampler::VERY_HIGH_QUALITY,
            android::AudioResampler::DYN_LOW_QUALITY,
            android::AudioResampler::DYN_MED_QUALITY,
            android::AudioResampler::DYN_HIGH_QUALITY,
    };

    // in this test we assume a maximum transition band between 12kHz and 20kHz.
    // there must be at least 60dB relative attenuation between stopband and passband.
    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
        testStopbandDownconversion<float, float>(
                8, 48000, 32000, 12000, 20000, kQualityArray[i]);
    }

    // in this test we assume a maximum transition band between 7kHz and 15kHz.
    // there must be at least 60dB relative attenuation between stopband and passband.
    // (the weird ratio triggers interpolative resampling)
    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
        testStopbandDownconversion<float, float>(
                8, 48000, 22101, 7000, 15000, kQualityArray[i]);
    }
}