blob: e4c0b5bcbfadde64350952d86be622babebc7a87 [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:
Andy Hungf3702642017-05-05 17:33:32 -070054 // S and T are like template typenames (matching the Interpolator<S, T>)
55 using S = float; // time type
56 using T = float; // volume type
Andy Hung9fc8b5c2017-01-24 13:36:48 -080057
Andy Hungf3702642017-05-05 17:33:32 -070058// Curve and dimension information
59// TODO: member static const or constexpr float initialization not permitted in C++11
60#define MIN_CURVE_TIME 0.f // type S: start of VolumeShaper curve (normalized)
61#define MAX_CURVE_TIME 1.f // type S: end of VolumeShaper curve (normalized)
62#define MIN_LINEAR_VOLUME 0.f // type T: silence / mute audio
63#define MAX_LINEAR_VOLUME 1.f // type T: max volume, unity gain
64#define MAX_LOG_VOLUME 0.f // type T: max volume, unity gain in dBFS
Andy Hung9fc8b5c2017-01-24 13:36:48 -080065
Andy Hungf3702642017-05-05 17:33:32 -070066 /* kSystemVolumeShapersMax is the maximum number of system VolumeShapers.
67 * Each system VolumeShapers has a predefined Id, which ranges from 0
68 * to kSystemVolumeShapersMax - 1 and is unique for its usage.
69 *
70 * "1" is reserved for system ducking.
71 */
72 static const int kSystemVolumeShapersMax = 16;
73
74 /* kUserVolumeShapersMax is the maximum number of application
75 * VolumeShapers for a player/track. Application VolumeShapers are
76 * assigned on creation by the client, and have Ids ranging
77 * from kSystemVolumeShapersMax to INT32_MAX.
78 *
79 * The number of user/application volume shapers is independent to the
80 * system volume shapers. If an application tries to create more than
81 * kUserVolumeShapersMax to a player, then the apply() will fail.
82 * This prevents exhausting server side resources by a potentially malicious
83 * application.
84 */
85 static const int kUserVolumeShapersMax = 16;
86
87 /* VolumeShaper::Status is equivalent to status_t if negative
88 * but if non-negative represents the id operated on.
89 * It must be expressible as an int32_t for binder purposes.
90 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -080091 using Status = status_t;
92
Andy Hungf3702642017-05-05 17:33:32 -070093 // Local definition for clamp as std::clamp is included in C++17 only.
94 // TODO: use the std::clamp version when Android build uses C++17.
95 template<typename R>
96 static constexpr const R &clamp(const R &v, const R &lo, const R &hi) {
97 return (v < lo) ? lo : (hi < v) ? hi : v;
98 }
99
100 /* VolumeShaper.Configuration derives from the Interpolator class and adds
101 * parameters relating to the volume shape.
102 *
103 * This parallels the Java implementation and the enums must match.
104 * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
105 * details on the Java implementation.
106 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800107 class Configuration : public Interpolator<S, T>, public RefBase {
108 public:
Andy Hungf3702642017-05-05 17:33:32 -0700109 // Must match with VolumeShaper.java in frameworks/base.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800110 enum Type : int32_t {
111 TYPE_ID,
112 TYPE_SCALE,
113 };
114
Andy Hungf3702642017-05-05 17:33:32 -0700115 // Must match with VolumeShaper.java in frameworks/base.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800116 enum OptionFlag : int32_t {
117 OPTION_FLAG_NONE = 0,
118 OPTION_FLAG_VOLUME_IN_DBFS = (1 << 0),
119 OPTION_FLAG_CLOCK_TIME = (1 << 1),
120
121 OPTION_FLAG_ALL = (OPTION_FLAG_VOLUME_IN_DBFS | OPTION_FLAG_CLOCK_TIME),
122 };
123
Andy Hungf3702642017-05-05 17:33:32 -0700124 // Bring from base class; must match with VolumeShaper.java in frameworks/base.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800125 using InterpolatorType = Interpolator<S, T>::InterpolatorType;
126
127 Configuration()
128 : Interpolator<S, T>()
Colin Cross549bd022017-05-02 10:32:56 -0700129 , RefBase()
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800130 , mType(TYPE_SCALE)
131 , mOptionFlags(OPTION_FLAG_NONE)
132 , mDurationMs(1000.)
133 , mId(-1) {
134 }
135
Andy Hung7d712bb2017-04-20 14:23:41 -0700136 explicit Configuration(const Configuration &configuration)
Andy Hung10cbff12017-02-21 17:30:14 -0800137 : Interpolator<S, T>(*static_cast<const Interpolator<S, T> *>(&configuration))
Colin Cross549bd022017-05-02 10:32:56 -0700138 , RefBase()
Andy Hung10cbff12017-02-21 17:30:14 -0800139 , mType(configuration.mType)
140 , mOptionFlags(configuration.mOptionFlags)
141 , mDurationMs(configuration.mDurationMs)
142 , mId(configuration.mId) {
143 }
144
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800145 Type getType() const {
146 return mType;
147 }
148
149 status_t setType(Type type) {
150 switch (type) {
151 case TYPE_ID:
152 case TYPE_SCALE:
153 mType = type;
154 return NO_ERROR;
155 default:
156 ALOGE("invalid Type: %d", type);
157 return BAD_VALUE;
158 }
159 }
160
161 OptionFlag getOptionFlags() const {
162 return mOptionFlags;
163 }
164
165 status_t setOptionFlags(OptionFlag optionFlags) {
166 if ((optionFlags & ~OPTION_FLAG_ALL) != 0) {
167 ALOGE("optionFlags has invalid bits: %#x", optionFlags);
168 return BAD_VALUE;
169 }
170 mOptionFlags = optionFlags;
171 return NO_ERROR;
172 }
173
174 double getDurationMs() const {
175 return mDurationMs;
176 }
177
Andy Hungf3702642017-05-05 17:33:32 -0700178 status_t setDurationMs(double durationMs) {
179 if (durationMs > 0.) {
180 mDurationMs = durationMs;
181 return NO_ERROR;
182 }
183 // zero, negative, or nan. These values not possible from Java.
184 return BAD_VALUE;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800185 }
186
187 int32_t getId() const {
188 return mId;
189 }
190
191 void setId(int32_t id) {
Andy Hungf3702642017-05-05 17:33:32 -0700192 // We permit a negative id here (representing invalid).
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800193 mId = id;
194 }
195
Andy Hungf3702642017-05-05 17:33:32 -0700196 /* Adjust the volume to be in linear range from MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME
197 * and compensate for log dbFS volume as needed.
198 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800199 T adjustVolume(T volume) const {
200 if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700201 const T out = powf(10.f, volume / 10.f);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800202 VS_LOG("in: %f out: %f", volume, out);
203 volume = out;
204 }
Andy Hungf3702642017-05-05 17:33:32 -0700205 return clamp(volume, MIN_LINEAR_VOLUME /* lo */, MAX_LINEAR_VOLUME /* hi */);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800206 }
207
Andy Hungf3702642017-05-05 17:33:32 -0700208 /* Check if the existing curve is valid.
209 */
210 status_t checkCurve() const {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800211 if (mType == TYPE_ID) return NO_ERROR;
212 if (this->size() < 2) {
213 ALOGE("curve must have at least 2 points");
214 return BAD_VALUE;
215 }
Andy Hungf3702642017-05-05 17:33:32 -0700216 if (first().first != MIN_CURVE_TIME || last().first != MAX_CURVE_TIME) {
217 ALOGE("curve must start at MIN_CURVE_TIME and end at MAX_CURVE_TIME");
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800218 return BAD_VALUE;
219 }
220 if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
221 for (const auto &pt : *this) {
Andy Hungf3702642017-05-05 17:33:32 -0700222 if (!(pt.second <= MAX_LOG_VOLUME) /* handle nan */) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800223 ALOGE("positive volume dbFS");
224 return BAD_VALUE;
225 }
226 }
227 } else {
228 for (const auto &pt : *this) {
Andy Hungf3702642017-05-05 17:33:32 -0700229 if (!(pt.second >= MIN_LINEAR_VOLUME)
230 || !(pt.second <= MAX_LINEAR_VOLUME) /* handle nan */) {
231 ALOGE("volume < MIN_LINEAR_VOLUME or > MAX_LINEAR_VOLUME");
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800232 return BAD_VALUE;
233 }
234 }
235 }
236 return NO_ERROR;
237 }
238
Andy Hungf3702642017-05-05 17:33:32 -0700239 /* Clamps the volume curve in the configuration to
240 * the valid range for log or linear scale.
241 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800242 void clampVolume() {
243 if ((mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
244 for (auto it = this->begin(); it != this->end(); ++it) {
Andy Hungf3702642017-05-05 17:33:32 -0700245 if (!(it->second <= MAX_LOG_VOLUME) /* handle nan */) {
246 it->second = MAX_LOG_VOLUME;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800247 }
248 }
249 } else {
250 for (auto it = this->begin(); it != this->end(); ++it) {
Andy Hungf3702642017-05-05 17:33:32 -0700251 if (!(it->second >= MIN_LINEAR_VOLUME) /* handle nan */) {
252 it->second = MIN_LINEAR_VOLUME;
253 } else if (!(it->second <= MAX_LINEAR_VOLUME)) {
254 it->second = MAX_LINEAR_VOLUME;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800255 }
256 }
257 }
258 }
259
260 /* scaleToStartVolume() is used to set the start volume of a
261 * new VolumeShaper curve, when replacing one VolumeShaper
262 * with another using the "join" (volume match) option.
263 *
264 * It works best for monotonic volume ramps or ducks.
265 */
266 void scaleToStartVolume(T volume) {
267 if (this->size() < 2) {
268 return;
269 }
270 const T startVolume = first().second;
271 const T endVolume = last().second;
272 if (endVolume == startVolume) {
273 // match with linear ramp
274 const T offset = volume - startVolume;
Andy Hungf3702642017-05-05 17:33:32 -0700275 static const T scale = 1.f / (MAX_CURVE_TIME - MIN_CURVE_TIME); // nominally 1.f
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800276 for (auto it = this->begin(); it != this->end(); ++it) {
Andy Hungf3702642017-05-05 17:33:32 -0700277 it->second = it->second + offset * (MAX_CURVE_TIME - it->first) * scale;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800278 }
279 } else {
280 const T scale = (volume - endVolume) / (startVolume - endVolume);
281 for (auto it = this->begin(); it != this->end(); ++it) {
282 it->second = scale * (it->second - endVolume) + endVolume;
283 }
284 }
285 clampVolume();
286 }
287
Andy Hung7d712bb2017-04-20 14:23:41 -0700288 // The parcel layout must match VolumeShaper.java
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800289 status_t writeToParcel(Parcel *parcel) const {
290 if (parcel == nullptr) return BAD_VALUE;
291 return parcel->writeInt32((int32_t)mType)
292 ?: parcel->writeInt32(mId)
293 ?: mType == TYPE_ID
294 ? NO_ERROR
295 : parcel->writeInt32((int32_t)mOptionFlags)
296 ?: parcel->writeDouble(mDurationMs)
297 ?: Interpolator<S, T>::writeToParcel(parcel);
298 }
299
300 status_t readFromParcel(const Parcel &parcel) {
301 int32_t type, optionFlags;
302 return parcel.readInt32(&type)
303 ?: setType((Type)type)
304 ?: parcel.readInt32(&mId)
305 ?: mType == TYPE_ID
306 ? NO_ERROR
307 : parcel.readInt32(&optionFlags)
308 ?: setOptionFlags((OptionFlag)optionFlags)
309 ?: parcel.readDouble(&mDurationMs)
310 ?: Interpolator<S, T>::readFromParcel(parcel)
311 ?: checkCurve();
312 }
313
Andy Hungf3702642017-05-05 17:33:32 -0700314 // Returns a string for debug printing.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800315 std::string toString() const {
316 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700317 ss << "VolumeShaper::Configuration{mType=" << static_cast<int32_t>(mType);
318 ss << ", mId=" << mId;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800319 if (mType != TYPE_ID) {
Andy Hungf3702642017-05-05 17:33:32 -0700320 ss << ", mOptionFlags=" << static_cast<int32_t>(mOptionFlags);
321 ss << ", mDurationMs=" << mDurationMs;
322 ss << ", " << Interpolator<S, T>::toString().c_str();
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800323 }
Andy Hungf3702642017-05-05 17:33:32 -0700324 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800325 return ss.str();
326 }
327
328 private:
Andy Hungf3702642017-05-05 17:33:32 -0700329 Type mType; // type of configuration
330 int32_t mId; // A valid id is >= 0.
331 OptionFlag mOptionFlags; // option flags for the configuration.
332 double mDurationMs; // duration, must be > 0; default is 1000 ms.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800333 }; // Configuration
334
Andy Hungf3702642017-05-05 17:33:32 -0700335 /* VolumeShaper::Operation expresses an operation to perform on the
336 * configuration (either explicitly specified or an id).
337 *
338 * This parallels the Java implementation and the enums must match.
339 * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
340 * details on the Java implementation.
341 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800342 class Operation : public RefBase {
343 public:
Andy Hungf3702642017-05-05 17:33:32 -0700344 // Must match with VolumeShaper.java.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800345 enum Flag : int32_t {
346 FLAG_NONE = 0,
Andy Hungf3702642017-05-05 17:33:32 -0700347 FLAG_REVERSE = (1 << 0), // the absence of this indicates "play"
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800348 FLAG_TERMINATE = (1 << 1),
349 FLAG_JOIN = (1 << 2),
350 FLAG_DELAY = (1 << 3),
Andy Hung4ef88d72017-02-21 19:47:53 -0800351 FLAG_CREATE_IF_NECESSARY = (1 << 4),
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800352
Andy Hung4ef88d72017-02-21 19:47:53 -0800353 FLAG_ALL = (FLAG_REVERSE | FLAG_TERMINATE | FLAG_JOIN | FLAG_DELAY
354 | FLAG_CREATE_IF_NECESSARY),
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800355 };
356
357 Operation()
Andy Hung4ef88d72017-02-21 19:47:53 -0800358 : Operation(FLAG_NONE, -1 /* replaceId */) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800359 }
360
Andy Hung7d712bb2017-04-20 14:23:41 -0700361 Operation(Flag flags, int replaceId)
Andy Hung4ef88d72017-02-21 19:47:53 -0800362 : Operation(flags, replaceId, std::numeric_limits<S>::quiet_NaN() /* xOffset */) {
363 }
364
Andy Hung7d712bb2017-04-20 14:23:41 -0700365 explicit Operation(const Operation &operation)
Andy Hung4ef88d72017-02-21 19:47:53 -0800366 : Operation(operation.mFlags, operation.mReplaceId, operation.mXOffset) {
367 }
368
Andy Hung7d712bb2017-04-20 14:23:41 -0700369 explicit Operation(const sp<Operation> &operation)
370 : Operation(*operation.get()) {
371 }
372
373 Operation(Flag flags, int replaceId, S xOffset)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800374 : mFlags(flags)
Andy Hung4ef88d72017-02-21 19:47:53 -0800375 , mReplaceId(replaceId)
376 , mXOffset(xOffset) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800377 }
378
379 int32_t getReplaceId() const {
380 return mReplaceId;
381 }
382
383 void setReplaceId(int32_t replaceId) {
384 mReplaceId = replaceId;
385 }
386
Andy Hung4ef88d72017-02-21 19:47:53 -0800387 S getXOffset() const {
388 return mXOffset;
389 }
390
391 void setXOffset(S xOffset) {
Andy Hungf3702642017-05-05 17:33:32 -0700392 mXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
Andy Hung4ef88d72017-02-21 19:47:53 -0800393 }
394
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800395 Flag getFlags() const {
396 return mFlags;
397 }
398
Andy Hungf3702642017-05-05 17:33:32 -0700399 /* xOffset is the position on the volume curve and may go backwards
400 * if you are in reverse mode. This must be in the range from
401 * [MIN_CURVE_TIME, MAX_CURVE_TIME].
402 *
403 * normalizedTime always increases as time or framecount increases.
404 * normalizedTime is nominally from MIN_CURVE_TIME to MAX_CURVE_TIME when
405 * running through the curve, but could be outside this range afterwards.
406 * If you are reversing, this means the position on the curve, or xOffset,
407 * is computed as MAX_CURVE_TIME - normalizedTime, clamped to
408 * [MIN_CURVE_TIME, MAX_CURVE_TIME].
409 */
410 void setNormalizedTime(S normalizedTime) {
411 setXOffset((mFlags & FLAG_REVERSE) != 0
412 ? MAX_CURVE_TIME - normalizedTime : normalizedTime);
413 }
414
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800415 status_t setFlags(Flag flags) {
416 if ((flags & ~FLAG_ALL) != 0) {
417 ALOGE("flags has invalid bits: %#x", flags);
418 return BAD_VALUE;
419 }
420 mFlags = flags;
421 return NO_ERROR;
422 }
423
424 status_t writeToParcel(Parcel *parcel) const {
425 if (parcel == nullptr) return BAD_VALUE;
426 return parcel->writeInt32((int32_t)mFlags)
Andy Hung4ef88d72017-02-21 19:47:53 -0800427 ?: parcel->writeInt32(mReplaceId)
428 ?: parcel->writeFloat(mXOffset);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800429 }
430
431 status_t readFromParcel(const Parcel &parcel) {
432 int32_t flags;
433 return parcel.readInt32(&flags)
434 ?: parcel.readInt32(&mReplaceId)
Andy Hung4ef88d72017-02-21 19:47:53 -0800435 ?: parcel.readFloat(&mXOffset)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800436 ?: setFlags((Flag)flags);
437 }
438
439 std::string toString() const {
440 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700441 ss << "VolumeShaper::Operation{mFlags=" << static_cast<int32_t>(mFlags) ;
442 ss << ", mReplaceId=" << mReplaceId;
443 ss << ", mXOffset=" << mXOffset;
444 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800445 return ss.str();
446 }
447
448 private:
Andy Hungf3702642017-05-05 17:33:32 -0700449 Flag mFlags; // operation to do
450 int32_t mReplaceId; // if >= 0 the id to remove in a replace operation.
451 S mXOffset; // position in the curve to set if a valid number (not nan)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800452 }; // Operation
453
Andy Hungf3702642017-05-05 17:33:32 -0700454 /* VolumeShaper.State is returned when requesting the last
455 * state of the VolumeShaper.
456 *
457 * This parallels the Java implementation.
458 * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
459 * details on the Java implementation.
460 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800461 class State : public RefBase {
462 public:
Andy Hung7d712bb2017-04-20 14:23:41 -0700463 State(T volume, S xOffset)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800464 : mVolume(volume)
465 , mXOffset(xOffset) {
466 }
467
468 State()
Andy Hungf3702642017-05-05 17:33:32 -0700469 : State(NAN, NAN) { }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800470
471 T getVolume() const {
472 return mVolume;
473 }
474
475 void setVolume(T volume) {
476 mVolume = volume;
477 }
478
479 S getXOffset() const {
480 return mXOffset;
481 }
482
483 void setXOffset(S xOffset) {
484 mXOffset = xOffset;
485 }
486
487 status_t writeToParcel(Parcel *parcel) const {
488 if (parcel == nullptr) return BAD_VALUE;
489 return parcel->writeFloat(mVolume)
490 ?: parcel->writeFloat(mXOffset);
491 }
492
493 status_t readFromParcel(const Parcel &parcel) {
494 return parcel.readFloat(&mVolume)
495 ?: parcel.readFloat(&mXOffset);
496 }
497
498 std::string toString() const {
499 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700500 ss << "VolumeShaper::State{mVolume=" << mVolume;
501 ss << ", mXOffset=" << mXOffset;
502 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800503 return ss.str();
504 }
505
506 private:
Andy Hungf3702642017-05-05 17:33:32 -0700507 T mVolume; // linear volume in the range MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME
508 S mXOffset; // position on curve expressed from MIN_CURVE_TIME to MAX_CURVE_TIME
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800509 }; // State
510
Andy Hungf3702642017-05-05 17:33:32 -0700511 // Internal helper class to do an affine transform for time and amplitude scaling.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800512 template <typename R>
513 class Translate {
514 public:
515 Translate()
516 : mOffset(0)
517 , mScale(1) {
518 }
519
520 R getOffset() const {
521 return mOffset;
522 }
523
524 void setOffset(R offset) {
525 mOffset = offset;
526 }
527
528 R getScale() const {
529 return mScale;
530 }
531
532 void setScale(R scale) {
533 mScale = scale;
534 }
535
536 R operator()(R in) const {
537 return mScale * (in - mOffset);
538 }
539
540 std::string toString() const {
541 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700542 ss << "VolumeShaper::Translate{mOffset=" << mOffset;
543 ss << ", mScale=" << mScale;
544 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800545 return ss.str();
546 }
547
548 private:
549 R mOffset;
550 R mScale;
551 }; // Translate
552
553 static int64_t convertTimespecToUs(const struct timespec &tv)
554 {
555 return tv.tv_sec * 1000000ll + tv.tv_nsec / 1000;
556 }
557
558 // current monotonic time in microseconds.
559 static int64_t getNowUs()
560 {
561 struct timespec tv;
562 if (clock_gettime(CLOCK_MONOTONIC, &tv) != 0) {
563 return 0; // system is really sick, just return 0 for consistency.
564 }
565 return convertTimespecToUs(tv);
566 }
567
Andy Hungf3702642017-05-05 17:33:32 -0700568 /* Native implementation of VolumeShaper. This is NOT mirrored
569 * on the Java side, so we don't need to mimic Java side layout
570 * and data; furthermore, this isn't refcounted as a "RefBase" object.
571 *
572 * Since we pass configuration and operation as shared pointers (like
573 * Java) there is a potential risk that the caller may modify
574 * these after delivery.
575 */
Andy Hung7d712bb2017-04-20 14:23:41 -0700576 VolumeShaper(
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800577 const sp<VolumeShaper::Configuration> &configuration,
578 const sp<VolumeShaper::Operation> &operation)
579 : mConfiguration(configuration) // we do not make a copy
580 , mOperation(operation) // ditto
581 , mStartFrame(-1)
582 , mLastVolume(T(1))
Andy Hungf3702642017-05-05 17:33:32 -0700583 , mLastXOffset(MIN_CURVE_TIME)
584 , mDelayXOffset(MIN_CURVE_TIME) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800585 if (configuration.get() != nullptr
586 && (getFlags() & VolumeShaper::Operation::FLAG_DELAY) == 0) {
587 mLastVolume = configuration->first().second;
588 }
589 }
590
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800591 // We allow a null operation here, though VolumeHandler always provides one.
592 VolumeShaper::Operation::Flag getFlags() const {
593 return mOperation == nullptr
Andy Hungf3702642017-05-05 17:33:32 -0700594 ? VolumeShaper::Operation::FLAG_NONE : mOperation->getFlags();
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800595 }
596
Andy Hungf3702642017-05-05 17:33:32 -0700597 /* Returns the last volume and xoffset reported to the AudioFlinger.
598 * If the VolumeShaper has not been started, compute what the volume
599 * should be based on the initial offset specified.
600 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800601 sp<VolumeShaper::State> getState() const {
Andy Hungf3702642017-05-05 17:33:32 -0700602 if (!isStarted()) {
603 const T volume = computeVolumeFromXOffset(mDelayXOffset);
604 VS_LOG("delayed VolumeShaper, using cached offset:%f for volume:%f",
605 mDelayXOffset, volume);
606 return new VolumeShaper::State(volume, mDelayXOffset);
607 } else {
608 return new VolumeShaper::State(mLastVolume, mLastXOffset);
609 }
610 }
611
612 S getDelayXOffset() const {
613 return mDelayXOffset;
Andy Hung4ef88d72017-02-21 19:47:53 -0800614 }
615
616 void setDelayXOffset(S xOffset) {
Andy Hungf3702642017-05-05 17:33:32 -0700617 mDelayXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800618 }
619
Andy Hung39399b62017-04-21 15:07:45 -0700620 bool isStarted() const {
621 return mStartFrame >= 0;
622 }
623
Andy Hungf3702642017-05-05 17:33:32 -0700624 /* getVolume() updates the last volume/xoffset state so it is not
625 * const, even though logically it may be viewed as const.
626 */
Andy Hung10cbff12017-02-21 17:30:14 -0800627 std::pair<T /* volume */, bool /* active */> getVolume(
628 int64_t trackFrameCount, double trackSampleRate) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800629 if ((getFlags() & VolumeShaper::Operation::FLAG_DELAY) != 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700630 // We haven't had PLAY called yet, so just return the value
631 // as if PLAY were called just now.
632 VS_LOG("delayed VolumeShaper, using cached offset %f", mDelayXOffset);
633 const T volume = computeVolumeFromXOffset(mDelayXOffset);
634 return std::make_pair(volume, false);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800635 }
636 const bool clockTime = (mConfiguration->getOptionFlags()
637 & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
638 const int64_t frameCount = clockTime ? getNowUs() : trackFrameCount;
639 const double sampleRate = clockTime ? 1000000 : trackSampleRate;
640
641 if (mStartFrame < 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700642 updatePosition(frameCount, sampleRate, mDelayXOffset);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800643 mStartFrame = frameCount;
644 }
645 VS_LOG("frameCount: %lld", (long long)frameCount);
Andy Hungf3702642017-05-05 17:33:32 -0700646 const S x = mXTranslate((T)frameCount);
647 VS_LOG("translation to normalized time: %f", x);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800648
Andy Hungf3702642017-05-05 17:33:32 -0700649 std::tuple<T /* volume */, S /* position */, bool /* active */> vt =
650 computeStateFromNormalizedTime(x);
651
652 mLastVolume = std::get<0>(vt);
653 mLastXOffset = std::get<1>(vt);
654 const bool active = std::get<2>(vt);
655 VS_LOG("rescaled time:%f volume:%f xOffset:%f active:%s",
656 x, mLastVolume, mLastXOffset, active ? "true" : "false");
657 return std::make_pair(mLastVolume, active);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800658 }
659
660 std::string toString() const {
661 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700662 ss << "VolumeShaper{mStartFrame=" << mStartFrame;
663 ss << ", mXTranslate=" << mXTranslate.toString().c_str();
664 ss << ", mConfiguration=" <<
665 (mConfiguration.get() == nullptr
666 ? "nullptr" : mConfiguration->toString().c_str());
667 ss << ", mOperation=" <<
668 (mOperation.get() == nullptr
669 ? "nullptr" : mOperation->toString().c_str());
670 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800671 return ss.str();
672 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800673
Andy Hungf3702642017-05-05 17:33:32 -0700674 Translate<S> mXTranslate; // translation from frames (usec for clock time) to normalized time.
Andy Hung4ef88d72017-02-21 19:47:53 -0800675 sp<VolumeShaper::Configuration> mConfiguration;
676 sp<VolumeShaper::Operation> mOperation;
Andy Hungf3702642017-05-05 17:33:32 -0700677
678private:
Andy Hung4ef88d72017-02-21 19:47:53 -0800679 int64_t mStartFrame; // starting frame, non-negative when started (in usec for clock time)
680 T mLastVolume; // last computed interpolated volume (y-axis)
681 S mLastXOffset; // last computed interpolated xOffset/time (x-axis)
Andy Hungf3702642017-05-05 17:33:32 -0700682 S mDelayXOffset; // xOffset to use for first invocation of VolumeShaper.
683
684 // Called internally to adjust mXTranslate for first time start.
685 void updatePosition(int64_t startFrame, double sampleRate, S xOffset) {
686 double scale = (mConfiguration->last().first - mConfiguration->first().first)
687 / (mConfiguration->getDurationMs() * 0.001 * sampleRate);
688 const double minScale = 1. / static_cast<double>(INT64_MAX);
689 scale = std::max(scale, minScale);
690 VS_LOG("update position: scale %lf frameCount:%lld, sampleRate:%lf, xOffset:%f",
691 scale, (long long) startFrame, sampleRate, xOffset);
692
693 S normalizedTime = (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) != 0 ?
694 MAX_CURVE_TIME - xOffset : xOffset;
695 mXTranslate.setOffset(static_cast<float>(static_cast<double>(startFrame)
696 - static_cast<double>(normalizedTime) / scale));
697 mXTranslate.setScale(static_cast<float>(scale));
698 VS_LOG("translate: %s", mXTranslate.toString().c_str());
699 }
700
701 T computeVolumeFromXOffset(S xOffset) const {
702 const T unscaledVolume = mConfiguration->findY(xOffset);
703 const T volume = mConfiguration->adjustVolume(unscaledVolume); // handle log scale
704 VS_LOG("computeVolumeFromXOffset %f -> %f -> %f", xOffset, unscaledVolume, volume);
705 return volume;
706 }
707
708 std::tuple<T /* volume */, S /* position */, bool /* active */>
709 computeStateFromNormalizedTime(S x) const {
710 bool active = true;
711 // handle reversal of position
712 if (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) {
713 x = MAX_CURVE_TIME - x;
714 VS_LOG("reversing to %f", x);
715 if (x < MIN_CURVE_TIME) {
716 x = MIN_CURVE_TIME;
717 active = false; // at the end
718 } else if (x > MAX_CURVE_TIME) {
719 x = MAX_CURVE_TIME; //early
720 }
721 } else {
722 if (x < MIN_CURVE_TIME) {
723 x = MIN_CURVE_TIME; // early
724 } else if (x > MAX_CURVE_TIME) {
725 x = MAX_CURVE_TIME;
726 active = false; // at end
727 }
728 }
729 const S xOffset = x;
730 const T volume = computeVolumeFromXOffset(xOffset);
731 return std::make_tuple(volume, xOffset, active);
732 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800733}; // VolumeShaper
734
Andy Hungf3702642017-05-05 17:33:32 -0700735/* VolumeHandler combines the volume factors of multiple VolumeShapers associated
736 * with a player. It is thread safe by synchronizing all public methods.
737 *
738 * This is a native-only implementation.
739 *
740 * The server side VolumeHandler is used to maintain a list of volume handlers,
741 * keep state, and obtain volume.
742 *
743 * The client side VolumeHandler is used to maintain a list of volume handlers,
744 * keep some partial state, and restore if the server dies.
745 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800746class VolumeHandler : public RefBase {
747public:
748 using S = float;
749 using T = float;
750
Andy Hung4ef88d72017-02-21 19:47:53 -0800751 // A volume handler which just keeps track of active VolumeShapers does not need sampleRate.
752 VolumeHandler()
753 : VolumeHandler(0 /* sampleRate */) {
754 }
755
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800756 explicit VolumeHandler(uint32_t sampleRate)
757 : mSampleRate((double)sampleRate)
Andy Hung4ef88d72017-02-21 19:47:53 -0800758 , mLastFrame(0)
Andy Hungf3702642017-05-05 17:33:32 -0700759 , mVolumeShaperIdCounter(VolumeShaper::kSystemVolumeShapersMax)
Andy Hungda540db2017-04-20 14:06:17 -0700760 , mLastVolume(1.f, false) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800761 }
762
763 VolumeShaper::Status applyVolumeShaper(
764 const sp<VolumeShaper::Configuration> &configuration,
Andy Hungf3702642017-05-05 17:33:32 -0700765 const sp<VolumeShaper::Operation> &operation_in) {
766 // make a local copy of operation, as we modify it.
767 sp<VolumeShaper::Operation> operation(new VolumeShaper::Operation(operation_in));
Andy Hung10cbff12017-02-21 17:30:14 -0800768 VS_LOG("applyVolumeShaper:configuration: %s", configuration->toString().c_str());
769 VS_LOG("applyVolumeShaper:operation: %s", operation->toString().c_str());
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800770 AutoMutex _l(mLock);
771 if (configuration == nullptr) {
772 ALOGE("null configuration");
773 return VolumeShaper::Status(BAD_VALUE);
774 }
775 if (operation == nullptr) {
776 ALOGE("null operation");
777 return VolumeShaper::Status(BAD_VALUE);
778 }
779 const int32_t id = configuration->getId();
780 if (id < 0) {
781 ALOGE("negative id: %d", id);
782 return VolumeShaper::Status(BAD_VALUE);
783 }
784 VS_LOG("applyVolumeShaper id: %d", id);
785
786 switch (configuration->getType()) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800787 case VolumeShaper::Configuration::TYPE_SCALE: {
788 const int replaceId = operation->getReplaceId();
789 if (replaceId >= 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700790 VS_LOG("replacing %d", replaceId);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800791 auto replaceIt = findId_l(replaceId);
792 if (replaceIt == mVolumeShapers.end()) {
793 ALOGW("cannot find replace id: %d", replaceId);
794 } else {
Andy Hungf3702642017-05-05 17:33:32 -0700795 if ((operation->getFlags() & VolumeShaper::Operation::FLAG_JOIN) != 0) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800796 // For join, we scale the start volume of the current configuration
797 // to match the last-used volume of the replacing VolumeShaper.
798 auto state = replaceIt->getState();
Andy Hungf3702642017-05-05 17:33:32 -0700799 ALOGD("join: state:%s", state->toString().c_str());
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800800 if (state->getXOffset() >= 0) { // valid
801 const T volume = state->getVolume();
802 ALOGD("join: scaling start volume to %f", volume);
803 configuration->scaleToStartVolume(volume);
804 }
805 }
806 (void)mVolumeShapers.erase(replaceIt);
807 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800808 operation->setReplaceId(-1);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800809 }
810 // check if we have another of the same id.
811 auto oldIt = findId_l(id);
812 if (oldIt != mVolumeShapers.end()) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800813 if ((operation->getFlags()
814 & VolumeShaper::Operation::FLAG_CREATE_IF_NECESSARY) != 0) {
815 // TODO: move the case to a separate function.
816 goto HANDLE_TYPE_ID; // no need to create, take over existing id.
817 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800818 ALOGW("duplicate id, removing old %d", id);
819 (void)mVolumeShapers.erase(oldIt);
820 }
Andy Hungf3702642017-05-05 17:33:32 -0700821
822 /* Check if too many application VolumeShapers (with id >= kSystemVolumeShapersMax).
823 * We check on the server side to ensure synchronization and robustness.
824 *
825 * This shouldn't fail on a replace command unless the replaced id is
826 * already invalid (which *should* be checked in the Java layer).
827 */
828 if (id >= VolumeShaper::kSystemVolumeShapersMax
829 && numberOfUserVolumeShapers_l() >= VolumeShaper::kUserVolumeShapersMax) {
830 ALOGW("Too many app VolumeShapers, cannot add to VolumeHandler");
831 return VolumeShaper::Status(INVALID_OPERATION);
832 }
833
834 // create new VolumeShaper with default behavior.
835 mVolumeShapers.emplace_back(configuration, new VolumeShaper::Operation());
836 VS_LOG("after adding, number of volumeShapers:%zu", mVolumeShapers.size());
Andy Hung10cbff12017-02-21 17:30:14 -0800837 }
838 // fall through to handle the operation
Andy Hung4ef88d72017-02-21 19:47:53 -0800839 HANDLE_TYPE_ID:
Andy Hung10cbff12017-02-21 17:30:14 -0800840 case VolumeShaper::Configuration::TYPE_ID: {
841 VS_LOG("trying to find id: %d", id);
842 auto it = findId_l(id);
843 if (it == mVolumeShapers.end()) {
844 VS_LOG("couldn't find id: %d", id);
845 return VolumeShaper::Status(INVALID_OPERATION);
846 }
Andy Hungf3702642017-05-05 17:33:32 -0700847 if ((operation->getFlags() & VolumeShaper::Operation::FLAG_TERMINATE) != 0) {
Andy Hung10cbff12017-02-21 17:30:14 -0800848 VS_LOG("terminate id: %d", id);
849 mVolumeShapers.erase(it);
850 break;
851 }
852 const bool clockTime = (it->mConfiguration->getOptionFlags()
853 & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
854 if ((it->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) !=
855 (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE)) {
Andy Hungf3702642017-05-05 17:33:32 -0700856 if (it->isStarted()) {
857 const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
858 const S x = it->mXTranslate((T)frameCount);
859 VS_LOG("reverse normalizedTime: %f", x);
860 // reflect position
861 S target = MAX_CURVE_TIME - x;
862 if (target < MIN_CURVE_TIME) {
863 VS_LOG("clamp to start - begin immediately");
864 target = MIN_CURVE_TIME;
865 }
866 VS_LOG("reverse normalizedTime target: %f", target);
867 it->mXTranslate.setOffset(it->mXTranslate.getOffset()
868 + (x - target) / it->mXTranslate.getScale());
Andy Hung10cbff12017-02-21 17:30:14 -0800869 }
Andy Hungf3702642017-05-05 17:33:32 -0700870 // if not started, the delay offset doesn't change.
Andy Hung10cbff12017-02-21 17:30:14 -0800871 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800872 const S xOffset = operation->getXOffset();
873 if (!std::isnan(xOffset)) {
Andy Hungf3702642017-05-05 17:33:32 -0700874 if (it->isStarted()) {
875 const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
876 const S x = it->mXTranslate((T)frameCount);
877 VS_LOG("normalizedTime translation: %f", x);
878 const S target =
879 (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) != 0 ?
880 MAX_CURVE_TIME - xOffset : xOffset;
881 VS_LOG("normalizedTime target x offset: %f", target);
882 it->mXTranslate.setOffset(it->mXTranslate.getOffset()
883 + (x - target) / it->mXTranslate.getScale());
884 } else {
885 it->setDelayXOffset(xOffset);
886 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800887 }
Andy Hung10cbff12017-02-21 17:30:14 -0800888 it->mOperation = operation; // replace the operation
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800889 } break;
890 }
891 return VolumeShaper::Status(id);
892 }
893
894 sp<VolumeShaper::State> getVolumeShaperState(int id) {
895 AutoMutex _l(mLock);
896 auto it = findId_l(id);
897 if (it == mVolumeShapers.end()) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800898 VS_LOG("cannot find state for id: %d", id);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800899 return nullptr;
900 }
901 return it->getState();
902 }
903
Andy Hungf3702642017-05-05 17:33:32 -0700904 /* getVolume() is not const, as it updates internal state.
905 * Once called, any VolumeShapers not already started begin running.
906 */
Andy Hung10cbff12017-02-21 17:30:14 -0800907 std::pair<T /* volume */, bool /* active */> getVolume(int64_t trackFrameCount) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800908 AutoMutex _l(mLock);
909 mLastFrame = trackFrameCount;
910 T volume(1);
Andy Hung10cbff12017-02-21 17:30:14 -0800911 size_t activeCount = 0;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800912 for (auto it = mVolumeShapers.begin(); it != mVolumeShapers.end();) {
Andy Hungf3702642017-05-05 17:33:32 -0700913 const std::pair<T, bool> shaperVolume =
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800914 it->getVolume(trackFrameCount, mSampleRate);
915 volume *= shaperVolume.first;
Andy Hung10cbff12017-02-21 17:30:14 -0800916 activeCount += shaperVolume.second;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800917 ++it;
918 }
Andy Hungda540db2017-04-20 14:06:17 -0700919 mLastVolume = std::make_pair(volume, activeCount != 0);
Andy Hungf3702642017-05-05 17:33:32 -0700920 VS_LOG("getVolume: <%f, %s>", mLastVolume.first, mLastVolume.second ? "true" : "false");
Andy Hungda540db2017-04-20 14:06:17 -0700921 return mLastVolume;
922 }
923
Andy Hungf3702642017-05-05 17:33:32 -0700924 /* Used by a client side VolumeHandler to ensure all the VolumeShapers
925 * indicate that they have been started. Upon a change in audioserver
926 * output sink, this information is used for restoration of the server side
927 * VolumeHandler.
928 */
Andy Hung39399b62017-04-21 15:07:45 -0700929 void setStarted() {
930 (void)getVolume(mLastFrame); // getVolume() will start the individual VolumeShapers.
931 }
932
Andy Hungda540db2017-04-20 14:06:17 -0700933 std::pair<T /* volume */, bool /* active */> getLastVolume() const {
934 AutoMutex _l(mLock);
935 return mLastVolume;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800936 }
937
938 std::string toString() const {
939 AutoMutex _l(mLock);
940 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700941 ss << "VolumeHandler{mSampleRate=" << mSampleRate;
942 ss << ", mLastFrame=" << mLastFrame;
943 ss << ", mVolumeShapers={";
944 bool first = true;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800945 for (const auto &shaper : mVolumeShapers) {
Andy Hungf3702642017-05-05 17:33:32 -0700946 if (first) {
947 first = false;
948 } else {
949 ss << ", ";
950 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800951 ss << shaper.toString().c_str();
952 }
Andy Hungf3702642017-05-05 17:33:32 -0700953 ss << "}}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800954 return ss.str();
955 }
956
Andy Hung39399b62017-04-21 15:07:45 -0700957 void forall(const std::function<VolumeShaper::Status (const VolumeShaper &)> &lambda) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800958 AutoMutex _l(mLock);
Andy Hungda540db2017-04-20 14:06:17 -0700959 VS_LOG("forall: mVolumeShapers.size() %zu", mVolumeShapers.size());
Andy Hung4ef88d72017-02-21 19:47:53 -0800960 for (const auto &shaper : mVolumeShapers) {
Andy Hung39399b62017-04-21 15:07:45 -0700961 VolumeShaper::Status status = lambda(shaper);
962 VS_LOG("forall applying lambda on shaper (%p): %d", &shaper, (int)status);
Andy Hung4ef88d72017-02-21 19:47:53 -0800963 }
964 }
965
966 void reset() {
967 AutoMutex _l(mLock);
968 mVolumeShapers.clear();
Andy Hung7d712bb2017-04-20 14:23:41 -0700969 mLastFrame = 0;
Andy Hung4ef88d72017-02-21 19:47:53 -0800970 // keep mVolumeShaperIdCounter as is.
971 }
972
Andy Hungf3702642017-05-05 17:33:32 -0700973 /* Sets the configuration id if necessary - This is based on the counter
974 * internal to the VolumeHandler.
975 */
Andy Hung4ef88d72017-02-21 19:47:53 -0800976 void setIdIfNecessary(const sp<VolumeShaper::Configuration> &configuration) {
977 if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
978 const int id = configuration->getId();
979 if (id == -1) {
980 // Reassign to a unique id, skipping system ids.
981 AutoMutex _l(mLock);
982 while (true) {
983 if (mVolumeShaperIdCounter == INT32_MAX) {
Andy Hungf3702642017-05-05 17:33:32 -0700984 mVolumeShaperIdCounter = VolumeShaper::kSystemVolumeShapersMax;
Andy Hung4ef88d72017-02-21 19:47:53 -0800985 } else {
986 ++mVolumeShaperIdCounter;
987 }
988 if (findId_l(mVolumeShaperIdCounter) != mVolumeShapers.end()) {
989 continue; // collision with an existing id.
990 }
991 configuration->setId(mVolumeShaperIdCounter);
992 ALOGD("setting id to %d", mVolumeShaperIdCounter);
993 break;
994 }
995 }
996 }
997 }
998
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800999private:
1000 std::list<VolumeShaper>::iterator findId_l(int32_t id) {
1001 std::list<VolumeShaper>::iterator it = mVolumeShapers.begin();
1002 for (; it != mVolumeShapers.end(); ++it) {
1003 if (it->mConfiguration->getId() == id) {
1004 break;
1005 }
1006 }
1007 return it;
1008 }
1009
Andy Hungf3702642017-05-05 17:33:32 -07001010 size_t numberOfUserVolumeShapers_l() const {
1011 size_t count = 0;
1012 for (const auto &shaper : mVolumeShapers) {
1013 count += (shaper.mConfiguration->getId() >= VolumeShaper::kSystemVolumeShapersMax);
1014 }
1015 return count;
1016 }
1017
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001018 mutable Mutex mLock;
1019 double mSampleRate; // in samples (frames) per second
Andy Hung7d712bb2017-04-20 14:23:41 -07001020 int64_t mLastFrame; // logging purpose only, 0 on start
Andy Hung4ef88d72017-02-21 19:47:53 -08001021 int32_t mVolumeShaperIdCounter; // a counter to return a unique volume shaper id.
Andy Hungda540db2017-04-20 14:06:17 -07001022 std::pair<T /* volume */, bool /* active */> mLastVolume;
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001023 std::list<VolumeShaper> mVolumeShapers; // list provides stable iterators on erase
1024}; // VolumeHandler
1025
1026} // namespace android
1027
1028#pragma pop_macro("LOG_TAG")
1029
1030#endif // ANDROID_VOLUME_SHAPER_H