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

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

Merge "Update DownmixerBufferProvider to use CopyBufferProvider" into lmp-dev

parents c6078051 34803d59
Loading
Loading
Loading
Loading
+143 −144
Original line number Diff line number Diff line
@@ -176,49 +176,146 @@ void AudioMixer::CopyBufferProvider::reset()
    mConsumed = 0;
}

AudioMixer::DownmixerBufferProvider::DownmixerBufferProvider() : AudioBufferProvider(),
        mTrackBufferProvider(NULL), mDownmixHandle(NULL)
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("AudioMixer deleting DownmixerBufferProvider (%p)", this);
    ALOGV("~DownmixerBufferProvider (%p)", this);
    EffectRelease(mDownmixHandle);
    mDownmixHandle = NULL;
}

status_t AudioMixer::DownmixerBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer,
        int64_t pts) {
    //ALOGV("DownmixerBufferProvider::getNextBuffer()");
    if (mTrackBufferProvider != NULL) {
        status_t res = mTrackBufferProvider->getNextBuffer(pBuffer, pts);
        if (res == OK) {
            mDownmixConfig.inputCfg.buffer.frameCount = pBuffer->frameCount;
            mDownmixConfig.inputCfg.buffer.raw = pBuffer->raw;
            mDownmixConfig.outputCfg.buffer.frameCount = pBuffer->frameCount;
            mDownmixConfig.outputCfg.buffer.raw = mDownmixConfig.inputCfg.buffer.raw;
            // in-place so overwrite the buffer contents, has been set in prepareTrackForDownmix()
            //mDownmixConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;

            res = (*mDownmixHandle)->process(mDownmixHandle,
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);
            //ALOGV("getNextBuffer is downmixing");
    ALOGE_IF(res != OK, "DownmixBufferProvider error %d", res);
}
        return res;
    } else {
        ALOGE("DownmixerBufferProvider::getNextBuffer() error: NULL track buffer provider");

/* 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);

void AudioMixer::DownmixerBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer) {
    //ALOGV("DownmixerBufferProvider::releaseBuffer()");
    if (mTrackBufferProvider != NULL) {
        mTrackBufferProvider->releaseBuffer(pBuffer);
    } else {
        ALOGE("DownmixerBufferProvider::releaseBuffer() error: NULL track buffer provider");
    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::ReformatBufferProvider::ReformatBufferProvider(int32_t channels,
        audio_format_t inputFormat, audio_format_t outputFormat,
@@ -240,9 +337,6 @@ void AudioMixer::ReformatBufferProvider::copyFrames(void *dst, const void *src,
}

// ----------------------------------------------------------------------------
bool AudioMixer::sIsMultichannelCapable = false;

effect_descriptor_t AudioMixer::sDwnmFxDesc;

// Ensure mConfiguredNames bitmask is initialized properly on all architectures.
// The value of 1 << x is undefined in C when x >= 32.
@@ -434,95 +528,20 @@ status_t AudioMixer::prepareTrackForDownmix(track_t* pTrack, int trackName)

    // discard the previous downmixer if there was one
    unprepareTrackForDownmix(pTrack, trackName);

    DownmixerBufferProvider* pDbp = new DownmixerBufferProvider();
    int32_t status;

    if (!sIsMultichannelCapable) {
        ALOGE("prepareTrackForDownmix(%d) fails: mixer doesn't support multichannel content",
                trackName);
        goto noDownmixForActiveTrack;
    }

    if (EffectCreate(&sDwnmFxDesc.uuid,
            pTrack->sessionId /*sessionId*/, -2 /*ioId not relevant here, using random value*/,
            &pDbp->mDownmixHandle/*pHandle*/) != 0) {
        ALOGE("prepareTrackForDownmix(%d) fails: error creating downmixer effect", trackName);
        goto noDownmixForActiveTrack;
    }

    // channel input configuration will be overridden per-track
    pDbp->mDownmixConfig.inputCfg.channels = pTrack->channelMask;
    pDbp->mDownmixConfig.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
    pDbp->mDownmixConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
    pDbp->mDownmixConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
    pDbp->mDownmixConfig.inputCfg.samplingRate = pTrack->sampleRate;
    pDbp->mDownmixConfig.outputCfg.samplingRate = pTrack->sampleRate;
    pDbp->mDownmixConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
    pDbp->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())
    pDbp->mDownmixConfig.inputCfg.mask = EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS |
            EFFECT_CONFIG_FORMAT | EFFECT_CONFIG_ACC_MODE;
    pDbp->mDownmixConfig.outputCfg.mask = pDbp->mDownmixConfig.inputCfg.mask;

    {// scope for local variables that are not used in goto label "noDownmixForActiveTrack"
        int cmdStatus;
        uint32_t replySize = sizeof(int);

        // Configure and enable downmixer
        status = (*pDbp->mDownmixHandle)->command(pDbp->mDownmixHandle,
                EFFECT_CMD_SET_CONFIG /*cmdCode*/, sizeof(effect_config_t) /*cmdSize*/,
                &pDbp->mDownmixConfig /*pCmdData*/,
                &replySize /*replySize*/, &cmdStatus /*pReplyData*/);
        if ((status != 0) || (cmdStatus != 0)) {
            ALOGE("error %d while configuring downmixer for track %d", status, trackName);
            goto noDownmixForActiveTrack;
        }
        replySize = sizeof(int);
        status = (*pDbp->mDownmixHandle)->command(pDbp->mDownmixHandle,
                EFFECT_CMD_ENABLE /*cmdCode*/, 0 /*cmdSize*/, NULL /*pCmdData*/,
                &replySize /*replySize*/, &cmdStatus /*pReplyData*/);
        if ((status != 0) || (cmdStatus != 0)) {
            ALOGE("error %d while enabling downmixer for track %d", status, trackName);
            goto noDownmixForActiveTrack;
        }

        // 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);

        status = (*pDbp->mDownmixHandle)->command(pDbp->mDownmixHandle,
                EFFECT_CMD_SET_PARAM /* cmdCode */, downmixParamSize/* cmdSize */,
                param /*pCmndData*/, &replySize /*replySize*/, &cmdStatus /*pReplyData*/);

        free(param);

        if ((status != 0) || (cmdStatus != 0)) {
            ALOGE("error %d while setting downmix type for track %d", status, trackName);
            goto noDownmixForActiveTrack;
        } else {
            ALOGV("downmix type set to %d for track %d", (int) downmixType, trackName);
        }
    }// end of scope for local variables that are not used in goto label "noDownmixForActiveTrack"

    // initialization successful:
    pTrack->mMixerInFormat = AUDIO_FORMAT_PCM_16_BIT; // 16 bit input is required for downmix
    if (DownmixerBufferProvider::isMultichannelCapable()) {
        DownmixerBufferProvider* pDbp = new DownmixerBufferProvider(pTrack->channelMask,
                /* pTrack->mMixerChannelMask */ audio_channel_out_mask_from_count(2),
                /* pTrack->mMixerInFormat */ AUDIO_FORMAT_PCM_16_BIT,
                pTrack->sampleRate, pTrack->sessionId, kCopyBufferFrameCount);

        if (pDbp->isValid()) { // if constructor completed properly
            pTrack->mMixerInFormat = AUDIO_FORMAT_PCM_16_BIT; // PCM 16 bit required for downmix
            pTrack->downmixerBufferProvider = pDbp;
            reconfigureBufferProviders(pTrack);
            return NO_ERROR;

noDownmixForActiveTrack:
        }
        delete pDbp;
    }
    pTrack->downmixerBufferProvider = NULL;
    reconfigureBufferProviders(pTrack);
    return NO_INIT;
