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

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

Merge "Add playback rate to AudioMixer"

parents 1c2dc064 c5656cc9
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -34,6 +34,14 @@
// an int32_t of the phase increments, making the resulting sample rate inexact.
#define AUDIO_RESAMPLER_UP_RATIO_MAX 65536

#define AUDIO_TIMESTRETCH_SPEED_MIN    0.5f
#define AUDIO_TIMESTRETCH_SPEED_MAX    2.0f
#define AUDIO_TIMESTRETCH_SPEED_NORMAL 1.0f

#define AUDIO_TIMESTRETCH_PITCH_MIN    0.5f
#define AUDIO_TIMESTRETCH_PITCH_MAX    2.0f
#define AUDIO_TIMESTRETCH_PITCH_NORMAL 1.0f

// Returns the source frames needed to resample to destination frames.  This is not a precise
// value and depends on the resampler (and possibly how it handles rounding internally).
// Nevertheless, this should be an upper bound on the requirements of the resampler.
+56 −1
Original line number Diff line number Diff line
@@ -123,6 +123,7 @@ AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate, uint32_t maxNumTr
        t->resampler = NULL;
        t->downmixerBufferProvider = NULL;
        t->mReformatBufferProvider = NULL;
        t->mTimestretchBufferProvider = NULL;
        t++;
    }

@@ -135,6 +136,7 @@ AudioMixer::~AudioMixer()
        delete t->resampler;
        delete t->downmixerBufferProvider;
        delete t->mReformatBufferProvider;
        delete t->mTimestretchBufferProvider;
        t++;
    }
    delete [] mState.outputTemp;
@@ -213,6 +215,7 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask,
        t->mReformatBufferProvider = NULL;
        t->downmixerBufferProvider = NULL;
        t->mPostDownmixReformatBufferProvider = NULL;
        t->mTimestretchBufferProvider = NULL;
        t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT;
        t->mFormat = format;
        t->mMixerInFormat = selectMixerInFormat(format);
@@ -220,6 +223,8 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask,
        t->mMixerChannelMask = audio_channel_mask_from_representation_and_bits(
                AUDIO_CHANNEL_REPRESENTATION_POSITION, AUDIO_CHANNEL_OUT_STEREO);
        t->mMixerChannelCount = audio_channel_count_from_out_mask(t->mMixerChannelMask);
        t->mSpeed = AUDIO_TIMESTRETCH_SPEED_NORMAL;
        t->mPitch = AUDIO_TIMESTRETCH_PITCH_NORMAL;
        // Check the downmixing (or upmixing) requirements.
        status_t status = t->prepareForDownmix();
        if (status != OK) {
@@ -412,6 +417,10 @@ void AudioMixer::track_t::reconfigureBufferProviders()
        mPostDownmixReformatBufferProvider->setBufferProvider(bufferProvider);
        bufferProvider = mPostDownmixReformatBufferProvider;
    }
    if (mTimestretchBufferProvider) {
        mTimestretchBufferProvider->setBufferProvider(bufferProvider);
        bufferProvider = mTimestretchBufferProvider;
    }
}

void AudioMixer::deleteTrackName(int name)
@@ -432,7 +441,9 @@ void AudioMixer::deleteTrackName(int name)
    mState.tracks[name].unprepareForDownmix();
    // delete the reformatter
    mState.tracks[name].unprepareForReformat();

    // delete the timestretch provider
    delete track.mTimestretchBufferProvider;
    track.mTimestretchBufferProvider = NULL;
    mTrackNames &= ~(1<<name);
}

@@ -654,6 +665,26 @@ void AudioMixer::setParameter(int name, int target, int param, void *value)
            }
        }
        break;
        case TIMESTRETCH:
            switch (param) {
            case PLAYBACK_RATE: {
                const float speed = reinterpret_cast<float*>(value)[0];
                const float pitch = reinterpret_cast<float*>(value)[1];
                ALOG_ASSERT(AUDIO_TIMESTRETCH_SPEED_MIN <= speed
                        && speed <= AUDIO_TIMESTRETCH_SPEED_MAX,
                        "bad speed %f", speed);
                ALOG_ASSERT(AUDIO_TIMESTRETCH_PITCH_MIN <= pitch
                        && pitch <= AUDIO_TIMESTRETCH_PITCH_MAX,
                        "bad pitch %f", pitch);
                if (track.setPlaybackRate(speed, pitch)) {
                    ALOGV("setParameter(TIMESTRETCH, PLAYBACK_RATE, %f %f", speed, pitch);
                    // invalidateState(1 << name);
                }
                } break;
            default:
                LOG_ALWAYS_FATAL("setParameter timestretch: bad param %d", param);
            }
            break;

    default:
        LOG_ALWAYS_FATAL("setParameter: bad target %d", target);
