blob: 4f6cbdc6af352cce9706f94de8bdd12751da07a9 [file] [log] [blame]
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001/*
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
Andy Hung4ef88d72017-02-21 19:47:53 -080020#include <cmath>
Andy Hung9fc8b5c2017-01-24 13:36:48 -080021#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
Andy Hung10cbff12017-02-21 17:30:14 -080098 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
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800106 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),
Andy Hung4ef88d72017-02-21 19:47:53 -0800293 FLAG_CREATE_IF_NECESSARY = (1 << 4),
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800294
Andy Hung4ef88d72017-02-21 19:47:53 -0800295 FLAG_ALL = (FLAG_REVERSE | FLAG_TERMINATE | FLAG_JOIN | FLAG_DELAY
296 | FLAG_CREATE_IF_NECESSARY),
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800297 };
298
299 Operation()
Andy Hung4ef88d72017-02-21 19:47:53 -0800300 : Operation(FLAG_NONE, -1 /* replaceId */) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800301 }
302
303 explicit Operation(Flag flags, int replaceId)
Andy Hung4ef88d72017-02-21 19:47:53 -0800304 : 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)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800312 : mFlags(flags)
Andy Hung4ef88d72017-02-21 19:47:53 -0800313 , mReplaceId(replaceId)
314 , mXOffset(xOffset) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800315 }
316
317 int32_t getReplaceId() const {
318 return mReplaceId;
319 }
320
321 void setReplaceId(int32_t replaceId) {
322 mReplaceId = replaceId;
323 }
324
Andy Hung4ef88d72017-02-21 19:47:53 -0800325 S getXOffset() const {
326 return mXOffset;
327 }
328
329 void setXOffset(S xOffset) {
330 mXOffset = xOffset;
331 }
332
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800333 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)
Andy Hung4ef88d72017-02-21 19:47:53 -0800349 ?: parcel->writeInt32(mReplaceId)
350 ?: parcel->writeFloat(mXOffset);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800351 }
352
353 status_t readFromParcel(const Parcel &parcel) {
354 int32_t flags;
355 return parcel.readInt32(&flags)
356 ?: parcel.readInt32(&mReplaceId)
Andy Hung4ef88d72017-02-21 19:47:53 -0800357 ?: parcel.readFloat(&mXOffset)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800358 ?: 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;
Andy Hung4ef88d72017-02-21 19:47:53 -0800365 ss << "mXOffset: " << mXOffset << std::endl;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800366 return ss.str();
367 }
368
369 private:
370 Flag mFlags;
371 int32_t mReplaceId;
Andy Hung4ef88d72017-02-21 19:47:53 -0800372 S mXOffset;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800373 }; // 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
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800480 // 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))
Andy Hung4ef88d72017-02-21 19:47:53 -0800490 , mLastXOffset(0.f)
491 , mDelayXOffset(std::numeric_limits<S>::quiet_NaN()) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800492 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);
Andy Hung4ef88d72017-02-21 19:47:53 -0800503 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);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800508 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 {
Andy Hung4ef88d72017-02-21 19:47:53 -0800519 return new VolumeShaper::State(mLastVolume, mLastXOffset);
520 }
521
522 void setDelayXOffset(S xOffset) {
523 mDelayXOffset = xOffset;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800524 }
525
Andy Hung10cbff12017-02-21 17:30:14 -0800526 std::pair<T /* volume */, bool /* active */> getVolume(
527 int64_t trackFrameCount, double trackSampleRate) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800528 if ((getFlags() & VolumeShaper::Operation::FLAG_DELAY) != 0) {
529 VS_LOG("delayed VolumeShaper, ignoring");
530 mLastVolume = T(1);
Andy Hung4ef88d72017-02-21 19:47:53 -0800531 mLastXOffset = 0.;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800532 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) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800552 mLastXOffset = 1.f;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800553 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) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800560 mLastXOffset = 0.f;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800561 mLastVolume = 1.f;
Andy Hung10cbff12017-02-21 17:30:14 -0800562 return std::make_pair(T(1), true); // too early
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800563 }
564 } else {
565 if (x < mConfiguration->first().first) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800566 mLastXOffset = 0.f;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800567 mLastVolume = 1.f;
Andy Hung10cbff12017-02-21 17:30:14 -0800568 return std::make_pair(T(1), true); // too early
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800569 }
570 if (x > mConfiguration->last().first) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800571 mLastXOffset = 1.f;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800572 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 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800579 mLastXOffset = x;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800580 // x contains the location on the volume curve to use.
581 const T unscaledVolume = mConfiguration->findY(x);
Andy Hung4ef88d72017-02-21 19:47:53 -0800582 const T volume = mConfiguration->adjustVolume(unscaledVolume); // handle log scale
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800583 VS_LOG("volume: %f unscaled: %f", volume, unscaledVolume);
584 mLastVolume = volume;
Andy Hung10cbff12017-02-21 17:30:14 -0800585 return std::make_pair(volume, true);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800586 }
587
588 std::string toString() const {
589 std::stringstream ss;
590 ss << "StartFrame: " << mStartFrame << std::endl;
591 ss << mXTranslate.toString().c_str();
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800592 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 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800606
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.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800614}; // 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
Andy Hung4ef88d72017-02-21 19:47:53 -0800623 // A volume handler which just keeps track of active VolumeShapers does not need sampleRate.
624 VolumeHandler()
625 : VolumeHandler(0 /* sampleRate */) {
626 }
627
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800628 explicit VolumeHandler(uint32_t sampleRate)
629 : mSampleRate((double)sampleRate)
Andy Hung4ef88d72017-02-21 19:47:53 -0800630 , mLastFrame(0)
Andy Hungda540db2017-04-20 14:06:17 -0700631 , mVolumeShaperIdCounter(VolumeShaper::kSystemIdMax)
632 , mLastVolume(1.f, false) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800633 }
634
635 VolumeShaper::Status applyVolumeShaper(
636 const sp<VolumeShaper::Configuration> &configuration,
637 const sp<VolumeShaper::Operation> &operation) {
Andy Hung10cbff12017-02-21 17:30:14 -0800638 VS_LOG("applyVolumeShaper:configuration: %s", configuration->toString().c_str());
639 VS_LOG("applyVolumeShaper:operation: %s", operation->toString().c_str());
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800640 AutoMutex _l(mLock);
641 if (configuration == nullptr) {
642 ALOGE("null configuration");
643 return VolumeShaper::Status(BAD_VALUE);
644 }
645 if (operation == nullptr) {
646 ALOGE("null operation");
647 return VolumeShaper::Status(BAD_VALUE);
648 }
649 const int32_t id = configuration->getId();
650 if (id < 0) {
651 ALOGE("negative id: %d", id);
652 return VolumeShaper::Status(BAD_VALUE);
653 }
654 VS_LOG("applyVolumeShaper id: %d", id);
655
656 switch (configuration->getType()) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800657 case VolumeShaper::Configuration::TYPE_SCALE: {
658 const int replaceId = operation->getReplaceId();
659 if (replaceId >= 0) {
660 auto replaceIt = findId_l(replaceId);
661 if (replaceIt == mVolumeShapers.end()) {
662 ALOGW("cannot find replace id: %d", replaceId);
663 } else {
664 if ((replaceIt->getFlags() & VolumeShaper::Operation::FLAG_JOIN) != 0) {
665 // For join, we scale the start volume of the current configuration
666 // to match the last-used volume of the replacing VolumeShaper.
667 auto state = replaceIt->getState();
668 if (state->getXOffset() >= 0) { // valid
669 const T volume = state->getVolume();
670 ALOGD("join: scaling start volume to %f", volume);
671 configuration->scaleToStartVolume(volume);
672 }
673 }
674 (void)mVolumeShapers.erase(replaceIt);
675 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800676 operation->setReplaceId(-1);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800677 }
678 // check if we have another of the same id.
679 auto oldIt = findId_l(id);
680 if (oldIt != mVolumeShapers.end()) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800681 if ((operation->getFlags()
682 & VolumeShaper::Operation::FLAG_CREATE_IF_NECESSARY) != 0) {
683 // TODO: move the case to a separate function.
684 goto HANDLE_TYPE_ID; // no need to create, take over existing id.
685 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800686 ALOGW("duplicate id, removing old %d", id);
687 (void)mVolumeShapers.erase(oldIt);
688 }
689 // create new VolumeShaper
690 mVolumeShapers.emplace_back(configuration, operation);
Andy Hung10cbff12017-02-21 17:30:14 -0800691 }
692 // fall through to handle the operation
Andy Hung4ef88d72017-02-21 19:47:53 -0800693 HANDLE_TYPE_ID:
Andy Hung10cbff12017-02-21 17:30:14 -0800694 case VolumeShaper::Configuration::TYPE_ID: {
695 VS_LOG("trying to find id: %d", id);
696 auto it = findId_l(id);
697 if (it == mVolumeShapers.end()) {
698 VS_LOG("couldn't find id: %d", id);
699 return VolumeShaper::Status(INVALID_OPERATION);
700 }
701 if ((it->getFlags() & VolumeShaper::Operation::FLAG_TERMINATE) != 0) {
702 VS_LOG("terminate id: %d", id);
703 mVolumeShapers.erase(it);
704 break;
705 }
706 const bool clockTime = (it->mConfiguration->getOptionFlags()
707 & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
708 if ((it->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) !=
709 (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE)) {
710 const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
711 const S x = it->mXTranslate((T)frameCount);
712 VS_LOG("reverse translation: %f", x);
713 // reflect position
714 S target = 1.f - x;
715 if (target < it->mConfiguration->first().first) {
716 VS_LOG("clamp to start - begin immediately");
717 target = 0.;
718 }
719 VS_LOG("target reverse: %f", target);
720 it->mXTranslate.setOffset(it->mXTranslate.getOffset()
721 + (x - target) / it->mXTranslate.getScale());
722 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800723 const S xOffset = operation->getXOffset();
724 if (!std::isnan(xOffset)) {
725 const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
726 const S x = it->mXTranslate((T)frameCount);
727 VS_LOG("xOffset translation: %f", x);
728 const S target = xOffset; // offset
729 VS_LOG("xOffset target x offset: %f", target);
730 it->mXTranslate.setOffset(it->mXTranslate.getOffset()
731 + (x - target) / it->mXTranslate.getScale());
732 it->setDelayXOffset(xOffset);
733 }
Andy Hung10cbff12017-02-21 17:30:14 -0800734 it->mOperation = operation; // replace the operation
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800735 } break;
736 }
737 return VolumeShaper::Status(id);
738 }
739
740 sp<VolumeShaper::State> getVolumeShaperState(int id) {
741 AutoMutex _l(mLock);
742 auto it = findId_l(id);
743 if (it == mVolumeShapers.end()) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800744 VS_LOG("cannot find state for id: %d", id);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800745 return nullptr;
746 }
747 return it->getState();
748 }
749
Andy Hung10cbff12017-02-21 17:30:14 -0800750 std::pair<T /* volume */, bool /* active */> getVolume(int64_t trackFrameCount) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800751 AutoMutex _l(mLock);
752 mLastFrame = trackFrameCount;
753 T volume(1);
Andy Hung10cbff12017-02-21 17:30:14 -0800754 size_t activeCount = 0;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800755 for (auto it = mVolumeShapers.begin(); it != mVolumeShapers.end();) {
756 std::pair<T, bool> shaperVolume =
757 it->getVolume(trackFrameCount, mSampleRate);
758 volume *= shaperVolume.first;
Andy Hung10cbff12017-02-21 17:30:14 -0800759 activeCount += shaperVolume.second;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800760 ++it;
761 }
Andy Hungda540db2017-04-20 14:06:17 -0700762 mLastVolume = std::make_pair(volume, activeCount != 0);
763 return mLastVolume;
764 }
765
766 std::pair<T /* volume */, bool /* active */> getLastVolume() const {
767 AutoMutex _l(mLock);
768 return mLastVolume;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800769 }
770
771 std::string toString() const {
772 AutoMutex _l(mLock);
773 std::stringstream ss;
774 ss << "mSampleRate: " << mSampleRate << std::endl;
775 ss << "mLastFrame: " << mLastFrame << std::endl;
776 for (const auto &shaper : mVolumeShapers) {
777 ss << shaper.toString().c_str();
778 }
779 return ss.str();
780 }
781
Andy Hung4ef88d72017-02-21 19:47:53 -0800782 void forall(const std::function<VolumeShaper::Status (
783 const sp<VolumeShaper::Configuration> &configuration,
784 const sp<VolumeShaper::Operation> &operation)> &lambda) {
785 AutoMutex _l(mLock);
Andy Hungda540db2017-04-20 14:06:17 -0700786 VS_LOG("forall: mVolumeShapers.size() %zu", mVolumeShapers.size());
Andy Hung4ef88d72017-02-21 19:47:53 -0800787 for (const auto &shaper : mVolumeShapers) {
788 VS_LOG("forall applying lambda");
789 (void)lambda(shaper.mConfiguration, shaper.mOperation);
790 }
791 }
792
793 void reset() {
794 AutoMutex _l(mLock);
795 mVolumeShapers.clear();
796 mLastFrame = -1;
797 // keep mVolumeShaperIdCounter as is.
798 }
799
800 // Sets the configuration id if necessary - This is based on the counter
801 // internal to the VolumeHandler.
802 void setIdIfNecessary(const sp<VolumeShaper::Configuration> &configuration) {
803 if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
804 const int id = configuration->getId();
805 if (id == -1) {
806 // Reassign to a unique id, skipping system ids.
807 AutoMutex _l(mLock);
808 while (true) {
809 if (mVolumeShaperIdCounter == INT32_MAX) {
810 mVolumeShaperIdCounter = VolumeShaper::kSystemIdMax;
811 } else {
812 ++mVolumeShaperIdCounter;
813 }
814 if (findId_l(mVolumeShaperIdCounter) != mVolumeShapers.end()) {
815 continue; // collision with an existing id.
816 }
817 configuration->setId(mVolumeShaperIdCounter);
818 ALOGD("setting id to %d", mVolumeShaperIdCounter);
819 break;
820 }
821 }
822 }
823 }
824
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800825private:
826 std::list<VolumeShaper>::iterator findId_l(int32_t id) {
827 std::list<VolumeShaper>::iterator it = mVolumeShapers.begin();
828 for (; it != mVolumeShapers.end(); ++it) {
829 if (it->mConfiguration->getId() == id) {
830 break;
831 }
832 }
833 return it;
834 }
835
836 mutable Mutex mLock;
837 double mSampleRate; // in samples (frames) per second
838 int64_t mLastFrame; // logging purpose only
Andy Hung4ef88d72017-02-21 19:47:53 -0800839 int32_t mVolumeShaperIdCounter; // a counter to return a unique volume shaper id.
Andy Hungda540db2017-04-20 14:06:17 -0700840 std::pair<T /* volume */, bool /* active */> mLastVolume;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800841 std::list<VolumeShaper> mVolumeShapers; // list provides stable iterators on erase
842}; // VolumeHandler
843
844} // namespace android
845
846#pragma pop_macro("LOG_TAG")
847
848#endif // ANDROID_VOLUME_SHAPER_H