blob: 7d76f6673bc3608b212e9d9218a406230d7244c5 [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
Colin Cross4e399992017-04-27 16:15:51 -070035#define VS_LOGGING 0
36#define VS_LOG(...) ALOGD_IF(VS_LOGGING, __VA_ARGS__)
Andy Hung9fc8b5c2017-01-24 13:36:48 -080037
38namespace android {
39
40// The native VolumeShaper class mirrors the java VolumeShaper class;
41// in addition, the native class contains implementation for actual operation.
42//
43// VolumeShaper methods are not safe for multiple thread access.
44// Use VolumeHandler for thread-safe encapsulation of multiple VolumeShapers.
45//
46// Classes below written are to avoid naked pointers so there are no
47// explicit destructors required.
48
49class VolumeShaper {
50public:
51 using S = float;
52 using T = float;
53
54 static const int kSystemIdMax = 16;
55
56 // VolumeShaper::Status is equivalent to status_t if negative
57 // but if non-negative represents the id operated on.
58 // It must be expressible as an int32_t for binder purposes.
59 using Status = status_t;
60
61 class Configuration : public Interpolator<S, T>, public RefBase {
62 public:
63 /* VolumeShaper.Configuration derives from the Interpolator class and adds
64 * parameters relating to the volume shape.
65 */
66
67 // TODO document as per VolumeShaper.java flags.
68
69 // must match with VolumeShaper.java in frameworks/base
70 enum Type : int32_t {
71 TYPE_ID,
72 TYPE_SCALE,
73 };
74
75 // must match with VolumeShaper.java in frameworks/base
76 enum OptionFlag : int32_t {
77 OPTION_FLAG_NONE = 0,
78 OPTION_FLAG_VOLUME_IN_DBFS = (1 << 0),
79 OPTION_FLAG_CLOCK_TIME = (1 << 1),
80
81 OPTION_FLAG_ALL = (OPTION_FLAG_VOLUME_IN_DBFS | OPTION_FLAG_CLOCK_TIME),
82 };
83
84 // bring to derived class; must match with VolumeShaper.java in frameworks/base
85 using InterpolatorType = Interpolator<S, T>::InterpolatorType;
86
87 Configuration()
88 : Interpolator<S, T>()
89 , mType(TYPE_SCALE)
Colin Cross4e399992017-04-27 16:15:51 -070090 , mId(-1)
Andy Hung9fc8b5c2017-01-24 13:36:48 -080091 , mOptionFlags(OPTION_FLAG_NONE)
Colin Cross4e399992017-04-27 16:15:51 -070092 , mDurationMs(1000.) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -080093 }
94
Andy Hung7d712bb2017-04-20 14:23:41 -070095 explicit Configuration(const Configuration &configuration)
Andy Hung10cbff12017-02-21 17:30:14 -080096 : Interpolator<S, T>(*static_cast<const Interpolator<S, T> *>(&configuration))
97 , mType(configuration.mType)
Colin Cross4e399992017-04-27 16:15:51 -070098 , mId(configuration.mId)
Andy Hung10cbff12017-02-21 17:30:14 -080099 , mOptionFlags(configuration.mOptionFlags)
Colin Cross4e399992017-04-27 16:15:51 -0700100 , mDurationMs(configuration.mDurationMs) {
Andy Hung10cbff12017-02-21 17:30:14 -0800101 }
102
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800103 Type getType() const {
104 return mType;
105 }
106
107 status_t setType(Type type) {
108 switch (type) {
109 case TYPE_ID:
110 case TYPE_SCALE:
111 mType = type;
112 return NO_ERROR;
113 default:
114 ALOGE("invalid Type: %d", type);
115 return BAD_VALUE;
116 }
117 }
118
119 OptionFlag getOptionFlags() const {
120 return mOptionFlags;
121 }
122
123 status_t setOptionFlags(OptionFlag optionFlags) {
124 if ((optionFlags & ~OPTION_FLAG_ALL) != 0) {
125 ALOGE("optionFlags has invalid bits: %#x", optionFlags);
126 return BAD_VALUE;
127 }
128 mOptionFlags = optionFlags;
129 return NO_ERROR;
130 }
131
132 double getDurationMs() const {
133 return mDurationMs;
134 }
135
136 void setDurationMs(double durationMs) {
137 mDurationMs = durationMs;
138 }
139
140 int32_t getId() const {
141 return mId;
142 }
143
144 void setId(int32_t id) {
145 mId = id;
146 }
147
148 T adjustVolume(T volume) const {
149 if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
150 const T out = powf(10.f, volume / 10.);
151 VS_LOG("in: %f out: %f", volume, out);
152 volume = out;
153 }
154 // clamp
155 if (volume < 0.f) {
156 volume = 0.f;
157 } else if (volume > 1.f) {
158 volume = 1.f;
159 }
160 return volume;
161 }
162
163 status_t checkCurve() {
164 if (mType == TYPE_ID) return NO_ERROR;
165 if (this->size() < 2) {
166 ALOGE("curve must have at least 2 points");
167 return BAD_VALUE;
168 }
169 if (first().first != 0.f || last().first != 1.f) {
170 ALOGE("curve must start at 0.f and end at 1.f");
171 return BAD_VALUE;
172 }
173 if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
174 for (const auto &pt : *this) {
175 if (!(pt.second <= 0.f) /* handle nan */) {
176 ALOGE("positive volume dbFS");
177 return BAD_VALUE;
178 }
179 }
180 } else {
181 for (const auto &pt : *this) {
182 if (!(pt.second >= 0.f) || !(pt.second <= 1.f) /* handle nan */) {
183 ALOGE("volume < 0.f or > 1.f");
184 return BAD_VALUE;
185 }
186 }
187 }
188 return NO_ERROR;
189 }
190
191 void clampVolume() {
192 if ((mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
193 for (auto it = this->begin(); it != this->end(); ++it) {
194 if (!(it->second <= 0.f) /* handle nan */) {
195 it->second = 0.f;
196 }
197 }
198 } else {
199 for (auto it = this->begin(); it != this->end(); ++it) {
200 if (!(it->second >= 0.f) /* handle nan */) {
201 it->second = 0.f;
202 } else if (!(it->second <= 1.f)) {
203 it->second = 1.f;
204 }
205 }
206 }
207 }
208
209 /* scaleToStartVolume() is used to set the start volume of a
210 * new VolumeShaper curve, when replacing one VolumeShaper
211 * with another using the "join" (volume match) option.
212 *
213 * It works best for monotonic volume ramps or ducks.
214 */
215 void scaleToStartVolume(T volume) {
216 if (this->size() < 2) {
217 return;
218 }
219 const T startVolume = first().second;
220 const T endVolume = last().second;
221 if (endVolume == startVolume) {
222 // match with linear ramp
223 const T offset = volume - startVolume;
224 for (auto it = this->begin(); it != this->end(); ++it) {
225 it->second = it->second + offset * (1.f - it->first);
226 }
227 } else {
228 const T scale = (volume - endVolume) / (startVolume - endVolume);
229 for (auto it = this->begin(); it != this->end(); ++it) {
230 it->second = scale * (it->second - endVolume) + endVolume;
231 }
232 }
233 clampVolume();
234 }
235
Andy Hung7d712bb2017-04-20 14:23:41 -0700236 // The parcel layout must match VolumeShaper.java
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800237 status_t writeToParcel(Parcel *parcel) const {
238 if (parcel == nullptr) return BAD_VALUE;
239 return parcel->writeInt32((int32_t)mType)
240 ?: parcel->writeInt32(mId)
241 ?: mType == TYPE_ID
242 ? NO_ERROR
243 : parcel->writeInt32((int32_t)mOptionFlags)
244 ?: parcel->writeDouble(mDurationMs)
245 ?: Interpolator<S, T>::writeToParcel(parcel);
246 }
247
248 status_t readFromParcel(const Parcel &parcel) {
249 int32_t type, optionFlags;
250 return parcel.readInt32(&type)
251 ?: setType((Type)type)
252 ?: parcel.readInt32(&mId)
253 ?: mType == TYPE_ID
254 ? NO_ERROR
255 : parcel.readInt32(&optionFlags)
256 ?: setOptionFlags((OptionFlag)optionFlags)
257 ?: parcel.readDouble(&mDurationMs)
258 ?: Interpolator<S, T>::readFromParcel(parcel)
259 ?: checkCurve();
260 }
261
262 std::string toString() const {
263 std::stringstream ss;
264 ss << "mType: " << mType << std::endl;
265 ss << "mId: " << mId << std::endl;
266 if (mType != TYPE_ID) {
267 ss << "mOptionFlags: " << mOptionFlags << std::endl;
268 ss << "mDurationMs: " << mDurationMs << std::endl;
269 ss << Interpolator<S, T>::toString().c_str();
270 }
271 return ss.str();
272 }
273
274 private:
275 Type mType;
276 int32_t mId;
277 OptionFlag mOptionFlags;
278 double mDurationMs;
279 }; // Configuration
280
281 // must match with VolumeShaper.java in frameworks/base
282 // TODO document per VolumeShaper.java flags.
283 class Operation : public RefBase {
284 public:
285 enum Flag : int32_t {
286 FLAG_NONE = 0,
287 FLAG_REVERSE = (1 << 0),
288 FLAG_TERMINATE = (1 << 1),
289 FLAG_JOIN = (1 << 2),
290 FLAG_DELAY = (1 << 3),
Andy Hung4ef88d72017-02-21 19:47:53 -0800291 FLAG_CREATE_IF_NECESSARY = (1 << 4),
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800292
Andy Hung4ef88d72017-02-21 19:47:53 -0800293 FLAG_ALL = (FLAG_REVERSE | FLAG_TERMINATE | FLAG_JOIN | FLAG_DELAY
294 | FLAG_CREATE_IF_NECESSARY),
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800295 };
296
297 Operation()
Andy Hung4ef88d72017-02-21 19:47:53 -0800298 : Operation(FLAG_NONE, -1 /* replaceId */) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800299 }
300
Andy Hung7d712bb2017-04-20 14:23:41 -0700301 Operation(Flag flags, int replaceId)
Andy Hung4ef88d72017-02-21 19:47:53 -0800302 : Operation(flags, replaceId, std::numeric_limits<S>::quiet_NaN() /* xOffset */) {
303 }
304
Andy Hung7d712bb2017-04-20 14:23:41 -0700305 explicit Operation(const Operation &operation)
Andy Hung4ef88d72017-02-21 19:47:53 -0800306 : Operation(operation.mFlags, operation.mReplaceId, operation.mXOffset) {
307 }
308
Andy Hung7d712bb2017-04-20 14:23:41 -0700309 explicit Operation(const sp<Operation> &operation)
310 : Operation(*operation.get()) {
311 }
312
313 Operation(Flag flags, int replaceId, S xOffset)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800314 : mFlags(flags)
Andy Hung4ef88d72017-02-21 19:47:53 -0800315 , mReplaceId(replaceId)
316 , mXOffset(xOffset) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800317 }
318
319 int32_t getReplaceId() const {
320 return mReplaceId;
321 }
322
323 void setReplaceId(int32_t replaceId) {
324 mReplaceId = replaceId;
325 }
326
Andy Hung4ef88d72017-02-21 19:47:53 -0800327 S getXOffset() const {
328 return mXOffset;
329 }
330
331 void setXOffset(S xOffset) {
332 mXOffset = xOffset;
333 }
334
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800335 Flag getFlags() const {
336 return mFlags;
337 }
338
339 status_t setFlags(Flag flags) {
340 if ((flags & ~FLAG_ALL) != 0) {
341 ALOGE("flags has invalid bits: %#x", flags);
342 return BAD_VALUE;
343 }
344 mFlags = flags;
345 return NO_ERROR;
346 }
347
348 status_t writeToParcel(Parcel *parcel) const {
349 if (parcel == nullptr) return BAD_VALUE;
350 return parcel->writeInt32((int32_t)mFlags)
Andy Hung4ef88d72017-02-21 19:47:53 -0800351 ?: parcel->writeInt32(mReplaceId)
352 ?: parcel->writeFloat(mXOffset);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800353 }
354
355 status_t readFromParcel(const Parcel &parcel) {
356 int32_t flags;
357 return parcel.readInt32(&flags)
358 ?: parcel.readInt32(&mReplaceId)
Andy Hung4ef88d72017-02-21 19:47:53 -0800359 ?: parcel.readFloat(&mXOffset)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800360 ?: setFlags((Flag)flags);
361 }
362
363 std::string toString() const {
364 std::stringstream ss;
365 ss << "mFlags: " << mFlags << std::endl;
366 ss << "mReplaceId: " << mReplaceId << std::endl;
Andy Hung4ef88d72017-02-21 19:47:53 -0800367 ss << "mXOffset: " << mXOffset << std::endl;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800368 return ss.str();
369 }
370
371 private:
372 Flag mFlags;
373 int32_t mReplaceId;
Andy Hung4ef88d72017-02-21 19:47:53 -0800374 S mXOffset;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800375 }; // Operation
376
377 // must match with VolumeShaper.java in frameworks/base
378 class State : public RefBase {
379 public:
Andy Hung7d712bb2017-04-20 14:23:41 -0700380 State(T volume, S xOffset)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800381 : mVolume(volume)
382 , mXOffset(xOffset) {
383 }
384
385 State()
386 : State(-1.f, -1.f) { }
387
388 T getVolume() const {
389 return mVolume;
390 }
391
392 void setVolume(T volume) {
393 mVolume = volume;
394 }
395
396 S getXOffset() const {
397 return mXOffset;
398 }
399
400 void setXOffset(S xOffset) {
401 mXOffset = xOffset;
402 }
403
404 status_t writeToParcel(Parcel *parcel) const {
405 if (parcel == nullptr) return BAD_VALUE;
406 return parcel->writeFloat(mVolume)
407 ?: parcel->writeFloat(mXOffset);
408 }
409
410 status_t readFromParcel(const Parcel &parcel) {
411 return parcel.readFloat(&mVolume)
412 ?: parcel.readFloat(&mXOffset);
413 }
414
415 std::string toString() const {
416 std::stringstream ss;
417 ss << "mVolume: " << mVolume << std::endl;
418 ss << "mXOffset: " << mXOffset << std::endl;
419 return ss.str();
420 }
421
422 private:
423 T mVolume;
424 S mXOffset;
425 }; // State
426
427 template <typename R>
428 class Translate {
429 public:
430 Translate()
431 : mOffset(0)
432 , mScale(1) {
433 }
434
435 R getOffset() const {
436 return mOffset;
437 }
438
439 void setOffset(R offset) {
440 mOffset = offset;
441 }
442
443 R getScale() const {
444 return mScale;
445 }
446
447 void setScale(R scale) {
448 mScale = scale;
449 }
450
451 R operator()(R in) const {
452 return mScale * (in - mOffset);
453 }
454
455 std::string toString() const {
456 std::stringstream ss;
457 ss << "mOffset: " << mOffset << std::endl;
458 ss << "mScale: " << mScale << std::endl;
459 return ss.str();
460 }
461
462 private:
463 R mOffset;
464 R mScale;
465 }; // Translate
466
467 static int64_t convertTimespecToUs(const struct timespec &tv)
468 {
469 return tv.tv_sec * 1000000ll + tv.tv_nsec / 1000;
470 }
471
472 // current monotonic time in microseconds.
473 static int64_t getNowUs()
474 {
475 struct timespec tv;
476 if (clock_gettime(CLOCK_MONOTONIC, &tv) != 0) {
477 return 0; // system is really sick, just return 0 for consistency.
478 }
479 return convertTimespecToUs(tv);
480 }
481
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800482 // TODO: Since we pass configuration and operation as shared pointers
483 // there is a potential risk that the caller may modify these after
484 // delivery. Currently, we don't require copies made here.
Andy Hung7d712bb2017-04-20 14:23:41 -0700485 VolumeShaper(
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800486 const sp<VolumeShaper::Configuration> &configuration,
487 const sp<VolumeShaper::Operation> &operation)
488 : mConfiguration(configuration) // we do not make a copy
489 , mOperation(operation) // ditto
490 , mStartFrame(-1)
491 , mLastVolume(T(1))
Andy Hung4ef88d72017-02-21 19:47:53 -0800492 , mLastXOffset(0.f)
493 , mDelayXOffset(std::numeric_limits<S>::quiet_NaN()) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800494 if (configuration.get() != nullptr
495 && (getFlags() & VolumeShaper::Operation::FLAG_DELAY) == 0) {
496 mLastVolume = configuration->first().second;
497 }
498 }
499
500 void updatePosition(int64_t startFrame, double sampleRate) {
501 double scale = (mConfiguration->last().first - mConfiguration->first().first)
502 / (mConfiguration->getDurationMs() * 0.001 * sampleRate);
503 const double minScale = 1. / INT64_MAX;
504 scale = std::max(scale, minScale);
Andy Hung4ef88d72017-02-21 19:47:53 -0800505 const S xOffset = std::isnan(mDelayXOffset) ? mConfiguration->first().first : mDelayXOffset;
506 VS_LOG("update position: scale %lf frameCount:%lld, sampleRate:%lf, xOffset:%f",
507 scale, (long long) startFrame, sampleRate, xOffset);
508
509 mXTranslate.setOffset(startFrame - xOffset / scale);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800510 mXTranslate.setScale(scale);
511 VS_LOG("translate: %s", mXTranslate.toString().c_str());
512 }
513
514 // We allow a null operation here, though VolumeHandler always provides one.
515 VolumeShaper::Operation::Flag getFlags() const {
516 return mOperation == nullptr
517 ? VolumeShaper::Operation::FLAG_NONE :mOperation->getFlags();
518 }
519
520 sp<VolumeShaper::State> getState() const {
Andy Hung4ef88d72017-02-21 19:47:53 -0800521 return new VolumeShaper::State(mLastVolume, mLastXOffset);
522 }
523
524 void setDelayXOffset(S xOffset) {
525 mDelayXOffset = xOffset;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800526 }
527
Andy Hung39399b62017-04-21 15:07:45 -0700528 bool isStarted() const {
529 return mStartFrame >= 0;
530 }
531
Andy Hung10cbff12017-02-21 17:30:14 -0800532 std::pair<T /* volume */, bool /* active */> getVolume(
533 int64_t trackFrameCount, double trackSampleRate) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800534 if ((getFlags() & VolumeShaper::Operation::FLAG_DELAY) != 0) {
535 VS_LOG("delayed VolumeShaper, ignoring");
536 mLastVolume = T(1);
Andy Hung4ef88d72017-02-21 19:47:53 -0800537 mLastXOffset = 0.;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800538 return std::make_pair(T(1), false);
539 }
540 const bool clockTime = (mConfiguration->getOptionFlags()
541 & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
542 const int64_t frameCount = clockTime ? getNowUs() : trackFrameCount;
543 const double sampleRate = clockTime ? 1000000 : trackSampleRate;
544
545 if (mStartFrame < 0) {
546 updatePosition(frameCount, sampleRate);
547 mStartFrame = frameCount;
548 }
549 VS_LOG("frameCount: %lld", (long long)frameCount);
550 S x = mXTranslate((T)frameCount);
551 VS_LOG("translation: %f", x);
552
553 // handle reversal of position
554 if (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) {
555 x = 1.f - x;
556 VS_LOG("reversing to %f", x);
557 if (x < mConfiguration->first().first) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800558 mLastXOffset = 1.f;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800559 const T volume = mConfiguration->adjustVolume(
560 mConfiguration->first().second); // persist last value
561 VS_LOG("persisting volume %f", volume);
562 mLastVolume = volume;
563 return std::make_pair(volume, false);
564 }
565 if (x > mConfiguration->last().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 } else {
571 if (x < mConfiguration->first().first) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800572 mLastXOffset = 0.f;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800573 mLastVolume = 1.f;
Andy Hung10cbff12017-02-21 17:30:14 -0800574 return std::make_pair(T(1), true); // too early
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800575 }
576 if (x > mConfiguration->last().first) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800577 mLastXOffset = 1.f;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800578 const T volume = mConfiguration->adjustVolume(
579 mConfiguration->last().second); // persist last value
580 VS_LOG("persisting volume %f", volume);
581 mLastVolume = volume;
582 return std::make_pair(volume, false);
583 }
584 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800585 mLastXOffset = x;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800586 // x contains the location on the volume curve to use.
587 const T unscaledVolume = mConfiguration->findY(x);
Andy Hung4ef88d72017-02-21 19:47:53 -0800588 const T volume = mConfiguration->adjustVolume(unscaledVolume); // handle log scale
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800589 VS_LOG("volume: %f unscaled: %f", volume, unscaledVolume);
590 mLastVolume = volume;
Andy Hung10cbff12017-02-21 17:30:14 -0800591 return std::make_pair(volume, true);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800592 }
593
594 std::string toString() const {
595 std::stringstream ss;
596 ss << "StartFrame: " << mStartFrame << std::endl;
597 ss << mXTranslate.toString().c_str();
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800598 if (mConfiguration.get() == nullptr) {
599 ss << "VolumeShaper::Configuration: nullptr" << std::endl;
600 } else {
601 ss << "VolumeShaper::Configuration:" << std::endl;
602 ss << mConfiguration->toString().c_str();
603 }
604 if (mOperation.get() == nullptr) {
605 ss << "VolumeShaper::Operation: nullptr" << std::endl;
606 } else {
607 ss << "VolumeShaper::Operation:" << std::endl;
608 ss << mOperation->toString().c_str();
609 }
610 return ss.str();
611 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800612
613 Translate<S> mXTranslate; // x axis translation from frames (in usec for clock time)
614 sp<VolumeShaper::Configuration> mConfiguration;
615 sp<VolumeShaper::Operation> mOperation;
616 int64_t mStartFrame; // starting frame, non-negative when started (in usec for clock time)
617 T mLastVolume; // last computed interpolated volume (y-axis)
618 S mLastXOffset; // last computed interpolated xOffset/time (x-axis)
619 S mDelayXOffset; // delay xOffset on first volumeshaper start.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800620}; // VolumeShaper
621
622// VolumeHandler combines the volume factors of multiple VolumeShapers and handles
623// multiple thread access by synchronizing all public methods.
624class VolumeHandler : public RefBase {
625public:
626 using S = float;
627 using T = float;
628
Andy Hung4ef88d72017-02-21 19:47:53 -0800629 // A volume handler which just keeps track of active VolumeShapers does not need sampleRate.
630 VolumeHandler()
631 : VolumeHandler(0 /* sampleRate */) {
632 }
633
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800634 explicit VolumeHandler(uint32_t sampleRate)
635 : mSampleRate((double)sampleRate)
Andy Hung4ef88d72017-02-21 19:47:53 -0800636 , mLastFrame(0)
Andy Hungda540db2017-04-20 14:06:17 -0700637 , mVolumeShaperIdCounter(VolumeShaper::kSystemIdMax)
638 , mLastVolume(1.f, false) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800639 }
640
641 VolumeShaper::Status applyVolumeShaper(
642 const sp<VolumeShaper::Configuration> &configuration,
643 const sp<VolumeShaper::Operation> &operation) {
Andy Hung10cbff12017-02-21 17:30:14 -0800644 VS_LOG("applyVolumeShaper:configuration: %s", configuration->toString().c_str());
645 VS_LOG("applyVolumeShaper:operation: %s", operation->toString().c_str());
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800646 AutoMutex _l(mLock);
647 if (configuration == nullptr) {
648 ALOGE("null configuration");
649 return VolumeShaper::Status(BAD_VALUE);
650 }
651 if (operation == nullptr) {
652 ALOGE("null operation");
653 return VolumeShaper::Status(BAD_VALUE);
654 }
655 const int32_t id = configuration->getId();
656 if (id < 0) {
657 ALOGE("negative id: %d", id);
658 return VolumeShaper::Status(BAD_VALUE);
659 }
660 VS_LOG("applyVolumeShaper id: %d", id);
661
662 switch (configuration->getType()) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800663 case VolumeShaper::Configuration::TYPE_SCALE: {
664 const int replaceId = operation->getReplaceId();
665 if (replaceId >= 0) {
666 auto replaceIt = findId_l(replaceId);
667 if (replaceIt == mVolumeShapers.end()) {
668 ALOGW("cannot find replace id: %d", replaceId);
669 } else {
670 if ((replaceIt->getFlags() & VolumeShaper::Operation::FLAG_JOIN) != 0) {
671 // For join, we scale the start volume of the current configuration
672 // to match the last-used volume of the replacing VolumeShaper.
673 auto state = replaceIt->getState();
674 if (state->getXOffset() >= 0) { // valid
675 const T volume = state->getVolume();
676 ALOGD("join: scaling start volume to %f", volume);
677 configuration->scaleToStartVolume(volume);
678 }
679 }
680 (void)mVolumeShapers.erase(replaceIt);
681 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800682 operation->setReplaceId(-1);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800683 }
684 // check if we have another of the same id.
685 auto oldIt = findId_l(id);
686 if (oldIt != mVolumeShapers.end()) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800687 if ((operation->getFlags()
688 & VolumeShaper::Operation::FLAG_CREATE_IF_NECESSARY) != 0) {
689 // TODO: move the case to a separate function.
690 goto HANDLE_TYPE_ID; // no need to create, take over existing id.
691 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800692 ALOGW("duplicate id, removing old %d", id);
693 (void)mVolumeShapers.erase(oldIt);
694 }
695 // create new VolumeShaper
696 mVolumeShapers.emplace_back(configuration, operation);
Andy Hung10cbff12017-02-21 17:30:14 -0800697 }
698 // fall through to handle the operation
Andy Hung4ef88d72017-02-21 19:47:53 -0800699 HANDLE_TYPE_ID:
Andy Hung10cbff12017-02-21 17:30:14 -0800700 case VolumeShaper::Configuration::TYPE_ID: {
701 VS_LOG("trying to find id: %d", id);
702 auto it = findId_l(id);
703 if (it == mVolumeShapers.end()) {
704 VS_LOG("couldn't find id: %d", id);
705 return VolumeShaper::Status(INVALID_OPERATION);
706 }
707 if ((it->getFlags() & VolumeShaper::Operation::FLAG_TERMINATE) != 0) {
708 VS_LOG("terminate id: %d", id);
709 mVolumeShapers.erase(it);
710 break;
711 }
712 const bool clockTime = (it->mConfiguration->getOptionFlags()
713 & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
714 if ((it->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) !=
715 (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE)) {
716 const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
717 const S x = it->mXTranslate((T)frameCount);
718 VS_LOG("reverse translation: %f", x);
719 // reflect position
720 S target = 1.f - x;
721 if (target < it->mConfiguration->first().first) {
722 VS_LOG("clamp to start - begin immediately");
723 target = 0.;
724 }
725 VS_LOG("target reverse: %f", target);
726 it->mXTranslate.setOffset(it->mXTranslate.getOffset()
727 + (x - target) / it->mXTranslate.getScale());
728 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800729 const S xOffset = operation->getXOffset();
730 if (!std::isnan(xOffset)) {
731 const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
732 const S x = it->mXTranslate((T)frameCount);
733 VS_LOG("xOffset translation: %f", x);
734 const S target = xOffset; // offset
735 VS_LOG("xOffset target x offset: %f", target);
736 it->mXTranslate.setOffset(it->mXTranslate.getOffset()
737 + (x - target) / it->mXTranslate.getScale());
738 it->setDelayXOffset(xOffset);
739 }
Andy Hung10cbff12017-02-21 17:30:14 -0800740 it->mOperation = operation; // replace the operation
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800741 } break;
742 }
743 return VolumeShaper::Status(id);
744 }
745
746 sp<VolumeShaper::State> getVolumeShaperState(int id) {
747 AutoMutex _l(mLock);
748 auto it = findId_l(id);
749 if (it == mVolumeShapers.end()) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800750 VS_LOG("cannot find state for id: %d", id);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800751 return nullptr;
752 }
753 return it->getState();
754 }
755
Andy Hung39399b62017-04-21 15:07:45 -0700756 // getVolume() is not const, as it updates internal state.
757 // Once called, any VolumeShapers not already started begin running.
Andy Hung10cbff12017-02-21 17:30:14 -0800758 std::pair<T /* volume */, bool /* active */> getVolume(int64_t trackFrameCount) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800759 AutoMutex _l(mLock);
760 mLastFrame = trackFrameCount;
761 T volume(1);
Andy Hung10cbff12017-02-21 17:30:14 -0800762 size_t activeCount = 0;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800763 for (auto it = mVolumeShapers.begin(); it != mVolumeShapers.end();) {
764 std::pair<T, bool> shaperVolume =
765 it->getVolume(trackFrameCount, mSampleRate);
766 volume *= shaperVolume.first;
Andy Hung10cbff12017-02-21 17:30:14 -0800767 activeCount += shaperVolume.second;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800768 ++it;
769 }
Andy Hungda540db2017-04-20 14:06:17 -0700770 mLastVolume = std::make_pair(volume, activeCount != 0);
771 return mLastVolume;
772 }
773
Andy Hung39399b62017-04-21 15:07:45 -0700774 // Used by a client side VolumeHandler to ensure all the VolumeShapers
775 // indicate that they have been started. Upon a change in audioserver
776 // output sink, this information is used for restoration of the server side
777 // VolumeHandler.
778 void setStarted() {
779 (void)getVolume(mLastFrame); // getVolume() will start the individual VolumeShapers.
780 }
781
Andy Hungda540db2017-04-20 14:06:17 -0700782 std::pair<T /* volume */, bool /* active */> getLastVolume() const {
783 AutoMutex _l(mLock);
784 return mLastVolume;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800785 }
786
787 std::string toString() const {
788 AutoMutex _l(mLock);
789 std::stringstream ss;
790 ss << "mSampleRate: " << mSampleRate << std::endl;
791 ss << "mLastFrame: " << mLastFrame << std::endl;
792 for (const auto &shaper : mVolumeShapers) {
793 ss << shaper.toString().c_str();
794 }
795 return ss.str();
796 }
797
Andy Hung39399b62017-04-21 15:07:45 -0700798 void forall(const std::function<VolumeShaper::Status (const VolumeShaper &)> &lambda) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800799 AutoMutex _l(mLock);
Andy Hungda540db2017-04-20 14:06:17 -0700800 VS_LOG("forall: mVolumeShapers.size() %zu", mVolumeShapers.size());
Andy Hung4ef88d72017-02-21 19:47:53 -0800801 for (const auto &shaper : mVolumeShapers) {
Andy Hung39399b62017-04-21 15:07:45 -0700802 VolumeShaper::Status status = lambda(shaper);
803 VS_LOG("forall applying lambda on shaper (%p): %d", &shaper, (int)status);
Andy Hung4ef88d72017-02-21 19:47:53 -0800804 }
805 }
806
807 void reset() {
808 AutoMutex _l(mLock);
809 mVolumeShapers.clear();
Andy Hung7d712bb2017-04-20 14:23:41 -0700810 mLastFrame = 0;
Andy Hung4ef88d72017-02-21 19:47:53 -0800811 // keep mVolumeShaperIdCounter as is.
812 }
813
814 // Sets the configuration id if necessary - This is based on the counter
815 // internal to the VolumeHandler.
816 void setIdIfNecessary(const sp<VolumeShaper::Configuration> &configuration) {
817 if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
818 const int id = configuration->getId();
819 if (id == -1) {
820 // Reassign to a unique id, skipping system ids.
821 AutoMutex _l(mLock);
822 while (true) {
823 if (mVolumeShaperIdCounter == INT32_MAX) {
824 mVolumeShaperIdCounter = VolumeShaper::kSystemIdMax;
825 } else {
826 ++mVolumeShaperIdCounter;
827 }
828 if (findId_l(mVolumeShaperIdCounter) != mVolumeShapers.end()) {
829 continue; // collision with an existing id.
830 }
831 configuration->setId(mVolumeShaperIdCounter);
832 ALOGD("setting id to %d", mVolumeShaperIdCounter);
833 break;
834 }
835 }
836 }
837 }
838
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800839private:
840 std::list<VolumeShaper>::iterator findId_l(int32_t id) {
841 std::list<VolumeShaper>::iterator it = mVolumeShapers.begin();
842 for (; it != mVolumeShapers.end(); ++it) {
843 if (it->mConfiguration->getId() == id) {
844 break;
845 }
846 }
847 return it;
848 }
849
850 mutable Mutex mLock;
851 double mSampleRate; // in samples (frames) per second
Andy Hung7d712bb2017-04-20 14:23:41 -0700852 int64_t mLastFrame; // logging purpose only, 0 on start
Andy Hung4ef88d72017-02-21 19:47:53 -0800853 int32_t mVolumeShaperIdCounter; // a counter to return a unique volume shaper id.
Andy Hungda540db2017-04-20 14:06:17 -0700854 std::pair<T /* volume */, bool /* active */> mLastVolume;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800855 std::list<VolumeShaper> mVolumeShapers; // list provides stable iterators on erase
856}; // VolumeHandler
857
858} // namespace android
859
860#pragma pop_macro("LOG_TAG")
861
862#endif // ANDROID_VOLUME_SHAPER_H