VolumeShaper: Enable for offload and direct tracks

Test: Play Music in offload mode
Bug: 31015569
Change-Id: I00bb59e3e6809d4682f42057b1cc083f4fa9b9d1
diff --git a/include/media/VolumeShaper.h b/include/media/VolumeShaper.h
index acb22ab..1421ae7 100644
--- a/include/media/VolumeShaper.h
+++ b/include/media/VolumeShaper.h
@@ -94,6 +94,14 @@
             , mId(-1) {
         }
 
+        Configuration(const Configuration &configuration)
+            : Interpolator<S, T>(*static_cast<const Interpolator<S, T> *>(&configuration))
+            , mType(configuration.mType)
+            , mOptionFlags(configuration.mOptionFlags)
+            , mDurationMs(configuration.mDurationMs)
+            , mId(configuration.mId) {
+        }
+
         Type getType() const {
             return mType;
         }
@@ -493,13 +501,8 @@
         return new VolumeShaper::State(mLastVolume, mXOffset);
     }
 
-    std::pair<T, bool> getVolume(int64_t trackFrameCount, double trackSampleRate) {
-        if (mConfiguration.get() == nullptr || mConfiguration->empty()) {
-            ALOGE("nonexistent VolumeShaper, removing");
-            mLastVolume = T(1);
-            mXOffset = 0.f;
-            return std::make_pair(T(1), true);
-        }
+    std::pair<T /* volume */, bool /* active */> getVolume(
+            int64_t trackFrameCount, double trackSampleRate) {
         if ((getFlags() & VolumeShaper::Operation::FLAG_DELAY) != 0) {
             VS_LOG("delayed VolumeShaper, ignoring");
             mLastVolume = T(1);
@@ -534,13 +537,13 @@
             if (x > mConfiguration->last().first) {
                 mXOffset = 0.f;
                 mLastVolume = 1.f;
-                return std::make_pair(T(1), false); // too early
+                return std::make_pair(T(1), true); // too early
             }
         } else {
             if (x < mConfiguration->first().first) {
                 mXOffset = 0.f;
                 mLastVolume = 1.f;
-                return std::make_pair(T(1), false); // too early
+                return std::make_pair(T(1), true); // too early
             }
             if (x > mConfiguration->last().first) {
                 mXOffset = 1.f;
@@ -558,7 +561,7 @@
         const T volume = mConfiguration->adjustVolume(volumeChange);
         VS_LOG("volume: %f  unscaled: %f", volume, unscaledVolume);
         mLastVolume = volume;
-        return std::make_pair(volume, false);
+        return std::make_pair(volume, true);
     }
 
     std::string toString() const {
@@ -597,6 +600,8 @@
     VolumeShaper::Status applyVolumeShaper(
             const sp<VolumeShaper::Configuration> &configuration,
             const sp<VolumeShaper::Operation> &operation) {
+        VS_LOG("applyVolumeShaper:configuration: %s", configuration->toString().c_str());
+        VS_LOG("applyVolumeShaper:operation: %s", operation->toString().c_str());
         AutoMutex _l(mLock);
         if (configuration == nullptr) {
             ALOGE("null configuration");
@@ -614,34 +619,6 @@
         VS_LOG("applyVolumeShaper id: %d", id);
 
         switch (configuration->getType()) {
-        case VolumeShaper::Configuration::TYPE_ID: {
-            VS_LOG("trying to find id: %d", id);
-            auto it = findId_l(id);
-            if (it == mVolumeShapers.end()) {
-                VS_LOG("couldn't find id: %d\n%s", id, this->toString().c_str());
-                return VolumeShaper::Status(INVALID_OPERATION);
-            }
-            if ((it->getFlags() & VolumeShaper::Operation::FLAG_TERMINATE) != 0) {
-                VS_LOG("terminate id: %d", id);
-                mVolumeShapers.erase(it);
-                break;
-            }
-            if ((it->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) !=
-                    (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE)) {
-                const S x = it->mXTranslate((T)mLastFrame);
-                VS_LOG("translation: %f", x);
-                // reflect position
-                S target = 1.f - x;
-                if (target < it->mConfiguration->first().first) {
-                    VS_LOG("clamp to start - begin immediately");
-                    target = 0.;
-                }
-                VS_LOG("target: %f", target);
-                it->mXTranslate.setOffset(it->mXTranslate.getOffset()
-                        + (x - target) / it->mXTranslate.getScale());
-            }
-            it->mOperation = operation; // replace the operation
-        } break;
         case VolumeShaper::Configuration::TYPE_SCALE: {
             const int replaceId = operation->getReplaceId();
             if (replaceId >= 0) {
@@ -670,6 +647,38 @@
             }
             // create new VolumeShaper
             mVolumeShapers.emplace_back(configuration, operation);
+        }
+        // fall through to handle the operation
+        case VolumeShaper::Configuration::TYPE_ID: {
+            VS_LOG("trying to find id: %d", id);
+            auto it = findId_l(id);
+            if (it == mVolumeShapers.end()) {
+                VS_LOG("couldn't find id: %d", id);
+                return VolumeShaper::Status(INVALID_OPERATION);
+            }
+            if ((it->getFlags() & VolumeShaper::Operation::FLAG_TERMINATE) != 0) {
+                VS_LOG("terminate id: %d", id);
+                mVolumeShapers.erase(it);
+                break;
+            }
+            const bool clockTime = (it->mConfiguration->getOptionFlags()
+                    & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
+            if ((it->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) !=
+                    (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE)) {
+                const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
+                const S x = it->mXTranslate((T)frameCount);
+                VS_LOG("reverse translation: %f", x);
+                // reflect position
+                S target = 1.f - x;
+                if (target < it->mConfiguration->first().first) {
+                    VS_LOG("clamp to start - begin immediately");
+                    target = 0.;
+                }
+                VS_LOG("target reverse: %f", target);
+                it->mXTranslate.setOffset(it->mXTranslate.getOffset()
+                        + (x - target) / it->mXTranslate.getScale());
+            }
+            it->mOperation = operation; // replace the operation
         } break;
         }
         return VolumeShaper::Status(id);
@@ -684,21 +693,19 @@
         return it->getState();
     }
 
-    T getVolume(int64_t trackFrameCount) {
+    std::pair<T /* volume */, bool /* active */> getVolume(int64_t trackFrameCount) {
         AutoMutex _l(mLock);
         mLastFrame = trackFrameCount;
         T volume(1);
+        size_t activeCount = 0;
         for (auto it = mVolumeShapers.begin(); it != mVolumeShapers.end();) {
             std::pair<T, bool> shaperVolume =
                     it->getVolume(trackFrameCount, mSampleRate);
             volume *= shaperVolume.first;
-            if (shaperVolume.second) {
-                it = mVolumeShapers.erase(it);
-                continue;
-            }
+            activeCount += shaperVolume.second;
             ++it;
         }
-        return volume;
+        return std::make_pair(volume, activeCount != 0);
     }
 
     std::string toString() const {
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index f33c710..f84ba08 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -59,6 +59,10 @@
             bool        isOffloaded() const
                                 { return (mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0; }
             bool        isDirect() const { return (mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0; }
+            bool        isOffloadedOrDirect() const { return (mFlags
+                            & (AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD
+                                    | AUDIO_OUTPUT_FLAG_DIRECT)) != 0; }
+
             status_t    setParameters(const String8& keyValuePairs);
             status_t    attachAuxEffect(int EffectId);
             void        setAuxBuffer(int EffectId, int32_t *buffer);
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 8bf1688..b10e42c 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3063,8 +3063,13 @@
                     releaseWakeLock_l();
                     released = true;
                 }
-                ALOGV("wait async completion");
-                mWaitWorkCV.wait(mLock);
+
+                const int64_t waitNs = computeWaitTimeNs_l();
+                ALOGV("wait async completion (wait time: %lld)", (long long)waitNs);
+                status_t status = mWaitWorkCV.waitRelative(mLock, waitNs);
+                if (status == TIMED_OUT) {
+                    mSignalPending = true; // if timeout recheck everything
+                }
                 ALOGV("async completion/wake");
                 if (released) {
                     acquireWakeLock_l();
@@ -4131,7 +4136,7 @@
                 // cache the combined master volume and stream type volume for fast mixer; this
                 // lacks any synchronization or barrier so VolumeProvider may read a stale value
                 const float vh = track->getVolumeHandler()->getVolume(
-                        track->mAudioTrackServerProxy->framesReleased());
+                        track->mAudioTrackServerProxy->framesReleased()).first;
                 track->mCachedVolume = masterVolume
                         * mStreamTypes[track->streamType()].volume
                         * vh;
@@ -4277,7 +4282,7 @@
                     vrf = GAIN_FLOAT_UNITY;
                 }
                 const float vh = track->getVolumeHandler()->getVolume(
-                        track->mAudioTrackServerProxy->framesReleased());
+                        track->mAudioTrackServerProxy->framesReleased()).first;
                 // now apply the master volume and stream type volume and shaper volume
                 vlf *= v * vh;
                 vrf *= v * vh;
@@ -4756,6 +4761,7 @@
         ThreadBase::type_t type, bool systemReady)
     :   PlaybackThread(audioFlinger, output, id, device, type, systemReady)
         // mLeftVolFloat, mRightVolFloat
+        , mVolumeShaperActive(false)
 {
 }
 
@@ -4774,13 +4780,12 @@
         float v = mMasterVolume * typeVolume;
         sp<AudioTrackServerProxy> proxy = track->mAudioTrackServerProxy;
 
-        if (audio_is_linear_pcm(mFormat) && !usesHwAvSync()) {
-            const float vh = track->getVolumeHandler()->getVolume(
+        // Get volumeshaper scaling
+        std::pair<float /* volume */, bool /* active */>
+            vh = track->getVolumeHandler()->getVolume(
                     track->mAudioTrackServerProxy->framesReleased());
-            v *= vh;
-        } else {
-            // TODO: implement volume scaling in HW
-        }
+        v *= vh.first;
+        mVolumeShaperActive = vh.second;
 
         gain_minifloat_packed_t vlr = proxy->getVolumeLR();
         left = float_from_gain(gain_minifloat_unpack_left(vlr));
@@ -5238,6 +5243,13 @@
     mFlushPending = false;
 }
 
+int64_t AudioFlinger::DirectOutputThread::computeWaitTimeNs_l() const {
+    // If a VolumeShaper is active, we must wake up periodically to update volume.
+    const int64_t NS_PER_MS = 1000000;
+    return mVolumeShaperActive ?
+            kMinNormalSinkBufferSizeMs * NS_PER_MS : PlaybackThread::computeWaitTimeNs_l();
+}
+
 // ----------------------------------------------------------------------------
 
 AudioFlinger::AsyncCallbackThread::AsyncCallbackThread(
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 2a6652d..0a17a8e 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -757,6 +757,9 @@
 
     virtual     void        getAudioPortConfig(struct audio_port_config *config);
 
+                // Return the asynchronous signal wait time.
+    virtual     int64_t     computeWaitTimeNs_l() const { return INT64_MAX; }
+
 protected:
     // updated by readOutputParameters_l()
     size_t                          mNormalFrameCount;  // normal mixer and effects
@@ -1174,6 +1177,7 @@
     // volumes last sent to audio HAL with stream->set_volume()
     float mLeftVolFloat;
     float mRightVolFloat;
+    bool mVolumeShaperActive;
 
     DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
                         audio_io_handle_t id, uint32_t device, ThreadBase::type_t type,
@@ -1187,6 +1191,8 @@
 
 public:
     virtual     bool        hasFastMixer() const { return false; }
+
+    virtual     int64_t     computeWaitTimeNs_l() const override;
 };
 
 class OffloadThread : public DirectOutputThread {
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index edf41fd..5e07e3b 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -905,10 +905,33 @@
         const sp<VolumeShaper::Configuration>& configuration,
         const sp<VolumeShaper::Operation>& operation)
 {
-    // Note: We don't check if Thread exists.
+    sp<VolumeShaper::Configuration> newConfiguration;
 
-    // mVolumeHandler is thread-safe.
-    return mVolumeHandler->applyVolumeShaper(configuration, operation);
+    if (isOffloadedOrDirect()) {
+        const VolumeShaper::Configuration::OptionFlag optionFlag
+            = configuration->getOptionFlags();
+        if ((optionFlag & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) == 0) {
+            ALOGW("%s tracks do not support frame counted VolumeShaper,"
+                    " using clock time instead", isOffloaded() ? "Offload" : "Direct");
+            newConfiguration = new VolumeShaper::Configuration(*configuration);
+            newConfiguration->setOptionFlags(
+                VolumeShaper::Configuration::OptionFlag(optionFlag
+                        | VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME));
+        }
+    }
+
+    VolumeShaper::Status status = mVolumeHandler->applyVolumeShaper(
+            (newConfiguration.get() != nullptr ? newConfiguration : configuration), operation);
+
+    if (isOffloadedOrDirect()) {
+        // Signal thread to fetch new volume.
+        sp<ThreadBase> thread = mThread.promote();
+        if (thread != 0) {
+             Mutex::Autolock _l(thread->mLock);
+            thread->broadcast_l();
+        }
+    }
+    return status;
 }
 
 sp<VolumeShaper::State> AudioFlinger::PlaybackThread::Track::getVolumeShaperState(int id)