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

Commit 857d5a20 authored by Andy Hung's avatar Andy Hung
Browse files

Factor out buffer provider code from AudioMixer

In preparation for playback rate support and timestretching.

Bug: 19196501
Change-Id: I435accb852d32110dd0b3a9917488522c567ba80
parent f129b03f
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -44,9 +44,9 @@ LOCAL_SRC_FILES:= \
    SpdifStreamOut.cpp          \
    Effects.cpp                 \
    AudioMixer.cpp.arm          \
    PatchPanel.cpp

LOCAL_SRC_FILES += StateQueue.cpp
    BufferProviders.cpp         \
    PatchPanel.cpp              \
    StateQueue.cpp

LOCAL_C_INCLUDES := \
    $(TOPDIR)frameworks/av/services/audiopolicy \
+1 −320
Original line number Diff line number Diff line
@@ -38,9 +38,7 @@
#include <audio_utils/format.h>
#include <common_time/local_clock.h>
#include <common_time/cc_helper.h>

#include <media/EffectsFactoryApi.h>
#include <audio_effects/effect_downmix.h>
#include <media/AudioResamplerPublic.h>

#include "AudioMixerOps.h"
#include "AudioMixer.h"
@@ -91,323 +89,6 @@ T min(const T& a, const T& b)
    return a < b ? a : b;
}

AudioMixer::CopyBufferProvider::CopyBufferProvider(size_t inputFrameSize,
        size_t outputFrameSize, size_t bufferFrameCount) :
        mInputFrameSize(inputFrameSize),
        mOutputFrameSize(outputFrameSize),
        mLocalBufferFrameCount(bufferFrameCount),
        mLocalBufferData(NULL),
        mConsumed(0)
{
    ALOGV("CopyBufferProvider(%p)(%zu, %zu, %zu)", this,
            inputFrameSize, outputFrameSize, bufferFrameCount);
    LOG_ALWAYS_FATAL_IF(inputFrameSize < outputFrameSize && bufferFrameCount == 0,
            "Requires local buffer if inputFrameSize(%zu) < outputFrameSize(%zu)",
            inputFrameSize, outputFrameSize);
    if (mLocalBufferFrameCount) {
        (void)posix_memalign(&mLocalBufferData, 32, mLocalBufferFrameCount * mOutputFrameSize);
    }
    mBuffer.frameCount = 0;
}

AudioMixer::CopyBufferProvider::~CopyBufferProvider()
{
    ALOGV("~CopyBufferProvider(%p)", this);
    if (mBuffer.frameCount != 0) {
        mTrackBufferProvider->releaseBuffer(&mBuffer);
    }
    free(mLocalBufferData);
}

status_t AudioMixer::CopyBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer,
        int64_t pts)
{
    //ALOGV("CopyBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)",
    //        this, pBuffer, pBuffer->frameCount, pts);
    if (mLocalBufferFrameCount == 0) {
        status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts);
        if (res == OK) {
            copyFrames(pBuffer->raw, pBuffer->raw, pBuffer->frameCount);
        }
        return res;
    }
    if (mBuffer.frameCount == 0) {
        mBuffer.frameCount = pBuffer->frameCount;
        status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts);
        // At one time an upstream buffer provider had
        // res == OK and mBuffer.frameCount == 0, doesn't seem to happen now 7/18/2014.
        //
        // By API spec, if res != OK, then mBuffer.frameCount == 0.
        // but there may be improper implementations.
        ALOG_ASSERT(res == OK || mBuffer.frameCount == 0);
        if (res != OK || mBuffer.frameCount == 0) { // not needed by API spec, but to be safe.
            pBuffer->raw = NULL;
            pBuffer->frameCount = 0;
            return res;
        }
        mConsumed = 0;
    }
    ALOG_ASSERT(mConsumed < mBuffer.frameCount);
    size_t count = min(mLocalBufferFrameCount, mBuffer.frameCount - mConsumed);
    count = min(count, pBuffer->frameCount);
    pBuffer->raw = mLocalBufferData;
    pBuffer->frameCount = count;
    copyFrames(pBuffer->raw, (uint8_t*)mBuffer.raw + mConsumed * mInputFrameSize,
            pBuffer->frameCount);
    return OK;
}

