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