@@ -561,7 +580,7 @@ void AudioMixer::reconfigureBufferProviders(track_t* pTrack)
        pTrack->bufferProvider = pTrack->mReformatBufferProvider;
    }
    if (pTrack->downmixerBufferProvider) {
        pTrack->downmixerBufferProvider->mTrackBufferProvider = pTrack->bufferProvider;
        pTrack->downmixerBufferProvider->setBufferProvider(pTrack->bufferProvider);
        pTrack->bufferProvider = pTrack->downmixerBufferProvider;
    }
}
@@ -1806,29 +1825,9 @@ int64_t AudioMixer::calculateOutputPTS(const track_t& t, int64_t basePTS,
/*static*/ void AudioMixer::sInitRoutine()
{
    LocalClock lc;
    sLocalTimeFreq = lc.getLocalFreq();
    sLocalTimeFreq = lc.getLocalFreq(); // for the resampler

    // 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;
    }
    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");
    DownmixerBufferProvider::init(); // for the downmixer
}

template <int MIXTYPE, int NCHAN, bool USEFLOATVOL, bool ADJUSTVOL,
+23 −14
Original line number Diff line number Diff line
@@ -153,7 +153,6 @@ private:

    struct state_t;
    struct track_t;
    class DownmixerBufferProvider;
    class CopyBufferProvider;

    typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp,
@@ -208,7 +207,7 @@ private:
        // 16-byte boundary
        AudioBufferProvider*     mInputBufferProvider;    // externally provided buffer provider.
        CopyBufferProvider*      mReformatBufferProvider; // provider wrapper for reformatting.
        DownmixerBufferProvider* downmixerBufferProvider; // 4 bytes
        CopyBufferProvider*      downmixerBufferProvider; // wrapper for channel conversion.

        int32_t     sessionId;

@@ -253,7 +252,8 @@ private:
        track_t         tracks[MAX_NUM_TRACKS] __attribute__((aligned(32)));
    };

    // Base AudioBufferProvider class used for ReformatBufferProvider.
    // Base AudioBufferProvider class used for ReformatBufferProvider and
    // DownmixerBufferProvider.
    // 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
@@ -299,17 +299,31 @@ private:
        size_t               mConsumed;
    };

    // AudioBufferProvider that wraps a track AudioBufferProvider by a call to a downmix effect
    class DownmixerBufferProvider : public AudioBufferProvider {
    // DownmixerBufferProvider wraps a track AudioBufferProvider to provide
    // position dependent downmixing by an Audio Effect.
    class DownmixerBufferProvider : public CopyBufferProvider {
    public:
        virtual status_t getNextBuffer(Buffer* buffer, int64_t pts);
        virtual void releaseBuffer(Buffer* buffer);
        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);
        virtual ~DownmixerBufferProvider();
        virtual void copyFrames(void *dst, const void *src, size_t frames);
        bool isValid() const { return mDownmixHandle != NULL; }

        AudioBufferProvider* mTrackBufferProvider;
        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;
    };

    // ReformatBufferProvider wraps a track AudioBufferProvider to convert the input data
@@ -342,11 +356,6 @@ public:
private:
    state_t         mState __attribute__((aligned(32)));

    // 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;

    // Call after changing either the enabled status of a track, or parameters of an enabled track.
    // OK to call more often than that, but unnecessary.
    void invalidateState(uint32_t mask);