19fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung/*
29fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung * Copyright 2017 The Android Open Source Project
39fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung *
49fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung * Licensed under the Apache License, Version 2.0 (the "License");
59fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung * you may not use this file except in compliance with the License.
69fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung * You may obtain a copy of the License at
79fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung *
89fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung *      http://www.apache.org/licenses/LICENSE-2.0
99fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung *
109fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung * Unless required by applicable law or agreed to in writing, software
119fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung * distributed under the License is distributed on an "AS IS" BASIS,
129fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung * See the License for the specific language governing permissions and
149fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung * limitations under the License.
159fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung */
169fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
179fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung#ifndef ANDROID_VOLUME_SHAPER_H
189fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung#define ANDROID_VOLUME_SHAPER_H
199fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
204ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung#include <cmath>
219fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung#include <list>
229fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung#include <math.h>
239fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung#include <sstream>
249fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
259fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung#include <binder/Parcel.h>
269fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung#include <media/Interpolator.h>
279fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung#include <utils/Mutex.h>
289fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung#include <utils/RefBase.h>
299fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
309fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung#pragma push_macro("LOG_TAG")
319fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung#undef LOG_TAG
329fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung#define LOG_TAG "VolumeShaper"
339fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
349fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung// turn on VolumeShaper logging
359fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung#if 0
369fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung#define VS_LOG ALOGD
379fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung#else
389fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung#define VS_LOG(...)
399fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung#endif
409fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
419fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hungnamespace android {
429fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
439fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung// The native VolumeShaper class mirrors the java VolumeShaper class;
449fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung// in addition, the native class contains implementation for actual operation.
459fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung//
469fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung// VolumeShaper methods are not safe for multiple thread access.
479fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung// Use VolumeHandler for thread-safe encapsulation of multiple VolumeShapers.
489fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung//
499fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung// Classes below written are to avoid naked pointers so there are no
509fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung// explicit destructors required.
519fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
529fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hungclass VolumeShaper {
539fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hungpublic:
54f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    // S and T are like template typenames (matching the Interpolator<S, T>)
55f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    using S = float; // time type
56f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    using T = float; // volume type
57f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung
58f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung// Curve and dimension information
59f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung// TODO: member static const or constexpr float initialization not permitted in C++11
60f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung#define MIN_CURVE_TIME    0.f  // type S: start of VolumeShaper curve (normalized)
61f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung#define MAX_CURVE_TIME    1.f  // type S: end of VolumeShaper curve (normalized)
62f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung#define MIN_LINEAR_VOLUME 0.f  // type T: silence / mute audio
63f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung#define MAX_LINEAR_VOLUME 1.f  // type T: max volume, unity gain
64f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung#define MAX_LOG_VOLUME    0.f  // type T: max volume, unity gain in dBFS
65f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung
66f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    /* kSystemVolumeShapersMax is the maximum number of system VolumeShapers.
67f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * Each system VolumeShapers has a predefined Id, which ranges from 0
68f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * to kSystemVolumeShapersMax - 1 and is unique for its usage.
69f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     *
70f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * "1" is reserved for system ducking.
71f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     */
72f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    static const int kSystemVolumeShapersMax = 16;
73f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung
74f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    /* kUserVolumeShapersMax is the maximum number of application
75f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * VolumeShapers for a player/track.  Application VolumeShapers are
76f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * assigned on creation by the client, and have Ids ranging
77f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * from kSystemVolumeShapersMax to INT32_MAX.
78f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     *
79f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * The number of user/application volume shapers is independent to the
80f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * system volume shapers. If an application tries to create more than
81f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * kUserVolumeShapersMax to a player, then the apply() will fail.
82f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * This prevents exhausting server side resources by a potentially malicious
83f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * application.
84f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     */
85f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    static const int kUserVolumeShapersMax = 16;
86f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung
87f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    /* VolumeShaper::Status is equivalent to status_t if negative
88f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * but if non-negative represents the id operated on.
89f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * It must be expressible as an int32_t for binder purposes.
90f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     */
919fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    using Status = status_t;
929fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
93f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    // Local definition for clamp as std::clamp is included in C++17 only.
94f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    // TODO: use the std::clamp version when Android build uses C++17.
95f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    template<typename R>
96f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    static constexpr const R &clamp(const R &v, const R &lo, const R &hi) {
97f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        return (v < lo) ? lo : (hi < v) ? hi : v;
98f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    }
99f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung
100f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    /* VolumeShaper.Configuration derives from the Interpolator class and adds
101f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * parameters relating to the volume shape.
102f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     *
103f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * This parallels the Java implementation and the enums must match.
104f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
105f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * details on the Java implementation.
106f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     */
1079fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    class Configuration : public Interpolator<S, T>, public RefBase {
1089fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    public:
109f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        // Must match with VolumeShaper.java in frameworks/base.
1109fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        enum Type : int32_t {
1119fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            TYPE_ID,
1129fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            TYPE_SCALE,
1139fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        };
1149fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
115f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        // Must match with VolumeShaper.java in frameworks/base.
1169fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        enum OptionFlag : int32_t {
1179fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            OPTION_FLAG_NONE           = 0,
1189fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            OPTION_FLAG_VOLUME_IN_DBFS = (1 << 0),
1199fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            OPTION_FLAG_CLOCK_TIME     = (1 << 1),
1209fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
1219fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            OPTION_FLAG_ALL            = (OPTION_FLAG_VOLUME_IN_DBFS | OPTION_FLAG_CLOCK_TIME),
1229fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        };
1239fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
124f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        // Bring from base class; must match with VolumeShaper.java in frameworks/base.
1259fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        using InterpolatorType = Interpolator<S, T>::InterpolatorType;
1269fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
1279fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        Configuration()
1289fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            : Interpolator<S, T>()
129549bd02a8421b0b53a1b5e8961a41cf551fce72dColin Cross            , RefBase()
1309fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            , mType(TYPE_SCALE)
1319fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            , mOptionFlags(OPTION_FLAG_NONE)
1329fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            , mDurationMs(1000.)
1339fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            , mId(-1) {
1349fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
1359fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
1367d712bb0916182cb73f05ec9144b39314ddd5eabAndy Hung        explicit Configuration(const Configuration &configuration)
13710cbff139360f3f642e0e3b3ccf2d463dbed22cfAndy Hung            : Interpolator<S, T>(*static_cast<const Interpolator<S, T> *>(&configuration))
138549bd02a8421b0b53a1b5e8961a41cf551fce72dColin Cross            , RefBase()
13910cbff139360f3f642e0e3b3ccf2d463dbed22cfAndy Hung            , mType(configuration.mType)
14010cbff139360f3f642e0e3b3ccf2d463dbed22cfAndy Hung            , mOptionFlags(configuration.mOptionFlags)
14110cbff139360f3f642e0e3b3ccf2d463dbed22cfAndy Hung            , mDurationMs(configuration.mDurationMs)
14210cbff139360f3f642e0e3b3ccf2d463dbed22cfAndy Hung            , mId(configuration.mId) {
14310cbff139360f3f642e0e3b3ccf2d463dbed22cfAndy Hung        }
14410cbff139360f3f642e0e3b3ccf2d463dbed22cfAndy Hung
1459fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        Type getType() const {
1469fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return mType;
1479fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
1489fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
1499fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        status_t setType(Type type) {
1509fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            switch (type) {
1519fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            case TYPE_ID:
1529fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            case TYPE_SCALE:
1539fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                mType = type;
1549fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                return NO_ERROR;
1559fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            default:
1569fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                ALOGE("invalid Type: %d", type);
1579fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                return BAD_VALUE;
1589fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            }
1599fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
1609fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
1619fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        OptionFlag getOptionFlags() const {
1629fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return mOptionFlags;
1639fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
1649fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
1659fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        status_t setOptionFlags(OptionFlag optionFlags) {
1669fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            if ((optionFlags & ~OPTION_FLAG_ALL) != 0) {
1679fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                ALOGE("optionFlags has invalid bits: %#x", optionFlags);
1689fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                return BAD_VALUE;
1699fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            }
1709fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            mOptionFlags = optionFlags;
1719fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return NO_ERROR;
1729fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
1739fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
1749fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        double getDurationMs() const {
1759fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return mDurationMs;
1769fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
1779fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
178f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        status_t setDurationMs(double durationMs) {
179f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            if (durationMs > 0.) {
180f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                mDurationMs = durationMs;
181f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                return NO_ERROR;
182f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            }
183f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            // zero, negative, or nan. These values not possible from Java.
184f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            return BAD_VALUE;
1859fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
1869fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
1879fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        int32_t getId() const {
1889fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return mId;
1899fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
1909fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
1919fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        void setId(int32_t id) {
192f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            // We permit a negative id here (representing invalid).
1939fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            mId = id;
1949fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
1959fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
196f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        /* Adjust the volume to be in linear range from MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME
197f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung         * and compensate for log dbFS volume as needed.
198f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung         */
1999fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        T adjustVolume(T volume) const {
2009fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
201f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                const T out = powf(10.f, volume / 10.f);
2029fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                VS_LOG("in: %f  out: %f", volume, out);
2039fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                volume = out;
2049fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            }
205f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            return clamp(volume, MIN_LINEAR_VOLUME /* lo */, MAX_LINEAR_VOLUME /* hi */);
2069fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
2079fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
208f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        /* Check if the existing curve is valid.
209f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung         */
210f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        status_t checkCurve() const {
2119fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            if (mType == TYPE_ID) return NO_ERROR;
2129fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            if (this->size() < 2) {
2139fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                ALOGE("curve must have at least 2 points");
2149fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                return BAD_VALUE;
2159fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            }
216f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            if (first().first != MIN_CURVE_TIME || last().first != MAX_CURVE_TIME) {
217f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                ALOGE("curve must start at MIN_CURVE_TIME and end at MAX_CURVE_TIME");
2189fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                return BAD_VALUE;
2199fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            }
2209fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
2219fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                for (const auto &pt : *this) {
222f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                    if (!(pt.second <= MAX_LOG_VOLUME) /* handle nan */) {
2239fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                        ALOGE("positive volume dbFS");
2249fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                        return BAD_VALUE;
2259fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                    }
2269fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                }
2279fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            } else {
2289fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                for (const auto &pt : *this) {
229f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                    if (!(pt.second >= MIN_LINEAR_VOLUME)
230f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                            || !(pt.second <= MAX_LINEAR_VOLUME) /* handle nan */) {
231f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                        ALOGE("volume < MIN_LINEAR_VOLUME or > MAX_LINEAR_VOLUME");
2329fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                        return BAD_VALUE;
2339fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                    }
2349fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                }
2359fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            }
2369fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return NO_ERROR;
2379fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
2389fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
239f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        /* Clamps the volume curve in the configuration to
240f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung         * the valid range for log or linear scale.
241f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung         */
2429fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        void clampVolume() {
2439fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            if ((mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
2449fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                for (auto it = this->begin(); it != this->end(); ++it) {
245f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                    if (!(it->second <= MAX_LOG_VOLUME) /* handle nan */) {
246f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                        it->second = MAX_LOG_VOLUME;
2479fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                    }
2489fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                }
2499fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            } else {
2509fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                for (auto it = this->begin(); it != this->end(); ++it) {
251f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                    if (!(it->second >= MIN_LINEAR_VOLUME) /* handle nan */) {
252f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                        it->second = MIN_LINEAR_VOLUME;
253f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                    } else if (!(it->second <= MAX_LINEAR_VOLUME)) {
254f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                        it->second = MAX_LINEAR_VOLUME;
2559fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                    }
2569fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                }
2579fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            }
2589fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
2599fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
2609fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        /* scaleToStartVolume() is used to set the start volume of a
2619fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung         * new VolumeShaper curve, when replacing one VolumeShaper
2629fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung         * with another using the "join" (volume match) option.
2639fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung         *
2649fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung         * It works best for monotonic volume ramps or ducks.
2659fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung         */
2669fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        void scaleToStartVolume(T volume) {
2679fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            if (this->size() < 2) {
2689fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                return;
2699fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            }
2709fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            const T startVolume = first().second;
2719fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            const T endVolume = last().second;
2729fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            if (endVolume == startVolume) {
2739fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                // match with linear ramp
2749fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                const T offset = volume - startVolume;
275f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                static const T scale =  1.f / (MAX_CURVE_TIME - MIN_CURVE_TIME); // nominally 1.f
2769fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                for (auto it = this->begin(); it != this->end(); ++it) {
277f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                    it->second = it->second + offset * (MAX_CURVE_TIME - it->first) * scale;
2789fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                }
2799fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            } else {
2809fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                const T  scale = (volume - endVolume) / (startVolume - endVolume);
2819fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                for (auto it = this->begin(); it != this->end(); ++it) {
2829fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                    it->second = scale * (it->second - endVolume) + endVolume;
2839fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                }
2849fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            }
2859fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            clampVolume();
2869fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
2879fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
2887d712bb0916182cb73f05ec9144b39314ddd5eabAndy Hung        // The parcel layout must match VolumeShaper.java
2899fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        status_t writeToParcel(Parcel *parcel) const {
2909fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            if (parcel == nullptr) return BAD_VALUE;
2919fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return parcel->writeInt32((int32_t)mType)
2929fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                    ?: parcel->writeInt32(mId)
2939fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                    ?: mType == TYPE_ID
2949fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                        ? NO_ERROR
2959fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                        : parcel->writeInt32((int32_t)mOptionFlags)
2969fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                            ?: parcel->writeDouble(mDurationMs)
2979fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                            ?: Interpolator<S, T>::writeToParcel(parcel);
2989fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
2999fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
3009fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        status_t readFromParcel(const Parcel &parcel) {
3019fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            int32_t type, optionFlags;
3029fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return parcel.readInt32(&type)
3039fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                    ?: setType((Type)type)
3049fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                    ?: parcel.readInt32(&mId)
3059fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                    ?: mType == TYPE_ID
3069fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                        ? NO_ERROR
3079fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                        : parcel.readInt32(&optionFlags)
3089fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                            ?: setOptionFlags((OptionFlag)optionFlags)
3099fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                            ?: parcel.readDouble(&mDurationMs)
3109fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                            ?: Interpolator<S, T>::readFromParcel(parcel)
3119fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                            ?: checkCurve();
3129fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
3139fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
314f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        // Returns a string for debug printing.
3159fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        std::string toString() const {
3169fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            std::stringstream ss;
317f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            ss << "VolumeShaper::Configuration{mType=" << static_cast<int32_t>(mType);
318f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            ss << ", mId=" << mId;
3199fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            if (mType != TYPE_ID) {
320f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                ss << ", mOptionFlags=" << static_cast<int32_t>(mOptionFlags);
321f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                ss << ", mDurationMs=" << mDurationMs;
322f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                ss << ", " << Interpolator<S, T>::toString().c_str();
3239fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            }
324f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            ss << "}";
3259fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return ss.str();
3269fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
3279fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
3289fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    private:
329f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        Type mType;              // type of configuration
330f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        int32_t mId;             // A valid id is >= 0.
331f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        OptionFlag mOptionFlags; // option flags for the configuration.
332f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        double mDurationMs;      // duration, must be > 0; default is 1000 ms.
3339fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    }; // Configuration
3349fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
335f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    /* VolumeShaper::Operation expresses an operation to perform on the
336f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * configuration (either explicitly specified or an id).
337f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     *
338f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * This parallels the Java implementation and the enums must match.
339f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
340f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * details on the Java implementation.
341f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     */
3429fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    class Operation : public RefBase {
3439fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    public:
344f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        // Must match with VolumeShaper.java.
3459fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        enum Flag : int32_t {
3469fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            FLAG_NONE      = 0,
347f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            FLAG_REVERSE   = (1 << 0), // the absence of this indicates "play"
3489fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            FLAG_TERMINATE = (1 << 1),
3499fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            FLAG_JOIN      = (1 << 2),
3509fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            FLAG_DELAY     = (1 << 3),
3514ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung            FLAG_CREATE_IF_NECESSARY = (1 << 4),
3529fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
3534ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung            FLAG_ALL       = (FLAG_REVERSE | FLAG_TERMINATE | FLAG_JOIN | FLAG_DELAY
3544ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung                            | FLAG_CREATE_IF_NECESSARY),
3559fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        };
3569fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
3579fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        Operation()
3584ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung            : Operation(FLAG_NONE, -1 /* replaceId */) {
3599fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
3609fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
3617d712bb0916182cb73f05ec9144b39314ddd5eabAndy Hung        Operation(Flag flags, int replaceId)
3624ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung            : Operation(flags, replaceId, std::numeric_limits<S>::quiet_NaN() /* xOffset */) {
3634ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung        }
3644ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung
3657d712bb0916182cb73f05ec9144b39314ddd5eabAndy Hung        explicit Operation(const Operation &operation)
3664ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung            : Operation(operation.mFlags, operation.mReplaceId, operation.mXOffset) {
3674ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung        }
3684ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung
3697d712bb0916182cb73f05ec9144b39314ddd5eabAndy Hung        explicit Operation(const sp<Operation> &operation)
3707d712bb0916182cb73f05ec9144b39314ddd5eabAndy Hung            : Operation(*operation.get()) {
3717d712bb0916182cb73f05ec9144b39314ddd5eabAndy Hung        }
3727d712bb0916182cb73f05ec9144b39314ddd5eabAndy Hung
3737d712bb0916182cb73f05ec9144b39314ddd5eabAndy Hung        Operation(Flag flags, int replaceId, S xOffset)
3749fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            : mFlags(flags)
3754ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung            , mReplaceId(replaceId)
3764ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung            , mXOffset(xOffset) {
3779fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
3789fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
3799fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        int32_t getReplaceId() const {
3809fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return mReplaceId;
3819fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
3829fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
3839fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        void setReplaceId(int32_t replaceId) {
3849fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            mReplaceId = replaceId;
3859fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
3869fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
3874ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung        S getXOffset() const {
3884ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung            return mXOffset;
3894ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung        }
3904ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung
3914ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung        void setXOffset(S xOffset) {
392f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            mXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
3934ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung        }
3944ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung
3959fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        Flag getFlags() const {
3969fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return mFlags;
3979fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
3989fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
399f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        /* xOffset is the position on the volume curve and may go backwards
400f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung         * if you are in reverse mode. This must be in the range from
401f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung         * [MIN_CURVE_TIME, MAX_CURVE_TIME].
402f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung         *
403f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung         * normalizedTime always increases as time or framecount increases.
404f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung         * normalizedTime is nominally from MIN_CURVE_TIME to MAX_CURVE_TIME when
405f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung         * running through the curve, but could be outside this range afterwards.
406f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung         * If you are reversing, this means the position on the curve, or xOffset,
407f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung         * is computed as MAX_CURVE_TIME - normalizedTime, clamped to
408f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung         * [MIN_CURVE_TIME, MAX_CURVE_TIME].
409f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung         */
410f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        void setNormalizedTime(S normalizedTime) {
411f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            setXOffset((mFlags & FLAG_REVERSE) != 0
412f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                    ? MAX_CURVE_TIME - normalizedTime : normalizedTime);
413f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        }
414f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung
4159fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        status_t setFlags(Flag flags) {
4169fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            if ((flags & ~FLAG_ALL) != 0) {
4179fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                ALOGE("flags has invalid bits: %#x", flags);
4189fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                return BAD_VALUE;
4199fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            }
4209fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            mFlags = flags;
4219fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return NO_ERROR;
4229fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
4239fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
4249fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        status_t writeToParcel(Parcel *parcel) const {
4259fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            if (parcel == nullptr) return BAD_VALUE;
4269fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return parcel->writeInt32((int32_t)mFlags)
4274ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung                    ?: parcel->writeInt32(mReplaceId)
4284ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung                    ?: parcel->writeFloat(mXOffset);
4299fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
4309fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
4319fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        status_t readFromParcel(const Parcel &parcel) {
4329fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            int32_t flags;
4339fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return parcel.readInt32(&flags)
4349fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                    ?: parcel.readInt32(&mReplaceId)
4354ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung                    ?: parcel.readFloat(&mXOffset)
4369fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                    ?: setFlags((Flag)flags);
4379fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
4389fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
4399fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        std::string toString() const {
4409fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            std::stringstream ss;
441f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            ss << "VolumeShaper::Operation{mFlags=" << static_cast<int32_t>(mFlags) ;
442f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            ss << ", mReplaceId=" << mReplaceId;
443f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            ss << ", mXOffset=" << mXOffset;
444f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            ss << "}";
4459fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return ss.str();
4469fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
4479fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
4489fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    private:
449f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        Flag mFlags;        // operation to do
450f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        int32_t mReplaceId; // if >= 0 the id to remove in a replace operation.
451f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        S mXOffset;         // position in the curve to set if a valid number (not nan)
4529fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    }; // Operation
4539fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
454f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    /* VolumeShaper.State is returned when requesting the last
455f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * state of the VolumeShaper.
456f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     *
457f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * This parallels the Java implementation.
458f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
459f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * details on the Java implementation.
460f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     */
4619fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    class State : public RefBase {
4629fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    public:
4637d712bb0916182cb73f05ec9144b39314ddd5eabAndy Hung        State(T volume, S xOffset)
4649fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            : mVolume(volume)
4659fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            , mXOffset(xOffset) {
4669fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
4679fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
4689fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        State()
469f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            : State(NAN, NAN) { }
4709fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
4719fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        T getVolume() const {
4729fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return mVolume;
4739fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
4749fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
4759fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        void setVolume(T volume) {
4769fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            mVolume = volume;
4779fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
4789fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
4799fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        S getXOffset() const {
4809fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return mXOffset;
4819fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
4829fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
4839fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        void setXOffset(S xOffset) {
4849fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            mXOffset = xOffset;
4859fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
4869fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
4879fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        status_t writeToParcel(Parcel *parcel) const {
4889fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            if (parcel == nullptr) return BAD_VALUE;
4899fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return parcel->writeFloat(mVolume)
4909fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                    ?: parcel->writeFloat(mXOffset);
4919fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
4929fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
4939fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        status_t readFromParcel(const Parcel &parcel) {
4949fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return parcel.readFloat(&mVolume)
4959fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                     ?: parcel.readFloat(&mXOffset);
4969fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
4979fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
4989fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        std::string toString() const {
4999fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            std::stringstream ss;
500f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            ss << "VolumeShaper::State{mVolume=" << mVolume;
501f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            ss << ", mXOffset=" << mXOffset;
502f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            ss << "}";
5039fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return ss.str();
5049fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
5059fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
5069fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    private:
507f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        T mVolume;   // linear volume in the range MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME
508f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        S mXOffset;  // position on curve expressed from MIN_CURVE_TIME to MAX_CURVE_TIME
5099fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    }; // State
5109fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
511f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    // Internal helper class to do an affine transform for time and amplitude scaling.
5129fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    template <typename R>
5139fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    class Translate {
5149fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    public:
5159fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        Translate()
5169fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            : mOffset(0)
5179fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            , mScale(1) {
5189fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
5199fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
5209fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        R getOffset() const {
5219fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return mOffset;
5229fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
5239fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
5249fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        void setOffset(R offset) {
5259fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            mOffset = offset;
5269fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
5279fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
5289fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        R getScale() const {
5299fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return mScale;
5309fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
5319fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
5329fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        void setScale(R scale) {
5339fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            mScale = scale;
5349fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
5359fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
5369fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        R operator()(R in) const {
5379fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return mScale * (in - mOffset);
5389fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
5399fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
5409fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        std::string toString() const {
5419fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            std::stringstream ss;
542f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            ss << "VolumeShaper::Translate{mOffset=" << mOffset;
543f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            ss << ", mScale=" << mScale;
544f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            ss << "}";
5459fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return ss.str();
5469fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
5479fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
5489fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    private:
5499fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        R mOffset;
5509fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        R mScale;
5519fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    }; // Translate
5529fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
5539fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    static int64_t convertTimespecToUs(const struct timespec &tv)
5549fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    {
5559fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        return tv.tv_sec * 1000000ll + tv.tv_nsec / 1000;
5569fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    }
5579fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
5589fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    // current monotonic time in microseconds.
5599fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    static int64_t getNowUs()
5609fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    {
5619fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        struct timespec tv;
5629fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        if (clock_gettime(CLOCK_MONOTONIC, &tv) != 0) {
5639fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            return 0; // system is really sick, just return 0 for consistency.
5649fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
5659fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        return convertTimespecToUs(tv);
5669fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    }
5679fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
568f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    /* Native implementation of VolumeShaper.  This is NOT mirrored
569f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * on the Java side, so we don't need to mimic Java side layout
570f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * and data; furthermore, this isn't refcounted as a "RefBase" object.
571f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     *
572f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * Since we pass configuration and operation as shared pointers (like
573f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * Java) there is a potential risk that the caller may modify
574f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * these after delivery.
575f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     */
5767d712bb0916182cb73f05ec9144b39314ddd5eabAndy Hung    VolumeShaper(
5779fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            const sp<VolumeShaper::Configuration> &configuration,
5789fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            const sp<VolumeShaper::Operation> &operation)
5799fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        : mConfiguration(configuration) // we do not make a copy
5809fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        , mOperation(operation)         // ditto
5819fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        , mStartFrame(-1)
5829fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        , mLastVolume(T(1))
583f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        , mLastXOffset(MIN_CURVE_TIME)
584f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        , mDelayXOffset(MIN_CURVE_TIME) {
5859fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        if (configuration.get() != nullptr
5869fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                && (getFlags() & VolumeShaper::Operation::FLAG_DELAY) == 0) {
5879fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            mLastVolume = configuration->first().second;
5889fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
5899fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    }
5909fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
5919fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    // We allow a null operation here, though VolumeHandler always provides one.
5929fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    VolumeShaper::Operation::Flag getFlags() const {
5939fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        return mOperation == nullptr
594f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                ? VolumeShaper::Operation::FLAG_NONE : mOperation->getFlags();
5959fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    }
5969fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
597f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    /* Returns the last volume and xoffset reported to the AudioFlinger.
598f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * If the VolumeShaper has not been started, compute what the volume
599f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * should be based on the initial offset specified.
600f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     */
6019fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    sp<VolumeShaper::State> getState() const {
602f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        if (!isStarted()) {
603f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            const T volume = computeVolumeFromXOffset(mDelayXOffset);
604f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            VS_LOG("delayed VolumeShaper, using cached offset:%f for volume:%f",
605f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                    mDelayXOffset, volume);
606f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            return new VolumeShaper::State(volume, mDelayXOffset);
607f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        } else {
608f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            return new VolumeShaper::State(mLastVolume, mLastXOffset);
609f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        }
610f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    }
611f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung
612f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    S getDelayXOffset() const {
613f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        return mDelayXOffset;
6144ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung    }
6154ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung
6164ef88d7106c01f81109ee163cb6789073d80c6aeAndy Hung    void setDelayXOffset(S xOffset) {
617f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        mDelayXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
6189fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung    }
6199fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
62039399b6b08b4e9fd7eae50e58e93b07216ad697fAndy Hung    bool isStarted() const {
62139399b6b08b4e9fd7eae50e58e93b07216ad697fAndy Hung        return mStartFrame >= 0;
62239399b6b08b4e9fd7eae50e58e93b07216ad697fAndy Hung    }
62339399b6b08b4e9fd7eae50e58e93b07216ad697fAndy Hung
624f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung    /* getVolume() updates the last volume/xoffset state so it is not
625f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     * const, even though logically it may be viewed as const.
626f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung     */
62710cbff139360f3f642e0e3b3ccf2d463dbed22cfAndy Hung    std::pair<T /* volume */, bool /* active */> getVolume(
62810cbff139360f3f642e0e3b3ccf2d463dbed22cfAndy Hung            int64_t trackFrameCount, double trackSampleRate) {
6299fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        if ((getFlags() & VolumeShaper::Operation::FLAG_DELAY) != 0) {
630f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            // We haven't had PLAY called yet, so just return the value
631f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            // as if PLAY were called just now.
632f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            VS_LOG("delayed VolumeShaper, using cached offset %f", mDelayXOffset);
633f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            const T volume = computeVolumeFromXOffset(mDelayXOffset);
634f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            return std::make_pair(volume, false);
6359fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
6369fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        const bool clockTime = (mConfiguration->getOptionFlags()
6379fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung                & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
6389fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        const int64_t frameCount = clockTime ? getNowUs() : trackFrameCount;
6399fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        const double sampleRate = clockTime ? 1000000 : trackSampleRate;
6409fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung
6419fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        if (mStartFrame < 0) {
642f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung            updatePosition(frameCount, sampleRate, mDelayXOffset);
6439fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung            mStartFrame = frameCount;
6449fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        }
6459fc8b5cd4a64ef07e84c69112461324d5c13a0b0Andy Hung        VS_LOG("frameCount: %lld", (long long)frameCount);
646f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        const S x = mXTranslate((T)frameCount);
647f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        VS_LOG("translation to normalized time: %f", x);
648f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung
649f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        std::tuple<T /* volume */, S /* position */, bool /* active */> vt =
650f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung                computeStateFromNormalizedTime(x);
651f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung
652f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        mLastVolume = std::get<0>(vt);
653f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        mLastXOffset = std::get<1>(vt);
654f370264dfa5237524a3535f5242a0c68cbbb13ebAndy Hung        const bool active = std::get<2>(vt);
655