xref: /aosp_15_r20/frameworks/av/include/media/VolumeShaper.h (revision ec779b8e0859a360c3d303172224686826e6e0e1)
1*ec779b8eSAndroid Build Coastguard Worker /*
2*ec779b8eSAndroid Build Coastguard Worker  * Copyright 2017 The Android Open Source Project
3*ec779b8eSAndroid Build Coastguard Worker  *
4*ec779b8eSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*ec779b8eSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*ec779b8eSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*ec779b8eSAndroid Build Coastguard Worker  *
8*ec779b8eSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*ec779b8eSAndroid Build Coastguard Worker  *
10*ec779b8eSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*ec779b8eSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*ec779b8eSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*ec779b8eSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*ec779b8eSAndroid Build Coastguard Worker  * limitations under the License.
15*ec779b8eSAndroid Build Coastguard Worker  */
16*ec779b8eSAndroid Build Coastguard Worker 
17*ec779b8eSAndroid Build Coastguard Worker #ifndef ANDROID_VOLUME_SHAPER_H
18*ec779b8eSAndroid Build Coastguard Worker #define ANDROID_VOLUME_SHAPER_H
19*ec779b8eSAndroid Build Coastguard Worker 
20*ec779b8eSAndroid Build Coastguard Worker #include <cmath>
21*ec779b8eSAndroid Build Coastguard Worker #include <list>
22*ec779b8eSAndroid Build Coastguard Worker #include <math.h>
23*ec779b8eSAndroid Build Coastguard Worker #include <sstream>
24*ec779b8eSAndroid Build Coastguard Worker 
25*ec779b8eSAndroid Build Coastguard Worker #include <android/media/VolumeShaperConfiguration.h>
26*ec779b8eSAndroid Build Coastguard Worker #include <android/media/VolumeShaperConfigurationOptionFlag.h>
27*ec779b8eSAndroid Build Coastguard Worker #include <android/media/VolumeShaperOperation.h>
28*ec779b8eSAndroid Build Coastguard Worker #include <android/media/VolumeShaperOperationFlag.h>
29*ec779b8eSAndroid Build Coastguard Worker #include <android/media/VolumeShaperState.h>
30*ec779b8eSAndroid Build Coastguard Worker #include <binder/Parcel.h>
31*ec779b8eSAndroid Build Coastguard Worker #include <media/Interpolator.h>
32*ec779b8eSAndroid Build Coastguard Worker #include <utils/Mutex.h>
33*ec779b8eSAndroid Build Coastguard Worker #include <utils/RefBase.h>
34*ec779b8eSAndroid Build Coastguard Worker 
35*ec779b8eSAndroid Build Coastguard Worker #pragma push_macro("LOG_TAG")
36*ec779b8eSAndroid Build Coastguard Worker #undef LOG_TAG
37*ec779b8eSAndroid Build Coastguard Worker #define LOG_TAG "VolumeShaper"
38*ec779b8eSAndroid Build Coastguard Worker 
39*ec779b8eSAndroid Build Coastguard Worker // turn on VolumeShaper logging
40*ec779b8eSAndroid Build Coastguard Worker #define VS_LOGGING 0
41*ec779b8eSAndroid Build Coastguard Worker #define VS_LOG(...) ALOGD_IF(VS_LOGGING, __VA_ARGS__)
42*ec779b8eSAndroid Build Coastguard Worker 
43*ec779b8eSAndroid Build Coastguard Worker namespace android {
44*ec779b8eSAndroid Build Coastguard Worker 
45*ec779b8eSAndroid Build Coastguard Worker namespace media {
46*ec779b8eSAndroid Build Coastguard Worker 
47*ec779b8eSAndroid Build Coastguard Worker // The native VolumeShaper class mirrors the java VolumeShaper class;
48*ec779b8eSAndroid Build Coastguard Worker // in addition, the native class contains implementation for actual operation.
49*ec779b8eSAndroid Build Coastguard Worker //
50*ec779b8eSAndroid Build Coastguard Worker // VolumeShaper methods are not safe for multiple thread access.
51*ec779b8eSAndroid Build Coastguard Worker // Use VolumeHandler for thread-safe encapsulation of multiple VolumeShapers.
52*ec779b8eSAndroid Build Coastguard Worker //
53*ec779b8eSAndroid Build Coastguard Worker // Classes below written are to avoid naked pointers so there are no
54*ec779b8eSAndroid Build Coastguard Worker // explicit destructors required.
55*ec779b8eSAndroid Build Coastguard Worker 
56*ec779b8eSAndroid Build Coastguard Worker class VolumeShaper {
57*ec779b8eSAndroid Build Coastguard Worker public:
58*ec779b8eSAndroid Build Coastguard Worker     // S and T are like template typenames (matching the Interpolator<S, T>)
59*ec779b8eSAndroid Build Coastguard Worker     using S = float; // time type
60*ec779b8eSAndroid Build Coastguard Worker     using T = float; // volume type
61*ec779b8eSAndroid Build Coastguard Worker 
62*ec779b8eSAndroid Build Coastguard Worker // Curve and dimension information
63*ec779b8eSAndroid Build Coastguard Worker // TODO: member static const or constexpr float initialization not permitted in C++11
64*ec779b8eSAndroid Build Coastguard Worker #define MIN_CURVE_TIME    0.f  // type S: start of VolumeShaper curve (normalized)
65*ec779b8eSAndroid Build Coastguard Worker #define MAX_CURVE_TIME    1.f  // type S: end of VolumeShaper curve (normalized)
66*ec779b8eSAndroid Build Coastguard Worker #define MIN_LINEAR_VOLUME 0.f  // type T: silence / mute audio
67*ec779b8eSAndroid Build Coastguard Worker #define MAX_LINEAR_VOLUME 1.f  // type T: max volume, unity gain
68*ec779b8eSAndroid Build Coastguard Worker #define MAX_LOG_VOLUME    0.f  // type T: max volume, unity gain in dBFS
69*ec779b8eSAndroid Build Coastguard Worker 
70*ec779b8eSAndroid Build Coastguard Worker     /* kSystemVolumeShapersMax is the maximum number of system VolumeShapers.
71*ec779b8eSAndroid Build Coastguard Worker      * Each system VolumeShapers has a predefined Id, which ranges from 0
72*ec779b8eSAndroid Build Coastguard Worker      * to kSystemVolumeShapersMax - 1 and is unique for its usage.
73*ec779b8eSAndroid Build Coastguard Worker      *
74*ec779b8eSAndroid Build Coastguard Worker      * "1" is reserved for system ducking.
75*ec779b8eSAndroid Build Coastguard Worker      */
76*ec779b8eSAndroid Build Coastguard Worker     static const int kSystemVolumeShapersMax = 16;
77*ec779b8eSAndroid Build Coastguard Worker 
78*ec779b8eSAndroid Build Coastguard Worker     /* kUserVolumeShapersMax is the maximum number of application
79*ec779b8eSAndroid Build Coastguard Worker      * VolumeShapers for a player/track.  Application VolumeShapers are
80*ec779b8eSAndroid Build Coastguard Worker      * assigned on creation by the client, and have Ids ranging
81*ec779b8eSAndroid Build Coastguard Worker      * from kSystemVolumeShapersMax to INT32_MAX.
82*ec779b8eSAndroid Build Coastguard Worker      *
83*ec779b8eSAndroid Build Coastguard Worker      * The number of user/application volume shapers is independent to the
84*ec779b8eSAndroid Build Coastguard Worker      * system volume shapers. If an application tries to create more than
85*ec779b8eSAndroid Build Coastguard Worker      * kUserVolumeShapersMax to a player, then the apply() will fail.
86*ec779b8eSAndroid Build Coastguard Worker      * This prevents exhausting server side resources by a potentially malicious
87*ec779b8eSAndroid Build Coastguard Worker      * application.
88*ec779b8eSAndroid Build Coastguard Worker      */
89*ec779b8eSAndroid Build Coastguard Worker     static const int kUserVolumeShapersMax = 16;
90*ec779b8eSAndroid Build Coastguard Worker 
91*ec779b8eSAndroid Build Coastguard Worker     /* VolumeShaper::Status is equivalent to status_t if negative
92*ec779b8eSAndroid Build Coastguard Worker      * but if non-negative represents the id operated on.
93*ec779b8eSAndroid Build Coastguard Worker      * It must be expressible as an int32_t for binder purposes.
94*ec779b8eSAndroid Build Coastguard Worker      */
95*ec779b8eSAndroid Build Coastguard Worker     using Status = status_t;
96*ec779b8eSAndroid Build Coastguard Worker 
97*ec779b8eSAndroid Build Coastguard Worker     // Local definition for clamp as std::clamp is included in C++17 only.
98*ec779b8eSAndroid Build Coastguard Worker     // TODO: use the std::clamp version when Android build uses C++17.
99*ec779b8eSAndroid Build Coastguard Worker     template<typename R>
clamp(const R & v,const R & lo,const R & hi)100*ec779b8eSAndroid Build Coastguard Worker     static constexpr const R &clamp(const R &v, const R &lo, const R &hi) {
101*ec779b8eSAndroid Build Coastguard Worker         return (v < lo) ? lo : (hi < v) ? hi : v;
102*ec779b8eSAndroid Build Coastguard Worker     }
103*ec779b8eSAndroid Build Coastguard Worker 
104*ec779b8eSAndroid Build Coastguard Worker     /* VolumeShaper.Configuration derives from the Interpolator class and adds
105*ec779b8eSAndroid Build Coastguard Worker      * parameters relating to the volume shape.
106*ec779b8eSAndroid Build Coastguard Worker      *
107*ec779b8eSAndroid Build Coastguard Worker      * This parallels the Java implementation and the enums must match.
108*ec779b8eSAndroid Build Coastguard Worker      * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
109*ec779b8eSAndroid Build Coastguard Worker      * details on the Java implementation.
110*ec779b8eSAndroid Build Coastguard Worker      */
111*ec779b8eSAndroid Build Coastguard Worker     class Configuration : public Interpolator<S, T>, public RefBase, public Parcelable {
112*ec779b8eSAndroid Build Coastguard Worker     public:
113*ec779b8eSAndroid Build Coastguard Worker         // Must match with VolumeShaper.java in frameworks/base.
114*ec779b8eSAndroid Build Coastguard Worker         enum Type : int32_t {
115*ec779b8eSAndroid Build Coastguard Worker             TYPE_ID,
116*ec779b8eSAndroid Build Coastguard Worker             TYPE_SCALE,
117*ec779b8eSAndroid Build Coastguard Worker         };
118*ec779b8eSAndroid Build Coastguard Worker 
toString(Type type)119*ec779b8eSAndroid Build Coastguard Worker         static std::string toString(Type type) {
120*ec779b8eSAndroid Build Coastguard Worker             switch (type) {
121*ec779b8eSAndroid Build Coastguard Worker                 case TYPE_ID: return "TYPE_ID";
122*ec779b8eSAndroid Build Coastguard Worker                 case TYPE_SCALE: return "TYPE_SCALE";
123*ec779b8eSAndroid Build Coastguard Worker                 default:
124*ec779b8eSAndroid Build Coastguard Worker                     return std::string("Unknown Type: ")
125*ec779b8eSAndroid Build Coastguard Worker                             .append(std::to_string(static_cast<int>(type)));
126*ec779b8eSAndroid Build Coastguard Worker             }
127*ec779b8eSAndroid Build Coastguard Worker         }
128*ec779b8eSAndroid Build Coastguard Worker 
129*ec779b8eSAndroid Build Coastguard Worker         // Must match with VolumeShaper.java in frameworks/base.
130*ec779b8eSAndroid Build Coastguard Worker         enum OptionFlag : int32_t {
131*ec779b8eSAndroid Build Coastguard Worker             OPTION_FLAG_NONE           = 0,
132*ec779b8eSAndroid Build Coastguard Worker             OPTION_FLAG_VOLUME_IN_DBFS = (1 << 0),
133*ec779b8eSAndroid Build Coastguard Worker             OPTION_FLAG_CLOCK_TIME     = (1 << 1),
134*ec779b8eSAndroid Build Coastguard Worker 
135*ec779b8eSAndroid Build Coastguard Worker             OPTION_FLAG_ALL            = (OPTION_FLAG_VOLUME_IN_DBFS | OPTION_FLAG_CLOCK_TIME),
136*ec779b8eSAndroid Build Coastguard Worker         };
137*ec779b8eSAndroid Build Coastguard Worker 
toString(OptionFlag flag)138*ec779b8eSAndroid Build Coastguard Worker         static std::string toString(OptionFlag flag) {
139*ec779b8eSAndroid Build Coastguard Worker             std::string s;
140*ec779b8eSAndroid Build Coastguard Worker             for (const auto& flagPair : std::initializer_list<std::pair<OptionFlag, const char*>>{
141*ec779b8eSAndroid Build Coastguard Worker                     {OPTION_FLAG_VOLUME_IN_DBFS, "OPTION_FLAG_VOLUME_IN_DBFS"},
142*ec779b8eSAndroid Build Coastguard Worker                     {OPTION_FLAG_CLOCK_TIME, "OPTION_FLAG_CLOCK_TIME"},
143*ec779b8eSAndroid Build Coastguard Worker                 }) {
144*ec779b8eSAndroid Build Coastguard Worker                 if (flag & flagPair.first) {
145*ec779b8eSAndroid Build Coastguard Worker                     if (!s.empty()) {
146*ec779b8eSAndroid Build Coastguard Worker                         s.append("|");
147*ec779b8eSAndroid Build Coastguard Worker                     }
148*ec779b8eSAndroid Build Coastguard Worker                     s.append(flagPair.second);
149*ec779b8eSAndroid Build Coastguard Worker                 }
150*ec779b8eSAndroid Build Coastguard Worker             }
151*ec779b8eSAndroid Build Coastguard Worker             return s;
152*ec779b8eSAndroid Build Coastguard Worker         }
153*ec779b8eSAndroid Build Coastguard Worker 
154*ec779b8eSAndroid Build Coastguard Worker         // Bring from base class; must match with VolumeShaper.java in frameworks/base.
155*ec779b8eSAndroid Build Coastguard Worker         using InterpolatorType = Interpolator<S, T>::InterpolatorType;
156*ec779b8eSAndroid Build Coastguard Worker 
Configuration()157*ec779b8eSAndroid Build Coastguard Worker         Configuration()
158*ec779b8eSAndroid Build Coastguard Worker             : Interpolator<S, T>()
159*ec779b8eSAndroid Build Coastguard Worker             , RefBase()
160*ec779b8eSAndroid Build Coastguard Worker             , mType(TYPE_SCALE)
161*ec779b8eSAndroid Build Coastguard Worker             , mId(-1)
162*ec779b8eSAndroid Build Coastguard Worker             , mOptionFlags(OPTION_FLAG_NONE)
163*ec779b8eSAndroid Build Coastguard Worker             , mDurationMs(1000.) {
164*ec779b8eSAndroid Build Coastguard Worker         }
165*ec779b8eSAndroid Build Coastguard Worker 
Configuration(const Configuration & configuration)166*ec779b8eSAndroid Build Coastguard Worker         Configuration(const Configuration &configuration)
167*ec779b8eSAndroid Build Coastguard Worker             : Interpolator<S, T>(*static_cast<const Interpolator<S, T> *>(&configuration))
168*ec779b8eSAndroid Build Coastguard Worker             , RefBase()
169*ec779b8eSAndroid Build Coastguard Worker             , mType(configuration.mType)
170*ec779b8eSAndroid Build Coastguard Worker             , mId(configuration.mId)
171*ec779b8eSAndroid Build Coastguard Worker             , mOptionFlags(configuration.mOptionFlags)
172*ec779b8eSAndroid Build Coastguard Worker             , mDurationMs(configuration.mDurationMs) {
173*ec779b8eSAndroid Build Coastguard Worker         }
174*ec779b8eSAndroid Build Coastguard Worker 
getType()175*ec779b8eSAndroid Build Coastguard Worker         Type getType() const {
176*ec779b8eSAndroid Build Coastguard Worker             return mType;
177*ec779b8eSAndroid Build Coastguard Worker         }
178*ec779b8eSAndroid Build Coastguard Worker 
setType(Type type)179*ec779b8eSAndroid Build Coastguard Worker         status_t setType(Type type) {
180*ec779b8eSAndroid Build Coastguard Worker             switch (type) {
181*ec779b8eSAndroid Build Coastguard Worker             case TYPE_ID:
182*ec779b8eSAndroid Build Coastguard Worker             case TYPE_SCALE:
183*ec779b8eSAndroid Build Coastguard Worker                 mType = type;
184*ec779b8eSAndroid Build Coastguard Worker                 return NO_ERROR;
185*ec779b8eSAndroid Build Coastguard Worker             default:
186*ec779b8eSAndroid Build Coastguard Worker                 ALOGE("invalid Type: %d", type);
187*ec779b8eSAndroid Build Coastguard Worker                 return BAD_VALUE;
188*ec779b8eSAndroid Build Coastguard Worker             }
189*ec779b8eSAndroid Build Coastguard Worker         }
190*ec779b8eSAndroid Build Coastguard Worker 
getOptionFlags()191*ec779b8eSAndroid Build Coastguard Worker         OptionFlag getOptionFlags() const {
192*ec779b8eSAndroid Build Coastguard Worker             return mOptionFlags;
193*ec779b8eSAndroid Build Coastguard Worker         }
194*ec779b8eSAndroid Build Coastguard Worker 
setOptionFlags(OptionFlag optionFlags)195*ec779b8eSAndroid Build Coastguard Worker         status_t setOptionFlags(OptionFlag optionFlags) {
196*ec779b8eSAndroid Build Coastguard Worker             if ((optionFlags & ~OPTION_FLAG_ALL) != 0) {
197*ec779b8eSAndroid Build Coastguard Worker                 ALOGE("optionFlags has invalid bits: %#x", optionFlags);
198*ec779b8eSAndroid Build Coastguard Worker                 return BAD_VALUE;
199*ec779b8eSAndroid Build Coastguard Worker             }
200*ec779b8eSAndroid Build Coastguard Worker             mOptionFlags = optionFlags;
201*ec779b8eSAndroid Build Coastguard Worker             return NO_ERROR;
202*ec779b8eSAndroid Build Coastguard Worker         }
203*ec779b8eSAndroid Build Coastguard Worker 
getDurationMs()204*ec779b8eSAndroid Build Coastguard Worker         double getDurationMs() const {
205*ec779b8eSAndroid Build Coastguard Worker             return mDurationMs;
206*ec779b8eSAndroid Build Coastguard Worker         }
207*ec779b8eSAndroid Build Coastguard Worker 
setDurationMs(double durationMs)208*ec779b8eSAndroid Build Coastguard Worker         status_t setDurationMs(double durationMs) {
209*ec779b8eSAndroid Build Coastguard Worker             if (durationMs > 0.) {
210*ec779b8eSAndroid Build Coastguard Worker                 mDurationMs = durationMs;
211*ec779b8eSAndroid Build Coastguard Worker                 return NO_ERROR;
212*ec779b8eSAndroid Build Coastguard Worker             }
213*ec779b8eSAndroid Build Coastguard Worker             // zero, negative, or nan. These values not possible from Java.
214*ec779b8eSAndroid Build Coastguard Worker             return BAD_VALUE;
215*ec779b8eSAndroid Build Coastguard Worker         }
216*ec779b8eSAndroid Build Coastguard Worker 
getId()217*ec779b8eSAndroid Build Coastguard Worker         int32_t getId() const {
218*ec779b8eSAndroid Build Coastguard Worker             return mId;
219*ec779b8eSAndroid Build Coastguard Worker         }
220*ec779b8eSAndroid Build Coastguard Worker 
setId(int32_t id)221*ec779b8eSAndroid Build Coastguard Worker         void setId(int32_t id) {
222*ec779b8eSAndroid Build Coastguard Worker             // We permit a negative id here (representing invalid).
223*ec779b8eSAndroid Build Coastguard Worker             mId = id;
224*ec779b8eSAndroid Build Coastguard Worker         }
225*ec779b8eSAndroid Build Coastguard Worker 
226*ec779b8eSAndroid Build Coastguard Worker         /* Adjust the volume to be in linear range from MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME
227*ec779b8eSAndroid Build Coastguard Worker          * and compensate for log dbFS volume as needed.
228*ec779b8eSAndroid Build Coastguard Worker          */
adjustVolume(T volume)229*ec779b8eSAndroid Build Coastguard Worker         T adjustVolume(T volume) const {
230*ec779b8eSAndroid Build Coastguard Worker             if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
231*ec779b8eSAndroid Build Coastguard Worker                 const T out = powf(10.f, volume / 10.f);
232*ec779b8eSAndroid Build Coastguard Worker                 VS_LOG("in: %f  out: %f", volume, out);
233*ec779b8eSAndroid Build Coastguard Worker                 volume = out;
234*ec779b8eSAndroid Build Coastguard Worker             }
235*ec779b8eSAndroid Build Coastguard Worker             return clamp(volume, MIN_LINEAR_VOLUME /* lo */, MAX_LINEAR_VOLUME /* hi */);
236*ec779b8eSAndroid Build Coastguard Worker         }
237*ec779b8eSAndroid Build Coastguard Worker 
238*ec779b8eSAndroid Build Coastguard Worker         /* Check if the existing curve is valid.
239*ec779b8eSAndroid Build Coastguard Worker          */
checkCurve()240*ec779b8eSAndroid Build Coastguard Worker         status_t checkCurve() const {
241*ec779b8eSAndroid Build Coastguard Worker             if (mType == TYPE_ID) return NO_ERROR;
242*ec779b8eSAndroid Build Coastguard Worker             if (this->size() < 2) {
243*ec779b8eSAndroid Build Coastguard Worker                 ALOGE("curve must have at least 2 points");
244*ec779b8eSAndroid Build Coastguard Worker                 return BAD_VALUE;
245*ec779b8eSAndroid Build Coastguard Worker             }
246*ec779b8eSAndroid Build Coastguard Worker             if (first().first != MIN_CURVE_TIME || last().first != MAX_CURVE_TIME) {
247*ec779b8eSAndroid Build Coastguard Worker                 ALOGE("curve must start at MIN_CURVE_TIME and end at MAX_CURVE_TIME");
248*ec779b8eSAndroid Build Coastguard Worker                 return BAD_VALUE;
249*ec779b8eSAndroid Build Coastguard Worker             }
250*ec779b8eSAndroid Build Coastguard Worker             if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
251*ec779b8eSAndroid Build Coastguard Worker                 for (const auto &pt : *this) {
252*ec779b8eSAndroid Build Coastguard Worker                     if (!(pt.second <= MAX_LOG_VOLUME) /* handle nan */) {
253*ec779b8eSAndroid Build Coastguard Worker                         ALOGE("positive volume dbFS");
254*ec779b8eSAndroid Build Coastguard Worker                         return BAD_VALUE;
255*ec779b8eSAndroid Build Coastguard Worker                     }
256*ec779b8eSAndroid Build Coastguard Worker                 }
257*ec779b8eSAndroid Build Coastguard Worker             } else {
258*ec779b8eSAndroid Build Coastguard Worker                 for (const auto &pt : *this) {
259*ec779b8eSAndroid Build Coastguard Worker                     if (!(pt.second >= MIN_LINEAR_VOLUME)
260*ec779b8eSAndroid Build Coastguard Worker                             || !(pt.second <= MAX_LINEAR_VOLUME) /* handle nan */) {
261*ec779b8eSAndroid Build Coastguard Worker                         ALOGE("volume < MIN_LINEAR_VOLUME or > MAX_LINEAR_VOLUME");
262*ec779b8eSAndroid Build Coastguard Worker                         return BAD_VALUE;
263*ec779b8eSAndroid Build Coastguard Worker                     }
264*ec779b8eSAndroid Build Coastguard Worker                 }
265*ec779b8eSAndroid Build Coastguard Worker             }
266*ec779b8eSAndroid Build Coastguard Worker             return NO_ERROR;
267*ec779b8eSAndroid Build Coastguard Worker         }
268*ec779b8eSAndroid Build Coastguard Worker 
269*ec779b8eSAndroid Build Coastguard Worker         /* Clamps the volume curve in the configuration to
270*ec779b8eSAndroid Build Coastguard Worker          * the valid range for log or linear scale.
271*ec779b8eSAndroid Build Coastguard Worker          */
clampVolume()272*ec779b8eSAndroid Build Coastguard Worker         void clampVolume() {
273*ec779b8eSAndroid Build Coastguard Worker             if ((mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
274*ec779b8eSAndroid Build Coastguard Worker                 for (auto it = this->begin(); it != this->end(); ++it) {
275*ec779b8eSAndroid Build Coastguard Worker                     if (!(it->second <= MAX_LOG_VOLUME) /* handle nan */) {
276*ec779b8eSAndroid Build Coastguard Worker                         it->second = MAX_LOG_VOLUME;
277*ec779b8eSAndroid Build Coastguard Worker                     }
278*ec779b8eSAndroid Build Coastguard Worker                 }
279*ec779b8eSAndroid Build Coastguard Worker             } else {
280*ec779b8eSAndroid Build Coastguard Worker                 for (auto it = this->begin(); it != this->end(); ++it) {
281*ec779b8eSAndroid Build Coastguard Worker                     if (!(it->second >= MIN_LINEAR_VOLUME) /* handle nan */) {
282*ec779b8eSAndroid Build Coastguard Worker                         it->second = MIN_LINEAR_VOLUME;
283*ec779b8eSAndroid Build Coastguard Worker                     } else if (!(it->second <= MAX_LINEAR_VOLUME)) {
284*ec779b8eSAndroid Build Coastguard Worker                         it->second = MAX_LINEAR_VOLUME;
285*ec779b8eSAndroid Build Coastguard Worker                     }
286*ec779b8eSAndroid Build Coastguard Worker                 }
287*ec779b8eSAndroid Build Coastguard Worker             }
288*ec779b8eSAndroid Build Coastguard Worker         }
289*ec779b8eSAndroid Build Coastguard Worker 
290*ec779b8eSAndroid Build Coastguard Worker         /* scaleToStartVolume() is used to set the start volume of a
291*ec779b8eSAndroid Build Coastguard Worker          * new VolumeShaper curve, when replacing one VolumeShaper
292*ec779b8eSAndroid Build Coastguard Worker          * with another using the "join" (volume match) option.
293*ec779b8eSAndroid Build Coastguard Worker          *
294*ec779b8eSAndroid Build Coastguard Worker          * It works best for monotonic volume ramps or ducks.
295*ec779b8eSAndroid Build Coastguard Worker          */
scaleToStartVolume(T volume)296*ec779b8eSAndroid Build Coastguard Worker         void scaleToStartVolume(T volume) {
297*ec779b8eSAndroid Build Coastguard Worker             if (this->size() < 2) {
298*ec779b8eSAndroid Build Coastguard Worker                 return;
299*ec779b8eSAndroid Build Coastguard Worker             }
300*ec779b8eSAndroid Build Coastguard Worker             const T startVolume = first().second;
301*ec779b8eSAndroid Build Coastguard Worker             const T endVolume = last().second;
302*ec779b8eSAndroid Build Coastguard Worker             if (endVolume == startVolume) {
303*ec779b8eSAndroid Build Coastguard Worker                 // match with linear ramp
304*ec779b8eSAndroid Build Coastguard Worker                 const T offset = volume - startVolume;
305*ec779b8eSAndroid Build Coastguard Worker                 static const T scale =  1.f / (MAX_CURVE_TIME - MIN_CURVE_TIME); // nominally 1.f
306*ec779b8eSAndroid Build Coastguard Worker                 for (auto it = this->begin(); it != this->end(); ++it) {
307*ec779b8eSAndroid Build Coastguard Worker                     it->second = it->second + offset * (MAX_CURVE_TIME - it->first) * scale;
308*ec779b8eSAndroid Build Coastguard Worker                 }
309*ec779b8eSAndroid Build Coastguard Worker             } else {
310*ec779b8eSAndroid Build Coastguard Worker                 const T  scale = (volume - endVolume) / (startVolume - endVolume);
311*ec779b8eSAndroid Build Coastguard Worker                 for (auto it = this->begin(); it != this->end(); ++it) {
312*ec779b8eSAndroid Build Coastguard Worker                     it->second = scale * (it->second - endVolume) + endVolume;
313*ec779b8eSAndroid Build Coastguard Worker                 }
314*ec779b8eSAndroid Build Coastguard Worker             }
315*ec779b8eSAndroid Build Coastguard Worker             clampVolume();
316*ec779b8eSAndroid Build Coastguard Worker         }
317*ec779b8eSAndroid Build Coastguard Worker 
writeToParcel(Parcel * parcel)318*ec779b8eSAndroid Build Coastguard Worker         status_t writeToParcel(Parcel *parcel) const override {
319*ec779b8eSAndroid Build Coastguard Worker             VolumeShaperConfiguration parcelable;
320*ec779b8eSAndroid Build Coastguard Worker             writeToParcelable(&parcelable);
321*ec779b8eSAndroid Build Coastguard Worker             return parcelable.writeToParcel(parcel);
322*ec779b8eSAndroid Build Coastguard Worker         }
323*ec779b8eSAndroid Build Coastguard Worker 
writeToParcelable(VolumeShaperConfiguration * parcelable)324*ec779b8eSAndroid Build Coastguard Worker         void writeToParcelable(VolumeShaperConfiguration *parcelable) const {
325*ec779b8eSAndroid Build Coastguard Worker             parcelable->id = getId();
326*ec779b8eSAndroid Build Coastguard Worker             parcelable->type = getTypeAsAidl();
327*ec779b8eSAndroid Build Coastguard Worker             parcelable->optionFlags = 0;
328*ec779b8eSAndroid Build Coastguard Worker             if (mType != TYPE_ID) {
329*ec779b8eSAndroid Build Coastguard Worker                 parcelable->optionFlags = getOptionFlagsAsAidl();
330*ec779b8eSAndroid Build Coastguard Worker                 parcelable->durationMs = getDurationMs();
331*ec779b8eSAndroid Build Coastguard Worker                 parcelable->interpolatorConfig.emplace(); // create value in std::optional
332*ec779b8eSAndroid Build Coastguard Worker                 Interpolator<S, T>::writeToConfig(&*parcelable->interpolatorConfig);
333*ec779b8eSAndroid Build Coastguard Worker             }
334*ec779b8eSAndroid Build Coastguard Worker         }
335*ec779b8eSAndroid Build Coastguard Worker 
readFromParcel(const Parcel * parcel)336*ec779b8eSAndroid Build Coastguard Worker         status_t readFromParcel(const Parcel* parcel) override {
337*ec779b8eSAndroid Build Coastguard Worker             VolumeShaperConfiguration data;
338*ec779b8eSAndroid Build Coastguard Worker             return data.readFromParcel(parcel)
339*ec779b8eSAndroid Build Coastguard Worker                    ?: readFromParcelable(data);
340*ec779b8eSAndroid Build Coastguard Worker         }
341*ec779b8eSAndroid Build Coastguard Worker 
readFromParcelable(const VolumeShaperConfiguration & parcelable)342*ec779b8eSAndroid Build Coastguard Worker         status_t readFromParcelable(const VolumeShaperConfiguration& parcelable) {
343*ec779b8eSAndroid Build Coastguard Worker             setId(parcelable.id);
344*ec779b8eSAndroid Build Coastguard Worker             return setTypeFromAidl(parcelable.type)
345*ec779b8eSAndroid Build Coastguard Worker                    ?: mType == TYPE_ID
346*ec779b8eSAndroid Build Coastguard Worker                       ? NO_ERROR
347*ec779b8eSAndroid Build Coastguard Worker                       : setOptionFlagsFromAidl(parcelable.optionFlags)
348*ec779b8eSAndroid Build Coastguard Worker                         ?: setDurationMs(parcelable.durationMs)
349*ec779b8eSAndroid Build Coastguard Worker                            ?: !parcelable.interpolatorConfig  // check std::optional for value
350*ec779b8eSAndroid Build Coastguard Worker                                ? BAD_VALUE // must be nonnull.
351*ec779b8eSAndroid Build Coastguard Worker                                : Interpolator<S, T>::readFromConfig(*parcelable.interpolatorConfig)
352*ec779b8eSAndroid Build Coastguard Worker                                    ?: checkCurve();
353*ec779b8eSAndroid Build Coastguard Worker         }
354*ec779b8eSAndroid Build Coastguard Worker 
355*ec779b8eSAndroid Build Coastguard Worker         // Returns a string for debug printing.
toString()356*ec779b8eSAndroid Build Coastguard Worker         std::string toString() const {
357*ec779b8eSAndroid Build Coastguard Worker             std::stringstream ss;
358*ec779b8eSAndroid Build Coastguard Worker             ss << "VolumeShaper::Configuration{mType=" << toString(mType);
359*ec779b8eSAndroid Build Coastguard Worker             ss << ", mId=" << mId;
360*ec779b8eSAndroid Build Coastguard Worker             if (mType != TYPE_ID) {
361*ec779b8eSAndroid Build Coastguard Worker                 ss << ", mOptionFlags=" << toString(mOptionFlags);
362*ec779b8eSAndroid Build Coastguard Worker                 ss << ", mDurationMs=" << mDurationMs;
363*ec779b8eSAndroid Build Coastguard Worker                 ss << ", " << Interpolator<S, T>::toString().c_str();
364*ec779b8eSAndroid Build Coastguard Worker             }
365*ec779b8eSAndroid Build Coastguard Worker             ss << "}";
366*ec779b8eSAndroid Build Coastguard Worker             return ss.str();
367*ec779b8eSAndroid Build Coastguard Worker         }
368*ec779b8eSAndroid Build Coastguard Worker 
369*ec779b8eSAndroid Build Coastguard Worker     private:
370*ec779b8eSAndroid Build Coastguard Worker         Type mType;              // type of configuration
371*ec779b8eSAndroid Build Coastguard Worker         int32_t mId;             // A valid id is >= 0.
372*ec779b8eSAndroid Build Coastguard Worker         OptionFlag mOptionFlags; // option flags for the configuration.
373*ec779b8eSAndroid Build Coastguard Worker         double mDurationMs;      // duration, must be > 0; default is 1000 ms.
374*ec779b8eSAndroid Build Coastguard Worker 
getOptionFlagsAsAidl()375*ec779b8eSAndroid Build Coastguard Worker         int32_t getOptionFlagsAsAidl() const {
376*ec779b8eSAndroid Build Coastguard Worker             int32_t result = 0;
377*ec779b8eSAndroid Build Coastguard Worker             if (getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) {
378*ec779b8eSAndroid Build Coastguard Worker                 result |=
379*ec779b8eSAndroid Build Coastguard Worker                         1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::VOLUME_IN_DBFS);
380*ec779b8eSAndroid Build Coastguard Worker             }
381*ec779b8eSAndroid Build Coastguard Worker             if (getOptionFlags() & OPTION_FLAG_CLOCK_TIME) {
382*ec779b8eSAndroid Build Coastguard Worker                 result |= 1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::CLOCK_TIME);
383*ec779b8eSAndroid Build Coastguard Worker             }
384*ec779b8eSAndroid Build Coastguard Worker             return result;
385*ec779b8eSAndroid Build Coastguard Worker         }
386*ec779b8eSAndroid Build Coastguard Worker 
setOptionFlagsFromAidl(int32_t aidl)387*ec779b8eSAndroid Build Coastguard Worker         status_t setOptionFlagsFromAidl(int32_t aidl) {
388*ec779b8eSAndroid Build Coastguard Worker             std::underlying_type_t<OptionFlag> options = 0;
389*ec779b8eSAndroid Build Coastguard Worker             if (aidl & (1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::VOLUME_IN_DBFS))) {
390*ec779b8eSAndroid Build Coastguard Worker                 options |= OPTION_FLAG_VOLUME_IN_DBFS;
391*ec779b8eSAndroid Build Coastguard Worker             }
392*ec779b8eSAndroid Build Coastguard Worker             if (aidl & (1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::CLOCK_TIME))) {
393*ec779b8eSAndroid Build Coastguard Worker                 options |= OPTION_FLAG_CLOCK_TIME;
394*ec779b8eSAndroid Build Coastguard Worker             }
395*ec779b8eSAndroid Build Coastguard Worker             return setOptionFlags(static_cast<OptionFlag>(options));
396*ec779b8eSAndroid Build Coastguard Worker         }
397*ec779b8eSAndroid Build Coastguard Worker 
setTypeFromAidl(VolumeShaperConfigurationType aidl)398*ec779b8eSAndroid Build Coastguard Worker         status_t setTypeFromAidl(VolumeShaperConfigurationType aidl) {
399*ec779b8eSAndroid Build Coastguard Worker             switch (aidl) {
400*ec779b8eSAndroid Build Coastguard Worker                 case VolumeShaperConfigurationType::ID:
401*ec779b8eSAndroid Build Coastguard Worker                     return setType(TYPE_ID);
402*ec779b8eSAndroid Build Coastguard Worker                 case VolumeShaperConfigurationType::SCALE:
403*ec779b8eSAndroid Build Coastguard Worker                     return setType(TYPE_SCALE);
404*ec779b8eSAndroid Build Coastguard Worker                 default:
405*ec779b8eSAndroid Build Coastguard Worker                     return BAD_VALUE;
406*ec779b8eSAndroid Build Coastguard Worker             }
407*ec779b8eSAndroid Build Coastguard Worker         }
408*ec779b8eSAndroid Build Coastguard Worker 
getTypeAsAidl()409*ec779b8eSAndroid Build Coastguard Worker         VolumeShaperConfigurationType getTypeAsAidl() const {
410*ec779b8eSAndroid Build Coastguard Worker             switch (getType()) {
411*ec779b8eSAndroid Build Coastguard Worker                 case TYPE_ID:
412*ec779b8eSAndroid Build Coastguard Worker                     return VolumeShaperConfigurationType::ID;
413*ec779b8eSAndroid Build Coastguard Worker                 case TYPE_SCALE:
414*ec779b8eSAndroid Build Coastguard Worker                     return VolumeShaperConfigurationType::SCALE;
415*ec779b8eSAndroid Build Coastguard Worker                 default:
416*ec779b8eSAndroid Build Coastguard Worker                     LOG_ALWAYS_FATAL("Shouldn't get here");
417*ec779b8eSAndroid Build Coastguard Worker             }
418*ec779b8eSAndroid Build Coastguard Worker         }
419*ec779b8eSAndroid Build Coastguard Worker     }; // Configuration
420*ec779b8eSAndroid Build Coastguard Worker 
421*ec779b8eSAndroid Build Coastguard Worker     /* VolumeShaper::Operation expresses an operation to perform on the
422*ec779b8eSAndroid Build Coastguard Worker      * configuration (either explicitly specified or an id).
423*ec779b8eSAndroid Build Coastguard Worker      *
424*ec779b8eSAndroid Build Coastguard Worker      * This parallels the Java implementation and the enums must match.
425*ec779b8eSAndroid Build Coastguard Worker      * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
426*ec779b8eSAndroid Build Coastguard Worker      * details on the Java implementation.
427*ec779b8eSAndroid Build Coastguard Worker      */
428*ec779b8eSAndroid Build Coastguard Worker     class Operation : public RefBase, public Parcelable {
429*ec779b8eSAndroid Build Coastguard Worker     public:
430*ec779b8eSAndroid Build Coastguard Worker         // Must match with VolumeShaper.java.
431*ec779b8eSAndroid Build Coastguard Worker         enum Flag : int32_t {
432*ec779b8eSAndroid Build Coastguard Worker             FLAG_NONE      = 0,
433*ec779b8eSAndroid Build Coastguard Worker             FLAG_REVERSE   = (1 << 0), // the absence of this indicates "play"
434*ec779b8eSAndroid Build Coastguard Worker             FLAG_TERMINATE = (1 << 1),
435*ec779b8eSAndroid Build Coastguard Worker             FLAG_JOIN      = (1 << 2),
436*ec779b8eSAndroid Build Coastguard Worker             FLAG_DELAY     = (1 << 3),
437*ec779b8eSAndroid Build Coastguard Worker             FLAG_CREATE_IF_NECESSARY = (1 << 4),
438*ec779b8eSAndroid Build Coastguard Worker 
439*ec779b8eSAndroid Build Coastguard Worker             FLAG_ALL       = (FLAG_REVERSE | FLAG_TERMINATE | FLAG_JOIN | FLAG_DELAY
440*ec779b8eSAndroid Build Coastguard Worker                             | FLAG_CREATE_IF_NECESSARY),
441*ec779b8eSAndroid Build Coastguard Worker         };
442*ec779b8eSAndroid Build Coastguard Worker 
toString(Flag flag)443*ec779b8eSAndroid Build Coastguard Worker         static std::string toString(Flag flag) {
444*ec779b8eSAndroid Build Coastguard Worker             std::string s;
445*ec779b8eSAndroid Build Coastguard Worker             for (const auto& flagPair : std::initializer_list<std::pair<Flag, const char*>>{
446*ec779b8eSAndroid Build Coastguard Worker                     {FLAG_REVERSE, "FLAG_REVERSE"},
447*ec779b8eSAndroid Build Coastguard Worker                     {FLAG_TERMINATE, "FLAG_TERMINATE"},
448*ec779b8eSAndroid Build Coastguard Worker                     {FLAG_JOIN, "FLAG_JOIN"},
449*ec779b8eSAndroid Build Coastguard Worker                     {FLAG_DELAY, "FLAG_DELAY"},
450*ec779b8eSAndroid Build Coastguard Worker                     {FLAG_CREATE_IF_NECESSARY, "FLAG_CREATE_IF_NECESSARY"},
451*ec779b8eSAndroid Build Coastguard Worker                 }) {
452*ec779b8eSAndroid Build Coastguard Worker                 if (flag & flagPair.first) {
453*ec779b8eSAndroid Build Coastguard Worker                     if (!s.empty()) {
454*ec779b8eSAndroid Build Coastguard Worker                         s.append("|");
455*ec779b8eSAndroid Build Coastguard Worker                     }
456*ec779b8eSAndroid Build Coastguard Worker                     s.append(flagPair.second);
457*ec779b8eSAndroid Build Coastguard Worker                 }
458*ec779b8eSAndroid Build Coastguard Worker             }
459*ec779b8eSAndroid Build Coastguard Worker             return s;
460*ec779b8eSAndroid Build Coastguard Worker         }
461*ec779b8eSAndroid Build Coastguard Worker 
Operation()462*ec779b8eSAndroid Build Coastguard Worker         Operation()
463*ec779b8eSAndroid Build Coastguard Worker             : Operation(FLAG_NONE, -1 /* replaceId */) {
464*ec779b8eSAndroid Build Coastguard Worker         }
465*ec779b8eSAndroid Build Coastguard Worker 
Operation(Flag flags,int replaceId)466*ec779b8eSAndroid Build Coastguard Worker         Operation(Flag flags, int replaceId)
467*ec779b8eSAndroid Build Coastguard Worker             : Operation(flags, replaceId, std::numeric_limits<S>::quiet_NaN() /* xOffset */) {
468*ec779b8eSAndroid Build Coastguard Worker         }
469*ec779b8eSAndroid Build Coastguard Worker 
Operation(const Operation & operation)470*ec779b8eSAndroid Build Coastguard Worker         Operation(const Operation &operation)
471*ec779b8eSAndroid Build Coastguard Worker             : Operation(operation.mFlags, operation.mReplaceId, operation.mXOffset) {
472*ec779b8eSAndroid Build Coastguard Worker         }
473*ec779b8eSAndroid Build Coastguard Worker 
Operation(const sp<Operation> & operation)474*ec779b8eSAndroid Build Coastguard Worker         explicit Operation(const sp<Operation> &operation)
475*ec779b8eSAndroid Build Coastguard Worker             : Operation(*operation.get()) {
476*ec779b8eSAndroid Build Coastguard Worker         }
477*ec779b8eSAndroid Build Coastguard Worker 
Operation(Flag flags,int replaceId,S xOffset)478*ec779b8eSAndroid Build Coastguard Worker         Operation(Flag flags, int replaceId, S xOffset)
479*ec779b8eSAndroid Build Coastguard Worker             : mFlags(flags)
480*ec779b8eSAndroid Build Coastguard Worker             , mReplaceId(replaceId)
481*ec779b8eSAndroid Build Coastguard Worker             , mXOffset(xOffset) {
482*ec779b8eSAndroid Build Coastguard Worker         }
483*ec779b8eSAndroid Build Coastguard Worker 
getReplaceId()484*ec779b8eSAndroid Build Coastguard Worker         int32_t getReplaceId() const {
485*ec779b8eSAndroid Build Coastguard Worker             return mReplaceId;
486*ec779b8eSAndroid Build Coastguard Worker         }
487*ec779b8eSAndroid Build Coastguard Worker 
setReplaceId(int32_t replaceId)488*ec779b8eSAndroid Build Coastguard Worker         void setReplaceId(int32_t replaceId) {
489*ec779b8eSAndroid Build Coastguard Worker             mReplaceId = replaceId;
490*ec779b8eSAndroid Build Coastguard Worker         }
491*ec779b8eSAndroid Build Coastguard Worker 
getXOffset()492*ec779b8eSAndroid Build Coastguard Worker         S getXOffset() const {
493*ec779b8eSAndroid Build Coastguard Worker             return mXOffset;
494*ec779b8eSAndroid Build Coastguard Worker         }
495*ec779b8eSAndroid Build Coastguard Worker 
setXOffset(S xOffset)496*ec779b8eSAndroid Build Coastguard Worker         void setXOffset(S xOffset) {
497*ec779b8eSAndroid Build Coastguard Worker             mXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
498*ec779b8eSAndroid Build Coastguard Worker         }
499*ec779b8eSAndroid Build Coastguard Worker 
getFlags()500*ec779b8eSAndroid Build Coastguard Worker         Flag getFlags() const {
501*ec779b8eSAndroid Build Coastguard Worker             return mFlags;
502*ec779b8eSAndroid Build Coastguard Worker         }
503*ec779b8eSAndroid Build Coastguard Worker 
504*ec779b8eSAndroid Build Coastguard Worker         /* xOffset is the position on the volume curve and may go backwards
505*ec779b8eSAndroid Build Coastguard Worker          * if you are in reverse mode. This must be in the range from
506*ec779b8eSAndroid Build Coastguard Worker          * [MIN_CURVE_TIME, MAX_CURVE_TIME].
507*ec779b8eSAndroid Build Coastguard Worker          *
508*ec779b8eSAndroid Build Coastguard Worker          * normalizedTime always increases as time or framecount increases.
509*ec779b8eSAndroid Build Coastguard Worker          * normalizedTime is nominally from MIN_CURVE_TIME to MAX_CURVE_TIME when
510*ec779b8eSAndroid Build Coastguard Worker          * running through the curve, but could be outside this range afterwards.
511*ec779b8eSAndroid Build Coastguard Worker          * If you are reversing, this means the position on the curve, or xOffset,
512*ec779b8eSAndroid Build Coastguard Worker          * is computed as MAX_CURVE_TIME - normalizedTime, clamped to
513*ec779b8eSAndroid Build Coastguard Worker          * [MIN_CURVE_TIME, MAX_CURVE_TIME].
514*ec779b8eSAndroid Build Coastguard Worker          */
setNormalizedTime(S normalizedTime)515*ec779b8eSAndroid Build Coastguard Worker         void setNormalizedTime(S normalizedTime) {
516*ec779b8eSAndroid Build Coastguard Worker             setXOffset((mFlags & FLAG_REVERSE) != 0
517*ec779b8eSAndroid Build Coastguard Worker                     ? MAX_CURVE_TIME - normalizedTime : normalizedTime);
518*ec779b8eSAndroid Build Coastguard Worker         }
519*ec779b8eSAndroid Build Coastguard Worker 
setFlags(Flag flags)520*ec779b8eSAndroid Build Coastguard Worker         status_t setFlags(Flag flags) {
521*ec779b8eSAndroid Build Coastguard Worker             if ((flags & ~FLAG_ALL) != 0) {
522*ec779b8eSAndroid Build Coastguard Worker                 ALOGE("flags has invalid bits: %#x", flags);
523*ec779b8eSAndroid Build Coastguard Worker                 return BAD_VALUE;
524*ec779b8eSAndroid Build Coastguard Worker             }
525*ec779b8eSAndroid Build Coastguard Worker             mFlags = flags;
526*ec779b8eSAndroid Build Coastguard Worker             return NO_ERROR;
527*ec779b8eSAndroid Build Coastguard Worker         }
528*ec779b8eSAndroid Build Coastguard Worker 
writeToParcel(Parcel * parcel)529*ec779b8eSAndroid Build Coastguard Worker         status_t writeToParcel(Parcel* parcel) const override {
530*ec779b8eSAndroid Build Coastguard Worker             if (parcel == nullptr) return BAD_VALUE;
531*ec779b8eSAndroid Build Coastguard Worker             VolumeShaperOperation op;
532*ec779b8eSAndroid Build Coastguard Worker             writeToParcelable(&op);
533*ec779b8eSAndroid Build Coastguard Worker             return op.writeToParcel(parcel);
534*ec779b8eSAndroid Build Coastguard Worker         }
535*ec779b8eSAndroid Build Coastguard Worker 
writeToParcelable(VolumeShaperOperation * op)536*ec779b8eSAndroid Build Coastguard Worker         void writeToParcelable(VolumeShaperOperation* op) const {
537*ec779b8eSAndroid Build Coastguard Worker             op->flags = getFlagsAsAidl();
538*ec779b8eSAndroid Build Coastguard Worker             op->replaceId = mReplaceId;
539*ec779b8eSAndroid Build Coastguard Worker             op->xOffset = mXOffset;
540*ec779b8eSAndroid Build Coastguard Worker         }
541*ec779b8eSAndroid Build Coastguard Worker 
readFromParcel(const Parcel * parcel)542*ec779b8eSAndroid Build Coastguard Worker         status_t readFromParcel(const Parcel* parcel) override {
543*ec779b8eSAndroid Build Coastguard Worker             VolumeShaperOperation op;
544*ec779b8eSAndroid Build Coastguard Worker             return op.readFromParcel(parcel)
545*ec779b8eSAndroid Build Coastguard Worker                    ?: readFromParcelable(op);
546*ec779b8eSAndroid Build Coastguard Worker         }
547*ec779b8eSAndroid Build Coastguard Worker 
readFromParcelable(const VolumeShaperOperation & op)548*ec779b8eSAndroid Build Coastguard Worker         status_t readFromParcelable(const VolumeShaperOperation& op) {
549*ec779b8eSAndroid Build Coastguard Worker             mReplaceId = op.replaceId;
550*ec779b8eSAndroid Build Coastguard Worker             mXOffset = op.xOffset;
551*ec779b8eSAndroid Build Coastguard Worker             return setFlagsFromAidl(op.flags);
552*ec779b8eSAndroid Build Coastguard Worker         }
553*ec779b8eSAndroid Build Coastguard Worker 
toString()554*ec779b8eSAndroid Build Coastguard Worker         std::string toString() const {
555*ec779b8eSAndroid Build Coastguard Worker             std::stringstream ss;
556*ec779b8eSAndroid Build Coastguard Worker             ss << "VolumeShaper::Operation{mFlags=" << toString(mFlags);
557*ec779b8eSAndroid Build Coastguard Worker             ss << ", mReplaceId=" << mReplaceId;
558*ec779b8eSAndroid Build Coastguard Worker             ss << ", mXOffset=" << mXOffset;
559*ec779b8eSAndroid Build Coastguard Worker             ss << "}";
560*ec779b8eSAndroid Build Coastguard Worker             return ss.str();
561*ec779b8eSAndroid Build Coastguard Worker         }
562*ec779b8eSAndroid Build Coastguard Worker 
563*ec779b8eSAndroid Build Coastguard Worker     private:
setFlagsFromAidl(int32_t aidl)564*ec779b8eSAndroid Build Coastguard Worker         status_t setFlagsFromAidl(int32_t aidl) {
565*ec779b8eSAndroid Build Coastguard Worker             std::underlying_type_t<Flag> flags = 0;
566*ec779b8eSAndroid Build Coastguard Worker             if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::REVERSE))) {
567*ec779b8eSAndroid Build Coastguard Worker                 flags |= FLAG_REVERSE;
568*ec779b8eSAndroid Build Coastguard Worker             }
569*ec779b8eSAndroid Build Coastguard Worker             if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::TERMINATE))) {
570*ec779b8eSAndroid Build Coastguard Worker                 flags |= FLAG_TERMINATE;
571*ec779b8eSAndroid Build Coastguard Worker             }
572*ec779b8eSAndroid Build Coastguard Worker             if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::JOIN))) {
573*ec779b8eSAndroid Build Coastguard Worker                 flags |= FLAG_JOIN;
574*ec779b8eSAndroid Build Coastguard Worker             }
575*ec779b8eSAndroid Build Coastguard Worker             if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::DELAY))) {
576*ec779b8eSAndroid Build Coastguard Worker                 flags |= FLAG_DELAY;
577*ec779b8eSAndroid Build Coastguard Worker             }
578*ec779b8eSAndroid Build Coastguard Worker             if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::CREATE_IF_NECESSARY))) {
579*ec779b8eSAndroid Build Coastguard Worker                 flags |= FLAG_CREATE_IF_NECESSARY;
580*ec779b8eSAndroid Build Coastguard Worker             }
581*ec779b8eSAndroid Build Coastguard Worker             return setFlags(static_cast<Flag>(flags));
582*ec779b8eSAndroid Build Coastguard Worker         }
583*ec779b8eSAndroid Build Coastguard Worker 
getFlagsAsAidl()584*ec779b8eSAndroid Build Coastguard Worker         int32_t getFlagsAsAidl() const {
585*ec779b8eSAndroid Build Coastguard Worker             int32_t aidl = 0;
586*ec779b8eSAndroid Build Coastguard Worker             std::underlying_type_t<Flag> flags = getFlags();
587*ec779b8eSAndroid Build Coastguard Worker             if (flags & FLAG_REVERSE) {
588*ec779b8eSAndroid Build Coastguard Worker                 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::REVERSE));
589*ec779b8eSAndroid Build Coastguard Worker             }
590*ec779b8eSAndroid Build Coastguard Worker             if (flags & FLAG_TERMINATE) {
591*ec779b8eSAndroid Build Coastguard Worker                 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::TERMINATE));
592*ec779b8eSAndroid Build Coastguard Worker             }
593*ec779b8eSAndroid Build Coastguard Worker             if (flags & FLAG_JOIN) {
594*ec779b8eSAndroid Build Coastguard Worker                 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::JOIN));
595*ec779b8eSAndroid Build Coastguard Worker             }
596*ec779b8eSAndroid Build Coastguard Worker             if (flags & FLAG_DELAY) {
597*ec779b8eSAndroid Build Coastguard Worker                 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::DELAY));
598*ec779b8eSAndroid Build Coastguard Worker             }
599*ec779b8eSAndroid Build Coastguard Worker             if (flags & FLAG_CREATE_IF_NECESSARY) {
600*ec779b8eSAndroid Build Coastguard Worker                 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::CREATE_IF_NECESSARY));
601*ec779b8eSAndroid Build Coastguard Worker             }
602*ec779b8eSAndroid Build Coastguard Worker             return aidl;
603*ec779b8eSAndroid Build Coastguard Worker         }
604*ec779b8eSAndroid Build Coastguard Worker 
605*ec779b8eSAndroid Build Coastguard Worker     private:
606*ec779b8eSAndroid Build Coastguard Worker         Flag mFlags;        // operation to do
607*ec779b8eSAndroid Build Coastguard Worker         int32_t mReplaceId; // if >= 0 the id to remove in a replace operation.
608*ec779b8eSAndroid Build Coastguard Worker         S mXOffset;         // position in the curve to set if a valid number (not nan)
609*ec779b8eSAndroid Build Coastguard Worker     }; // Operation
610*ec779b8eSAndroid Build Coastguard Worker 
611*ec779b8eSAndroid Build Coastguard Worker     /* VolumeShaper.State is returned when requesting the last
612*ec779b8eSAndroid Build Coastguard Worker      * state of the VolumeShaper.
613*ec779b8eSAndroid Build Coastguard Worker      *
614*ec779b8eSAndroid Build Coastguard Worker      * This parallels the Java implementation.
615*ec779b8eSAndroid Build Coastguard Worker      * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
616*ec779b8eSAndroid Build Coastguard Worker      * details on the Java implementation.
617*ec779b8eSAndroid Build Coastguard Worker      */
618*ec779b8eSAndroid Build Coastguard Worker     class State : public RefBase, public Parcelable {
619*ec779b8eSAndroid Build Coastguard Worker     public:
State(T volume,S xOffset)620*ec779b8eSAndroid Build Coastguard Worker         State(T volume, S xOffset)
621*ec779b8eSAndroid Build Coastguard Worker             : mVolume(volume)
622*ec779b8eSAndroid Build Coastguard Worker             , mXOffset(xOffset) {
623*ec779b8eSAndroid Build Coastguard Worker         }
624*ec779b8eSAndroid Build Coastguard Worker 
State()625*ec779b8eSAndroid Build Coastguard Worker         State()
626*ec779b8eSAndroid Build Coastguard Worker             : State(NAN, NAN) { }
627*ec779b8eSAndroid Build Coastguard Worker 
getVolume()628*ec779b8eSAndroid Build Coastguard Worker         T getVolume() const {
629*ec779b8eSAndroid Build Coastguard Worker             return mVolume;
630*ec779b8eSAndroid Build Coastguard Worker         }
631*ec779b8eSAndroid Build Coastguard Worker 
setVolume(T volume)632*ec779b8eSAndroid Build Coastguard Worker         void setVolume(T volume) {
633*ec779b8eSAndroid Build Coastguard Worker             mVolume = volume;
634*ec779b8eSAndroid Build Coastguard Worker         }
635*ec779b8eSAndroid Build Coastguard Worker 
getXOffset()636*ec779b8eSAndroid Build Coastguard Worker         S getXOffset() const {
637*ec779b8eSAndroid Build Coastguard Worker             return mXOffset;
638*ec779b8eSAndroid Build Coastguard Worker         }
639*ec779b8eSAndroid Build Coastguard Worker 
setXOffset(S xOffset)640*ec779b8eSAndroid Build Coastguard Worker         void setXOffset(S xOffset) {
641*ec779b8eSAndroid Build Coastguard Worker             mXOffset = xOffset;
642*ec779b8eSAndroid Build Coastguard Worker         }
643*ec779b8eSAndroid Build Coastguard Worker 
writeToParcel(Parcel * parcel)644*ec779b8eSAndroid Build Coastguard Worker         status_t writeToParcel(Parcel* parcel) const override {
645*ec779b8eSAndroid Build Coastguard Worker             if (parcel == nullptr) return BAD_VALUE;
646*ec779b8eSAndroid Build Coastguard Worker             VolumeShaperState state;
647*ec779b8eSAndroid Build Coastguard Worker             writeToParcelable(&state);
648*ec779b8eSAndroid Build Coastguard Worker             return state.writeToParcel(parcel);
649*ec779b8eSAndroid Build Coastguard Worker         }
650*ec779b8eSAndroid Build Coastguard Worker 
writeToParcelable(VolumeShaperState * parcelable)651*ec779b8eSAndroid Build Coastguard Worker         void writeToParcelable(VolumeShaperState* parcelable) const {
652*ec779b8eSAndroid Build Coastguard Worker             parcelable->volume = mVolume;
653*ec779b8eSAndroid Build Coastguard Worker             parcelable->xOffset = mXOffset;
654*ec779b8eSAndroid Build Coastguard Worker         }
655*ec779b8eSAndroid Build Coastguard Worker 
readFromParcel(const Parcel * parcel)656*ec779b8eSAndroid Build Coastguard Worker         status_t readFromParcel(const Parcel* parcel) override {
657*ec779b8eSAndroid Build Coastguard Worker             VolumeShaperState state;
658*ec779b8eSAndroid Build Coastguard Worker             return state.readFromParcel(parcel)
659*ec779b8eSAndroid Build Coastguard Worker                    ?: readFromParcelable(state);
660*ec779b8eSAndroid Build Coastguard Worker         }
661*ec779b8eSAndroid Build Coastguard Worker 
readFromParcelable(const VolumeShaperState & parcelable)662*ec779b8eSAndroid Build Coastguard Worker         status_t readFromParcelable(const VolumeShaperState& parcelable) {
663*ec779b8eSAndroid Build Coastguard Worker             mVolume = parcelable.volume;
664*ec779b8eSAndroid Build Coastguard Worker             mXOffset = parcelable.xOffset;
665*ec779b8eSAndroid Build Coastguard Worker             return OK;
666*ec779b8eSAndroid Build Coastguard Worker         }
667*ec779b8eSAndroid Build Coastguard Worker 
toString()668*ec779b8eSAndroid Build Coastguard Worker         std::string toString() const {
669*ec779b8eSAndroid Build Coastguard Worker             std::stringstream ss;
670*ec779b8eSAndroid Build Coastguard Worker             ss << "VolumeShaper::State{mVolume=" << mVolume;
671*ec779b8eSAndroid Build Coastguard Worker             ss << ", mXOffset=" << mXOffset;
672*ec779b8eSAndroid Build Coastguard Worker             ss << "}";
673*ec779b8eSAndroid Build Coastguard Worker             return ss.str();
674*ec779b8eSAndroid Build Coastguard Worker         }
675*ec779b8eSAndroid Build Coastguard Worker 
676*ec779b8eSAndroid Build Coastguard Worker     private:
677*ec779b8eSAndroid Build Coastguard Worker         T mVolume;   // linear volume in the range MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME
678*ec779b8eSAndroid Build Coastguard Worker         S mXOffset;  // position on curve expressed from MIN_CURVE_TIME to MAX_CURVE_TIME
679*ec779b8eSAndroid Build Coastguard Worker     }; // State
680*ec779b8eSAndroid Build Coastguard Worker 
681*ec779b8eSAndroid Build Coastguard Worker     // Internal helper class to do an affine transform for time and amplitude scaling.
682*ec779b8eSAndroid Build Coastguard Worker     template <typename R>
683*ec779b8eSAndroid Build Coastguard Worker     class Translate {
684*ec779b8eSAndroid Build Coastguard Worker     public:
Translate()685*ec779b8eSAndroid Build Coastguard Worker         Translate()
686*ec779b8eSAndroid Build Coastguard Worker             : mOffset(0)
687*ec779b8eSAndroid Build Coastguard Worker             , mScale(1) {
688*ec779b8eSAndroid Build Coastguard Worker         }
689*ec779b8eSAndroid Build Coastguard Worker 
getOffset()690*ec779b8eSAndroid Build Coastguard Worker         R getOffset() const {
691*ec779b8eSAndroid Build Coastguard Worker             return mOffset;
692*ec779b8eSAndroid Build Coastguard Worker         }
693*ec779b8eSAndroid Build Coastguard Worker 
setOffset(R offset)694*ec779b8eSAndroid Build Coastguard Worker         void setOffset(R offset) {
695*ec779b8eSAndroid Build Coastguard Worker             mOffset = offset;
696*ec779b8eSAndroid Build Coastguard Worker         }
697*ec779b8eSAndroid Build Coastguard Worker 
getScale()698*ec779b8eSAndroid Build Coastguard Worker         R getScale() const {
699*ec779b8eSAndroid Build Coastguard Worker             return mScale;
700*ec779b8eSAndroid Build Coastguard Worker         }
701*ec779b8eSAndroid Build Coastguard Worker 
setScale(R scale)702*ec779b8eSAndroid Build Coastguard Worker         void setScale(R scale) {
703*ec779b8eSAndroid Build Coastguard Worker             mScale = scale;
704*ec779b8eSAndroid Build Coastguard Worker         }
705*ec779b8eSAndroid Build Coastguard Worker 
operator()706*ec779b8eSAndroid Build Coastguard Worker         R operator()(R in) const {
707*ec779b8eSAndroid Build Coastguard Worker             return mScale * (in - mOffset);
708*ec779b8eSAndroid Build Coastguard Worker         }
709*ec779b8eSAndroid Build Coastguard Worker 
toString()710*ec779b8eSAndroid Build Coastguard Worker         std::string toString() const {
711*ec779b8eSAndroid Build Coastguard Worker             std::stringstream ss;
712*ec779b8eSAndroid Build Coastguard Worker             ss << "VolumeShaper::Translate{mOffset=" << mOffset;
713*ec779b8eSAndroid Build Coastguard Worker             ss << ", mScale=" << mScale;
714*ec779b8eSAndroid Build Coastguard Worker             ss << "}";
715*ec779b8eSAndroid Build Coastguard Worker             return ss.str();
716*ec779b8eSAndroid Build Coastguard Worker         }
717*ec779b8eSAndroid Build Coastguard Worker 
718*ec779b8eSAndroid Build Coastguard Worker     private:
719*ec779b8eSAndroid Build Coastguard Worker         R mOffset;
720*ec779b8eSAndroid Build Coastguard Worker         R mScale;
721*ec779b8eSAndroid Build Coastguard Worker     }; // Translate
722*ec779b8eSAndroid Build Coastguard Worker 
convertTimespecToUs(const struct timespec & tv)723*ec779b8eSAndroid Build Coastguard Worker     static int64_t convertTimespecToUs(const struct timespec &tv)
724*ec779b8eSAndroid Build Coastguard Worker     {
725*ec779b8eSAndroid Build Coastguard Worker         return tv.tv_sec * 1000000LL + tv.tv_nsec / 1000;
726*ec779b8eSAndroid Build Coastguard Worker     }
727*ec779b8eSAndroid Build Coastguard Worker 
728*ec779b8eSAndroid Build Coastguard Worker     // current monotonic time in microseconds.
getNowUs()729*ec779b8eSAndroid Build Coastguard Worker     static int64_t getNowUs()
730*ec779b8eSAndroid Build Coastguard Worker     {
731*ec779b8eSAndroid Build Coastguard Worker         struct timespec tv;
732*ec779b8eSAndroid Build Coastguard Worker         if (clock_gettime(CLOCK_MONOTONIC, &tv) != 0) {
733*ec779b8eSAndroid Build Coastguard Worker             return 0; // system is really sick, just return 0 for consistency.
734*ec779b8eSAndroid Build Coastguard Worker         }
735*ec779b8eSAndroid Build Coastguard Worker         return convertTimespecToUs(tv);
736*ec779b8eSAndroid Build Coastguard Worker     }
737*ec779b8eSAndroid Build Coastguard Worker 
738*ec779b8eSAndroid Build Coastguard Worker     /* Native implementation of VolumeShaper.  This is NOT mirrored
739*ec779b8eSAndroid Build Coastguard Worker      * on the Java side, so we don't need to mimic Java side layout
740*ec779b8eSAndroid Build Coastguard Worker      * and data; furthermore, this isn't refcounted as a "RefBase" object.
741*ec779b8eSAndroid Build Coastguard Worker      *
742*ec779b8eSAndroid Build Coastguard Worker      * Since we pass configuration and operation as shared pointers (like
743*ec779b8eSAndroid Build Coastguard Worker      * Java) there is a potential risk that the caller may modify
744*ec779b8eSAndroid Build Coastguard Worker      * these after delivery.
745*ec779b8eSAndroid Build Coastguard Worker      */
VolumeShaper(const sp<VolumeShaper::Configuration> & configuration,const sp<VolumeShaper::Operation> & operation)746*ec779b8eSAndroid Build Coastguard Worker     VolumeShaper(
747*ec779b8eSAndroid Build Coastguard Worker             const sp<VolumeShaper::Configuration> &configuration,
748*ec779b8eSAndroid Build Coastguard Worker             const sp<VolumeShaper::Operation> &operation)
749*ec779b8eSAndroid Build Coastguard Worker         : mConfiguration(configuration) // we do not make a copy
750*ec779b8eSAndroid Build Coastguard Worker         , mOperation(operation)         // ditto
751*ec779b8eSAndroid Build Coastguard Worker         , mStartFrame(-1)
752*ec779b8eSAndroid Build Coastguard Worker         , mLastVolume(T(1))
753*ec779b8eSAndroid Build Coastguard Worker         , mLastXOffset(MIN_CURVE_TIME)
754*ec779b8eSAndroid Build Coastguard Worker         , mDelayXOffset(MIN_CURVE_TIME) {
755*ec779b8eSAndroid Build Coastguard Worker         if (configuration.get() != nullptr
756*ec779b8eSAndroid Build Coastguard Worker                 && (getFlags() & VolumeShaper::Operation::FLAG_DELAY) == 0) {
757*ec779b8eSAndroid Build Coastguard Worker             mLastVolume = configuration->first().second;
758*ec779b8eSAndroid Build Coastguard Worker         }
759*ec779b8eSAndroid Build Coastguard Worker     }
760*ec779b8eSAndroid Build Coastguard Worker 
761*ec779b8eSAndroid Build Coastguard Worker     // We allow a null operation here, though VolumeHandler always provides one.
getFlags()762*ec779b8eSAndroid Build Coastguard Worker     VolumeShaper::Operation::Flag getFlags() const {
763*ec779b8eSAndroid Build Coastguard Worker         return mOperation == nullptr
764*ec779b8eSAndroid Build Coastguard Worker                 ? VolumeShaper::Operation::FLAG_NONE : mOperation->getFlags();
765*ec779b8eSAndroid Build Coastguard Worker     }
766*ec779b8eSAndroid Build Coastguard Worker 
767*ec779b8eSAndroid Build Coastguard Worker     /* Returns the last volume and xoffset reported to the AudioFlinger.
768*ec779b8eSAndroid Build Coastguard Worker      * If the VolumeShaper has not been started, compute what the volume
769*ec779b8eSAndroid Build Coastguard Worker      * should be based on the initial offset specified.
770*ec779b8eSAndroid Build Coastguard Worker      */
getState()771*ec779b8eSAndroid Build Coastguard Worker     sp<VolumeShaper::State> getState() const {
772*ec779b8eSAndroid Build Coastguard Worker         if (!isStarted()) {
773*ec779b8eSAndroid Build Coastguard Worker             const T volume = computeVolumeFromXOffset(mDelayXOffset);
774*ec779b8eSAndroid Build Coastguard Worker             VS_LOG("delayed VolumeShaper, using cached offset:%f for volume:%f",
775*ec779b8eSAndroid Build Coastguard Worker                     mDelayXOffset, volume);
776*ec779b8eSAndroid Build Coastguard Worker             return new VolumeShaper::State(volume, mDelayXOffset);
777*ec779b8eSAndroid Build Coastguard Worker         } else {
778*ec779b8eSAndroid Build Coastguard Worker             return new VolumeShaper::State(mLastVolume, mLastXOffset);
779*ec779b8eSAndroid Build Coastguard Worker         }
780*ec779b8eSAndroid Build Coastguard Worker     }
781*ec779b8eSAndroid Build Coastguard Worker 
getDelayXOffset()782*ec779b8eSAndroid Build Coastguard Worker     S getDelayXOffset() const {
783*ec779b8eSAndroid Build Coastguard Worker         return mDelayXOffset;
784*ec779b8eSAndroid Build Coastguard Worker     }
785*ec779b8eSAndroid Build Coastguard Worker 
setDelayXOffset(S xOffset)786*ec779b8eSAndroid Build Coastguard Worker     void setDelayXOffset(S xOffset) {
787*ec779b8eSAndroid Build Coastguard Worker         mDelayXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
788*ec779b8eSAndroid Build Coastguard Worker     }
789*ec779b8eSAndroid Build Coastguard Worker 
isStarted()790*ec779b8eSAndroid Build Coastguard Worker     bool isStarted() const {
791*ec779b8eSAndroid Build Coastguard Worker         return mStartFrame >= 0;
792*ec779b8eSAndroid Build Coastguard Worker     }
793*ec779b8eSAndroid Build Coastguard Worker 
794*ec779b8eSAndroid Build Coastguard Worker     /* getVolume() updates the last volume/xoffset state so it is not
795*ec779b8eSAndroid Build Coastguard Worker      * const, even though logically it may be viewed as const.
796*ec779b8eSAndroid Build Coastguard Worker      */
getVolume(int64_t trackFrameCount,double trackSampleRate)797*ec779b8eSAndroid Build Coastguard Worker     std::pair<T /* volume */, bool /* active */> getVolume(
798*ec779b8eSAndroid Build Coastguard Worker             int64_t trackFrameCount, double trackSampleRate) {
799*ec779b8eSAndroid Build Coastguard Worker         if ((getFlags() & VolumeShaper::Operation::FLAG_DELAY) != 0) {
800*ec779b8eSAndroid Build Coastguard Worker             // We haven't had PLAY called yet, so just return the value
801*ec779b8eSAndroid Build Coastguard Worker             // as if PLAY were called just now.
802*ec779b8eSAndroid Build Coastguard Worker             VS_LOG("delayed VolumeShaper, using cached offset %f", mDelayXOffset);
803*ec779b8eSAndroid Build Coastguard Worker             const T volume = computeVolumeFromXOffset(mDelayXOffset);
804*ec779b8eSAndroid Build Coastguard Worker             return std::make_pair(volume, false);
805*ec779b8eSAndroid Build Coastguard Worker         }
806*ec779b8eSAndroid Build Coastguard Worker         const bool clockTime = (mConfiguration->getOptionFlags()
807*ec779b8eSAndroid Build Coastguard Worker                 & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
808*ec779b8eSAndroid Build Coastguard Worker         const int64_t frameCount = clockTime ? getNowUs() : trackFrameCount;
809*ec779b8eSAndroid Build Coastguard Worker         const double sampleRate = clockTime ? 1000000 : trackSampleRate;
810*ec779b8eSAndroid Build Coastguard Worker 
811*ec779b8eSAndroid Build Coastguard Worker         if (mStartFrame < 0) {
812*ec779b8eSAndroid Build Coastguard Worker             updatePosition(frameCount, sampleRate, mDelayXOffset);
813*ec779b8eSAndroid Build Coastguard Worker             mStartFrame = frameCount;
814*ec779b8eSAndroid Build Coastguard Worker         }
815*ec779b8eSAndroid Build Coastguard Worker         VS_LOG("frameCount: %lld", (long long)frameCount);
816*ec779b8eSAndroid Build Coastguard Worker         const S x = mXTranslate((T)frameCount);
817*ec779b8eSAndroid Build Coastguard Worker         VS_LOG("translation to normalized time: %f", x);
818*ec779b8eSAndroid Build Coastguard Worker 
819*ec779b8eSAndroid Build Coastguard Worker         std::tuple<T /* volume */, S /* position */, bool /* active */> vt =
820*ec779b8eSAndroid Build Coastguard Worker                 computeStateFromNormalizedTime(x);
821*ec779b8eSAndroid Build Coastguard Worker 
822*ec779b8eSAndroid Build Coastguard Worker         mLastVolume = std::get<0>(vt);
823*ec779b8eSAndroid Build Coastguard Worker         mLastXOffset = std::get<1>(vt);
824*ec779b8eSAndroid Build Coastguard Worker         const bool active = std::get<2>(vt);
825*ec779b8eSAndroid Build Coastguard Worker         VS_LOG("rescaled time:%f  volume:%f  xOffset:%f  active:%s",
826*ec779b8eSAndroid Build Coastguard Worker                 x, mLastVolume, mLastXOffset, active ? "true" : "false");
827*ec779b8eSAndroid Build Coastguard Worker         return std::make_pair(mLastVolume, active);
828*ec779b8eSAndroid Build Coastguard Worker     }
829*ec779b8eSAndroid Build Coastguard Worker 
toString()830*ec779b8eSAndroid Build Coastguard Worker     std::string toString() const {
831*ec779b8eSAndroid Build Coastguard Worker         std::stringstream ss;
832*ec779b8eSAndroid Build Coastguard Worker         ss << "VolumeShaper{mStartFrame=" << mStartFrame;
833*ec779b8eSAndroid Build Coastguard Worker         ss << ", mXTranslate=" << mXTranslate.toString().c_str();
834*ec779b8eSAndroid Build Coastguard Worker         ss << ", mConfiguration=" <<
835*ec779b8eSAndroid Build Coastguard Worker                 (mConfiguration.get() == nullptr
836*ec779b8eSAndroid Build Coastguard Worker                         ? "nullptr" : mConfiguration->toString().c_str());
837*ec779b8eSAndroid Build Coastguard Worker         ss << ", mOperation=" <<
838*ec779b8eSAndroid Build Coastguard Worker                 (mOperation.get() == nullptr
839*ec779b8eSAndroid Build Coastguard Worker                         ? "nullptr" :  mOperation->toString().c_str());
840*ec779b8eSAndroid Build Coastguard Worker         ss << "}";
841*ec779b8eSAndroid Build Coastguard Worker         return ss.str();
842*ec779b8eSAndroid Build Coastguard Worker     }
843*ec779b8eSAndroid Build Coastguard Worker 
844*ec779b8eSAndroid Build Coastguard Worker     Translate<S> mXTranslate; // translation from frames (usec for clock time) to normalized time.
845*ec779b8eSAndroid Build Coastguard Worker     sp<VolumeShaper::Configuration> mConfiguration;
846*ec779b8eSAndroid Build Coastguard Worker     sp<VolumeShaper::Operation> mOperation;
847*ec779b8eSAndroid Build Coastguard Worker 
848*ec779b8eSAndroid Build Coastguard Worker private:
849*ec779b8eSAndroid Build Coastguard Worker     int64_t mStartFrame; // starting frame, non-negative when started (in usec for clock time)
850*ec779b8eSAndroid Build Coastguard Worker     T mLastVolume;       // last computed interpolated volume (y-axis)
851*ec779b8eSAndroid Build Coastguard Worker     S mLastXOffset;      // last computed interpolated xOffset/time (x-axis)
852*ec779b8eSAndroid Build Coastguard Worker     S mDelayXOffset;     // xOffset to use for first invocation of VolumeShaper.
853*ec779b8eSAndroid Build Coastguard Worker 
854*ec779b8eSAndroid Build Coastguard Worker     // Called internally to adjust mXTranslate for first time start.
updatePosition(int64_t startFrame,double sampleRate,S xOffset)855*ec779b8eSAndroid Build Coastguard Worker     void updatePosition(int64_t startFrame, double sampleRate, S xOffset) {
856*ec779b8eSAndroid Build Coastguard Worker         double scale = (mConfiguration->last().first - mConfiguration->first().first)
857*ec779b8eSAndroid Build Coastguard Worker                         / (mConfiguration->getDurationMs() * 0.001 * sampleRate);
858*ec779b8eSAndroid Build Coastguard Worker         const double minScale = 1. / static_cast<double>(INT64_MAX);
859*ec779b8eSAndroid Build Coastguard Worker         scale = std::max(scale, minScale);
860*ec779b8eSAndroid Build Coastguard Worker         VS_LOG("update position: scale %lf  frameCount:%lld, sampleRate:%lf, xOffset:%f",
861*ec779b8eSAndroid Build Coastguard Worker                 scale, (long long) startFrame, sampleRate, xOffset);
862*ec779b8eSAndroid Build Coastguard Worker 
863*ec779b8eSAndroid Build Coastguard Worker         S normalizedTime = (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) != 0 ?
864*ec779b8eSAndroid Build Coastguard Worker                 MAX_CURVE_TIME - xOffset : xOffset;
865*ec779b8eSAndroid Build Coastguard Worker         mXTranslate.setOffset(static_cast<float>(static_cast<double>(startFrame)
866*ec779b8eSAndroid Build Coastguard Worker                                                  - static_cast<double>(normalizedTime) / scale));
867*ec779b8eSAndroid Build Coastguard Worker         mXTranslate.setScale(static_cast<float>(scale));
868*ec779b8eSAndroid Build Coastguard Worker         VS_LOG("translate: %s", mXTranslate.toString().c_str());
869*ec779b8eSAndroid Build Coastguard Worker     }
870*ec779b8eSAndroid Build Coastguard Worker 
computeVolumeFromXOffset(S xOffset)871*ec779b8eSAndroid Build Coastguard Worker     T computeVolumeFromXOffset(S xOffset) const {
872*ec779b8eSAndroid Build Coastguard Worker         const T unscaledVolume = mConfiguration->findY(xOffset);
873*ec779b8eSAndroid Build Coastguard Worker         const T volume = mConfiguration->adjustVolume(unscaledVolume); // handle log scale
874*ec779b8eSAndroid Build Coastguard Worker         VS_LOG("computeVolumeFromXOffset %f -> %f -> %f", xOffset, unscaledVolume, volume);
875*ec779b8eSAndroid Build Coastguard Worker         return volume;
876*ec779b8eSAndroid Build Coastguard Worker     }
877*ec779b8eSAndroid Build Coastguard Worker 
878*ec779b8eSAndroid Build Coastguard Worker     std::tuple<T /* volume */, S /* position */, bool /* active */>
computeStateFromNormalizedTime(S x)879*ec779b8eSAndroid Build Coastguard Worker     computeStateFromNormalizedTime(S x) const {
880*ec779b8eSAndroid Build Coastguard Worker         bool active = true;
881*ec779b8eSAndroid Build Coastguard Worker         // handle reversal of position
882*ec779b8eSAndroid Build Coastguard Worker         if (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) {
883*ec779b8eSAndroid Build Coastguard Worker             x = MAX_CURVE_TIME - x;
884*ec779b8eSAndroid Build Coastguard Worker             VS_LOG("reversing to %f", x);
885*ec779b8eSAndroid Build Coastguard Worker             if (x < MIN_CURVE_TIME) {
886*ec779b8eSAndroid Build Coastguard Worker                 x = MIN_CURVE_TIME;
887*ec779b8eSAndroid Build Coastguard Worker                 active = false; // at the end
888*ec779b8eSAndroid Build Coastguard Worker             } else if (x > MAX_CURVE_TIME) {
889*ec779b8eSAndroid Build Coastguard Worker                 x = MAX_CURVE_TIME; //early
890*ec779b8eSAndroid Build Coastguard Worker             }
891*ec779b8eSAndroid Build Coastguard Worker         } else {
892*ec779b8eSAndroid Build Coastguard Worker             if (x < MIN_CURVE_TIME) {
893*ec779b8eSAndroid Build Coastguard Worker                 x = MIN_CURVE_TIME; // early
894*ec779b8eSAndroid Build Coastguard Worker             } else if (x > MAX_CURVE_TIME) {
895*ec779b8eSAndroid Build Coastguard Worker                 x = MAX_CURVE_TIME;
896*ec779b8eSAndroid Build Coastguard Worker                 active = false; // at end
897*ec779b8eSAndroid Build Coastguard Worker             }
898*ec779b8eSAndroid Build Coastguard Worker         }
899*ec779b8eSAndroid Build Coastguard Worker         const S xOffset = x;
900*ec779b8eSAndroid Build Coastguard Worker         const T volume = computeVolumeFromXOffset(xOffset);
901*ec779b8eSAndroid Build Coastguard Worker         return std::make_tuple(volume, xOffset, active);
902*ec779b8eSAndroid Build Coastguard Worker     }
903*ec779b8eSAndroid Build Coastguard Worker }; // VolumeShaper
904*ec779b8eSAndroid Build Coastguard Worker 
905*ec779b8eSAndroid Build Coastguard Worker /* VolumeHandler combines the volume factors of multiple VolumeShapers associated
906*ec779b8eSAndroid Build Coastguard Worker  * with a player.  It is thread safe by synchronizing all public methods.
907*ec779b8eSAndroid Build Coastguard Worker  *
908*ec779b8eSAndroid Build Coastguard Worker  * This is a native-only implementation.
909*ec779b8eSAndroid Build Coastguard Worker  *
910*ec779b8eSAndroid Build Coastguard Worker  * The server side VolumeHandler is used to maintain a list of volume handlers,
911*ec779b8eSAndroid Build Coastguard Worker  * keep state, and obtain volume.
912*ec779b8eSAndroid Build Coastguard Worker  *
913*ec779b8eSAndroid Build Coastguard Worker  * The client side VolumeHandler is used to maintain a list of volume handlers,
914*ec779b8eSAndroid Build Coastguard Worker  * keep some partial state, and restore if the server dies.
915*ec779b8eSAndroid Build Coastguard Worker  */
916*ec779b8eSAndroid Build Coastguard Worker class VolumeHandler : public RefBase {
917*ec779b8eSAndroid Build Coastguard Worker public:
918*ec779b8eSAndroid Build Coastguard Worker     using S = float;
919*ec779b8eSAndroid Build Coastguard Worker     using T = float;
920*ec779b8eSAndroid Build Coastguard Worker 
921*ec779b8eSAndroid Build Coastguard Worker     // A volume handler which just keeps track of active VolumeShapers does not need sampleRate.
VolumeHandler()922*ec779b8eSAndroid Build Coastguard Worker     VolumeHandler()
923*ec779b8eSAndroid Build Coastguard Worker         : VolumeHandler(0 /* sampleRate */) {
924*ec779b8eSAndroid Build Coastguard Worker     }
925*ec779b8eSAndroid Build Coastguard Worker 
VolumeHandler(uint32_t sampleRate)926*ec779b8eSAndroid Build Coastguard Worker     explicit VolumeHandler(uint32_t sampleRate)
927*ec779b8eSAndroid Build Coastguard Worker         : mSampleRate((double)sampleRate)
928*ec779b8eSAndroid Build Coastguard Worker         , mLastFrame(0)
929*ec779b8eSAndroid Build Coastguard Worker         , mVolumeShaperIdCounter(VolumeShaper::kSystemVolumeShapersMax)
930*ec779b8eSAndroid Build Coastguard Worker         , mLastVolume(1.f, false) {
931*ec779b8eSAndroid Build Coastguard Worker     }
932*ec779b8eSAndroid Build Coastguard Worker 
applyVolumeShaper(const sp<VolumeShaper::Configuration> & configuration,const sp<VolumeShaper::Operation> & operation_in)933*ec779b8eSAndroid Build Coastguard Worker     VolumeShaper::Status applyVolumeShaper(
934*ec779b8eSAndroid Build Coastguard Worker             const sp<VolumeShaper::Configuration> &configuration,
935*ec779b8eSAndroid Build Coastguard Worker             const sp<VolumeShaper::Operation> &operation_in) {
936*ec779b8eSAndroid Build Coastguard Worker         // make a local copy of operation, as we modify it.
937*ec779b8eSAndroid Build Coastguard Worker         sp<VolumeShaper::Operation> operation(new VolumeShaper::Operation(operation_in));
938*ec779b8eSAndroid Build Coastguard Worker         VS_LOG("applyVolumeShaper:configuration: %s", configuration->toString().c_str());
939*ec779b8eSAndroid Build Coastguard Worker         VS_LOG("applyVolumeShaper:operation: %s", operation->toString().c_str());
940*ec779b8eSAndroid Build Coastguard Worker         AutoMutex _l(mLock);
941*ec779b8eSAndroid Build Coastguard Worker         if (configuration == nullptr) {
942*ec779b8eSAndroid Build Coastguard Worker             ALOGE("null configuration");
943*ec779b8eSAndroid Build Coastguard Worker             return VolumeShaper::Status(BAD_VALUE);
944*ec779b8eSAndroid Build Coastguard Worker         }
945*ec779b8eSAndroid Build Coastguard Worker         if (operation == nullptr) {
946*ec779b8eSAndroid Build Coastguard Worker             ALOGE("null operation");
947*ec779b8eSAndroid Build Coastguard Worker             return VolumeShaper::Status(BAD_VALUE);
948*ec779b8eSAndroid Build Coastguard Worker         }
949*ec779b8eSAndroid Build Coastguard Worker         const int32_t id = configuration->getId();
950*ec779b8eSAndroid Build Coastguard Worker         if (id < 0) {
951*ec779b8eSAndroid Build Coastguard Worker             ALOGE("negative id: %d", id);
952*ec779b8eSAndroid Build Coastguard Worker             return VolumeShaper::Status(BAD_VALUE);
953*ec779b8eSAndroid Build Coastguard Worker         }
954*ec779b8eSAndroid Build Coastguard Worker         VS_LOG("applyVolumeShaper id: %d", id);
955*ec779b8eSAndroid Build Coastguard Worker 
956*ec779b8eSAndroid Build Coastguard Worker         switch (configuration->getType()) {
957*ec779b8eSAndroid Build Coastguard Worker         case VolumeShaper::Configuration::TYPE_SCALE: {
958*ec779b8eSAndroid Build Coastguard Worker             const int replaceId = operation->getReplaceId();
959*ec779b8eSAndroid Build Coastguard Worker             if (replaceId >= 0) {
960*ec779b8eSAndroid Build Coastguard Worker                 VS_LOG("replacing %d", replaceId);
961*ec779b8eSAndroid Build Coastguard Worker                 auto replaceIt = findId_l(replaceId);
962*ec779b8eSAndroid Build Coastguard Worker                 if (replaceIt == mVolumeShapers.end()) {
963*ec779b8eSAndroid Build Coastguard Worker                     ALOGW("cannot find replace id: %d", replaceId);
964*ec779b8eSAndroid Build Coastguard Worker                 } else {
965*ec779b8eSAndroid Build Coastguard Worker                     if ((operation->getFlags() & VolumeShaper::Operation::FLAG_JOIN) != 0) {
966*ec779b8eSAndroid Build Coastguard Worker                         // For join, we scale the start volume of the current configuration
967*ec779b8eSAndroid Build Coastguard Worker                         // to match the last-used volume of the replacing VolumeShaper.
968*ec779b8eSAndroid Build Coastguard Worker                         auto state = replaceIt->getState();
969*ec779b8eSAndroid Build Coastguard Worker                         ALOGD("join: state:%s", state->toString().c_str());
970*ec779b8eSAndroid Build Coastguard Worker                         if (state->getXOffset() >= 0) { // valid
971*ec779b8eSAndroid Build Coastguard Worker                             const T volume = state->getVolume();
972*ec779b8eSAndroid Build Coastguard Worker                             ALOGD("join: scaling start volume to %f", volume);
973*ec779b8eSAndroid Build Coastguard Worker                             configuration->scaleToStartVolume(volume);
974*ec779b8eSAndroid Build Coastguard Worker                         }
975*ec779b8eSAndroid Build Coastguard Worker                     }
976*ec779b8eSAndroid Build Coastguard Worker                     (void)mVolumeShapers.erase(replaceIt);
977*ec779b8eSAndroid Build Coastguard Worker                 }
978*ec779b8eSAndroid Build Coastguard Worker                 operation->setReplaceId(-1);
979*ec779b8eSAndroid Build Coastguard Worker             }
980*ec779b8eSAndroid Build Coastguard Worker             // check if we have another of the same id.
981*ec779b8eSAndroid Build Coastguard Worker             auto oldIt = findId_l(id);
982*ec779b8eSAndroid Build Coastguard Worker             if (oldIt != mVolumeShapers.end()) {
983*ec779b8eSAndroid Build Coastguard Worker                 if ((operation->getFlags()
984*ec779b8eSAndroid Build Coastguard Worker                         & VolumeShaper::Operation::FLAG_CREATE_IF_NECESSARY) != 0) {
985*ec779b8eSAndroid Build Coastguard Worker                     // TODO: move the case to a separate function.
986*ec779b8eSAndroid Build Coastguard Worker                     goto HANDLE_TYPE_ID; // no need to create, take over existing id.
987*ec779b8eSAndroid Build Coastguard Worker                 }
988*ec779b8eSAndroid Build Coastguard Worker                 ALOGW("duplicate id, removing old %d", id);
989*ec779b8eSAndroid Build Coastguard Worker                 (void)mVolumeShapers.erase(oldIt);
990*ec779b8eSAndroid Build Coastguard Worker             }
991*ec779b8eSAndroid Build Coastguard Worker 
992*ec779b8eSAndroid Build Coastguard Worker             /* Check if too many application VolumeShapers (with id >= kSystemVolumeShapersMax).
993*ec779b8eSAndroid Build Coastguard Worker              * We check on the server side to ensure synchronization and robustness.
994*ec779b8eSAndroid Build Coastguard Worker              *
995*ec779b8eSAndroid Build Coastguard Worker              * This shouldn't fail on a replace command unless the replaced id is
996*ec779b8eSAndroid Build Coastguard Worker              * already invalid (which *should* be checked in the Java layer).
997*ec779b8eSAndroid Build Coastguard Worker              */
998*ec779b8eSAndroid Build Coastguard Worker             if (id >= VolumeShaper::kSystemVolumeShapersMax
999*ec779b8eSAndroid Build Coastguard Worker                     && numberOfUserVolumeShapers_l() >= VolumeShaper::kUserVolumeShapersMax) {
1000*ec779b8eSAndroid Build Coastguard Worker                 ALOGW("Too many app VolumeShapers, cannot add to VolumeHandler");
1001*ec779b8eSAndroid Build Coastguard Worker                 return VolumeShaper::Status(INVALID_OPERATION);
1002*ec779b8eSAndroid Build Coastguard Worker             }
1003*ec779b8eSAndroid Build Coastguard Worker 
1004*ec779b8eSAndroid Build Coastguard Worker             // create new VolumeShaper with default behavior.
1005*ec779b8eSAndroid Build Coastguard Worker             mVolumeShapers.emplace_back(configuration, new VolumeShaper::Operation());
1006*ec779b8eSAndroid Build Coastguard Worker             VS_LOG("after adding, number of volumeShapers:%zu", mVolumeShapers.size());
1007*ec779b8eSAndroid Build Coastguard Worker         }
1008*ec779b8eSAndroid Build Coastguard Worker         // fall through to handle the operation
1009*ec779b8eSAndroid Build Coastguard Worker         HANDLE_TYPE_ID:
1010*ec779b8eSAndroid Build Coastguard Worker         case VolumeShaper::Configuration::TYPE_ID: {
1011*ec779b8eSAndroid Build Coastguard Worker             VS_LOG("trying to find id: %d", id);
1012*ec779b8eSAndroid Build Coastguard Worker             auto it = findId_l(id);
1013*ec779b8eSAndroid Build Coastguard Worker             if (it == mVolumeShapers.end()) {
1014*ec779b8eSAndroid Build Coastguard Worker                 VS_LOG("couldn't find id: %d", id);
1015*ec779b8eSAndroid Build Coastguard Worker                 return VolumeShaper::Status(INVALID_OPERATION);
1016*ec779b8eSAndroid Build Coastguard Worker             }
1017*ec779b8eSAndroid Build Coastguard Worker             if ((operation->getFlags() & VolumeShaper::Operation::FLAG_TERMINATE) != 0) {
1018*ec779b8eSAndroid Build Coastguard Worker                 VS_LOG("terminate id: %d", id);
1019*ec779b8eSAndroid Build Coastguard Worker                 mVolumeShapers.erase(it);
1020*ec779b8eSAndroid Build Coastguard Worker                 break;
1021*ec779b8eSAndroid Build Coastguard Worker             }
1022*ec779b8eSAndroid Build Coastguard Worker             const bool clockTime = (it->mConfiguration->getOptionFlags()
1023*ec779b8eSAndroid Build Coastguard Worker                     & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
1024*ec779b8eSAndroid Build Coastguard Worker             if ((it->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) !=
1025*ec779b8eSAndroid Build Coastguard Worker                     (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE)) {
1026*ec779b8eSAndroid Build Coastguard Worker                 if (it->isStarted()) {
1027*ec779b8eSAndroid Build Coastguard Worker                     const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
1028*ec779b8eSAndroid Build Coastguard Worker                     const S x = it->mXTranslate((T)frameCount);
1029*ec779b8eSAndroid Build Coastguard Worker                     VS_LOG("reverse normalizedTime: %f", x);
1030*ec779b8eSAndroid Build Coastguard Worker                     // reflect position
1031*ec779b8eSAndroid Build Coastguard Worker                     S target = MAX_CURVE_TIME - x;
1032*ec779b8eSAndroid Build Coastguard Worker                     if (target < MIN_CURVE_TIME) {
1033*ec779b8eSAndroid Build Coastguard Worker                         VS_LOG("clamp to start - begin immediately");
1034*ec779b8eSAndroid Build Coastguard Worker                         target = MIN_CURVE_TIME;
1035*ec779b8eSAndroid Build Coastguard Worker                     }
1036*ec779b8eSAndroid Build Coastguard Worker                     VS_LOG("reverse normalizedTime target: %f", target);
1037*ec779b8eSAndroid Build Coastguard Worker                     it->mXTranslate.setOffset(it->mXTranslate.getOffset()
1038*ec779b8eSAndroid Build Coastguard Worker                             + (x - target) / it->mXTranslate.getScale());
1039*ec779b8eSAndroid Build Coastguard Worker                 }
1040*ec779b8eSAndroid Build Coastguard Worker                 // if not started, the delay offset doesn't change.
1041*ec779b8eSAndroid Build Coastguard Worker             }
1042*ec779b8eSAndroid Build Coastguard Worker             const S xOffset = operation->getXOffset();
1043*ec779b8eSAndroid Build Coastguard Worker             if (!std::isnan(xOffset)) {
1044*ec779b8eSAndroid Build Coastguard Worker                 if (it->isStarted()) {
1045*ec779b8eSAndroid Build Coastguard Worker                     const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
1046*ec779b8eSAndroid Build Coastguard Worker                     const S x = it->mXTranslate((T)frameCount);
1047*ec779b8eSAndroid Build Coastguard Worker                     VS_LOG("normalizedTime translation: %f", x);
1048*ec779b8eSAndroid Build Coastguard Worker                     const S target =
1049*ec779b8eSAndroid Build Coastguard Worker                             (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) != 0 ?
1050*ec779b8eSAndroid Build Coastguard Worker                                     MAX_CURVE_TIME - xOffset : xOffset;
1051*ec779b8eSAndroid Build Coastguard Worker                     VS_LOG("normalizedTime target x offset: %f", target);
1052*ec779b8eSAndroid Build Coastguard Worker                     it->mXTranslate.setOffset(it->mXTranslate.getOffset()
1053*ec779b8eSAndroid Build Coastguard Worker                             + (x - target) / it->mXTranslate.getScale());
1054*ec779b8eSAndroid Build Coastguard Worker                 } else {
1055*ec779b8eSAndroid Build Coastguard Worker                     it->setDelayXOffset(xOffset);
1056*ec779b8eSAndroid Build Coastguard Worker                 }
1057*ec779b8eSAndroid Build Coastguard Worker             }
1058*ec779b8eSAndroid Build Coastguard Worker             it->mOperation = operation; // replace the operation
1059*ec779b8eSAndroid Build Coastguard Worker         } break;
1060*ec779b8eSAndroid Build Coastguard Worker         }
1061*ec779b8eSAndroid Build Coastguard Worker         return VolumeShaper::Status(id);
1062*ec779b8eSAndroid Build Coastguard Worker     }
1063*ec779b8eSAndroid Build Coastguard Worker 
getVolumeShaperState(int id)1064*ec779b8eSAndroid Build Coastguard Worker     sp<VolumeShaper::State> getVolumeShaperState(int id) {
1065*ec779b8eSAndroid Build Coastguard Worker         AutoMutex _l(mLock);
1066*ec779b8eSAndroid Build Coastguard Worker         auto it = findId_l(id);
1067*ec779b8eSAndroid Build Coastguard Worker         if (it == mVolumeShapers.end()) {
1068*ec779b8eSAndroid Build Coastguard Worker             VS_LOG("cannot find state for id: %d", id);
1069*ec779b8eSAndroid Build Coastguard Worker             return nullptr;
1070*ec779b8eSAndroid Build Coastguard Worker         }
1071*ec779b8eSAndroid Build Coastguard Worker         return it->getState();
1072*ec779b8eSAndroid Build Coastguard Worker     }
1073*ec779b8eSAndroid Build Coastguard Worker 
1074*ec779b8eSAndroid Build Coastguard Worker     /* getVolume() is not const, as it updates internal state.
1075*ec779b8eSAndroid Build Coastguard Worker      * Once called, any VolumeShapers not already started begin running.
1076*ec779b8eSAndroid Build Coastguard Worker      */
getVolume(int64_t trackFrameCount)1077*ec779b8eSAndroid Build Coastguard Worker     std::pair<T /* volume */, bool /* active */> getVolume(int64_t trackFrameCount) {
1078*ec779b8eSAndroid Build Coastguard Worker         AutoMutex _l(mLock);
1079*ec779b8eSAndroid Build Coastguard Worker         mLastFrame = trackFrameCount;
1080*ec779b8eSAndroid Build Coastguard Worker         T volume(1);
1081*ec779b8eSAndroid Build Coastguard Worker         size_t activeCount = 0;
1082*ec779b8eSAndroid Build Coastguard Worker         for (auto it = mVolumeShapers.begin(); it != mVolumeShapers.end();) {
1083*ec779b8eSAndroid Build Coastguard Worker             const std::pair<T, bool> shaperVolume =
1084*ec779b8eSAndroid Build Coastguard Worker                     it->getVolume(trackFrameCount, mSampleRate);
1085*ec779b8eSAndroid Build Coastguard Worker             volume *= shaperVolume.first;
1086*ec779b8eSAndroid Build Coastguard Worker             activeCount += shaperVolume.second;
1087*ec779b8eSAndroid Build Coastguard Worker             ++it;
1088*ec779b8eSAndroid Build Coastguard Worker         }
1089*ec779b8eSAndroid Build Coastguard Worker         mLastVolume = std::make_pair(volume, activeCount != 0);
1090*ec779b8eSAndroid Build Coastguard Worker         VS_LOG("getVolume: <%f, %s>", mLastVolume.first, mLastVolume.second ? "true" : "false");
1091*ec779b8eSAndroid Build Coastguard Worker         return mLastVolume;
1092*ec779b8eSAndroid Build Coastguard Worker     }
1093*ec779b8eSAndroid Build Coastguard Worker 
1094*ec779b8eSAndroid Build Coastguard Worker     /* Used by a client side VolumeHandler to ensure all the VolumeShapers
1095*ec779b8eSAndroid Build Coastguard Worker      * indicate that they have been started.  Upon a change in audioserver
1096*ec779b8eSAndroid Build Coastguard Worker      * output sink, this information is used for restoration of the server side
1097*ec779b8eSAndroid Build Coastguard Worker      * VolumeHandler.
1098*ec779b8eSAndroid Build Coastguard Worker      */
setStarted()1099*ec779b8eSAndroid Build Coastguard Worker     void setStarted() {
1100*ec779b8eSAndroid Build Coastguard Worker         (void)getVolume(mLastFrame);  // getVolume() will start the individual VolumeShapers.
1101*ec779b8eSAndroid Build Coastguard Worker     }
1102*ec779b8eSAndroid Build Coastguard Worker 
getLastVolume()1103*ec779b8eSAndroid Build Coastguard Worker     std::pair<T /* volume */, bool /* active */> getLastVolume() const {
1104*ec779b8eSAndroid Build Coastguard Worker         AutoMutex _l(mLock);
1105*ec779b8eSAndroid Build Coastguard Worker         return mLastVolume;
1106*ec779b8eSAndroid Build Coastguard Worker     }
1107*ec779b8eSAndroid Build Coastguard Worker 
toString()1108*ec779b8eSAndroid Build Coastguard Worker     std::string toString() const {
1109*ec779b8eSAndroid Build Coastguard Worker         AutoMutex _l(mLock);
1110*ec779b8eSAndroid Build Coastguard Worker         std::stringstream ss;
1111*ec779b8eSAndroid Build Coastguard Worker         ss << "VolumeHandler{mSampleRate=" << mSampleRate;
1112*ec779b8eSAndroid Build Coastguard Worker         ss << ", mLastFrame=" << mLastFrame;
1113*ec779b8eSAndroid Build Coastguard Worker         ss << ", mVolumeShapers={";
1114*ec779b8eSAndroid Build Coastguard Worker         bool first = true;
1115*ec779b8eSAndroid Build Coastguard Worker         for (const auto &shaper : mVolumeShapers) {
1116*ec779b8eSAndroid Build Coastguard Worker             if (first) {
1117*ec779b8eSAndroid Build Coastguard Worker                 first = false;
1118*ec779b8eSAndroid Build Coastguard Worker             } else {
1119*ec779b8eSAndroid Build Coastguard Worker                 ss << ", ";
1120*ec779b8eSAndroid Build Coastguard Worker             }
1121*ec779b8eSAndroid Build Coastguard Worker             ss << shaper.toString().c_str();
1122*ec779b8eSAndroid Build Coastguard Worker         }
1123*ec779b8eSAndroid Build Coastguard Worker         ss << "}}";
1124*ec779b8eSAndroid Build Coastguard Worker         return ss.str();
1125*ec779b8eSAndroid Build Coastguard Worker     }
1126*ec779b8eSAndroid Build Coastguard Worker 
forall(const std::function<VolumeShaper::Status (const VolumeShaper &)> & lambda)1127*ec779b8eSAndroid Build Coastguard Worker     void forall(const std::function<VolumeShaper::Status (const VolumeShaper &)> &lambda) {
1128*ec779b8eSAndroid Build Coastguard Worker         AutoMutex _l(mLock);
1129*ec779b8eSAndroid Build Coastguard Worker         VS_LOG("forall: mVolumeShapers.size() %zu", mVolumeShapers.size());
1130*ec779b8eSAndroid Build Coastguard Worker         for (const auto &shaper : mVolumeShapers) {
1131*ec779b8eSAndroid Build Coastguard Worker             VolumeShaper::Status status = lambda(shaper);
1132*ec779b8eSAndroid Build Coastguard Worker             VS_LOG("forall applying lambda on shaper (%p): %d", &shaper, (int)status);
1133*ec779b8eSAndroid Build Coastguard Worker         }
1134*ec779b8eSAndroid Build Coastguard Worker     }
1135*ec779b8eSAndroid Build Coastguard Worker 
reset()1136*ec779b8eSAndroid Build Coastguard Worker     void reset() {
1137*ec779b8eSAndroid Build Coastguard Worker         AutoMutex _l(mLock);
1138*ec779b8eSAndroid Build Coastguard Worker         mVolumeShapers.clear();
1139*ec779b8eSAndroid Build Coastguard Worker         mLastFrame = 0;
1140*ec779b8eSAndroid Build Coastguard Worker         // keep mVolumeShaperIdCounter as is.
1141*ec779b8eSAndroid Build Coastguard Worker     }
1142*ec779b8eSAndroid Build Coastguard Worker 
1143*ec779b8eSAndroid Build Coastguard Worker     /* Sets the configuration id if necessary - This is based on the counter
1144*ec779b8eSAndroid Build Coastguard Worker      * internal to the VolumeHandler.
1145*ec779b8eSAndroid Build Coastguard Worker      */
setIdIfNecessary(const sp<VolumeShaper::Configuration> & configuration)1146*ec779b8eSAndroid Build Coastguard Worker     void setIdIfNecessary(const sp<VolumeShaper::Configuration> &configuration) {
1147*ec779b8eSAndroid Build Coastguard Worker         if (configuration && configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
1148*ec779b8eSAndroid Build Coastguard Worker             const int id = configuration->getId();
1149*ec779b8eSAndroid Build Coastguard Worker             if (id == -1) {
1150*ec779b8eSAndroid Build Coastguard Worker                 // Reassign to a unique id, skipping system ids.
1151*ec779b8eSAndroid Build Coastguard Worker                 AutoMutex _l(mLock);
1152*ec779b8eSAndroid Build Coastguard Worker                 while (true) {
1153*ec779b8eSAndroid Build Coastguard Worker                     if (mVolumeShaperIdCounter == INT32_MAX) {
1154*ec779b8eSAndroid Build Coastguard Worker                         mVolumeShaperIdCounter = VolumeShaper::kSystemVolumeShapersMax;
1155*ec779b8eSAndroid Build Coastguard Worker                     } else {
1156*ec779b8eSAndroid Build Coastguard Worker                         ++mVolumeShaperIdCounter;
1157*ec779b8eSAndroid Build Coastguard Worker                     }
1158*ec779b8eSAndroid Build Coastguard Worker                     if (findId_l(mVolumeShaperIdCounter) != mVolumeShapers.end()) {
1159*ec779b8eSAndroid Build Coastguard Worker                         continue; // collision with an existing id.
1160*ec779b8eSAndroid Build Coastguard Worker                     }
1161*ec779b8eSAndroid Build Coastguard Worker                     configuration->setId(mVolumeShaperIdCounter);
1162*ec779b8eSAndroid Build Coastguard Worker                     ALOGD("setting id to %d", mVolumeShaperIdCounter);
1163*ec779b8eSAndroid Build Coastguard Worker                     break;
1164*ec779b8eSAndroid Build Coastguard Worker                 }
1165*ec779b8eSAndroid Build Coastguard Worker             }
1166*ec779b8eSAndroid Build Coastguard Worker         }
1167*ec779b8eSAndroid Build Coastguard Worker     }
1168*ec779b8eSAndroid Build Coastguard Worker 
1169*ec779b8eSAndroid Build Coastguard Worker private:
findId_l(int32_t id)1170*ec779b8eSAndroid Build Coastguard Worker     std::list<VolumeShaper>::iterator findId_l(int32_t id) {
1171*ec779b8eSAndroid Build Coastguard Worker         std::list<VolumeShaper>::iterator it = mVolumeShapers.begin();
1172*ec779b8eSAndroid Build Coastguard Worker         for (; it != mVolumeShapers.end(); ++it) {
1173*ec779b8eSAndroid Build Coastguard Worker             if (it->mConfiguration->getId() == id) {
1174*ec779b8eSAndroid Build Coastguard Worker                 break;
1175*ec779b8eSAndroid Build Coastguard Worker             }
1176*ec779b8eSAndroid Build Coastguard Worker         }
1177*ec779b8eSAndroid Build Coastguard Worker         return it;
1178*ec779b8eSAndroid Build Coastguard Worker     }
1179*ec779b8eSAndroid Build Coastguard Worker 
numberOfUserVolumeShapers_l()1180*ec779b8eSAndroid Build Coastguard Worker     size_t numberOfUserVolumeShapers_l() const {
1181*ec779b8eSAndroid Build Coastguard Worker         size_t count = 0;
1182*ec779b8eSAndroid Build Coastguard Worker         for (const auto &shaper : mVolumeShapers) {
1183*ec779b8eSAndroid Build Coastguard Worker             count += (shaper.mConfiguration->getId() >= VolumeShaper::kSystemVolumeShapersMax);
1184*ec779b8eSAndroid Build Coastguard Worker         }
1185*ec779b8eSAndroid Build Coastguard Worker         return count;
1186*ec779b8eSAndroid Build Coastguard Worker     }
1187*ec779b8eSAndroid Build Coastguard Worker 
1188*ec779b8eSAndroid Build Coastguard Worker     mutable Mutex mLock;
1189*ec779b8eSAndroid Build Coastguard Worker     double mSampleRate; // in samples (frames) per second
1190*ec779b8eSAndroid Build Coastguard Worker     int64_t mLastFrame; // logging purpose only, 0 on start
1191*ec779b8eSAndroid Build Coastguard Worker     int32_t mVolumeShaperIdCounter; // a counter to return a unique volume shaper id.
1192*ec779b8eSAndroid Build Coastguard Worker     std::pair<T /* volume */, bool /* active */> mLastVolume;
1193*ec779b8eSAndroid Build Coastguard Worker     std::list<VolumeShaper> mVolumeShapers; // list provides stable iterators on erase
1194*ec779b8eSAndroid Build Coastguard Worker }; // VolumeHandler
1195*ec779b8eSAndroid Build Coastguard Worker 
1196*ec779b8eSAndroid Build Coastguard Worker } // namespace media
1197*ec779b8eSAndroid Build Coastguard Worker 
1198*ec779b8eSAndroid Build Coastguard Worker } // namespace android
1199*ec779b8eSAndroid Build Coastguard Worker 
1200*ec779b8eSAndroid Build Coastguard Worker #pragma pop_macro("LOG_TAG")
1201*ec779b8eSAndroid Build Coastguard Worker 
1202*ec779b8eSAndroid Build Coastguard Worker #endif // ANDROID_VOLUME_SHAPER_H
1203