blob: fe519bb56321bfa9b1e27c7fb1c1317370be4921 [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
Ivan Lozano8cf3a072017-08-09 09:01:33 -070040namespace media {
41
Andy Hung9fc8b5c2017-01-24 13:36:48 -080042// The native VolumeShaper class mirrors the java VolumeShaper class;
43// in addition, the native class contains implementation for actual operation.
44//
45// VolumeShaper methods are not safe for multiple thread access.
46// Use VolumeHandler for thread-safe encapsulation of multiple VolumeShapers.
47//
48// Classes below written are to avoid naked pointers so there are no
49// explicit destructors required.
50
51class VolumeShaper {
52public:
Andy Hungf3702642017-05-05 17:33:32 -070053 // S and T are like template typenames (matching the Interpolator<S, T>)
54 using S = float; // time type
55 using T = float; // volume type
Andy Hung9fc8b5c2017-01-24 13:36:48 -080056
Andy Hungf3702642017-05-05 17:33:32 -070057// Curve and dimension information
58// TODO: member static const or constexpr float initialization not permitted in C++11
59#define MIN_CURVE_TIME 0.f // type S: start of VolumeShaper curve (normalized)
60#define MAX_CURVE_TIME 1.f // type S: end of VolumeShaper curve (normalized)
61#define MIN_LINEAR_VOLUME 0.f // type T: silence / mute audio
62#define MAX_LINEAR_VOLUME 1.f // type T: max volume, unity gain
63#define MAX_LOG_VOLUME 0.f // type T: max volume, unity gain in dBFS
Andy Hung9fc8b5c2017-01-24 13:36:48 -080064
Andy Hungf3702642017-05-05 17:33:32 -070065 /* kSystemVolumeShapersMax is the maximum number of system VolumeShapers.
66 * Each system VolumeShapers has a predefined Id, which ranges from 0
67 * to kSystemVolumeShapersMax - 1 and is unique for its usage.
68 *
69 * "1" is reserved for system ducking.
70 */
71 static const int kSystemVolumeShapersMax = 16;
72
73 /* kUserVolumeShapersMax is the maximum number of application
74 * VolumeShapers for a player/track. Application VolumeShapers are
75 * assigned on creation by the client, and have Ids ranging
76 * from kSystemVolumeShapersMax to INT32_MAX.
77 *
78 * The number of user/application volume shapers is independent to the
79 * system volume shapers. If an application tries to create more than
80 * kUserVolumeShapersMax to a player, then the apply() will fail.
81 * This prevents exhausting server side resources by a potentially malicious
82 * application.
83 */
84 static const int kUserVolumeShapersMax = 16;
85
86 /* VolumeShaper::Status is equivalent to status_t if negative
87 * but if non-negative represents the id operated on.
88 * It must be expressible as an int32_t for binder purposes.
89 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -080090 using Status = status_t;
91
Andy Hungf3702642017-05-05 17:33:32 -070092 // Local definition for clamp as std::clamp is included in C++17 only.
93 // TODO: use the std::clamp version when Android build uses C++17.
94 template<typename R>
95 static constexpr const R &clamp(const R &v, const R &lo, const R &hi) {
96 return (v < lo) ? lo : (hi < v) ? hi : v;
97 }
98
99 /* VolumeShaper.Configuration derives from the Interpolator class and adds
100 * parameters relating to the volume shape.
101 *
102 * This parallels the Java implementation and the enums must match.
103 * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
104 * details on the Java implementation.
105 */
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700106 class Configuration : public Interpolator<S, T>, public RefBase, public Parcelable {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800107 public:
Andy Hungf3702642017-05-05 17:33:32 -0700108 // Must match with VolumeShaper.java in frameworks/base.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800109 enum Type : int32_t {
110 TYPE_ID,
111 TYPE_SCALE,
112 };
113
Andy Hungf3702642017-05-05 17:33:32 -0700114 // Must match with VolumeShaper.java in frameworks/base.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800115 enum OptionFlag : int32_t {
116 OPTION_FLAG_NONE = 0,
117 OPTION_FLAG_VOLUME_IN_DBFS = (1 << 0),
118 OPTION_FLAG_CLOCK_TIME = (1 << 1),
119
120 OPTION_FLAG_ALL = (OPTION_FLAG_VOLUME_IN_DBFS | OPTION_FLAG_CLOCK_TIME),
121 };
122
Andy Hungf3702642017-05-05 17:33:32 -0700123 // Bring from base class; must match with VolumeShaper.java in frameworks/base.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800124 using InterpolatorType = Interpolator<S, T>::InterpolatorType;
125
126 Configuration()
127 : Interpolator<S, T>()
Colin Cross11280a12017-05-02 10:32:56 -0700128 , RefBase()
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800129 , mType(TYPE_SCALE)
Colin Cross4e399992017-04-27 16:15:51 -0700130 , mId(-1)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800131 , mOptionFlags(OPTION_FLAG_NONE)
Colin Cross4e399992017-04-27 16:15:51 -0700132 , mDurationMs(1000.) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800133 }
134
Andy Hung06a730b2020-04-09 13:28:31 -0700135 Configuration(const Configuration &configuration)
Andy Hung10cbff12017-02-21 17:30:14 -0800136 : Interpolator<S, T>(*static_cast<const Interpolator<S, T> *>(&configuration))
Colin Cross11280a12017-05-02 10:32:56 -0700137 , RefBase()
Andy Hung10cbff12017-02-21 17:30:14 -0800138 , mType(configuration.mType)
Colin Cross4e399992017-04-27 16:15:51 -0700139 , mId(configuration.mId)
Andy Hung10cbff12017-02-21 17:30:14 -0800140 , mOptionFlags(configuration.mOptionFlags)
Colin Cross4e399992017-04-27 16:15:51 -0700141 , mDurationMs(configuration.mDurationMs) {
Andy Hung10cbff12017-02-21 17:30:14 -0800142 }
143
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800144 Type getType() const {
145 return mType;
146 }
147
148 status_t setType(Type type) {
149 switch (type) {
150 case TYPE_ID:
151 case TYPE_SCALE:
152 mType = type;
153 return NO_ERROR;
154 default:
155 ALOGE("invalid Type: %d", type);
156 return BAD_VALUE;
157 }
158 }
159
160 OptionFlag getOptionFlags() const {
161 return mOptionFlags;
162 }
163
164 status_t setOptionFlags(OptionFlag optionFlags) {
165 if ((optionFlags & ~OPTION_FLAG_ALL) != 0) {
166 ALOGE("optionFlags has invalid bits: %#x", optionFlags);
167 return BAD_VALUE;
168 }
169 mOptionFlags = optionFlags;
170 return NO_ERROR;
171 }
172
173 double getDurationMs() const {
174 return mDurationMs;
175 }
176
Andy Hungf3702642017-05-05 17:33:32 -0700177 status_t setDurationMs(double durationMs) {
178 if (durationMs > 0.) {
179 mDurationMs = durationMs;
180 return NO_ERROR;
181 }
182 // zero, negative, or nan. These values not possible from Java.
183 return BAD_VALUE;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800184 }
185
186 int32_t getId() const {
187 return mId;
188 }
189
190 void setId(int32_t id) {
Andy Hungf3702642017-05-05 17:33:32 -0700191 // We permit a negative id here (representing invalid).
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800192 mId = id;
193 }
194
Andy Hungf3702642017-05-05 17:33:32 -0700195 /* Adjust the volume to be in linear range from MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME
196 * and compensate for log dbFS volume as needed.
197 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800198 T adjustVolume(T volume) const {
199 if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700200 const T out = powf(10.f, volume / 10.f);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800201 VS_LOG("in: %f out: %f", volume, out);
202 volume = out;
203 }
Andy Hungf3702642017-05-05 17:33:32 -0700204 return clamp(volume, MIN_LINEAR_VOLUME /* lo */, MAX_LINEAR_VOLUME /* hi */);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800205 }
206
Andy Hungf3702642017-05-05 17:33:32 -0700207 /* Check if the existing curve is valid.
208 */
209 status_t checkCurve() const {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800210 if (mType == TYPE_ID) return NO_ERROR;
211 if (this->size() < 2) {
212 ALOGE("curve must have at least 2 points");
213 return BAD_VALUE;
214 }
Andy Hungf3702642017-05-05 17:33:32 -0700215 if (first().first != MIN_CURVE_TIME || last().first != MAX_CURVE_TIME) {
216 ALOGE("curve must start at MIN_CURVE_TIME and end at MAX_CURVE_TIME");
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800217 return BAD_VALUE;
218 }
219 if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
220 for (const auto &pt : *this) {
Andy Hungf3702642017-05-05 17:33:32 -0700221 if (!(pt.second <= MAX_LOG_VOLUME) /* handle nan */) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800222 ALOGE("positive volume dbFS");
223 return BAD_VALUE;
224 }
225 }
226 } else {
227 for (const auto &pt : *this) {
Andy Hungf3702642017-05-05 17:33:32 -0700228 if (!(pt.second >= MIN_LINEAR_VOLUME)
229 || !(pt.second <= MAX_LINEAR_VOLUME) /* handle nan */) {
230 ALOGE("volume < MIN_LINEAR_VOLUME or > MAX_LINEAR_VOLUME");
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800231 return BAD_VALUE;
232 }
233 }
234 }
235 return NO_ERROR;
236 }
237
Andy Hungf3702642017-05-05 17:33:32 -0700238 /* Clamps the volume curve in the configuration to
239 * the valid range for log or linear scale.
240 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800241 void clampVolume() {
242 if ((mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
243 for (auto it = this->begin(); it != this->end(); ++it) {
Andy Hungf3702642017-05-05 17:33:32 -0700244 if (!(it->second <= MAX_LOG_VOLUME) /* handle nan */) {
245 it->second = MAX_LOG_VOLUME;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800246 }
247 }
248 } else {
249 for (auto it = this->begin(); it != this->end(); ++it) {
Andy Hungf3702642017-05-05 17:33:32 -0700250 if (!(it->second >= MIN_LINEAR_VOLUME) /* handle nan */) {
251 it->second = MIN_LINEAR_VOLUME;
252 } else if (!(it->second <= MAX_LINEAR_VOLUME)) {
253 it->second = MAX_LINEAR_VOLUME;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800254 }
255 }
256 }
257 }
258
259 /* scaleToStartVolume() is used to set the start volume of a
260 * new VolumeShaper curve, when replacing one VolumeShaper
261 * with another using the "join" (volume match) option.
262 *
263 * It works best for monotonic volume ramps or ducks.
264 */
265 void scaleToStartVolume(T volume) {
266 if (this->size() < 2) {
267 return;
268 }
269 const T startVolume = first().second;
270 const T endVolume = last().second;
271 if (endVolume == startVolume) {
272 // match with linear ramp
273 const T offset = volume - startVolume;
Andy Hungf3702642017-05-05 17:33:32 -0700274 static const T scale = 1.f / (MAX_CURVE_TIME - MIN_CURVE_TIME); // nominally 1.f
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800275 for (auto it = this->begin(); it != this->end(); ++it) {
Andy Hungf3702642017-05-05 17:33:32 -0700276 it->second = it->second + offset * (MAX_CURVE_TIME - it->first) * scale;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800277 }
278 } else {
279 const T scale = (volume - endVolume) / (startVolume - endVolume);
280 for (auto it = this->begin(); it != this->end(); ++it) {
281 it->second = scale * (it->second - endVolume) + endVolume;
282 }
283 }
284 clampVolume();
285 }
286
Andy Hung7d712bb2017-04-20 14:23:41 -0700287 // The parcel layout must match VolumeShaper.java
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700288 status_t writeToParcel(Parcel *parcel) const override {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800289 if (parcel == nullptr) return BAD_VALUE;
290 return parcel->writeInt32((int32_t)mType)
291 ?: parcel->writeInt32(mId)
292 ?: mType == TYPE_ID
293 ? NO_ERROR
294 : parcel->writeInt32((int32_t)mOptionFlags)
295 ?: parcel->writeDouble(mDurationMs)
296 ?: Interpolator<S, T>::writeToParcel(parcel);
297 }
298
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700299 status_t readFromParcel(const Parcel *parcel) override {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800300 int32_t type, optionFlags;
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700301 return parcel->readInt32(&type)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800302 ?: setType((Type)type)
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700303 ?: parcel->readInt32(&mId)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800304 ?: mType == TYPE_ID
305 ? NO_ERROR
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700306 : parcel->readInt32(&optionFlags)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800307 ?: setOptionFlags((OptionFlag)optionFlags)
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700308 ?: parcel->readDouble(&mDurationMs)
309 ?: Interpolator<S, T>::readFromParcel(*parcel)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800310 ?: checkCurve();
311 }
312
Andy Hungf3702642017-05-05 17:33:32 -0700313 // Returns a string for debug printing.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800314 std::string toString() const {
315 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700316 ss << "VolumeShaper::Configuration{mType=" << static_cast<int32_t>(mType);
317 ss << ", mId=" << mId;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800318 if (mType != TYPE_ID) {
Andy Hungf3702642017-05-05 17:33:32 -0700319 ss << ", mOptionFlags=" << static_cast<int32_t>(mOptionFlags);
320 ss << ", mDurationMs=" << mDurationMs;
321 ss << ", " << Interpolator<S, T>::toString().c_str();
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800322 }
Andy Hungf3702642017-05-05 17:33:32 -0700323 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800324 return ss.str();
325 }
326
327 private:
Andy Hungf3702642017-05-05 17:33:32 -0700328 Type mType; // type of configuration
329 int32_t mId; // A valid id is >= 0.
330 OptionFlag mOptionFlags; // option flags for the configuration.
331 double mDurationMs; // duration, must be > 0; default is 1000 ms.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800332 }; // Configuration
333
Andy Hungf3702642017-05-05 17:33:32 -0700334 /* VolumeShaper::Operation expresses an operation to perform on the
335 * configuration (either explicitly specified or an id).
336 *
337 * This parallels the Java implementation and the enums must match.
338 * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
339 * details on the Java implementation.
340 */
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700341 class Operation : public RefBase, public Parcelable {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800342 public:
Andy Hungf3702642017-05-05 17:33:32 -0700343 // Must match with VolumeShaper.java.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800344 enum Flag : int32_t {
345 FLAG_NONE = 0,
Andy Hungf3702642017-05-05 17:33:32 -0700346 FLAG_REVERSE = (1 << 0), // the absence of this indicates "play"
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800347 FLAG_TERMINATE = (1 << 1),
348 FLAG_JOIN = (1 << 2),
349 FLAG_DELAY = (1 << 3),
Andy Hung4ef88d72017-02-21 19:47:53 -0800350 FLAG_CREATE_IF_NECESSARY = (1 << 4),
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800351
Andy Hung4ef88d72017-02-21 19:47:53 -0800352 FLAG_ALL = (FLAG_REVERSE | FLAG_TERMINATE | FLAG_JOIN | FLAG_DELAY
353 | FLAG_CREATE_IF_NECESSARY),
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800354 };
355
356 Operation()
Andy Hung4ef88d72017-02-21 19:47:53 -0800357 : Operation(FLAG_NONE, -1 /* replaceId */) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800358 }
359
Andy Hung7d712bb2017-04-20 14:23:41 -0700360 Operation(Flag flags, int replaceId)
Andy Hung4ef88d72017-02-21 19:47:53 -0800361 : Operation(flags, replaceId, std::numeric_limits<S>::quiet_NaN() /* xOffset */) {
362 }
363
Andy Hung06a730b2020-04-09 13:28:31 -0700364 Operation(const Operation &operation)
Andy Hung4ef88d72017-02-21 19:47:53 -0800365 : Operation(operation.mFlags, operation.mReplaceId, operation.mXOffset) {
366 }
367
Andy Hung7d712bb2017-04-20 14:23:41 -0700368 explicit Operation(const sp<Operation> &operation)
369 : Operation(*operation.get()) {
370 }
371
372 Operation(Flag flags, int replaceId, S xOffset)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800373 : mFlags(flags)
Andy Hung4ef88d72017-02-21 19:47:53 -0800374 , mReplaceId(replaceId)
375 , mXOffset(xOffset) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800376 }
377
378 int32_t getReplaceId() const {
379 return mReplaceId;
380 }
381
382 void setReplaceId(int32_t replaceId) {
383 mReplaceId = replaceId;
384 }
385
Andy Hung4ef88d72017-02-21 19:47:53 -0800386 S getXOffset() const {
387 return mXOffset;
388 }
389
390 void setXOffset(S xOffset) {
Andy Hungf3702642017-05-05 17:33:32 -0700391 mXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
Andy Hung4ef88d72017-02-21 19:47:53 -0800392 }
393
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800394 Flag getFlags() const {
395 return mFlags;
396 }
397
Andy Hungf3702642017-05-05 17:33:32 -0700398 /* xOffset is the position on the volume curve and may go backwards
399 * if you are in reverse mode. This must be in the range from
400 * [MIN_CURVE_TIME, MAX_CURVE_TIME].
401 *
402 * normalizedTime always increases as time or framecount increases.
403 * normalizedTime is nominally from MIN_CURVE_TIME to MAX_CURVE_TIME when
404 * running through the curve, but could be outside this range afterwards.
405 * If you are reversing, this means the position on the curve, or xOffset,
406 * is computed as MAX_CURVE_TIME - normalizedTime, clamped to
407 * [MIN_CURVE_TIME, MAX_CURVE_TIME].
408 */
409 void setNormalizedTime(S normalizedTime) {
410 setXOffset((mFlags & FLAG_REVERSE) != 0
411 ? MAX_CURVE_TIME - normalizedTime : normalizedTime);
412 }
413
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800414 status_t setFlags(Flag flags) {
415 if ((flags & ~FLAG_ALL) != 0) {
416 ALOGE("flags has invalid bits: %#x", flags);
417 return BAD_VALUE;
418 }
419 mFlags = flags;
420 return NO_ERROR;
421 }
422
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700423 status_t writeToParcel(Parcel *parcel) const override {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800424 if (parcel == nullptr) return BAD_VALUE;
425 return parcel->writeInt32((int32_t)mFlags)
Andy Hung4ef88d72017-02-21 19:47:53 -0800426 ?: parcel->writeInt32(mReplaceId)
427 ?: parcel->writeFloat(mXOffset);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800428 }
429
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700430 status_t readFromParcel(const Parcel *parcel) override {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800431 int32_t flags;
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700432 return parcel->readInt32(&flags)
433 ?: parcel->readInt32(&mReplaceId)
434 ?: parcel->readFloat(&mXOffset)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800435 ?: setFlags((Flag)flags);
436 }
437
438 std::string toString() const {
439 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700440 ss << "VolumeShaper::Operation{mFlags=" << static_cast<int32_t>(mFlags) ;
441 ss << ", mReplaceId=" << mReplaceId;
442 ss << ", mXOffset=" << mXOffset;
443 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800444 return ss.str();
445 }
446
447 private:
Andy Hungf3702642017-05-05 17:33:32 -0700448 Flag mFlags; // operation to do
449 int32_t mReplaceId; // if >= 0 the id to remove in a replace operation.
450 S mXOffset; // position in the curve to set if a valid number (not nan)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800451 }; // Operation
452
Andy Hungf3702642017-05-05 17:33:32 -0700453 /* VolumeShaper.State is returned when requesting the last
454 * state of the VolumeShaper.
455 *
456 * This parallels the Java implementation.
457 * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
458 * details on the Java implementation.
459 */
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700460 class State : public RefBase, public Parcelable {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800461 public:
Andy Hung7d712bb2017-04-20 14:23:41 -0700462 State(T volume, S xOffset)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800463 : mVolume(volume)
464 , mXOffset(xOffset) {
465 }
466
467 State()
Andy Hungf3702642017-05-05 17:33:32 -0700468 : State(NAN, NAN) { }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800469
470 T getVolume() const {
471 return mVolume;
472 }
473
474 void setVolume(T volume) {
475 mVolume = volume;
476 }
477
478 S getXOffset() const {
479 return mXOffset;
480 }
481
482 void setXOffset(S xOffset) {
483 mXOffset = xOffset;
484 }
485
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700486 status_t writeToParcel(Parcel *parcel) const override {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800487 if (parcel == nullptr) return BAD_VALUE;
488 return parcel->writeFloat(mVolume)
489 ?: parcel->writeFloat(mXOffset);
490 }
491
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700492 status_t readFromParcel(const Parcel *parcel) override {
493 return parcel->readFloat(&mVolume)
494 ?: parcel->readFloat(&mXOffset);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800495 }
496
497 std::string toString() const {
498 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700499 ss << "VolumeShaper::State{mVolume=" << mVolume;
500 ss << ", mXOffset=" << mXOffset;
501 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800502 return ss.str();
503 }
504
505 private:
Andy Hungf3702642017-05-05 17:33:32 -0700506 T mVolume; // linear volume in the range MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME
507 S mXOffset; // position on curve expressed from MIN_CURVE_TIME to MAX_CURVE_TIME
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800508 }; // State
509
Andy Hungf3702642017-05-05 17:33:32 -0700510 // Internal helper class to do an affine transform for time and amplitude scaling.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800511 template <typename R>
512 class Translate {
513 public:
514 Translate()
515 : mOffset(0)
516 , mScale(1) {
517 }
518
519 R getOffset() const {
520 return mOffset;
521 }
522
523 void setOffset(R offset) {
524 mOffset = offset;
525 }
526
527 R getScale() const {
528 return mScale;
529 }
530
531 void setScale(R scale) {
532 mScale = scale;
533 }
534
535 R operator()(R in) const {
536 return mScale * (in - mOffset);
537 }
538
539 std::string toString() const {
540 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700541 ss << "VolumeShaper::Translate{mOffset=" << mOffset;
542 ss << ", mScale=" << mScale;
543 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800544 return ss.str();
545 }
546
547 private:
548 R mOffset;
549 R mScale;
550 }; // Translate
551
552 static int64_t convertTimespecToUs(const struct timespec &tv)
553 {
Chih-Hung Hsieh21b96162018-12-11 13:48:32 -0800554 return tv.tv_sec * 1000000LL + tv.tv_nsec / 1000;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800555 }
556
557 // current monotonic time in microseconds.
558 static int64_t getNowUs()
559 {
560 struct timespec tv;
561 if (clock_gettime(CLOCK_MONOTONIC, &tv) != 0) {
562 return 0; // system is really sick, just return 0 for consistency.
563 }
564 return convertTimespecToUs(tv);
565 }
566
Andy Hungf3702642017-05-05 17:33:32 -0700567 /* Native implementation of VolumeShaper. This is NOT mirrored
568 * on the Java side, so we don't need to mimic Java side layout
569 * and data; furthermore, this isn't refcounted as a "RefBase" object.
570 *
571 * Since we pass configuration and operation as shared pointers (like
572 * Java) there is a potential risk that the caller may modify
573 * these after delivery.
574 */
Andy Hung7d712bb2017-04-20 14:23:41 -0700575 VolumeShaper(
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800576 const sp<VolumeShaper::Configuration> &configuration,
577 const sp<VolumeShaper::Operation> &operation)
578 : mConfiguration(configuration) // we do not make a copy
579 , mOperation(operation) // ditto
580 , mStartFrame(-1)
581 , mLastVolume(T(1))
Andy Hungf3702642017-05-05 17:33:32 -0700582 , mLastXOffset(MIN_CURVE_TIME)
583 , mDelayXOffset(MIN_CURVE_TIME) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800584 if (configuration.get() != nullptr
585 && (getFlags() & VolumeShaper::Operation::FLAG_DELAY) == 0) {
586 mLastVolume = configuration->first().second;
587 }
588 }
589
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800590 // We allow a null operation here, though VolumeHandler always provides one.
591 VolumeShaper::Operation::Flag getFlags() const {
592 return mOperation == nullptr
Andy Hungf3702642017-05-05 17:33:32 -0700593 ? VolumeShaper::Operation::FLAG_NONE : mOperation->getFlags();
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800594 }
595
Andy Hungf3702642017-05-05 17:33:32 -0700596 /* Returns the last volume and xoffset reported to the AudioFlinger.
597 * If the VolumeShaper has not been started, compute what the volume
598 * should be based on the initial offset specified.
599 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800600 sp<VolumeShaper::State> getState() const {
Andy Hungf3702642017-05-05 17:33:32 -0700601 if (!isStarted()) {
602 const T volume = computeVolumeFromXOffset(mDelayXOffset);
603 VS_LOG("delayed VolumeShaper, using cached offset:%f for volume:%f",
604 mDelayXOffset, volume);
605 return new VolumeShaper::State(volume, mDelayXOffset);
606 } else {
607 return new VolumeShaper::State(mLastVolume, mLastXOffset);
608 }
609 }
610
611 S getDelayXOffset() const {
612 return mDelayXOffset;
Andy Hung4ef88d72017-02-21 19:47:53 -0800613 }
614
615 void setDelayXOffset(S xOffset) {
Andy Hungf3702642017-05-05 17:33:32 -0700616 mDelayXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800617 }
618
Andy Hung39399b62017-04-21 15:07:45 -0700619 bool isStarted() const {
620 return mStartFrame >= 0;
621 }
622
Andy Hungf3702642017-05-05 17:33:32 -0700623 /* getVolume() updates the last volume/xoffset state so it is not
624 * const, even though logically it may be viewed as const.
625 */
Andy Hung10cbff12017-02-21 17:30:14 -0800626 std::pair<T /* volume */, bool /* active */> getVolume(
627 int64_t trackFrameCount, double trackSampleRate) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800628 if ((getFlags() & VolumeShaper::Operation::FLAG_DELAY) != 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700629 // We haven't had PLAY called yet, so just return the value
630 // as if PLAY were called just now.
631 VS_LOG("delayed VolumeShaper, using cached offset %f", mDelayXOffset);
632 const T volume = computeVolumeFromXOffset(mDelayXOffset);
633 return std::make_pair(volume, false);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800634 }
635 const bool clockTime = (mConfiguration->getOptionFlags()
636 & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
637 const int64_t frameCount = clockTime ? getNowUs() : trackFrameCount;
638 const double sampleRate = clockTime ? 1000000 : trackSampleRate;
639
640 if (mStartFrame < 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700641 updatePosition(frameCount, sampleRate, mDelayXOffset);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800642 mStartFrame = frameCount;
643 }
644 VS_LOG("frameCount: %lld", (long long)frameCount);
Andy Hungf3702642017-05-05 17:33:32 -0700645 const S x = mXTranslate((T)frameCount);
646 VS_LOG("translation to normalized time: %f", x);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800647
Andy Hungf3702642017-05-05 17:33:32 -0700648 std::tuple<T /* volume */, S /* position */, bool /* active */> vt =
649 computeStateFromNormalizedTime(x);
650
651 mLastVolume = std::get<0>(vt);
652 mLastXOffset = std::get<1>(vt);
653 const bool active = std::get<2>(vt);
654 VS_LOG("rescaled time:%f volume:%f xOffset:%f active:%s",
655 x, mLastVolume, mLastXOffset, active ? "true" : "false");
656 return std::make_pair(mLastVolume, active);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800657 }
658
659 std::string toString() const {
660 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700661 ss << "VolumeShaper{mStartFrame=" << mStartFrame;
662 ss << ", mXTranslate=" << mXTranslate.toString().c_str();
663 ss << ", mConfiguration=" <<
664 (mConfiguration.get() == nullptr
665 ? "nullptr" : mConfiguration->toString().c_str());
666 ss << ", mOperation=" <<
667 (mOperation.get() == nullptr
668 ? "nullptr" : mOperation->toString().c_str());
669 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800670 return ss.str();
671 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800672
Andy Hungf3702642017-05-05 17:33:32 -0700673 Translate<S> mXTranslate; // translation from frames (usec for clock time) to normalized time.
Andy Hung4ef88d72017-02-21 19:47:53 -0800674 sp<VolumeShaper::Configuration> mConfiguration;
675 sp<VolumeShaper::Operation> mOperation;
Andy Hungf3702642017-05-05 17:33:32 -0700676
677private:
Andy Hung4ef88d72017-02-21 19:47:53 -0800678 int64_t mStartFrame; // starting frame, non-negative when started (in usec for clock time)
679 T mLastVolume; // last computed interpolated volume (y-axis)
680 S mLastXOffset; // last computed interpolated xOffset/time (x-axis)
Andy Hungf3702642017-05-05 17:33:32 -0700681 S mDelayXOffset; // xOffset to use for first invocation of VolumeShaper.
682
683 // Called internally to adjust mXTranslate for first time start.
684 void updatePosition(int64_t startFrame, double sampleRate, S xOffset) {
685 double scale = (mConfiguration->last().first - mConfiguration->first().first)
686 / (mConfiguration->getDurationMs() * 0.001 * sampleRate);
687 const double minScale = 1. / static_cast<double>(INT64_MAX);
688 scale = std::max(scale, minScale);
689 VS_LOG("update position: scale %lf frameCount:%lld, sampleRate:%lf, xOffset:%f",
690 scale, (long long) startFrame, sampleRate, xOffset);
691
692 S normalizedTime = (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) != 0 ?
693 MAX_CURVE_TIME - xOffset : xOffset;
694 mXTranslate.setOffset(static_cast<float>(static_cast<double>(startFrame)
695 - static_cast<double>(normalizedTime) / scale));
696 mXTranslate.setScale(static_cast<float>(scale));
697 VS_LOG("translate: %s", mXTranslate.toString().c_str());
698 }
699
700 T computeVolumeFromXOffset(S xOffset) const {
701 const T unscaledVolume = mConfiguration->findY(xOffset);
702 const T volume = mConfiguration->adjustVolume(unscaledVolume); // handle log scale
703 VS_LOG("computeVolumeFromXOffset %f -> %f -> %f", xOffset, unscaledVolume, volume);
704 return volume;
705 }
706
707 std::tuple<T /* volume */, S /* position */, bool /* active */>
708 computeStateFromNormalizedTime(S x) const {
709 bool active = true;
710 // handle reversal of position
711 if (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) {
712 x = MAX_CURVE_TIME - x;
713 VS_LOG("reversing to %f", x);
714 if (x < MIN_CURVE_TIME) {
715 x = MIN_CURVE_TIME;
716 active = false; // at the end
717 } else if (x > MAX_CURVE_TIME) {
718 x = MAX_CURVE_TIME; //early
719 }
720 } else {
721 if (x < MIN_CURVE_TIME) {
722 x = MIN_CURVE_TIME; // early
723 } else if (x > MAX_CURVE_TIME) {
724 x = MAX_CURVE_TIME;
725 active = false; // at end
726 }
727 }
728 const S xOffset = x;
729 const T volume = computeVolumeFromXOffset(xOffset);
730 return std::make_tuple(volume, xOffset, active);
731 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800732}; // VolumeShaper
733
Andy Hungf3702642017-05-05 17:33:32 -0700734/* VolumeHandler combines the volume factors of multiple VolumeShapers associated
735 * with a player. It is thread safe by synchronizing all public methods.
736 *
737 * This is a native-only implementation.
738 *
739 * The server side VolumeHandler is used to maintain a list of volume handlers,
740 * keep state, and obtain volume.
741 *
742 * The client side VolumeHandler is used to maintain a list of volume handlers,
743 * keep some partial state, and restore if the server dies.
744 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800745class VolumeHandler : public RefBase {
746public:
747 using S = float;
748 using T = float;
749
Andy Hung4ef88d72017-02-21 19:47:53 -0800750 // A volume handler which just keeps track of active VolumeShapers does not need sampleRate.
751 VolumeHandler()
752 : VolumeHandler(0 /* sampleRate */) {
753 }
754
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800755 explicit VolumeHandler(uint32_t sampleRate)
756 : mSampleRate((double)sampleRate)
Andy Hung4ef88d72017-02-21 19:47:53 -0800757 , mLastFrame(0)
Andy Hungf3702642017-05-05 17:33:32 -0700758 , mVolumeShaperIdCounter(VolumeShaper::kSystemVolumeShapersMax)
Andy Hungda540db2017-04-20 14:06:17 -0700759 , mLastVolume(1.f, false) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800760 }
761
762 VolumeShaper::Status applyVolumeShaper(
763 const sp<VolumeShaper::Configuration> &configuration,
Andy Hungf3702642017-05-05 17:33:32 -0700764 const sp<VolumeShaper::Operation> &operation_in) {
765 // make a local copy of operation, as we modify it.
766 sp<VolumeShaper::Operation> operation(new VolumeShaper::Operation(operation_in));
Andy Hung10cbff12017-02-21 17:30:14 -0800767 VS_LOG("applyVolumeShaper:configuration: %s", configuration->toString().c_str());
768 VS_LOG("applyVolumeShaper:operation: %s", operation->toString().c_str());
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800769 AutoMutex _l(mLock);
770 if (configuration == nullptr) {
771 ALOGE("null configuration");
772 return VolumeShaper::Status(BAD_VALUE);
773 }
774 if (operation == nullptr) {
775 ALOGE("null operation");
776 return VolumeShaper::Status(BAD_VALUE);
777 }
778 const int32_t id = configuration->getId();
779 if (id < 0) {
780 ALOGE("negative id: %d", id);
781 return VolumeShaper::Status(BAD_VALUE);
782 }
783 VS_LOG("applyVolumeShaper id: %d", id);
784
785 switch (configuration->getType()) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800786 case VolumeShaper::Configuration::TYPE_SCALE: {
787 const int replaceId = operation->getReplaceId();
788 if (replaceId >= 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700789 VS_LOG("replacing %d", replaceId);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800790 auto replaceIt = findId_l(replaceId);
791 if (replaceIt == mVolumeShapers.end()) {
792 ALOGW("cannot find replace id: %d", replaceId);
793 } else {
Andy Hungf3702642017-05-05 17:33:32 -0700794 if ((operation->getFlags() & VolumeShaper::Operation::FLAG_JOIN) != 0) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800795 // For join, we scale the start volume of the current configuration
796 // to match the last-used volume of the replacing VolumeShaper.
797 auto state = replaceIt->getState();
Andy Hungf3702642017-05-05 17:33:32 -0700798 ALOGD("join: state:%s", state->toString().c_str());
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800799 if (state->getXOffset() >= 0) { // valid
800 const T volume = state->getVolume();
801 ALOGD("join: scaling start volume to %f", volume);
802 configuration->scaleToStartVolume(volume);
803 }
804 }
805 (void)mVolumeShapers.erase(replaceIt);
806 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800807 operation->setReplaceId(-1);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800808 }
809 // check if we have another of the same id.
810 auto oldIt = findId_l(id);
811 if (oldIt != mVolumeShapers.end()) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800812 if ((operation->getFlags()
813 & VolumeShaper::Operation::FLAG_CREATE_IF_NECESSARY) != 0) {
814 // TODO: move the case to a separate function.
815 goto HANDLE_TYPE_ID; // no need to create, take over existing id.
816 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800817 ALOGW("duplicate id, removing old %d", id);
818 (void)mVolumeShapers.erase(oldIt);
819 }
Andy Hungf3702642017-05-05 17:33:32 -0700820
821 /* Check if too many application VolumeShapers (with id >= kSystemVolumeShapersMax).
822 * We check on the server side to ensure synchronization and robustness.
823 *
824 * This shouldn't fail on a replace command unless the replaced id is
825 * already invalid (which *should* be checked in the Java layer).
826 */
827 if (id >= VolumeShaper::kSystemVolumeShapersMax
828 && numberOfUserVolumeShapers_l() >= VolumeShaper::kUserVolumeShapersMax) {
829 ALOGW("Too many app VolumeShapers, cannot add to VolumeHandler");
830 return VolumeShaper::Status(INVALID_OPERATION);
831 }
832
833 // create new VolumeShaper with default behavior.
834 mVolumeShapers.emplace_back(configuration, new VolumeShaper::Operation());
835 VS_LOG("after adding, number of volumeShapers:%zu", mVolumeShapers.size());
Andy Hung10cbff12017-02-21 17:30:14 -0800836 }
837 // fall through to handle the operation
Andy Hung4ef88d72017-02-21 19:47:53 -0800838 HANDLE_TYPE_ID:
Andy Hung10cbff12017-02-21 17:30:14 -0800839 case VolumeShaper::Configuration::TYPE_ID: {
840 VS_LOG("trying to find id: %d", id);
841 auto it = findId_l(id);
842 if (it == mVolumeShapers.end()) {
843 VS_LOG("couldn't find id: %d", id);
844 return VolumeShaper::Status(INVALID_OPERATION);
845 }
Andy Hungf3702642017-05-05 17:33:32 -0700846 if ((operation->getFlags() & VolumeShaper::Operation::FLAG_TERMINATE) != 0) {
Andy Hung10cbff12017-02-21 17:30:14 -0800847 VS_LOG("terminate id: %d", id);
848 mVolumeShapers.erase(it);
849 break;
850 }
851 const bool clockTime = (it->mConfiguration->getOptionFlags()
852 & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
853 if ((it->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) !=
854 (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE)) {
Andy Hungf3702642017-05-05 17:33:32 -0700855 if (it->isStarted()) {
856 const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
857 const S x = it->mXTranslate((T)frameCount);
858 VS_LOG("reverse normalizedTime: %f", x);
859 // reflect position
860 S target = MAX_CURVE_TIME - x;
861 if (target < MIN_CURVE_TIME) {
862 VS_LOG("clamp to start - begin immediately");
863 target = MIN_CURVE_TIME;
864 }
865 VS_LOG("reverse normalizedTime target: %f", target);
866 it->mXTranslate.setOffset(it->mXTranslate.getOffset()
867 + (x - target) / it->mXTranslate.getScale());
Andy Hung10cbff12017-02-21 17:30:14 -0800868 }
Andy Hungf3702642017-05-05 17:33:32 -0700869 // if not started, the delay offset doesn't change.
Andy Hung10cbff12017-02-21 17:30:14 -0800870 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800871 const S xOffset = operation->getXOffset();
872 if (!std::isnan(xOffset)) {
Andy Hungf3702642017-05-05 17:33:32 -0700873 if (it->isStarted()) {
874 const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
875 const S x = it->mXTranslate((T)frameCount);
876 VS_LOG("normalizedTime translation: %f", x);
877 const S target =
878 (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) != 0 ?
879 MAX_CURVE_TIME - xOffset : xOffset;
880 VS_LOG("normalizedTime target x offset: %f", target);
881 it->mXTranslate.setOffset(it->mXTranslate.getOffset()
882 + (x - target) / it->mXTranslate.getScale());
883 } else {
884 it->setDelayXOffset(xOffset);
885 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800886 }
Andy Hung10cbff12017-02-21 17:30:14 -0800887 it->mOperation = operation; // replace the operation
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800888 } break;
889 }
890 return VolumeShaper::Status(id);
891 }
892
893 sp<VolumeShaper::State> getVolumeShaperState(int id) {
894 AutoMutex _l(mLock);
895 auto it = findId_l(id);
896 if (it == mVolumeShapers.end()) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800897 VS_LOG("cannot find state for id: %d", id);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800898 return nullptr;
899 }
900 return it->getState();
901 }
902
Andy Hungf3702642017-05-05 17:33:32 -0700903 /* getVolume() is not const, as it updates internal state.
904 * Once called, any VolumeShapers not already started begin running.
905 */
Andy Hung10cbff12017-02-21 17:30:14 -0800906 std::pair<T /* volume */, bool /* active */> getVolume(int64_t trackFrameCount) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800907 AutoMutex _l(mLock);
908 mLastFrame = trackFrameCount;
909 T volume(1);
Andy Hung10cbff12017-02-21 17:30:14 -0800910 size_t activeCount = 0;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800911 for (auto it = mVolumeShapers.begin(); it != mVolumeShapers.end();) {
Andy Hungf3702642017-05-05 17:33:32 -0700912 const std::pair<T, bool> shaperVolume =
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800913 it->getVolume(trackFrameCount, mSampleRate);
914 volume *= shaperVolume.first;
Andy Hung10cbff12017-02-21 17:30:14 -0800915 activeCount += shaperVolume.second;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800916 ++it;
917 }
Andy Hungda540db2017-04-20 14:06:17 -0700918 mLastVolume = std::make_pair(volume, activeCount != 0);
Andy Hungf3702642017-05-05 17:33:32 -0700919 VS_LOG("getVolume: <%f, %s>", mLastVolume.first, mLastVolume.second ? "true" : "false");
Andy Hungda540db2017-04-20 14:06:17 -0700920 return mLastVolume;
921 }
922
Andy Hungf3702642017-05-05 17:33:32 -0700923 /* Used by a client side VolumeHandler to ensure all the VolumeShapers
924 * indicate that they have been started. Upon a change in audioserver
925 * output sink, this information is used for restoration of the server side
926 * VolumeHandler.
927 */
Andy Hung39399b62017-04-21 15:07:45 -0700928 void setStarted() {
929 (void)getVolume(mLastFrame); // getVolume() will start the individual VolumeShapers.
930 }
931
Andy Hungda540db2017-04-20 14:06:17 -0700932 std::pair<T /* volume */, bool /* active */> getLastVolume() const {
933 AutoMutex _l(mLock);
934 return mLastVolume;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800935 }
936
937 std::string toString() const {
938 AutoMutex _l(mLock);
939 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700940 ss << "VolumeHandler{mSampleRate=" << mSampleRate;
941 ss << ", mLastFrame=" << mLastFrame;
942 ss << ", mVolumeShapers={";
943 bool first = true;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800944 for (const auto &shaper : mVolumeShapers) {
Andy Hungf3702642017-05-05 17:33:32 -0700945 if (first) {
946 first = false;
947 } else {
948 ss << ", ";
949 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800950 ss << shaper.toString().c_str();
951 }
Andy Hungf3702642017-05-05 17:33:32 -0700952 ss << "}}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800953 return ss.str();
954 }
955
Andy Hung39399b62017-04-21 15:07:45 -0700956 void forall(const std::function<VolumeShaper::Status (const VolumeShaper &)> &lambda) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800957 AutoMutex _l(mLock);
Andy Hungda540db2017-04-20 14:06:17 -0700958 VS_LOG("forall: mVolumeShapers.size() %zu", mVolumeShapers.size());
Andy Hung4ef88d72017-02-21 19:47:53 -0800959 for (const auto &shaper : mVolumeShapers) {
Andy Hung39399b62017-04-21 15:07:45 -0700960 VolumeShaper::Status status = lambda(shaper);
961 VS_LOG("forall applying lambda on shaper (%p): %d", &shaper, (int)status);
Andy Hung4ef88d72017-02-21 19:47:53 -0800962 }
963 }
964
965 void reset() {
966 AutoMutex _l(mLock);
967 mVolumeShapers.clear();
Andy Hung7d712bb2017-04-20 14:23:41 -0700968 mLastFrame = 0;
Andy Hung4ef88d72017-02-21 19:47:53 -0800969 // keep mVolumeShaperIdCounter as is.
970 }
971
Andy Hungf3702642017-05-05 17:33:32 -0700972 /* Sets the configuration id if necessary - This is based on the counter
973 * internal to the VolumeHandler.
974 */
Andy Hung4ef88d72017-02-21 19:47:53 -0800975 void setIdIfNecessary(const sp<VolumeShaper::Configuration> &configuration) {
976 if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
977 const int id = configuration->getId();
978 if (id == -1) {
979 // Reassign to a unique id, skipping system ids.
980 AutoMutex _l(mLock);
981 while (true) {
982 if (mVolumeShaperIdCounter == INT32_MAX) {
Andy Hungf3702642017-05-05 17:33:32 -0700983 mVolumeShaperIdCounter = VolumeShaper::kSystemVolumeShapersMax;
Andy Hung4ef88d72017-02-21 19:47:53 -0800984 } else {
985 ++mVolumeShaperIdCounter;
986 }
987 if (findId_l(mVolumeShaperIdCounter) != mVolumeShapers.end()) {
988 continue; // collision with an existing id.
989 }
990 configuration->setId(mVolumeShaperIdCounter);
991 ALOGD("setting id to %d", mVolumeShaperIdCounter);
992 break;
993 }
994 }
995 }
996 }
997
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800998private:
999 std::list<VolumeShaper>::iterator findId_l(int32_t id) {
1000 std::list<VolumeShaper>::iterator it = mVolumeShapers.begin();
1001 for (; it != mVolumeShapers.end(); ++it) {
1002 if (it->mConfiguration->getId() == id) {
1003 break;
1004 }
1005 }
1006 return it;
1007 }
1008
Andy Hungf3702642017-05-05 17:33:32 -07001009 size_t numberOfUserVolumeShapers_l() const {
1010 size_t count = 0;
1011 for (const auto &shaper : mVolumeShapers) {
1012 count += (shaper.mConfiguration->getId() >= VolumeShaper::kSystemVolumeShapersMax);
1013 }
1014 return count;
1015 }
1016
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001017 mutable Mutex mLock;
1018 double mSampleRate; // in samples (frames) per second
Andy Hung7d712bb2017-04-20 14:23:41 -07001019 int64_t mLastFrame; // logging purpose only, 0 on start
Andy Hung4ef88d72017-02-21 19:47:53 -08001020 int32_t mVolumeShaperIdCounter; // a counter to return a unique volume shaper id.
Andy Hungda540db2017-04-20 14:06:17 -07001021 std::pair<T /* volume */, bool /* active */> mLastVolume;
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001022 std::list<VolumeShaper> mVolumeShapers; // list provides stable iterators on erase
1023}; // VolumeHandler
1024
Ivan Lozano8cf3a072017-08-09 09:01:33 -07001025} // namespace media
1026
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001027} // namespace android
1028
1029#pragma pop_macro("LOG_TAG")
1030
1031#endif // ANDROID_VOLUME_SHAPER_H