VolumeShaper: Add AudioTrack restore

MediaPlayer VolumeShaper can now be set before start().

Test: CTS and Ducking
Bug: 31015569
Change-Id: Idf63c167e164161b200e2467fbeb9409b3097dbe
diff --git a/include/media/VolumeShaper.h b/include/media/VolumeShaper.h
index 1421ae7..f5a74d8 100644
--- a/include/media/VolumeShaper.h
+++ b/include/media/VolumeShaper.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_VOLUME_SHAPER_H
 #define ANDROID_VOLUME_SHAPER_H
 
+#include <cmath>
 #include <list>
 #include <math.h>
 #include <sstream>
@@ -289,18 +290,28 @@
             FLAG_TERMINATE = (1 << 1),
             FLAG_JOIN      = (1 << 2),
             FLAG_DELAY     = (1 << 3),
+            FLAG_CREATE_IF_NECESSARY = (1 << 4),
 
-            FLAG_ALL       = (FLAG_REVERSE | FLAG_TERMINATE | FLAG_JOIN | FLAG_DELAY),
+            FLAG_ALL       = (FLAG_REVERSE | FLAG_TERMINATE | FLAG_JOIN | FLAG_DELAY
+                            | FLAG_CREATE_IF_NECESSARY),
         };
 
         Operation()
-            : mFlags(FLAG_NONE)
-            , mReplaceId(-1) {
+            : Operation(FLAG_NONE, -1 /* replaceId */) {
         }
 
         explicit Operation(Flag flags, int replaceId)
+            : Operation(flags, replaceId, std::numeric_limits<S>::quiet_NaN() /* xOffset */) {
+        }
+
+        Operation(const Operation &operation)
+            : Operation(operation.mFlags, operation.mReplaceId, operation.mXOffset) {
+        }
+
+        explicit Operation(Flag flags, int replaceId, S xOffset)
             : mFlags(flags)
-            , mReplaceId(replaceId) {
+            , mReplaceId(replaceId)
+            , mXOffset(xOffset) {
         }
 
         int32_t getReplaceId() const {
@@ -311,6 +322,14 @@
             mReplaceId = replaceId;
         }
 
+        S getXOffset() const {
+            return mXOffset;
+        }
+
+        void setXOffset(S xOffset) {
+            mXOffset = xOffset;
+        }
+
         Flag getFlags() const {
             return mFlags;
         }
@@ -327,13 +346,15 @@
         status_t writeToParcel(Parcel *parcel) const {
             if (parcel == nullptr) return BAD_VALUE;
             return parcel->writeInt32((int32_t)mFlags)
-                    ?: parcel->writeInt32(mReplaceId);
+                    ?: parcel->writeInt32(mReplaceId)
+                    ?: parcel->writeFloat(mXOffset);
         }
 
         status_t readFromParcel(const Parcel &parcel) {
             int32_t flags;
             return parcel.readInt32(&flags)
                     ?: parcel.readInt32(&mReplaceId)
+                    ?: parcel.readFloat(&mXOffset)
                     ?: setFlags((Flag)flags);
         }
 
@@ -341,12 +362,14 @@
             std::stringstream ss;
             ss << "mFlags: " << mFlags << std::endl;
             ss << "mReplaceId: " << mReplaceId << std::endl;
+            ss << "mXOffset: " << mXOffset << std::endl;
             return ss.str();
         }
 
     private:
         Flag mFlags;
         int32_t mReplaceId;
+        S mXOffset;
     }; // Operation
 
     // must match with VolumeShaper.java in frameworks/base
@@ -454,14 +477,6 @@
         return convertTimespecToUs(tv);
     }
 
-    Translate<S> mXTranslate;
-    Translate<T> mYTranslate;
-    sp<VolumeShaper::Configuration> mConfiguration;
-    sp<VolumeShaper::Operation> mOperation;
-    int64_t mStartFrame;
-    T mLastVolume;
-    S mXOffset;
-
     // TODO: Since we pass configuration and operation as shared pointers
     // there is a potential risk that the caller may modify these after
     // delivery.  Currently, we don't require copies made here.
@@ -472,7 +487,8 @@
         , mOperation(operation)         // ditto
         , mStartFrame(-1)
         , mLastVolume(T(1))
