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