@@ -699,6 +730,28 @@ bool AudioMixer::track_t::setResampler(uint32_t trackSampleRate, uint32_t devSam
    return false;
}

bool AudioMixer::track_t::setPlaybackRate(float speed, float pitch)
{
    if (speed == mSpeed && pitch == mPitch) {
        return false;
    }
    mSpeed = speed;
    mPitch = pitch;
    if (mTimestretchBufferProvider == NULL) {
        // TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer
        // but if none exists, it is the channel count (1 for mono).
        const int timestretchChannelCount = downmixerBufferProvider != NULL
                ? mMixerChannelCount : channelCount;
        mTimestretchBufferProvider = new TimestretchBufferProvider(timestretchChannelCount,
                mMixerInFormat, sampleRate, speed, pitch);
        reconfigureBufferProviders();
    } else {
        reinterpret_cast<TimestretchBufferProvider*>(mTimestretchBufferProvider)
                ->setPlaybackRate(speed, pitch);
    }
    return true;
}

/* Checks to see if the volume ramp has completed and clears the increment
 * variables appropriately.
 *
@@ -777,6 +830,8 @@ void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider
        mState.tracks[name].downmixerBufferProvider->reset();
    } else if (mState.tracks[name].mPostDownmixReformatBufferProvider != NULL) {
        mState.tracks[name].mPostDownmixReformatBufferProvider->reset();
    } else if (mState.tracks[name].mTimestretchBufferProvider != NULL) {
        mState.tracks[name].mTimestretchBufferProvider->reset();
    }

    mState.tracks[name].mInputBufferProvider = bufferProvider;
+13 −0
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ public:
        RESAMPLE        = 0x3001,
        RAMP_VOLUME     = 0x3002, // ramp to new volume
        VOLUME          = 0x3003, // don't ramp
        TIMESTRETCH     = 0x3004,

        // set Parameter names
        // for target TRACK
@@ -100,6 +101,9 @@ public:
        VOLUME0         = 0x4200,
        VOLUME1         = 0x4201,
        AUXLEVEL        = 0x4210,
        // for target TIMESTRETCH
        PLAYBACK_RATE   = 0x4300, // Configure timestretch on this track name;
                                  // parameter 'value' is a pointer to the new playback rate.
    };


@@ -213,6 +217,9 @@ private:
        // 16-byte boundary

        /* Buffer providers are constructed to translate the track input data as needed.
         *
         * TODO: perhaps make a single PlaybackConverterProvider class to move
         * all pre-mixer track buffer conversions outside the AudioMixer class.
         *
         * 1) mInputBufferProvider: The AudioTrack buffer provider.
         * 2) mReformatBufferProvider: If not NULL, performs the audio reformat to
@@ -223,11 +230,13 @@ private:
         *    the number of channels required by the mixer sink.
         * 4) mPostDownmixReformatBufferProvider: If not NULL, performs reformatting from
         *    the downmixer requirements to the mixer engine input requirements.
         * 5) mTimestretchBufferProvider: Adds timestretching for playback rate
         */
        AudioBufferProvider*     mInputBufferProvider;    // externally provided buffer provider.
        PassthruBufferProvider*  mReformatBufferProvider; // provider wrapper for reformatting.
        PassthruBufferProvider*  downmixerBufferProvider; // wrapper for channel conversion.
        PassthruBufferProvider*  mPostDownmixReformatBufferProvider;
        PassthruBufferProvider*  mTimestretchBufferProvider;

        int32_t     sessionId;