-        , mXOffset(0.f) {
+        , mLastXOffset(0.f)
+        , mDelayXOffset(std::numeric_limits<S>::quiet_NaN()) {
         if (configuration.get() != nullptr
                 && (getFlags() & VolumeShaper::Operation::FLAG_DELAY) == 0) {
             mLastVolume = configuration->first().second;
@@ -484,9 +500,11 @@
                         / (mConfiguration->getDurationMs() * 0.001 * sampleRate);
         const double minScale = 1. / INT64_MAX;
         scale = std::max(scale, minScale);
-        VS_LOG("update position: scale %lf  frameCount:%lld, sampleRate:%lf",
-                scale, (long long) startFrame, sampleRate);
-        mXTranslate.setOffset(startFrame - mConfiguration->first().first / scale);
+        const S xOffset = std::isnan(mDelayXOffset) ? mConfiguration->first().first : mDelayXOffset;
+        VS_LOG("update position: scale %lf  frameCount:%lld, sampleRate:%lf, xOffset:%f",
+                scale, (long long) startFrame, sampleRate, xOffset);
+
+        mXTranslate.setOffset(startFrame - xOffset / scale);
         mXTranslate.setScale(scale);
         VS_LOG("translate: %s", mXTranslate.toString().c_str());
     }
@@ -498,7 +516,11 @@
     }
 
     sp<VolumeShaper::State> getState() const {
-        return new VolumeShaper::State(mLastVolume, mXOffset);
+        return new VolumeShaper::State(mLastVolume, mLastXOffset);
+    }
+
+    void setDelayXOffset(S xOffset) {
+        mDelayXOffset = xOffset;
     }
 
     std::pair<T /* volume */, bool /* active */> getVolume(
@@ -506,7 +528,7 @@
         if ((getFlags() & VolumeShaper::Operation::FLAG_DELAY) != 0) {
             VS_LOG("delayed VolumeShaper, ignoring");
             mLastVolume = T(1);
-            mXOffset = 0.;
+            mLastXOffset = 0.;
             return std::make_pair(T(1), false);
         }
         const bool clockTime = (mConfiguration->getOptionFlags()
@@ -527,7 +549,7 @@
             x = 1.f - x;
             VS_LOG("reversing to %f", x);
             if (x < mConfiguration->first().first) {
-                mXOffset = 1.f;
+                mLastXOffset = 1.f;
                 const T volume = mConfiguration->adjustVolume(
                         mConfiguration->first().second);  // persist last value
                 VS_LOG("persisting volume %f", volume);
@@ -535,18 +557,18 @@
                 return std::make_pair(volume, false);
             }
             if (x > mConfiguration->last().first) {
-                mXOffset = 0.f;
+                mLastXOffset = 0.f;
                 mLastVolume = 1.f;
                 return std::make_pair(T(1), true); // too early
             }
         } else {
             if (x < mConfiguration->first().first) {
-                mXOffset = 0.f;
+                mLastXOffset = 0.f;
                 mLastVolume = 1.f;
                 return std::make_pair(T(1), true); // too early
             }
             if (x > mConfiguration->last().first) {
-                mXOffset = 1.f;
+                mLastXOffset = 1.f;
                 const T volume = mConfiguration->adjustVolume(
                         mConfiguration->last().second);  // persist last value
                 VS_LOG("persisting volume %f", volume);
@@ -554,11 +576,10 @@
                 return std::make_pair(volume, false);
             }
         }
-        mXOffset = x;
+        mLastXOffset = x;
         // x contains the location on the volume curve to use.
         const T unscaledVolume = mConfiguration->findY(x);
-        const T volumeChange = mYTranslate(unscaledVolume);
-        const T volume = mConfiguration->adjustVolume(volumeChange);
+        const T volume = mConfiguration->adjustVolume(unscaledVolume); // handle log scale
         VS_LOG("volume: %f  unscaled: %f", volume, unscaledVolume);
         mLastVolume = volume;
         return std::make_pair(volume, true);
@@ -568,7 +589,6 @@
         std::stringstream ss;
         ss << "StartFrame: " << mStartFrame << std::endl;
         ss << mXTranslate.toString().c_str();
-        ss << mYTranslate.toString().c_str();
         if (mConfiguration.get() == nullptr) {
             ss << "VolumeShaper::Configuration: nullptr" << std::endl;
         } else {
@@ -583,6 +603,14 @@
         }
         return ss.str();
     }
+
+    Translate<S> mXTranslate; // x axis translation from frames (in usec for clock time)
+    sp<VolumeShaper::Configuration> mConfiguration;
+    sp<VolumeShaper::Operation> mOperation;
+    int64_t mStartFrame; // starting frame, non-negative when started (in usec for clock time)
+    T mLastVolume;       // last computed interpolated volume (y-axis)
+    S mLastXOffset;      // last computed interpolated xOffset/time (x-axis)
+    S mDelayXOffset;     // delay xOffset on first volumeshaper start.
 }; // VolumeShaper
 
 // VolumeHandler combines the volume factors of multiple VolumeShapers and handles
@@ -592,9 +620,15 @@
     using S = float;
     using T = float;
 
