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