blob: a3b6b36fd2d9f85419c3507bb237a2852865e5e2 [file] [log] [blame]
François Gaffiedfd74092015-03-19 12:10:59 +01001/*
2 * Copyright (C) 2015 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#pragma once
18
19#include <system/audio.h>
20#include <utils/Log.h>
Eric Laurentffbc80f2015-03-18 18:30:19 -070021#include <math.h>
22
François Gaffie1c878552018-11-22 16:53:21 +010023namespace android {
24/**
25 * VolumeSource is the discriminent for volume management on an output.
26 * It used to be the stream type by legacy, it may be host volume group or a volume curves if
27 * we allow to have more than one curve per volume group.
28 */
29enum VolumeSource : std::underlying_type<audio_stream_type_t>::type;
30static const VolumeSource VOLUME_SOURCE_NONE = static_cast<VolumeSource>(AUDIO_STREAM_DEFAULT);
31
32static inline VolumeSource streamToVolumeSource(audio_stream_type_t stream) {
33 return static_cast<VolumeSource>(stream);
34}
35
36
37} // namespace android
38
Eric Laurentffbc80f2015-03-18 18:30:19 -070039// Absolute min volume in dB (can be represented in single precision normal float value)
40#define VOLUME_MIN_DB (-758)
François Gaffiedfd74092015-03-19 12:10:59 +010041
42class VolumeCurvePoint
43{
44public:
45 int mIndex;
46 float mDBAttenuation;
47};
48
François Gaffied0609ad2015-12-01 17:56:08 +010049/**
50 * device categories used for volume curve management.
51 */
52enum device_category {
53 DEVICE_CATEGORY_HEADSET,
54 DEVICE_CATEGORY_SPEAKER,
55 DEVICE_CATEGORY_EARPIECE,
56 DEVICE_CATEGORY_EXT_MEDIA,
Jakub Pawlowski24b0a892018-04-02 19:17:11 -070057 DEVICE_CATEGORY_HEARING_AID,
François Gaffied0609ad2015-12-01 17:56:08 +010058 DEVICE_CATEGORY_CNT
59};
60
François Gaffiedfd74092015-03-19 12:10:59 +010061class Volume
62{
63public:
64 /**
65 * 4 points to define the volume attenuation curve, each characterized by the volume
66 * index (from 0 to 100) at which they apply, and the attenuation in dB at that index.
Eric Laurentffbc80f2015-03-18 18:30:19 -070067 * we use 100 steps to avoid rounding errors when computing the volume in volIndexToDb()
François Gaffiedfd74092015-03-19 12:10:59 +010068 *
69 * @todo shall become configurable
70 */
71 enum {
72 VOLMIN = 0,
73 VOLKNEE1 = 1,
74 VOLKNEE2 = 2,
75 VOLMAX = 3,
76
77 VOLCNT = 4
78 };
79
80 /**
François Gaffiedfd74092015-03-19 12:10:59 +010081 * extract one device relevant for volume control from multiple device selection
82 *
83 * @param[in] device for which the volume category is associated
84 *
85 * @return subset of device required to limit the number of volume category per device
86 */
87 static audio_devices_t getDeviceForVolume(audio_devices_t device)
88 {
89 if (device == AUDIO_DEVICE_NONE) {
90 // this happens when forcing a route update and no track is active on an output.
91 // In this case the returned category is not important.
92 device = AUDIO_DEVICE_OUT_SPEAKER;
93 } else if (popcount(device) > 1) {
94 // Multiple device selection is either:
95 // - speaker + one other device: give priority to speaker in this case.
96 // - one A2DP device + another device: happens with duplicated output. In this case
97 // retain the device on the A2DP output as the other must not correspond to an active
98 // selection if not the speaker.
99 // - HDMI-CEC system audio mode only output: give priority to available item in order.
100 if (device & AUDIO_DEVICE_OUT_SPEAKER) {
101 device = AUDIO_DEVICE_OUT_SPEAKER;
Eric Laurent9a7d9222015-07-02 15:30:23 -0700102 } else if (device & AUDIO_DEVICE_OUT_SPEAKER_SAFE) {
103 device = AUDIO_DEVICE_OUT_SPEAKER_SAFE;
François Gaffiedfd74092015-03-19 12:10:59 +0100104 } else if (device & AUDIO_DEVICE_OUT_HDMI_ARC) {
105 device = AUDIO_DEVICE_OUT_HDMI_ARC;
106 } else if (device & AUDIO_DEVICE_OUT_AUX_LINE) {
107 device = AUDIO_DEVICE_OUT_AUX_LINE;
108 } else if (device & AUDIO_DEVICE_OUT_SPDIF) {
109 device = AUDIO_DEVICE_OUT_SPDIF;
110 } else {
111 device = (audio_devices_t)(device & AUDIO_DEVICE_OUT_ALL_A2DP);
112 }
113 }
114
115 /*SPEAKER_SAFE is an alias of SPEAKER for purposes of volume control*/
116 if (device == AUDIO_DEVICE_OUT_SPEAKER_SAFE)
117 device = AUDIO_DEVICE_OUT_SPEAKER;
118
119 ALOGW_IF(popcount(device) != 1,
120 "getDeviceForVolume() invalid device combination: %08x",
121 device);
122
123 return device;
124 }
125
126 /**
127 * returns the category the device belongs to with regard to volume curve management
128 *
129 * @param[in] device to check upon the category to whom it belongs to.
130 *
131 * @return device category.
132 */
133 static device_category getDeviceCategory(audio_devices_t device)
134 {
135 switch(getDeviceForVolume(device)) {
136 case AUDIO_DEVICE_OUT_EARPIECE:
137 return DEVICE_CATEGORY_EARPIECE;
138 case AUDIO_DEVICE_OUT_WIRED_HEADSET:
139 case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
140 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
141 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
142 case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP:
143 case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES:
Eric Laurent904d6322017-03-17 17:20:47 -0700144 case AUDIO_DEVICE_OUT_USB_HEADSET:
François Gaffiedfd74092015-03-19 12:10:59 +0100145 return DEVICE_CATEGORY_HEADSET;
Jakub Pawlowski24b0a892018-04-02 19:17:11 -0700146 case AUDIO_DEVICE_OUT_HEARING_AID:
147 return DEVICE_CATEGORY_HEARING_AID;
François Gaffiedfd74092015-03-19 12:10:59 +0100148 case AUDIO_DEVICE_OUT_LINE:
149 case AUDIO_DEVICE_OUT_AUX_DIGITAL:
Eric Laurent904d6322017-03-17 17:20:47 -0700150 case AUDIO_DEVICE_OUT_USB_DEVICE:
François Gaffiedfd74092015-03-19 12:10:59 +0100151 return DEVICE_CATEGORY_EXT_MEDIA;
152 case AUDIO_DEVICE_OUT_SPEAKER:
Jean-Michel Trivi227342a2018-09-14 11:42:37 -0700153 case AUDIO_DEVICE_OUT_SPEAKER_SAFE:
François Gaffiedfd74092015-03-19 12:10:59 +0100154 case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
155 case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER:
156 case AUDIO_DEVICE_OUT_USB_ACCESSORY:
François Gaffiedfd74092015-03-19 12:10:59 +0100157 case AUDIO_DEVICE_OUT_REMOTE_SUBMIX:
158 default:
159 return DEVICE_CATEGORY_SPEAKER;
160 }
161 }
162
Eric Laurentffbc80f2015-03-18 18:30:19 -0700163 static inline float DbToAmpl(float decibels)
164 {
165 if (decibels <= VOLUME_MIN_DB) {
166 return 0.0f;
167 }
168 return exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 )
169 }
170
171 static inline float AmplToDb(float amplification)
172 {
173 if (amplification == 0) {
174 return VOLUME_MIN_DB;
175 }
176 return 20 * log10(amplification);
177 }
178
François Gaffiedfd74092015-03-19 12:10:59 +0100179};