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

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

Merge "Add playback rate to AudioTrack"

parents e62e1637 8edb8dc4
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -42,6 +42,8 @@
#define AUDIO_TIMESTRETCH_PITCH_MAX    2.0f
#define AUDIO_TIMESTRETCH_PITCH_NORMAL 1.0f

// TODO: Consider putting these inlines into a class scope

// 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.
@@ -66,4 +68,13 @@ static inline size_t destinationFramesPossible(size_t srcFrames, uint32_t srcSam
    return dstFrames > 2 ? dstFrames - 2 : 0;
}

static inline size_t sourceFramesNeededWithTimestretch(
        uint32_t srcSampleRate, size_t dstFramesRequired, uint32_t dstSampleRate,
        float speed) {
    // required is the number of input frames the resampler needs
    size_t required = sourceFramesNeeded(srcSampleRate, dstFramesRequired, dstSampleRate);
    // to deliver this, the time stretcher requires:
    return required * (double)speed + 1 + 1; // accounting for rounding dependencies
}

#endif // ANDROID_AUDIO_RESAMPLER_PUBLIC_H
+20 −0
Original line number Diff line number Diff line
@@ -359,6 +359,21 @@ public:
    /* Return current source sample rate in Hz */
            uint32_t    getSampleRate() const;

    /* Set source playback rate for timestretch
     * 1.0 is normal speed: < 1.0 is slower, > 1.0 is faster
     * 1.0 is normal pitch: < 1.0 is lower pitch, > 1.0 is higher pitch
     *
     * AUDIO_TIMESTRETCH_SPEED_MIN <= speed <= AUDIO_TIMESTRETCH_SPEED_MAX
     * AUDIO_TIMESTRETCH_PITCH_MIN <= pitch <= AUDIO_TIMESTRETCH_PITCH_MAX
     *
     * Speed increases the playback rate of media, but does not alter pitch.
     * Pitch increases the "tonal frequency" of media, but does not affect the playback rate.
     */
            status_t    setPlaybackRate(float speed, float pitch);

    /* Return current playback rate */
            void        getPlaybackRate(float *speed, float *pitch) const;

    /* Enables looping and sets the start and end points of looping.
     * Only supported for static buffer mode.
     *
@@ -719,6 +734,9 @@ protected:
            // increment mPosition by the delta of mServer, and return new value of mPosition
            uint32_t updateAndGetPosition_l();

            // check sample rate and speed is compatible with AudioTrack
            bool     isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed) const;

    // Next 4 fields may be changed if IAudioTrack is re-created, but always != 0
    sp<IAudioTrack>         mAudioTrack;
    sp<IMemory>             mCblkMemory;
@@ -730,6 +748,8 @@ protected:
    float                   mVolume[2];
    float                   mSendLevel;
    mutable uint32_t        mSampleRate;            // mutable because getSampleRate() can update it
    float                   mSpeed;                 // timestretch: 1.0f for normal speed.
    float                   mPitch;                 // timestretch: 1.0f for normal pitch.
    size_t                  mFrameCount;            // corresponds to current IAudioTrack, value is
                                                    // reported back by AudioFlinger to the client
    size_t                  mReqFrameCount;         // frame count to request the first or next time
+34 −2
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include <utils/Log.h>
#include <utils/RefBase.h>
#include <audio_utils/roundup.h>
#include <media/AudioResamplerPublic.h>
#include <media/SingleStateQueue.h>

namespace android {
@@ -113,6 +114,14 @@ struct AudioTrackSharedStatic {
                    mPosLoopQueue;
};


struct AudioTrackPlaybackRate {
    float mSpeed;
    float mPitch;
};

typedef SingleStateQueue<AudioTrackPlaybackRate> AudioTrackPlaybackRateQueue;

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

// Important: do not add any virtual methods, including ~
@@ -159,6 +168,8 @@ private:
                uint32_t    mSampleRate;    // AudioTrack only: client's requested sample rate in Hz
                                            // or 0 == default. Write-only client, read-only server.

                AudioTrackPlaybackRateQueue::Shared mPlaybackRateQueue;

                // client write-only, server read-only
                uint16_t    mSendLevel;      // Fixed point U4.12 so 0x1000 means 1.0

@@ -313,7 +324,8 @@ public:
    AudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
            size_t frameSize, bool clientInServer = false)
        : ClientProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/,
          clientInServer) { }
          clientInServer),
          mPlaybackRateMutator(&cblk->mPlaybackRateQueue) { }
    virtual ~AudioTrackClientProxy() { }

    // No barriers on the following operations, so the ordering of loads/stores
@@ -333,6 +345,13 @@ public:
        mCblk->mSampleRate = sampleRate;
    }

    void        setPlaybackRate(float speed, float pitch) {
        AudioTrackPlaybackRate playbackRate;
        playbackRate.mSpeed = speed;
        playbackRate.mPitch = pitch;
        mPlaybackRateMutator.push(playbackRate);
    }

    virtual void flush();

    virtual uint32_t    getUnderrunFrames() const {
@@ -344,6 +363,9 @@ public:
    bool        getStreamEndDone() const;

    status_t    waitStreamEndDone(const struct timespec *requested);

private:
    AudioTrackPlaybackRateQueue::Mutator   mPlaybackRateMutator;
};

class StaticAudioTrackClientProxy : public AudioTrackClientProxy {
@@ -458,8 +480,11 @@ class AudioTrackServerProxy : public ServerProxy {
public:
    AudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
            size_t frameSize, bool clientInServer = false, uint32_t sampleRate = 0)
        : ServerProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/, clientInServer) {
        : ServerProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/, clientInServer),
          mPlaybackRateObserver(&cblk->mPlaybackRateQueue) {
        mCblk->mSampleRate = sampleRate;
        mPlaybackRate.mSpeed = AUDIO_TIMESTRETCH_SPEED_NORMAL;
        mPlaybackRate.mPitch = AUDIO_TIMESTRETCH_PITCH_NORMAL;
    }
protected:
    virtual ~AudioTrackServerProxy() { }
@@ -493,6 +518,13 @@ public:

    // Return the total number of frames that AudioFlinger has obtained and released
    virtual size_t      framesReleased() const { return mCblk->mServer; }

    // Return the playback speed and pitch read atomically. Not multi-thread safe on server side.
    void                getPlaybackRate(float *speed, float *pitch);

private:
    AudioTrackPlaybackRate                  mPlaybackRate;  // last observed playback rate
    AudioTrackPlaybackRateQueue::Observer   mPlaybackRateObserver;
};

class StaticAudioTrackServerProxy : public AudioTrackServerProxy {
+118 −15
Original line number Diff line number Diff line
@@ -56,6 +56,24 @@ static int64_t getNowUs()
    return convertTimespecToUs(tv);
}

// Must match similar computation in createTrack_l in Threads.cpp.
// TODO: Move to a common library
static size_t calculateMinFrameCount(
        uint32_t afLatencyMs, uint32_t afFrameCount, uint32_t afSampleRate,
        uint32_t sampleRate, float speed)
{
    // Ensure that buffer depth covers at least audio hardware latency
    uint32_t minBufCount = afLatencyMs / ((1000 * afFrameCount) / afSampleRate);
    if (minBufCount < 2) {
        minBufCount = 2;
    }
    ALOGV("calculateMinFrameCount afLatency %u  afFrameCount %u  afSampleRate %u  "
            "sampleRate %u  speed %f  minBufCount: %u",
            afLatencyMs, afFrameCount, afSampleRate, sampleRate, speed, minBufCount);
    return minBufCount * sourceFramesNeededWithTimestretch(
            sampleRate, afFrameCount, afSampleRate, speed);
}

// static
status_t AudioTrack::getMinFrameCount(
        size_t* frameCount,
@@ -94,13 +112,10 @@ status_t AudioTrack::getMinFrameCount(
        return status;
    }

    // Ensure that buffer depth covers at least audio hardware latency
    uint32_t minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate);
    if (minBufCount < 2) {
        minBufCount = 2;
    }
    // When called from createTrack, speed is 1.0f (normal speed).
    // This is rechecked again on setting playback rate (TODO: on setting sample rate, too).
    *frameCount = calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, 1.0f);

    *frameCount = minBufCount * sourceFramesNeeded(sampleRate, afFrameCount, afSampleRate);
    // The formula above should always produce a non-zero value under normal circumstances:
    // AudioTrack.SAMPLE_RATE_HZ_MIN <= sampleRate <= AudioTrack.SAMPLE_RATE_HZ_MAX.
    // Return error in the unlikely event that it does not, as that's part of the API contract.
@@ -109,8 +124,8 @@ status_t AudioTrack::getMinFrameCount(
                streamType, sampleRate);
        return BAD_VALUE;
    }
    ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, minBufCount=%u, afSampleRate=%u, afLatency=%u",
            *frameCount, afFrameCount, minBufCount, afSampleRate, afLatency);
    ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, afSampleRate=%u, afLatency=%u",
            *frameCount, afFrameCount, afSampleRate, afLatency);
    return NO_ERROR;
}

@@ -360,6 +375,8 @@ status_t AudioTrack::set(
        return BAD_VALUE;
    }
    mSampleRate = sampleRate;
    mSpeed = AUDIO_TIMESTRETCH_SPEED_NORMAL;
    mPitch = AUDIO_TIMESTRETCH_PITCH_NORMAL;

    // Make copy of input parameter offloadInfo so that in the future:
    //  (a) createTrack_l doesn't need it as an input parameter
@@ -689,6 +706,7 @@ status_t AudioTrack::setSampleRate(uint32_t rate)
    if (rate == 0 || rate > afSamplingRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX) {
        return BAD_VALUE;
    }
    // TODO: Should we also check if the buffer size is compatible?

    mSampleRate = rate;
    mProxy->setSampleRate(rate);
@@ -719,6 +737,42 @@ uint32_t AudioTrack::getSampleRate() const
    return mSampleRate;
}

status_t AudioTrack::setPlaybackRate(float speed, float pitch)
{
    if (speed < AUDIO_TIMESTRETCH_SPEED_MIN
            || speed > AUDIO_TIMESTRETCH_SPEED_MAX
            || pitch < AUDIO_TIMESTRETCH_PITCH_MIN
            || pitch > AUDIO_TIMESTRETCH_PITCH_MAX) {
        return BAD_VALUE;
    }
    AutoMutex lock(mLock);
    if (speed == mSpeed && pitch == mPitch) {
        return NO_ERROR;
    }
    if (mIsTimed || isOffloadedOrDirect_l()) {
        return INVALID_OPERATION;
    }
    if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
        return INVALID_OPERATION;
    }
    // Check if the buffer size is compatible.
    if (!isSampleRateSpeedAllowed_l(mSampleRate, speed)) {
        ALOGV("setPlaybackRate(%f, %f) failed", speed, pitch);
        return BAD_VALUE;
    }
    mSpeed = speed;
    mPitch = pitch;
    mProxy->setPlaybackRate(speed, pitch);
    return NO_ERROR;
}

void AudioTrack::getPlaybackRate(float *speed, float *pitch) const
{
    AutoMutex lock(mLock);
    *speed = mSpeed;
    *pitch = mPitch;
}

status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount)
{
    if (mSharedBuffer == 0 || mIsTimed || isOffloadedOrDirect()) {
@@ -1086,8 +1140,16 @@ status_t AudioTrack::createTrack_l()
        // there _is_ a frameCount parameter.  We silently ignore it.
        frameCount = mSharedBuffer->size() / mFrameSize;
    } else {
        // For fast and normal streaming tracks,
        // the frame count calculations and checks are done by server
        // For fast tracks the frame count calculations and checks are done by server

        if ((mFlags & AUDIO_OUTPUT_FLAG_FAST) == 0) {
            // for normal tracks precompute the frame count based on speed.
            const size_t minFrameCount = calculateMinFrameCount(
                    afLatency, afFrameCount, afSampleRate, mSampleRate, mSpeed);
            if (frameCount < minFrameCount) {
                frameCount = minFrameCount;
            }
        }
    }

    IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT;
@@ -1230,6 +1292,7 @@ status_t AudioTrack::createTrack_l()
    }

    mAudioTrack->attachAuxEffect(mAuxEffectId);
    // FIXME doesn't take into account speed or future sample rate changes (until restoreTrack)
    // FIXME don't believe this lie
    mLatency = afLatency + (1000*frameCount) / mSampleRate;

@@ -1255,6 +1318,7 @@ status_t AudioTrack::createTrack_l()

    mProxy->setSendLevel(mSendLevel);
    mProxy->setSampleRate(mSampleRate);
    mProxy->setPlaybackRate(mSpeed, mPitch);
    mProxy->setMinimum(mNotificationFramesAct);

    mDeathNotifier = new DeathNotifier(this);
@@ -1617,6 +1681,7 @@ nsecs_t AudioTrack::processAudioBuffer()

    // Cache other fields that will be needed soon
    uint32_t sampleRate = mSampleRate;
    float speed = mSpeed;
    uint32_t notificationFrames = mNotificationFramesAct;
    if (mRefreshRemaining) {
        mRefreshRemaining = false;
@@ -1745,7 +1810,7 @@ nsecs_t AudioTrack::processAudioBuffer()
    if (minFrames != (uint32_t) ~0) {
        // This "fudge factor" avoids soaking CPU, and compensates for late progress by server
        static const nsecs_t kFudgeNs = 10000000LL; // 10 ms
        ns = ((minFrames * 1000000000LL) / sampleRate) + kFudgeNs;
        ns = ((double)minFrames * 1000000000) / ((double)sampleRate * speed) + kFudgeNs;
    }

    // If not supplying data by EVENT_MORE_DATA, then we're done
@@ -1786,7 +1851,8 @@ nsecs_t AudioTrack::processAudioBuffer()
        if (mRetryOnPartialBuffer && !isOffloaded()) {
            mRetryOnPartialBuffer = false;
            if (avail < mRemainingFrames) {
                int64_t myns = ((mRemainingFrames - avail) * 1100000000LL) / sampleRate;
                int64_t myns = ((double)(mRemainingFrames - avail) * 1100000000)
                        / ((double)sampleRate * speed);
                if (ns < 0 || myns < ns) {
                    ns = myns;
                }
@@ -1841,7 +1907,7 @@ nsecs_t AudioTrack::processAudioBuffer()
        // that total to a sum == notificationFrames.
        if (0 < misalignment && misalignment <= mRemainingFrames) {
            mRemainingFrames = misalignment;
            return (mRemainingFrames * 1100000000LL) / sampleRate;
            return ((double)mRemainingFrames * 1100000000) / ((double)sampleRate * speed);
        }
#endif

@@ -1936,6 +2002,41 @@ uint32_t AudioTrack::updateAndGetPosition_l()
    return mPosition += (uint32_t) delta;
}

bool AudioTrack::isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed) const
{
    // applicable for mixing tracks only (not offloaded or direct)
    if (mStaticProxy != 0) {
        return true; // static tracks do not have issues with buffer sizing.
    }
    status_t status;
    uint32_t afLatency;
    status = AudioSystem::getLatency(mOutput, &afLatency);
    if (status != NO_ERROR) {
        ALOGE("getLatency(%d) failed status %d", mOutput, status);
        return false;
    }

    size_t afFrameCount;
    status = AudioSystem::getFrameCount(mOutput, &afFrameCount);
    if (status != NO_ERROR) {
        ALOGE("getFrameCount(output=%d) status %d", mOutput, status);
        return false;
    }

    uint32_t afSampleRate;
    status = AudioSystem::getSamplingRate(mOutput, &afSampleRate);
    if (status != NO_ERROR) {
        ALOGE("getSamplingRate(output=%d) status %d", mOutput, status);
        return false;
    }

    const size_t minFrameCount =
            calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, speed);
    ALOGV("isSampleRateSpeedAllowed_l mFrameCount %zu  minFrameCount %zu",
            mFrameCount, minFrameCount);
    return mFrameCount >= minFrameCount;
}

status_t AudioTrack::setParameters(const String8& keyValuePairs)
{
    AutoMutex lock(mLock);
@@ -2001,7 +2102,8 @@ status_t AudioTrack::getTimestamp(AudioTimestamp& timestamp)
                    return WOULD_BLOCK;  // stale timestamp time, occurs before start.
                }
                const int64_t deltaTimeUs = timestampTimeUs - mStartUs;
                const int64_t deltaPositionByUs = timestamp.mPosition * 1000000LL / mSampleRate;
                const int64_t deltaPositionByUs = (double)timestamp.mPosition * 1000000
                        / ((double)mSampleRate * mSpeed);

                if (deltaPositionByUs > deltaTimeUs + kTimeJitterUs) {
                    // Verify that the counter can't count faster than the sample rate
@@ -2088,7 +2190,8 @@ status_t AudioTrack::dump(int fd, const Vector<String16>& args __unused) const
    snprintf(buffer, 255, "  format(%d), channel count(%d), frame count(%zu)\n", mFormat,
            mChannelCount, mFrameCount);
    result.append(buffer);
    snprintf(buffer, 255, "  sample rate(%u), status(%d)\n", mSampleRate, mStatus);
    snprintf(buffer, 255, "  sample rate(%u), speed(%f), status(%d)\n",
            mSampleRate, mSpeed, mStatus);
    result.append(buffer);
    snprintf(buffer, 255, "  state(%d), latency (%d)\n", mState, mLatency);
    result.append(buffer);
+10 −0
Original line number Diff line number Diff line
@@ -793,6 +793,16 @@ void AudioTrackServerProxy::tallyUnderrunFrames(uint32_t frameCount)
    (void) android_atomic_or(CBLK_UNDERRUN, &cblk->mFlags);
}

void AudioTrackServerProxy::getPlaybackRate(float *speed, float *pitch)
{   // do not call from multiple threads without holding lock
    AudioTrackPlaybackRate playbackRate;
    if (mPlaybackRateObserver.poll(playbackRate)) {
        mPlaybackRate = playbackRate;
    }
    *speed = mPlaybackRate.mSpeed;
    *pitch = mPlaybackRate.mPitch;
}

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

StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers,
Loading