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

Commit 075abae2 authored by Andy Hung's avatar Andy Hung
Browse files

Add and enable multichannel for audio resampler



Change-Id: I2b86fb73d70abc4c456f7567270a888086b301d4
Signed-off-by: default avatarAndy Hung <hunga@google.com>
parent 68ffa200
Loading
Loading
Loading
Loading
+6 −5
Original line number Diff line number Diff line
@@ -259,13 +259,14 @@ AudioResampler::AudioResampler(int bitDepth, int inChannelCount,
            mPhaseFraction(0), mLocalTimeFreq(0),
            mPTS(AudioBufferProvider::kInvalidPTS), mQuality(quality) {
    // sanity check on format
    if ((bitDepth != 16) ||(inChannelCount < 1) || (inChannelCount > 2)) {
        ALOGE("Unsupported sample format, %d bits, %d channels", bitDepth,
                inChannelCount);
        // ALOG_ASSERT(0);
    if ((bitDepth != 16 && (quality < DYN_LOW_QUALITY || bitDepth != 32))
            || inChannelCount < 1
            || inChannelCount > (quality < DYN_LOW_QUALITY ? 2 : 8)) {
        LOG_ALWAYS_FATAL("Unsupported sample format %d quality %d bits, %d channels",
                quality, bitDepth, inChannelCount);
    }
    if (sampleRate <= 0) {
        ALOGE("Unsupported sample rate %d Hz", sampleRate);
        LOG_ALWAYS_FATAL("Unsupported sample rate %d Hz", sampleRate);
    }

    // initialize common members
+72 −34
Original line number Diff line number Diff line
@@ -38,11 +38,6 @@

namespace android {

// generate a unique resample type compile-time constant (constexpr)
#define RESAMPLETYPE(CHANNELS, LOCKED, STRIDE) \
    ((((CHANNELS)-1)&1) | !!(LOCKED)<<1 \
    | ((STRIDE)==8 ? 1 : (STRIDE)==16 ? 2 : 0)<<2)

/*
 * InBuffer is a type agnostic input buffer.
 *
@@ -403,12 +398,76 @@ void AudioResamplerDyn<TC, TI, TO>::setSampleRate(int32_t inSampleRate)
    // determine which resampler to use
    // check if locked phase (works only if mPhaseIncrement has no "fractional phase bits")
    int locked = (mPhaseIncrement << (sizeof(mPhaseIncrement)*8 - c.mShift)) == 0;
    int stride = (c.mHalfNumCoefs&7)==0 ? 16 : (c.mHalfNumCoefs&3)==0 ? 8 : 2;
    if (locked) {
        mPhaseFraction = mPhaseFraction >> c.mShift << c.mShift; // remove fractional phase
    }

    setResampler(RESAMPLETYPE(mChannelCount, locked, stride));
    // stride is the minimum number of filter coefficients processed per loop iteration.
    // We currently only allow a stride of 16 to match with SIMD processing.
    // This means that the filter length must be a multiple of 16,
    // or half the filter length (mHalfNumCoefs) must be a multiple of 8.
    //
    // Note: A stride of 2 is achieved with non-SIMD processing.
    int stride = ((c.mHalfNumCoefs & 7) == 0) ? 16 : 2;
    LOG_ALWAYS_FATAL_IF(stride < 16, "Resampler stride must be 16 or more");
    LOG_ALWAYS_FATAL_IF(mChannelCount > 8 || mChannelCount < 1,
            "Resampler channels(%d) must be between 1 to 8", mChannelCount);
    // stride 16 (falls back to stride 2 for machines that do not support NEON)
    if (locked) {
        switch (mChannelCount) {
        case 1:
            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<1, true, 16>;
            break;
        case 2:
            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<2, true, 16>;
            break;
        case 3:
            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<3, true, 16>;
            break;
        case 4:
            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<4, true, 16>;
            break;
        case 5:
            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<5, true, 16>;
            break;
        case 6:
            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<6, true, 16>;
            break;
        case 7:
            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<7, true, 16>;
            break;
        case 8:
            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<8, true, 16>;
            break;
        }
    } else {
        switch (mChannelCount) {
        case 1:
            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<1, false, 16>;
            break;
        case 2:
            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<2, false, 16>;
            break;
        case 3:
            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<3, false, 16>;
            break;
        case 4:
            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<4, false, 16>;
            break;
        case 5:
            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<5, false, 16>;
            break;
        case 6:
            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<6, false, 16>;
            break;
        case 7:
            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<7, false, 16>;
            break;
        case 8:
            mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<8, false, 16>;
            break;
        }
    }
#ifdef DEBUG_RESAMPLER
    printf("channels:%d  %s  stride:%d  %s  coef:%d  shift:%d\n",
            mChannelCount, locked ? "locked" : "interpolated",
@@ -423,35 +482,13 @@ void AudioResamplerDyn<TC, TI, TO>::resample(int32_t* out, size_t outFrameCount,
    (this->*mResampleFunc)(reinterpret_cast<TO*>(out), outFrameCount, provider);
}

template<typename TC, typename TI, typename TO>
void AudioResamplerDyn<TC, TI, TO>::setResampler(unsigned resampleType)
{
    // stride 16 (falls back to stride 2 for machines that do not support NEON)
    switch (resampleType) {
    case RESAMPLETYPE(1, true, 16):
        mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<1, true, 16>;
        return;
    case RESAMPLETYPE(2, true, 16):
        mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<2, true, 16>;
        return;
    case RESAMPLETYPE(1, false, 16):
        mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<1, false, 16>;
        return;
    case RESAMPLETYPE(2, false, 16):
        mResampleFunc = &AudioResamplerDyn<TC, TI, TO>::resample<2, false, 16>;
        return;
    default:
        LOG_ALWAYS_FATAL("Invalid resampler type: %u", resampleType);
        mResampleFunc = NULL;
        return;
    }
}

template<typename TC, typename TI, typename TO>
template<int CHANNELS, bool LOCKED, int STRIDE>
void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount,
        AudioBufferProvider* provider)
{
    // TODO Mono -> Mono is not supported. OUTPUT_CHANNELS reflects minimum of stereo out.
    const int OUTPUT_CHANNELS = (CHANNELS < 2) ? 2 : CHANNELS;
    const Constants& c(mConstants);
    const TC* const coefs = mConstants.mFirCoefs;
    TI* impulse = mInBuffer.getImpulse();
@@ -459,7 +496,7 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount,
    uint32_t phaseFraction = mPhaseFraction;
    const uint32_t phaseIncrement = mPhaseIncrement;
    size_t outputIndex = 0;
    size_t outputSampleCount = outFrameCount * 2;   // stereo output
    size_t outputSampleCount = outFrameCount * OUTPUT_CHANNELS;
    const uint32_t phaseWrapLimit = c.mL << c.mShift;
    size_t inFrameCount = (phaseIncrement * (uint64_t)outFrameCount + phaseFraction)
            / phaseWrapLimit;
@@ -490,7 +527,7 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount,
        while (mBuffer.frameCount == 0 && inFrameCount > 0) {
            mBuffer.frameCount = inFrameCount;
            provider->getNextBuffer(&mBuffer,
                    calculateOutputPTS(outputIndex / 2));
                    calculateOutputPTS(outputIndex / OUTPUT_CHANNELS));
            if (mBuffer.raw == NULL) {
                goto resample_exit;
            }
@@ -538,7 +575,8 @@ void AudioResamplerDyn<TC, TI, TO>::resample(TO* out, size_t outFrameCount,
                    phaseFraction, phaseWrapLimit,
                    coefShift, halfNumCoefs, coefs,
                    impulse, volumeSimd);
            outputIndex += 2;

            outputIndex += OUTPUT_CHANNELS;

            phaseFraction += phaseIncrement;
            while (phaseFraction >= phaseWrapLimit) {
+1 −3
Original line number Diff line number Diff line
@@ -110,12 +110,10 @@ private:
    void createKaiserFir(Constants &c, double stopBandAtten,
            int inSampleRate, int outSampleRate, double tbwCheat);

    void setResampler(unsigned resampleType);

    template<int CHANNELS, bool LOCKED, int STRIDE>
    void resample(TO* out, size_t outFrameCount, AudioBufferProvider* provider);

    // declare a pointer to member function for resample
    // define a pointer to member function type for resample
    typedef void (AudioResamplerDyn<TC, TI, TO>::*resample_ABP_t)(TO* out,
            size_t outFrameCount, AudioBufferProvider* provider);

+51 −14
Original line number Diff line number Diff line
@@ -35,7 +35,8 @@
#include "AudioResampler.h"
#include "test_utils.h"

void resample(void *output, size_t outputFrames, const std::vector<size_t> &outputIncr,
void resample(int channels, void *output,
        size_t outputFrames, const std::vector<size_t> &outputIncr,
        android::AudioBufferProvider *provider, android::AudioResampler *resampler)
{
    for (size_t i = 0, j = 0; i < outputFrames; ) {
@@ -46,7 +47,7 @@ void resample(void *output, size_t outputFrames, const std::vector<size_t> &outp
        if (thisFrames == 0 || thisFrames > outputFrames - i) {
            thisFrames = outputFrames - i;
        }
        resampler->resample((int32_t*) output + 2*i, thisFrames, provider);
        resampler->resample((int32_t*) output + channels*i, thisFrames, provider);
        i += thisFrames;
    }
}
@@ -64,19 +65,26 @@ void buffercmp(const void *reference, const void *test,
    }
}

void testBufferIncrement(size_t channels, unsigned inputFreq, unsigned outputFreq,
void testBufferIncrement(size_t channels, bool useFloat,
        unsigned inputFreq, unsigned outputFreq,
        enum android::AudioResampler::src_quality quality)
{
    const int bits = useFloat ? 32 : 16;
    // create the provider
    std::vector<int> inputIncr;
    SignalProvider provider;
    if (useFloat) {
        provider.setChirp<float>(channels,
                0., outputFreq/2., outputFreq, outputFreq/2000.);
    } else {
        provider.setChirp<int16_t>(channels,
                0., outputFreq/2., outputFreq, outputFreq/2000.);
    }
    provider.setIncr(inputIncr);

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

@@ -84,7 +92,7 @@ void testBufferIncrement(size_t channels, unsigned inputFreq, unsigned outputFre
    const int volumePrecision = 12; /* typical unity gain */
    android::AudioResampler* resampler;

    resampler = android::AudioResampler::create(16, channels, outputFreq, quality);
    resampler = android::AudioResampler::create(bits, channels, outputFreq, quality);
    resampler->setSampleRate(inputFreq);
    resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);

@@ -92,7 +100,7 @@ void testBufferIncrement(size_t channels, unsigned inputFreq, unsigned outputFre
    std::vector<size_t> refIncr;
    refIncr.push_back(outputFrames);
    void* reference = malloc(outputSize);
    resample(reference, outputFrames, refIncr, &provider, resampler);
    resample(channels, reference, outputFrames, refIncr, &provider, resampler);

    provider.reset();

@@ -101,7 +109,7 @@ void testBufferIncrement(size_t channels, unsigned inputFreq, unsigned outputFre
    resampler->reset();
#else
    delete resampler;
    resampler = android::AudioResampler::create(16, channels, outputFreq, quality);
    resampler = android::AudioResampler::create(bits, channels, outputFreq, quality);
    resampler->setSampleRate(inputFreq);
    resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
#endif
@@ -112,7 +120,10 @@ void testBufferIncrement(size_t channels, unsigned inputFreq, unsigned outputFre
    outIncr.push_back(2);
    outIncr.push_back(3);
    void* test = malloc(outputSize);
    resample(test, outputFrames, outIncr, &provider, resampler);
    inputIncr.push_back(1);
    inputIncr.push_back(3);
    provider.setIncr(inputIncr);
    resample(channels, test, outputFrames, outIncr, &provider, resampler);

    // check
    buffercmp(reference, test, outputFrameSize, outputFrames);
@@ -155,7 +166,7 @@ void testStopbandDownconversion(size_t channels,

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

@@ -171,7 +182,7 @@ void testStopbandDownconversion(size_t channels,
    std::vector<size_t> refIncr;
    refIncr.push_back(outputFrames);
    void* reference = malloc(outputSize);
    resample(reference, outputFrames, refIncr, &provider, resampler);
    resample(channels, reference, outputFrames, refIncr, &provider, resampler);

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

@@ -226,7 +237,7 @@ TEST(audioflinger_resampler, bufferincrement_fixedphase) {
    };

    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
        testBufferIncrement(2, 48000, 32000, kQualityArray[i]);
        testBufferIncrement(2, false, 48000, 32000, kQualityArray[i]);
    }
}

@@ -243,7 +254,33 @@ TEST(audioflinger_resampler, bufferincrement_interpolatedphase) {
    };

    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
        testBufferIncrement(2, 22050, 48000, kQualityArray[i]);
        testBufferIncrement(2, false, 22050, 48000, kQualityArray[i]);
    }
}

TEST(audioflinger_resampler, bufferincrement_fixedphase_multi) {
    // only dynamic quality
    static const enum android::AudioResampler::src_quality kQualityArray[] = {
            android::AudioResampler::DYN_LOW_QUALITY,
            android::AudioResampler::DYN_MED_QUALITY,
            android::AudioResampler::DYN_HIGH_QUALITY,
    };

    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
        testBufferIncrement(4, false, 48000, 32000, kQualityArray[i]);
    }
}

TEST(audioflinger_resampler, bufferincrement_interpolatedphase_multi_float) {
    // only dynamic quality
    static const enum android::AudioResampler::src_quality kQualityArray[] = {
            android::AudioResampler::DYN_LOW_QUALITY,
            android::AudioResampler::DYN_MED_QUALITY,
            android::AudioResampler::DYN_HIGH_QUALITY,
    };

    for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
        testBufferIncrement(8, true, 22050, 48000, kQualityArray[i]);
    }
}