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