void AudioMixer::CopyBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer)
{
    //ALOGV("CopyBufferProvider(%p)::releaseBuffer(%p(%zu))",
    //        this, pBuffer, pBuffer->frameCount);
    if (mLocalBufferFrameCount == 0) {
        mTrackBufferProvider->releaseBuffer(pBuffer);
        return;
    }
    // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount");
    mConsumed += pBuffer->frameCount; // TODO: update for efficiency to reuse existing content
    if (mConsumed != 0 && mConsumed >= mBuffer.frameCount) {
        mTrackBufferProvider->releaseBuffer(&mBuffer);
        ALOG_ASSERT(mBuffer.frameCount == 0);
    }
    pBuffer->raw = NULL;
    pBuffer->frameCount = 0;
}

void AudioMixer::CopyBufferProvider::reset()
{
    if (mBuffer.frameCount != 0) {
        mTrackBufferProvider->releaseBuffer(&mBuffer);
    }
    mConsumed = 0;
}

AudioMixer::DownmixerBufferProvider::DownmixerBufferProvider(
        audio_channel_mask_t inputChannelMask,
        audio_channel_mask_t outputChannelMask, audio_format_t format,
        uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount) :
        CopyBufferProvider(
            audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(inputChannelMask),
            audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(outputChannelMask),
            bufferFrameCount)  // set bufferFrameCount to 0 to do in-place
{
    ALOGV("DownmixerBufferProvider(%p)(%#x, %#x, %#x %u %d)",
            this, inputChannelMask, outputChannelMask, format,
            sampleRate, sessionId);
    if (!sIsMultichannelCapable
            || EffectCreate(&sDwnmFxDesc.uuid,
                    sessionId,
                    SESSION_ID_INVALID_AND_IGNORED,
                    &mDownmixHandle) != 0) {
         ALOGE("DownmixerBufferProvider() error creating downmixer effect");
         mDownmixHandle = NULL;
         return;
     }
     // channel input configuration will be overridden per-track
     mDownmixConfig.inputCfg.channels = inputChannelMask;   // FIXME: Should be bits
     mDownmixConfig.outputCfg.channels = outputChannelMask; // FIXME: should be bits
     mDownmixConfig.inputCfg.format = format;
     mDownmixConfig.outputCfg.format = format;
     mDownmixConfig.inputCfg.samplingRate = sampleRate;
     mDownmixConfig.outputCfg.samplingRate = sampleRate;
     mDownmixConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
     mDownmixConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
     // input and output buffer provider, and frame count will not be used as the downmix effect
     // process() function is called directly (see DownmixerBufferProvider::getNextBuffer())
     mDownmixConfig.inputCfg.mask = EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS |
             EFFECT_CONFIG_FORMAT | EFFECT_CONFIG_ACC_MODE;
     mDownmixConfig.outputCfg.mask = mDownmixConfig.inputCfg.mask;

     int cmdStatus;
     uint32_t replySize = sizeof(int);

     // Configure downmixer
     status_t status = (*mDownmixHandle)->command(mDownmixHandle,
             EFFECT_CMD_SET_CONFIG /*cmdCode*/, sizeof(effect_config_t) /*cmdSize*/,
             &mDownmixConfig /*pCmdData*/,
             &replySize, &cmdStatus /*pReplyData*/);
     if (status != 0 || cmdStatus != 0) {
         ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while configuring downmixer",
                 status, cmdStatus);
         EffectRelease(mDownmixHandle);
         mDownmixHandle = NULL;
         return;
     }

     // Enable downmixer
     replySize = sizeof(int);
     status = (*mDownmixHandle)->command(mDownmixHandle,
             EFFECT_CMD_ENABLE /*cmdCode*/, 0 /*cmdSize*/, NULL /*pCmdData*/,
             &replySize, &cmdStatus /*pReplyData*/);
     if (status != 0 || cmdStatus != 0) {
         ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while enabling downmixer",
                 status, cmdStatus);
         EffectRelease(mDownmixHandle);
         mDownmixHandle = NULL;
         return;
     }

     // Set downmix type
     // parameter size rounded for padding on 32bit boundary
     const int psizePadded = ((sizeof(downmix_params_t) - 1)/sizeof(int) + 1) * sizeof(int);
     const int downmixParamSize =
             sizeof(effect_param_t) + psizePadded + sizeof(downmix_type_t);
     effect_param_t * const param = (effect_param_t *) malloc(downmixParamSize);
     param->psize = sizeof(downmix_params_t);
     const downmix_params_t downmixParam = DOWNMIX_PARAM_TYPE;
     memcpy(param->data, &downmixParam, param->psize);
     const downmix_type_t downmixType = DOWNMIX_TYPE_FOLD;
     param->vsize = sizeof(downmix_type_t);
     memcpy(param->data + psizePadded, &downmixType, param->vsize);
     replySize = sizeof(int);
     status = (*mDownmixHandle)->command(mDownmixHandle,
             EFFECT_CMD_SET_PARAM /* cmdCode */, downmixParamSize /* cmdSize */,
             param /*pCmdData*/, &replySize, &cmdStatus /*pReplyData*/);
     free(param);
     if (status != 0 || cmdStatus != 0) {
         ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while setting downmix type",
                 status, cmdStatus);
         EffectRelease(mDownmixHandle);
         mDownmixHandle = NULL;
         return;
     }
     ALOGV("DownmixerBufferProvider() downmix type set to %d", (int) downmixType);
}

