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