blob: 26da3634c84a9b9260dd5d897f129f0ad59f14bd [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 Hung23f81622024-06-07 18:48:49 -0700119 static std::string toString(Type type) {
120 switch (type) {
121 case TYPE_ID: return "TYPE_ID";
122 case TYPE_SCALE: return "TYPE_SCALE";
123 default:
124 return std::string("Unknown Type: ")
125 .append(std::to_string(static_cast<int>(type)));
126 }
127 }
128
Andy Hungf3702642017-05-05 17:33:32 -0700129 // Must match with VolumeShaper.java in frameworks/base.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800130 enum OptionFlag : int32_t {
131 OPTION_FLAG_NONE = 0,
132 OPTION_FLAG_VOLUME_IN_DBFS = (1 << 0),
133 OPTION_FLAG_CLOCK_TIME = (1 << 1),
134
135 OPTION_FLAG_ALL = (OPTION_FLAG_VOLUME_IN_DBFS | OPTION_FLAG_CLOCK_TIME),
136 };
137
Andy Hung23f81622024-06-07 18:48:49 -0700138 static std::string toString(OptionFlag flag) {
139 std::string s;
140 for (const auto& flagPair : std::initializer_list<std::pair<OptionFlag, const char*>>{
141 {OPTION_FLAG_VOLUME_IN_DBFS, "OPTION_FLAG_VOLUME_IN_DBFS"},
142 {OPTION_FLAG_CLOCK_TIME, "OPTION_FLAG_CLOCK_TIME"},
143 }) {
144 if (flag & flagPair.first) {
145 if (!s.empty()) {
146 s.append("|");
147 }
148 s.append(flagPair.second);
149 }
150 }
151 return s;
152 }
153
Andy Hungf3702642017-05-05 17:33:32 -0700154 // Bring from base class; must match with VolumeShaper.java in frameworks/base.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800155 using InterpolatorType = Interpolator<S, T>::InterpolatorType;
156
157 Configuration()
158 : Interpolator<S, T>()
Colin Cross11280a12017-05-02 10:32:56 -0700159 , RefBase()
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800160 , mType(TYPE_SCALE)
Colin Cross4e399992017-04-27 16:15:51 -0700161 , mId(-1)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800162 , mOptionFlags(OPTION_FLAG_NONE)
Colin Cross4e399992017-04-27 16:15:51 -0700163 , mDurationMs(1000.) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800164 }
165
Andy Hung06a730b2020-04-09 13:28:31 -0700166 Configuration(const Configuration &configuration)
Andy Hung10cbff12017-02-21 17:30:14 -0800167 : Interpolator<S, T>(*static_cast<const Interpolator<S, T> *>(&configuration))
Colin Cross11280a12017-05-02 10:32:56 -0700168 , RefBase()
Andy Hung10cbff12017-02-21 17:30:14 -0800169 , mType(configuration.mType)
Colin Cross4e399992017-04-27 16:15:51 -0700170 , mId(configuration.mId)
Andy Hung10cbff12017-02-21 17:30:14 -0800171 , mOptionFlags(configuration.mOptionFlags)
Colin Cross4e399992017-04-27 16:15:51 -0700172 , mDurationMs(configuration.mDurationMs) {
Andy Hung10cbff12017-02-21 17:30:14 -0800173 }
174
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800175 Type getType() const {
176 return mType;
177 }
178
179 status_t setType(Type type) {
180 switch (type) {
181 case TYPE_ID:
182 case TYPE_SCALE:
183 mType = type;
184 return NO_ERROR;
185 default:
186 ALOGE("invalid Type: %d", type);
187 return BAD_VALUE;
188 }
189 }
190
191 OptionFlag getOptionFlags() const {
192 return mOptionFlags;
193 }
194
195 status_t setOptionFlags(OptionFlag optionFlags) {
196 if ((optionFlags & ~OPTION_FLAG_ALL) != 0) {
197 ALOGE("optionFlags has invalid bits: %#x", optionFlags);
198 return BAD_VALUE;
199 }
200 mOptionFlags = optionFlags;
201 return NO_ERROR;
202 }
203
204 double getDurationMs() const {
205 return mDurationMs;
206 }
207
Andy Hungf3702642017-05-05 17:33:32 -0700208 status_t setDurationMs(double durationMs) {
209 if (durationMs > 0.) {
210 mDurationMs = durationMs;
211 return NO_ERROR;
212 }
213 // zero, negative, or nan. These values not possible from Java.
214 return BAD_VALUE;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800215 }
216
217 int32_t getId() const {
218 return mId;
219 }
220
221 void setId(int32_t id) {
Andy Hungf3702642017-05-05 17:33:32 -0700222 // We permit a negative id here (representing invalid).
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800223 mId = id;
224 }
225
Andy Hungf3702642017-05-05 17:33:32 -0700226 /* Adjust the volume to be in linear range from MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME
227 * and compensate for log dbFS volume as needed.
228 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800229 T adjustVolume(T volume) const {
230 if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700231 const T out = powf(10.f, volume / 10.f);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800232 VS_LOG("in: %f out: %f", volume, out);
233 volume = out;
234 }
Andy Hungf3702642017-05-05 17:33:32 -0700235 return clamp(volume, MIN_LINEAR_VOLUME /* lo */, MAX_LINEAR_VOLUME /* hi */);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800236 }
237
Andy Hungf3702642017-05-05 17:33:32 -0700238 /* Check if the existing curve is valid.
239 */
240 status_t checkCurve() const {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800241 if (mType == TYPE_ID) return NO_ERROR;
242 if (this->size() < 2) {
243 ALOGE("curve must have at least 2 points");
244 return BAD_VALUE;
245 }
Andy Hungf3702642017-05-05 17:33:32 -0700246 if (first().first != MIN_CURVE_TIME || last().first != MAX_CURVE_TIME) {
247 ALOGE("curve must start at MIN_CURVE_TIME and end at MAX_CURVE_TIME");
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800248 return BAD_VALUE;
249 }
250 if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
251 for (const auto &pt : *this) {
Andy Hungf3702642017-05-05 17:33:32 -0700252 if (!(pt.second <= MAX_LOG_VOLUME) /* handle nan */) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800253 ALOGE("positive volume dbFS");
254 return BAD_VALUE;
255 }
256 }
257 } else {
258 for (const auto &pt : *this) {
Andy Hungf3702642017-05-05 17:33:32 -0700259 if (!(pt.second >= MIN_LINEAR_VOLUME)
260 || !(pt.second <= MAX_LINEAR_VOLUME) /* handle nan */) {
261 ALOGE("volume < MIN_LINEAR_VOLUME or > MAX_LINEAR_VOLUME");
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800262 return BAD_VALUE;
263 }
264 }
265 }
266 return NO_ERROR;
267 }
268
Andy Hungf3702642017-05-05 17:33:32 -0700269 /* Clamps the volume curve in the configuration to
270 * the valid range for log or linear scale.
271 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800272 void clampVolume() {
273 if ((mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
274 for (auto it = this->begin(); it != this->end(); ++it) {
Andy Hungf3702642017-05-05 17:33:32 -0700275 if (!(it->second <= MAX_LOG_VOLUME) /* handle nan */) {
276 it->second = MAX_LOG_VOLUME;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800277 }
278 }
279 } else {
280 for (auto it = this->begin(); it != this->end(); ++it) {
Andy Hungf3702642017-05-05 17:33:32 -0700281 if (!(it->second >= MIN_LINEAR_VOLUME) /* handle nan */) {
282 it->second = MIN_LINEAR_VOLUME;
283 } else if (!(it->second <= MAX_LINEAR_VOLUME)) {
284 it->second = MAX_LINEAR_VOLUME;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800285 }
286 }
287 }
288 }
289
290 /* scaleToStartVolume() is used to set the start volume of a
291 * new VolumeShaper curve, when replacing one VolumeShaper
292 * with another using the "join" (volume match) option.
293 *
294 * It works best for monotonic volume ramps or ducks.
295 */
296 void scaleToStartVolume(T volume) {
297 if (this->size() < 2) {
298 return;
299 }
300 const T startVolume = first().second;
301 const T endVolume = last().second;
302 if (endVolume == startVolume) {
303 // match with linear ramp
304 const T offset = volume - startVolume;
Andy Hungf3702642017-05-05 17:33:32 -0700305 static const T scale = 1.f / (MAX_CURVE_TIME - MIN_CURVE_TIME); // nominally 1.f
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800306 for (auto it = this->begin(); it != this->end(); ++it) {
Andy Hungf3702642017-05-05 17:33:32 -0700307 it->second = it->second + offset * (MAX_CURVE_TIME - it->first) * scale;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800308 }
309 } else {
310 const T scale = (volume - endVolume) / (startVolume - endVolume);
311 for (auto it = this->begin(); it != this->end(); ++it) {
312 it->second = scale * (it->second - endVolume) + endVolume;
313 }
314 }
315 clampVolume();
316 }
317
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700318 status_t writeToParcel(Parcel *parcel) const override {
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700319 VolumeShaperConfiguration parcelable;
320 writeToParcelable(&parcelable);
321 return parcelable.writeToParcel(parcel);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800322 }
323
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700324 void writeToParcelable(VolumeShaperConfiguration *parcelable) const {
325 parcelable->id = getId();
326 parcelable->type = getTypeAsAidl();
327 parcelable->optionFlags = 0;
328 if (mType != TYPE_ID) {
329 parcelable->optionFlags = getOptionFlagsAsAidl();
330 parcelable->durationMs = getDurationMs();
Andy Hung71455ba2021-05-05 09:08:25 -0700331 parcelable->interpolatorConfig.emplace(); // create value in std::optional
332 Interpolator<S, T>::writeToConfig(&*parcelable->interpolatorConfig);
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700333 }
334 }
335
336 status_t readFromParcel(const Parcel* parcel) override {
337 VolumeShaperConfiguration data;
338 return data.readFromParcel(parcel)
339 ?: readFromParcelable(data);
340 }
341
342 status_t readFromParcelable(const VolumeShaperConfiguration& parcelable) {
343 setId(parcelable.id);
344 return setTypeFromAidl(parcelable.type)
345 ?: mType == TYPE_ID
346 ? NO_ERROR
347 : setOptionFlagsFromAidl(parcelable.optionFlags)
348 ?: setDurationMs(parcelable.durationMs)
Andy Hung71455ba2021-05-05 09:08:25 -0700349 ?: !parcelable.interpolatorConfig // check std::optional for value
350 ? BAD_VALUE // must be nonnull.
351 : Interpolator<S, T>::readFromConfig(*parcelable.interpolatorConfig)
352 ?: checkCurve();
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800353 }
354
Andy Hungf3702642017-05-05 17:33:32 -0700355 // Returns a string for debug printing.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800356 std::string toString() const {
357 std::stringstream ss;
Andy Hung23f81622024-06-07 18:48:49 -0700358 ss << "VolumeShaper::Configuration{mType=" << toString(mType);
Andy Hungf3702642017-05-05 17:33:32 -0700359 ss << ", mId=" << mId;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800360 if (mType != TYPE_ID) {
Andy Hung23f81622024-06-07 18:48:49 -0700361 ss << ", mOptionFlags=" << toString(mOptionFlags);
Andy Hungf3702642017-05-05 17:33:32 -0700362 ss << ", mDurationMs=" << mDurationMs;
363 ss << ", " << Interpolator<S, T>::toString().c_str();
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800364 }
Andy Hungf3702642017-05-05 17:33:32 -0700365 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800366 return ss.str();
367 }
368
369 private:
Andy Hungf3702642017-05-05 17:33:32 -0700370 Type mType; // type of configuration
371 int32_t mId; // A valid id is >= 0.
372 OptionFlag mOptionFlags; // option flags for the configuration.
373 double mDurationMs; // duration, must be > 0; default is 1000 ms.
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700374
375 int32_t getOptionFlagsAsAidl() const {
376 int32_t result = 0;
377 if (getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) {
378 result |=
379 1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::VOLUME_IN_DBFS);
380 }
381 if (getOptionFlags() & OPTION_FLAG_CLOCK_TIME) {
382 result |= 1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::CLOCK_TIME);
383 }
384 return result;
385 }
386
387 status_t setOptionFlagsFromAidl(int32_t aidl) {
388 std::underlying_type_t<OptionFlag> options = 0;
389 if (aidl & (1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::VOLUME_IN_DBFS))) {
390 options |= OPTION_FLAG_VOLUME_IN_DBFS;
391 }
392 if (aidl & (1 << static_cast<int>(VolumeShaperConfigurationOptionFlag::CLOCK_TIME))) {
393 options |= OPTION_FLAG_CLOCK_TIME;
394 }
395 return setOptionFlags(static_cast<OptionFlag>(options));
396 }
397
398 status_t setTypeFromAidl(VolumeShaperConfigurationType aidl) {
399 switch (aidl) {
400 case VolumeShaperConfigurationType::ID:
401 return setType(TYPE_ID);
402 case VolumeShaperConfigurationType::SCALE:
403 return setType(TYPE_SCALE);
404 default:
405 return BAD_VALUE;
406 }
407 }
408
409 VolumeShaperConfigurationType getTypeAsAidl() const {
410 switch (getType()) {
411 case TYPE_ID:
412 return VolumeShaperConfigurationType::ID;
413 case TYPE_SCALE:
414 return VolumeShaperConfigurationType::SCALE;
415 default:
416 LOG_ALWAYS_FATAL("Shouldn't get here");
417 }
418 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800419 }; // Configuration
420
Andy Hungf3702642017-05-05 17:33:32 -0700421 /* VolumeShaper::Operation expresses an operation to perform on the
422 * configuration (either explicitly specified or an id).
423 *
424 * This parallels the Java implementation and the enums must match.
425 * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
426 * details on the Java implementation.
427 */
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700428 class Operation : public RefBase, public Parcelable {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800429 public:
Andy Hungf3702642017-05-05 17:33:32 -0700430 // Must match with VolumeShaper.java.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800431 enum Flag : int32_t {
432 FLAG_NONE = 0,
Andy Hungf3702642017-05-05 17:33:32 -0700433 FLAG_REVERSE = (1 << 0), // the absence of this indicates "play"
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800434 FLAG_TERMINATE = (1 << 1),
435 FLAG_JOIN = (1 << 2),
436 FLAG_DELAY = (1 << 3),
Andy Hung4ef88d72017-02-21 19:47:53 -0800437 FLAG_CREATE_IF_NECESSARY = (1 << 4),
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800438
Andy Hung4ef88d72017-02-21 19:47:53 -0800439 FLAG_ALL = (FLAG_REVERSE | FLAG_TERMINATE | FLAG_JOIN | FLAG_DELAY
440 | FLAG_CREATE_IF_NECESSARY),
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800441 };
442
Andy Hung23f81622024-06-07 18:48:49 -0700443 static std::string toString(Flag flag) {
444 std::string s;
445 for (const auto& flagPair : std::initializer_list<std::pair<Flag, const char*>>{
446 {FLAG_REVERSE, "FLAG_REVERSE"},
447 {FLAG_TERMINATE, "FLAG_TERMINATE"},
448 {FLAG_JOIN, "FLAG_JOIN"},
449 {FLAG_DELAY, "FLAG_DELAY"},
450 {FLAG_CREATE_IF_NECESSARY, "FLAG_CREATE_IF_NECESSARY"},
451 }) {
452 if (flag & flagPair.first) {
453 if (!s.empty()) {
454 s.append("|");
455 }
456 s.append(flagPair.second);
457 }
458 }
459 return s;
460 }
461
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800462 Operation()
Andy Hung4ef88d72017-02-21 19:47:53 -0800463 : Operation(FLAG_NONE, -1 /* replaceId */) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800464 }
465
Andy Hung7d712bb2017-04-20 14:23:41 -0700466 Operation(Flag flags, int replaceId)
Andy Hung4ef88d72017-02-21 19:47:53 -0800467 : Operation(flags, replaceId, std::numeric_limits<S>::quiet_NaN() /* xOffset */) {
468 }
469
Andy Hung06a730b2020-04-09 13:28:31 -0700470 Operation(const Operation &operation)
Andy Hung4ef88d72017-02-21 19:47:53 -0800471 : Operation(operation.mFlags, operation.mReplaceId, operation.mXOffset) {
472 }
473
Andy Hung7d712bb2017-04-20 14:23:41 -0700474 explicit Operation(const sp<Operation> &operation)
475 : Operation(*operation.get()) {
476 }
477
478 Operation(Flag flags, int replaceId, S xOffset)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800479 : mFlags(flags)
Andy Hung4ef88d72017-02-21 19:47:53 -0800480 , mReplaceId(replaceId)
481 , mXOffset(xOffset) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800482 }
483
484 int32_t getReplaceId() const {
485 return mReplaceId;
486 }
487
488 void setReplaceId(int32_t replaceId) {
489 mReplaceId = replaceId;
490 }
491
Andy Hung4ef88d72017-02-21 19:47:53 -0800492 S getXOffset() const {
493 return mXOffset;
494 }
495
496 void setXOffset(S xOffset) {
Andy Hungf3702642017-05-05 17:33:32 -0700497 mXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
Andy Hung4ef88d72017-02-21 19:47:53 -0800498 }
499
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800500 Flag getFlags() const {
501 return mFlags;
502 }
503
Andy Hungf3702642017-05-05 17:33:32 -0700504 /* xOffset is the position on the volume curve and may go backwards
505 * if you are in reverse mode. This must be in the range from
506 * [MIN_CURVE_TIME, MAX_CURVE_TIME].
507 *
508 * normalizedTime always increases as time or framecount increases.
509 * normalizedTime is nominally from MIN_CURVE_TIME to MAX_CURVE_TIME when
510 * running through the curve, but could be outside this range afterwards.
511 * If you are reversing, this means the position on the curve, or xOffset,
512 * is computed as MAX_CURVE_TIME - normalizedTime, clamped to
513 * [MIN_CURVE_TIME, MAX_CURVE_TIME].
514 */
515 void setNormalizedTime(S normalizedTime) {
516 setXOffset((mFlags & FLAG_REVERSE) != 0
517 ? MAX_CURVE_TIME - normalizedTime : normalizedTime);
518 }
519
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800520 status_t setFlags(Flag flags) {
521 if ((flags & ~FLAG_ALL) != 0) {
522 ALOGE("flags has invalid bits: %#x", flags);
523 return BAD_VALUE;
524 }
525 mFlags = flags;
526 return NO_ERROR;
527 }
528
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700529 status_t writeToParcel(Parcel* parcel) const override {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800530 if (parcel == nullptr) return BAD_VALUE;
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700531 VolumeShaperOperation op;
532 writeToParcelable(&op);
533 return op.writeToParcel(parcel);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800534 }
535
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700536 void writeToParcelable(VolumeShaperOperation* op) const {
537 op->flags = getFlagsAsAidl();
538 op->replaceId = mReplaceId;
539 op->xOffset = mXOffset;
540 }
541
542 status_t readFromParcel(const Parcel* parcel) override {
543 VolumeShaperOperation op;
544 return op.readFromParcel(parcel)
545 ?: readFromParcelable(op);
546 }
547
548 status_t readFromParcelable(const VolumeShaperOperation& op) {
549 mReplaceId = op.replaceId;
550 mXOffset = op.xOffset;
551 return setFlagsFromAidl(op.flags);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800552 }
553
554 std::string toString() const {
555 std::stringstream ss;
Andy Hung23f81622024-06-07 18:48:49 -0700556 ss << "VolumeShaper::Operation{mFlags=" << toString(mFlags);
Andy Hungf3702642017-05-05 17:33:32 -0700557 ss << ", mReplaceId=" << mReplaceId;
558 ss << ", mXOffset=" << mXOffset;
559 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800560 return ss.str();
561 }
562
563 private:
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700564 status_t setFlagsFromAidl(int32_t aidl) {
565 std::underlying_type_t<Flag> flags = 0;
566 if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::REVERSE))) {
567 flags |= FLAG_REVERSE;
568 }
569 if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::TERMINATE))) {
570 flags |= FLAG_TERMINATE;
571 }
572 if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::JOIN))) {
573 flags |= FLAG_JOIN;
574 }
575 if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::DELAY))) {
576 flags |= FLAG_DELAY;
577 }
578 if (aidl & (1 << static_cast<int>(VolumeShaperOperationFlag::CREATE_IF_NECESSARY))) {
579 flags |= FLAG_CREATE_IF_NECESSARY;
580 }
581 return setFlags(static_cast<Flag>(flags));
582 }
583
584 int32_t getFlagsAsAidl() const {
585 int32_t aidl = 0;
586 std::underlying_type_t<Flag> flags = getFlags();
587 if (flags & FLAG_REVERSE) {
588 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::REVERSE));
589 }
590 if (flags & FLAG_TERMINATE) {
591 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::TERMINATE));
592 }
593 if (flags & FLAG_JOIN) {
594 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::JOIN));
595 }
596 if (flags & FLAG_DELAY) {
597 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::DELAY));
598 }
599 if (flags & FLAG_CREATE_IF_NECESSARY) {
600 aidl |= (1 << static_cast<int>(VolumeShaperOperationFlag::CREATE_IF_NECESSARY));
601 }
602 return aidl;
603 }
604
605 private:
Andy Hungf3702642017-05-05 17:33:32 -0700606 Flag mFlags; // operation to do
607 int32_t mReplaceId; // if >= 0 the id to remove in a replace operation.
608 S mXOffset; // position in the curve to set if a valid number (not nan)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800609 }; // Operation
610
Andy Hungf3702642017-05-05 17:33:32 -0700611 /* VolumeShaper.State is returned when requesting the last
612 * state of the VolumeShaper.
613 *
614 * This parallels the Java implementation.
615 * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
616 * details on the Java implementation.
617 */
Ivan Lozano8cf3a072017-08-09 09:01:33 -0700618 class State : public RefBase, public Parcelable {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800619 public:
Andy Hung7d712bb2017-04-20 14:23:41 -0700620 State(T volume, S xOffset)
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800621 : mVolume(volume)
622 , mXOffset(xOffset) {
623 }
624
625 State()
Andy Hungf3702642017-05-05 17:33:32 -0700626 : State(NAN, NAN) { }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800627
628 T getVolume() const {
629 return mVolume;
630 }
631
632 void setVolume(T volume) {
633 mVolume = volume;
634 }
635
636 S getXOffset() const {
637 return mXOffset;
638 }
639
640 void setXOffset(S xOffset) {
641 mXOffset = xOffset;
642 }
643
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700644 status_t writeToParcel(Parcel* parcel) const override {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800645 if (parcel == nullptr) return BAD_VALUE;
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700646 VolumeShaperState state;
647 writeToParcelable(&state);
648 return state.writeToParcel(parcel);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800649 }
650
Ytai Ben-Tsvif0658f42020-10-26 11:51:14 -0700651 void writeToParcelable(VolumeShaperState* parcelable) const {
652 parcelable->volume = mVolume;
653 parcelable->xOffset = mXOffset;
654 }
655
656 status_t readFromParcel(const Parcel* parcel) override {
657 VolumeShaperState state;
658 return state.readFromParcel(parcel)
659 ?: readFromParcelable(state);
660 }
661
662 status_t readFromParcelable(const VolumeShaperState& parcelable) {
663 mVolume = parcelable.volume;
664 mXOffset = parcelable.xOffset;
665 return OK;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800666 }
667
668 std::string toString() const {
669 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700670 ss << "VolumeShaper::State{mVolume=" << mVolume;
671 ss << ", mXOffset=" << mXOffset;
672 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800673 return ss.str();
674 }
675
676 private:
Andy Hungf3702642017-05-05 17:33:32 -0700677 T mVolume; // linear volume in the range MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME
678 S mXOffset; // position on curve expressed from MIN_CURVE_TIME to MAX_CURVE_TIME
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800679 }; // State
680
Andy Hungf3702642017-05-05 17:33:32 -0700681 // Internal helper class to do an affine transform for time and amplitude scaling.
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800682 template <typename R>
683 class Translate {
684 public:
685 Translate()
686 : mOffset(0)
687 , mScale(1) {
688 }
689
690 R getOffset() const {
691 return mOffset;
692 }
693
694 void setOffset(R offset) {
695 mOffset = offset;
696 }
697
698 R getScale() const {
699 return mScale;
700 }
701
702 void setScale(R scale) {
703 mScale = scale;
704 }
705
706 R operator()(R in) const {
707 return mScale * (in - mOffset);
708 }
709
710 std::string toString() const {
711 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700712 ss << "VolumeShaper::Translate{mOffset=" << mOffset;
713 ss << ", mScale=" << mScale;
714 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800715 return ss.str();
716 }
717
718 private:
719 R mOffset;
720 R mScale;
721 }; // Translate
722
723 static int64_t convertTimespecToUs(const struct timespec &tv)
724 {
Chih-Hung Hsieh21b96162018-12-11 13:48:32 -0800725 return tv.tv_sec * 1000000LL + tv.tv_nsec / 1000;
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800726 }
727
728 // current monotonic time in microseconds.
729 static int64_t getNowUs()
730 {
731 struct timespec tv;
732 if (clock_gettime(CLOCK_MONOTONIC, &tv) != 0) {
733 return 0; // system is really sick, just return 0 for consistency.
734 }
735 return convertTimespecToUs(tv);
736 }
737
Andy Hungf3702642017-05-05 17:33:32 -0700738 /* Native implementation of VolumeShaper. This is NOT mirrored
739 * on the Java side, so we don't need to mimic Java side layout
740 * and data; furthermore, this isn't refcounted as a "RefBase" object.
741 *
742 * Since we pass configuration and operation as shared pointers (like
743 * Java) there is a potential risk that the caller may modify
744 * these after delivery.
745 */
Andy Hung7d712bb2017-04-20 14:23:41 -0700746 VolumeShaper(
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800747 const sp<VolumeShaper::Configuration> &configuration,
748 const sp<VolumeShaper::Operation> &operation)
749 : mConfiguration(configuration) // we do not make a copy
750 , mOperation(operation) // ditto
751 , mStartFrame(-1)
752 , mLastVolume(T(1))
Andy Hungf3702642017-05-05 17:33:32 -0700753 , mLastXOffset(MIN_CURVE_TIME)
754 , mDelayXOffset(MIN_CURVE_TIME) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800755 if (configuration.get() != nullptr
756 && (getFlags() & VolumeShaper::Operation::FLAG_DELAY) == 0) {
757 mLastVolume = configuration->first().second;
758 }
759 }
760
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800761 // We allow a null operation here, though VolumeHandler always provides one.
762 VolumeShaper::Operation::Flag getFlags() const {
763 return mOperation == nullptr
Andy Hungf3702642017-05-05 17:33:32 -0700764 ? VolumeShaper::Operation::FLAG_NONE : mOperation->getFlags();
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800765 }
766
Andy Hungf3702642017-05-05 17:33:32 -0700767 /* Returns the last volume and xoffset reported to the AudioFlinger.
768 * If the VolumeShaper has not been started, compute what the volume
769 * should be based on the initial offset specified.
770 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800771 sp<VolumeShaper::State> getState() const {
Andy Hungf3702642017-05-05 17:33:32 -0700772 if (!isStarted()) {
773 const T volume = computeVolumeFromXOffset(mDelayXOffset);
774 VS_LOG("delayed VolumeShaper, using cached offset:%f for volume:%f",
775 mDelayXOffset, volume);
776 return new VolumeShaper::State(volume, mDelayXOffset);
777 } else {
778 return new VolumeShaper::State(mLastVolume, mLastXOffset);
779 }
780 }
781
782 S getDelayXOffset() const {
783 return mDelayXOffset;
Andy Hung4ef88d72017-02-21 19:47:53 -0800784 }
785
786 void setDelayXOffset(S xOffset) {
Andy Hungf3702642017-05-05 17:33:32 -0700787 mDelayXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800788 }
789
Andy Hung39399b62017-04-21 15:07:45 -0700790 bool isStarted() const {
791 return mStartFrame >= 0;
792 }
793
Andy Hungf3702642017-05-05 17:33:32 -0700794 /* getVolume() updates the last volume/xoffset state so it is not
795 * const, even though logically it may be viewed as const.
796 */
Andy Hung10cbff12017-02-21 17:30:14 -0800797 std::pair<T /* volume */, bool /* active */> getVolume(
798 int64_t trackFrameCount, double trackSampleRate) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800799 if ((getFlags() & VolumeShaper::Operation::FLAG_DELAY) != 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700800 // We haven't had PLAY called yet, so just return the value
801 // as if PLAY were called just now.
802 VS_LOG("delayed VolumeShaper, using cached offset %f", mDelayXOffset);
803 const T volume = computeVolumeFromXOffset(mDelayXOffset);
804 return std::make_pair(volume, false);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800805 }
806 const bool clockTime = (mConfiguration->getOptionFlags()
807 & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
808 const int64_t frameCount = clockTime ? getNowUs() : trackFrameCount;
809 const double sampleRate = clockTime ? 1000000 : trackSampleRate;
810
811 if (mStartFrame < 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700812 updatePosition(frameCount, sampleRate, mDelayXOffset);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800813 mStartFrame = frameCount;
814 }
815 VS_LOG("frameCount: %lld", (long long)frameCount);
Andy Hungf3702642017-05-05 17:33:32 -0700816 const S x = mXTranslate((T)frameCount);
817 VS_LOG("translation to normalized time: %f", x);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800818
Andy Hungf3702642017-05-05 17:33:32 -0700819 std::tuple<T /* volume */, S /* position */, bool /* active */> vt =
820 computeStateFromNormalizedTime(x);
821
822 mLastVolume = std::get<0>(vt);
823 mLastXOffset = std::get<1>(vt);
824 const bool active = std::get<2>(vt);
825 VS_LOG("rescaled time:%f volume:%f xOffset:%f active:%s",
826 x, mLastVolume, mLastXOffset, active ? "true" : "false");
827 return std::make_pair(mLastVolume, active);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800828 }
829
830 std::string toString() const {
831 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -0700832 ss << "VolumeShaper{mStartFrame=" << mStartFrame;
833 ss << ", mXTranslate=" << mXTranslate.toString().c_str();
834 ss << ", mConfiguration=" <<
835 (mConfiguration.get() == nullptr
836 ? "nullptr" : mConfiguration->toString().c_str());
837 ss << ", mOperation=" <<
838 (mOperation.get() == nullptr
839 ? "nullptr" : mOperation->toString().c_str());
840 ss << "}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800841 return ss.str();
842 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800843
Andy Hungf3702642017-05-05 17:33:32 -0700844 Translate<S> mXTranslate; // translation from frames (usec for clock time) to normalized time.
Andy Hung4ef88d72017-02-21 19:47:53 -0800845 sp<VolumeShaper::Configuration> mConfiguration;
846 sp<VolumeShaper::Operation> mOperation;
Andy Hungf3702642017-05-05 17:33:32 -0700847
848private:
Andy Hung4ef88d72017-02-21 19:47:53 -0800849 int64_t mStartFrame; // starting frame, non-negative when started (in usec for clock time)
850 T mLastVolume; // last computed interpolated volume (y-axis)
851 S mLastXOffset; // last computed interpolated xOffset/time (x-axis)
Andy Hungf3702642017-05-05 17:33:32 -0700852 S mDelayXOffset; // xOffset to use for first invocation of VolumeShaper.
853
854 // Called internally to adjust mXTranslate for first time start.
855 void updatePosition(int64_t startFrame, double sampleRate, S xOffset) {
856 double scale = (mConfiguration->last().first - mConfiguration->first().first)
857 / (mConfiguration->getDurationMs() * 0.001 * sampleRate);
858 const double minScale = 1. / static_cast<double>(INT64_MAX);
859 scale = std::max(scale, minScale);
860 VS_LOG("update position: scale %lf frameCount:%lld, sampleRate:%lf, xOffset:%f",
861 scale, (long long) startFrame, sampleRate, xOffset);
862
863 S normalizedTime = (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) != 0 ?
864 MAX_CURVE_TIME - xOffset : xOffset;
865 mXTranslate.setOffset(static_cast<float>(static_cast<double>(startFrame)
866 - static_cast<double>(normalizedTime) / scale));
867 mXTranslate.setScale(static_cast<float>(scale));
868 VS_LOG("translate: %s", mXTranslate.toString().c_str());
869 }
870
871 T computeVolumeFromXOffset(S xOffset) const {
872 const T unscaledVolume = mConfiguration->findY(xOffset);
873 const T volume = mConfiguration->adjustVolume(unscaledVolume); // handle log scale
874 VS_LOG("computeVolumeFromXOffset %f -> %f -> %f", xOffset, unscaledVolume, volume);
875 return volume;
876 }
877
878 std::tuple<T /* volume */, S /* position */, bool /* active */>
879 computeStateFromNormalizedTime(S x) const {
880 bool active = true;
881 // handle reversal of position
882 if (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) {
883 x = MAX_CURVE_TIME - x;
884 VS_LOG("reversing to %f", x);
885 if (x < MIN_CURVE_TIME) {
886 x = MIN_CURVE_TIME;
887 active = false; // at the end
888 } else if (x > MAX_CURVE_TIME) {
889 x = MAX_CURVE_TIME; //early
890 }
891 } else {
892 if (x < MIN_CURVE_TIME) {
893 x = MIN_CURVE_TIME; // early
894 } else if (x > MAX_CURVE_TIME) {
895 x = MAX_CURVE_TIME;
896 active = false; // at end
897 }
898 }
899 const S xOffset = x;
900 const T volume = computeVolumeFromXOffset(xOffset);
901 return std::make_tuple(volume, xOffset, active);
902 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800903}; // VolumeShaper
904
Andy Hungf3702642017-05-05 17:33:32 -0700905/* VolumeHandler combines the volume factors of multiple VolumeShapers associated
906 * with a player. It is thread safe by synchronizing all public methods.
907 *
908 * This is a native-only implementation.
909 *
910 * The server side VolumeHandler is used to maintain a list of volume handlers,
911 * keep state, and obtain volume.
912 *
913 * The client side VolumeHandler is used to maintain a list of volume handlers,
914 * keep some partial state, and restore if the server dies.
915 */
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800916class VolumeHandler : public RefBase {
917public:
918 using S = float;
919 using T = float;
920
Andy Hung4ef88d72017-02-21 19:47:53 -0800921 // A volume handler which just keeps track of active VolumeShapers does not need sampleRate.
922 VolumeHandler()
923 : VolumeHandler(0 /* sampleRate */) {
924 }
925
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800926 explicit VolumeHandler(uint32_t sampleRate)
927 : mSampleRate((double)sampleRate)
Andy Hung4ef88d72017-02-21 19:47:53 -0800928 , mLastFrame(0)
Andy Hungf3702642017-05-05 17:33:32 -0700929 , mVolumeShaperIdCounter(VolumeShaper::kSystemVolumeShapersMax)
Andy Hungda540db2017-04-20 14:06:17 -0700930 , mLastVolume(1.f, false) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800931 }
932
933 VolumeShaper::Status applyVolumeShaper(
934 const sp<VolumeShaper::Configuration> &configuration,
Andy Hungf3702642017-05-05 17:33:32 -0700935 const sp<VolumeShaper::Operation> &operation_in) {
936 // make a local copy of operation, as we modify it.
937 sp<VolumeShaper::Operation> operation(new VolumeShaper::Operation(operation_in));
Andy Hung10cbff12017-02-21 17:30:14 -0800938 VS_LOG("applyVolumeShaper:configuration: %s", configuration->toString().c_str());
939 VS_LOG("applyVolumeShaper:operation: %s", operation->toString().c_str());
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800940 AutoMutex _l(mLock);
941 if (configuration == nullptr) {
942 ALOGE("null configuration");
943 return VolumeShaper::Status(BAD_VALUE);
944 }
945 if (operation == nullptr) {
946 ALOGE("null operation");
947 return VolumeShaper::Status(BAD_VALUE);
948 }
949 const int32_t id = configuration->getId();
950 if (id < 0) {
951 ALOGE("negative id: %d", id);
952 return VolumeShaper::Status(BAD_VALUE);
953 }
954 VS_LOG("applyVolumeShaper id: %d", id);
955
956 switch (configuration->getType()) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800957 case VolumeShaper::Configuration::TYPE_SCALE: {
958 const int replaceId = operation->getReplaceId();
959 if (replaceId >= 0) {
Andy Hungf3702642017-05-05 17:33:32 -0700960 VS_LOG("replacing %d", replaceId);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800961 auto replaceIt = findId_l(replaceId);
962 if (replaceIt == mVolumeShapers.end()) {
963 ALOGW("cannot find replace id: %d", replaceId);
964 } else {
Andy Hungf3702642017-05-05 17:33:32 -0700965 if ((operation->getFlags() & VolumeShaper::Operation::FLAG_JOIN) != 0) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800966 // For join, we scale the start volume of the current configuration
967 // to match the last-used volume of the replacing VolumeShaper.
968 auto state = replaceIt->getState();
Andy Hungf3702642017-05-05 17:33:32 -0700969 ALOGD("join: state:%s", state->toString().c_str());
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800970 if (state->getXOffset() >= 0) { // valid
971 const T volume = state->getVolume();
972 ALOGD("join: scaling start volume to %f", volume);
973 configuration->scaleToStartVolume(volume);
974 }
975 }
976 (void)mVolumeShapers.erase(replaceIt);
977 }
Andy Hung4ef88d72017-02-21 19:47:53 -0800978 operation->setReplaceId(-1);
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800979 }
980 // check if we have another of the same id.
981 auto oldIt = findId_l(id);
982 if (oldIt != mVolumeShapers.end()) {
Andy Hung4ef88d72017-02-21 19:47:53 -0800983 if ((operation->getFlags()
984 & VolumeShaper::Operation::FLAG_CREATE_IF_NECESSARY) != 0) {
985 // TODO: move the case to a separate function.
986 goto HANDLE_TYPE_ID; // no need to create, take over existing id.
987 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -0800988 ALOGW("duplicate id, removing old %d", id);
989 (void)mVolumeShapers.erase(oldIt);
990 }
Andy Hungf3702642017-05-05 17:33:32 -0700991
992 /* Check if too many application VolumeShapers (with id >= kSystemVolumeShapersMax).
993 * We check on the server side to ensure synchronization and robustness.
994 *
995 * This shouldn't fail on a replace command unless the replaced id is
996 * already invalid (which *should* be checked in the Java layer).
997 */
998 if (id >= VolumeShaper::kSystemVolumeShapersMax
999 && numberOfUserVolumeShapers_l() >= VolumeShaper::kUserVolumeShapersMax) {
1000 ALOGW("Too many app VolumeShapers, cannot add to VolumeHandler");
1001 return VolumeShaper::Status(INVALID_OPERATION);
1002 }
1003
1004 // create new VolumeShaper with default behavior.
1005 mVolumeShapers.emplace_back(configuration, new VolumeShaper::Operation());
1006 VS_LOG("after adding, number of volumeShapers:%zu", mVolumeShapers.size());
Andy Hung10cbff12017-02-21 17:30:14 -08001007 }
1008 // fall through to handle the operation
Andy Hung4ef88d72017-02-21 19:47:53 -08001009 HANDLE_TYPE_ID:
Andy Hung10cbff12017-02-21 17:30:14 -08001010 case VolumeShaper::Configuration::TYPE_ID: {
1011 VS_LOG("trying to find id: %d", id);
1012 auto it = findId_l(id);
1013 if (it == mVolumeShapers.end()) {
1014 VS_LOG("couldn't find id: %d", id);
1015 return VolumeShaper::Status(INVALID_OPERATION);
1016 }
Andy Hungf3702642017-05-05 17:33:32 -07001017 if ((operation->getFlags() & VolumeShaper::Operation::FLAG_TERMINATE) != 0) {
Andy Hung10cbff12017-02-21 17:30:14 -08001018 VS_LOG("terminate id: %d", id);
1019 mVolumeShapers.erase(it);
1020 break;
1021 }
1022 const bool clockTime = (it->mConfiguration->getOptionFlags()
1023 & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
1024 if ((it->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) !=
1025 (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE)) {
Andy Hungf3702642017-05-05 17:33:32 -07001026 if (it->isStarted()) {
1027 const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
1028 const S x = it->mXTranslate((T)frameCount);
1029 VS_LOG("reverse normalizedTime: %f", x);
1030 // reflect position
1031 S target = MAX_CURVE_TIME - x;
1032 if (target < MIN_CURVE_TIME) {
1033 VS_LOG("clamp to start - begin immediately");
1034 target = MIN_CURVE_TIME;
1035 }
1036 VS_LOG("reverse normalizedTime target: %f", target);
1037 it->mXTranslate.setOffset(it->mXTranslate.getOffset()
1038 + (x - target) / it->mXTranslate.getScale());
Andy Hung10cbff12017-02-21 17:30:14 -08001039 }
Andy Hungf3702642017-05-05 17:33:32 -07001040 // if not started, the delay offset doesn't change.
Andy Hung10cbff12017-02-21 17:30:14 -08001041 }
Andy Hung4ef88d72017-02-21 19:47:53 -08001042 const S xOffset = operation->getXOffset();
1043 if (!std::isnan(xOffset)) {
Andy Hungf3702642017-05-05 17:33:32 -07001044 if (it->isStarted()) {
1045 const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
1046 const S x = it->mXTranslate((T)frameCount);
1047 VS_LOG("normalizedTime translation: %f", x);
1048 const S target =
1049 (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) != 0 ?
1050 MAX_CURVE_TIME - xOffset : xOffset;
1051 VS_LOG("normalizedTime target x offset: %f", target);
1052 it->mXTranslate.setOffset(it->mXTranslate.getOffset()
1053 + (x - target) / it->mXTranslate.getScale());
1054 } else {
1055 it->setDelayXOffset(xOffset);
1056 }
Andy Hung4ef88d72017-02-21 19:47:53 -08001057 }
Andy Hung10cbff12017-02-21 17:30:14 -08001058 it->mOperation = operation; // replace the operation
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001059 } break;
1060 }
1061 return VolumeShaper::Status(id);
1062 }
1063
1064 sp<VolumeShaper::State> getVolumeShaperState(int id) {
1065 AutoMutex _l(mLock);
1066 auto it = findId_l(id);
1067 if (it == mVolumeShapers.end()) {
Andy Hung4ef88d72017-02-21 19:47:53 -08001068 VS_LOG("cannot find state for id: %d", id);
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001069 return nullptr;
1070 }
1071 return it->getState();
1072 }
1073
Andy Hungf3702642017-05-05 17:33:32 -07001074 /* getVolume() is not const, as it updates internal state.
1075 * Once called, any VolumeShapers not already started begin running.
1076 */
Andy Hung10cbff12017-02-21 17:30:14 -08001077 std::pair<T /* volume */, bool /* active */> getVolume(int64_t trackFrameCount) {
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001078 AutoMutex _l(mLock);
1079 mLastFrame = trackFrameCount;
1080 T volume(1);
Andy Hung10cbff12017-02-21 17:30:14 -08001081 size_t activeCount = 0;
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001082 for (auto it = mVolumeShapers.begin(); it != mVolumeShapers.end();) {
Andy Hungf3702642017-05-05 17:33:32 -07001083 const std::pair<T, bool> shaperVolume =
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001084 it->getVolume(trackFrameCount, mSampleRate);
1085 volume *= shaperVolume.first;
Andy Hung10cbff12017-02-21 17:30:14 -08001086 activeCount += shaperVolume.second;
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001087 ++it;
1088 }
Andy Hungda540db2017-04-20 14:06:17 -07001089 mLastVolume = std::make_pair(volume, activeCount != 0);
Andy Hungf3702642017-05-05 17:33:32 -07001090 VS_LOG("getVolume: <%f, %s>", mLastVolume.first, mLastVolume.second ? "true" : "false");
Andy Hungda540db2017-04-20 14:06:17 -07001091 return mLastVolume;
1092 }
1093
Andy Hungf3702642017-05-05 17:33:32 -07001094 /* Used by a client side VolumeHandler to ensure all the VolumeShapers
1095 * indicate that they have been started. Upon a change in audioserver
1096 * output sink, this information is used for restoration of the server side
1097 * VolumeHandler.
1098 */
Andy Hung39399b62017-04-21 15:07:45 -07001099 void setStarted() {
1100 (void)getVolume(mLastFrame); // getVolume() will start the individual VolumeShapers.
1101 }
1102
Andy Hungda540db2017-04-20 14:06:17 -07001103 std::pair<T /* volume */, bool /* active */> getLastVolume() const {
1104 AutoMutex _l(mLock);
1105 return mLastVolume;
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001106 }
1107
1108 std::string toString() const {
1109 AutoMutex _l(mLock);
1110 std::stringstream ss;
Andy Hungf3702642017-05-05 17:33:32 -07001111 ss << "VolumeHandler{mSampleRate=" << mSampleRate;
1112 ss << ", mLastFrame=" << mLastFrame;
1113 ss << ", mVolumeShapers={";
1114 bool first = true;
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001115 for (const auto &shaper : mVolumeShapers) {
Andy Hungf3702642017-05-05 17:33:32 -07001116 if (first) {
1117 first = false;
1118 } else {
1119 ss << ", ";
1120 }
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001121 ss << shaper.toString().c_str();
1122 }
Andy Hungf3702642017-05-05 17:33:32 -07001123 ss << "}}";
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001124 return ss.str();
1125 }
1126
Andy Hung39399b62017-04-21 15:07:45 -07001127 void forall(const std::function<VolumeShaper::Status (const VolumeShaper &)> &lambda) {
Andy Hung4ef88d72017-02-21 19:47:53 -08001128 AutoMutex _l(mLock);
Andy Hungda540db2017-04-20 14:06:17 -07001129 VS_LOG("forall: mVolumeShapers.size() %zu", mVolumeShapers.size());
Andy Hung4ef88d72017-02-21 19:47:53 -08001130 for (const auto &shaper : mVolumeShapers) {
Andy Hung39399b62017-04-21 15:07:45 -07001131 VolumeShaper::Status status = lambda(shaper);
1132 VS_LOG("forall applying lambda on shaper (%p): %d", &shaper, (int)status);
Andy Hung4ef88d72017-02-21 19:47:53 -08001133 }
1134 }
1135
1136 void reset() {
1137 AutoMutex _l(mLock);
1138 mVolumeShapers.clear();
Andy Hung7d712bb2017-04-20 14:23:41 -07001139 mLastFrame = 0;
Andy Hung4ef88d72017-02-21 19:47:53 -08001140 // keep mVolumeShaperIdCounter as is.
1141 }
1142
Andy Hungf3702642017-05-05 17:33:32 -07001143 /* Sets the configuration id if necessary - This is based on the counter
1144 * internal to the VolumeHandler.
1145 */
Andy Hung4ef88d72017-02-21 19:47:53 -08001146 void setIdIfNecessary(const sp<VolumeShaper::Configuration> &configuration) {
Pawan Wagh8282bb42023-07-05 23:01:17 +00001147 if (configuration && configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
Andy Hung4ef88d72017-02-21 19:47:53 -08001148 const int id = configuration->getId();
1149 if (id == -1) {
1150 // Reassign to a unique id, skipping system ids.
1151 AutoMutex _l(mLock);
1152 while (true) {
1153 if (mVolumeShaperIdCounter == INT32_MAX) {
Andy Hungf3702642017-05-05 17:33:32 -07001154 mVolumeShaperIdCounter = VolumeShaper::kSystemVolumeShapersMax;
Andy Hung4ef88d72017-02-21 19:47:53 -08001155 } else {
1156 ++mVolumeShaperIdCounter;
1157 }
1158 if (findId_l(mVolumeShaperIdCounter) != mVolumeShapers.end()) {
1159 continue; // collision with an existing id.
1160 }
1161 configuration->setId(mVolumeShaperIdCounter);
1162 ALOGD("setting id to %d", mVolumeShaperIdCounter);
1163 break;
1164 }
1165 }
1166 }
1167 }
1168
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001169private:
1170 std::list<VolumeShaper>::iterator findId_l(int32_t id) {
1171 std::list<VolumeShaper>::iterator it = mVolumeShapers.begin();
1172 for (; it != mVolumeShapers.end(); ++it) {
1173 if (it->mConfiguration->getId() == id) {
1174 break;
1175 }
1176 }
1177 return it;
1178 }
1179
Andy Hungf3702642017-05-05 17:33:32 -07001180 size_t numberOfUserVolumeShapers_l() const {
1181 size_t count = 0;
1182 for (const auto &shaper : mVolumeShapers) {
1183 count += (shaper.mConfiguration->getId() >= VolumeShaper::kSystemVolumeShapersMax);
1184 }
1185 return count;
1186 }
1187
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001188 mutable Mutex mLock;
1189 double mSampleRate; // in samples (frames) per second
Andy Hung7d712bb2017-04-20 14:23:41 -07001190 int64_t mLastFrame; // logging purpose only, 0 on start
Andy Hung4ef88d72017-02-21 19:47:53 -08001191 int32_t mVolumeShaperIdCounter; // a counter to return a unique volume shaper id.
Andy Hungda540db2017-04-20 14:06:17 -07001192 std::pair<T /* volume */, bool /* active */> mLastVolume;
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001193 std::list<VolumeShaper> mVolumeShapers; // list provides stable iterators on erase
1194}; // VolumeHandler
1195
Ivan Lozano8cf3a072017-08-09 09:01:33 -07001196} // namespace media
1197
Andy Hung9fc8b5c2017-01-24 13:36:48 -08001198} // namespace android
1199
1200#pragma pop_macro("LOG_TAG")
1201
1202#endif // ANDROID_VOLUME_SHAPER_H