AudioMixer::DownmixerBufferProvider::~DownmixerBufferProvider()
{
    ALOGV("~DownmixerBufferProvider (%p)", this);
    EffectRelease(mDownmixHandle);
    mDownmixHandle = NULL;
}

void AudioMixer::DownmixerBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
{
    mDownmixConfig.inputCfg.buffer.frameCount = frames;
    mDownmixConfig.inputCfg.buffer.raw = const_cast<void *>(src);
    mDownmixConfig.outputCfg.buffer.frameCount = frames;
    mDownmixConfig.outputCfg.buffer.raw = dst;
    // may be in-place if src == dst.
    status_t res = (*mDownmixHandle)->process(mDownmixHandle,
            &mDownmixConfig.inputCfg.buffer, &mDownmixConfig.outputCfg.buffer);
    ALOGE_IF(res != OK, "DownmixBufferProvider error %d", res);
}

/* call once in a pthread_once handler. */
/*static*/ status_t AudioMixer::DownmixerBufferProvider::init()
{
    // find multichannel downmix effect if we have to play multichannel content
    uint32_t numEffects = 0;
    int ret = EffectQueryNumberEffects(&numEffects);
    if (ret != 0) {
        ALOGE("AudioMixer() error %d querying number of effects", ret);
        return NO_INIT;
    }
    ALOGV("EffectQueryNumberEffects() numEffects=%d", numEffects);

    for (uint32_t i = 0 ; i < numEffects ; i++) {
        if (EffectQueryEffect(i, &sDwnmFxDesc) == 0) {
            ALOGV("effect %d is called %s", i, sDwnmFxDesc.name);
            if (memcmp(&sDwnmFxDesc.type, EFFECT_UIID_DOWNMIX, sizeof(effect_uuid_t)) == 0) {
                ALOGI("found effect \"%s\" from %s",
                        sDwnmFxDesc.name, sDwnmFxDesc.implementor);
                sIsMultichannelCapable = true;
                break;
            }
        }
    }
    ALOGW_IF(!sIsMultichannelCapable, "unable to find downmix effect");
    return NO_INIT;
}

/*static*/ bool AudioMixer::DownmixerBufferProvider::sIsMultichannelCapable = false;
/*static*/ effect_descriptor_t AudioMixer::DownmixerBufferProvider::sDwnmFxDesc;

