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