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

Commit ea38ee77 authored by Glenn Kasten's avatar Glenn Kasten
Browse files

Can now indicate the desired number of notifications (sub-buffers)

per fast track buffer when using the default buffer size.

There is no change for normal tracks, or fast tracks with a non-default buffer size.

Also fix related bugs:
Notification period was not set correctly when fast multiplier is not 1.
Incorrect warning when client adjusted notification frames.

Bug: 27819623
Bug: 28117362
Change-Id: Ifc2121e874f51718cd56ec04e4bd5f89f4963132
parent ecd0a41d
Loading
Loading
Loading
Loading
+27 −5
Original line number Original line Diff line number Diff line
@@ -186,8 +186,21 @@ public:
     *                     and inform of marker, position updates, etc.
     *                     and inform of marker, position updates, etc.
     * user:               Context for use by the callback receiver.
     * user:               Context for use by the callback receiver.
     * notificationFrames: The callback function is called each time notificationFrames PCM
     * notificationFrames: The callback function is called each time notificationFrames PCM
     *                     frames have been consumed from track input buffer.
     *                     frames have been consumed from track input buffer by server.
     *                     This is expressed in units of frames at the initial source sample rate.
     *                     Zero means to use a default value, which is typically:
     *                      - fast tracks: HAL buffer size, even if track frameCount is larger
     *                      - normal tracks: 1/2 of track frameCount
     *                     A positive value means that many frames at initial source sample rate.
     *                     A negative value for this parameter specifies the negative of the
     *                     requested number of notifications (sub-buffers) in the entire buffer.
     *                     For fast tracks, the FastMixer will process one sub-buffer at a time.
     *                     The size of each sub-buffer is determined by the HAL.
     *                     To get "double buffering", for example, one should pass -2.
     *                     The minimum number of sub-buffers is 1 (expressed as -1),
     *                     and the maximum number of sub-buffers is 8 (expressed as -8).
     *                     Negative is only permitted for fast tracks, and if frameCount is zero.
     *                     TODO It is ugly to overload a parameter in this way depending on
     *                     whether it is positive, negative, or zero.  Consider splitting apart.
     * sessionId:          Specific session ID, or zero to use default.
     * sessionId:          Specific session ID, or zero to use default.
     * transferType:       How data is transferred to AudioTrack.
     * transferType:       How data is transferred to AudioTrack.
     * offloadInfo:        If not NULL, provides offload parameters for
     * offloadInfo:        If not NULL, provides offload parameters for
@@ -216,7 +229,7 @@ public:
                                    audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
                                    audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
                                    callback_t cbf       = NULL,
                                    callback_t cbf       = NULL,
                                    void* user           = NULL,
                                    void* user           = NULL,
                                    uint32_t notificationFrames = 0,
                                    int32_t notificationFrames = 0,
                                    audio_session_t sessionId  = AUDIO_SESSION_ALLOCATE,
                                    audio_session_t sessionId  = AUDIO_SESSION_ALLOCATE,
                                    transfer_type transferType = TRANSFER_DEFAULT,
                                    transfer_type transferType = TRANSFER_DEFAULT,
                                    const audio_offload_info_t *offloadInfo = NULL,
                                    const audio_offload_info_t *offloadInfo = NULL,
@@ -246,7 +259,7 @@ public:
                                    audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
                                    audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
                                    callback_t cbf      = NULL,
                                    callback_t cbf      = NULL,
                                    void* user          = NULL,
                                    void* user          = NULL,
                                    uint32_t notificationFrames = 0,
                                    int32_t notificationFrames = 0,
                                    audio_session_t sessionId   = AUDIO_SESSION_ALLOCATE,
                                    audio_session_t sessionId   = AUDIO_SESSION_ALLOCATE,
                                    transfer_type transferType = TRANSFER_DEFAULT,
                                    transfer_type transferType = TRANSFER_DEFAULT,
                                    const audio_offload_info_t *offloadInfo = NULL,
                                    const audio_offload_info_t *offloadInfo = NULL,
@@ -290,7 +303,7 @@ public:
                            audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
                            audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
                            callback_t cbf      = NULL,
                            callback_t cbf      = NULL,
                            void* user          = NULL,
                            void* user          = NULL,
                            uint32_t notificationFrames = 0,
                            int32_t notificationFrames = 0,
                            const sp<IMemory>& sharedBuffer = 0,
                            const sp<IMemory>& sharedBuffer = 0,
                            bool threadCanCallJava = false,
                            bool threadCanCallJava = false,
                            audio_session_t sessionId  = AUDIO_SESSION_ALLOCATE,
                            audio_session_t sessionId  = AUDIO_SESSION_ALLOCATE,
