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
38namespace 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
49class VolumeShaper {
50public:
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>
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
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
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
142        Type getType() const {
143            return mType;
144        }
145
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
158        OptionFlag getOptionFlags() const {
159            return mOptionFlags;
160        }
161
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
171        double getDurationMs() const {
172            return mDurationMs;
173        }
174
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
184        int32_t getId() const {
185            return mId;
186        }
187
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         */
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         */
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         */
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         */
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
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
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.
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
354        Operation()
355            : Operation(FLAG_NONE, -1 /* replaceId */) {
356        }
357
358        Operation(Flag flags, int replaceId)
359            : Operation(flags, replaceId, std::numeric_limits<S>::quiet_NaN() /* xOffset */) {
360        }
361
362        explicit Operation(const Operation &operation)
363            : Operation(operation.mFlags, operation.mReplaceId, operation.mXOffset) {
364        }
365
366        explicit Operation(const sp<Operation> &operation)
367            : Operation(*operation.get()) {
368        }
369
370        Operation(Flag flags, int replaceId, S xOffset)
371            : mFlags(flags)
372            , mReplaceId(replaceId)
373            , mXOffset(xOffset) {
374        }
375
376        int32_t getReplaceId() const {
377            return mReplaceId;
378        }
379
380        void setReplaceId(int32_t replaceId) {
381            mReplaceId = replaceId;
382        }
383
384        S getXOffset() const {
385            return mXOffset;
386        }
387
388        void setXOffset(S xOffset) {
389            mXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
390        }
391
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         */
407        void setNormalizedTime(S normalizedTime) {
408            setXOffset((mFlags & FLAG_REVERSE) != 0
409                    ? MAX_CURVE_TIME - normalizedTime : normalizedTime);
410        }
411
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
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
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
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:
460        State(T volume, S xOffset)
461            : mVolume(volume)
462            , mXOffset(xOffset) {
463        }
464
465        State()
466            : State(NAN, NAN) { }
467
468        T getVolume() const {
469            return mVolume;
470        }
471
472        void setVolume(T volume) {
473            mVolume = volume;
474        }
475
476        S getXOffset() const {
477            return mXOffset;
478        }
479
480        void setXOffset(S xOffset) {
481            mXOffset = xOffset;
482        }
483
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
490        status_t readFromParcel(const Parcel &parcel) {
491            return parcel.readFloat(&mVolume)
492                     ?: parcel.readFloat(&mXOffset);
493        }
494
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:
512        Translate()
513            : mOffset(0)
514            , mScale(1) {
515        }
516
517        R getOffset() const {
518            return mOffset;
519        }
520
521        void setOffset(R offset) {
522            mOffset = offset;
523        }
524
525        R getScale() const {
526            return mScale;
527        }
528
529        void setScale(R scale) {
530            mScale = scale;
531        }
532
533        R operator()(R in) const {
534            return mScale * (in - mOffset);
535        }
536
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
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.
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     */
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.
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     */
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
609    S getDelayXOffset() const {
610        return mDelayXOffset;
611    }
612
613    void setDelayXOffset(S xOffset) {
614        mDelayXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
615    }
616
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     */
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
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
675private:
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.
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
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 */>
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 */
743class VolumeHandler : public RefBase {
744public:
745    using S = float;
746    using T = float;
747
748    // A volume handler which just keeps track of active VolumeShapers does not need sampleRate.
749    VolumeHandler()
750        : VolumeHandler(0 /* sampleRate */) {
751    }
752
753    explicit VolumeHandler(uint32_t sampleRate)
754        : mSampleRate((double)sampleRate)
755        , mLastFrame(0)
756        , mVolumeShaperIdCounter(VolumeShaper::kSystemVolumeShapersMax)
757        , mLastVolume(1.f, false) {
758    }
759
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
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     */
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     */
926    void setStarted() {
927        (void)getVolume(mLastFrame);  // getVolume() will start the individual VolumeShapers.
928    }
929
930    std::pair<T /* volume */, bool /* active */> getLastVolume() const {
931        AutoMutex _l(mLock);
932        return mLastVolume;
933    }
934
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
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
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     */
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
996private:
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
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