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