@@ -335,6 +348,8 @@ public:
            uint32_t    channelCount() const { return mChannelCount; }
            uint32_t    channelCount() const { return mChannelCount; }
            size_t      frameCount() const  { return mFrameCount; }
            size_t      frameCount() const  { return mFrameCount; }


    // TODO consider notificationFrames() if needed

    /* Return effective size of audio buffer that an application writes to
    /* Return effective size of audio buffer that an application writes to
     * or a negative error if the track is uninitialized.
     * or a negative error if the track is uninitialized.
     */
     */
@@ -977,9 +992,16 @@ protected:
    void*                   mUserData;
    void*                   mUserData;


    // for notification APIs
    // for notification APIs

    // next 2 fields are const after constructor or set()
    uint32_t                mNotificationFramesReq; // requested number of frames between each
    uint32_t                mNotificationFramesReq; // requested number of frames between each
                                                    // notification callback,
                                                    // notification callback,
                                                    // at initial source sample rate
                                                    // at initial source sample rate
    uint32_t                mNotificationsPerBufferReq;
                                                    // requested number of notifications per buffer,
                                                    // currently only used for fast tracks with
                                                    // default track buffer size

    uint32_t                mNotificationFramesAct; // actual number of frames between each
    uint32_t                mNotificationFramesAct; // actual number of frames between each
                                                    // notification callback,
                                                    // notification callback,
                                                    // at initial source sample rate
                                                    // at initial source sample rate
+88 −30
Original line number Original line Diff line number Diff line
@@ -90,16 +90,24 @@ static inline float adjustPitch(float pitch)
// TODO: Move to a common library
// TODO: Move to a common library
static size_t calculateMinFrameCount(
static size_t calculateMinFrameCount(
        uint32_t afLatencyMs, uint32_t afFrameCount, uint32_t afSampleRate,
        uint32_t afLatencyMs, uint32_t afFrameCount, uint32_t afSampleRate,
        uint32_t sampleRate, float speed)
        uint32_t sampleRate, float speed /*, uint32_t notificationsPerBufferReq*/)
{
{
    // Ensure that buffer depth covers at least audio hardware latency
    // Ensure that buffer depth covers at least audio hardware latency
    uint32_t minBufCount = afLatencyMs / ((1000 * afFrameCount) / afSampleRate);
    uint32_t minBufCount = afLatencyMs / ((1000 * afFrameCount) / afSampleRate);
    if (minBufCount < 2) {
    if (minBufCount < 2) {
        minBufCount = 2;
        minBufCount = 2;
    }
    }
#if 0
    // The notificationsPerBufferReq parameter is not yet used for non-fast tracks,
    // but keeping the code here to make it easier to add later.
    if (minBufCount < notificationsPerBufferReq) {
        minBufCount = notificationsPerBufferReq;
    }
#endif
    ALOGV("calculateMinFrameCount afLatency %u  afFrameCount %u  afSampleRate %u  "
    ALOGV("calculateMinFrameCount afLatency %u  afFrameCount %u  afSampleRate %u  "
            "sampleRate %u  speed %f  minBufCount: %u",
            "sampleRate %u  speed %f  minBufCount: %u" /*"  notificationsPerBufferReq %u"*/,
            afLatencyMs, afFrameCount, afSampleRate, sampleRate, speed, minBufCount);
            afLatencyMs, afFrameCount, afSampleRate, sampleRate, speed, minBufCount
            /*, notificationsPerBufferReq*/);
    return minBufCount * sourceFramesNeededWithTimestretch(
    return minBufCount * sourceFramesNeededWithTimestretch(
            sampleRate, afFrameCount, afSampleRate, speed);
            sampleRate, afFrameCount, afSampleRate, speed);
}
}
@@ -144,7 +152,8 @@ status_t AudioTrack::getMinFrameCount(


    // When called from createTrack, speed is 1.0f (normal speed).
    // When called from createTrack, speed is 1.0f (normal speed).
    // This is rechecked again on setting playback rate (TODO: on setting sample rate, too).
    // This is rechecked again on setting playback rate (TODO: on setting sample rate, too).
    *frameCount = calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, 1.0f);
    *frameCount = calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, 1.0f
            /*, 0 notificationsPerBufferReq*/);


    // The formula above should always produce a non-zero value under normal circumstances:
    // The formula above should always produce a non-zero value under normal circumstances:
    // AudioTrack.SAMPLE_RATE_HZ_MIN <= sampleRate <= AudioTrack.SAMPLE_RATE_HZ_MAX.
    // AudioTrack.SAMPLE_RATE_HZ_MIN <= sampleRate <= AudioTrack.SAMPLE_RATE_HZ_MAX.
@@ -184,7 +193,7 @@ AudioTrack::AudioTrack(
        audio_output_flags_t flags,
        audio_output_flags_t flags,
        callback_t cbf,
        callback_t cbf,
        void* user,
        void* user,
        uint32_t notificationFrames,
        int32_t notificationFrames,
        audio_session_t sessionId,
        audio_session_t sessionId,
        transfer_type transferType,
        transfer_type transferType,
        const audio_offload_info_t *offloadInfo,
        const audio_offload_info_t *offloadInfo,
@@ -215,7 +224,7 @@ AudioTrack::AudioTrack(
        audio_output_flags_t flags,
        audio_output_flags_t flags,
        callback_t cbf,
        callback_t cbf,
        void* user,
        void* user,
        uint32_t notificationFrames,
        int32_t notificationFrames,
        audio_session_t sessionId,
        audio_session_t sessionId,
        transfer_type transferType,
        transfer_type transferType,
        const audio_offload_info_t *offloadInfo,
        const audio_offload_info_t *offloadInfo,
@@ -274,7 +283,7 @@ status_t AudioTrack::set(
        audio_output_flags_t flags,
        audio_output_flags_t flags,
        callback_t cbf,
        callback_t cbf,
        void* user,
        void* user,
        uint32_t notificationFrames,
        int32_t notificationFrames,
        const sp<IMemory>& sharedBuffer,
        const sp<IMemory>& sharedBuffer,
        bool threadCanCallJava,
        bool threadCanCallJava,
        audio_session_t sessionId,
        audio_session_t sessionId,
@@ -287,7 +296,7 @@ status_t AudioTrack::set(
        float maxRequiredSpeed)
        float maxRequiredSpeed)
{
{
    ALOGV("set(): streamType %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
    ALOGV("set(): streamType %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
          "flags #%x, notificationFrames %u, sessionId %d, transferType %d, uid %d, pid %d",
          "flags #%x, notificationFrames %d, sessionId %d, transferType %d, uid %d, pid %d",
          streamType, sampleRate, format, channelMask, frameCount, flags, notificationFrames,
          streamType, sampleRate, format, channelMask, frameCount, flags, notificationFrames,
          sessionId, transferType, uid, pid);
          sessionId, transferType, uid, pid);


@@ -443,7 +452,29 @@ status_t AudioTrack::set(
    mSendLevel = 0.0f;
    mSendLevel = 0.0f;
    // mFrameCount is initialized in createTrack_l
    // mFrameCount is initialized in createTrack_l
    mReqFrameCount = frameCount;
    mReqFrameCount = frameCount;
    if (notificationFrames >= 0) {
        mNotificationFramesReq = notificationFrames;
        mNotificationFramesReq = notificationFrames;
        mNotificationsPerBufferReq = 0;
    } else {
        if (!(flags & AUDIO_OUTPUT_FLAG_FAST)) {
            ALOGE("notificationFrames=%d not permitted for non-fast track",
                    notificationFrames);
            return BAD_VALUE;
        }
        if (frameCount > 0) {
            ALOGE("notificationFrames=%d not permitted with non-zero frameCount=%zu",
                    notificationFrames, frameCount);
            return BAD_VALUE;
        }
        mNotificationFramesReq = 0;
        const uint32_t minNotificationsPerBuffer = 1;
        const uint32_t maxNotificationsPerBuffer = 8;
        mNotificationsPerBufferReq = min(maxNotificationsPerBuffer,
                max((uint32_t) -notificationFrames, minNotificationsPerBuffer));
        ALOGW_IF(mNotificationsPerBufferReq != (uint32_t) -notificationFrames,
                "notificationFrames=%d clamped to the range -%u to -%u",
                notificationFrames, minNotificationsPerBuffer, maxNotificationsPerBuffer);
    }
    mNotificationFramesAct = 0;
    mNotificationFramesAct = 0;
    if (sessionId == AUDIO_SESSION_ALLOCATE) {
    if (sessionId == AUDIO_SESSION_ALLOCATE) {
        mSessionId = (audio_session_t) AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
        mSessionId = (audio_session_t) AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
@@ -1231,6 +1262,15 @@ status_t AudioTrack::createTrack_l()
        goto release;
        goto release;
    }
    }


    // TODO consider making this a member variable if there are other uses for it later
    size_t afFrameCountHAL;
    status = AudioSystem::getFrameCountHAL(output, &afFrameCountHAL);
    if (status != NO_ERROR) {
        ALOGE("getFrameCountHAL(output=%d) status %d", output, status);
        goto release;
    }
    ALOG_ASSERT(afFrameCountHAL > 0);

    status = AudioSystem::getSamplingRate(output, &mAfSampleRate);
    status = AudioSystem::getSamplingRate(output, &mAfSampleRate);
    if (status != NO_ERROR) {
    if (status != NO_ERROR) {
        ALOGE("getSamplingRate(output=%d) status %d", output, status);
        ALOGE("getSamplingRate(output=%d) status %d", output, status);
@@ -1303,20 +1343,32 @@ status_t AudioTrack::createTrack_l()
        // there _is_ a frameCount parameter.  We silently ignore it.
        // there _is_ a frameCount parameter.  We silently ignore it.
        frameCount = mSharedBuffer->size() / mFrameSize;
        frameCount = mSharedBuffer->size() / mFrameSize;
    } else {
    } else {
        // For fast tracks the frame count calculations and checks are done by server
        size_t minFrameCount = 0;

        // For fast tracks the frame count calculations and checks are mostly done by server,
        if ((mFlags & AUDIO_OUTPUT_FLAG_FAST) == 0) {
        // but we try to respect the application's request for notifications per buffer.
        if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
            if (mNotificationsPerBufferReq > 0) {
                // Avoid possible arithmetic overflow during multiplication.
                // mNotificationsPerBuffer is clamped to a small integer earlier, so it is unlikely.
                if (mNotificationsPerBufferReq > SIZE_MAX / afFrameCountHAL) {
                    ALOGE("Requested notificationPerBuffer=%u ignored for HAL frameCount=%zu",
                            mNotificationsPerBufferReq, afFrameCountHAL);
                } else {
                    minFrameCount = afFrameCountHAL * mNotificationsPerBufferReq;
                }
            }
        } else {
            // for normal tracks precompute the frame count based on speed.
            // for normal tracks precompute the frame count based on speed.
            const float speed = !isPurePcmData_l() || isOffloadedOrDirect_l() ? 1.0f :
            const float speed = !isPurePcmData_l() || isOffloadedOrDirect_l() ? 1.0f :
                            max(mMaxRequiredSpeed, mPlaybackRate.mSpeed);
                            max(mMaxRequiredSpeed, mPlaybackRate.mSpeed);
            const size_t minFrameCount = calculateMinFrameCount(
            minFrameCount = calculateMinFrameCount(
                    mAfLatency, mAfFrameCount, mAfSampleRate, mSampleRate,
                    mAfLatency, mAfFrameCount, mAfSampleRate, mSampleRate,
                    speed);
                    speed /*, 0 mNotificationsPerBufferReq*/);
        }
        if (frameCount < minFrameCount) {
        if (frameCount < minFrameCount) {
            frameCount = minFrameCount;
            frameCount = minFrameCount;
        }
        }
    }
    }
    }


    IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT;
    IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT;


@@ -1408,22 +1460,26 @@ status_t AudioTrack::createTrack_l()
    }
    }


    // Make sure that application is notified with sufficient margin before underrun.
    // Make sure that application is notified with sufficient margin before underrun.
    // The client's AudioTrack buffer is divided into n parts for purpose of wakeup by server, where
    // The client can divide the AudioTrack buffer into sub-buffers,
    //  n = 1   fast track with single buffering; nBuffering is ignored
    // and expresses its desire to server as the notification frame count.
    //  n = 2   fast track with double buffering
    //  n = 2   normal track, (including those with sample rate conversion)
    //  n >= 3  very high latency or very small notification interval (unused).
    // FIXME Move the computation from client side to server side,
    //       and allow nBuffering to be larger than 1 for OpenSL ES, like it can be for Java.
    if (mSharedBuffer == 0 && audio_is_linear_pcm(mFormat)) {
    if (mSharedBuffer == 0 && audio_is_linear_pcm(mFormat)) {
        size_t maxNotificationFrames = frameCount;
        size_t maxNotificationFrames;
        if (!(trackFlags & IAudioFlinger::TRACK_FAST)) {
        if (trackFlags & IAudioFlinger::TRACK_FAST) {
            const uint32_t nBuffering = 2;
            // notify every HAL buffer, regardless of the size of the track buffer
            maxNotificationFrames /= nBuffering;
            maxNotificationFrames = afFrameCountHAL;
        } else {
            // For normal tracks, use double-buffering
            const int nBuffering = 2;
            maxNotificationFrames = frameCount / nBuffering;
        }
        }
        if (mNotificationFramesAct == 0 || mNotificationFramesAct > maxNotificationFrames) {
        if (mNotificationFramesAct == 0 || mNotificationFramesAct > maxNotificationFrames) {
            if (mNotificationFramesAct == 0) {
                ALOGD("Client defaulted notificationFrames to %zu for frameCount %zu",
                    maxNotificationFrames, frameCount);
            } else {
                ALOGW("Client adjusted notificationFrames from %u to %zu for frameCount %zu",
                ALOGW("Client adjusted notificationFrames from %u to %zu for frameCount %zu",
                    mNotificationFramesAct, maxNotificationFrames, frameCount);
                    mNotificationFramesAct, maxNotificationFrames, frameCount);
            }
            mNotificationFramesAct = (uint32_t) maxNotificationFrames;
            mNotificationFramesAct = (uint32_t) maxNotificationFrames;
        }
        }
    }
    }
@@ -2043,6 +2099,7 @@ nsecs_t AudioTrack::processAudioBuffer()
                const nsecs_t datans = mRemainingFrames <= avail ? 0 :
                const nsecs_t datans = mRemainingFrames <= avail ? 0 :
                        framesToNanoseconds(mRemainingFrames - avail, sampleRate, speed);
                        framesToNanoseconds(mRemainingFrames - avail, sampleRate, speed);
                // audio flinger thread buffer size (TODO: adjust for fast tracks)
                // audio flinger thread buffer size (TODO: adjust for fast tracks)
                // FIXME: use mAfFrameCountHAL instead of mAfFrameCount below for fast tracks.
                const nsecs_t afns = framesToNanoseconds(mAfFrameCount, mAfSampleRate, speed);
                const nsecs_t afns = framesToNanoseconds(mAfFrameCount, mAfSampleRate, speed);
                // add a half the AudioFlinger buffer time to avoid soaking CPU if datans is 0.
                // add a half the AudioFlinger buffer time to avoid soaking CPU if datans is 0.
                myns = datans + (afns / 2);
                myns = datans + (afns / 2);
@@ -2203,7 +2260,8 @@ bool AudioTrack::isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed) co
        return true; // static tracks do not have issues with buffer sizing.
        return true; // static tracks do not have issues with buffer sizing.
    }
    }
    const size_t minFrameCount =
    const size_t minFrameCount =
            calculateMinFrameCount(mAfLatency, mAfFrameCount, mAfSampleRate, sampleRate, speed);
            calculateMinFrameCount(mAfLatency, mAfFrameCount, mAfSampleRate, sampleRate, speed
                /*, 0 mNotificationsPerBufferReq*/);
    ALOGV("isSampleRateSpeedAllowed_l mFrameCount %zu  minFrameCount %zu",
    ALOGV("isSampleRateSpeedAllowed_l mFrameCount %zu  minFrameCount %zu",
            mFrameCount, minFrameCount);
            mFrameCount, minFrameCount);
    return mFrameCount >= minFrameCount;
    return mFrameCount >= minFrameCount;
+3 −7
Original line number Original line Diff line number Diff line
@@ -182,13 +182,9 @@ static const int kPriorityAudioApp = 2;
static const int kPriorityFastMixer = 3;
static const int kPriorityFastMixer = 3;
static const int kPriorityFastCapture = 3;
static const int kPriorityFastCapture = 3;


// IAudioFlinger::createTrack() reports back to client the total size of shared memory area
// IAudioFlinger::createTrack() has an in/out parameter 'pFrameCount' for the total size of the
// for the track.  The client then sub-divides this into smaller buffers for its use.
// track buffer in shared memory.  Zero on input means to use a default value.  For fast tracks,
// Currently the client uses N-buffering by default, but doesn't tell us about the value of N.
// AudioFlinger derives the default from HAL buffer size and 'fast track multiplier'.
// So for now we just assume that client is double-buffered for fast tracks.
// FIXME It would be better for client to tell AudioFlinger the value of N,
// so AudioFlinger could allocate the right amount of memory.
// See the client's minBufCount and mNotificationFramesAct calculations for details.


// This is the default value, if not specified by property.
// This is the default value, if not specified by property.
static const int kFastTrackMultiplier = 2;
static const int kFastTrackMultiplier = 2;