blob: 5271e100a1de48ec7c09ac0bf263617a910a00fa [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();
Andy Hung71455ba2021-05-05 09:08:25 -0700305 parcelable->interpolatorConfig.emplace(); // create value in std::optional
306 Interpolator<S, T>::writeToConfig(&*parcelable->interpolatorConfig);
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700307 }
308 }
309
310 status_t readFromParcel(const Parcel* parcel) override {
311 VolumeShaperConfiguration data;
312 return data.readFromParcel(parcel)
313 ?: readFromParcelable(data);
314 }
315
316 status_t readFromParcelable(const VolumeShaperConfiguration& parcelable) {
317 setId(parcelable.id);
318 return setTypeFromAidl(parcelable.type)
319 ?: mType == TYPE_ID
320 ? NO_ERROR
321 : setOptionFlagsFromAidl(parcelable.optionFlags)
322 ?: setDurationMs(parcelable.durationMs)
Andy Hung71455ba2021-05-05 09:08:25 -0700323 ?: !parcelable.interpolatorConfig // check std::optional for value
324 ? BAD_VALUE // must be nonnull.
325 : Interpolator<S, T>::readFromConfig(*parcelable.interpolatorConfig)
326 ?: checkCurve();
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800327 }
328
Andy Hungf3702642017-05-05 17:33:32 -0700329 // Returns a string for debug printing.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800330 std::string toString() const {
331 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700332 ss << "VolumeShaper::Configuration{mType=" << static_cast<int32_t>(mType);
333 ss << ", mId=" << mId;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800334 if (mType != TYPE_ID) {
Andy Hungf3702642017-05-05 17:33:32 -0700335 ss << ", mOptionFlags=" << static_cast<int32_t>(mOptionFlags);
336 ss << ", mDurationMs=" << mDurationMs;
337 ss << ", " << Interpolator<S, T>::toString().c_str();
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800338 }
Andy Hungf3702642017-05-05 17:33:32 -0700339 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800340 return ss.str();
341 }
342
343 private:
Andy Hungf3702642017-05-05 17:33:32 -0700344 Type mType; // type of configuration
345 int32_t mId; // A valid id is >= 0.
346 OptionFlag mOptionFlags; // option flags for the configuration.
347 double mDurationMs; // duration, must be > 0; default is 1000 ms.
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700348
349 int32_t getOptionFlagsAsAidl() const {
350 int32_t result = 0;
351 if (getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) {
352 result |=
353 1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::VOLUME_IN_DBFS);
354 }
355 if (getOptionFlags() & OPTION_FLAG_CLOCK_TIME) {
356 result |= 1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::CLOCK_TIME);
357 }
358 return result;
359 }
360
361 status_t setOptionFlagsFromAidl(int32_t aidl) {
362 std::underlying_type_t<OptionFlag> options = 0;
363 if (aidl & (1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::VOLUME_IN_DBFS))) {
364 options |= OPTION_FLAG_VOLUME_IN_DBFS;
365 }
366 if (aidl & (1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::CLOCK_TIME))) {
367 options |= OPTION_FLAG_CLOCK_TIME;
368 }
369 return setOptionFlags(static_cast<OptionFlag>(options));
370 }
371
372 status_t setTypeFromAidl(VolumeShaperConfigurationType aidl) {
373 switch (aidl) {
374 case VolumeShaperConfigurationType::ID:
375 return setType(TYPE_ID);
376 case VolumeShaperConfigurationType::SCALE:
377 return setType(TYPE_SCALE);
378 default:
379 return BAD_VALUE;
380 }
381 }
382
383 VolumeShaperConfigurationType getTypeAsAidl() const {
384 switch (getType()) {
385 case TYPE_ID:
386 return VolumeShaperConfigurationType::ID;
387 case TYPE_SCALE:
388 return VolumeShaperConfigurationType::SCALE;
389 default:
390 LOG_ALWAYS_FATAL("Shouldn't get here");
391 }
392 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800393 }; // Configuration
394
Andy Hungf3702642017-05-05 17:33:32 -0700395 /* VolumeShaper::Operation expresses an operation to perform on the
396 * configuration (either explicitly specified or an id).
397 *
398 * This parallels the Java implementation and the enums must match.
399 * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
400 * details on the Java implementation.
401 */
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700402 class Operation : public RefBase, public Parcelable {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800403 public:
Andy Hungf3702642017-05-05 17:33:32 -0700404 // Must match with VolumeShaper.java.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800405 enum Flag : int32_t {
406 FLAG_NONE = 0,
Andy Hungf3702642017-05-05 17:33:32 -0700407 FLAG_REVERSE = (1 << 0), // the absence of this indicates "play"
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800408 FLAG_TERMINATE = (1 << 1),
409 FLAG_JOIN = (1 << 2),
410 FLAG_DELAY = (1 << 3),
Andy Hung4ef88d72017-02-21 19:47:53 -0800411 FLAG_CREATE_IF_NECESSARY = (1 << 4),
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800412
Andy Hung4ef88d72017-02-21 19:47:53 -0800413 FLAG_ALL = (FLAG_REVERSE | FLAG_TERMINATE | FLAG_JOIN | FLAG_DELAY
414 | FLAG_CREATE_IF_NECESSARY),
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800415 };
416
417 Operation()
Andy Hung4ef88d72017-02-21 19:47:53 -0800418 : Operation(FLAG_NONE, -1 /* replaceId */) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800419 }
420
Andy Hung7d712bb2017-04-20 14:23:41 -0700421 Operation(Flag flags, int replaceId)
Andy Hung4ef88d72017-02-21 19:47:53 -0800422 : Operation(flags, replaceId, std::numeric_limits<S>::quiet_NaN() /* xOffset */) {
423 }
424
Andy Hung06a730b2020-04-09 13:28:31 -0700425 Operation(const Operation &operation)
Andy Hung4ef88d72017-02-21 19:47:53 -0800426 : Operation(operation.mFlags, operation.mReplaceId, operation.mXOffset) {
427 }
428
Andy Hung7d712bb2017-04-20 14:23:41 -0700429 explicit Operation(const sp<Operation> &operation)
430 : Operation(*operation.get()) {
431 }
432
433 Operation(Flag flags, int replaceId, S xOffset)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800434 : mFlags(flags)
Andy Hung4ef88d72017-02-21 19:47:53 -0800435 , mReplaceId(replaceId)
436 , mXOffset(xOffset) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800437 }
438
439 int32_t getReplaceId() const {
440 return mReplaceId;
441 }
442
443 void setReplaceId(int32_t replaceId) {
444 mReplaceId = replaceId;
445 }
446
Andy Hung4ef88d72017-02-21 19:47:53 -0800447 S getXOffset() const {
448 return mXOffset;
449 }
450
451 void setXOffset(S xOffset) {
Andy Hungf3702642017-05-05 17:33:32 -0700452 mXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
Andy Hung4ef88d72017-02-21 19:47:53 -0800453 }
454
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800455 Flag getFlags() const {
456 return mFlags;
457 }
458
Andy Hungf3702642017-05-05 17:33:32 -0700459 /* xOffset is the position on the volume curve and may go backwards
460 * if you are in reverse mode. This must be in the range from
461 * [MIN_CURVE_TIME, MAX_CURVE_TIME].
462 *
463 * normalizedTime always increases as time or framecount increases.
464 * normalizedTime is nominally from MIN_CURVE_TIME to MAX_CURVE_TIME when
465 * running through the curve, but could be outside this range afterwards.
466 * If you are reversing, this means the position on the curve, or xOffset,
467 * is computed as MAX_CURVE_TIME - normalizedTime, clamped to
468 * [MIN_CURVE_TIME, MAX_CURVE_TIME].
469 */
470 void setNormalizedTime(S normalizedTime) {
471 setXOffset((mFlags & FLAG_REVERSE) != 0
472 ? MAX_CURVE_TIME - normalizedTime : normalizedTime);
473 }
474
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800475 status_t setFlags(Flag flags) {
476 if ((flags & ~FLAG_ALL) != 0) {
477 ALOGE("flags has invalid bits: %#x", flags);
478 return BAD_VALUE;
479 }
480 mFlags = flags;
481 return NO_ERROR;
482 }
483
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700484 status_t writeToParcel(Parcel* parcel) const override {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800485 if (parcel == nullptr) return BAD_VALUE;
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700486 VolumeShaperOperation op;
487 writeToParcelable(&op);
488 return op.writeToParcel(parcel);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800489 }
490
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700491 void writeToParcelable(VolumeShaperOperation* op) const {
492 op->flags = getFlagsAsAidl();
493 op->replaceId = mReplaceId;
494 op->xOffset = mXOffset;
495 }
496
497 status_t readFromParcel(const Parcel* parcel) override {
498 VolumeShaperOperation op;
499 return op.readFromParcel(parcel)
500 ?: readFromParcelable(op);
501 }
502
503 status_t readFromParcelable(const VolumeShaperOperation& op) {
504 mReplaceId = op.replaceId;
505 mXOffset = op.xOffset;
506 return setFlagsFromAidl(op.flags);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800507 }
508
509 std::string toString() const {
510 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700511 ss << "VolumeShaper::Operation{mFlags=" << static_cast<int32_t>(mFlags) ;
512 ss << ", mReplaceId=" << mReplaceId;
513 ss << ", mXOffset=" << mXOffset;
514 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800515 return ss.str();
516 }
517
518 private:
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700519 status_t setFlagsFromAidl(int32_t aidl) {
520 std::underlying_type_t<Flag> flags = 0;
521 if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::REVERSE))) {
522 flags |= FLAG_REVERSE;
523 }
524 if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::TERMINATE))) {
525 flags |= FLAG_TERMINATE;
526 }
527 if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::JOIN))) {
528 flags |= FLAG_JOIN;
529 }
530 if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::DELAY))) {
531 flags |= FLAG_DELAY;
532 }
533 if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::CREATE_IF_NECESSARY))) {
534 flags |= FLAG_CREATE_IF_NECESSARY;
535 }
536 return setFlags(static_cast<Flag>(flags));
537 }
538
539 int32_t getFlagsAsAidl() const {
540 int32_t aidl = 0;
541 std::underlying_type_t<Flag> flags = getFlags();
542 if (flags & FLAG_REVERSE) {
543 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::REVERSE));
544 }
545 if (flags & FLAG_TERMINATE) {
546 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::TERMINATE));
547 }
548 if (flags & FLAG_JOIN) {
549 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::JOIN));
550 }
551 if (flags & FLAG_DELAY) {
552 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::DELAY));
553 }
554 if (flags & FLAG_CREATE_IF_NECESSARY) {
555 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::CREATE_IF_NECESSARY));
556 }
557 return aidl;
558 }
559
560 private:
Andy Hungf3702642017-05-05 17:33:32 -0700561 Flag mFlags; // operation to do
562 int32_t mReplaceId; // if >= 0 the id to remove in a replace operation.
563 S mXOffset; // position in the curve to set if a valid number (not nan)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800564 }; // Operation
565
Andy Hungf3702642017-05-05 17:33:32 -0700566 /* VolumeShaper.State is returned when requesting the last
567 * state of the VolumeShaper.
568 *
569 * This parallels the Java implementation.
570 * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
571 * details on the Java implementation.
572 */
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700573 class State : public RefBase, public Parcelable {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800574 public:
Andy Hung7d712bb2017-04-20 14:23:41 -0700575 State(T volume, S xOffset)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800576 : mVolume(volume)
577 , mXOffset(xOffset) {
578 }
579
580 State()
Andy Hungf3702642017-05-05 17:33:32 -0700581 : State(NAN, NAN) { }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800582
583 T getVolume() const {
584 return mVolume;
585 }
586
587 void setVolume(T volume) {
588 mVolume = volume;
589 }
590
591 S getXOffset() const {
592 return mXOffset;
593 }
594
595 void setXOffset(S xOffset) {
596 mXOffset = xOffset;
597 }
598
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700599 status_t writeToParcel(Parcel* parcel) const override {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800600 if (parcel == nullptr) return BAD_VALUE;
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700601 VolumeShaperState state;
602 writeToParcelable(&state);
603 return state.writeToParcel(parcel);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800604 }
605
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700606 void writeToParcelable(VolumeShaperState* parcelable) const {
607 parcelable->volume = mVolume;
608 parcelable->xOffset = mXOffset;
609 }
610
611 status_t readFromParcel(const Parcel* parcel) override {
612 VolumeShaperState state;
613 return state.readFromParcel(parcel)
614 ?: readFromParcelable(state);
615 }
616
617 status_t readFromParcelable(const VolumeShaperState& parcelable) {
618 mVolume = parcelable.volume;
619 mXOffset = parcelable.xOffset;
620 return OK;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800621 }
622
623 std::string toString() const {
624 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700625 ss << "VolumeShaper::State{mVolume=" << mVolume;
626 ss << ", mXOffset=" << mXOffset;
627 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800628 return ss.str();
629 }
630
631 private:
Andy Hungf3702642017-05-05 17:33:32 -0700632 T mVolume; // linear volume in the range MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME
633 S mXOffset; // position on curve expressed from MIN_CURVE_TIME to MAX_CURVE_TIME
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800634 }; // State
635
Andy Hungf3702642017-05-05 17:33:32 -0700636 // Internal helper class to do an affine transform for time and amplitude scaling.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800637 template <typename R>
638 class Translate {
639 public:
640 Translate()
641 : mOffset(0)
642 , mScale(1) {
643 }
644
645 R getOffset() const {
646 return mOffset;
647 }
648
649 void setOffset(R offset) {
650 mOffset = offset;
651 }
652
653 R getScale() const {
654 return mScale;
655 }
656
657 void setScale(R scale) {
658 mScale = scale;
659 }
660
661 R operator()(R in) const {
662 return mScale * (in - mOffset);
663 }
664
665 std::string toString() const {
666 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700667 ss << "VolumeShaper::Translate{mOffset=" << mOffset;
668 ss << ", mScale=" << mScale;
669 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800670 return ss.str();
671 }
672
673 private:
674 R mOffset;
675 R mScale;
676 }; // Translate
677
678 static int64_t convertTimespecToUs(const struct timespec &tv)
679 {
Chih-Hung Hsieh21b96162018-12-11 13:48:32 -0800680 return tv.tv_sec * 1000000LL + tv.tv_nsec / 1000;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800681 }
682
683 // current monotonic time in microseconds.
684 static int64_t getNowUs()
685 {
686 struct timespec tv;
687 if (clock_gettime(CLOCK_MONOTONIC, &tv) != 0) {
688 return 0; // system is really sick, just return 0 for consistency.
689 }
690 return convertTimespecToUs(tv);
691 }
692
Andy Hungf3702642017-05-05 17:33:32 -0700693 /* Native implementation of VolumeShaper. This is NOT mirrored
694 * on the Java side, so we don't need to mimic Java side layout
695 * and data; furthermore, this isn't refcounted as a "RefBase" object.
696 *
697 * Since we pass configuration and operation as shared pointers (like
698 * Java) there is a potential risk that the caller may modify
699 * these after delivery.
700 */
Andy Hung7d712bb2017-04-20 14:23:41 -0700701 VolumeShaper(
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800702 const sp<VolumeShaper::Configuration> &configuration,
703 const sp<VolumeShaper::Operation> &operation)
704 : mConfiguration(configuration) // we do not make a copy
705 , mOperation(operation) // ditto
706 , mStartFrame(-1)
707 , mLastVolume(T(1))
Andy Hungf3702642017-05-05 17:33:32 -0700708 , mLastXOffset(MIN_CURVE_TIME)
709 , mDelayXOffset(MIN_CURVE_TIME) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800710 if (configuration.get() != nullptr
711 && (getFlags() & VolumeShaper::Operation::FLAG_DELAY) == 0) {
712 mLastVolume = configuration->first().second;
713 }
714 }
715
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800716 // We allow a null operation here, though VolumeHandler always provides one.
717 VolumeShaper::Operation::Flag getFlags() const {
718 return mOperation == nullptr
Andy Hungf3702642017-05-05 17:33:32 -0700719 ? VolumeShaper::Operation::FLAG_NONE : mOperation->getFlags();
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800720 }
721
Andy Hungf3702642017-05-05 17:33:32 -0700722 /* Returns the last volume and xoffset reported to the AudioFlinger.
723 * If the VolumeShaper has not been started, compute what the volume
724 * should be based on the initial offset specified.
725 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800726 sp<VolumeShaper::State> getState() const {
Andy Hungf3702642017-05-05 17:33:32 -0700727 if (!isStarted()) {
728 const T volume = computeVolumeFromXOffset(mDelayXOffset);
729 VS_LOG("delayed VolumeShaper, using cached offset:%f for volume:%f",
730 mDelayXOffset, volume);
731 return new VolumeShaper::State(volume, mDelayXOffset);
732 } else {
733 return new VolumeShaper::State(mLastVolume, mLastXOffset);
734 }
735 }
736
737 S getDelayXOffset() const {
738 return mDelayXOffset;
Andy Hung4ef88d72017-02-21 19:47:53 -0800739 }
740
741 void setDelayXOffset(S xOffset) {
Andy Hungf3702642017-05-05 17:33:32 -0700742 mDelayXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800743 }
744
Andy Hung39399b62017-04-21 15:07:45 -0700745 bool isStarted() const {
746 return mStartFrame >= 0;
747 }
748
Andy Hungf3702642017-05-05 17:33:32 -0700749 /* getVolume() updates the last volume/xoffset state so it is not
750 * const, even though logically it may be viewed as const.
751 */
Andy Hung10cbff12017-02-21 17:30:14 -0800752 std::pair<T /* volume */, bool /* active */> getVolume(
753 int64_t trackFrameCount, double trackSampleRate) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800754 if ((getFlags() & VolumeShaper::Operation::FLAG_DELAY) != 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700755 // We haven't had PLAY called yet, so just return the value
756 // as if PLAY were called just now.
757 VS_LOG("delayed VolumeShaper, using cached offset %f", mDelayXOffset);
758 const T volume = computeVolumeFromXOffset(mDelayXOffset);
759 return std::make_pair(volume, false);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800760 }
761 const bool clockTime = (mConfiguration->getOptionFlags()
762 & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
763 const int64_t frameCount = clockTime ? getNowUs() : trackFrameCount;
764 const double sampleRate = clockTime ? 1000000 : trackSampleRate;
765
766 if (mStartFrame < 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700767 updatePosition(frameCount, sampleRate, mDelayXOffset);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800768 mStartFrame = frameCount;
769 }
770 VS_LOG("frameCount: %lld", (long long)frameCount);
Andy Hungf3702642017-05-05 17:33:32 -0700771 const S x = mXTranslate((T)frameCount);
772 VS_LOG("translation to normalized time: %f", x);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800773
Andy Hungf3702642017-05-05 17:33:32 -0700774 std::tuple<T /* volume */, S /* position */, bool /* active */> vt =
775 computeStateFromNormalizedTime(x);
776
777 mLastVolume = std::get<0>(vt);
778 mLastXOffset = std::get<1>(vt);
779 const bool active = std::get<2>(vt);
780 VS_LOG("rescaled time:%f volume:%f xOffset:%f active:%s",
781 x, mLastVolume, mLastXOffset, active ? "true" : "false");
782 return std::make_pair(mLastVolume, active);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800783 }
784
785 std::string toString() const {
786 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700787 ss << "VolumeShaper{mStartFrame=" << mStartFrame;
788 ss << ", mXTranslate=" << mXTranslate.toString().c_str();
789 ss << ", mConfiguration=" <<
790 (mConfiguration.get() == nullptr
791 ? "nullptr" : mConfiguration->toString().c_str());
792 ss << ", mOperation=" <<
793 (mOperation.get() == nullptr
794 ? "nullptr" : mOperation->toString().c_str());
795 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800796 return ss.str();
797 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800798
Andy Hungf3702642017-05-05 17:33:32 -0700799 Translate<S> mXTranslate; // translation from frames (usec for clock time) to normalized time.
Andy Hung4ef88d72017-02-21 19:47:53 -0800800 sp<VolumeShaper::Configuration> mConfiguration;
801 sp<VolumeShaper::Operation> mOperation;
Andy Hungf3702642017-05-05 17:33:32 -0700802
803private:
Andy Hung4ef88d72017-02-21 19:47:53 -0800804 int64_t mStartFrame; // starting frame, non-negative when started (in usec for clock time)
805 T mLastVolume; // last computed interpolated volume (y-axis)
806 S mLastXOffset; // last computed interpolated xOffset/time (x-axis)
Andy Hungf3702642017-05-05 17:33:32 -0700807 S mDelayXOffset; // xOffset to use for first invocation of VolumeShaper.
808
809 // Called internally to adjust mXTranslate for first time start.
810 void updatePosition(int64_t startFrame, double sampleRate, S xOffset) {
811 double scale = (mConfiguration->last().first - mConfiguration->first().first)
812 / (mConfiguration->getDurationMs() * 0.001 * sampleRate);
813 const double minScale = 1. / static_cast<double>(INT64_MAX);
814 scale = std::max(scale, minScale);
815 VS_LOG("update position: scale %lf frameCount:%lld, sampleRate:%lf, xOffset:%f",
816 scale, (long long) startFrame, sampleRate, xOffset);
817
818 S normalizedTime = (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) != 0 ?
819 MAX_CURVE_TIME - xOffset : xOffset;
820 mXTranslate.setOffset(static_cast<float>(static_cast<double>(startFrame)
821 - static_cast<double>(normalizedTime) / scale));
822 mXTranslate.setScale(static_cast<float>(scale));
823 VS_LOG("translate: %s", mXTranslate.toString().c_str());
824 }
825
826 T computeVolumeFromXOffset(S xOffset) const {
827 const T unscaledVolume = mConfiguration->findY(xOffset);
828 const T volume = mConfiguration->adjustVolume(unscaledVolume); // handle log scale
829 VS_LOG("computeVolumeFromXOffset %f -> %f -> %f", xOffset, unscaledVolume, volume);
830 return volume;
831 }
832
833 std::tuple<T /* volume */, S /* position */, bool /* active */>
834 computeStateFromNormalizedTime(S x) const {
835 bool active = true;
836 // handle reversal of position
837 if (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) {
838 x = MAX_CURVE_TIME - x;
839 VS_LOG("reversing to %f", x);
840 if (x < MIN_CURVE_TIME) {
841 x = MIN_CURVE_TIME;
842 active = false; // at the end
843 } else if (x > MAX_CURVE_TIME) {
844 x = MAX_CURVE_TIME; //early
845 }
846 } else {
847 if (x < MIN_CURVE_TIME) {
848 x = MIN_CURVE_TIME; // early
849 } else if (x > MAX_CURVE_TIME) {
850 x = MAX_CURVE_TIME;
851 active = false; // at end
852 }
853 }
854 const S xOffset = x;
855 const T volume = computeVolumeFromXOffset(xOffset);
856 return std::make_tuple(volume, xOffset, active);
857 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800858}; // VolumeShaper
859
Andy Hungf3702642017-05-05 17:33:32 -0700860/* VolumeHandler combines the volume factors of multiple VolumeShapers associated
861 * with a player. It is thread safe by synchronizing all public methods.
862 *
863 * This is a native-only implementation.
864 *
865 * The server side VolumeHandler is used to maintain a list of volume handlers,
866 * keep state, and obtain volume.
867 *
868 * The client side VolumeHandler is used to maintain a list of volume handlers,
869 * keep some partial state, and restore if the server dies.
870 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800871class VolumeHandler : public RefBase {
872public:
873 using S = float;
874 using T = float;
875
Andy Hung4ef88d72017-02-21 19:47:53 -0800876 // A volume handler which just keeps track of active VolumeShapers does not need sampleRate.
877 VolumeHandler()
878 : VolumeHandler(0 /* sampleRate */) {
879 }
880
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800881 explicit VolumeHandler(uint32_t sampleRate)
882 : mSampleRate((double)sampleRate)
Andy Hung4ef88d72017-02-21 19:47:53 -0800883 , mLastFrame(0)
Andy Hungf3702642017-05-05 17:33:32 -0700884 , mVolumeShaperIdCounter(VolumeShaper::kSystemVolumeShapersMax)
Andy Hungda540db2017-04-20 14:06:17 -0700885 , mLastVolume(1.f, false) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800886 }
887
888 VolumeShaper::Status applyVolumeShaper(
889 const sp<VolumeShaper::Configuration> &configuration,
Andy Hungf3702642017-05-05 17:33:32 -0700890 const sp<VolumeShaper::Operation> &operation_in) {
891 // make a local copy of operation, as we modify it.
892 sp<VolumeShaper::Operation> operation(new VolumeShaper::Operation(operation_in));
Andy Hung10cbff12017-02-21 17:30:14 -0800893 VS_LOG("applyVolumeShaper:configuration: %s", configuration->toString().c_str());
894 VS_LOG("applyVolumeShaper:operation: %s", operation->toString().c_str());
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800895 AutoMutex _l(mLock);
896 if (configuration == nullptr) {
897 ALOGE("null configuration");
898 return VolumeShaper::Status(BAD_VALUE);
899 }
900 if (operation == nullptr) {
901 ALOGE("null operation");
902 return VolumeShaper::Status(BAD_VALUE);
903 }
904 const int32_t id = configuration->getId();
905 if (id < 0) {
906 ALOGE("negative id: %d", id);
907 return VolumeShaper::Status(BAD_VALUE);
908 }
909 VS_LOG("applyVolumeShaper id: %d", id);
910
911 switch (configuration->getType()) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800912 case VolumeShaper::Configuration::TYPE_SCALE: {
913 const int replaceId = operation->getReplaceId();
914 if (replaceId >= 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700915 VS_LOG("replacing %d", replaceId);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800916 auto replaceIt = findId_l(replaceId);
917 if (replaceIt == mVolumeShapers.end()) {
918 ALOGW("cannot find replace id: %d", replaceId);
919 } else {
Andy Hungf3702642017-05-05 17:33:32 -0700920 if ((operation->getFlags() & VolumeShaper::Operation::FLAG_JOIN) != 0) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800921 // For join, we scale the start volume of the current configuration
922 // to match the last-used volume of the replacing VolumeShaper.
923 auto state = replaceIt->getState();
Andy Hungf3702642017-05-05 17:33:32 -0700924 ALOGD("join: state:%s", state->toString().c_str());
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800925 if (state->getXOffset() >= 0) { // valid
926 const T volume = state->getVolume();
927 ALOGD("join: scaling start volume to %f", volume);
928 configuration->scaleToStartVolume(volume);
929 }
930 }
931 (void)mVolumeShapers.erase(replaceIt);
932 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800933 operation->setReplaceId(-1);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800934 }
935 // check if we have another of the same id.
936 auto oldIt = findId_l(id);
937 if (oldIt != mVolumeShapers.end()) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800938 if ((operation->getFlags()
939 & VolumeShaper::Operation::FLAG_CREATE_IF_NECESSARY) != 0) {
940 // TODO: move the case to a separate function.
941 goto HANDLE_TYPE_ID; // no need to create, take over existing id.
942 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800943 ALOGW("duplicate id, removing old %d", id);
944 (void)mVolumeShapers.erase(oldIt);
945 }
Andy Hungf3702642017-05-05 17:33:32 -0700946
947 /* Check if too many application VolumeShapers (with id >= kSystemVolumeShapersMax).
948 * We check on the server side to ensure synchronization and robustness.
949 *
950 * This shouldn't fail on a replace command unless the replaced id is
951 * already invalid (which *should* be checked in the Java layer).
952 */
953 if (id >= VolumeShaper::kSystemVolumeShapersMax
954 && numberOfUserVolumeShapers_l() >= VolumeShaper::kUserVolumeShapersMax) {
955 ALOGW("Too many app VolumeShapers, cannot add to VolumeHandler");
956 return VolumeShaper::Status(INVALID_OPERATION);
957 }
958
959 // create new VolumeShaper with default behavior.
960 mVolumeShapers.emplace_back(configuration, new VolumeShaper::Operation());
961 VS_LOG("after adding, number of volumeShapers:%zu", mVolumeShapers.size());
Andy Hung10cbff12017-02-21 17:30:14 -0800962 }
963 // fall through to handle the operation
Andy Hung4ef88d72017-02-21 19:47:53 -0800964 HANDLE_TYPE_ID:
Andy Hung10cbff12017-02-21 17:30:14 -0800965 case VolumeShaper::Configuration::TYPE_ID: {
966 VS_LOG("trying to find id: %d", id);
967 auto it = findId_l(id);
968 if (it == mVolumeShapers.end()) {
969 VS_LOG("couldn't find id: %d", id);
970 return VolumeShaper::Status(INVALID_OPERATION);
971 }
Andy Hungf3702642017-05-05 17:33:32 -0700972 if ((operation->getFlags() & VolumeShaper::Operation::FLAG_TERMINATE) != 0) {
Andy Hung10cbff12017-02-21 17:30:14 -0800973 VS_LOG("terminate id: %d", id);
974 mVolumeShapers.erase(it);
975 break;
976 }
977 const bool clockTime = (it->mConfiguration->getOptionFlags()
978 & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
979 if ((it->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) !=
980 (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE)) {
Andy Hungf3702642017-05-05 17:33:32 -0700981 if (it->isStarted()) {
982 const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
983 const S x = it->mXTranslate((T)frameCount);
984 VS_LOG("reverse normalizedTime: %f", x);
985 // reflect position
986 S target = MAX_CURVE_TIME - x;
987 if (target < MIN_CURVE_TIME) {
988 VS_LOG("clamp to start - begin immediately");
989 target = MIN_CURVE_TIME;
990 }
991 VS_LOG("reverse normalizedTime target: %f", target);
992 it->mXTranslate.setOffset(it->mXTranslate.getOffset()
993 + (x - target) / it->mXTranslate.getScale());
Andy Hung10cbff12017-02-21 17:30:14 -0800994 }
Andy Hungf3702642017-05-05 17:33:32 -0700995 // if not started, the delay offset doesn't change.
Andy Hung10cbff12017-02-21 17:30:14 -0800996 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800997 const S xOffset = operation->getXOffset();
998 if (!std::isnan(xOffset)) {
Andy Hungf3702642017-05-05 17:33:32 -0700999 if (it->isStarted()) {
1000 const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
1001 const S x = it->mXTranslate((T)frameCount);
1002 VS_LOG("normalizedTime translation: %f", x);
1003 const S target =
1004 (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) != 0 ?
1005 MAX_CURVE_TIME - xOffset : xOffset;
1006 VS_LOG("normalizedTime target x offset: %f", target);
1007 it->mXTranslate.setOffset(it->mXTranslate.getOffset()
1008 + (x - target) / it->mXTranslate.getScale());
1009 } else {
1010 it->setDelayXOffset(xOffset);
1011 }
Andy Hung4ef88d72017-02-21 19:47:53 -08001012 }
Andy Hung10cbff12017-02-21 17:30:14 -08001013 it->mOperation = operation; // replace the operation
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001014 } break;
1015 }
1016 return VolumeShaper::Status(id);
1017 }
1018
1019 sp<VolumeShaper::State> getVolumeShaperState(int id) {
1020 AutoMutex _l(mLock);
1021 auto it = findId_l(id);
1022 if (it == mVolumeShapers.end()) {
Andy Hung4ef88d72017-02-21 19:47:53 -08001023 VS_LOG("cannot find state for id: %d", id);
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001024 return nullptr;
1025 }
1026 return it->getState();
1027 }
1028
Andy Hungf3702642017-05-05 17:33:32 -07001029 /* getVolume() is not const, as it updates internal state.
1030 * Once called, any VolumeShapers not already started begin running.
1031 */
Andy Hung10cbff12017-02-21 17:30:14 -08001032 std::pair<T /* volume */, bool /* active */> getVolume(int64_t trackFrameCount) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001033 AutoMutex _l(mLock);
1034 mLastFrame = trackFrameCount;
1035 T volume(1);
Andy Hung10cbff12017-02-21 17:30:14 -08001036 size_t activeCount = 0;
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001037 for (auto it = mVolumeShapers.begin(); it != mVolumeShapers.end();) {
Andy Hungf3702642017-05-05 17:33:32 -07001038 const std::pair<T, bool> shaperVolume =
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001039 it->getVolume(trackFrameCount, mSampleRate);
1040 volume *= shaperVolume.first;
Andy Hung10cbff12017-02-21 17:30:14 -08001041 activeCount += shaperVolume.second;
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001042 ++it;
1043 }
Andy Hungda540db2017-04-20 14:06:17 -07001044 mLastVolume = std::make_pair(volume, activeCount != 0);
Andy Hungf3702642017-05-05 17:33:32 -07001045 VS_LOG("getVolume: <%f, %s>", mLastVolume.first, mLastVolume.second ? "true" : "false");
Andy Hungda540db2017-04-20 14:06:17 -07001046 return mLastVolume;
1047 }
1048
Andy Hungf3702642017-05-05 17:33:32 -07001049 /* Used by a client side VolumeHandler to ensure all the VolumeShapers
1050 * indicate that they have been started. Upon a change in audioserver
1051 * output sink, this information is used for restoration of the server side
1052 * VolumeHandler.
1053 */
Andy Hung39399b62017-04-21 15:07:45 -07001054 void setStarted() {
1055 (void)getVolume(mLastFrame); // getVolume() will start the individual VolumeShapers.
1056 }
1057
Andy Hungda540db2017-04-20 14:06:17 -07001058 std::pair<T /* volume */, bool /* active */> getLastVolume() const {
1059 AutoMutex _l(mLock);
1060 return mLastVolume;
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001061 }
1062
1063 std::string toString() const {
1064 AutoMutex _l(mLock);
1065 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -07001066 ss << "VolumeHandler{mSampleRate=" << mSampleRate;
1067 ss << ", mLastFrame=" << mLastFrame;
1068 ss << ", mVolumeShapers={";
1069 bool first = true;
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001070 for (const auto &shaper : mVolumeShapers) {
Andy Hungf3702642017-05-05 17:33:32 -07001071 if (first) {
1072 first = false;
1073 } else {
1074 ss << ", ";
1075 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001076 ss << shaper.toString().c_str();
1077 }
Andy Hungf3702642017-05-05 17:33:32 -07001078 ss << "}}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001079 return ss.str();
1080 }
1081
Andy Hung39399b62017-04-21 15:07:45 -07001082 void forall(const std::function<VolumeShaper::Status (const VolumeShaper &)> &lambda) {
Andy Hung4ef88d72017-02-21 19:47:53 -08001083 AutoMutex _l(mLock);
Andy Hungda540db2017-04-20 14:06:17 -07001084 VS_LOG("forall: mVolumeShapers.size() %zu", mVolumeShapers.size());
Andy Hung4ef88d72017-02-21 19:47:53 -08001085 for (const auto &shaper : mVolumeShapers) {
Andy Hung39399b62017-04-21 15:07:45 -07001086 VolumeShaper::Status status = lambda(shaper);
1087 VS_LOG("forall applying lambda on shaper (%p): %d", &shaper, (int)status);
Andy Hung4ef88d72017-02-21 19:47:53 -08001088 }
1089 }
1090
1091 void reset() {
1092 AutoMutex _l(mLock);
1093 mVolumeShapers.clear();
Andy Hung7d712bb2017-04-20 14:23:41 -07001094 mLastFrame = 0;
Andy Hung4ef88d72017-02-21 19:47:53 -08001095 // keep mVolumeShaperIdCounter as is.
1096 }
1097
Andy Hungf3702642017-05-05 17:33:32 -07001098 /* Sets the configuration id if necessary - This is based on the counter
1099 * internal to the VolumeHandler.
1100 */
Andy Hung4ef88d72017-02-21 19:47:53 -08001101 void setIdIfNecessary(const sp<VolumeShaper::Configuration> &configuration) {
1102 if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
1103 const int id = configuration->getId();
1104 if (id == -1) {
1105 // Reassign to a unique id, skipping system ids.
1106 AutoMutex _l(mLock);
1107 while (true) {
1108 if (mVolumeShaperIdCounter == INT32_MAX) {
Andy Hungf3702642017-05-05 17:33:32 -07001109 mVolumeShaperIdCounter = VolumeShaper::kSystemVolumeShapersMax;
Andy Hung4ef88d72017-02-21 19:47:53 -08001110 } else {
1111 ++mVolumeShaperIdCounter;
1112 }
1113 if (findId_l(mVolumeShaperIdCounter) != mVolumeShapers.end()) {
1114 continue; // collision with an existing id.
1115 }
1116 configuration->setId(mVolumeShaperIdCounter);
1117 ALOGD("setting id to %d", mVolumeShaperIdCounter);
1118 break;
1119 }
1120 }
1121 }
1122 }
1123
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001124private:
1125 std::list<VolumeShaper>::iterator findId_l(int32_t id) {
1126 std::list<VolumeShaper>::iterator it = mVolumeShapers.begin();
1127 for (; it != mVolumeShapers.end(); ++it) {
1128 if (it->mConfiguration->getId() == id) {
1129 break;
1130 }
1131 }
1132 return it;
1133 }
1134
Andy Hungf3702642017-05-05 17:33:32 -07001135 size_t numberOfUserVolumeShapers_l() const {
1136 size_t count = 0;
1137 for (const auto &shaper : mVolumeShapers) {
1138 count += (shaper.mConfiguration->getId() >= VolumeShaper::kSystemVolumeShapersMax);
1139 }
1140 return count;
1141 }
1142
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001143 mutable Mutex mLock;
1144 double mSampleRate; // in samples (frames) per second
Andy Hung7d712bb2017-04-20 14:23:41 -07001145 int64_t mLastFrame; // logging purpose only, 0 on start
Andy Hung4ef88d72017-02-21 19:47:53 -08001146 int32_t mVolumeShaperIdCounter; // a counter to return a unique volume shaper id.
Andy Hungda540db2017-04-20 14:06:17 -07001147 std::pair<T /* volume */, bool /* active */> mLastVolume;
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001148 std::list<VolumeShaper> mVolumeShapers; // list provides stable iterators on erase
1149}; // VolumeHandler
1150
Ivan Lozano8cf3a072017-08-09 09:01:33 -07001151} // namespace media
1152
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001153} // namespace android
1154
1155#pragma pop_macro("LOG_TAG")
1156
1157#endif // ANDROID_VOLUME_SHAPER_H