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