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

Commit b31b66f3 authored by Phil Burk's avatar Phil Burk
Browse files

aaudio: offset MMAP timestamps

This can be used to debug and analyze problems
with the AAudio MMAP IsochronousClockModel.
That is a model of the DSP that is used by AAudio
to predict the read/write timing of the DSP.
If there is an error in the model or in the timestamps
then the DSP and CPU pointers can cross, which causes
an audio glitch.
By changing the offset we can force glitches and indirectly
measure the time distribution of the DSP transfers.

Test: adb shell setprop aaudio.out_mmap_offset_usec
Bug: 123096058
Change-Id: I9a3df345a6820baf20a24d261642b3c8c0c2a27e
parent 3b93ca58
Loading
Loading
Loading
Loading
+20 −1
Original line number Diff line number Diff line
@@ -232,6 +232,24 @@ aaudio_result_t AudioStreamInternal::open(const AudioStreamBuilder &builder) {
        mCallbackBuffer = new uint8_t[callbackBufferSize];
    }

    // For debugging and analyzing the distribution of MMAP timestamps.
    // For OUTPUT, use a NEGATIVE offset to move the CPU writes further BEFORE the HW reads.
    // For INPUT, use a POSITIVE offset to move the CPU reads further AFTER the HW writes.
    // You can use this offset to reduce glitching.
    // You can also use this offset to force glitching. By iterating over multiple
    // values you can reveal the distribution of the hardware timing jitter.
    if (mAudioEndpoint.isFreeRunning()) { // MMAP?
        int32_t offsetMicros = (getDirection() == AAUDIO_DIRECTION_OUTPUT)
                ? AAudioProperty_getOutputMMapOffsetMicros()
                : AAudioProperty_getInputMMapOffsetMicros();
        // This log is used to debug some tricky glitch issues. Please leave.
        ALOGD_IF(offsetMicros, "%s() - %s mmap offset = %d micros",
                __func__,
                (getDirection() == AAUDIO_DIRECTION_OUTPUT) ? "output" : "input",
                offsetMicros);
        mTimeOffsetNanos = offsetMicros * AAUDIO_NANOS_PER_MICROSECOND;
    }

    setState(AAUDIO_STREAM_STATE_OPEN);

    return result;
@@ -478,7 +496,8 @@ aaudio_result_t AudioStreamInternal::onTimestampService(AAudioServiceMessage *me
#if LOG_TIMESTAMPS
    logTimestamp(*message);
#endif
    processTimestamp(message->timestamp.position, message->timestamp.timestamp);
    processTimestamp(message->timestamp.position,
            message->timestamp.timestamp + mTimeOffsetNanos);
    return AAUDIO_OK;
}

+1 −0
Original line number Diff line number Diff line
@@ -194,6 +194,7 @@ private:
    // By delaying slightly we can avoid waking up before other side is ready.
    const int32_t            mWakeupDelayNanos; // delay past typical wakeup jitter
    const int32_t            mMinimumSleepNanos; // minimum sleep while polling
    int32_t                  mTimeOffsetNanos = 0; // add to time part of an MMAP timestamp

    AudioEndpointParcelable  mEndPointParcelable; // description of the buffers filled by service
    EndpointDescriptor       mEndpointDescriptor; // buffer description with resolved addresses
+24 −0
Original line number Diff line number Diff line
@@ -335,6 +335,30 @@ int32_t AAudioProperty_getHardwareBurstMinMicros() {
    return prop;
}

static int32_t AAudioProperty_getMMapOffsetMicros(const char *functionName,
        const char *propertyName) {
    const int32_t minMicros = -20000; // arbitrary
    const int32_t defaultMicros = 0;  // arbitrary
    const int32_t maxMicros =  20000; // arbitrary
    int32_t prop = property_get_int32(propertyName, defaultMicros);
    if (prop < minMicros) {
        ALOGW("%s: clipped %d to %d", functionName, prop, minMicros);
        prop = minMicros;
    } else if (prop > maxMicros) {
        ALOGW("%s: clipped %d to %d", functionName, prop, minMicros);
        prop = maxMicros;
    }
    return prop;
}

