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

Commit ac602050 authored by Glenn Kasten's avatar Glenn Kasten
Browse files

Integrate improved coefficient sinc resampler: VHQ

Summary:
Very high quality is enabled only for 44.1 -> 48 or 48 -> 44.1,
  and uses low quality for all other use cases.
Track estimated CPU load and throttles the quality based on load;
  as currently configured it should allow up to 2 instances of very high quality.
Medium quality and high quality are currently disabled unless explicitly requested.

Details:
Only load .so the first time it is needed.
Cleanup code style: formatting, indentation, whitespace.
Restore medium quality resampler, but it is not used (see next line).
Fix memory leak for sinc resampler.
Check sample rate in resampler constructor.
Add logs for debugging.
Rename DEFAULT to DEFAULT_QUALITY for consistency with other quality levels.
Renumber VERY_HIGH_QUALITY from 255 to 4.
Use enum src_quality consistently.
Improve parsing of property af.resampler.quality.
Fix reentrancy bug - allow an instance of high quality and an instance
  of very high quality to both be active concurrently.

Bug: 7229644
Change-Id: I0ce6b913b05038889f50462a38830b61a602a9f7
parent d69fd4d2
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -321,8 +321,7 @@ void VideoEditorSRC::checkAndSetResampler() {
        mResampler = AudioResampler::create(
                        16 /* bit depth */,
                        mChannelCnt,
                        mOutputSampleRate,
                        AudioResampler::DEFAULT);
                        mOutputSampleRate);
        CHECK(mResampler);
        mResampler->setSampleRate(mSampleRate);
        mResampler->setVolume(kUnityGain, kUnityGain);
+1 −1
Original line number Diff line number Diff line
@@ -80,7 +80,7 @@ M4OSA_Context LVAudioResamplerCreate(M4OSA_Int32 bitDepth, M4OSA_Int32 inChanne

    VideoEditorResampler *context = new VideoEditorResampler();
    context->mResampler = AudioResampler::create(
        bitDepth, inChannelCount, sampleRate, AudioResampler::DEFAULT);
        bitDepth, inChannelCount, sampleRate);
    if (context->mResampler == NULL) {
        return NULL;
    }
+3 −1
Original line number Diff line number Diff line
@@ -20,7 +20,9 @@ LOCAL_SRC_FILES:= \
    AudioPolicyService.cpp      \
    ServiceUtilities.cpp        \
    AudioResamplerSinc.cpp.arm
#   AudioResamplerCubic.cpp.arm

# uncomment to enable AudioResampler::MED_QUALITY
# LOCAL_SRC_FILES += AudioResamplerCubic.cpp.arm

LOCAL_SRC_FILES += StateQueue.cpp

+13 −1
Original line number Diff line number Diff line
@@ -539,11 +539,23 @@ bool AudioMixer::track_t::setResampler(uint32_t value, uint32_t devSampleRate)
        if (sampleRate != value) {
            sampleRate = value;
            if (resampler == NULL) {
                ALOGV("creating resampler from track %d Hz to device %d Hz", value, devSampleRate);
                AudioResampler::src_quality quality;
                // force lowest quality level resampler if use case isn't music or video
                // FIXME this is flawed for dynamic sample rates, as we choose the resampler
                // quality level based on the initial ratio, but that could change later.
                // Should have a way to distinguish tracks with static ratios vs. dynamic ratios.
                if (!((value == 44100 && devSampleRate == 48000) ||
                      (value == 48000 && devSampleRate == 44100))) {
                    quality = AudioResampler::LOW_QUALITY;
                } else {
                    quality = AudioResampler::DEFAULT_QUALITY;
                }
                resampler = AudioResampler::create(
                        format,
                        // the resampler sees the number of channels after the downmixer, if any
                        downmixerBufferProvider != NULL ? MAX_NUM_CHANNELS : channelCount,
                        devSampleRate);
                        devSampleRate, quality);
                resampler->setLocalTimeFreq(localTimeFreq);
            }
            return true;
+118 −16
Original line number Diff line number Diff line
@@ -24,9 +24,7 @@
#include <cutils/properties.h>
#include "AudioResampler.h"
#include "AudioResamplerSinc.h"
#if 0
#include "AudioResamplerCubic.h"
#endif

