VolumeShaper.h revision 39399b6b08b4e9fd7eae50e58e93b07216ad697f
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    bool isStarted() const {
532        return mStartFrame >= 0;
533    }
534
535    std::pair<T /* volume */, bool /* active */> getVolume(
536            int64_t trackFrameCount, double trackSampleRate) {
537        if ((getFlags() & VolumeShaper::Operation::FLAG_DELAY) != 0) {
538            VS_LOG("delayed VolumeShaper, ignoring");
539            mLastVolume = T(1);
540            mLastXOffset = 0.;
541            return std::make_pair(T(1), false);
542        }
543        const bool clockTime = (mConfiguration->getOptionFlags()
544                & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
545        const int64_t frameCount = clockTime ? getNowUs() : trackFrameCount;
546        const double sampleRate = clockTime ? 1000000 : trackSampleRate;
547
548        if (mStartFrame < 0) {
549            updatePosition(frameCount, sampleRate);
550            mStartFrame = frameCount;
551        }
552        VS_LOG("frameCount: %lld", (long long)frameCount);
553        S x = mXTranslate((T)frameCount);
554        VS_LOG("translation: %f", x);
555
556        // handle reversal of position
557        if (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) {
558            x = 1.f - x;
559            VS_LOG("reversing to %f", x);
560            if (x < mConfiguration->first().first) {
561                mLastXOffset = 1.f;
562                const T volume = mConfiguration->adjustVolume(
563                        mConfiguration->first().second);  // persist last value
564                VS_LOG("persisting volume %f", volume);
565                mLastVolume = volume;
566                return std::make_pair(volume, false);
567            }
568            if (x > mConfiguration->last().first) {
569                mLastXOffset = 0.f;
570                mLastVolume = 1.f;
571                return std::make_pair(T(1), true); // too early
572            }
573        } else {
574            if (x < mConfiguration->first().first) {
575                mLastXOffset = 0.f;
576                mLastVolume = 1.f;
577                return std::make_pair(T(1), true); // too early
578            }
579            if (x > mConfiguration->last().first) {
580                mLastXOffset = 1.f;
581                const T volume = mConfiguration->adjustVolume(
582                        mConfiguration->last().second);  // persist last value
583                VS_LOG("persisting volume %f", volume);
584                mLastVolume = volume;
585                return std::make_pair(volume, false);
586            }
587        }
588        mLastXOffset = x;
589        // x contains the location on the volume curve to use.
590        const T unscaledVolume = mConfiguration->findY(x);
591        const T volume = mConfiguration->adjustVolume(unscaledVolume); // handle log scale
592        VS_LOG("volume: %f  unscaled: %f", volume, unscaledVolume);
593        mLastVolume = volume;
594        return std::make_pair(volume, true);
595    }
596
597    std::string toString() const {
598        std::stringstream ss;
599        ss << "StartFrame: " << mStartFrame << std::endl;
600        ss << mXTranslate.toString().c_str();
601        if (mConfiguration.get() == nullptr) {
602            ss << "VolumeShaper::Configuration: nullptr" << std::endl;
603        } else {
604            ss << "VolumeShaper::Configuration:" << std::endl;
605            ss << mConfiguration->toString().c_str();
606        }
607        if (mOperation.get() == nullptr) {
608            ss << "VolumeShaper::Operation: nullptr" << std::endl;
609        } else {
610            ss << "VolumeShaper::Operation:" << std::endl;
611            ss << mOperation->toString().c_str();
612        }
613        return ss.str();
614    }
615
616    Translate<S> mXTranslate; // x axis translation from frames (in usec for clock time)
617    sp<VolumeShaper::Configuration> mConfiguration;
618    sp<VolumeShaper::Operation> mOperation;
619    int64_t mStartFrame; // starting frame, non-negative when started (in usec for clock time)
620    T mLastVolume;       // last computed interpolated volume (y-axis)
621    S mLastXOffset;      // last computed interpolated xOffset/time (x-axis)
622    S mDelayXOffset;     // delay xOffset on first volumeshaper start.
623}; // VolumeShaper
624
625// VolumeHandler combines the volume factors of multiple VolumeShapers and handles
626// multiple thread access by synchronizing all public methods.
627class VolumeHandler : public RefBase {
628public:
629    using S = float;
630    using T = float;
631
632    // A volume handler which just keeps track of active VolumeShapers does not need sampleRate.
633    VolumeHandler()
634        : VolumeHandler(0 /* sampleRate */) {
635    }
636
637    explicit VolumeHandler(uint32_t sampleRate)
638        : mSampleRate((double)sampleRate)
639        , mLastFrame(0)
640        , mVolumeShaperIdCounter(VolumeShaper::kSystemIdMax)
641        , mLastVolume(1.f, false) {
642    }
643
644    VolumeShaper::Status applyVolumeShaper(
645            const sp<VolumeShaper::Configuration> &configuration,
646            const sp<VolumeShaper::Operation> &operation) {
647        VS_LOG("applyVolumeShaper:configuration: %s", configuration->toString().c_str());
648        VS_LOG("applyVolumeShaper:operation: %s", operation->toString().c_str());
649        AutoMutex _l(mLock);
650        if (configuration == nullptr) {
651            ALOGE("null configuration");
652            return VolumeShaper::Status(BAD_VALUE);
653        }
654        if (operation == nullptr) {
655            ALOGE("null operation");
656            return VolumeShaper::Status(BAD_VALUE);
657        }
658        const int32_t id = configuration->getId();
659        if (id < 0) {
660            ALOGE("negative id: %d", id);
661            return VolumeShaper::Status(BAD_VALUE);
662        }
663        VS_LOG("applyVolumeShaper id: %d", id);
664
665        switch (configuration->getType()) {
666        case VolumeShaper::Configuration::TYPE_SCALE: {
667            const int replaceId = operation->getReplaceId();
668            if (replaceId >= 0) {
669                auto replaceIt = findId_l(replaceId);
670                if (replaceIt == mVolumeShapers.end()) {
671                    ALOGW("cannot find replace id: %d", replaceId);
672                } else {
673                    if ((replaceIt->getFlags() & VolumeShaper::Operation::FLAG_JOIN) != 0) {
674                        // For join, we scale the start volume of the current configuration
675                        // to match the last-used volume of the replacing VolumeShaper.
676                        auto state = replaceIt->getState();
677                        if (state->getXOffset() >= 0) { // valid
678                            const T volume = state->getVolume();
679                            ALOGD("join: scaling start volume to %f", volume);
680                            configuration->scaleToStartVolume(volume);
681                        }
682                    }
683                    (void)mVolumeShapers.erase(replaceIt);
684                }
685                operation->setReplaceId(-1);
686            }
687            // check if we have another of the same id.
688            auto oldIt = findId_l(id);
689            if (oldIt != mVolumeShapers.end()) {
690                if ((operation->getFlags()
691                        & VolumeShaper::Operation::FLAG_CREATE_IF_NECESSARY) != 0) {
692                    // TODO: move the case to a separate function.
693                    goto HANDLE_TYPE_ID; // no need to create, take over existing id.
694                }
695                ALOGW("duplicate id, removing old %d", id);
696                (void)mVolumeShapers.erase(oldIt);
697            }
698            // create new VolumeShaper
699            mVolumeShapers.emplace_back(configuration, operation);
700        }
701        // fall through to handle the operation
702        HANDLE_TYPE_ID:
703        case VolumeShaper::Configuration::TYPE_ID: {
704            VS_LOG("trying to find id: %d", id);
705            auto it = findId_l(id);
706            if (it == mVolumeShapers.end()) {
707                VS_LOG("couldn't find id: %d", id);
708                return VolumeShaper::Status(INVALID_OPERATION);
709            }
710            if ((it->getFlags() & VolumeShaper::Operation::FLAG_TERMINATE) != 0) {
711                VS_LOG("terminate id: %d", id);
712                mVolumeShapers.erase(it);
713                break;
714            }
715            const bool clockTime = (it->mConfiguration->getOptionFlags()
716                    & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
717            if ((it->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) !=
718                    (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE)) {
719                const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
720                const S x = it->mXTranslate((T)frameCount);
721                VS_LOG("reverse translation: %f", x);
722                // reflect position
723                S target = 1.f - x;
724                if (target < it->mConfiguration->first().first) {
725                    VS_LOG("clamp to start - begin immediately");
726                    target = 0.;
727                }
728                VS_LOG("target reverse: %f", target);
729                it->mXTranslate.setOffset(it->mXTranslate.getOffset()
730                        + (x - target) / it->mXTranslate.getScale());
731            }
732            const S xOffset = operation->getXOffset();
733            if (!std::isnan(xOffset)) {
734                const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
735                const S x = it->mXTranslate((T)frameCount);
736                VS_LOG("xOffset translation: %f", x);
737                const S target = xOffset; // offset
738                VS_LOG("xOffset target x offset: %f", target);
739                it->mXTranslate.setOffset(it->mXTranslate.getOffset()
740                        + (x - target) / it->mXTranslate.getScale());
741                it->setDelayXOffset(xOffset);
742            }
743            it->mOperation = operation; // replace the operation
744        } break;
745        }
746        return VolumeShaper::Status(id);
747    }
748
749    sp<VolumeShaper::State> getVolumeShaperState(int id) {
750        AutoMutex _l(mLock);
751        auto it = findId_l(id);
752        if (it == mVolumeShapers.end()) {
753            VS_LOG("cannot find state for id: %d", id);
754            return nullptr;
755        }
756        return it->getState();
757    }
758
759    // getVolume() is not const, as it updates internal state.
760    // Once called, any VolumeShapers not already started begin running.
761    std::pair<T /* volume */, bool /* active */> getVolume(int64_t trackFrameCount) {
762        AutoMutex _l(mLock);
763        mLastFrame = trackFrameCount;
764        T volume(1);
765        size_t activeCount = 0;
766        for (auto it = mVolumeShapers.begin(); it != mVolumeShapers.end();) {
767            std::pair<T, bool> shaperVolume =
768                    it->getVolume(trackFrameCount, mSampleRate);
769            volume *= shaperVolume.first;
770            activeCount += shaperVolume.second;
771            ++it;
772        }
773        mLastVolume = std::make_pair(volume, activeCount != 0);
774        return mLastVolume;
775    }
776
777    // Used by a client side VolumeHandler to ensure all the VolumeShapers
778    // indicate that they have been started.  Upon a change in audioserver
779    // output sink, this information is used for restoration of the server side
780    // VolumeHandler.
781    void setStarted() {
782        (void)getVolume(mLastFrame);  // getVolume() will start the individual VolumeShapers.
783    }
784
785    std::pair<T /* volume */, bool /* active */> getLastVolume() const {
786        AutoMutex _l(mLock);
787        return mLastVolume;
788    }
789
790    std::string toString() const {
791        AutoMutex _l(mLock);
792        std::stringstream ss;
793        ss << "mSampleRate: " << mSampleRate << std::endl;
794        ss << "mLastFrame: " << mLastFrame << std::endl;
795        for (const auto &shaper : mVolumeShapers) {
796            ss << shaper.toString().c_str();
797        }
798        return ss.str();
799    }
800
801    void forall(const std::function<VolumeShaper::Status (const VolumeShaper &)> &lambda) {
802        AutoMutex _l(mLock);
803        VS_LOG("forall: mVolumeShapers.size() %zu", mVolumeShapers.size());
804        for (const auto &shaper : mVolumeShapers) {
805            VolumeShaper::Status status = lambda(shaper);
806            VS_LOG("forall applying lambda on shaper (%p): %d", &shaper, (int)status);
807        }
808    }
809
810    void reset() {
811        AutoMutex _l(mLock);
812        mVolumeShapers.clear();
813        mLastFrame = 0;
814        // keep mVolumeShaperIdCounter as is.
815    }
816
817    // Sets the configuration id if necessary - This is based on the counter
818    // internal to the VolumeHandler.
819    void setIdIfNecessary(const sp<VolumeShaper::Configuration> &configuration) {
820        if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
821            const int id = configuration->getId();
822            if (id == -1) {
823                // Reassign to a unique id, skipping system ids.
824                AutoMutex _l(mLock);
825                while (true) {
826                    if (mVolumeShaperIdCounter == INT32_MAX) {
827                        mVolumeShaperIdCounter = VolumeShaper::kSystemIdMax;
828                    } else {
829                        ++mVolumeShaperIdCounter;
830                    }
831                    if (findId_l(mVolumeShaperIdCounter) != mVolumeShapers.end()) {
832                        continue; // collision with an existing id.
833                    }
834                    configuration->setId(mVolumeShaperIdCounter);
835                    ALOGD("setting id to %d", mVolumeShaperIdCounter);
836                    break;
837                }
838            }
839        }
840    }
841
842private:
843    std::list<VolumeShaper>::iterator findId_l(int32_t id) {
844        std::list<VolumeShaper>::iterator it = mVolumeShapers.begin();
845        for (; it != mVolumeShapers.end(); ++it) {
846            if (it->mConfiguration->getId() == id) {
847                break;
848            }
849        }
850        return it;
851    }
852
853    mutable Mutex mLock;
854    double mSampleRate; // in samples (frames) per second
855    int64_t mLastFrame; // logging purpose only, 0 on start
856    int32_t mVolumeShaperIdCounter; // a counter to return a unique volume shaper id.
857    std::pair<T /* volume */, bool /* active */> mLastVolume;
858    std::list<VolumeShaper> mVolumeShapers; // list provides stable iterators on erase
859}; // VolumeHandler
860
861} // namespace android
862
863#pragma pop_macro("LOG_TAG")
864
865#endif // ANDROID_VOLUME_SHAPER_H
866