@@ -250,6 +259,9 @@ private:
        audio_channel_mask_t mMixerChannelMask;
        uint32_t             mMixerChannelCount;

        float          mSpeed;
        float          mPitch;

        bool        needsRamp() { return (volumeInc[0] | volumeInc[1] | auxInc) != 0; }
        bool        setResampler(uint32_t trackSampleRate, uint32_t devSampleRate);
        bool        doesResample() const { return resampler != NULL; }
@@ -262,6 +274,7 @@ private:
        void        unprepareForDownmix();
        status_t    prepareForReformat();
        void        unprepareForReformat();
        bool        setPlaybackRate(float speed, float pitch);
        void        reconfigureBufferProviders();
    };

+162 −0
Original line number Diff line number Diff line
@@ -20,7 +20,9 @@
#include <audio_effects/effect_downmix.h>
#include <audio_utils/primitives.h>
#include <audio_utils/format.h>
#include <media/AudioResamplerPublic.h>
#include <media/EffectsFactoryApi.h>

#include <utils/Log.h>

#include "Configuration.h"
@@ -358,5 +360,165 @@ void ReformatBufferProvider::copyFrames(void *dst, const void *src, size_t frame
    memcpy_by_audio_format(dst, mOutputFormat, src, mInputFormat, frames * mChannelCount);
}

TimestretchBufferProvider::TimestretchBufferProvider(int32_t channelCount,
        audio_format_t format, uint32_t sampleRate, float speed, float pitch) :
        mChannelCount(channelCount),
        mFormat(format),
        mSampleRate(sampleRate),
        mFrameSize(channelCount * audio_bytes_per_sample(format)),
        mSpeed(speed),
        mPitch(pitch),
        mLocalBufferFrameCount(0),
        mLocalBufferData(NULL),
        mRemaining(0)
{
    ALOGV("TimestretchBufferProvider(%p)(%u, %#x, %u %f %f)",
            this, channelCount, format, sampleRate, speed, pitch);
    mBuffer.frameCount = 0;
}

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

status_t TimestretchBufferProvider::getNextBuffer(
        AudioBufferProvider::Buffer *pBuffer, int64_t pts)
{
    ALOGV("TimestretchBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)",
            this, pBuffer, pBuffer->frameCount, pts);

    // BYPASS
    //return mTrackBufferProvider->getNextBuffer(pBuffer, pts);

    // check if previously processed data is sufficient.
    if (pBuffer->frameCount <= mRemaining) {
        ALOGV("previous sufficient");
        pBuffer->raw = mLocalBufferData;
        return OK;
    }

    // do we need to resize our buffer?
    if (pBuffer->frameCount > mLocalBufferFrameCount) {
        void *newmem;
        if (posix_memalign(&newmem, 32, pBuffer->frameCount * mFrameSize) == OK) {
            if (mRemaining != 0) {
                memcpy(newmem, mLocalBufferData, mRemaining * mFrameSize);
            }
            free(mLocalBufferData);
            mLocalBufferData = newmem;
            mLocalBufferFrameCount = pBuffer->frameCount;
        }
    }

    // need to fetch more data
    const size_t outputDesired = pBuffer->frameCount - mRemaining;
    mBuffer.frameCount = mSpeed == AUDIO_TIMESTRETCH_SPEED_NORMAL
            ? outputDesired : outputDesired * mSpeed + 1;

    status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts);

    ALOG_ASSERT(res == OK || mBuffer.frameCount == 0);
    if (res != OK || mBuffer.frameCount == 0) { // not needed by API spec, but to be safe.
        ALOGD("buffer error");
        if (mRemaining == 0) {
            pBuffer->raw = NULL;
            pBuffer->frameCount = 0;
            return res;
        } else { // return partial count
            pBuffer->raw = mLocalBufferData;
            pBuffer->frameCount = mRemaining;
            return OK;
        }
    }

    // time-stretch the data
    size_t dstAvailable = min(mLocalBufferFrameCount - mRemaining, outputDesired);
    size_t srcAvailable = mBuffer.frameCount;
    processFrames((uint8_t*)mLocalBufferData + mRemaining * mFrameSize, &dstAvailable,
            mBuffer.raw, &srcAvailable);

    // release all data consumed
    mBuffer.frameCount = srcAvailable;
    mTrackBufferProvider->releaseBuffer(&mBuffer);

    // update buffer vars with the actual data processed and return with buffer
    mRemaining += dstAvailable;

    pBuffer->raw = mLocalBufferData;
    pBuffer->frameCount = mRemaining;

    return OK;
}

void TimestretchBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer)
{
    ALOGV("TimestretchBufferProvider(%p)::releaseBuffer(%p (%zu))",
       this, pBuffer, pBuffer->frameCount);

    // BYPASS
    //return mTrackBufferProvider->releaseBuffer(pBuffer);

    // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount");
    if (pBuffer->frameCount < mRemaining) {
        memcpy(mLocalBufferData,
                (uint8_t*)mLocalBufferData + pBuffer->frameCount * mFrameSize,
                (mRemaining - pBuffer->frameCount) * mFrameSize);
        mRemaining -= pBuffer->frameCount;
    } else if (pBuffer->frameCount == mRemaining) {
        mRemaining = 0;
    } else {
        LOG_ALWAYS_FATAL("Releasing more frames(%zu) than available(%zu)",
                pBuffer->frameCount, mRemaining);
    }

    pBuffer->raw = NULL;
    pBuffer->frameCount = 0;
}

void TimestretchBufferProvider::reset()
{
    mRemaining = 0;
}

status_t TimestretchBufferProvider::setPlaybackRate(float speed, float pitch)
{
    mSpeed = speed;
    mPitch = pitch;
    return OK;
}

void TimestretchBufferProvider::processFrames(void *dstBuffer, size_t *dstFrames,
        const void *srcBuffer, size_t *srcFrames)
{
    ALOGV("processFrames(%zu %zu)  remaining(%zu)", *dstFrames, *srcFrames, mRemaining);
    // Note dstFrames is the required number of frames.

    // Ensure consumption from src is as expected.
    const size_t targetSrc = *dstFrames * mSpeed;
    if (*srcFrames < targetSrc) { // limit dst frames to that possible
        *dstFrames = *srcFrames / mSpeed;
    } else if (*srcFrames > targetSrc + 1) {
        *srcFrames = targetSrc + 1;
    }

    // Do the time stretch by memory copy without any local buffer.
    if (*dstFrames <= *srcFrames) {
        size_t copySize = mFrameSize * *dstFrames;
        memcpy(dstBuffer, srcBuffer, copySize);
    } else {
        // cyclically repeat the source.
        for (size_t count = 0; count < *dstFrames; count += *srcFrames) {
            size_t remaining = min(*srcFrames, *dstFrames - count);
            memcpy((uint8_t*)dstBuffer + mFrameSize * count,
                    srcBuffer, mFrameSize * *srcFrames);
        }
    }
}

// ----------------------------------------------------------------------------
} // namespace android
+39 −0
Original line number Diff line number Diff line
@@ -146,6 +146,45 @@ protected:
    const audio_format_t mOutputFormat;
};

// TimestretchBufferProvider derives from PassthruBufferProvider for time stretching
class TimestretchBufferProvider : public PassthruBufferProvider {
public:
    TimestretchBufferProvider(int32_t channelCount,
            audio_format_t format, uint32_t sampleRate, float speed, float pitch);
    virtual ~TimestretchBufferProvider();

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

    // Overrides PassthruBufferProvider
    virtual void reset();

    virtual status_t setPlaybackRate(float speed, float pitch);

    // processes frames
    // dstBuffer is where to place the data
    // dstFrames [in/out] is the desired frames (return with actual placed in buffer)
    // srcBuffer is the source data
    // srcFrames [in/out] is the available source frames (return with consumed)
    virtual void processFrames(void *dstBuffer, size_t *dstFrames,
            const void *srcBuffer, size_t *srcFrames);

protected:
    const uint32_t       mChannelCount;
    const audio_format_t mFormat;
    const uint32_t       mSampleRate; // const for now (TODO change this)
    const size_t         mFrameSize;
    float                mSpeed;
    float                mPitch;

private:
    AudioBufferProvider::Buffer mBuffer;
    size_t               mLocalBufferFrameCount;
    void                *mLocalBufferData;
    size_t               mRemaining;
};

// ----------------------------------------------------------------------------
} // namespace android