blob: 302641ff1469e0439639ad73faa752ab1d5bff46 [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:
Andy Hungf3702642017-05-05 17:33:32 -070051 // S and T are like template typenames (matching the Interpolator<S, T>)
52 using S = float; // time type
53 using T = float; // volume type
Andy Hung9fc8b5c2017-01-24 13:36:48 -080054
Andy Hungf3702642017-05-05 17:33:32 -070055// Curve and dimension information
56// TODO: member static const or constexpr float initialization not permitted in C++11
57#define MIN_CURVE_TIME 0.f // type S: start of VolumeShaper curve (normalized)
58#define MAX_CURVE_TIME 1.f // type S: end of VolumeShaper curve (normalized)
59#define MIN_LINEAR_VOLUME 0.f // type T: silence / mute audio
60#define MAX_LINEAR_VOLUME 1.f // type T: max volume, unity gain
61#define MAX_LOG_VOLUME 0.f // type T: max volume, unity gain in dBFS
Andy Hung9fc8b5c2017-01-24 13:36:48 -080062
Andy Hungf3702642017-05-05 17:33:32 -070063 /* kSystemVolumeShapersMax is the maximum number of system VolumeShapers.
64 * Each system VolumeShapers has a predefined Id, which ranges from 0
65 * to kSystemVolumeShapersMax - 1 and is unique for its usage.
66 *
67 * "1" is reserved for system ducking.
68 */
69 static const int kSystemVolumeShapersMax = 16;
70
71 /* kUserVolumeShapersMax is the maximum number of application
72 * VolumeShapers for a player/track. Application VolumeShapers are
73 * assigned on creation by the client, and have Ids ranging
74 * from kSystemVolumeShapersMax to INT32_MAX.
75 *
76 * The number of user/application volume shapers is independent to the
77 * system volume shapers. If an application tries to create more than
78 * kUserVolumeShapersMax to a player, then the apply() will fail.
79 * This prevents exhausting server side resources by a potentially malicious
80 * application.
81 */
82 static const int kUserVolumeShapersMax = 16;
83
84 /* VolumeShaper::Status is equivalent to status_t if negative
85 * but if non-negative represents the id operated on.
86 * It must be expressible as an int32_t for binder purposes.
87 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -080088 using Status = status_t;
89
Andy Hungf3702642017-05-05 17:33:32 -070090 // Local definition for clamp as std::clamp is included in C++17 only.
91 // TODO: use the std::clamp version when Android build uses C++17.
92 template<typename R>
93 static constexpr const R &clamp(const R &v, const R &lo, const R &hi) {
94 return (v < lo) ? lo : (hi < v) ? hi : v;
95 }
96
97 /* VolumeShaper.Configuration derives from the Interpolator class and adds
98 * parameters relating to the volume shape.
99 *
100 * This parallels the Java implementation and the enums must match.
101 * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
102 * details on the Java implementation.
103 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800104 class Configuration : public Interpolator<S, T>, public RefBase {
105 public:
Andy Hungf3702642017-05-05 17:33:32 -0700106 // Must match with VolumeShaper.java in frameworks/base.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800107 enum Type : int32_t {
108 TYPE_ID,
109 TYPE_SCALE,
110 };
111
Andy Hungf3702642017-05-05 17:33:32 -0700112 // Must match with VolumeShaper.java in frameworks/base.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800113 enum OptionFlag : int32_t {
114 OPTION_FLAG_NONE = 0,
115 OPTION_FLAG_VOLUME_IN_DBFS = (1 << 0),
116 OPTION_FLAG_CLOCK_TIME = (1 << 1),
117
118 OPTION_FLAG_ALL = (OPTION_FLAG_VOLUME_IN_DBFS | OPTION_FLAG_CLOCK_TIME),
119 };
120
Andy Hungf3702642017-05-05 17:33:32 -0700121 // Bring from base class; must match with VolumeShaper.java in frameworks/base.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800122 using InterpolatorType = Interpolator<S, T>::InterpolatorType;
123
124 Configuration()
125 : Interpolator<S, T>()
Colin Cross11280a12017-05-02 10:32:56 -0700126 , RefBase()
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800127 , mType(TYPE_SCALE)
Colin Cross4e399992017-04-27 16:15:51 -0700128 , mId(-1)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800129 , mOptionFlags(OPTION_FLAG_NONE)
Colin Cross4e399992017-04-27 16:15:51 -0700130 , mDurationMs(1000.) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800131 }
132
Andy Hung7d712bb2017-04-20 14:23:41 -0700133 explicit Configuration(const Configuration &configuration)
Andy Hung10cbff12017-02-21 17:30:14 -0800134 : Interpolator<S, T>(*static_cast<const Interpolator<S, T> *>(&configuration))
Colin Cross11280a12017-05-02 10:32:56 -0700135 , RefBase()
Andy Hung10cbff12017-02-21 17:30:14 -0800136 , mType(configuration.mType)
Colin Cross4e399992017-04-27 16:15:51 -0700137 , mId(configuration.mId)
Andy Hung10cbff12017-02-21 17:30:14 -0800138 , mOptionFlags(configuration.mOptionFlags)
Colin Cross4e399992017-04-27 16:15:51 -0700139 , mDurationMs(configuration.mDurationMs) {
Andy Hung10cbff12017-02-21 17:30:14 -0800140 }
141
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800142 Type getType() const {
143 return mType;
144 }
145
146 status_t setType(Type type) {
147 switch (type) {
148 case TYPE_ID:
149 case TYPE_SCALE:
150 mType = type;
151 return NO_ERROR;
152 default:
153 ALOGE("invalid Type: %d", type);
154 return BAD_VALUE;
155 }
156 }
157
158 OptionFlag getOptionFlags() const {
159 return mOptionFlags;
160 }
161
162 status_t setOptionFlags(OptionFlag optionFlags) {
163 if ((optionFlags & ~OPTION_FLAG_ALL) != 0) {
164 ALOGE("optionFlags has invalid bits: %#x", optionFlags);
165 return BAD_VALUE;
166 }
167 mOptionFlags = optionFlags;
168 return NO_ERROR;
169 }
170
171 double getDurationMs() const {
172 return mDurationMs;
173 }
174
Andy Hungf3702642017-05-05 17:33:32 -0700175 status_t setDurationMs(double durationMs) {
176 if (durationMs > 0.) {
177 mDurationMs = durationMs;
178 return NO_ERROR;
179 }
180 // zero, negative, or nan. These values not possible from Java.
181 return BAD_VALUE;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800182 }
183
184 int32_t getId() const {
185 return mId;
186 }
187
188 void setId(int32_t id) {
Andy Hungf3702642017-05-05 17:33:32 -0700189 // We permit a negative id here (representing invalid).
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800190 mId = id;
191 }
192
Andy Hungf3702642017-05-05 17:33:32 -0700193 /* Adjust the volume to be in linear range from MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME
194 * and compensate for log dbFS volume as needed.
195 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800196 T adjustVolume(T volume) const {
197 if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700198 const T out = powf(10.f, volume / 10.f);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800199 VS_LOG("in: %f out: %f", volume, out);
200 volume = out;
201 }
Andy Hungf3702642017-05-05 17:33:32 -0700202 return clamp(volume, MIN_LINEAR_VOLUME /* lo */, MAX_LINEAR_VOLUME /* hi */);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800203 }
204
Andy Hungf3702642017-05-05 17:33:32 -0700205 /* Check if the existing curve is valid.
206 */
207 status_t checkCurve() const {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800208 if (mType == TYPE_ID) return NO_ERROR;
209 if (this->size() < 2) {
210 ALOGE("curve must have at least 2 points");
211 return BAD_VALUE;
212 }
Andy Hungf3702642017-05-05 17:33:32 -0700213 if (first().first != MIN_CURVE_TIME || last().first != MAX_CURVE_TIME) {
214 ALOGE("curve must start at MIN_CURVE_TIME and end at MAX_CURVE_TIME");
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800215 return BAD_VALUE;
216 }
217 if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
218 for (const auto &pt : *this) {
Andy Hungf3702642017-05-05 17:33:32 -0700219 if (!(pt.second <= MAX_LOG_VOLUME) /* handle nan */) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800220 ALOGE("positive volume dbFS");
221 return BAD_VALUE;
222 }
223 }
224 } else {
225 for (const auto &pt : *this) {
Andy Hungf3702642017-05-05 17:33:32 -0700226 if (!(pt.second >= MIN_LINEAR_VOLUME)
227 || !(pt.second <= MAX_LINEAR_VOLUME) /* handle nan */) {
228 ALOGE("volume < MIN_LINEAR_VOLUME or > MAX_LINEAR_VOLUME");
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800229 return BAD_VALUE;
230 }
231 }
232 }
233 return NO_ERROR;
234 }
235
Andy Hungf3702642017-05-05 17:33:32 -0700236 /* Clamps the volume curve in the configuration to
237 * the valid range for log or linear scale.
238 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800239 void clampVolume() {
240 if ((mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
241 for (auto it = this->begin(); it != this->end(); ++it) {
Andy Hungf3702642017-05-05 17:33:32 -0700242 if (!(it->second <= MAX_LOG_VOLUME) /* handle nan */) {
243 it->second = MAX_LOG_VOLUME;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800244 }
245 }
246 } else {
247 for (auto it = this->begin(); it != this->end(); ++it) {
Andy Hungf3702642017-05-05 17:33:32 -0700248 if (!(it->second >= MIN_LINEAR_VOLUME) /* handle nan */) {
249 it->second = MIN_LINEAR_VOLUME;
250 } else if (!(it->second <= MAX_LINEAR_VOLUME)) {
251 it->second = MAX_LINEAR_VOLUME;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800252 }
253 }
254 }
255 }
256
257 /* scaleToStartVolume() is used to set the start volume of a
258 * new VolumeShaper curve, when replacing one VolumeShaper
259 * with another using the "join" (volume match) option.
260 *
261 * It works best for monotonic volume ramps or ducks.
262 */
263 void scaleToStartVolume(T volume) {
264 if (this->size() < 2) {
265 return;
266 }
267 const T startVolume = first().second;
268 const T endVolume = last().second;
269 if (endVolume == startVolume) {
270 // match with linear ramp
271 const T offset = volume - startVolume;
Andy Hungf3702642017-05-05 17:33:32 -0700272 static const T scale = 1.f / (MAX_CURVE_TIME - MIN_CURVE_TIME); // nominally 1.f
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800273 for (auto it = this->begin(); it != this->end(); ++it) {
Andy Hungf3702642017-05-05 17:33:32 -0700274 it->second = it->second + offset * (MAX_CURVE_TIME - it->first) * scale;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800275 }
276 } else {
277 const T scale = (volume - endVolume) / (startVolume - endVolume);
278 for (auto it = this->begin(); it != this->end(); ++it) {
279 it->second = scale * (it->second - endVolume) + endVolume;
280 }
281 }
282 clampVolume();
283 }
284
Andy Hung7d712bb2017-04-20 14:23:41 -0700285 // The parcel layout must match VolumeShaper.java
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800286 status_t writeToParcel(Parcel *parcel) const {
287 if (parcel == nullptr) return BAD_VALUE;
288 return parcel->writeInt32((int32_t)mType)
289 ?: parcel->writeInt32(mId)
290 ?: mType == TYPE_ID
291 ? NO_ERROR
292 : parcel->writeInt32((int32_t)mOptionFlags)
293 ?: parcel->writeDouble(mDurationMs)
294 ?: Interpolator<S, T>::writeToParcel(parcel);
295 }
296
297 status_t readFromParcel(const Parcel &parcel) {
298 int32_t type, optionFlags;
299 return parcel.readInt32(&type)
300 ?: setType((Type)type)
301 ?: parcel.readInt32(&mId)
302 ?: mType == TYPE_ID
303 ? NO_ERROR
304 : parcel.readInt32(&optionFlags)
305 ?: setOptionFlags((OptionFlag)optionFlags)
306 ?: parcel.readDouble(&mDurationMs)
307 ?: Interpolator<S, T>::readFromParcel(parcel)
308 ?: checkCurve();
309 }
310
Andy Hungf3702642017-05-05 17:33:32 -0700311 // Returns a string for debug printing.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800312 std::string toString() const {
313 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700314 ss << "VolumeShaper::Configuration{mType=" << static_cast<int32_t>(mType);
315 ss << ", mId=" << mId;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800316 if (mType != TYPE_ID) {
Andy Hungf3702642017-05-05 17:33:32 -0700317 ss << ", mOptionFlags=" << static_cast<int32_t>(mOptionFlags);
318 ss << ", mDurationMs=" << mDurationMs;
319 ss << ", " << Interpolator<S, T>::toString().c_str();
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800320 }
Andy Hungf3702642017-05-05 17:33:32 -0700321 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800322 return ss.str();
323 }
324
325 private:
Andy Hungf3702642017-05-05 17:33:32 -0700326 Type mType; // type of configuration
327 int32_t mId; // A valid id is >= 0.
328 OptionFlag mOptionFlags; // option flags for the configuration.
329 double mDurationMs; // duration, must be > 0; default is 1000 ms.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800330 }; // Configuration
331
Andy Hungf3702642017-05-05 17:33:32 -0700332 /* VolumeShaper::Operation expresses an operation to perform on the
333 * configuration (either explicitly specified or an id).
334 *
335 * This parallels the Java implementation and the enums must match.
336 * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
337 * details on the Java implementation.
338 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800339 class Operation : public RefBase {
340 public:
Andy Hungf3702642017-05-05 17:33:32 -0700341 // Must match with VolumeShaper.java.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800342 enum Flag : int32_t {
343 FLAG_NONE = 0,
Andy Hungf3702642017-05-05 17:33:32 -0700344 FLAG_REVERSE = (1 << 0), // the absence of this indicates "play"
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800345 FLAG_TERMINATE = (1 << 1),
346 FLAG_JOIN = (1 << 2),
347 FLAG_DELAY = (1 << 3),
Andy Hung4ef88d72017-02-21 19:47:53 -0800348 FLAG_CREATE_IF_NECESSARY = (1 << 4),
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800349
Andy Hung4ef88d72017-02-21 19:47:53 -0800350 FLAG_ALL = (FLAG_REVERSE | FLAG_TERMINATE | FLAG_JOIN | FLAG_DELAY
351 | FLAG_CREATE_IF_NECESSARY),
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800352 };
353
354 Operation()
Andy Hung4ef88d72017-02-21 19:47:53 -0800355 : Operation(FLAG_NONE, -1 /* replaceId */) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800356 }
357
Andy Hung7d712bb2017-04-20 14:23:41 -0700358 Operation(Flag flags, int replaceId)
Andy Hung4ef88d72017-02-21 19:47:53 -0800359 : Operation(flags, replaceId, std::numeric_limits<S>::quiet_NaN() /* xOffset */) {
360 }
361
Andy Hung7d712bb2017-04-20 14:23:41 -0700362 explicit Operation(const Operation &operation)
Andy Hung4ef88d72017-02-21 19:47:53 -0800363 : Operation(operation.mFlags, operation.mReplaceId, operation.mXOffset) {
364 }
365
Andy Hung7d712bb2017-04-20 14:23:41 -0700366 explicit Operation(const sp<Operation> &operation)
367 : Operation(*operation.get()) {
368 }
369
370 Operation(Flag flags, int replaceId, S xOffset)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800371 : mFlags(flags)
Andy Hung4ef88d72017-02-21 19:47:53 -0800372 , mReplaceId(replaceId)
373 , mXOffset(xOffset) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800374 }
375
376 int32_t getReplaceId() const {
377 return mReplaceId;
378 }
379
380 void setReplaceId(int32_t replaceId) {
381 mReplaceId = replaceId;
382 }
383
Andy Hung4ef88d72017-02-21 19:47:53 -0800384 S getXOffset() const {
385 return mXOffset;
386 }
387
388 void setXOffset(S xOffset) {
Andy Hungf3702642017-05-05 17:33:32 -0700389 mXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
Andy Hung4ef88d72017-02-21 19:47:53 -0800390 }
391
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800392 Flag getFlags() const {
393 return mFlags;
394 }
395
Andy Hungf3702642017-05-05 17:33:32 -0700396 /* xOffset is the position on the volume curve and may go backwards
397 * if you are in reverse mode. This must be in the range from
398 * [MIN_CURVE_TIME, MAX_CURVE_TIME].
399 *
400 * normalizedTime always increases as time or framecount increases.
401 * normalizedTime is nominally from MIN_CURVE_TIME to MAX_CURVE_TIME when
402 * running through the curve, but could be outside this range afterwards.
403 * If you are reversing, this means the position on the curve, or xOffset,
404 * is computed as MAX_CURVE_TIME - normalizedTime, clamped to
405 * [MIN_CURVE_TIME, MAX_CURVE_TIME].
406 */
407 void setNormalizedTime(S normalizedTime) {
408 setXOffset((mFlags & FLAG_REVERSE) != 0
409 ? MAX_CURVE_TIME - normalizedTime : normalizedTime);
410 }
411
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800412 status_t setFlags(Flag flags) {
413 if ((flags & ~FLAG_ALL) != 0) {
414 ALOGE("flags has invalid bits: %#x", flags);
415 return BAD_VALUE;
416 }
417 mFlags = flags;
418 return NO_ERROR;
419 }
420
421 status_t writeToParcel(Parcel *parcel) const {
422 if (parcel == nullptr) return BAD_VALUE;
423 return parcel->writeInt32((int32_t)mFlags)
Andy Hung4ef88d72017-02-21 19:47:53 -0800424 ?: parcel->writeInt32(mReplaceId)
425 ?: parcel->writeFloat(mXOffset);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800426 }
427
428 status_t readFromParcel(const Parcel &parcel) {
429 int32_t flags;
430 return parcel.readInt32(&flags)
431 ?: parcel.readInt32(&mReplaceId)
Andy Hung4ef88d72017-02-21 19:47:53 -0800432 ?: parcel.readFloat(&mXOffset)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800433 ?: setFlags((Flag)flags);
434 }
435
436 std::string toString() const {
437 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700438 ss << "VolumeShaper::Operation{mFlags=" << static_cast<int32_t>(mFlags) ;
439 ss << ", mReplaceId=" << mReplaceId;
440 ss << ", mXOffset=" << mXOffset;
441 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800442 return ss.str();
443 }
444
445 private:
Andy Hungf3702642017-05-05 17:33:32 -0700446 Flag mFlags; // operation to do
447 int32_t mReplaceId; // if >= 0 the id to remove in a replace operation.
448 S mXOffset; // position in the curve to set if a valid number (not nan)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800449 }; // Operation
450
Andy Hungf3702642017-05-05 17:33:32 -0700451 /* VolumeShaper.State is returned when requesting the last
452 * state of the VolumeShaper.
453 *
454 * This parallels the Java implementation.
455 * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
456 * details on the Java implementation.
457 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800458 class State : public RefBase {
459 public:
Andy Hung7d712bb2017-04-20 14:23:41 -0700460 State(T volume, S xOffset)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800461 : mVolume(volume)
462 , mXOffset(xOffset) {
463 }
464
465 State()
Andy Hungf3702642017-05-05 17:33:32 -0700466 : State(NAN, NAN) { }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800467
468 T getVolume() const {
469 return mVolume;
470 }
471
472 void setVolume(T volume) {
473 mVolume = volume;
474 }
475
476 S getXOffset() const {
477 return mXOffset;
478 }
479
480 void setXOffset(S xOffset) {
481 mXOffset = xOffset;
482 }
483
484 status_t writeToParcel(Parcel *parcel) const {
485 if (parcel == nullptr) return BAD_VALUE;
486 return parcel->writeFloat(mVolume)
487 ?: parcel->writeFloat(mXOffset);
488 }
489
490 status_t readFromParcel(const Parcel &parcel) {
491 return parcel.readFloat(&mVolume)
492 ?: parcel.readFloat(&mXOffset);
493 }
494
495 std::string toString() const {
496 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700497 ss << "VolumeShaper::State{mVolume=" << mVolume;
498 ss << ", mXOffset=" << mXOffset;
499 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800500 return ss.str();
501 }
502
503 private:
Andy Hungf3702642017-05-05 17:33:32 -0700504 T mVolume; // linear volume in the range MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME
505 S mXOffset; // position on curve expressed from MIN_CURVE_TIME to MAX_CURVE_TIME
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800506 }; // State
507
Andy Hungf3702642017-05-05 17:33:32 -0700508 // Internal helper class to do an affine transform for time and amplitude scaling.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800509 template <typename R>
510 class Translate {
511 public:
512 Translate()
513 : mOffset(0)
514 , mScale(1) {
515 }
516
517 R getOffset() const {
518 return mOffset;
519 }
520
521 void setOffset(R offset) {
522 mOffset = offset;
523 }
524
525 R getScale() const {
526 return mScale;
527 }
528
529 void setScale(R scale) {
530 mScale = scale;
531 }
532
533 R operator()(R in) const {
534 return mScale * (in - mOffset);
535 }
536
537 std::string toString() const {
538 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700539 ss << "VolumeShaper::Translate{mOffset=" << mOffset;
540 ss << ", mScale=" << mScale;
541 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800542 return ss.str();
543 }
544
545 private:
546 R mOffset;
547 R mScale;
548 }; // Translate
549
550 static int64_t convertTimespecToUs(const struct timespec &tv)
551 {
552 return tv.tv_sec * 1000000ll + tv.tv_nsec / 1000;
553 }
554
555 // current monotonic time in microseconds.
556 static int64_t getNowUs()
557 {
558 struct timespec tv;
559 if (clock_gettime(CLOCK_MONOTONIC, &tv) != 0) {
560 return 0; // system is really sick, just return 0 for consistency.
561 }
562 return convertTimespecToUs(tv);
563 }
564
Andy Hungf3702642017-05-05 17:33:32 -0700565 /* Native implementation of VolumeShaper. This is NOT mirrored
566 * on the Java side, so we don't need to mimic Java side layout
567 * and data; furthermore, this isn't refcounted as a "RefBase" object.
568 *
569 * Since we pass configuration and operation as shared pointers (like
570 * Java) there is a potential risk that the caller may modify
571 * these after delivery.
572 */
Andy Hung7d712bb2017-04-20 14:23:41 -0700573 VolumeShaper(
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800574 const sp<VolumeShaper::Configuration> &configuration,
575 const sp<VolumeShaper::Operation> &operation)
576 : mConfiguration(configuration) // we do not make a copy
577 , mOperation(operation) // ditto
578 , mStartFrame(-1)
579 , mLastVolume(T(1))
Andy Hungf3702642017-05-05 17:33:32 -0700580 , mLastXOffset(MIN_CURVE_TIME)
581 , mDelayXOffset(MIN_CURVE_TIME) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800582 if (configuration.get() != nullptr
583 && (getFlags() & VolumeShaper::Operation::FLAG_DELAY) == 0) {
584 mLastVolume = configuration->first().second;
585 }
586 }
587
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800588 // We allow a null operation here, though VolumeHandler always provides one.
589 VolumeShaper::Operation::Flag getFlags() const {
590 return mOperation == nullptr
Andy Hungf3702642017-05-05 17:33:32 -0700591 ? VolumeShaper::Operation::FLAG_NONE : mOperation->getFlags();
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800592 }
593
Andy Hungf3702642017-05-05 17:33:32 -0700594 /* Returns the last volume and xoffset reported to the AudioFlinger.
595 * If the VolumeShaper has not been started, compute what the volume
596 * should be based on the initial offset specified.
597 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800598 sp<VolumeShaper::State> getState() const {
Andy Hungf3702642017-05-05 17:33:32 -0700599 if (!isStarted()) {
600 const T volume = computeVolumeFromXOffset(mDelayXOffset);
601 VS_LOG("delayed VolumeShaper, using cached offset:%f for volume:%f",
602 mDelayXOffset, volume);
603 return new VolumeShaper::State(volume, mDelayXOffset);
604 } else {
605 return new VolumeShaper::State(mLastVolume, mLastXOffset);
606 }
607 }
608
609 S getDelayXOffset() const {
610 return mDelayXOffset;
Andy Hung4ef88d72017-02-21 19:47:53 -0800611 }
612
613 void setDelayXOffset(S xOffset) {
Andy Hungf3702642017-05-05 17:33:32 -0700614 mDelayXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800615 }
616
Andy Hung39399b62017-04-21 15:07:45 -0700617 bool isStarted() const {
618 return mStartFrame >= 0;
619 }
620
Andy Hungf3702642017-05-05 17:33:32 -0700621 /* getVolume() updates the last volume/xoffset state so it is not
622 * const, even though logically it may be viewed as const.
623 */
Andy Hung10cbff12017-02-21 17:30:14 -0800624 std::pair<T /* volume */, bool /* active */> getVolume(
625 int64_t trackFrameCount, double trackSampleRate) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800626 if ((getFlags() & VolumeShaper::Operation::FLAG_DELAY) != 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700627 // We haven't had PLAY called yet, so just return the value
628 // as if PLAY were called just now.
629 VS_LOG("delayed VolumeShaper, using cached offset %f", mDelayXOffset);
630 const T volume = computeVolumeFromXOffset(mDelayXOffset);
631 return std::make_pair(volume, false);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800632 }
633 const bool clockTime = (mConfiguration->getOptionFlags()
634 & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
635 const int64_t frameCount = clockTime ? getNowUs() : trackFrameCount;
636 const double sampleRate = clockTime ? 1000000 : trackSampleRate;
637
638 if (mStartFrame < 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700639 updatePosition(frameCount, sampleRate, mDelayXOffset);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800640 mStartFrame = frameCount;
641 }
642 VS_LOG("frameCount: %lld", (long long)frameCount);
Andy Hungf3702642017-05-05 17:33:32 -0700643 const S x = mXTranslate((T)frameCount);
644 VS_LOG("translation to normalized time: %f", x);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800645
Andy Hungf3702642017-05-05 17:33:32 -0700646 std::tuple<T /* volume */, S /* position */, bool /* active */> vt =
647 computeStateFromNormalizedTime(x);
648
649 mLastVolume = std::get<0>(vt);
650 mLastXOffset = std::get<1>(vt);
651 const bool active = std::get<2>(vt);
652 VS_LOG("rescaled time:%f volume:%f xOffset:%f active:%s",
653 x, mLastVolume, mLastXOffset, active ? "true" : "false");
654 return std::make_pair(mLastVolume, active);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800655 }
656
657 std::string toString() const {
658 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700659 ss << "VolumeShaper{mStartFrame=" << mStartFrame;
660 ss << ", mXTranslate=" << mXTranslate.toString().c_str();
661 ss << ", mConfiguration=" <<
662 (mConfiguration.get() == nullptr
663 ? "nullptr" : mConfiguration->toString().c_str());
664 ss << ", mOperation=" <<
665 (mOperation.get() == nullptr
666 ? "nullptr" : mOperation->toString().c_str());
667 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800668 return ss.str();
669 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800670
Andy Hungf3702642017-05-05 17:33:32 -0700671 Translate<S> mXTranslate; // translation from frames (usec for clock time) to normalized time.
Andy Hung4ef88d72017-02-21 19:47:53 -0800672 sp<VolumeShaper::Configuration> mConfiguration;
673 sp<VolumeShaper::Operation> mOperation;
Andy Hungf3702642017-05-05 17:33:32 -0700674
675private:
Andy Hung4ef88d72017-02-21 19:47:53 -0800676 int64_t mStartFrame; // starting frame, non-negative when started (in usec for clock time)
677 T mLastVolume; // last computed interpolated volume (y-axis)
678 S mLastXOffset; // last computed interpolated xOffset/time (x-axis)
Andy Hungf3702642017-05-05 17:33:32 -0700679 S mDelayXOffset; // xOffset to use for first invocation of VolumeShaper.
680
681 // Called internally to adjust mXTranslate for first time start.
682 void updatePosition(int64_t startFrame, double sampleRate, S xOffset) {
683 double scale = (mConfiguration->last().first - mConfiguration->first().first)
684 / (mConfiguration->getDurationMs() * 0.001 * sampleRate);
685 const double minScale = 1. / static_cast<double>(INT64_MAX);
686 scale = std::max(scale, minScale);
687 VS_LOG("update position: scale %lf frameCount:%lld, sampleRate:%lf, xOffset:%f",
688 scale, (long long) startFrame, sampleRate, xOffset);
689
690 S normalizedTime = (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) != 0 ?
691 MAX_CURVE_TIME - xOffset : xOffset;
692 mXTranslate.setOffset(static_cast<float>(static_cast<double>(startFrame)
693 - static_cast<double>(normalizedTime) / scale));
694 mXTranslate.setScale(static_cast<float>(scale));
695 VS_LOG("translate: %s", mXTranslate.toString().c_str());
696 }
697
698 T computeVolumeFromXOffset(S xOffset) const {
699 const T unscaledVolume = mConfiguration->findY(xOffset);
700 const T volume = mConfiguration->adjustVolume(unscaledVolume); // handle log scale
701 VS_LOG("computeVolumeFromXOffset %f -> %f -> %f", xOffset, unscaledVolume, volume);
702 return volume;
703 }
704
705 std::tuple<T /* volume */, S /* position */, bool /* active */>
706 computeStateFromNormalizedTime(S x) const {
707 bool active = true;
708 // handle reversal of position
709 if (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) {
710 x = MAX_CURVE_TIME - x;
711 VS_LOG("reversing to %f", x);
712 if (x < MIN_CURVE_TIME) {
713 x = MIN_CURVE_TIME;
714 active = false; // at the end
715 } else if (x > MAX_CURVE_TIME) {
716 x = MAX_CURVE_TIME; //early
717 }
718 } else {
719 if (x < MIN_CURVE_TIME) {
720 x = MIN_CURVE_TIME; // early
721 } else if (x > MAX_CURVE_TIME) {
722 x = MAX_CURVE_TIME;
723 active = false; // at end
724 }
725 }
726 const S xOffset = x;
727 const T volume = computeVolumeFromXOffset(xOffset);
728 return std::make_tuple(volume, xOffset, active);
729 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800730}; // VolumeShaper
731
Andy Hungf3702642017-05-05 17:33:32 -0700732/* VolumeHandler combines the volume factors of multiple VolumeShapers associated
733 * with a player. It is thread safe by synchronizing all public methods.
734 *
735 * This is a native-only implementation.
736 *
737 * The server side VolumeHandler is used to maintain a list of volume handlers,
738 * keep state, and obtain volume.
739 *
740 * The client side VolumeHandler is used to maintain a list of volume handlers,
741 * keep some partial state, and restore if the server dies.
742 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800743class VolumeHandler : public RefBase {
744public:
745 using S = float;
746 using T = float;
747
Andy Hung4ef88d72017-02-21 19:47:53 -0800748 // A volume handler which just keeps track of active VolumeShapers does not need sampleRate.
749 VolumeHandler()
750 : VolumeHandler(0 /* sampleRate */) {
751 }
752
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800753 explicit VolumeHandler(uint32_t sampleRate)
754 : mSampleRate((double)sampleRate)
Andy Hung4ef88d72017-02-21 19:47:53 -0800755 , mLastFrame(0)
Andy Hungf3702642017-05-05 17:33:32 -0700756 , mVolumeShaperIdCounter(VolumeShaper::kSystemVolumeShapersMax)
Andy Hungda540db2017-04-20 14:06:17 -0700757 , mLastVolume(1.f, false) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800758 }
759
760 VolumeShaper::Status applyVolumeShaper(
761 const sp<VolumeShaper::Configuration> &configuration,
Andy Hungf3702642017-05-05 17:33:32 -0700762 const sp<VolumeShaper::Operation> &operation_in) {
763 // make a local copy of operation, as we modify it.
764 sp<VolumeShaper::Operation> operation(new VolumeShaper::Operation(operation_in));
Andy Hung10cbff12017-02-21 17:30:14 -0800765 VS_LOG("applyVolumeShaper:configuration: %s", configuration->toString().c_str());
766 VS_LOG("applyVolumeShaper:operation: %s", operation->toString().c_str());
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800767 AutoMutex _l(mLock);
768 if (configuration == nullptr) {
769 ALOGE("null configuration");
770 return VolumeShaper::Status(BAD_VALUE);
771 }
772 if (operation == nullptr) {
773 ALOGE("null operation");
774 return VolumeShaper::Status(BAD_VALUE);
775 }
776 const int32_t id = configuration->getId();
777 if (id < 0) {
778 ALOGE("negative id: %d", id);
779 return VolumeShaper::Status(BAD_VALUE);
780 }
781 VS_LOG("applyVolumeShaper id: %d", id);
782
783 switch (configuration->getType()) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800784 case VolumeShaper::Configuration::TYPE_SCALE: {
785 const int replaceId = operation->getReplaceId();
786 if (replaceId >= 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700787 VS_LOG("replacing %d", replaceId);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800788 auto replaceIt = findId_l(replaceId);
789 if (replaceIt == mVolumeShapers.end()) {
790 ALOGW("cannot find replace id: %d", replaceId);
791 } else {
Andy Hungf3702642017-05-05 17:33:32 -0700792 if ((operation->getFlags() & VolumeShaper::Operation::FLAG_JOIN) != 0) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800793 // For join, we scale the start volume of the current configuration
794 // to match the last-used volume of the replacing VolumeShaper.
795 auto state = replaceIt->getState();
Andy Hungf3702642017-05-05 17:33:32 -0700796 ALOGD("join: state:%s", state->toString().c_str());
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800797 if (state->getXOffset() >= 0) { // valid
798 const T volume = state->getVolume();
799 ALOGD("join: scaling start volume to %f", volume);
800 configuration->scaleToStartVolume(volume);
801 }
802 }
803 (void)mVolumeShapers.erase(replaceIt);
804 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800805 operation->setReplaceId(-1);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800806 }
807 // check if we have another of the same id.
808 auto oldIt = findId_l(id);
809 if (oldIt != mVolumeShapers.end()) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800810 if ((operation->getFlags()
811 & VolumeShaper::Operation::FLAG_CREATE_IF_NECESSARY) != 0) {
812 // TODO: move the case to a separate function.
813 goto HANDLE_TYPE_ID; // no need to create, take over existing id.
814 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800815 ALOGW("duplicate id, removing old %d", id);
816 (void)mVolumeShapers.erase(oldIt);
817 }
Andy Hungf3702642017-05-05 17:33:32 -0700818
819 /* Check if too many application VolumeShapers (with id >= kSystemVolumeShapersMax).
820 * We check on the server side to ensure synchronization and robustness.
821 *
822 * This shouldn't fail on a replace command unless the replaced id is
823 * already invalid (which *should* be checked in the Java layer).
824 */
825 if (id >= VolumeShaper::kSystemVolumeShapersMax
826 && numberOfUserVolumeShapers_l() >= VolumeShaper::kUserVolumeShapersMax) {
827 ALOGW("Too many app VolumeShapers, cannot add to VolumeHandler");
828 return VolumeShaper::Status(INVALID_OPERATION);
829 }
830
831 // create new VolumeShaper with default behavior.
832 mVolumeShapers.emplace_back(configuration, new VolumeShaper::Operation());
833 VS_LOG("after adding, number of volumeShapers:%zu", mVolumeShapers.size());
Andy Hung10cbff12017-02-21 17:30:14 -0800834 }
835 // fall through to handle the operation
Andy Hung4ef88d72017-02-21 19:47:53 -0800836 HANDLE_TYPE_ID:
Andy Hung10cbff12017-02-21 17:30:14 -0800837 case VolumeShaper::Configuration::TYPE_ID: {
838 VS_LOG("trying to find id: %d", id);
839 auto it = findId_l(id);
840 if (it == mVolumeShapers.end()) {
841 VS_LOG("couldn't find id: %d", id);
842 return VolumeShaper::Status(INVALID_OPERATION);
843 }
Andy Hungf3702642017-05-05 17:33:32 -0700844 if ((operation->getFlags() & VolumeShaper::Operation::FLAG_TERMINATE) != 0) {
Andy Hung10cbff12017-02-21 17:30:14 -0800845 VS_LOG("terminate id: %d", id);
846 mVolumeShapers.erase(it);
847 break;
848 }
849 const bool clockTime = (it->mConfiguration->getOptionFlags()
850 & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
851 if ((it->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) !=
852 (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE)) {
Andy Hungf3702642017-05-05 17:33:32 -0700853 if (it->isStarted()) {
854 const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
855 const S x = it->mXTranslate((T)frameCount);
856 VS_LOG("reverse normalizedTime: %f", x);
857 // reflect position
858 S target = MAX_CURVE_TIME - x;
859 if (target < MIN_CURVE_TIME) {
860 VS_LOG("clamp to start - begin immediately");
861 target = MIN_CURVE_TIME;
862 }
863 VS_LOG("reverse normalizedTime target: %f", target);
864 it->mXTranslate.setOffset(it->mXTranslate.getOffset()
865 + (x - target) / it->mXTranslate.getScale());
Andy Hung10cbff12017-02-21 17:30:14 -0800866 }
Andy Hungf3702642017-05-05 17:33:32 -0700867 // if not started, the delay offset doesn't change.
Andy Hung10cbff12017-02-21 17:30:14 -0800868 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800869 const S xOffset = operation->getXOffset();
870 if (!std::isnan(xOffset)) {
Andy Hungf3702642017-05-05 17:33:32 -0700871 if (it->isStarted()) {
872 const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
873 const S x = it->mXTranslate((T)frameCount);
874 VS_LOG("normalizedTime translation: %f", x);
875 const S target =
876 (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) != 0 ?
877 MAX_CURVE_TIME - xOffset : xOffset;
878 VS_LOG("normalizedTime target x offset: %f", target);
879 it->mXTranslate.setOffset(it->mXTranslate.getOffset()
880 + (x - target) / it->mXTranslate.getScale());
881 } else {
882 it->setDelayXOffset(xOffset);
883 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800884 }
Andy Hung10cbff12017-02-21 17:30:14 -0800885 it->mOperation = operation; // replace the operation
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800886 } break;
887 }
888 return VolumeShaper::Status(id);
889 }
890
891 sp<VolumeShaper::State> getVolumeShaperState(int id) {
892 AutoMutex _l(mLock);
893 auto it = findId_l(id);
894 if (it == mVolumeShapers.end()) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800895 VS_LOG("cannot find state for id: %d", id);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800896 return nullptr;
897 }
898 return it->getState();
899 }
900
Andy Hungf3702642017-05-05 17:33:32 -0700901 /* getVolume() is not const, as it updates internal state.
902 * Once called, any VolumeShapers not already started begin running.
903 */
Andy Hung10cbff12017-02-21 17:30:14 -0800904 std::pair<T /* volume */, bool /* active */> getVolume(int64_t trackFrameCount) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800905 AutoMutex _l(mLock);
906 mLastFrame = trackFrameCount;
907 T volume(1);
Andy Hung10cbff12017-02-21 17:30:14 -0800908 size_t activeCount = 0;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800909 for (auto it = mVolumeShapers.begin(); it != mVolumeShapers.end();) {
Andy Hungf3702642017-05-05 17:33:32 -0700910 const std::pair<T, bool> shaperVolume =
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800911 it->getVolume(trackFrameCount, mSampleRate);
912 volume *= shaperVolume.first;
Andy Hung10cbff12017-02-21 17:30:14 -0800913 activeCount += shaperVolume.second;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800914 ++it;
915 }
Andy Hungda540db2017-04-20 14:06:17 -0700916 mLastVolume = std::make_pair(volume, activeCount != 0);
Andy Hungf3702642017-05-05 17:33:32 -0700917 VS_LOG("getVolume: <%f, %s>", mLastVolume.first, mLastVolume.second ? "true" : "false");
Andy Hungda540db2017-04-20 14:06:17 -0700918 return mLastVolume;
919 }
920
Andy Hungf3702642017-05-05 17:33:32 -0700921 /* Used by a client side VolumeHandler to ensure all the VolumeShapers
922 * indicate that they have been started. Upon a change in audioserver
923 * output sink, this information is used for restoration of the server side
924 * VolumeHandler.
925 */
Andy Hung39399b62017-04-21 15:07:45 -0700926 void setStarted() {
927 (void)getVolume(mLastFrame); // getVolume() will start the individual VolumeShapers.
928 }
929
Andy Hungda540db2017-04-20 14:06:17 -0700930 std::pair<T /* volume */, bool /* active */> getLastVolume() const {
931 AutoMutex _l(mLock);
932 return mLastVolume;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800933 }
934
935 std::string toString() const {
936 AutoMutex _l(mLock);
937 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700938 ss << "VolumeHandler{mSampleRate=" << mSampleRate;
939 ss << ", mLastFrame=" << mLastFrame;
940 ss << ", mVolumeShapers={";
941 bool first = true;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800942 for (const auto &shaper : mVolumeShapers) {
Andy Hungf3702642017-05-05 17:33:32 -0700943 if (first) {
944 first = false;
945 } else {
946 ss << ", ";
947 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800948 ss << shaper.toString().c_str();
949 }
Andy Hungf3702642017-05-05 17:33:32 -0700950 ss << "}}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800951 return ss.str();
952 }
953
Andy Hung39399b62017-04-21 15:07:45 -0700954 void forall(const std::function<VolumeShaper::Status (const VolumeShaper &)> &lambda) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800955 AutoMutex _l(mLock);
Andy Hungda540db2017-04-20 14:06:17 -0700956 VS_LOG("forall: mVolumeShapers.size() %zu", mVolumeShapers.size());
Andy Hung4ef88d72017-02-21 19:47:53 -0800957 for (const auto &shaper : mVolumeShapers) {
Andy Hung39399b62017-04-21 15:07:45 -0700958 VolumeShaper::Status status = lambda(shaper);
959 VS_LOG("forall applying lambda on shaper (%p): %d", &shaper, (int)status);
Andy Hung4ef88d72017-02-21 19:47:53 -0800960 }
961 }
962
963 void reset() {
964 AutoMutex _l(mLock);
965 mVolumeShapers.clear();
Andy Hung7d712bb2017-04-20 14:23:41 -0700966 mLastFrame = 0;
Andy Hung4ef88d72017-02-21 19:47:53 -0800967 // keep mVolumeShaperIdCounter as is.
968 }
969
Andy Hungf3702642017-05-05 17:33:32 -0700970 /* Sets the configuration id if necessary - This is based on the counter
971 * internal to the VolumeHandler.
972 */
Andy Hung4ef88d72017-02-21 19:47:53 -0800973 void setIdIfNecessary(const sp<VolumeShaper::Configuration> &configuration) {
974 if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
975 const int id = configuration->getId();
976 if (id == -1) {
977 // Reassign to a unique id, skipping system ids.
978 AutoMutex _l(mLock);
979 while (true) {
980 if (mVolumeShaperIdCounter == INT32_MAX) {
Andy Hungf3702642017-05-05 17:33:32 -0700981 mVolumeShaperIdCounter = VolumeShaper::kSystemVolumeShapersMax;
Andy Hung4ef88d72017-02-21 19:47:53 -0800982 } else {
983 ++mVolumeShaperIdCounter;
984 }
985 if (findId_l(mVolumeShaperIdCounter) != mVolumeShapers.end()) {
986 continue; // collision with an existing id.
987 }
988 configuration->setId(mVolumeShaperIdCounter);
989 ALOGD("setting id to %d", mVolumeShaperIdCounter);
990 break;
991 }
992 }
993 }
994 }
995
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800996private:
997 std::list<VolumeShaper>::iterator findId_l(int32_t id) {
998 std::list<VolumeShaper>::iterator it = mVolumeShapers.begin();
999 for (; it != mVolumeShapers.end(); ++it) {
1000 if (it->mConfiguration->getId() == id) {
1001 break;
1002 }
1003 }
1004 return it;
1005 }
1006
Andy Hungf3702642017-05-05 17:33:32 -07001007 size_t numberOfUserVolumeShapers_l() const {
1008 size_t count = 0;
1009 for (const auto &shaper : mVolumeShapers) {
1010 count += (shaper.mConfiguration->getId() >= VolumeShaper::kSystemVolumeShapersMax);
1011 }
1012 return count;
1013 }
1014
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001015 mutable Mutex mLock;
1016 double mSampleRate; // in samples (frames) per second
Andy Hung7d712bb2017-04-20 14:23:41 -07001017 int64_t mLastFrame; // logging purpose only, 0 on start
Andy Hung4ef88d72017-02-21 19:47:53 -08001018 int32_t mVolumeShaperIdCounter; // a counter to return a unique volume shaper id.
Andy Hungda540db2017-04-20 14:06:17 -07001019 std::pair<T /* volume */, bool /* active */> mLastVolume;
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001020 std::list<VolumeShaper> mVolumeShapers; // list provides stable iterators on erase
1021}; // VolumeHandler
1022
1023} // namespace android
1024
1025#pragma pop_macro("LOG_TAG")
1026
1027#endif // ANDROID_VOLUME_SHAPER_H