AudioMixer::RemixBufferProvider::RemixBufferProvider(audio_channel_mask_t inputChannelMask,
        audio_channel_mask_t outputChannelMask, audio_format_t format,
        size_t bufferFrameCount) :
        CopyBufferProvider(
                audio_bytes_per_sample(format)
                    * audio_channel_count_from_out_mask(inputChannelMask),
                audio_bytes_per_sample(format)
                    * audio_channel_count_from_out_mask(outputChannelMask),
                bufferFrameCount),
        mFormat(format),
        mSampleSize(audio_bytes_per_sample(format)),
        mInputChannels(audio_channel_count_from_out_mask(inputChannelMask)),
        mOutputChannels(audio_channel_count_from_out_mask(outputChannelMask))
{
    ALOGV("RemixBufferProvider(%p)(%#x, %#x, %#x) %zu %zu",
            this, format, inputChannelMask, outputChannelMask,
            mInputChannels, mOutputChannels);

    const audio_channel_representation_t inputRepresentation =
            audio_channel_mask_get_representation(inputChannelMask);
    const audio_channel_representation_t outputRepresentation =
            audio_channel_mask_get_representation(outputChannelMask);
    const uint32_t inputBits = audio_channel_mask_get_bits(inputChannelMask);
    const uint32_t outputBits = audio_channel_mask_get_bits(outputChannelMask);

    switch (inputRepresentation) {
    case AUDIO_CHANNEL_REPRESENTATION_POSITION:
        switch (outputRepresentation) {
        case AUDIO_CHANNEL_REPRESENTATION_POSITION:
            memcpy_by_index_array_initialization(mIdxAry, ARRAY_SIZE(mIdxAry),
                    outputBits, inputBits);
            return;
        case AUDIO_CHANNEL_REPRESENTATION_INDEX:
            // TODO: output channel index mask not currently allowed
            // fall through
        default:
            break;
        }
        break;
    case AUDIO_CHANNEL_REPRESENTATION_INDEX:
        switch (outputRepresentation) {
        case AUDIO_CHANNEL_REPRESENTATION_POSITION:
            memcpy_by_index_array_initialization_src_index(mIdxAry, ARRAY_SIZE(mIdxAry),
                    outputBits, inputBits);
            return;
        case AUDIO_CHANNEL_REPRESENTATION_INDEX:
            // TODO: output channel index mask not currently allowed
            // fall through
        default:
            break;
        }
        break;
    default:
        break;
    }
    LOG_ALWAYS_FATAL("invalid channel mask conversion from %#x to %#x",
            inputChannelMask, outputChannelMask);
}

void AudioMixer::RemixBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
{
    memcpy_by_index_array(dst, mOutputChannels,
            src, mInputChannels, mIdxAry, mSampleSize, frames);
}

AudioMixer::ReformatBufferProvider::ReformatBufferProvider(int32_t channels,
        audio_format_t inputFormat, audio_format_t outputFormat,
        size_t bufferFrameCount) :
        CopyBufferProvider(
            channels * audio_bytes_per_sample(inputFormat),
            channels * audio_bytes_per_sample(outputFormat),
            bufferFrameCount),
        mChannels(channels),
        mInputFormat(inputFormat),
        mOutputFormat(outputFormat)
{
    ALOGV("ReformatBufferProvider(%p)(%d, %#x, %#x)", this, channels, inputFormat, outputFormat);
}

void AudioMixer::ReformatBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
{
    memcpy_by_audio_format(dst, mOutputFormat, src, mInputFormat, frames * mChannels);
}

// ----------------------------------------------------------------------------

// Ensure mConfiguredNames bitmask is initialized properly on all architectures.
+4 −111
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include <utils/threads.h>

#include "AudioResampler.h"
#include "BufferProviders.h"

// FIXME This is actually unity gain, which might not be max in future, expressed in U.12
#define MAX_GAIN_INT AudioMixer::UNITY_GAIN_INT
@@ -159,7 +160,6 @@ private:

    struct state_t;
    struct track_t;
    class CopyBufferProvider;

    typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp,
                           int32_t* aux);
@@ -225,11 +225,10 @@ private:
         *    the downmixer requirements to the mixer engine input requirements.
         */
        AudioBufferProvider*     mInputBufferProvider;    // externally provided buffer provider.
        CopyBufferProvider*      mReformatBufferProvider; // provider wrapper for reformatting.
        CopyBufferProvider*      downmixerBufferProvider; // wrapper for channel conversion.
        CopyBufferProvider*      mPostDownmixReformatBufferProvider;
        PassthruBufferProvider*  mReformatBufferProvider; // provider wrapper for reformatting.
        PassthruBufferProvider*  downmixerBufferProvider; // wrapper for channel conversion.
        PassthruBufferProvider*  mPostDownmixReformatBufferProvider;

        // 16-byte boundary
        int32_t     sessionId;

        audio_format_t mMixerFormat;     // output mix format: AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