+    // A volume handler which just keeps track of active VolumeShapers does not need sampleRate.
+    VolumeHandler()
+        : VolumeHandler(0 /* sampleRate */) {
+    }
+
     explicit VolumeHandler(uint32_t sampleRate)
         : mSampleRate((double)sampleRate)
-        , mLastFrame(0) {
+        , mLastFrame(0)
+        , mVolumeShaperIdCounter(VolumeShaper::kSystemIdMax) {
     }
 
     VolumeShaper::Status applyVolumeShaper(
@@ -638,10 +672,16 @@
                     }
                     (void)mVolumeShapers.erase(replaceIt);
                 }
+                operation->setReplaceId(-1);
             }
             // check if we have another of the same id.
             auto oldIt = findId_l(id);
             if (oldIt != mVolumeShapers.end()) {
+                if ((operation->getFlags()
+                        & VolumeShaper::Operation::FLAG_CREATE_IF_NECESSARY) != 0) {
+                    // TODO: move the case to a separate function.
+                    goto HANDLE_TYPE_ID; // no need to create, take over existing id.
+                }
                 ALOGW("duplicate id, removing old %d", id);
                 (void)mVolumeShapers.erase(oldIt);
             }
@@ -649,6 +689,7 @@
             mVolumeShapers.emplace_back(configuration, operation);
         }
         // fall through to handle the operation
+        HANDLE_TYPE_ID:
         case VolumeShaper::Configuration::TYPE_ID: {
             VS_LOG("trying to find id: %d", id);
             auto it = findId_l(id);
@@ -678,6 +719,17 @@
                 it->mXTranslate.setOffset(it->mXTranslate.getOffset()
                         + (x - target) / it->mXTranslate.getScale());
             }
+            const S xOffset = operation->getXOffset();
+            if (!std::isnan(xOffset)) {
+                const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
+                const S x = it->mXTranslate((T)frameCount);
+                VS_LOG("xOffset translation: %f", x);
+                const S target = xOffset; // offset
+                VS_LOG("xOffset target x offset: %f", target);
+                it->mXTranslate.setOffset(it->mXTranslate.getOffset()
+                        + (x - target) / it->mXTranslate.getScale());
+                it->setDelayXOffset(xOffset);
+            }
             it->mOperation = operation; // replace the operation
         } break;
         }
@@ -688,6 +740,7 @@
         AutoMutex _l(mLock);
         auto it = findId_l(id);
         if (it == mVolumeShapers.end()) {
+            VS_LOG("cannot find state for id: %d", id);
             return nullptr;
         }
         return it->getState();
@@ -719,6 +772,48 @@
         return ss.str();
     }
 
+    void forall(const std::function<VolumeShaper::Status (
+            const sp<VolumeShaper::Configuration> &configuration,
+            const sp<VolumeShaper::Operation> &operation)> &lambda) {
+        AutoMutex _l(mLock);
+        for (const auto &shaper : mVolumeShapers) {
+            VS_LOG("forall applying lambda");
+            (void)lambda(shaper.mConfiguration, shaper.mOperation);
+        }
+    }
+
+    void reset() {
+        AutoMutex _l(mLock);
+        mVolumeShapers.clear();
+        mLastFrame = -1;
+        // keep mVolumeShaperIdCounter as is.
+    }
+
+    // Sets the configuration id if necessary - This is based on the counter
+    // internal to the VolumeHandler.
+    void setIdIfNecessary(const sp<VolumeShaper::Configuration> &configuration) {
+        if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
+            const int id = configuration->getId();
+            if (id == -1) {
+                // Reassign to a unique id, skipping system ids.
+                AutoMutex _l(mLock);
+                while (true) {
+                    if (mVolumeShaperIdCounter == INT32_MAX) {
+                        mVolumeShaperIdCounter = VolumeShaper::kSystemIdMax;
+                    } else {
+                        ++mVolumeShaperIdCounter;
+                    }
+                    if (findId_l(mVolumeShaperIdCounter) != mVolumeShapers.end()) {
+                        continue; // collision with an existing id.
+                    }
+                    configuration->setId(mVolumeShaperIdCounter);
+                    ALOGD("setting id to %d", mVolumeShaperIdCounter);
+                    break;
+                }
+            }
+        }
+    }
+
 private:
     std::list<VolumeShaper>::iterator findId_l(int32_t id) {
         std::list<VolumeShaper>::iterator it = mVolumeShapers.begin();
@@ -733,6 +828,7 @@
     mutable Mutex mLock;
     double mSampleRate; // in samples (frames) per second
     int64_t mLastFrame; // logging purpose only
+    int32_t mVolumeShaperIdCounter; // a counter to return a unique volume shaper id.
     std::list<VolumeShaper> mVolumeShapers; // list provides stable iterators on erase
 }; // VolumeHandler