int32_t AAudioProperty_getInputMMapOffsetMicros() {
    return AAudioProperty_getMMapOffsetMicros(__func__, AAUDIO_PROP_INPUT_MMAP_OFFSET_USEC);
}

int32_t AAudioProperty_getOutputMMapOffsetMicros() {
    return AAudioProperty_getMMapOffsetMicros(__func__, AAUDIO_PROP_OUTPUT_MMAP_OFFSET_USEC);
}

aaudio_result_t AAudio_isFlushAllowed(aaudio_stream_state_t state) {
    aaudio_result_t result = AAUDIO_OK;
    switch (state) {
+17 −10
Original line number Diff line number Diff line
@@ -94,31 +94,26 @@ audio_flags_mask_t AAudioConvert_allowCapturePolicyToAudioFlagsMask(

// Note that this code may be replaced by Settings or by some other system configuration tool.

#define AAUDIO_PROP_MMAP_POLICY           "aaudio.mmap_policy"

/**
 * Read system property.
 * @return AAUDIO_UNSPECIFIED, AAUDIO_POLICY_NEVER or AAUDIO_POLICY_AUTO or AAUDIO_POLICY_ALWAYS
 */
int32_t AAudioProperty_getMMapPolicy();

#define AAUDIO_PROP_MMAP_EXCLUSIVE_POLICY "aaudio.mmap_exclusive_policy"
#define AAUDIO_PROP_MMAP_POLICY           "aaudio.mmap_policy"

/**
 * Read system property.
 * @return AAUDIO_UNSPECIFIED, AAUDIO_POLICY_NEVER or AAUDIO_POLICY_AUTO or AAUDIO_POLICY_ALWAYS
 */
int32_t AAudioProperty_getMMapExclusivePolicy();

#define AAUDIO_PROP_MIXER_BURSTS           "aaudio.mixer_bursts"
#define AAUDIO_PROP_MMAP_EXCLUSIVE_POLICY "aaudio.mmap_exclusive_policy"

/**
 * Read system property.
 * @return number of bursts per AAudio service mixer cycle
 */
int32_t AAudioProperty_getMixerBursts();

#define AAUDIO_PROP_HW_BURST_MIN_USEC      "aaudio.hw_burst_min_usec"
#define AAUDIO_PROP_MIXER_BURSTS           "aaudio.mixer_bursts"

/**
 * Read a system property that specifies the number of extra microseconds that a thread
@@ -130,7 +125,6 @@ int32_t AAudioProperty_getMixerBursts();
 * @return number of microseconds to delay the wakeup.
 */
int32_t AAudioProperty_getWakeupDelayMicros();

#define AAUDIO_PROP_WAKEUP_DELAY_USEC      "aaudio.wakeup_delay_usec"

/**
@@ -139,7 +133,6 @@ int32_t AAudioProperty_getWakeupDelayMicros();
 * @return minimum number of microseconds to sleep.
 */
int32_t AAudioProperty_getMinimumSleepMicros();

#define AAUDIO_PROP_MINIMUM_SLEEP_USEC      "aaudio.minimum_sleep_usec"

/**
@@ -153,7 +146,21 @@ int32_t AAudioProperty_getMinimumSleepMicros();
 * @return minimum number of microseconds for a MMAP HW burst
 */
int32_t AAudioProperty_getHardwareBurstMinMicros();
#define AAUDIO_PROP_HW_BURST_MIN_USEC      "aaudio.hw_burst_min_usec"

/**
 * Read a system property that specifies an offset that will be added to MMAP timestamps.
 * This can be used to correct bias in the timestamp.
 * It can also be used to analyze the time distribution of the timestamp
 * by progressively modifying the offset and listening for glitches.
 *
 * @return number of microseconds to offset the time part of an MMAP timestamp
 */
int32_t AAudioProperty_getInputMMapOffsetMicros();
#define AAUDIO_PROP_INPUT_MMAP_OFFSET_USEC    "aaudio.in_mmap_offset_usec"

int32_t AAudioProperty_getOutputMMapOffsetMicros();
#define AAUDIO_PROP_OUTPUT_MMAP_OFFSET_USEC   "aaudio.out_mmap_offset_usec"

/**
 * Is flush allowed for the given state?