blob: f8ead2fd63db0e18abe9243de91fd963cf893799 [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
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -070025#include <android/media/VolumeShaperConfiguration.h>
26#include <android/media/VolumeShaperConfigurationOptionFlag.h>
27#include <android/media/VolumeShaperOperation.h>
28#include <android/media/VolumeShaperOperationFlag.h>
29#include <android/media/VolumeShaperState.h>
Andy Hung9fc8b5c2017-01-24 13:36:48 -080030#include <binder/Parcel.h>
31#include <media/Interpolator.h>
32#include <utils/Mutex.h>
33#include <utils/RefBase.h>
34
35#pragma push_macro("LOG_TAG")
36#undef LOG_TAG
37#define LOG_TAG "VolumeShaper"
38
39// turn on VolumeShaper logging
Colin Cross4e399992017-04-27 16:15:51 -070040#define VS_LOGGING 0
41#define VS_LOG(...) ALOGD_IF(VS_LOGGING, __VA_ARGS__)
Andy Hung9fc8b5c2017-01-24 13:36:48 -080042
43namespace android {
44
Ivan Lozano8cf3a072017-08-09 09:01:33 -070045namespace media {
46
Andy Hung9fc8b5c2017-01-24 13:36:48 -080047// The native VolumeShaper class mirrors the java VolumeShaper class;
48// in addition, the native class contains implementation for actual operation.
49//
50// VolumeShaper methods are not safe for multiple thread access.
51// Use VolumeHandler for thread-safe encapsulation of multiple VolumeShapers.
52//
53// Classes below written are to avoid naked pointers so there are no
54// explicit destructors required.
55
56class VolumeShaper {
57public:
Andy Hungf3702642017-05-05 17:33:32 -070058 // S and T are like template typenames (matching the Interpolator<S, T>)
59 using S = float; // time type
60 using T = float; // volume type
Andy Hung9fc8b5c2017-01-24 13:36:48 -080061
Andy Hungf3702642017-05-05 17:33:32 -070062// Curve and dimension information
63// TODO: member static const or constexpr float initialization not permitted in C++11
64#define MIN_CURVE_TIME 0.f // type S: start of VolumeShaper curve (normalized)
65#define MAX_CURVE_TIME 1.f // type S: end of VolumeShaper curve (normalized)
66#define MIN_LINEAR_VOLUME 0.f // type T: silence / mute audio
67#define MAX_LINEAR_VOLUME 1.f // type T: max volume, unity gain
68#define MAX_LOG_VOLUME 0.f // type T: max volume, unity gain in dBFS
Andy Hung9fc8b5c2017-01-24 13:36:48 -080069
Andy Hungf3702642017-05-05 17:33:32 -070070 /* kSystemVolumeShapersMax is the maximum number of system VolumeShapers.
71 * Each system VolumeShapers has a predefined Id, which ranges from 0
72 * to kSystemVolumeShapersMax - 1 and is unique for its usage.
73 *
74 * "1" is reserved for system ducking.
75 */
76 static const int kSystemVolumeShapersMax = 16;
77
78 /* kUserVolumeShapersMax is the maximum number of application
79 * VolumeShapers for a player/track. Application VolumeShapers are
80 * assigned on creation by the client, and have Ids ranging
81 * from kSystemVolumeShapersMax to INT32_MAX.
82 *
83 * The number of user/application volume shapers is independent to the
84 * system volume shapers. If an application tries to create more than
85 * kUserVolumeShapersMax to a player, then the apply() will fail.
86 * This prevents exhausting server side resources by a potentially malicious
87 * application.
88 */
89 static const int kUserVolumeShapersMax = 16;
90
91 /* VolumeShaper::Status is equivalent to status_t if negative
92 * but if non-negative represents the id operated on.
93 * It must be expressible as an int32_t for binder purposes.
94 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -080095 using Status = status_t;
96
Andy Hungf3702642017-05-05 17:33:32 -070097 // Local definition for clamp as std::clamp is included in C++17 only.
98 // TODO: use the std::clamp version when Android build uses C++17.
99 template<typename R>
100 static constexpr const R &clamp(const R &v, const R &lo, const R &hi) {
101 return (v < lo) ? lo : (hi < v) ? hi : v;
102 }
103
104 /* VolumeShaper.Configuration derives from the Interpolator class and adds
105 * parameters relating to the volume shape.
106 *
107 * This parallels the Java implementation and the enums must match.
108 * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
109 * details on the Java implementation.
110 */
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700111 class Configuration : public Interpolator<S, T>, public RefBase, public Parcelable {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800112 public:
Andy Hungf3702642017-05-05 17:33:32 -0700113 // Must match with VolumeShaper.java in frameworks/base.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800114 enum Type : int32_t {
115 TYPE_ID,
116 TYPE_SCALE,
117 };
118
Andy Hungf3702642017-05-05 17:33:32 -0700119 // Must match with VolumeShaper.java in frameworks/base.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800120 enum OptionFlag : int32_t {
121 OPTION_FLAG_NONE = 0,
122 OPTION_FLAG_VOLUME_IN_DBFS = (1 << 0),
123 OPTION_FLAG_CLOCK_TIME = (1 << 1),
124
125 OPTION_FLAG_ALL = (OPTION_FLAG_VOLUME_IN_DBFS | OPTION_FLAG_CLOCK_TIME),
126 };
127
Andy Hungf3702642017-05-05 17:33:32 -0700128 // Bring from base class; must match with VolumeShaper.java in frameworks/base.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800129 using InterpolatorType = Interpolator<S, T>::InterpolatorType;
130
131 Configuration()
132 : Interpolator<S, T>()
Colin Cross11280a12017-05-02 10:32:56 -0700133 , RefBase()
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800134 , mType(TYPE_SCALE)
Colin Cross4e399992017-04-27 16:15:51 -0700135 , mId(-1)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800136 , mOptionFlags(OPTION_FLAG_NONE)
Colin Cross4e399992017-04-27 16:15:51 -0700137 , mDurationMs(1000.) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800138 }
139
Andy Hung06a730b2020-04-09 13:28:31 -0700140 Configuration(const Configuration &configuration)
Andy Hung10cbff12017-02-21 17:30:14 -0800141 : Interpolator<S, T>(*static_cast<const Interpolator<S, T> *>(&configuration))
Colin Cross11280a12017-05-02 10:32:56 -0700142 , RefBase()
Andy Hung10cbff12017-02-21 17:30:14 -0800143 , mType(configuration.mType)
Colin Cross4e399992017-04-27 16:15:51 -0700144 , mId(configuration.mId)
Andy Hung10cbff12017-02-21 17:30:14 -0800145 , mOptionFlags(configuration.mOptionFlags)
Colin Cross4e399992017-04-27 16:15:51 -0700146 , mDurationMs(configuration.mDurationMs) {
Andy Hung10cbff12017-02-21 17:30:14 -0800147 }
148
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800149 Type getType() const {
150 return mType;
151 }
152
153 status_t setType(Type type) {
154 switch (type) {
155 case TYPE_ID:
156 case TYPE_SCALE:
157 mType = type;
158 return NO_ERROR;
159 default:
160 ALOGE("invalid Type: %d", type);
161 return BAD_VALUE;
162 }
163 }
164
165 OptionFlag getOptionFlags() const {
166 return mOptionFlags;
167 }
168
169 status_t setOptionFlags(OptionFlag optionFlags) {
170 if ((optionFlags & ~OPTION_FLAG_ALL) != 0) {
171 ALOGE("optionFlags has invalid bits: %#x", optionFlags);
172 return BAD_VALUE;
173 }
174 mOptionFlags = optionFlags;
175 return NO_ERROR;
176 }
177
178 double getDurationMs() const {
179 return mDurationMs;
180 }
181
Andy Hungf3702642017-05-05 17:33:32 -0700182 status_t setDurationMs(double durationMs) {
183 if (durationMs > 0.) {
184 mDurationMs = durationMs;
185 return NO_ERROR;
186 }
187 // zero, negative, or nan. These values not possible from Java.
188 return BAD_VALUE;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800189 }
190
191 int32_t getId() const {
192 return mId;
193 }
194
195 void setId(int32_t id) {
Andy Hungf3702642017-05-05 17:33:32 -0700196 // We permit a negative id here (representing invalid).
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800197 mId = id;
198 }
199
Andy Hungf3702642017-05-05 17:33:32 -0700200 /* Adjust the volume to be in linear range from MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME
201 * and compensate for log dbFS volume as needed.
202 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800203 T adjustVolume(T volume) const {
204 if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700205 const T out = powf(10.f, volume / 10.f);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800206 VS_LOG("in: %f out: %f", volume, out);
207 volume = out;
208 }
Andy Hungf3702642017-05-05 17:33:32 -0700209 return clamp(volume, MIN_LINEAR_VOLUME /* lo */, MAX_LINEAR_VOLUME /* hi */);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800210 }
211
Andy Hungf3702642017-05-05 17:33:32 -0700212 /* Check if the existing curve is valid.
213 */
214 status_t checkCurve() const {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800215 if (mType == TYPE_ID) return NO_ERROR;
216 if (this->size() < 2) {
217 ALOGE("curve must have at least 2 points");
218 return BAD_VALUE;
219 }
Andy Hungf3702642017-05-05 17:33:32 -0700220 if (first().first != MIN_CURVE_TIME || last().first != MAX_CURVE_TIME) {
221 ALOGE("curve must start at MIN_CURVE_TIME and end at MAX_CURVE_TIME");
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800222 return BAD_VALUE;
223 }
224 if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
225 for (const auto &pt : *this) {
Andy Hungf3702642017-05-05 17:33:32 -0700226 if (!(pt.second <= MAX_LOG_VOLUME) /* handle nan */) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800227 ALOGE("positive volume dbFS");
228 return BAD_VALUE;
229 }
230 }
231 } else {
232 for (const auto &pt : *this) {
Andy Hungf3702642017-05-05 17:33:32 -0700233 if (!(pt.second >= MIN_LINEAR_VOLUME)
234 || !(pt.second <= MAX_LINEAR_VOLUME) /* handle nan */) {
235 ALOGE("volume < MIN_LINEAR_VOLUME or > MAX_LINEAR_VOLUME");
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800236 return BAD_VALUE;
237 }
238 }
239 }
240 return NO_ERROR;
241 }
242
Andy Hungf3702642017-05-05 17:33:32 -0700243 /* Clamps the volume curve in the configuration to
244 * the valid range for log or linear scale.
245 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800246 void clampVolume() {
247 if ((mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
248 for (auto it = this->begin(); it != this->end(); ++it) {
Andy Hungf3702642017-05-05 17:33:32 -0700249 if (!(it->second <= MAX_LOG_VOLUME) /* handle nan */) {
250 it->second = MAX_LOG_VOLUME;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800251 }
252 }
253 } else {
254 for (auto it = this->begin(); it != this->end(); ++it) {
Andy Hungf3702642017-05-05 17:33:32 -0700255 if (!(it->second >= MIN_LINEAR_VOLUME) /* handle nan */) {
256 it->second = MIN_LINEAR_VOLUME;
257 } else if (!(it->second <= MAX_LINEAR_VOLUME)) {
258 it->second = MAX_LINEAR_VOLUME;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800259 }
260 }
261 }
262 }
263
264 /* scaleToStartVolume() is used to set the start volume of a
265 * new VolumeShaper curve, when replacing one VolumeShaper
266 * with another using the "join" (volume match) option.
267 *
268 * It works best for monotonic volume ramps or ducks.
269 */
270 void scaleToStartVolume(T volume) {
271 if (this->size() < 2) {
272 return;
273 }
274 const T startVolume = first().second;
275 const T endVolume = last().second;
276 if (endVolume == startVolume) {
277 // match with linear ramp
278 const T offset = volume - startVolume;
Andy Hungf3702642017-05-05 17:33:32 -0700279 static const T scale = 1.f / (MAX_CURVE_TIME - MIN_CURVE_TIME); // nominally 1.f
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800280 for (auto it = this->begin(); it != this->end(); ++it) {
Andy Hungf3702642017-05-05 17:33:32 -0700281 it->second = it->second + offset * (MAX_CURVE_TIME - it->first) * scale;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800282 }
283 } else {
284 const T scale = (volume - endVolume) / (startVolume - endVolume);
285 for (auto it = this->begin(); it != this->end(); ++it) {
286 it->second = scale * (it->second - endVolume) + endVolume;
287 }
288 }
289 clampVolume();
290 }
291
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700292 status_t writeToParcel(Parcel *parcel) const override {
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700293 VolumeShaperConfiguration parcelable;
294 writeToParcelable(&parcelable);
295 return parcelable.writeToParcel(parcel);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800296 }
297
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700298 void writeToParcelable(VolumeShaperConfiguration *parcelable) const {
299 parcelable->id = getId();
300 parcelable->type = getTypeAsAidl();
301 parcelable->optionFlags = 0;
302 if (mType != TYPE_ID) {
303 parcelable->optionFlags = getOptionFlagsAsAidl();
304 parcelable->durationMs = getDurationMs();
305 Interpolator<S, T>::writeToConfig(&parcelable->interpolatorConfig);
306 }
307 }
308
309 status_t readFromParcel(const Parcel* parcel) override {
310 VolumeShaperConfiguration data;
311 return data.readFromParcel(parcel)
312 ?: readFromParcelable(data);
313 }
314
315 status_t readFromParcelable(const VolumeShaperConfiguration& parcelable) {
316 setId(parcelable.id);
317 return setTypeFromAidl(parcelable.type)
318 ?: mType == TYPE_ID
319 ? NO_ERROR
320 : setOptionFlagsFromAidl(parcelable.optionFlags)
321 ?: setDurationMs(parcelable.durationMs)
322 ?: Interpolator<S, T>::readFromConfig(parcelable.interpolatorConfig)
323 ?: checkCurve();
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800324 }
325
Andy Hungf3702642017-05-05 17:33:32 -0700326 // Returns a string for debug printing.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800327 std::string toString() const {
328 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700329 ss << "VolumeShaper::Configuration{mType=" << static_cast<int32_t>(mType);
330 ss << ", mId=" << mId;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800331 if (mType != TYPE_ID) {
Andy Hungf3702642017-05-05 17:33:32 -0700332 ss << ", mOptionFlags=" << static_cast<int32_t>(mOptionFlags);
333 ss << ", mDurationMs=" << mDurationMs;
334 ss << ", " << Interpolator<S, T>::toString().c_str();
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800335 }
Andy Hungf3702642017-05-05 17:33:32 -0700336 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800337 return ss.str();
338 }
339
340 private:
Andy Hungf3702642017-05-05 17:33:32 -0700341 Type mType; // type of configuration
342 int32_t mId; // A valid id is >= 0.
343 OptionFlag mOptionFlags; // option flags for the configuration.
344 double mDurationMs; // duration, must be > 0; default is 1000 ms.
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700345
346 int32_t getOptionFlagsAsAidl() const {
347 int32_t result = 0;
348 if (getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) {
349 result |=
350 1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::VOLUME_IN_DBFS);
351 }
352 if (getOptionFlags() & OPTION_FLAG_CLOCK_TIME) {
353 result |= 1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::CLOCK_TIME);
354 }
355 return result;
356 }
357
358 status_t setOptionFlagsFromAidl(int32_t aidl) {
359 std::underlying_type_t<OptionFlag> options = 0;
360 if (aidl & (1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::VOLUME_IN_DBFS))) {
361 options |= OPTION_FLAG_VOLUME_IN_DBFS;
362 }
363 if (aidl & (1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::CLOCK_TIME))) {
364 options |= OPTION_FLAG_CLOCK_TIME;
365 }
366 return setOptionFlags(static_cast<OptionFlag>(options));
367 }
368
369 status_t setTypeFromAidl(VolumeShaperConfigurationType aidl) {
370 switch (aidl) {
371 case VolumeShaperConfigurationType::ID:
372 return setType(TYPE_ID);
373 case VolumeShaperConfigurationType::SCALE:
374 return setType(TYPE_SCALE);
375 default:
376 return BAD_VALUE;
377 }
378 }
379
380 VolumeShaperConfigurationType getTypeAsAidl() const {
381 switch (getType()) {
382 case TYPE_ID:
383 return VolumeShaperConfigurationType::ID;
384 case TYPE_SCALE:
385 return VolumeShaperConfigurationType::SCALE;
386 default:
387 LOG_ALWAYS_FATAL("Shouldn't get here");
388 }
389 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800390 }; // Configuration
391
Andy Hungf3702642017-05-05 17:33:32 -0700392 /* VolumeShaper::Operation expresses an operation to perform on the
393 * configuration (either explicitly specified or an id).
394 *
395 * This parallels the Java implementation and the enums must match.
396 * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
397 * details on the Java implementation.
398 */
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700399 class Operation : public RefBase, public Parcelable {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800400 public:
Andy Hungf3702642017-05-05 17:33:32 -0700401 // Must match with VolumeShaper.java.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800402 enum Flag : int32_t {
403 FLAG_NONE = 0,
Andy Hungf3702642017-05-05 17:33:32 -0700404 FLAG_REVERSE = (1 << 0), // the absence of this indicates "play"
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800405 FLAG_TERMINATE = (1 << 1),
406 FLAG_JOIN = (1 << 2),
407 FLAG_DELAY = (1 << 3),
Andy Hung4ef88d72017-02-21 19:47:53 -0800408 FLAG_CREATE_IF_NECESSARY = (1 << 4),
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800409
Andy Hung4ef88d72017-02-21 19:47:53 -0800410 FLAG_ALL = (FLAG_REVERSE | FLAG_TERMINATE | FLAG_JOIN | FLAG_DELAY
411 | FLAG_CREATE_IF_NECESSARY),
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800412 };
413
414 Operation()
Andy Hung4ef88d72017-02-21 19:47:53 -0800415 : Operation(FLAG_NONE, -1 /* replaceId */) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800416 }
417
Andy Hung7d712bb2017-04-20 14:23:41 -0700418 Operation(Flag flags, int replaceId)
Andy Hung4ef88d72017-02-21 19:47:53 -0800419 : Operation(flags, replaceId, std::numeric_limits<S>::quiet_NaN() /* xOffset */) {
420 }
421
Andy Hung06a730b2020-04-09 13:28:31 -0700422 Operation(const Operation &operation)
Andy Hung4ef88d72017-02-21 19:47:53 -0800423 : Operation(operation.mFlags, operation.mReplaceId, operation.mXOffset) {
424 }
425
Andy Hung7d712bb2017-04-20 14:23:41 -0700426 explicit Operation(const sp<Operation> &operation)
427 : Operation(*operation.get()) {
428 }
429
430 Operation(Flag flags, int replaceId, S xOffset)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800431 : mFlags(flags)
Andy Hung4ef88d72017-02-21 19:47:53 -0800432 , mReplaceId(replaceId)
433 , mXOffset(xOffset) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800434 }
435
436 int32_t getReplaceId() const {
437 return mReplaceId;
438 }
439
440 void setReplaceId(int32_t replaceId) {
441 mReplaceId = replaceId;
442 }
443
Andy Hung4ef88d72017-02-21 19:47:53 -0800444 S getXOffset() const {
445 return mXOffset;
446 }
447
448 void setXOffset(S xOffset) {
Andy Hungf3702642017-05-05 17:33:32 -0700449 mXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
Andy Hung4ef88d72017-02-21 19:47:53 -0800450 }
451
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800452 Flag getFlags() const {
453 return mFlags;
454 }
455
Andy Hungf3702642017-05-05 17:33:32 -0700456 /* xOffset is the position on the volume curve and may go backwards
457 * if you are in reverse mode. This must be in the range from
458 * [MIN_CURVE_TIME, MAX_CURVE_TIME].
459 *
460 * normalizedTime always increases as time or framecount increases.
461 * normalizedTime is nominally from MIN_CURVE_TIME to MAX_CURVE_TIME when
462 * running through the curve, but could be outside this range afterwards.
463 * If you are reversing, this means the position on the curve, or xOffset,
464 * is computed as MAX_CURVE_TIME - normalizedTime, clamped to
465 * [MIN_CURVE_TIME, MAX_CURVE_TIME].
466 */
467 void setNormalizedTime(S normalizedTime) {
468 setXOffset((mFlags & FLAG_REVERSE) != 0
469 ? MAX_CURVE_TIME - normalizedTime : normalizedTime);
470 }
471
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800472 status_t setFlags(Flag flags) {
473 if ((flags & ~FLAG_ALL) != 0) {
474 ALOGE("flags has invalid bits: %#x", flags);
475 return BAD_VALUE;
476 }
477 mFlags = flags;
478 return NO_ERROR;
479 }
480
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700481 status_t writeToParcel(Parcel* parcel) const override {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800482 if (parcel == nullptr) return BAD_VALUE;
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700483 VolumeShaperOperation op;
484 writeToParcelable(&op);
485 return op.writeToParcel(parcel);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800486 }
487
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700488 void writeToParcelable(VolumeShaperOperation* op) const {
489 op->flags = getFlagsAsAidl();
490 op->replaceId = mReplaceId;
491 op->xOffset = mXOffset;
492 }
493
494 status_t readFromParcel(const Parcel* parcel) override {
495 VolumeShaperOperation op;
496 return op.readFromParcel(parcel)
497 ?: readFromParcelable(op);
498 }
499
500 status_t readFromParcelable(const VolumeShaperOperation& op) {
501 mReplaceId = op.replaceId;
502 mXOffset = op.xOffset;
503 return setFlagsFromAidl(op.flags);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800504 }
505
506 std::string toString() const {
507 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700508 ss << "VolumeShaper::Operation{mFlags=" << static_cast<int32_t>(mFlags) ;
509 ss << ", mReplaceId=" << mReplaceId;
510 ss << ", mXOffset=" << mXOffset;
511 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800512 return ss.str();
513 }
514
515 private:
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700516 status_t setFlagsFromAidl(int32_t aidl) {
517 std::underlying_type_t<Flag> flags = 0;
518 if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::REVERSE))) {
519 flags |= FLAG_REVERSE;
520 }
521 if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::TERMINATE))) {
522 flags |= FLAG_TERMINATE;
523 }
524 if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::JOIN))) {
525 flags |= FLAG_JOIN;
526 }
527 if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::DELAY))) {
528 flags |= FLAG_DELAY;
529 }
530 if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::CREATE_IF_NECESSARY))) {
531 flags |= FLAG_CREATE_IF_NECESSARY;
532 }
533 return setFlags(static_cast<Flag>(flags));
534 }
535
536 int32_t getFlagsAsAidl() const {
537 int32_t aidl = 0;
538 std::underlying_type_t<Flag> flags = getFlags();
539 if (flags & FLAG_REVERSE) {
540 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::REVERSE));
541 }
542 if (flags & FLAG_TERMINATE) {
543 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::TERMINATE));
544 }
545 if (flags & FLAG_JOIN) {
546 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::JOIN));
547 }
548 if (flags & FLAG_DELAY) {
549 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::DELAY));
550 }
551 if (flags & FLAG_CREATE_IF_NECESSARY) {
552 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::CREATE_IF_NECESSARY));
553 }
554 return aidl;
555 }
556
557 private:
Andy Hungf3702642017-05-05 17:33:32 -0700558 Flag mFlags; // operation to do
559 int32_t mReplaceId; // if >= 0 the id to remove in a replace operation.
560 S mXOffset; // position in the curve to set if a valid number (not nan)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800561 }; // Operation
562
Andy Hungf3702642017-05-05 17:33:32 -0700563 /* VolumeShaper.State is returned when requesting the last
564 * state of the VolumeShaper.
565 *
566 * This parallels the Java implementation.
567 * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
568 * details on the Java implementation.
569 */
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700570 class State : public RefBase, public Parcelable {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800571 public:
Andy Hung7d712bb2017-04-20 14:23:41 -0700572 State(T volume, S xOffset)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800573 : mVolume(volume)
574 , mXOffset(xOffset) {
575 }
576
577 State()
Andy Hungf3702642017-05-05 17:33:32 -0700578 : State(NAN, NAN) { }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800579
580 T getVolume() const {
581 return mVolume;
582 }
583
584 void setVolume(T volume) {
585 mVolume = volume;
586 }
587
588 S getXOffset() const {
589 return mXOffset;
590 }
591
592 void setXOffset(S xOffset) {
593 mXOffset = xOffset;
594 }
595
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700596 status_t writeToParcel(Parcel* parcel) const override {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800597 if (parcel == nullptr) return BAD_VALUE;
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700598 VolumeShaperState state;
599 writeToParcelable(&state);
600 return state.writeToParcel(parcel);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800601 }
602
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700603 void writeToParcelable(VolumeShaperState* parcelable) const {
604 parcelable->volume = mVolume;
605 parcelable->xOffset = mXOffset;
606 }
607
608 status_t readFromParcel(const Parcel* parcel) override {
609 VolumeShaperState state;
610 return state.readFromParcel(parcel)
611 ?: readFromParcelable(state);
612 }
613
614 status_t readFromParcelable(const VolumeShaperState& parcelable) {
615 mVolume = parcelable.volume;
616 mXOffset = parcelable.xOffset;
617 return OK;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800618 }
619
620 std::string toString() const {
621 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700622 ss << "VolumeShaper::State{mVolume=" << mVolume;
623 ss << ", mXOffset=" << mXOffset;
624 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800625 return ss.str();
626 }
627
628 private:
Andy Hungf3702642017-05-05 17:33:32 -0700629 T mVolume; // linear volume in the range MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME
630 S mXOffset; // position on curve expressed from MIN_CURVE_TIME to MAX_CURVE_TIME
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800631 }; // State
632
Andy Hungf3702642017-05-05 17:33:32 -0700633 // Internal helper class to do an affine transform for time and amplitude scaling.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800634 template <typename R>
635 class Translate {
636 public:
637 Translate()
638 : mOffset(0)
639 , mScale(1) {
640 }
641
642 R getOffset() const {
643 return mOffset;
644 }
645
646 void setOffset(R offset) {
647 mOffset = offset;
648 }
649
650 R getScale() const {
651 return mScale;
652 }
653
654 void setScale(R scale) {
655 mScale = scale;
656 }
657
658 R operator()(R in) const {
659 return mScale * (in - mOffset);
660 }
661
662 std::string toString() const {
663 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700664 ss << "VolumeShaper::Translate{mOffset=" << mOffset;
665 ss << ", mScale=" << mScale;
666 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800667 return ss.str();
668 }
669
670 private:
671 R mOffset;
672 R mScale;
673 }; // Translate
674
675 static int64_t convertTimespecToUs(const struct timespec &tv)
676 {
Chih-Hung Hsieh21b96162018-12-11 13:48:32 -0800677 return tv.tv_sec * 1000000LL + tv.tv_nsec / 1000;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800678 }
679
680 // current monotonic time in microseconds.
681 static int64_t getNowUs()
682 {
683 struct timespec tv;
684 if (clock_gettime(CLOCK_MONOTONIC, &tv) != 0) {
685 return 0; // system is really sick, just return 0 for consistency.
686 }
687 return convertTimespecToUs(tv);
688 }
689
Andy Hungf3702642017-05-05 17:33:32 -0700690 /* Native implementation of VolumeShaper. This is NOT mirrored
691 * on the Java side, so we don't need to mimic Java side layout
692 * and data; furthermore, this isn't refcounted as a "RefBase" object.
693 *
694 * Since we pass configuration and operation as shared pointers (like
695 * Java) there is a potential risk that the caller may modify
696 * these after delivery.
697 */
Andy Hung7d712bb2017-04-20 14:23:41 -0700698 VolumeShaper(
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800699 const sp<VolumeShaper::Configuration> &configuration,
700 const sp<VolumeShaper::Operation> &operation)
701 : mConfiguration(configuration) // we do not make a copy
702 , mOperation(operation) // ditto
703 , mStartFrame(-1)
704 , mLastVolume(T(1))
Andy Hungf3702642017-05-05 17:33:32 -0700705 , mLastXOffset(MIN_CURVE_TIME)
706 , mDelayXOffset(MIN_CURVE_TIME) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800707 if (configuration.get() != nullptr
708 && (getFlags() & VolumeShaper::Operation::FLAG_DELAY) == 0) {
709 mLastVolume = configuration->first().second;
710 }
711 }
712
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800713 // We allow a null operation here, though VolumeHandler always provides one.
714 VolumeShaper::Operation::Flag getFlags() const {
715 return mOperation == nullptr
Andy Hungf3702642017-05-05 17:33:32 -0700716 ? VolumeShaper::Operation::FLAG_NONE : mOperation->getFlags();
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800717 }
718
Andy Hungf3702642017-05-05 17:33:32 -0700719 /* Returns the last volume and xoffset reported to the AudioFlinger.
720 * If the VolumeShaper has not been started, compute what the volume
721 * should be based on the initial offset specified.
722 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800723 sp<VolumeShaper::State> getState() const {
Andy Hungf3702642017-05-05 17:33:32 -0700724 if (!isStarted()) {
725 const T volume = computeVolumeFromXOffset(mDelayXOffset);
726 VS_LOG("delayed VolumeShaper, using cached offset:%f for volume:%f",
727 mDelayXOffset, volume);
728 return new VolumeShaper::State(volume, mDelayXOffset);
729 } else {
730 return new VolumeShaper::State(mLastVolume, mLastXOffset);
731 }
732 }
733
734 S getDelayXOffset() const {
735 return mDelayXOffset;
Andy Hung4ef88d72017-02-21 19:47:53 -0800736 }
737
738 void setDelayXOffset(S xOffset) {
Andy Hungf3702642017-05-05 17:33:32 -0700739 mDelayXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800740 }
741
Andy Hung39399b62017-04-21 15:07:45 -0700742 bool isStarted() const {
743 return mStartFrame >= 0;
744 }
745
Andy Hungf3702642017-05-05 17:33:32 -0700746 /* getVolume() updates the last volume/xoffset state so it is not
747 * const, even though logically it may be viewed as const.
748 */
Andy Hung10cbff12017-02-21 17:30:14 -0800749 std::pair<T /* volume */, bool /* active */> getVolume(
750 int64_t trackFrameCount, double trackSampleRate) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800751 if ((getFlags() & VolumeShaper::Operation::FLAG_DELAY) != 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700752 // We haven't had PLAY called yet, so just return the value
753 // as if PLAY were called just now.
754 VS_LOG("delayed VolumeShaper, using cached offset %f", mDelayXOffset);
755 const T volume = computeVolumeFromXOffset(mDelayXOffset);
756 return std::make_pair(volume, false);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800757 }
758 const bool clockTime = (mConfiguration->getOptionFlags()
759 & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
760 const int64_t frameCount = clockTime ? getNowUs() : trackFrameCount;
761 const double sampleRate = clockTime ? 1000000 : trackSampleRate;
762
763 if (mStartFrame < 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700764 updatePosition(frameCount, sampleRate, mDelayXOffset);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800765 mStartFrame = frameCount;
766 }
767 VS_LOG("frameCount: %lld", (long long)frameCount);
Andy Hungf3702642017-05-05 17:33:32 -0700768 const S x = mXTranslate((T)frameCount);
769 VS_LOG("translation to normalized time: %f", x);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800770
Andy Hungf3702642017-05-05 17:33:32 -0700771 std::tuple<T /* volume */, S /* position */, bool /* active */> vt =
772 computeStateFromNormalizedTime(x);
773
774 mLastVolume = std::get<0>(vt);
775 mLastXOffset = std::get<1>(vt);
776 const bool active = std::get<2>(vt);
777 VS_LOG("rescaled time:%f volume:%f xOffset:%f active:%s",
778 x, mLastVolume, mLastXOffset, active ? "true" : "false");
779 return std::make_pair(mLastVolume, active);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800780 }
781
782 std::string toString() const {
783 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700784 ss << "VolumeShaper{mStartFrame=" << mStartFrame;
785 ss << ", mXTranslate=" << mXTranslate.toString().c_str();
786 ss << ", mConfiguration=" <<
787 (mConfiguration.get() == nullptr
788 ? "nullptr" : mConfiguration->toString().c_str());
789 ss << ", mOperation=" <<
790 (mOperation.get() == nullptr
791 ? "nullptr" : mOperation->toString().c_str());
792 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800793 return ss.str();
794 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800795
Andy Hungf3702642017-05-05 17:33:32 -0700796 Translate<S> mXTranslate; // translation from frames (usec for clock time) to normalized time.
Andy Hung4ef88d72017-02-21 19:47:53 -0800797 sp<VolumeShaper::Configuration> mConfiguration;
798 sp<VolumeShaper::Operation> mOperation;
Andy Hungf3702642017-05-05 17:33:32 -0700799
800private:
Andy Hung4ef88d72017-02-21 19:47:53 -0800801 int64_t mStartFrame; // starting frame, non-negative when started (in usec for clock time)
802 T mLastVolume; // last computed interpolated volume (y-axis)
803 S mLastXOffset; // last computed interpolated xOffset/time (x-axis)
Andy Hungf3702642017-05-05 17:33:32 -0700804 S mDelayXOffset; // xOffset to use for first invocation of VolumeShaper.
805
806 // Called internally to adjust mXTranslate for first time start.
807 void updatePosition(int64_t startFrame, double sampleRate, S xOffset) {
808 double scale = (mConfiguration->last().first - mConfiguration->first().first)
809 / (mConfiguration->getDurationMs() * 0.001 * sampleRate);
810 const double minScale = 1. / static_cast<double>(INT64_MAX);
811 scale = std::max(scale, minScale);
812 VS_LOG("update position: scale %lf frameCount:%lld, sampleRate:%lf, xOffset:%f",
813 scale, (long long) startFrame, sampleRate, xOffset);
814
815 S normalizedTime = (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) != 0 ?
816 MAX_CURVE_TIME - xOffset : xOffset;
817 mXTranslate.setOffset(static_cast<float>(static_cast<double>(startFrame)
818 - static_cast<double>(normalizedTime) / scale));
819 mXTranslate.setScale(static_cast<float>(scale));
820 VS_LOG("translate: %s", mXTranslate.toString().c_str());
821 }
822
823 T computeVolumeFromXOffset(S xOffset) const {
824 const T unscaledVolume = mConfiguration->findY(xOffset);
825 const T volume = mConfiguration->adjustVolume(unscaledVolume); // handle log scale
826 VS_LOG("computeVolumeFromXOffset %f -> %f -> %f", xOffset, unscaledVolume, volume);
827 return volume;
828 }
829
830 std::tuple<T /* volume */, S /* position */, bool /* active */>
831 computeStateFromNormalizedTime(S x) const {
832 bool active = true;
833 // handle reversal of position
834 if (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) {
835 x = MAX_CURVE_TIME - x;
836 VS_LOG("reversing to %f", x);
837 if (x < MIN_CURVE_TIME) {
838 x = MIN_CURVE_TIME;
839 active = false; // at the end
840 } else if (x > MAX_CURVE_TIME) {
841 x = MAX_CURVE_TIME; //early
842 }
843 } else {
844 if (x < MIN_CURVE_TIME) {
845 x = MIN_CURVE_TIME; // early
846 } else if (x > MAX_CURVE_TIME) {
847 x = MAX_CURVE_TIME;
848 active = false; // at end
849 }
850 }
851 const S xOffset = x;
852 const T volume = computeVolumeFromXOffset(xOffset);
853 return std::make_tuple(volume, xOffset, active);
854 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800855}; // VolumeShaper
856
Andy Hungf3702642017-05-05 17:33:32 -0700857/* VolumeHandler combines the volume factors of multiple VolumeShapers associated
858 * with a player. It is thread safe by synchronizing all public methods.
859 *
860 * This is a native-only implementation.
861 *
862 * The server side VolumeHandler is used to maintain a list of volume handlers,
863 * keep state, and obtain volume.
864 *
865 * The client side VolumeHandler is used to maintain a list of volume handlers,
866 * keep some partial state, and restore if the server dies.
867 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800868class VolumeHandler : public RefBase {
869public:
870 using S = float;
871 using T = float;
872
Andy Hung4ef88d72017-02-21 19:47:53 -0800873 // A volume handler which just keeps track of active VolumeShapers does not need sampleRate.
874 VolumeHandler()
875 : VolumeHandler(0 /* sampleRate */) {
876 }
877
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800878 explicit VolumeHandler(uint32_t sampleRate)
879 : mSampleRate((double)sampleRate)
Andy Hung4ef88d72017-02-21 19:47:53 -0800880 , mLastFrame(0)
Andy Hungf3702642017-05-05 17:33:32 -0700881 , mVolumeShaperIdCounter(VolumeShaper::kSystemVolumeShapersMax)
Andy Hungda540db2017-04-20 14:06:17 -0700882 , mLastVolume(1.f, false) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800883 }
884
885 VolumeShaper::Status applyVolumeShaper(
886 const sp<VolumeShaper::Configuration> &configuration,
Andy Hungf3702642017-05-05 17:33:32 -0700887 const sp<VolumeShaper::Operation> &operation_in) {
888 // make a local copy of operation, as we modify it.
889 sp<VolumeShaper::Operation> operation(new VolumeShaper::Operation(operation_in));
Andy Hung10cbff12017-02-21 17:30:14 -0800890 VS_LOG("applyVolumeShaper:configuration: %s", configuration->toString().c_str());
891 VS_LOG("applyVolumeShaper:operation: %s", operation->toString().c_str());
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800892 AutoMutex _l(mLock);
893 if (configuration == nullptr) {
894 ALOGE("null configuration");
895 return VolumeShaper::Status(BAD_VALUE);
896 }
897 if (operation == nullptr) {
898 ALOGE("null operation");
899 return VolumeShaper::Status(BAD_VALUE);
900 }
901 const int32_t id = configuration->getId();
902 if (id < 0) {
903 ALOGE("negative id: %d", id);
904 return VolumeShaper::Status(BAD_VALUE);
905 }
906 VS_LOG("applyVolumeShaper id: %d", id);
907
908 switch (configuration->getType()) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800909 case VolumeShaper::Configuration::TYPE_SCALE: {
910 const int replaceId = operation->getReplaceId();
911 if (replaceId >= 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700912 VS_LOG("replacing %d", replaceId);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800913 auto replaceIt = findId_l(replaceId);
914 if (replaceIt == mVolumeShapers.end()) {
915 ALOGW("cannot find replace id: %d", replaceId);
916 } else {
Andy Hungf3702642017-05-05 17:33:32 -0700917 if ((operation->getFlags() & VolumeShaper::Operation::FLAG_JOIN) != 0) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800918 // For join, we scale the start volume of the current configuration
919 // to match the last-used volume of the replacing VolumeShaper.
920 auto state = replaceIt->getState();
Andy Hungf3702642017-05-05 17:33:32 -0700921 ALOGD("join: state:%s", state->toString().c_str());
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800922 if (state->getXOffset() >= 0) { // valid
923 const T volume = state->getVolume();
924 ALOGD("join: scaling start volume to %f", volume);
925 configuration->scaleToStartVolume(volume);
926 }
927 }
928 (void)mVolumeShapers.erase(replaceIt);
929 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800930 operation->setReplaceId(-1);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800931 }
932 // check if we have another of the same id.
933 auto oldIt = findId_l(id);
934 if (oldIt != mVolumeShapers.end()) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800935 if ((operation->getFlags()
936 & VolumeShaper::Operation::FLAG_CREATE_IF_NECESSARY) != 0) {
937 // TODO: move the case to a separate function.
938 goto HANDLE_TYPE_ID; // no need to create, take over existing id.
939 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800940 ALOGW("duplicate id, removing old %d", id);
941 (void)mVolumeShapers.erase(oldIt);
942 }
Andy Hungf3702642017-05-05 17:33:32 -0700943
944 /* Check if too many application VolumeShapers (with id >= kSystemVolumeShapersMax).
945 * We check on the server side to ensure synchronization and robustness.
946 *
947 * This shouldn't fail on a replace command unless the replaced id is
948 * already invalid (which *should* be checked in the Java layer).
949 */
950 if (id >= VolumeShaper::kSystemVolumeShapersMax
951 && numberOfUserVolumeShapers_l() >= VolumeShaper::kUserVolumeShapersMax) {
952 ALOGW("Too many app VolumeShapers, cannot add to VolumeHandler");
953 return VolumeShaper::Status(INVALID_OPERATION);
954 }
955
956 // create new VolumeShaper with default behavior.
957 mVolumeShapers.emplace_back(configuration, new VolumeShaper::Operation());
958 VS_LOG("after adding, number of volumeShapers:%zu", mVolumeShapers.size());
Andy Hung10cbff12017-02-21 17:30:14 -0800959 }
960 // fall through to handle the operation
Andy Hung4ef88d72017-02-21 19:47:53 -0800961 HANDLE_TYPE_ID:
Andy Hung10cbff12017-02-21 17:30:14 -0800962 case VolumeShaper::Configuration::TYPE_ID: {
963 VS_LOG("trying to find id: %d", id);
964 auto it = findId_l(id);
965 if (it == mVolumeShapers.end()) {
966 VS_LOG("couldn't find id: %d", id);
967 return VolumeShaper::Status(INVALID_OPERATION);
968 }
Andy Hungf3702642017-05-05 17:33:32 -0700969 if ((operation->getFlags() & VolumeShaper::Operation::FLAG_TERMINATE) != 0) {
Andy Hung10cbff12017-02-21 17:30:14 -0800970 VS_LOG("terminate id: %d", id);
971 mVolumeShapers.erase(it);
972 break;
973 }
974 const bool clockTime = (it->mConfiguration->getOptionFlags()
975 & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
976 if ((it->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) !=
977 (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE)) {
Andy Hungf3702642017-05-05 17:33:32 -0700978 if (it->isStarted()) {
979 const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
980 const S x = it->mXTranslate((T)frameCount);
981 VS_LOG("reverse normalizedTime: %f", x);
982 // reflect position
983 S target = MAX_CURVE_TIME - x;
984 if (target < MIN_CURVE_TIME) {
985 VS_LOG("clamp to start - begin immediately");
986 target = MIN_CURVE_TIME;
987 }
988 VS_LOG("reverse normalizedTime target: %f", target);
989 it->mXTranslate.setOffset(it->mXTranslate.getOffset()
990 + (x - target) / it->mXTranslate.getScale());
Andy Hung10cbff12017-02-21 17:30:14 -0800991 }
Andy Hungf3702642017-05-05 17:33:32 -0700992 // if not started, the delay offset doesn't change.
Andy Hung10cbff12017-02-21 17:30:14 -0800993 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800994 const S xOffset = operation->getXOffset();
995 if (!std::isnan(xOffset)) {
Andy Hungf3702642017-05-05 17:33:32 -0700996 if (it->isStarted()) {
997 const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
998 const S x = it->mXTranslate((T)frameCount);
999 VS_LOG("normalizedTime translation: %f", x);
1000 const S target =
1001 (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) != 0 ?
1002 MAX_CURVE_TIME - xOffset : xOffset;
1003 VS_LOG("normalizedTime target x offset: %f", target);
1004 it->mXTranslate.setOffset(it->mXTranslate.getOffset()
1005 + (x - target) / it->mXTranslate.getScale());
1006 } else {
1007 it->setDelayXOffset(xOffset);
1008 }
Andy Hung4ef88d72017-02-21 19:47:53 -08001009 }
Andy Hung10cbff12017-02-21 17:30:14 -08001010 it->mOperation = operation; // replace the operation
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001011 } break;
1012 }
1013 return VolumeShaper::Status(id);
1014 }
1015
1016 sp<VolumeShaper::State> getVolumeShaperState(int id) {
1017 AutoMutex _l(mLock);
1018 auto it = findId_l(id);
1019 if (it == mVolumeShapers.end()) {
Andy Hung4ef88d72017-02-21 19:47:53 -08001020 VS_LOG("cannot find state for id: %d", id);
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001021 return nullptr;
1022 }
1023 return it->getState();
1024 }
1025
Andy Hungf3702642017-05-05 17:33:32 -07001026 /* getVolume() is not const, as it updates internal state.
1027 * Once called, any VolumeShapers not already started begin running.
1028 */
Andy Hung10cbff12017-02-21 17:30:14 -08001029 std::pair<T /* volume */, bool /* active */> getVolume(int64_t trackFrameCount) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001030 AutoMutex _l(mLock);
1031 mLastFrame = trackFrameCount;
1032 T volume(1);
Andy Hung10cbff12017-02-21 17:30:14 -08001033 size_t activeCount = 0;
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001034 for (auto it = mVolumeShapers.begin(); it != mVolumeShapers.end();) {
Andy Hungf3702642017-05-05 17:33:32 -07001035 const std::pair<T, bool> shaperVolume =
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001036 it->getVolume(trackFrameCount, mSampleRate);
1037 volume *= shaperVolume.first;
Andy Hung10cbff12017-02-21 17:30:14 -08001038 activeCount += shaperVolume.second;
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001039 ++it;
1040 }
Andy Hungda540db2017-04-20 14:06:17 -07001041 mLastVolume = std::make_pair(volume, activeCount != 0);
Andy Hungf3702642017-05-05 17:33:32 -07001042 VS_LOG("getVolume: <%f, %s>", mLastVolume.first, mLastVolume.second ? "true" : "false");
Andy Hungda540db2017-04-20 14:06:17 -07001043 return mLastVolume;
1044 }
1045
Andy Hungf3702642017-05-05 17:33:32 -07001046 /* Used by a client side VolumeHandler to ensure all the VolumeShapers
1047 * indicate that they have been started. Upon a change in audioserver
1048 * output sink, this information is used for restoration of the server side
1049 * VolumeHandler.
1050 */
Andy Hung39399b62017-04-21 15:07:45 -07001051 void setStarted() {
1052 (void)getVolume(mLastFrame); // getVolume() will start the individual VolumeShapers.
1053 }
1054
Andy Hungda540db2017-04-20 14:06:17 -07001055 std::pair<T /* volume */, bool /* active */> getLastVolume() const {
1056 AutoMutex _l(mLock);
1057 return mLastVolume;
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001058 }
1059
1060 std::string toString() const {
1061 AutoMutex _l(mLock);
1062 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -07001063 ss << "VolumeHandler{mSampleRate=" << mSampleRate;
1064 ss << ", mLastFrame=" << mLastFrame;
1065 ss << ", mVolumeShapers={";
1066 bool first = true;
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001067 for (const auto &shaper : mVolumeShapers) {
Andy Hungf3702642017-05-05 17:33:32 -07001068 if (first) {
1069 first = false;
1070 } else {
1071 ss << ", ";
1072 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001073 ss << shaper.toString().c_str();
1074 }
Andy Hungf3702642017-05-05 17:33:32 -07001075 ss << "}}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001076 return ss.str();
1077 }
1078
Andy Hung39399b62017-04-21 15:07:45 -07001079 void forall(const std::function<VolumeShaper::Status (const VolumeShaper &)> &lambda) {
Andy Hung4ef88d72017-02-21 19:47:53 -08001080 AutoMutex _l(mLock);
Andy Hungda540db2017-04-20 14:06:17 -07001081 VS_LOG("forall: mVolumeShapers.size() %zu", mVolumeShapers.size());
Andy Hung4ef88d72017-02-21 19:47:53 -08001082 for (const auto &shaper : mVolumeShapers) {
Andy Hung39399b62017-04-21 15:07:45 -07001083 VolumeShaper::Status status = lambda(shaper);
1084 VS_LOG("forall applying lambda on shaper (%p): %d", &shaper, (int)status);
Andy Hung4ef88d72017-02-21 19:47:53 -08001085 }
1086 }
1087
1088 void reset() {
1089 AutoMutex _l(mLock);
1090 mVolumeShapers.clear();
Andy Hung7d712bb2017-04-20 14:23:41 -07001091 mLastFrame = 0;
Andy Hung4ef88d72017-02-21 19:47:53 -08001092 // keep mVolumeShaperIdCounter as is.
1093 }
1094
Andy Hungf3702642017-05-05 17:33:32 -07001095 /* Sets the configuration id if necessary - This is based on the counter
1096 * internal to the VolumeHandler.
1097 */
Andy Hung4ef88d72017-02-21 19:47:53 -08001098 void setIdIfNecessary(const sp<VolumeShaper::Configuration> &configuration) {
1099 if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
1100 const int id = configuration->getId();
1101 if (id == -1) {
1102 // Reassign to a unique id, skipping system ids.
1103 AutoMutex _l(mLock);
1104 while (true) {
1105 if (mVolumeShaperIdCounter == INT32_MAX) {
Andy Hungf3702642017-05-05 17:33:32 -07001106 mVolumeShaperIdCounter = VolumeShaper::kSystemVolumeShapersMax;
Andy Hung4ef88d72017-02-21 19:47:53 -08001107 } else {
1108 ++mVolumeShaperIdCounter;
1109 }
1110 if (findId_l(mVolumeShaperIdCounter) != mVolumeShapers.end()) {
1111 continue; // collision with an existing id.
1112 }
1113 configuration->setId(mVolumeShaperIdCounter);
1114 ALOGD("setting id to %d", mVolumeShaperIdCounter);
1115 break;
1116 }
1117 }
1118 }
1119 }
1120
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001121private:
1122 std::list<VolumeShaper>::iterator findId_l(int32_t id) {
1123 std::list<VolumeShaper>::iterator it = mVolumeShapers.begin();
1124 for (; it != mVolumeShapers.end(); ++it) {
1125 if (it->mConfiguration->getId() == id) {
1126 break;
1127 }
1128 }
1129 return it;
1130 }
1131
Andy Hungf3702642017-05-05 17:33:32 -07001132 size_t numberOfUserVolumeShapers_l() const {
1133 size_t count = 0;
1134 for (const auto &shaper : mVolumeShapers) {
1135 count += (shaper.mConfiguration->getId() >= VolumeShaper::kSystemVolumeShapersMax);
1136 }
1137 return count;
1138 }
1139
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001140 mutable Mutex mLock;
1141 double mSampleRate; // in samples (frames) per second
Andy Hung7d712bb2017-04-20 14:23:41 -07001142 int64_t mLastFrame; // logging purpose only, 0 on start
Andy Hung4ef88d72017-02-21 19:47:53 -08001143 int32_t mVolumeShaperIdCounter; // a counter to return a unique volume shaper id.
Andy Hungda540db2017-04-20 14:06:17 -07001144 std::pair<T /* volume */, bool /* active */> mLastVolume;
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001145 std::list<VolumeShaper> mVolumeShapers; // list provides stable iterators on erase
1146}; // VolumeHandler
1147
Ivan Lozano8cf3a072017-08-09 09:01:33 -07001148} // namespace media
1149
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001150} // namespace android
1151
1152#pragma pop_macro("LOG_TAG")
1153
1154#endif // ANDROID_VOLUME_SHAPER_H