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
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index fc7217a..1107142 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -186,8 +186,21 @@
      *                     and inform of marker, position updates, etc.
      * user:               Context for use by the callback receiver.
      * notificationFrames: The callback function is called each time notificationFrames PCM
-     *                     frames have been consumed from track input buffer.
-     *                     This is expressed in units of frames at the initial source sample rate.
+     *                     frames have been consumed from track input buffer by server.
+     *                     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.
      * transferType:       How data is transferred to AudioTrack.
      * offloadInfo:        If not NULL, provides offload parameters for
@@ -216,7 +229,7 @@
                                     audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
                                     callback_t cbf       = NULL,
                                     void* user           = NULL,
-                                    uint32_t notificationFrames = 0,
+                                    int32_t notificationFrames = 0,
                                     audio_session_t sessionId  = AUDIO_SESSION_ALLOCATE,
                                     transfer_type transferType = TRANSFER_DEFAULT,
                                     const audio_offload_info_t *offloadInfo = NULL,
@@ -246,7 +259,7 @@
                                     audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
                                     callback_t cbf      = NULL,
                                     void* user          = NULL,
-                                    uint32_t notificationFrames = 0,
+                                    int32_t notificationFrames = 0,
                                     audio_session_t sessionId   = AUDIO_SESSION_ALLOCATE,
                                     transfer_type transferType = TRANSFER_DEFAULT,
                                     const audio_offload_info_t *offloadInfo = NULL,
@@ -290,7 +303,7 @@
                             audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
                             callback_t cbf      = NULL,
                             void* user          = NULL,
-                            uint32_t notificationFrames = 0,
+                            int32_t notificationFrames = 0,
                             const sp<IMemory>& sharedBuffer = 0,
                             bool threadCanCallJava = false,
                             audio_session_t sessionId  = AUDIO_SESSION_ALLOCATE,
@@ -335,6 +348,8 @@
             uint32_t    channelCount() const { return mChannelCount; }
             size_t      frameCount() const  { return mFrameCount; }
 
+    // TODO consider notificationFrames() if needed
+
     /* Return effective size of audio buffer that an application writes to
      * or a negative error if the track is uninitialized.
      */
@@ -977,9 +992,16 @@
     void*                   mUserData;
 
     // for notification APIs
+
+    // next 2 fields are const after constructor or set()
     uint32_t                mNotificationFramesReq; // requested number of frames between each
                                                     // notification callback,
                                                     // 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
                                                     // notification callback,
                                                     // at initial source sample rate
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index b9138f2..472f6c0 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -90,16 +90,24 @@
 // TODO: Move to a common library
 static size_t calculateMinFrameCount(
         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
     uint32_t minBufCount = afLatencyMs / ((1000 * afFrameCount) / afSampleRate);
     if (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  "
-            "sampleRate %u  speed %f  minBufCount: %u",
-            afLatencyMs, afFrameCount, afSampleRate, sampleRate, speed, minBufCount);
+            "sampleRate %u  speed %f  minBufCount: %u" /*"  notificationsPerBufferReq %u"*/,
+            afLatencyMs, afFrameCount, afSampleRate, sampleRate, speed, minBufCount
+            /*, notificationsPerBufferReq*/);
     return minBufCount * sourceFramesNeededWithTimestretch(
             sampleRate, afFrameCount, afSampleRate, speed);
 }
@@ -144,7 +152,8 @@
 
     // 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 = calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, 1.0f
+            /*, 0 notificationsPerBufferReq*/);
 
     // The formula above should always produce a non-zero value under normal circumstances:
     // AudioTrack.SAMPLE_RATE_HZ_MIN <= sampleRate <= AudioTrack.SAMPLE_RATE_HZ_MAX.
@@ -184,7 +193,7 @@
         audio_output_flags_t flags,
         callback_t cbf,
         void* user,
-        uint32_t notificationFrames,
+        int32_t notificationFrames,
         audio_session_t sessionId,
         transfer_type transferType,
         const audio_offload_info_t *offloadInfo,
@@ -215,7 +224,7 @@
         audio_output_flags_t flags,
         callback_t cbf,
         void* user,
-        uint32_t notificationFrames,
+        int32_t notificationFrames,
         audio_session_t sessionId,
         transfer_type transferType,
         const audio_offload_info_t *offloadInfo,
@@ -274,7 +283,7 @@
         audio_output_flags_t flags,
         callback_t cbf,
         void* user,
-        uint32_t notificationFrames,
+        int32_t notificationFrames,
         const sp<IMemory>& sharedBuffer,
         bool threadCanCallJava,
         audio_session_t sessionId,
@@ -287,7 +296,7 @@
         float maxRequiredSpeed)
 {
     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,
           sessionId, transferType, uid, pid);
 
@@ -443,7 +452,29 @@
     mSendLevel = 0.0f;
     // mFrameCount is initialized in createTrack_l
     mReqFrameCount = frameCount;
-    mNotificationFramesReq = notificationFrames;
+    if (notificationFrames >= 0) {
+        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;
     if (sessionId == AUDIO_SESSION_ALLOCATE) {
         mSessionId = (audio_session_t) AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
@@ -1231,6 +1262,15 @@
         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);
     if (status != NO_ERROR) {
         ALOGE("getSamplingRate(output=%d) status %d", output, status);
@@ -1303,18 +1343,30 @@
         // there _is_ a frameCount parameter.  We silently ignore it.
         frameCount = mSharedBuffer->size() / mFrameSize;
     } else {
-        // For fast tracks the frame count calculations and checks are done by server
-
-        if ((mFlags & AUDIO_OUTPUT_FLAG_FAST) == 0) {
+        size_t minFrameCount = 0;
+        // For fast tracks the frame count calculations and checks are mostly done by server,
+        // 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.
             const float speed = !isPurePcmData_l() || isOffloadedOrDirect_l() ? 1.0f :
                             max(mMaxRequiredSpeed, mPlaybackRate.mSpeed);
-            const size_t minFrameCount = calculateMinFrameCount(
+            minFrameCount = calculateMinFrameCount(
                     mAfLatency, mAfFrameCount, mAfSampleRate, mSampleRate,
-                    speed);
-            if (frameCount < minFrameCount) {
-                frameCount = minFrameCount;
-            }
+                    speed /*, 0 mNotificationsPerBufferReq*/);
+        }
+        if (frameCount < minFrameCount) {
+            frameCount = minFrameCount;
         }
     }
 
@@ -1408,22 +1460,26 @@
     }
 
     // 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
-    //  n = 1   fast track with single buffering; nBuffering is ignored
-    //  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.
+    // The client can divide the AudioTrack buffer into sub-buffers,
+    // and expresses its desire to server as the notification frame count.
     if (mSharedBuffer == 0 && audio_is_linear_pcm(mFormat)) {
-        size_t maxNotificationFrames = frameCount;
-        if (!(trackFlags & IAudioFlinger::TRACK_FAST)) {
-            const uint32_t nBuffering = 2;
-            maxNotificationFrames /= nBuffering;
+        size_t maxNotificationFrames;
+        if (trackFlags & IAudioFlinger::TRACK_FAST) {
+            // notify every HAL buffer, regardless of the size of the track buffer
+            maxNotificationFrames = afFrameCountHAL;
+        } else {
+            // For normal tracks, use double-buffering
+            const int nBuffering = 2;
+            maxNotificationFrames = frameCount / nBuffering;
         }
         if (mNotificationFramesAct == 0 || mNotificationFramesAct > maxNotificationFrames) {
-            ALOGW("Client adjusted notificationFrames from %u to %zu for frameCount %zu",
+            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",
                     mNotificationFramesAct, maxNotificationFrames, frameCount);
+            }
             mNotificationFramesAct = (uint32_t) maxNotificationFrames;
         }
     }
@@ -2043,6 +2099,7 @@
                 const nsecs_t datans = mRemainingFrames <= avail ? 0 :
                         framesToNanoseconds(mRemainingFrames - avail, sampleRate, speed);
                 // 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);
                 // add a half the AudioFlinger buffer time to avoid soaking CPU if datans is 0.
                 myns = datans + (afns / 2);
@@ -2203,7 +2260,8 @@
         return true; // static tracks do not have issues with buffer sizing.
     }
     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",
             mFrameCount, minFrameCount);
     return mFrameCount >= minFrameCount;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index ff67fb2..3fdf99f 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -182,13 +182,9 @@
 static const int kPriorityFastMixer = 3;
 static const int kPriorityFastCapture = 3;
 
-// IAudioFlinger::createTrack() reports back to client the total size of shared memory area
-// for the track.  The client then sub-divides this into smaller buffers for its use.
-// Currently the client uses N-buffering by default, but doesn't tell us about the value of N.
-// 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.
+// IAudioFlinger::createTrack() has an in/out parameter 'pFrameCount' for the total size of the
+// track buffer in shared memory.  Zero on input means to use a default value.  For fast tracks,
+// AudioFlinger derives the default from HAL buffer size and 'fast track multiplier'.
 
 // This is the default value, if not specified by property.
 static const int kFastTrackMultiplier = 2;