VolumeShaper.h revision 7d712bb0916182cb73f05ec9144b39314ddd5eab
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 41namespace 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 52class VolumeShaper { 53public: 54 using S = float; 55 using T = float; 56 57 static const int kSystemIdMax = 16; 58 59 // VolumeShaper::Status is equivalent to status_t if negative 60 // but if non-negative represents the id operated on. 61 // It must be expressible as an int32_t for binder purposes. 62 using Status = status_t; 63 64 class Configuration : public Interpolator<S, T>, public RefBase { 65 public: 66 /* VolumeShaper.Configuration derives from the Interpolator class and adds 67 * parameters relating to the volume shape. 68 */ 69 70 // TODO document as per VolumeShaper.java flags. 71 72 // must match with VolumeShaper.java in frameworks/base 73 enum Type : int32_t { 74 TYPE_ID, 75 TYPE_SCALE, 76 }; 77 78 // must match with VolumeShaper.java in frameworks/base 79 enum OptionFlag : int32_t { 80 OPTION_FLAG_NONE = 0, 81 OPTION_FLAG_VOLUME_IN_DBFS = (1 << 0), 82 OPTION_FLAG_CLOCK_TIME = (1 << 1), 83 84 OPTION_FLAG_ALL = (OPTION_FLAG_VOLUME_IN_DBFS | OPTION_FLAG_CLOCK_TIME), 85 }; 86 87 // bring to derived class; must match with VolumeShaper.java in frameworks/base 88 using InterpolatorType = Interpolator<S, T>::InterpolatorType; 89 90 Configuration() 91 : Interpolator<S, T>() 92 , mType(TYPE_SCALE) 93 , mOptionFlags(OPTION_FLAG_NONE) 94 , mDurationMs(1000.) 95 , mId(-1) { 96 } 97 98 explicit Configuration(const Configuration &configuration) 99 : Interpolator<S, T>(*static_cast<const Interpolator<S, T> *>(&configuration)) 100 , mType(configuration.mType) 101 , mOptionFlags(configuration.mOptionFlags) 102 , mDurationMs(configuration.mDurationMs) 103 , mId(configuration.mId) { 104 } 105 106 Type getType() const { 107 return mType; 108 } 109 110 status_t setType(Type type) { 111 switch (type) { 112 case TYPE_ID: 113 case TYPE_SCALE: 114 mType = type; 115 return NO_ERROR; 116 default: 117 ALOGE("invalid Type: %d", type); 118 return BAD_VALUE; 119 } 120 } 121 122 OptionFlag getOptionFlags() const { 123 return mOptionFlags; 124 } 125 126 status_t setOptionFlags(OptionFlag optionFlags) { 127 if ((optionFlags & ~OPTION_FLAG_ALL) != 0) { 128 ALOGE("optionFlags has invalid bits: %#x", optionFlags); 129 return BAD_VALUE; 130 } 131 mOptionFlags = optionFlags; 132 return NO_ERROR; 133 } 134 135 double getDurationMs() const { 136 return mDurationMs; 137 } 138 139 void setDurationMs(double durationMs) { 140 mDurationMs = durationMs; 141 } 142 143 int32_t getId() const { 144 return mId; 145 } 146 147 void setId(int32_t id) { 148 mId = id; 149 } 150 151 T adjustVolume(T volume) const { 152 if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) { 153 const T out = powf(10.f, volume / 10.); 154 VS_LOG("in: %f out: %f", volume, out); 155 volume = out; 156 } 157 // clamp 158 if (volume < 0.f) { 159 volume = 0.f; 160 } else if (volume > 1.f) { 161 volume = 1.f; 162 } 163 return volume; 164 } 165 166 status_t checkCurve() { 167 if (mType == TYPE_ID) return NO_ERROR; 168 if (this->size() < 2) { 169 ALOGE("curve must have at least 2 points"); 170 return BAD_VALUE; 171 } 172 if (first().first != 0.f || last().first != 1.f) { 173 ALOGE("curve must start at 0.f and end at 1.f"); 174 return BAD_VALUE; 175 } 176 if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) { 177 for (const auto &pt : *this) { 178 if (!(pt.second <= 0.f) /* handle nan */) { 179 ALOGE("positive volume dbFS"); 180 return BAD_VALUE; 181 } 182 } 183 } else { 184 for (const auto &pt : *this) { 185 if (!(pt.second >= 0.f) || !(pt.second <= 1.f) /* handle nan */) { 186 ALOGE("volume < 0.f or > 1.f"); 187 return BAD_VALUE; 188 } 189 } 190 } 191 return NO_ERROR; 192 } 193 194 void clampVolume() { 195 if ((mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0) { 196 for (auto it = this->begin(); it != this->end(); ++it) { 197 if (!(it->second <= 0.f) /* handle nan */) { 198 it->second = 0.f; 199 } 200 } 201 } else { 202 for (auto it = this->begin(); it != this->end(); ++it) { 203 if (!(it->second >= 0.f) /* handle nan */) { 204 it->second = 0.f; 205 } else if (!(it->second <= 1.f)) { 206 it->second = 1.f; 207 } 208 } 209 } 210 } 211 212 /* scaleToStartVolume() is used to set the start volume of a 213 * new VolumeShaper curve, when replacing one VolumeShaper 214 * with another using the "join" (volume match) option. 215 * 216 * It works best for monotonic volume ramps or ducks. 217 */ 218 void scaleToStartVolume(T volume) { 219 if (this->size() < 2) { 220 return; 221 } 222 const T startVolume = first().second; 223 const T endVolume = last().second; 224 if (endVolume == startVolume) { 225 // match with linear ramp 226 const T offset = volume - startVolume; 227 for (auto it = this->begin(); it != this->end(); ++it) { 228 it->second = it->second + offset * (1.f - it->first); 229 } 230 } else { 231 const T scale = (volume - endVolume) / (startVolume - endVolume); 232 for (auto it = this->begin(); it != this->end(); ++it) { 233 it->second = scale * (it->second - endVolume) + endVolume; 234 } 235 } 236 clampVolume(); 237 } 238 239 // The parcel layout must match VolumeShaper.java 240 status_t writeToParcel(Parcel *parcel) const { 241 if (parcel == nullptr) return BAD_VALUE; 242 return parcel->writeInt32((int32_t)mType) 243 ?: parcel->writeInt32(mId) 244 ?: mType == TYPE_ID 245 ? NO_ERROR 246 : parcel->writeInt32((int32_t)mOptionFlags) 247 ?: parcel->writeDouble(mDurationMs) 248 ?: Interpolator<S, T>::writeToParcel(parcel); 249 } 250 251 status_t readFromParcel(const Parcel &parcel) { 252 int32_t type, optionFlags; 253 return parcel.readInt32(&type) 254 ?: setType((Type)type) 255 ?: parcel.readInt32(&mId) 256 ?: mType == TYPE_ID 257 ? NO_ERROR 258 : parcel.readInt32(&optionFlags) 259 ?: setOptionFlags((OptionFlag)optionFlags) 260 ?: parcel.readDouble(&mDurationMs) 261 ?: Interpolator<S, T>::readFromParcel(parcel) 262 ?: checkCurve(); 263 } 264 265 std::string toString() const { 266 std::stringstream ss; 267 ss << "mType: " << mType << std::endl; 268 ss << "mId: " << mId << std::endl; 269 if (mType != TYPE_ID) { 270 ss << "mOptionFlags: " << mOptionFlags << std::endl; 271 ss << "mDurationMs: " << mDurationMs << std::endl; 272 ss << Interpolator<S, T>::toString().c_str(); 273 } 274 return ss.str(); 275 } 276 277 private: 278 Type mType; 279 int32_t mId; 280 OptionFlag mOptionFlags; 281 double mDurationMs; 282 }; // Configuration 283 284 // must match with VolumeShaper.java in frameworks/base 285 // TODO document per VolumeShaper.java flags. 286 class Operation : public RefBase { 287 public: 288 enum Flag : int32_t { 289 FLAG_NONE = 0, 290 FLAG_REVERSE = (1 << 0), 291 FLAG_TERMINATE = (1 << 1), 292 FLAG_JOIN = (1 << 2), 293 FLAG_DELAY = (1 << 3), 294 FLAG_CREATE_IF_NECESSARY = (1 << 4), 295 296 FLAG_ALL = (FLAG_REVERSE | FLAG_TERMINATE | FLAG_JOIN | FLAG_DELAY 297 | FLAG_CREATE_IF_NECESSARY), 298 }; 299 300 Operation() 301 : Operation(FLAG_NONE, -1 /* replaceId */) { 302 } 303 304 Operation(Flag flags, int replaceId) 305 : Operation(flags, replaceId, std::numeric_limits<S>::quiet_NaN() /* xOffset */) { 306 } 307 308 explicit Operation(const Operation &operation) 309 : Operation(operation.mFlags, operation.mReplaceId, operation.mXOffset) { 310 } 311 312 explicit Operation(const sp<Operation> &operation) 313 : Operation(*operation.get()) { 314 } 315 316 Operation(Flag flags, int replaceId, S xOffset) 317 : mFlags(flags) 318 , mReplaceId(replaceId) 319 , mXOffset(xOffset) { 320 } 321 322 int32_t getReplaceId() const { 323 return mReplaceId; 324 } 325 326 void setReplaceId(int32_t replaceId) { 327 mReplaceId = replaceId; 328 } 329 330 S getXOffset() const { 331 return mXOffset; 332 } 333 334 void setXOffset(S xOffset) { 335 mXOffset = xOffset; 336 } 337 338 Flag getFlags() const { 339 return mFlags; 340 } 341 342 status_t setFlags(Flag flags) { 343 if ((flags & ~FLAG_ALL) != 0) { 344 ALOGE("flags has invalid bits: %#x", flags); 345 return BAD_VALUE; 346 } 347 mFlags = flags; 348 return NO_ERROR; 349 } 350 351 status_t writeToParcel(Parcel *parcel) const { 352 if (parcel == nullptr) return BAD_VALUE; 353 return parcel->writeInt32((int32_t)mFlags) 354 ?: parcel->writeInt32(mReplaceId) 355 ?: parcel->writeFloat(mXOffset); 356 } 357 358 status_t readFromParcel(const Parcel &parcel) { 359 int32_t flags; 360 return parcel.readInt32(&flags) 361 ?: parcel.readInt32(&mReplaceId) 362 ?: parcel.readFloat(&mXOffset) 363 ?: setFlags((Flag)flags); 364 } 365 366 std::string toString() const { 367 std::stringstream ss; 368 ss << "mFlags: " << mFlags << std::endl; 369 ss << "mReplaceId: " << mReplaceId << std::endl; 370 ss << "mXOffset: " << mXOffset << std::endl; 371 return ss.str(); 372 } 373 374 private: 375 Flag mFlags; 376 int32_t mReplaceId; 377 S mXOffset; 378 }; // Operation 379 380 // must match with VolumeShaper.java in frameworks/base 381 class State : public RefBase { 382 public: 383 State(T volume, S xOffset) 384 : mVolume(volume) 385 , mXOffset(xOffset) { 386 } 387 388 State() 389 : State(-1.f, -1.f) { } 390 391 T getVolume() const { 392 return mVolume; 393 } 394 395 void setVolume(T volume) { 396 mVolume = volume; 397 } 398 399 S getXOffset() const { 400 return mXOffset; 401 } 402 403 void setXOffset(S xOffset) { 404 mXOffset = xOffset; 405 } 406 407 status_t writeToParcel(Parcel *parcel) const { 408 if (parcel == nullptr) return BAD_VALUE; 409 return parcel->writeFloat(mVolume) 410 ?: parcel->writeFloat(mXOffset); 411 } 412 413 status_t readFromParcel(const Parcel &parcel) { 414 return parcel.readFloat(&mVolume) 415 ?: parcel.readFloat(&mXOffset); 416 } 417 418 std::string toString() const { 419 std::stringstream ss; 420 ss << "mVolume: " << mVolume << std::endl; 421 ss << "mXOffset: " << mXOffset << std::endl; 422 return ss.str(); 423 } 424 425 private: 426 T mVolume; 427 S mXOffset; 428 }; // State 429 430 template <typename R> 431 class Translate { 432 public: 433 Translate() 434 : mOffset(0) 435 , mScale(1) { 436 } 437 438 R getOffset() const { 439 return mOffset; 440 } 441 442 void setOffset(R offset) { 443 mOffset = offset; 444 } 445 446 R getScale() const { 447 return mScale; 448 } 449 450 void setScale(R scale) { 451 mScale = scale; 452 } 453 454 R operator()(R in) const { 455 return mScale * (in - mOffset); 456 } 457 458 std::string toString() const { 459 std::stringstream ss; 460 ss << "mOffset: " << mOffset << std::endl; 461 ss << "mScale: " << mScale << std::endl; 462 return ss.str(); 463 } 464 465 private: 466 R mOffset; 467 R mScale; 468 }; // Translate 469 470 static int64_t convertTimespecToUs(const struct timespec &tv) 471 { 472 return tv.tv_sec * 1000000ll + tv.tv_nsec / 1000; 473 } 474 475 // current monotonic time in microseconds. 476 static int64_t getNowUs() 477 { 478 struct timespec tv; 479 if (clock_gettime(CLOCK_MONOTONIC, &tv) != 0) { 480 return 0; // system is really sick, just return 0 for consistency. 481 } 482 return convertTimespecToUs(tv); 483 } 484 485 // TODO: Since we pass configuration and operation as shared pointers 486 // there is a potential risk that the caller may modify these after 487 // delivery. Currently, we don't require copies made here. 488 VolumeShaper( 489 const sp<VolumeShaper::Configuration> &configuration, 490 const sp<VolumeShaper::Operation> &operation) 491 : mConfiguration(configuration) // we do not make a copy 492 , mOperation(operation) // ditto 493 , mStartFrame(-1) 494 , mLastVolume(T(1)) 495 , mLastXOffset(0.f) 496 , mDelayXOffset(std::numeric_limits<S>::quiet_NaN()) { 497 if (configuration.get() != nullptr 498 && (getFlags() & VolumeShaper::Operation::FLAG_DELAY) == 0) { 499 mLastVolume = configuration->first().second; 500 } 501 } 502 503 void updatePosition(int64_t startFrame, double sampleRate) { 504 double scale = (mConfiguration->last().first - mConfiguration->first().first) 505 / (mConfiguration->getDurationMs() * 0.001 * sampleRate); 506 const double minScale = 1. / INT64_MAX; 507 scale = std::max(scale, minScale); 508 const S xOffset = std::isnan(mDelayXOffset) ? mConfiguration->first().first : mDelayXOffset; 509 VS_LOG("update position: scale %lf frameCount:%lld, sampleRate:%lf, xOffset:%f", 510 scale, (long long) startFrame, sampleRate, xOffset); 511 512 mXTranslate.setOffset(startFrame - xOffset / scale); 513 mXTranslate.setScale(scale); 514 VS_LOG("translate: %s", mXTranslate.toString().c_str()); 515 } 516 517 // We allow a null operation here, though VolumeHandler always provides one. 518 VolumeShaper::Operation::Flag getFlags() const { 519 return mOperation == nullptr 520 ? VolumeShaper::Operation::FLAG_NONE :mOperation->getFlags(); 521 } 522 523 sp<VolumeShaper::State> getState() const { 524 return new VolumeShaper::State(mLastVolume, mLastXOffset); 525 } 526 527 void setDelayXOffset(S xOffset) { 528 mDelayXOffset = xOffset; 529 } 530 531 std::pair<T /* volume */, bool /* active */> getVolume( 532 int64_t trackFrameCount, double trackSampleRate) { 533 if ((getFlags() & VolumeShaper::Operation::FLAG_DELAY) != 0) { 534 VS_LOG("delayed VolumeShaper, ignoring"); 535 mLastVolume = T(1); 536 mLastXOffset = 0.; 537 return std::make_pair(T(1), false); 538 } 539 const bool clockTime = (mConfiguration->getOptionFlags() 540 & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0; 541 const int64_t frameCount = clockTime ? getNowUs() : trackFrameCount; 542 const double sampleRate = clockTime ? 1000000 : trackSampleRate; 543 544 if (mStartFrame < 0) { 545 updatePosition(frameCount, sampleRate); 546 mStartFrame = frameCount; 547 } 548 VS_LOG("frameCount: %lld", (long long)frameCount); 549 S x = mXTranslate((T)frameCount); 550 VS_LOG("translation: %f", x); 551 552 // handle reversal of position 553 if (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) { 554 x = 1.f - x; 555 VS_LOG("reversing to %f", x); 556 if (x < mConfiguration->first().first) { 557 mLastXOffset = 1.f; 558 const T volume = mConfiguration->adjustVolume( 559 mConfiguration->first().second); // persist last value 560 VS_LOG("persisting volume %f", volume); 561 mLastVolume = volume; 562 return std::make_pair(volume, false); 563 } 564 if (x > mConfiguration->last().first) { 565 mLastXOffset = 0.f; 566 mLastVolume = 1.f; 567 return std::make_pair(T(1), true); // too early 568 } 569 } else { 570 if (x < mConfiguration->first().first) { 571 mLastXOffset = 0.f; 572 mLastVolume = 1.f; 573 return std::make_pair(T(1), true); // too early 574 } 575 if (x > mConfiguration->last().first) { 576 mLastXOffset = 1.f; 577 const T volume = mConfiguration->adjustVolume( 578 mConfiguration->last().second); // persist last value 579 VS_LOG("persisting volume %f", volume); 580 mLastVolume = volume; 581 return std::make_pair(volume, false); 582 } 583 } 584 mLastXOffset = x; 585 // x contains the location on the volume curve to use. 586 const T unscaledVolume = mConfiguration->findY(x); 587 const T volume = mConfiguration->adjustVolume(unscaledVolume); // handle log scale 588 VS_LOG("volume: %f unscaled: %f", volume, unscaledVolume); 589 mLastVolume = volume; 590 return std::make_pair(volume, true); 591 } 592 593 std::string toString() const { 594 std::stringstream ss; 595 ss << "StartFrame: " << mStartFrame << std::endl; 596 ss << mXTranslate.toString().c_str(); 597 if (mConfiguration.get() == nullptr) { 598 ss << "VolumeShaper::Configuration: nullptr" << std::endl; 599 } else { 600 ss << "VolumeShaper::Configuration:" << std::endl; 601 ss << mConfiguration->toString().c_str(); 602 } 603 if (mOperation.get() == nullptr) { 604 ss << "VolumeShaper::Operation: nullptr" << std::endl; 605 } else { 606 ss << "VolumeShaper::Operation:" << std::endl; 607 ss << mOperation->toString().c_str(); 608 } 609 return ss.str(); 610 } 611 612 Translate<S> mXTranslate; // x axis translation from frames (in usec for clock time) 613 sp<VolumeShaper::Configuration> mConfiguration; 614 sp<VolumeShaper::Operation> mOperation; 615 int64_t mStartFrame; // starting frame, non-negative when started (in usec for clock time) 616 T mLastVolume; // last computed interpolated volume (y-axis) 617 S mLastXOffset; // last computed interpolated xOffset/time (x-axis) 618 S mDelayXOffset; // delay xOffset on first volumeshaper start. 619}; // VolumeShaper 620 621// VolumeHandler combines the volume factors of multiple VolumeShapers and handles 622// multiple thread access by synchronizing all public methods. 623class VolumeHandler : public RefBase { 624public: 625 using S = float; 626 using T = float; 627 628 // A volume handler which just keeps track of active VolumeShapers does not need sampleRate. 629 VolumeHandler() 630 : VolumeHandler(0 /* sampleRate */) { 631 } 632 633 explicit VolumeHandler(uint32_t sampleRate) 634 : mSampleRate((double)sampleRate) 635 , mLastFrame(0) 636 , mVolumeShaperIdCounter(VolumeShaper::kSystemIdMax) 637 , mLastVolume(1.f, false) { 638 } 639 640 VolumeShaper::Status applyVolumeShaper( 641 const sp<VolumeShaper::Configuration> &configuration, 642 const sp<VolumeShaper::Operation> &operation) { 643 VS_LOG("applyVolumeShaper:configuration: %s", configuration->toString().c_str()); 644 VS_LOG("applyVolumeShaper:operation: %s", operation->toString().c_str()); 645 AutoMutex _l(mLock); 646 if (configuration == nullptr) { 647 ALOGE("null configuration"); 648 return VolumeShaper::Status(BAD_VALUE); 649 } 650 if (operation == nullptr) { 651 ALOGE("null operation"); 652 return VolumeShaper::Status(BAD_VALUE); 653 } 654 const int32_t id = configuration->getId(); 655 if (id < 0) { 656 ALOGE("negative id: %d", id); 657 return VolumeShaper::Status(BAD_VALUE); 658 } 659 VS_LOG("applyVolumeShaper id: %d", id); 660 661 switch (configuration->getType()) { 662 case VolumeShaper::Configuration::TYPE_SCALE: { 663 const int replaceId = operation->getReplaceId(); 664 if (replaceId >= 0) { 665 auto replaceIt = findId_l(replaceId); 666 if (replaceIt == mVolumeShapers.end()) { 667 ALOGW("cannot find replace id: %d", replaceId); 668 } else { 669 if ((replaceIt->getFlags() & VolumeShaper::Operation::FLAG_JOIN) != 0) { 670 // For join, we scale the start volume of the current configuration 671 // to match the last-used volume of the replacing VolumeShaper. 672 auto state = replaceIt->getState(); 673 if (state->getXOffset() >= 0) { // valid 674 const T volume = state->getVolume(); 675 ALOGD("join: scaling start volume to %f", volume); 676 configuration->scaleToStartVolume(volume); 677 } 678 } 679 (void)mVolumeShapers.erase(replaceIt); 680 } 681 operation->setReplaceId(-1); 682 } 683 // check if we have another of the same id. 684 auto oldIt = findId_l(id); 685 if (oldIt != mVolumeShapers.end()) { 686 if ((operation->getFlags() 687 & VolumeShaper::Operation::FLAG_CREATE_IF_NECESSARY) != 0) { 688 // TODO: move the case to a separate function. 689 goto HANDLE_TYPE_ID; // no need to create, take over existing id. 690 } 691 ALOGW("duplicate id, removing old %d", id); 692 (void)mVolumeShapers.erase(oldIt); 693 } 694 // create new VolumeShaper 695 mVolumeShapers.emplace_back(configuration, operation); 696 } 697 // fall through to handle the operation 698 HANDLE_TYPE_ID: 699 case VolumeShaper::Configuration::TYPE_ID: { 700 VS_LOG("trying to find id: %d", id); 701 auto it = findId_l(id); 702 if (it == mVolumeShapers.end()) { 703 VS_LOG("couldn't find id: %d", id); 704 return VolumeShaper::Status(INVALID_OPERATION); 705 } 706 if ((it->getFlags() & VolumeShaper::Operation::FLAG_TERMINATE) != 0) { 707 VS_LOG("terminate id: %d", id); 708 mVolumeShapers.erase(it); 709 break; 710 } 711 const bool clockTime = (it->mConfiguration->getOptionFlags() 712 & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0; 713 if ((it->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) != 714 (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE)) { 715 const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame; 716 const S x = it->mXTranslate((T)frameCount); 717 VS_LOG("reverse translation: %f", x); 718 // reflect position 719 S target = 1.f - x; 720 if (target < it->mConfiguration->first().first) { 721 VS_LOG("clamp to start - begin immediately"); 722 target = 0.; 723 } 724 VS_LOG("target reverse: %f", target); 725 it->mXTranslate.setOffset(it->mXTranslate.getOffset() 726 + (x - target) / it->mXTranslate.getScale()); 727 } 728 const S xOffset = operation->getXOffset(); 729 if (!std::isnan(xOffset)) { 730 const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame; 731 const S x = it->mXTranslate((T)frameCount); 732 VS_LOG("xOffset translation: %f", x); 733 const S target = xOffset; // offset 734 VS_LOG("xOffset target x offset: %f", target); 735 it->mXTranslate.setOffset(it->mXTranslate.getOffset() 736 + (x - target) / it->mXTranslate.getScale()); 737 it->setDelayXOffset(xOffset); 738 } 739 it->mOperation = operation; // replace the operation 740 } break; 741 } 742 return VolumeShaper::Status(id); 743 } 744 745 sp<VolumeShaper::State> getVolumeShaperState(int id) { 746 AutoMutex _l(mLock); 747 auto it = findId_l(id); 748 if (it == mVolumeShapers.end()) { 749 VS_LOG("cannot find state for id: %d", id); 750 return nullptr; 751 } 752 return it->getState(); 753 } 754 755 std::pair<T /* volume */, bool /* active */> getVolume(int64_t trackFrameCount) { 756 AutoMutex _l(mLock); 757 mLastFrame = trackFrameCount; 758 T volume(1); 759 size_t activeCount = 0; 760 for (auto it = mVolumeShapers.begin(); it != mVolumeShapers.end();) { 761 std::pair<T, bool> shaperVolume = 762 it->getVolume(trackFrameCount, mSampleRate); 763 volume *= shaperVolume.first; 764 activeCount += shaperVolume.second; 765 ++it; 766 } 767 mLastVolume = std::make_pair(volume, activeCount != 0); 768 return mLastVolume; 769 } 770 771 std::pair<T /* volume */, bool /* active */> getLastVolume() const { 772 AutoMutex _l(mLock); 773 return mLastVolume; 774 } 775 776 std::string toString() const { 777 AutoMutex _l(mLock); 778 std::stringstream ss; 779 ss << "mSampleRate: " << mSampleRate << std::endl; 780 ss << "mLastFrame: " << mLastFrame << std::endl; 781 for (const auto &shaper : mVolumeShapers) { 782 ss << shaper.toString().c_str(); 783 } 784 return ss.str(); 785 } 786 787 void forall(const std::function<VolumeShaper::Status ( 788 const sp<VolumeShaper::Configuration> &configuration, 789 const sp<VolumeShaper::Operation> &operation)> &lambda) { 790 AutoMutex _l(mLock); 791 VS_LOG("forall: mVolumeShapers.size() %zu", mVolumeShapers.size()); 792 for (const auto &shaper : mVolumeShapers) { 793 VS_LOG("forall applying lambda"); 794 (void)lambda(shaper.mConfiguration, shaper.mOperation); 795 } 796 } 797 798 void reset() { 799 AutoMutex _l(mLock); 800 mVolumeShapers.clear(); 801 mLastFrame = 0; 802 // keep mVolumeShaperIdCounter as is. 803 } 804 805 // Sets the configuration id if necessary - This is based on the counter 806 // internal to the VolumeHandler. 807 void setIdIfNecessary(const sp<VolumeShaper::Configuration> &configuration) { 808 if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) { 809 const int id = configuration->getId(); 810 if (id == -1) { 811 // Reassign to a unique id, skipping system ids. 812 AutoMutex _l(mLock); 813 while (true) { 814 if (mVolumeShaperIdCounter == INT32_MAX) { 815 mVolumeShaperIdCounter = VolumeShaper::kSystemIdMax; 816 } else { 817 ++mVolumeShaperIdCounter; 818 } 819 if (findId_l(mVolumeShaperIdCounter) != mVolumeShapers.end()) { 820 continue; // collision with an existing id. 821 } 822 configuration->setId(mVolumeShaperIdCounter); 823 ALOGD("setting id to %d", mVolumeShaperIdCounter); 824 break; 825 } 826 } 827 } 828 } 829 830private: 831 std::list<VolumeShaper>::iterator findId_l(int32_t id) { 832 std::list<VolumeShaper>::iterator it = mVolumeShapers.begin(); 833 for (; it != mVolumeShapers.end(); ++it) { 834 if (it->mConfiguration->getId() == id) { 835 break; 836 } 837 } 838 return it; 839 } 840 841 mutable Mutex mLock; 842 double mSampleRate; // in samples (frames) per second 843 int64_t mLastFrame; // logging purpose only, 0 on start 844 int32_t mVolumeShaperIdCounter; // a counter to return a unique volume shaper id. 845 std::pair<T /* volume */, bool /* active */> mLastVolume; 846 std::list<VolumeShaper> mVolumeShapers; // list provides stable iterators on erase 847}; // VolumeHandler 848 849} // namespace android 850 851#pragma pop_macro("LOG_TAG") 852 853#endif // ANDROID_VOLUME_SHAPER_H 854