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