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