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