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

Commit ed83b512 authored by Phil Burk's avatar Phil Burk Committed by android-build-merger
Browse files

Merge "aaudio: account for hardware jitter in clock model" into qt-r1-dev

am: 937b2d07

Change-Id: I31d1287b199bef2519ea811674bc576aef8fe55c
parents 32054310 937b2d07
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -89,7 +89,11 @@ aaudio_result_t AudioStreamInternalCapture::processDataNow(void *buffer, int32_t
    if (mAudioEndpoint.isFreeRunning()) {
        //ALOGD("AudioStreamInternalCapture::processDataNow() - update remote counter");
        // Update data queue based on the timing model.
        int64_t estimatedRemoteCounter = mClockModel.convertTimeToPosition(currentNanoTime);
        // Jitter in the DSP can cause late writes to the FIFO.
        // This might be caused by resampling.
        // We want to read the FIFO after the latest possible time
        // that the DSP could have written the data.
        int64_t estimatedRemoteCounter = mClockModel.convertLatestTimeToPosition(currentNanoTime);
        // TODO refactor, maybe use setRemoteCounter()
        mAudioEndpoint.setDataWriteCounter(estimatedRemoteCounter);
    }
@@ -139,7 +143,7 @@ aaudio_result_t AudioStreamInternalCapture::processDataNow(void *buffer, int32_t
                // the writeCounter might have just advanced in the background,
                // causing us to sleep until a later burst.
                int64_t nextPosition = mAudioEndpoint.getDataReadCounter() + mFramesPerBurst;
                wakeTime = mClockModel.convertPositionToTime(nextPosition);
                wakeTime = mClockModel.convertPositionToLatestTime(nextPosition);
            }
                break;
            default:
+76 −26
Original line number Diff line number Diff line
@@ -19,12 +19,11 @@
#include <log/log.h>

#include <stdint.h>
#include <algorithm>

#include "utility/AudioClock.h"
#include "IsochronousClockModel.h"

#define MIN_LATENESS_NANOS (10 * AAUDIO_NANOS_PER_MICROSECOND)

using namespace aaudio;

IsochronousClockModel::IsochronousClockModel()
@@ -32,7 +31,7 @@ IsochronousClockModel::IsochronousClockModel()
        , mMarkerNanoTime(0)
        , mSampleRate(48000)
        , mFramesPerBurst(64)
        , mMaxLatenessInNanos(0)
        , mMaxMeasuredLatenessNanos(0)
        , mState(STATE_STOPPED)
{
}
@@ -41,8 +40,7 @@ IsochronousClockModel::~IsochronousClockModel() {
}

void IsochronousClockModel::setPositionAndTime(int64_t framePosition, int64_t nanoTime) {
    ALOGV("setPositionAndTime(%lld, %lld)",
          (long long) framePosition, (long long) nanoTime);
    ALOGV("setPositionAndTime, %lld, %lld", (long long) framePosition, (long long) nanoTime);
    mMarkerFramePosition = framePosition;
    mMarkerNanoTime = nanoTime;
}
@@ -54,7 +52,9 @@ void IsochronousClockModel::start(int64_t nanoTime) {
}

void IsochronousClockModel::stop(int64_t nanoTime) {
    ALOGV("stop(nanos = %lld)\n", (long long) nanoTime);
    ALOGD("stop(nanos = %lld) max lateness = %d micros\n",
        (long long) nanoTime,
        (int) (mMaxMeasuredLatenessNanos / 1000));
    setPositionAndTime(convertTimeToPosition(nanoTime), nanoTime);
    // TODO should we set position?
    mState = STATE_STOPPED;
@@ -69,9 +69,10 @@ bool IsochronousClockModel::isRunning() const {
}

void IsochronousClockModel::processTimestamp(int64_t framePosition, int64_t nanoTime) {
//    ALOGD("processTimestamp() - framePosition = %lld at nanoTime %llu",
//         (long long)framePosition,
//         (long long)nanoTime);
    mTimestampCount++;
// Log position and time in CSV format so we can import it easily into spreadsheets.
    //ALOGD("%s() CSV, %d, %lld, %lld", __func__,
          //mTimestampCount, (long long)framePosition, (long long)nanoTime);
    int64_t framesDelta = framePosition - mMarkerFramePosition;
    int64_t nanosDelta = nanoTime - mMarkerNanoTime;
    if (nanosDelta < 1000) {
@@ -112,20 +113,52 @@ void IsochronousClockModel::processTimestamp(int64_t framePosition, int64_t nano
            // Or we may be drifting due to a fast HW clock.
            //int microsDelta = (int) (nanosDelta / 1000);
            //int expectedMicrosDelta = (int) (expectedNanosDelta / 1000);
//            ALOGD("processTimestamp() - STATE_RUNNING - %7d < %7d so %4d micros EARLY",
//                 microsDelta, expectedMicrosDelta, (expectedMicrosDelta - microsDelta));
            //ALOGD("%s() - STATE_RUNNING - #%d, %4d micros EARLY",
                //__func__, mTimestampCount, expectedMicrosDelta - microsDelta);

            setPositionAndTime(framePosition, nanoTime);
        } else if (nanosDelta > (expectedNanosDelta + mMaxLatenessInNanos)) {
            // Later than expected timestamp.
//            int microsDelta = (int) (nanosDelta / 1000);
//            int expectedMicrosDeadline = (int) ((expectedNanosDelta + mMaxLatenessInNanos) / 1000);
//            ALOGD("processTimestamp() - STATE_RUNNING - %7d > %7d so %4d micros LATE",
//                  microsDelta, expectedMicrosDeadline, (microsDelta - expectedMicrosDeadline));

            // When we are late it may be because of preemption in the kernel or
            //  we may be drifting due to a slow HW clock.
            setPositionAndTime(framePosition,  nanoTime - mMaxLatenessInNanos);
        } else if (nanosDelta > (expectedNanosDelta + (2 * mBurstPeriodNanos))) {
            // In this case we do not update mMaxMeasuredLatenessNanos because it
            // would force it too high.
            // mMaxMeasuredLatenessNanos should range from 1 to 2 * mBurstPeriodNanos
            //int32_t measuredLatenessNanos = (int32_t)(nanosDelta - expectedNanosDelta);
            //ALOGD("%s() - STATE_RUNNING - #%d, lateness %d - max %d = %4d micros VERY LATE",
                  //__func__,
                  //mTimestampCount,
                  //measuredLatenessNanos / 1000,
                  //mMaxMeasuredLatenessNanos / 1000,
                  //(measuredLatenessNanos - mMaxMeasuredLatenessNanos) / 1000
                  //);

            // This typically happens when we are modelling a service instead of a DSP.
            setPositionAndTime(framePosition,  nanoTime - (2 * mBurstPeriodNanos));
        } else if (nanosDelta > (expectedNanosDelta + mMaxMeasuredLatenessNanos)) {
            //int32_t previousLatenessNanos = mMaxMeasuredLatenessNanos;
            mMaxMeasuredLatenessNanos = (int32_t)(nanosDelta - expectedNanosDelta);

            //ALOGD("%s() - STATE_RUNNING - #%d, newmax %d - oldmax %d = %4d micros LATE",
                  //__func__,
                  //mTimestampCount,
                  //mMaxMeasuredLatenessNanos / 1000,
                  //previousLatenessNanos / 1000,
                  //(mMaxMeasuredLatenessNanos - previousLatenessNanos) / 1000
                  //);

            // When we are late, it may be because of preemption in the kernel,
            // or timing jitter caused by resampling in the DSP,
            // or we may be drifting due to a slow HW clock.
            // We add slight drift value just in case there is actual long term drift
            // forward caused by a slower clock.
            // If the clock is faster than the model will get pushed earlier
            // by the code in the preceding branch.
            // The two opposing forces should allow the model to track the real clock
            // over a long time.
            int64_t driftingTime = mMarkerNanoTime + expectedNanosDelta + kDriftNanos;
            setPositionAndTime(framePosition,  driftingTime);
            //ALOGD("%s() - #%d, max lateness = %d micros",
                  //__func__,
                  //mTimestampCount,
                  //(int) (mMaxMeasuredLatenessNanos / 1000));
        }
        break;
    default:
@@ -145,9 +178,12 @@ void IsochronousClockModel::setFramesPerBurst(int32_t framesPerBurst) {
    update();
}

// Update expected lateness based on sampleRate and framesPerBurst
void IsochronousClockModel::update() {
    int64_t nanosLate = convertDeltaPositionToTime(mFramesPerBurst); // uses mSampleRate
    mMaxLatenessInNanos = (nanosLate > MIN_LATENESS_NANOS) ? nanosLate : MIN_LATENESS_NANOS;
    mBurstPeriodNanos = convertDeltaPositionToTime(mFramesPerBurst); // uses mSampleRate
    // Timestamps may be late by up to a burst because we are randomly sampling the time period
    // after the DSP position is actually updated.
    mMaxMeasuredLatenessNanos = mBurstPeriodNanos;
}

int64_t IsochronousClockModel::convertDeltaPositionToTime(int64_t framesDelta) const {
@@ -190,11 +226,25 @@ int64_t IsochronousClockModel::convertTimeToPosition(int64_t nanoTime) const {
    return position;
}

int32_t IsochronousClockModel::getLateTimeOffsetNanos() const {
    // This will never be < 0 because mMaxLatenessNanos starts at
    // mBurstPeriodNanos and only gets bigger.
    return (mMaxMeasuredLatenessNanos - mBurstPeriodNanos) + kExtraLatenessNanos;
}

int64_t IsochronousClockModel::convertPositionToLatestTime(int64_t framePosition) const {
    return convertPositionToTime(framePosition) + getLateTimeOffsetNanos();
}

int64_t IsochronousClockModel::convertLatestTimeToPosition(int64_t nanoTime) const {
    return convertTimeToPosition(nanoTime - getLateTimeOffsetNanos());
}

void IsochronousClockModel::dump() const {
    ALOGD("mMarkerFramePosition = %lld", (long long) mMarkerFramePosition);
    ALOGD("mMarkerNanoTime      = %lld", (long long) mMarkerNanoTime);
    ALOGD("mSampleRate          = %6d", mSampleRate);
    ALOGD("mFramesPerBurst      = %6d", mFramesPerBurst);
    ALOGD("mMaxLatenessInNanos  = %6d", mMaxLatenessInNanos);
    ALOGD("mMaxMeasuredLatenessNanos = %6d", mMaxMeasuredLatenessNanos);
    ALOGD("mState               = %6d", mState);
}
+36 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#define ANDROID_AAUDIO_ISOCHRONOUS_CLOCK_MODEL_H

#include <stdint.h>
#include "utility/AudioClock.h"

namespace aaudio {

@@ -78,6 +79,15 @@ public:
     */
    int64_t convertPositionToTime(int64_t framePosition) const;

    /**
     * Calculate the latest estimated time that the stream will be at that position.
     * The more jittery the clock is then the later this will be.
     *
     * @param framePosition
     * @return time in nanoseconds
     */
    int64_t convertPositionToLatestTime(int64_t framePosition) const;

    /**
     * Calculate an estimated position where the stream will be at the specified time.
     *
@@ -86,6 +96,18 @@ public:
     */
    int64_t convertTimeToPosition(int64_t nanoTime) const;

    /**
     * Calculate the corresponding estimated position based on the specified time being
     * the latest possible time.
     *
     * For the same nanoTime, this may return an earlier position than
     * convertTimeToPosition().
     *
     * @param nanoTime
     * @return position in frames
     */
    int64_t convertLatestTimeToPosition(int64_t nanoTime) const;

    /**
     * @param framesDelta difference in frames
     * @return duration in nanoseconds
@@ -101,6 +123,9 @@ public:
    void dump() const;

private:

    int32_t getLateTimeOffsetNanos() const;

    enum clock_model_state_t {
        STATE_STOPPED,
        STATE_STARTING,
@@ -108,13 +133,23 @@ private:
        STATE_RUNNING
    };

    // Amount of time to drift forward when we get a late timestamp.
    // This value was calculated to allow tracking of a clock with 50 ppm error.
    static constexpr int32_t   kDriftNanos         =  10 * 1000;
    // TODO review value of kExtraLatenessNanos
    static constexpr int32_t   kExtraLatenessNanos = 100 * 1000;

    int64_t             mMarkerFramePosition;
    int64_t             mMarkerNanoTime;
    int32_t             mSampleRate;
    int32_t             mFramesPerBurst;
    int32_t             mMaxLatenessInNanos;
    int32_t             mBurstPeriodNanos;
    // Includes mBurstPeriodNanos because we sample randomly over time.
    int32_t             mMaxMeasuredLatenessNanos;
    clock_model_state_t mState;

    int32_t             mTimestampCount = 0;

    void update();
};