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

Commit bbc5c146 authored by Phil Burk's avatar Phil Burk Committed by Android (Google) Code Review
Browse files

Merge "aaudio: prevent noise when stopping a stream" into main

parents fbda730b 3a85be63
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -278,3 +278,9 @@ void AudioEndpoint::eraseDataMemory() {
        mDataQueue->eraseMemory();
    }
}

void AudioEndpoint::eraseEmptyDataMemory(int32_t numFrames) {
    if (mDataQueue != nullptr) {
        mDataQueue->eraseEmptyMemory(numFrames);
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -107,6 +107,8 @@ public:
     */
    void eraseDataMemory();

    void eraseEmptyDataMemory(int32_t numFrames);

    void freeDataQueue() { mDataQueue.reset(); }

    void dump() const;
+10 −0
Original line number Diff line number Diff line
@@ -575,10 +575,20 @@ aaudio_result_t AudioStreamInternal::requestStop_l() {
        return AAUDIO_ERROR_INVALID_STATE;
    }

    // For playback, sleep until all the audio data has played.
    // Then clear the buffer to prevent noise.
    prepareBuffersForStop();

    mClockModel.stop(AudioClock::getNanoseconds());
    setState(AAUDIO_STREAM_STATE_STOPPING);
    mAtomicInternalTimestamp.clear();

#if 0
    // Simulate very slow CPU, force race condition where the
    // DSP keeps playing after we stop writing.
    AudioClock::sleepForNanos(800 * AAUDIO_NANOS_PER_MILLISECOND);
#endif

    result = mServiceInterface.stopStream(mServiceStreamHandleInfo);
    if (result == AAUDIO_ERROR_INVALID_HANDLE) {
        ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__);
+2 −0
Original line number Diff line number Diff line
@@ -123,6 +123,8 @@ protected:

    virtual void prepareBuffersForStart() {}

    virtual void prepareBuffersForStop() {}

    virtual void advanceClientToMatchServerPosition(int32_t serverMargin) = 0;

    virtual void onFlushFromServer() {}
+76 −13
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@

#define ATRACE_TAG ATRACE_TAG_AUDIO

#include <algorithm>

#include <media/MediaMetricsItem.h>
#include <utils/Trace.h>

@@ -108,6 +110,61 @@ void AudioStreamInternalPlay::prepareBuffersForStart() {
    mAudioEndpoint->eraseDataMemory();
}

void AudioStreamInternalPlay::prepareBuffersForStop() {
    // If this is a shared stream and the FIFO is being read by the mixer then
    // we don't have to worry about the DSP reading past the valid data. We can skip all this.
    if(!mAudioEndpoint->isFreeRunning()) {
        return;
    }
    // Sleep until the DSP has read all of the data written.
    int64_t validFramesInBuffer = getFramesWritten() - getFramesRead();
    if (validFramesInBuffer >= 0) {
        int64_t emptyFramesInBuffer = ((int64_t) getBufferCapacity()) - validFramesInBuffer;

        // Prevent stale data from being played if the DSP is still running.
        // Erase some of the FIFO memory in front of the DSP read cursor.
        // Subtract one burst so we do not accidentally erase data that the DSP might be using.
        int64_t framesToErase = std::max((int64_t) 0,
                                         emptyFramesInBuffer - getFramesPerBurst());
        mAudioEndpoint->eraseEmptyDataMemory(framesToErase);

        // Sleep until we are confident the DSP has consumed all of the valid data.
        // Sleep for one extra burst as a safety margin because the IsochronousClockModel
        // is not perfectly accurate.
        int64_t positionInEmptyMemory = getFramesWritten() + getFramesPerBurst();
        int64_t timeAllConsumed = mClockModel.convertPositionToTime(positionInEmptyMemory);
        int64_t durationAllConsumed = timeAllConsumed - AudioClock::getNanoseconds();
        // Prevent sleeping for too long.
        durationAllConsumed = std::min(200 * AAUDIO_NANOS_PER_MILLISECOND, durationAllConsumed);
        AudioClock::sleepForNanos(durationAllConsumed);
    }

    // Erase all of the memory in case the DSP keeps going and wraps around.
    mAudioEndpoint->eraseDataMemory();

    // Wait for the last buffer to reach the DAC.
    // This is because the expected behavior of stop() is that all data written to the stream
    // should be played before the hardware actually shuts down.
    // This is different than pause(), where we just end as soon as possible.
    // This can be important when, for example, playing car navigation and
    // you want the user to hear the complete instruction.
    if (mAtomicInternalTimestamp.isValid()) {
        // Use timestamps to calculate the latency between the DSP reading
        // a frame and when it reaches the DAC.
        // This code assumes that timestamps are accurate.
        Timestamp timestamp = mAtomicInternalTimestamp.read();
        int64_t dacPosition = timestamp.getPosition();
        int64_t hardwareReadTime = mClockModel.convertPositionToTime(dacPosition);
        int64_t hardwareLatencyNanos = timestamp.getNanoseconds() - hardwareReadTime;
        ALOGD("%s() hardwareLatencyNanos = %lld", __func__,
              (long long) hardwareLatencyNanos);
        // Prevent sleeping for too long.
        hardwareLatencyNanos = std::min(30 * AAUDIO_NANOS_PER_MILLISECOND,
                                        hardwareLatencyNanos);
        AudioClock::sleepForNanos(hardwareLatencyNanos);
    }
}

void AudioStreamInternalPlay::advanceClientToMatchServerPosition(int32_t serverMargin) {
    int64_t readCounter = mAudioEndpoint->getDataReadCounter() + serverMargin;
    int64_t writeCounter = mAudioEndpoint->getDataWriteCounter();
@@ -353,8 +410,13 @@ void *AudioStreamInternalPlay::callbackLoop() {
        // Call application using the AAudio callback interface.
        callbackResult = maybeCallDataCallback(mCallbackBuffer.get(), mCallbackFrames);

        if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
        // Write audio data to stream. This is a BLOCKING WRITE!
        // Write data regardless of the callbackResult because we assume the data
        // is valid even when the callback returns AAUDIO_CALLBACK_RESULT_STOP.
        // Imagine a callback that is playing a large sound in menory.
        // When it gets to the end of the sound it can partially fill
        // the last buffer with the end of the sound, then zero pad the buffer, then return STOP.
        // If the callback has no valid data then it should zero-fill the entire buffer.
        result = write(mCallbackBuffer.get(), mCallbackFrames, timeoutNanos);
        if ((result != mCallbackFrames)) {
            if (result >= 0) {
@@ -366,7 +428,8 @@ void *AudioStreamInternalPlay::callbackLoop() {
            maybeCallErrorCallback(result);
            break;
        }
        } else if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {

        if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
            ALOGD("%s(): callback returned AAUDIO_CALLBACK_RESULT_STOP", __func__);
            result = systemStopInternal();
            break;
Loading