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