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

Commit 42b01116 authored by Andy Hung's avatar Andy Hung
Browse files

Fix multichannel integer resampling and add tests

Change-Id: I384bf8317d4f03616bf9f2b458a8700965d5cf56
parent 8bce8414
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]);
    }
}