VolumeShaper.h revision 549bd02a8421b0b53a1b5e8961a41cf551fce72d
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    using S = float;
55    using T = float;
56
57    static const int kSystemIdMax = 16;
58
59    // VolumeShaper::Status is equivalent to status_t if negative
60    // but if non-negative represents the id operated on.
61    // It must be expressible as an int32_t for binder purposes.
62    using Status = status_t;
63
64    class Configuration : public Interpolator<S, T>, public RefBase {
65    public:
66        /* VolumeShaper.Configuration derives from the Interpolator class and adds
67         * parameters relating to the volume shape.
68         */
69
70        // TODO document as per VolumeShaper.java flags.
71
72        // must match with VolumeShaper.java in frameworks/base
73        enum Type : int32_t {
74            TYPE_ID,
75            TYPE_SCALE,
76        };
77
78        // must match with VolumeShaper.java in frameworks/base
79        enum OptionFlag : int32_t {
80            OPTION_FLAG_NONE           = 0,
81            OPTION_FLAG_VOLUME_IN_DBFS = (1 << 0),
82            OPTION_FLAG_CLOCK_TIME     = (1 << 1),
83
84            OPTION_FLAG_ALL            = (OPTION_FLAG_VOLUME_IN_DBFS | OPTION_FLAG_CLOCK_TIME),
85        };
86
87        // bring to derived class; must match with VolumeShaper.java in frameworks/base
88        using InterpolatorType = Interpolator<S, T>::InterpolatorType;
89
90        Configuration()
91            : Interpolator<S, T>()
92            , RefBase()
93            , mType(TYPE_SCALE)
94            , mOptionFlags(OPTION_FLAG_NONE)
95            , mDurationMs(1000.)
96            , mId(-1) {
97        }
98
99        explicit Configuration(const Configuration &configuration)
100            : Interpolator<S, T>(*static_cast<const Interpolator<S, T> *>(&configuration))
101            , RefBase()
102            , mType(configuration.mType)
103            , mOptionFlags(configuration.mOptionFlags)
104            , mDurationMs(configuration.mDurationMs)
105            , mId(configuration.mId) {
106        }
107
108        Type getType() const {
109            return mType;
110        }
111
112        status_t setType(Type type) {
113            switch (type) {
114            case TYPE_ID:
115            case TYPE_SCALE:
116                mType = type;
117                return NO_ERROR;
118            default:
119                ALOGE("invalid Type: %d", type);
120                return BAD_VALUE;
121            }
122        }
123
124        OptionFlag getOptionFlags() const {
125            return mOptionFlags;
126        }
127
128        status_t setOptionFlags(OptionFlag optionFlags) {
129            if ((optionFlags & ~OPTION_FLAG_ALL) != 0) {
130                ALOGE("optionFlags has invalid bits: %#x", optionFlags);
131                return BAD_VALUE;
132            }
133            mOptionFlags = optionFlags;
134            return NO_ERROR;
135        }
136
137        double getDurationMs() const {
138            return mDurationMs;
139        }
140
141        void setDurationMs(double durationMs) {
142            mDurationMs = durationMs;
143        }
144
145        int32_t getId() const {
146            return mId;
147        }
148
149        void setId(int32_t id) {
150            mId = id;
151        }
152
153        T adjustVolume(T volume) const {
154            if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
155                const T out = powf(10.f, volume / 10.0f);
156                VS_LOG("in: %f  out: %f", volume, out);
157                volume = out;
158            }
159            // clamp
160            if (volume < 0.f) {
161                volume = 0.f;
162            } else if (volume > 1.f) {
163                volume = 1.f;
164            }
165            return volume;
166        }
167
168        status_t checkCurve() {
169            if (mType == TYPE_ID) return NO_ERROR;
170            if (this->size() < 2) {
171                ALOGE("curve must have at least 2 points");
172                return BAD_VALUE;
173            }
174            if (first().first != 0.f || last().first != 1.f) {
175                ALOGE("curve must start at 0.f and end at 1.f");
176                return BAD_VALUE;
177            }
178            if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
179                for (const auto &pt : *this) {
180                    if (!(pt.second <= 0.f) /* handle nan */) {
181                        ALOGE("positive volume dbFS");
182                        return BAD_VALUE;
183                    }
184                }
185            } else {
186                for (const auto &pt : *this) {
187                    if (!(pt.second >= 0.f) || !(pt.second <= 1.f) /* handle nan */) {
188                        ALOGE("volume < 0.f or > 1.f");
189                        return BAD_VALUE;
190                    }
191                }
192            }
193            return NO_ERROR;
194        }
195
196        void clampVolume() {
197            if ((mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
198                for (auto it = this->begin(); it != this->end(); ++it) {
199                    if (!(it->second <= 0.f) /* handle nan */) {
200                        it->second = 0.f;
201                    }
202                }
203            } else {
204                for (auto it = this->begin(); it != this->end(); ++it) {
205                    if (!(it->second >= 0.f) /* handle nan */) {
206                        it->second = 0.f;
207                    } else if (!(it->second <= 1.f)) {
208                        it->second = 1.f;
209                    }
210                }
211            }
212        }
213
214        /* scaleToStartVolume() is used to set the start volume of a
215         * new VolumeShaper curve, when replacing one VolumeShaper
216         * with another using the "join" (volume match) option.
217         *
218         * It works best for monotonic volume ramps or ducks.
219         */
220        void scaleToStartVolume(T volume) {
221            if (this->size() < 2) {
222                return;
223            }
224            const T startVolume = first().second;
225            const T endVolume = last().second;
226            if (endVolume == startVolume) {
227                // match with linear ramp
228                const T offset = volume - startVolume;
229                for (auto it = this->begin(); it != this->end(); ++it) {
230                    it->second = it->second + offset * (1.f - it->first);
231                }
232            } else {
233                const T  scale = (volume - endVolume) / (startVolume - endVolume);
234                for (auto it = this->begin(); it != this->end(); ++it) {
235                    it->second = scale * (it->second - endVolume) + endVolume;
236                }
237            }
238            clampVolume();
239        }
240
241        // The parcel layout must match VolumeShaper.java
242        status_t writeToParcel(Parcel *parcel) const {
243            if (parcel == nullptr) return BAD_VALUE;
244            return parcel->writeInt32((int32_t)mType)
245                    ?: parcel->writeInt32(mId)
246                    ?: mType == TYPE_ID
247                        ? NO_ERROR
248                        : parcel->writeInt32((int32_t)mOptionFlags)
249                            ?: parcel->writeDouble(mDurationMs)
250                            ?: Interpolator<S, T>::writeToParcel(parcel);
251        }
252
253        status_t readFromParcel(const Parcel &parcel) {
254            int32_t type, optionFlags;
255            return parcel.readInt32(&type)
256                    ?: setType((Type)type)
257                    ?: parcel.readInt32(&mId)
258                    ?: mType == TYPE_ID
259                        ? NO_ERROR
260                        : parcel.readInt32(&optionFlags)
261                            ?: setOptionFlags((OptionFlag)optionFlags)
262                            ?: parcel.readDouble(&mDurationMs)
263                            ?: Interpolator<S, T>::readFromParcel(parcel)
264                            ?: checkCurve();
265        }
266
267        std::string toString() const {
268            std::stringstream ss;
269            ss << "mType: " << static_cast<int32_t>(mType) << std::endl;
270            ss << "mId: " << mId << std::endl;
271            if (mType != TYPE_ID) {
272                ss << "mOptionFlags: " << static_cast<int32_t>(mOptionFlags) << std::endl;
273                ss << "mDurationMs: " << mDurationMs << std::endl;
274                ss << Interpolator<S, T>::toString().c_str();
275            }
276            return ss.str();
277        }
278
279    private:
280        Type mType;
281        int32_t mId;
282        OptionFlag mOptionFlags;
283        double mDurationMs;
284    }; // Configuration
285
286    // must match with VolumeShaper.java in frameworks/base
287    // TODO document per VolumeShaper.java flags.
288    class Operation : public RefBase {
289    public:
290        enum Flag : int32_t {
291            FLAG_NONE      = 0,
292            FLAG_REVERSE   = (1 << 0),
293            FLAG_TERMINATE = (1 << 1),
294            FLAG_JOIN      = (1 << 2),
295            FLAG_DELAY     = (1 << 3),
296            FLAG_CREATE_IF_NECESSARY = (1 << 4),
297
298            FLAG_ALL       = (FLAG_REVERSE | FLAG_TERMINATE | FLAG_JOIN | FLAG_DELAY
299                            | FLAG_CREATE_IF_NECESSARY),
300        };
301
302        Operation()
303            : Operation(FLAG_NONE, -1 /* replaceId */) {
304        }
305
306        Operation(Flag flags, int replaceId)
307            : Operation(flags, replaceId, std::numeric_limits<S>::quiet_NaN() /* xOffset */) {
308        }
309
310        explicit Operation(const Operation &operation)
311            : Operation(operation.mFlags, operation.mReplaceId, operation.mXOffset) {
312        }
313
314        explicit Operation(const sp<Operation> &operation)
315            : Operation(*operation.get()) {
316        }
317
318        Operation(Flag flags, int replaceId, S xOffset)
319            : mFlags(flags)
320            , mReplaceId(replaceId)
321            , mXOffset(xOffset) {
322        }
323
324        int32_t getReplaceId() const {
325            return mReplaceId;
326        }
327
328        void setReplaceId(int32_t replaceId) {
329            mReplaceId = replaceId;
330        }
331
332        S getXOffset() const {
333            return mXOffset;
334        }
335
336        void setXOffset(S xOffset) {
337            mXOffset = xOffset;
338        }
339
340        Flag getFlags() const {
341            return mFlags;
342        }
343
344        status_t setFlags(Flag flags) {
345            if ((flags & ~FLAG_ALL) != 0) {
346                ALOGE("flags has invalid bits: %#x", flags);
347                return BAD_VALUE;
348            }
349            mFlags = flags;
350            return NO_ERROR;
351        }
352
353        status_t writeToParcel(Parcel *parcel) const {
354            if (parcel == nullptr) return BAD_VALUE;
355            return parcel->writeInt32((int32_t)mFlags)
356                    ?: parcel->writeInt32(mReplaceId)
357                    ?: parcel->writeFloat(mXOffset);
358        }
359
360        status_t readFromParcel(const Parcel &parcel) {
361            int32_t flags;
362            return parcel.readInt32(&flags)
363                    ?: parcel.readInt32(&mReplaceId)
364                    ?: parcel.readFloat(&mXOffset)
365                    ?: setFlags((Flag)flags);
366        }
367
368        std::string toString() const {
369            std::stringstream ss;
370            ss << "mFlags: " << static_cast<int32_t>(mFlags) << std::endl;
371            ss << "mReplaceId: " << mReplaceId << std::endl;
372            ss << "mXOffset: " << mXOffset << std::endl;
373            return ss.str();
374        }
375
376    private:
377        Flag mFlags;
378        int32_t mReplaceId;
379        S mXOffset;
380    }; // Operation
381
382    // must match with VolumeShaper.java in frameworks/base
383    class State : public RefBase {
384    public:
385        State(T volume, S xOffset)
386            : mVolume(volume)
387            , mXOffset(xOffset) {
388        }
389
390        State()
391            : State(-1.f, -1.f) { }
392
393        T getVolume() const {
394            return mVolume;
395        }
396
397        void setVolume(T volume) {
398            mVolume = volume;
399        }
400
401        S getXOffset() const {
402            return mXOffset;
403        }
404
405        void setXOffset(S xOffset) {
406            mXOffset = xOffset;
407        }
408
409        status_t writeToParcel(Parcel *parcel) const {
410            if (parcel == nullptr) return BAD_VALUE;
411            return parcel->writeFloat(mVolume)
412                    ?: parcel->writeFloat(mXOffset);
413        }
414
415        status_t readFromParcel(const Parcel &parcel) {
416            return parcel.readFloat(&mVolume)
417                     ?: parcel.readFloat(&mXOffset);
418        }
419
420        std::string toString() const {
421            std::stringstream ss;
422            ss << "mVolume: " << mVolume << std::endl;
423            ss << "mXOffset: " << mXOffset << std::endl;
424            return ss.str();
425        }
426
427    private:
428        T mVolume;
429        S mXOffset;
430    }; // State
431
432    template <typename R>
433    class Translate {
434    public:
435        Translate()
436            : mOffset(0)
437            , mScale(1) {
438        }
439
440        R getOffset() const {
441            return mOffset;
442        }
443
444        void setOffset(R offset) {
445            mOffset = offset;
446        }
447
448        R getScale() const {
449            return mScale;
450        }
451
452        void setScale(R scale) {
453            mScale = scale;
454        }
455
456        R operator()(R in) const {
457            return mScale * (in - mOffset);
458        }
459
460        std::string toString() const {
461            std::stringstream ss;
462            ss << "mOffset: " << mOffset << std::endl;
463            ss << "mScale: " << mScale << std::endl;
464            return ss.str();
465        }
466
467    private:
468        R mOffset;
469        R mScale;
470    }; // Translate
471
472    static int64_t convertTimespecToUs(const struct timespec &tv)
473    {
474        return tv.tv_sec * 1000000ll + tv.tv_nsec / 1000;
475    }
476
477    // current monotonic time in microseconds.
478    static int64_t getNowUs()
479    {
480        struct timespec tv;
481        if (clock_gettime(CLOCK_MONOTONIC, &tv) != 0) {
482            return 0; // system is really sick, just return 0 for consistency.
483        }
484        return convertTimespecToUs(tv);
485    }
486
487    // TODO: Since we pass configuration and operation as shared pointers
488    // there is a potential risk that the caller may modify these after
489    // delivery.  Currently, we don't require copies made here.
490    VolumeShaper(
491            const sp<VolumeShaper::Configuration> &configuration,
492            const sp<VolumeShaper::Operation> &operation)
493        : mConfiguration(configuration) // we do not make a copy
494        , mOperation(operation)         // ditto
495        , mStartFrame(-1)
496        , mLastVolume(T(1))
497        , mLastXOffset(0.f)
498        , mDelayXOffset(std::numeric_limits<S>::quiet_NaN()) {
499        if (configuration.get() != nullptr
500                && (getFlags() & VolumeShaper::Operation::FLAG_DELAY) == 0) {
501            mLastVolume = configuration->first().second;
502        }
503    }
504
505    void updatePosition(int64_t startFrame, double sampleRate) {
506        double scale = (mConfiguration->last().first - mConfiguration->first().first)
507                        / (mConfiguration->getDurationMs() * 0.001 * sampleRate);
508        const double minScale = 1. / static_cast<double>(INT64_MAX);
509        scale = std::max(scale, minScale);
510        const S xOffset = std::isnan(mDelayXOffset) ? mConfiguration->first().first : mDelayXOffset;
511        VS_LOG("update position: scale %lf  frameCount:%lld, sampleRate:%lf, xOffset:%f",
512                scale, (long long) startFrame, sampleRate, xOffset);
513
514        mXTranslate.setOffset(static_cast<float>(static_cast<double>(startFrame)
515                                                 - static_cast<double>(xOffset) / scale));
516        mXTranslate.setScale(static_cast<float>(scale));
517        VS_LOG("translate: %s", mXTranslate.toString().c_str());
518    }
519
520    // We allow a null operation here, though VolumeHandler always provides one.
521    VolumeShaper::Operation::Flag getFlags() const {
522        return mOperation == nullptr
523                ? VolumeShaper::Operation::FLAG_NONE :mOperation->getFlags();
524    }
525
526    sp<VolumeShaper::State> getState() const {
527        return new VolumeShaper::State(mLastVolume, mLastXOffset);
528    }
529
530    void setDelayXOffset(S xOffset) {
531        mDelayXOffset = xOffset;
532    }
533
534    bool isStarted() const {
535        return mStartFrame >= 0;
536    }
537
538    std::pair<T /* volume */, bool /* active */> getVolume(
539            int64_t trackFrameCount, double trackSampleRate) {
540        if ((getFlags() & VolumeShaper::Operation::FLAG_DELAY) != 0) {
541            VS_LOG("delayed VolumeShaper, ignoring");
542            mLastVolume = T(1);
543            mLastXOffset = 0.;
544            return std::make_pair(T(1), false);
545        }
546        const bool clockTime = (mConfiguration->getOptionFlags()
547                & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
548        const int64_t frameCount = clockTime ? getNowUs() : trackFrameCount;
549        const double sampleRate = clockTime ? 1000000 : trackSampleRate;
550
551        if (mStartFrame < 0) {
552            updatePosition(frameCount, sampleRate);
553            mStartFrame = frameCount;
554        }
555        VS_LOG("frameCount: %lld", (long long)frameCount);
556        S x = mXTranslate((T)frameCount);
557        VS_LOG("translation: %f", x);
558
559        // handle reversal of position
560        if (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) {
561            x = 1.f - x;
562            VS_LOG("reversing to %f", x);
563            if (x < mConfiguration->first().first) {
564                mLastXOffset = 1.f;
565                const T volume = mConfiguration->adjustVolume(
566                        mConfiguration->first().second);  // persist last value
567                VS_LOG("persisting volume %f", volume);
568                mLastVolume = volume;
569                return std::make_pair(volume, false);
570            }
571            if (x > mConfiguration->last().first) {
572                mLastXOffset = 0.f;
573                mLastVolume = 1.f;
574                return std::make_pair(T(1), true); // too early
575            }
576        } else {
577            if (x < mConfiguration->first().first) {
578                mLastXOffset = 0.f;
579                mLastVolume = 1.f;
580                return std::make_pair(T(1), true); // too early
581            }
582            if (x > mConfiguration->last().first) {
583                mLastXOffset = 1.f;
584                const T volume = mConfiguration->adjustVolume(
585                        mConfiguration->last().second);  // persist last value
586                VS_LOG("persisting volume %f", volume);
587                mLastVolume = volume;
588                return std::make_pair(volume, false);
589            }
590        }
591        mLastXOffset = x;
592        // x contains the location on the volume curve to use.
593        const T unscaledVolume = mConfiguration->findY(x);
594        const T volume = mConfiguration->adjustVolume(unscaledVolume); // handle log scale
595        VS_LOG("volume: %f  unscaled: %f", volume, unscaledVolume);
596        mLastVolume = volume;
597        return std::make_pair(volume, true);
598    }
599
600    std::string toString() const {
601        std::stringstream ss;
602        ss << "StartFrame: " << mStartFrame << std::endl;
603        ss << mXTranslate.toString().c_str();
604        if (mConfiguration.get() == nullptr) {
605            ss << "VolumeShaper::Configuration: nullptr" << std::endl;
606        } else {
607            ss << "VolumeShaper::Configuration:" << std::endl;
608            ss << mConfiguration->toString().c_str();
609        }
610        if (mOperation.get() == nullptr) {
611            ss << "VolumeShaper::Operation: nullptr" << std::endl;
612        } else {
613            ss << "VolumeShaper::Operation:" << std::endl;
614            ss << mOperation->toString().c_str();
615        }
616        return ss.str();
617    }
618
619    Translate<S> mXTranslate; // x axis translation from frames (in usec for clock time)
620    sp<VolumeShaper::Configuration> mConfiguration;
621    sp<VolumeShaper::Operation> mOperation;
622    int64_t mStartFrame; // starting frame, non-negative when started (in usec for clock time)
623    T mLastVolume;       // last computed interpolated volume (y-axis)
624    S mLastXOffset;      // last computed interpolated xOffset/time (x-axis)
625    S mDelayXOffset;     // delay xOffset on first volumeshaper start.
626}; // VolumeShaper
627
628// VolumeHandler combines the volume factors of multiple VolumeShapers and handles
629// multiple thread access by synchronizing all public methods.
630class VolumeHandler : public RefBase {
631public:
632    using S = float;
633    using T = float;
634
635    // A volume handler which just keeps track of active VolumeShapers does not need sampleRate.
636    VolumeHandler()
637        : VolumeHandler(0 /* sampleRate */) {
638    }
639
640    explicit VolumeHandler(uint32_t sampleRate)
641        : mSampleRate((double)sampleRate)
642        , mLastFrame(0)
643        , mVolumeShaperIdCounter(VolumeShaper::kSystemIdMax)
644        , mLastVolume(1.f, false) {
645    }
646
647    VolumeShaper::Status applyVolumeShaper(
648            const sp<VolumeShaper::Configuration> &configuration,
649            const sp<VolumeShaper::Operation> &operation) {
650        VS_LOG("applyVolumeShaper:configuration: %s", configuration->toString().c_str());
651        VS_LOG("applyVolumeShaper:operation: %s", operation->toString().c_str());
652        AutoMutex _l(mLock);
653        if (configuration == nullptr) {
654            ALOGE("null configuration");
655            return VolumeShaper::Status(BAD_VALUE);
656        }
657        if (operation == nullptr) {
658            ALOGE("null operation");
659            return VolumeShaper::Status(BAD_VALUE);
660        }
661        const int32_t id = configuration->getId();
662        if (id < 0) {
663            ALOGE("negative id: %d", id);
664            return VolumeShaper::Status(BAD_VALUE);
665        }
666        VS_LOG("applyVolumeShaper id: %d", id);
667
668        switch (configuration->getType()) {
669        case VolumeShaper::Configuration::TYPE_SCALE: {
670            const int replaceId = operation->getReplaceId();
671            if (replaceId >= 0) {
672                auto replaceIt = findId_l(replaceId);
673                if (replaceIt == mVolumeShapers.end()) {
674                    ALOGW("cannot find replace id: %d", replaceId);
675                } else {
676                    if ((replaceIt->getFlags() & VolumeShaper::Operation::FLAG_JOIN) != 0) {
677                        // For join, we scale the start volume of the current configuration
678                        // to match the last-used volume of the replacing VolumeShaper.
679                        auto state = replaceIt->getState();
680                        if (state->getXOffset() >= 0) { // valid
681                            const T volume = state->getVolume();
682                            ALOGD("join: scaling start volume to %f", volume);
683                            configuration->scaleToStartVolume(volume);
684                        }
685                    }
686                    (void)mVolumeShapers.erase(replaceIt);
687                }
688                operation->setReplaceId(-1);
689            }
690            // check if we have another of the same id.
691            auto oldIt = findId_l(id);
692            if (oldIt != mVolumeShapers.end()) {
693                if ((operation->getFlags()
694                        & VolumeShaper::Operation::FLAG_CREATE_IF_NECESSARY) != 0) {
695                    // TODO: move the case to a separate function.
696                    goto HANDLE_TYPE_ID; // no need to create, take over existing id.
697                }
698                ALOGW("duplicate id, removing old %d", id);
699                (void)mVolumeShapers.erase(oldIt);
700            }
701            // create new VolumeShaper
702            mVolumeShapers.emplace_back(configuration, operation);
703        }
704        // fall through to handle the operation
705        HANDLE_TYPE_ID:
706        case VolumeShaper::Configuration::TYPE_ID: {
707            VS_LOG("trying to find id: %d", id);
708            auto it = findId_l(id);
709            if (it == mVolumeShapers.end()) {
710                VS_LOG("couldn't find id: %d", id);
711                return VolumeShaper::Status(INVALID_OPERATION);
712            }
713            if ((it->getFlags() & VolumeShaper::Operation::FLAG_TERMINATE) != 0) {
714                VS_LOG("terminate id: %d", id);
715                mVolumeShapers.erase(it);
716                break;
717            }
718            const bool clockTime = (it->mConfiguration->getOptionFlags()
719                    & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
720            if ((it->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) !=
721                    (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE)) {
722                const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
723                const S x = it->mXTranslate((T)frameCount);
724                VS_LOG("reverse translation: %f", x);
725                // reflect position
726                S target = 1.f - x;
727                if (target < it->mConfiguration->first().first) {
728                    VS_LOG("clamp to start - begin immediately");
729                    target = 0.;
730                }
731                VS_LOG("target reverse: %f", target);
732                it->mXTranslate.setOffset(it->mXTranslate.getOffset()
733                        + (x - target) / it->mXTranslate.getScale());
734            }
735            const S xOffset = operation->getXOffset();
736            if (!std::isnan(xOffset)) {
737                const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
738                const S x = it->mXTranslate((T)frameCount);
739                VS_LOG("xOffset translation: %f", x);
740                const S target = xOffset; // offset
741                VS_LOG("xOffset target x offset: %f", target);
742                it->mXTranslate.setOffset(it->mXTranslate.getOffset()
743                        + (x - target) / it->mXTranslate.getScale());
744                it->setDelayXOffset(xOffset);
745            }
746            it->mOperation = operation; // replace the operation
747        } break;
748        }
749        return VolumeShaper::Status(id);
750    }
751
752    sp<VolumeShaper::State> getVolumeShaperState(int id) {
753        AutoMutex _l(mLock);
754        auto it = findId_l(id);
755        if (it == mVolumeShapers.end()) {
756            VS_LOG("cannot find state for id: %d", id);
757            return nullptr;
758        }
759        return it->getState();
760    }
761
762    // getVolume() is not const, as it updates internal state.
763    // Once called, any VolumeShapers not already started begin running.
764    std::pair<T /* volume */, bool /* active */> getVolume(int64_t trackFrameCount) {
765        AutoMutex _l(mLock);
766        mLastFrame = trackFrameCount;
767        T volume(1);
768        size_t activeCount = 0;
769        for (auto it = mVolumeShapers.begin(); it != mVolumeShapers.end();) {
770            std::pair<T, bool> shaperVolume =
771                    it->getVolume(trackFrameCount, mSampleRate);
772            volume *= shaperVolume.first;
773            activeCount += shaperVolume.second;
774            ++it;
775        }
776        mLastVolume = std::make_pair(volume, activeCount != 0);
777        return mLastVolume;
778    }
779
780    // Used by a client side VolumeHandler to ensure all the VolumeShapers
781    // indicate that they have been started.  Upon a change in audioserver
782    // output sink, this information is used for restoration of the server side
783    // VolumeHandler.
784    void setStarted() {
785        (void)getVolume(mLastFrame);  // getVolume() will start the individual VolumeShapers.
786    }
787
788    std::pair<T /* volume */, bool /* active */> getLastVolume() const {
789        AutoMutex _l(mLock);
790        return mLastVolume;
791    }
792
793    std::string toString() const {
794        AutoMutex _l(mLock);
795        std::stringstream ss;
796        ss << "mSampleRate: " << mSampleRate << std::endl;
797        ss << "mLastFrame: " << mLastFrame << std::endl;
798        for (const auto &shaper : mVolumeShapers) {
799            ss << shaper.toString().c_str();
800        }
801        return ss.str();
802    }
803
804    void forall(const std::function<VolumeShaper::Status (const VolumeShaper &)> &lambda) {
805        AutoMutex _l(mLock);
806        VS_LOG("forall: mVolumeShapers.size() %zu", mVolumeShapers.size());
807        for (const auto &shaper : mVolumeShapers) {
808            VolumeShaper::Status status = lambda(shaper);
809            VS_LOG("forall applying lambda on shaper (%p): %d", &shaper, (int)status);
810        }
811    }
812
813    void reset() {
814        AutoMutex _l(mLock);
815        mVolumeShapers.clear();
816        mLastFrame = 0;
817        // keep mVolumeShaperIdCounter as is.
818    }
819
820    // Sets the configuration id if necessary - This is based on the counter
821    // internal to the VolumeHandler.
822    void setIdIfNecessary(const sp<VolumeShaper::Configuration> &configuration) {
823        if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
824            const int id = configuration->getId();
825            if (id == -1) {
826                // Reassign to a unique id, skipping system ids.
827                AutoMutex _l(mLock);
828                while (true) {
829                    if (mVolumeShaperIdCounter == INT32_MAX) {
830                        mVolumeShaperIdCounter = VolumeShaper::kSystemIdMax;
831                    } else {
832                        ++mVolumeShaperIdCounter;
833                    }
834                    if (findId_l(mVolumeShaperIdCounter) != mVolumeShapers.end()) {
835                        continue; // collision with an existing id.
836                    }
837                    configuration->setId(mVolumeShaperIdCounter);
838                    ALOGD("setting id to %d", mVolumeShaperIdCounter);
839                    break;
840                }
841            }
842        }
843    }
844
845private:
846    std::list<VolumeShaper>::iterator findId_l(int32_t id) {
847        std::list<VolumeShaper>::iterator it = mVolumeShapers.begin();
848        for (; it != mVolumeShapers.end(); ++it) {
849            if (it->mConfiguration->getId() == id) {
850                break;
851            }
852        }
853        return it;
854    }
855
856    mutable Mutex mLock;
857    double mSampleRate; // in samples (frames) per second
858    int64_t mLastFrame; // logging purpose only, 0 on start
859    int32_t mVolumeShaperIdCounter; // a counter to return a unique volume shaper id.
860    std::pair<T /* volume */, bool /* active */> mLastVolume;
861    std::list<VolumeShaper> mVolumeShapers; // list provides stable iterators on erase
862}; // VolumeHandler
863
864} // namespace android
865
866#pragma pop_macro("LOG_TAG")
867
868#endif // ANDROID_VOLUME_SHAPER_H
869