François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 1 | /* |
| 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 | |
François Gaffie | aaac0fd | 2018-11-22 17:56:39 +0100 | [diff] [blame] | 19 | #include <media/AudioCommonTypes.h> |
jiabin | 9a3361e | 2019-10-01 09:38:30 -0700 | [diff] [blame^] | 20 | #include <media/AudioContainers.h> |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 21 | #include <system/audio.h> |
| 22 | #include <utils/Log.h> |
Eric Laurent | ffbc80f | 2015-03-18 18:30:19 -0700 | [diff] [blame] | 23 | #include <math.h> |
| 24 | |
François Gaffie | 1c87855 | 2018-11-22 16:53:21 +0100 | [diff] [blame] | 25 | namespace android { |
François Gaffie | 251c7f0 | 2018-11-07 10:41:08 +0100 | [diff] [blame] | 26 | |
François Gaffie | 1c87855 | 2018-11-22 16:53:21 +0100 | [diff] [blame] | 27 | /** |
| 28 | * VolumeSource is the discriminent for volume management on an output. |
| 29 | * It used to be the stream type by legacy, it may be host volume group or a volume curves if |
François Gaffie | aaac0fd | 2018-11-22 17:56:39 +0100 | [diff] [blame] | 30 | * we allow to have more than one curve per volume group (mandatory to get rid of AudioServer |
| 31 | * stream aliases. |
François Gaffie | 1c87855 | 2018-11-22 16:53:21 +0100 | [diff] [blame] | 32 | */ |
François Gaffie | aaac0fd | 2018-11-22 17:56:39 +0100 | [diff] [blame] | 33 | enum VolumeSource : std::underlying_type<volume_group_t>::type; |
| 34 | static const VolumeSource VOLUME_SOURCE_NONE = static_cast<VolumeSource>(VOLUME_GROUP_NONE); |
François Gaffie | 1c87855 | 2018-11-22 16:53:21 +0100 | [diff] [blame] | 35 | |
François Gaffie | 1c87855 | 2018-11-22 16:53:21 +0100 | [diff] [blame] | 36 | } // namespace android |
| 37 | |
Eric Laurent | ffbc80f | 2015-03-18 18:30:19 -0700 | [diff] [blame] | 38 | // Absolute min volume in dB (can be represented in single precision normal float value) |
| 39 | #define VOLUME_MIN_DB (-758) |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 40 | |
| 41 | class VolumeCurvePoint |
| 42 | { |
| 43 | public: |
| 44 | int mIndex; |
| 45 | float mDBAttenuation; |
| 46 | }; |
| 47 | |
François Gaffie | d0609ad | 2015-12-01 17:56:08 +0100 | [diff] [blame] | 48 | /** |
| 49 | * device categories used for volume curve management. |
| 50 | */ |
| 51 | enum device_category { |
| 52 | DEVICE_CATEGORY_HEADSET, |
| 53 | DEVICE_CATEGORY_SPEAKER, |
| 54 | DEVICE_CATEGORY_EARPIECE, |
| 55 | DEVICE_CATEGORY_EXT_MEDIA, |
Jakub Pawlowski | 24b0a89 | 2018-04-02 19:17:11 -0700 | [diff] [blame] | 56 | DEVICE_CATEGORY_HEARING_AID, |
François Gaffie | d0609ad | 2015-12-01 17:56:08 +0100 | [diff] [blame] | 57 | DEVICE_CATEGORY_CNT |
| 58 | }; |
| 59 | |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 60 | class Volume |
| 61 | { |
| 62 | public: |
| 63 | /** |
| 64 | * 4 points to define the volume attenuation curve, each characterized by the volume |
| 65 | * index (from 0 to 100) at which they apply, and the attenuation in dB at that index. |
Eric Laurent | ffbc80f | 2015-03-18 18:30:19 -0700 | [diff] [blame] | 66 | * we use 100 steps to avoid rounding errors when computing the volume in volIndexToDb() |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 67 | * |
| 68 | * @todo shall become configurable |
| 69 | */ |
| 70 | enum { |
| 71 | VOLMIN = 0, |
| 72 | VOLKNEE1 = 1, |
| 73 | VOLKNEE2 = 2, |
| 74 | VOLMAX = 3, |
| 75 | |
| 76 | VOLCNT = 4 |
| 77 | }; |
| 78 | |
| 79 | /** |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 80 | * extract one device relevant for volume control from multiple device selection |
| 81 | * |
| 82 | * @param[in] device for which the volume category is associated |
| 83 | * |
| 84 | * @return subset of device required to limit the number of volume category per device |
| 85 | */ |
jiabin | 9a3361e | 2019-10-01 09:38:30 -0700 | [diff] [blame^] | 86 | static audio_devices_t getDeviceForVolume(const android::DeviceTypeSet& deviceTypes) |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 87 | { |
jiabin | 9a3361e | 2019-10-01 09:38:30 -0700 | [diff] [blame^] | 88 | audio_devices_t deviceType = AUDIO_DEVICE_NONE; |
| 89 | if (deviceTypes.empty()) { |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 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. |
jiabin | 9a3361e | 2019-10-01 09:38:30 -0700 | [diff] [blame^] | 92 | deviceType = AUDIO_DEVICE_OUT_SPEAKER; |
| 93 | } else if (deviceTypes.size() > 1) { |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 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. |
jiabin | 9a3361e | 2019-10-01 09:38:30 -0700 | [diff] [blame^] | 100 | if (deviceTypes.count(AUDIO_DEVICE_OUT_SPEAKER) != 0) { |
| 101 | deviceType = AUDIO_DEVICE_OUT_SPEAKER; |
| 102 | } else if (deviceTypes.count(AUDIO_DEVICE_OUT_SPEAKER_SAFE) != 0) { |
| 103 | deviceType = AUDIO_DEVICE_OUT_SPEAKER_SAFE; |
| 104 | } else if (deviceTypes.count(AUDIO_DEVICE_OUT_HDMI_ARC) != 0) { |
| 105 | deviceType = AUDIO_DEVICE_OUT_HDMI_ARC; |
| 106 | } else if (deviceTypes.count(AUDIO_DEVICE_OUT_AUX_LINE) != 0) { |
| 107 | deviceType = AUDIO_DEVICE_OUT_AUX_LINE; |
| 108 | } else if (deviceTypes.count(AUDIO_DEVICE_OUT_SPDIF) != 0) { |
| 109 | deviceType = AUDIO_DEVICE_OUT_SPDIF; |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 110 | } else { |
jiabin | 9a3361e | 2019-10-01 09:38:30 -0700 | [diff] [blame^] | 111 | std::vector<audio_devices_t> a2dpDevices = android::Intersection( |
| 112 | deviceTypes, android::getAudioDeviceOutAllA2dpSet()); |
| 113 | if (a2dpDevices.size() > 1) { |
| 114 | ALOGW("getDeviceForVolume() invalid device combination: %s", |
| 115 | android::dumpDeviceTypes(deviceTypes).c_str()); |
| 116 | } |
| 117 | if (!a2dpDevices.empty()) { |
| 118 | deviceType = a2dpDevices[0]; |
| 119 | } |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 120 | } |
jiabin | 9a3361e | 2019-10-01 09:38:30 -0700 | [diff] [blame^] | 121 | } else { |
| 122 | deviceType = *(deviceTypes.begin()); |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 123 | } |
| 124 | |
| 125 | /*SPEAKER_SAFE is an alias of SPEAKER for purposes of volume control*/ |
jiabin | 9a3361e | 2019-10-01 09:38:30 -0700 | [diff] [blame^] | 126 | if (deviceType == AUDIO_DEVICE_OUT_SPEAKER_SAFE) { |
| 127 | deviceType = AUDIO_DEVICE_OUT_SPEAKER; |
| 128 | } |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 129 | |
jiabin | 9a3361e | 2019-10-01 09:38:30 -0700 | [diff] [blame^] | 130 | ALOGW_IF(deviceType == AUDIO_DEVICE_NONE, |
| 131 | "getDeviceForVolume() invalid device combination: %s, returning AUDIO_DEVICE_NONE", |
| 132 | android::dumpDeviceTypes(deviceTypes).c_str()); |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 133 | |
jiabin | 9a3361e | 2019-10-01 09:38:30 -0700 | [diff] [blame^] | 134 | return deviceType; |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 135 | } |
| 136 | |
| 137 | /** |
| 138 | * returns the category the device belongs to with regard to volume curve management |
| 139 | * |
| 140 | * @param[in] device to check upon the category to whom it belongs to. |
| 141 | * |
| 142 | * @return device category. |
| 143 | */ |
jiabin | 9a3361e | 2019-10-01 09:38:30 -0700 | [diff] [blame^] | 144 | static device_category getDeviceCategory(const android::DeviceTypeSet& deviceTypes) |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 145 | { |
jiabin | 9a3361e | 2019-10-01 09:38:30 -0700 | [diff] [blame^] | 146 | switch(getDeviceForVolume(deviceTypes)) { |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 147 | case AUDIO_DEVICE_OUT_EARPIECE: |
| 148 | return DEVICE_CATEGORY_EARPIECE; |
| 149 | case AUDIO_DEVICE_OUT_WIRED_HEADSET: |
| 150 | case AUDIO_DEVICE_OUT_WIRED_HEADPHONE: |
| 151 | case AUDIO_DEVICE_OUT_BLUETOOTH_SCO: |
| 152 | case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET: |
| 153 | case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP: |
| 154 | case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES: |
Eric Laurent | 904d632 | 2017-03-17 17:20:47 -0700 | [diff] [blame] | 155 | case AUDIO_DEVICE_OUT_USB_HEADSET: |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 156 | return DEVICE_CATEGORY_HEADSET; |
Jakub Pawlowski | 24b0a89 | 2018-04-02 19:17:11 -0700 | [diff] [blame] | 157 | case AUDIO_DEVICE_OUT_HEARING_AID: |
| 158 | return DEVICE_CATEGORY_HEARING_AID; |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 159 | case AUDIO_DEVICE_OUT_LINE: |
| 160 | case AUDIO_DEVICE_OUT_AUX_DIGITAL: |
Eric Laurent | 904d632 | 2017-03-17 17:20:47 -0700 | [diff] [blame] | 161 | case AUDIO_DEVICE_OUT_USB_DEVICE: |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 162 | return DEVICE_CATEGORY_EXT_MEDIA; |
| 163 | case AUDIO_DEVICE_OUT_SPEAKER: |
Jean-Michel Trivi | 227342a | 2018-09-14 11:42:37 -0700 | [diff] [blame] | 164 | case AUDIO_DEVICE_OUT_SPEAKER_SAFE: |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 165 | case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: |
| 166 | case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER: |
| 167 | case AUDIO_DEVICE_OUT_USB_ACCESSORY: |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 168 | case AUDIO_DEVICE_OUT_REMOTE_SUBMIX: |
| 169 | default: |
| 170 | return DEVICE_CATEGORY_SPEAKER; |
| 171 | } |
| 172 | } |
| 173 | |
Eric Laurent | ffbc80f | 2015-03-18 18:30:19 -0700 | [diff] [blame] | 174 | static inline float DbToAmpl(float decibels) |
| 175 | { |
| 176 | if (decibels <= VOLUME_MIN_DB) { |
| 177 | return 0.0f; |
| 178 | } |
| 179 | return exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 ) |
| 180 | } |
| 181 | |
| 182 | static inline float AmplToDb(float amplification) |
| 183 | { |
| 184 | if (amplification == 0) { |
| 185 | return VOLUME_MIN_DB; |
| 186 | } |
| 187 | return 20 * log10(amplification); |
| 188 | } |
| 189 | |
François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 190 | }; |