@@ -282,112 +281,6 @@ private:
        track_t         tracks[MAX_NUM_TRACKS] __attribute__((aligned(32)));
    };

    // Base AudioBufferProvider class used for DownMixerBufferProvider, RemixBufferProvider,
    // and ReformatBufferProvider.
    // It handles a private buffer for use in converting format or channel masks from the
    // input data to a form acceptable by the mixer.
    // TODO: Make a ResamplerBufferProvider when integers are entirely removed from the
    // processing pipeline.
    class CopyBufferProvider : public AudioBufferProvider {
    public:
        // Use a private buffer of bufferFrameCount frames (each frame is outputFrameSize bytes).
        // If bufferFrameCount is 0, no private buffer is created and in-place modification of
        // the upstream buffer provider's buffers is performed by copyFrames().
        CopyBufferProvider(size_t inputFrameSize, size_t outputFrameSize,
                size_t bufferFrameCount);
        virtual ~CopyBufferProvider();

        // Overrides AudioBufferProvider methods
        virtual status_t getNextBuffer(Buffer* buffer, int64_t pts);
        virtual void releaseBuffer(Buffer* buffer);

        // Other public methods

        // call this to release the buffer to the upstream provider.
        // treat it as an audio discontinuity for future samples.
        virtual void reset();

        // this function should be supplied by the derived class.  It converts
        // #frames in the *src pointer to the *dst pointer.  It is public because
        // some providers will allow this to work on arbitrary buffers outside
        // of the internal buffers.
        virtual void copyFrames(void *dst, const void *src, size_t frames) = 0;

        // set the upstream buffer provider. Consider calling "reset" before this function.
        void setBufferProvider(AudioBufferProvider *p) {
            mTrackBufferProvider = p;
        }

    protected:
        AudioBufferProvider* mTrackBufferProvider;
        const size_t         mInputFrameSize;
        const size_t         mOutputFrameSize;
    private:
        AudioBufferProvider::Buffer mBuffer;
        const size_t         mLocalBufferFrameCount;
        void*                mLocalBufferData;
        size_t               mConsumed;
    };

    // DownmixerBufferProvider wraps a track AudioBufferProvider to provide
    // position dependent downmixing by an Audio Effect.
    class DownmixerBufferProvider : public CopyBufferProvider {
    public:
        DownmixerBufferProvider(audio_channel_mask_t inputChannelMask,
                audio_channel_mask_t outputChannelMask, audio_format_t format,
                uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount);
        virtual ~DownmixerBufferProvider();
        virtual void copyFrames(void *dst, const void *src, size_t frames);
        bool isValid() const { return mDownmixHandle != NULL; }

        static status_t init();
        static bool isMultichannelCapable() { return sIsMultichannelCapable; }

    protected:
        effect_handle_t    mDownmixHandle;
        effect_config_t    mDownmixConfig;

        // effect descriptor for the downmixer used by the mixer
        static effect_descriptor_t sDwnmFxDesc;
        // indicates whether a downmix effect has been found and is usable by this mixer
        static bool                sIsMultichannelCapable;
        // FIXME: should we allow effects outside of the framework?
        // We need to here. A special ioId that must be <= -2 so it does not map to a session.
        static const int32_t SESSION_ID_INVALID_AND_IGNORED = -2;
    };

    // RemixBufferProvider wraps a track AudioBufferProvider to perform an
    // upmix or downmix to the proper channel count and mask.
    class RemixBufferProvider : public CopyBufferProvider {
    public:
        RemixBufferProvider(audio_channel_mask_t inputChannelMask,
                audio_channel_mask_t outputChannelMask, audio_format_t format,
                size_t bufferFrameCount);
        virtual void copyFrames(void *dst, const void *src, size_t frames);

    protected:
        const audio_format_t mFormat;
        const size_t         mSampleSize;
        const size_t         mInputChannels;
        const size_t         mOutputChannels;
        int8_t               mIdxAry[sizeof(uint32_t)*8]; // 32 bits => channel indices
    };

    // ReformatBufferProvider wraps a track AudioBufferProvider to convert the input data
    // to an acceptable mixer input format type.
    class ReformatBufferProvider : public CopyBufferProvider {
    public:
        ReformatBufferProvider(int32_t channels,
                audio_format_t inputFormat, audio_format_t outputFormat,
                size_t bufferFrameCount);
        virtual void copyFrames(void *dst, const void *src, size_t frames);

    protected:
        const int32_t        mChannels;
        const audio_format_t mInputFormat;
        const audio_format_t mOutputFormat;
    };

    // bitmask of allocated track names, where bit 0 corresponds to TRACK0 etc.
    uint32_t        mTrackNames;

+362 −0

File added.

Preview size limit exceeded, changes collapsed.

+152 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading