Add throttle to have pipe fill at more stable rate

Change-Id: Ibff9ab62764e2acd04518a4a00e25f19e28aff4a
diff --git a/services/audioflinger/MonoPipe.cpp b/services/audioflinger/MonoPipe.cpp
index b0ddf83..fd16e92 100644
--- a/services/audioflinger/MonoPipe.cpp
+++ b/services/audioflinger/MonoPipe.cpp
@@ -33,16 +33,6 @@
         mRear(0),
         mWriteCanBlock(writeCanBlock)
 {
-    if (writeCanBlock) {
-        // compute sleep time to be about 2/3 of a full pipe;
-        // this gives a balance between risk of underrun vs. too-frequent wakeups
-        mSleep.tv_sec = 0;
-        uint64_t ns = mMaxFrames * (666666667 / Format_sampleRate(format));
-        if (ns > 999999999) {
-            ns = 999999999;
-        }
-        mSleep.tv_nsec = ns;
-    }
 }
 
 MonoPipe::~MonoPipe()
@@ -62,13 +52,13 @@
 
 ssize_t MonoPipe::write(const void *buffer, size_t count)
 {
-    // count == 0 is unlikely and not worth checking for explicitly; will be handled automatically
     if (CC_UNLIKELY(!mNegotiated)) {
         return NEGOTIATE;
     }
     size_t totalFramesWritten = 0;
-    for (;;) {
-        size_t written = availableToWrite();
+    while (count > 0) {
+        size_t avail = availableToWrite();
+        size_t written = avail;
         if (CC_LIKELY(written > count)) {
             written = count;
         }
@@ -88,12 +78,64 @@
             android_atomic_release_store(written + mRear, &mRear);
             totalFramesWritten += written;
         }
-        if ((count -= written) == 0 || !mWriteCanBlock) {
+        if (!mWriteCanBlock) {
             break;
         }
+        count -= written;
         buffer = (char *) buffer + (written << mBitShift);
-        // simulate blocking I/O by sleeping
-        nanosleep(&mSleep, NULL);
+        // Simulate blocking I/O by sleeping at different rates, depending on a throttle.
+        // The throttle tries to keep the pipe about 5/8 full on average, with a slight jitter.
+        uint64_t ns;
+        enum {
+            THROTTLE_VERY_FAST, // pipe is (nearly) empty, fill quickly
+            THROTTLE_FAST,      // pipe is normal, fill at slightly faster rate
+            THROTTLE_NOMINAL,   // pipe is normal, fill at nominal rate
+            THROTTLE_SLOW,      // pipe is normal, fill at slightly slower rate
+            THROTTLE_VERY_SLOW, // pipe is (nearly) full, fill slowly
+        } throttle;
+        avail -= written;
+        // FIXME cache these values to avoid re-computation
+        if (avail >= (mMaxFrames * 3) / 4) {
+            throttle = THROTTLE_VERY_FAST;
+        } else if (avail >= mMaxFrames / 2) {
+            throttle = THROTTLE_FAST;
+        } else if (avail >= (mMaxFrames * 3) / 8) {
+            throttle = THROTTLE_NOMINAL;
+        } else if (avail >= mMaxFrames / 4) {
+            throttle = THROTTLE_SLOW;
+        } else {
+            throttle = THROTTLE_VERY_SLOW;
+        }
+        if (written > 0) {
+            // FIXME cache these values also
+            switch (throttle) {
+            case THROTTLE_VERY_FAST:
+            default:
+                ns = written * ( 500000000 / Format_sampleRate(mFormat));
+                break;
+            case THROTTLE_FAST:
+                ns = written * ( 750000000 / Format_sampleRate(mFormat));
+                break;
+            case THROTTLE_NOMINAL:
+                ns = written * (1000000000 / Format_sampleRate(mFormat));
+                break;
+            case THROTTLE_SLOW:
+                ns = written * (1100000000 / Format_sampleRate(mFormat));
+                break;
+            case THROTTLE_VERY_SLOW:
+                ns = written * (1250000000 / Format_sampleRate(mFormat));
+                break;
+            }
+        } else {
+            ns = mMaxFrames * (250000000 / Format_sampleRate(mFormat));
+        }
+        if (ns > 999999999) {
+            ns = 999999999;
+        }
+        struct timespec sleep;
+        sleep.tv_sec = 0;
+        sleep.tv_nsec = ns;
+        nanosleep(&sleep, NULL);
     }
     mFramesWritten += totalFramesWritten;
     return totalFramesWritten;
diff --git a/services/audioflinger/MonoPipe.h b/services/audioflinger/MonoPipe.h
index 45e6bb4..1f56e54 100644
--- a/services/audioflinger/MonoPipe.h
+++ b/services/audioflinger/MonoPipe.h
@@ -67,7 +67,6 @@
     volatile int32_t mRear;         // written by writer with android_atomic_release_store,
                                     // read by reader with android_atomic_acquire_load
     const bool      mWriteCanBlock; // whether write() should block if the pipe is full
-    struct timespec mSleep;         // time to sleep if blocking is enabled and the pipe is full
 };
 
 }   // namespace android