#ifdef __arm__
#include <machine/cpu-features.h>
@@ -42,7 +40,7 @@ namespace android {
class AudioResamplerOrder1 : public AudioResampler {
public:
    AudioResamplerOrder1(int bitDepth, int inChannelCount, int32_t sampleRate) :
        AudioResampler(bitDepth, inChannelCount, sampleRate), mX0L(0), mX0R(0) {
        AudioResampler(bitDepth, inChannelCount, sampleRate, LOW_QUALITY), mX0L(0), mX0R(0) {
    }
    virtual void resample(int32_t* out, size_t outFrameCount,
            AudioBufferProvider* provider);
@@ -79,29 +77,120 @@ private:
    int mX0R;
};

bool AudioResampler::qualityIsSupported(src_quality quality)
{
    switch (quality) {
    case DEFAULT_QUALITY:
    case LOW_QUALITY:
#if 0   // these have not been qualified recently so are not supported unless explicitly requested
    case MED_QUALITY:
    case HIGH_QUALITY:
#endif
    case VERY_HIGH_QUALITY:
        return true;
    default:
        return false;
    }
}

// ----------------------------------------------------------------------------
AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount,
        int32_t sampleRate, int quality) {

    // can only create low quality resample now
    AudioResampler* resampler;
static pthread_once_t once_control = PTHREAD_ONCE_INIT;
static AudioResampler::src_quality defaultQuality = AudioResampler::DEFAULT_QUALITY;

void AudioResampler::init_routine()
{
    char value[PROPERTY_VALUE_MAX];
    if (property_get("af.resampler.quality", value, 0)) {
        quality = atoi(value);
        ALOGD("forcing AudioResampler quality to %d", quality);
    if (property_get("af.resampler.quality", value, NULL) > 0) {
        char *endptr;
        unsigned long l = strtoul(value, &endptr, 0);
        if (*endptr == '\0') {
            defaultQuality = (src_quality) l;
            ALOGD("forcing AudioResampler quality to %d", defaultQuality);
            if (defaultQuality < DEFAULT_QUALITY || defaultQuality > VERY_HIGH_QUALITY) {
                defaultQuality = DEFAULT_QUALITY;
            }
        }
    }
}

    if (quality == DEFAULT)
uint32_t AudioResampler::qualityMHz(src_quality quality)
{
    switch (quality) {
    default:
    case DEFAULT_QUALITY:
    case LOW_QUALITY:
        return 3;
    case MED_QUALITY:
        return 6;
    case HIGH_QUALITY:
        return 20;
    case VERY_HIGH_QUALITY:
        return 34;
    }
}

static const uint32_t maxMHz = 75;  // an arbitrary number that permits 2 VHQ, should be tunable
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static uint32_t currentMHz = 0;

AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount,
        int32_t sampleRate, src_quality quality) {

    bool atFinalQuality;
    if (quality == DEFAULT_QUALITY) {
        // read the resampler default quality property the first time it is needed
        int ok = pthread_once(&once_control, init_routine);
        if (ok != 0) {
            ALOGE("%s pthread_once failed: %d", __func__, ok);
        }
        quality = defaultQuality;
        atFinalQuality = false;
    } else {
        atFinalQuality = true;
    }

    // naive implementation of CPU load throttling doesn't account for whether resampler is active
    pthread_mutex_lock(&mutex);
    for (;;) {
        uint32_t deltaMHz = qualityMHz(quality);
        uint32_t newMHz = currentMHz + deltaMHz;
        if ((qualityIsSupported(quality) && newMHz <= maxMHz) || atFinalQuality) {
            ALOGV("resampler load %u -> %u MHz due to delta +%u MHz from quality %d",
                    currentMHz, newMHz, deltaMHz, quality);
            currentMHz = newMHz;
            break;
        }
        // not enough CPU available for proposed quality level, so try next lowest level
        switch (quality) {
        default:
        case DEFAULT_QUALITY:
        case LOW_QUALITY:
            atFinalQuality = true;
            break;
        case MED_QUALITY:
            quality = LOW_QUALITY;
            break;
        case HIGH_QUALITY:
            quality = MED_QUALITY;
            break;
        case VERY_HIGH_QUALITY:
            quality = HIGH_QUALITY;
            break;
        }
    }
    pthread_mutex_unlock(&mutex);

    AudioResampler* resampler;

    switch (quality) {
    default:
    case DEFAULT_QUALITY:
    case LOW_QUALITY:
        ALOGV("Create linear Resampler");
        resampler = new AudioResamplerOrder1(bitDepth, inChannelCount, sampleRate);
        break;
#if 0
#if 0   // disabled because it has not been qualified recently, if requested will use default:
    case MED_QUALITY:
        ALOGV("Create cubic Resampler");
        resampler = new AudioResamplerCubic(bitDepth, inChannelCount, sampleRate);
@@ -110,6 +199,7 @@ AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount,
    case HIGH_QUALITY:
        ALOGV("Create HIGH_QUALITY sinc Resampler");
        resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate);
        break;
    case VERY_HIGH_QUALITY:
        ALOGV("Create VERY_HIGH_QUALITY sinc Resampler = %d", quality);
        resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate, quality);
@@ -122,17 +212,20 @@ AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount,
}

AudioResampler::AudioResampler(int bitDepth, int inChannelCount,
        int32_t sampleRate) :
        int32_t sampleRate, src_quality quality) :
    mBitDepth(bitDepth), mChannelCount(inChannelCount),
            mSampleRate(sampleRate), mInSampleRate(sampleRate), mInputIndex(0),
            mPhaseFraction(0), mLocalTimeFreq(0),
            mPTS(AudioBufferProvider::kInvalidPTS) {
            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 (sampleRate <= 0) {
        ALOGE("Unsupported sample rate %d Hz", sampleRate);
    }

    // initialize common members
    mVolume[0] = mVolume[1] = 0;
@@ -141,6 +234,15 @@ AudioResampler::AudioResampler(int bitDepth, int inChannelCount,
}

AudioResampler::~AudioResampler() {
    pthread_mutex_lock(&mutex);
    src_quality quality = getQuality();
    uint32_t deltaMHz = qualityMHz(quality);
    int32_t newMHz = currentMHz - deltaMHz;
    ALOGV("resampler load %u -> %d MHz due to delta -%u MHz from quality %d",
            currentMHz, newMHz, deltaMHz, quality);
    LOG_ALWAYS_FATAL_IF(newMHz < 0, "negative resampler load %d MHz", newMHz);
    currentMHz = newMHz;
    pthread_mutex_unlock(&mutex);
}

void AudioResampler::setSampleRate(int32_t inSampleRate) {
Loading