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