| 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 |  | 
 | 19 | #include <system/audio.h> | 
 | 20 | #include <utils/Log.h> | 
| Eric Laurent | ffbc80f | 2015-03-18 18:30:19 -0700 | [diff] [blame] | 21 | #include <math.h> | 
 | 22 |  | 
 | 23 | // Absolute min volume in dB (can be represented in single precision normal float value) | 
 | 24 | #define VOLUME_MIN_DB (-758) | 
| François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 25 |  | 
 | 26 | class VolumeCurvePoint | 
 | 27 | { | 
 | 28 | public: | 
 | 29 |     int mIndex; | 
 | 30 |     float mDBAttenuation; | 
 | 31 | }; | 
 | 32 |  | 
| François Gaffie | d0609ad | 2015-12-01 17:56:08 +0100 | [diff] [blame] | 33 | /** | 
 | 34 |  * device categories used for volume curve management. | 
 | 35 |  */ | 
 | 36 | enum device_category { | 
 | 37 |     DEVICE_CATEGORY_HEADSET, | 
 | 38 |     DEVICE_CATEGORY_SPEAKER, | 
 | 39 |     DEVICE_CATEGORY_EARPIECE, | 
 | 40 |     DEVICE_CATEGORY_EXT_MEDIA, | 
 | 41 |     DEVICE_CATEGORY_CNT | 
 | 42 | }; | 
 | 43 |  | 
| François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 44 | class Volume | 
 | 45 | { | 
 | 46 | public: | 
 | 47 |     /** | 
 | 48 |      * 4 points to define the volume attenuation curve, each characterized by the volume | 
 | 49 |      * 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] | 50 |      * 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] | 51 |      * | 
 | 52 |      * @todo shall become configurable | 
 | 53 |      */ | 
 | 54 |     enum { | 
 | 55 |         VOLMIN = 0, | 
 | 56 |         VOLKNEE1 = 1, | 
 | 57 |         VOLKNEE2 = 2, | 
 | 58 |         VOLMAX = 3, | 
 | 59 |  | 
 | 60 |         VOLCNT = 4 | 
 | 61 |     }; | 
 | 62 |  | 
 | 63 |     /** | 
| François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 64 |      * extract one device relevant for volume control from multiple device selection | 
 | 65 |      * | 
 | 66 |      * @param[in] device for which the volume category is associated | 
 | 67 |      * | 
 | 68 |      * @return subset of device required to limit the number of volume category per device | 
 | 69 |      */ | 
 | 70 |     static audio_devices_t getDeviceForVolume(audio_devices_t device) | 
 | 71 |     { | 
 | 72 |         if (device == AUDIO_DEVICE_NONE) { | 
 | 73 |             // this happens when forcing a route update and no track is active on an output. | 
 | 74 |             // In this case the returned category is not important. | 
 | 75 |             device =  AUDIO_DEVICE_OUT_SPEAKER; | 
 | 76 |         } else if (popcount(device) > 1) { | 
 | 77 |             // Multiple device selection is either: | 
 | 78 |             //  - speaker + one other device: give priority to speaker in this case. | 
 | 79 |             //  - one A2DP device + another device: happens with duplicated output. In this case | 
 | 80 |             // retain the device on the A2DP output as the other must not correspond to an active | 
 | 81 |             // selection if not the speaker. | 
 | 82 |             //  - HDMI-CEC system audio mode only output: give priority to available item in order. | 
 | 83 |             if (device & AUDIO_DEVICE_OUT_SPEAKER) { | 
 | 84 |                 device = AUDIO_DEVICE_OUT_SPEAKER; | 
| Eric Laurent | 9a7d922 | 2015-07-02 15:30:23 -0700 | [diff] [blame] | 85 |             } else if (device & AUDIO_DEVICE_OUT_SPEAKER_SAFE) { | 
 | 86 |                 device = AUDIO_DEVICE_OUT_SPEAKER_SAFE; | 
| François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 87 |             } else if (device & AUDIO_DEVICE_OUT_HDMI_ARC) { | 
 | 88 |                 device = AUDIO_DEVICE_OUT_HDMI_ARC; | 
 | 89 |             } else if (device & AUDIO_DEVICE_OUT_AUX_LINE) { | 
 | 90 |                 device = AUDIO_DEVICE_OUT_AUX_LINE; | 
 | 91 |             } else if (device & AUDIO_DEVICE_OUT_SPDIF) { | 
 | 92 |                 device = AUDIO_DEVICE_OUT_SPDIF; | 
 | 93 |             } else { | 
 | 94 |                 device = (audio_devices_t)(device & AUDIO_DEVICE_OUT_ALL_A2DP); | 
 | 95 |             } | 
 | 96 |         } | 
 | 97 |  | 
 | 98 |         /*SPEAKER_SAFE is an alias of SPEAKER for purposes of volume control*/ | 
 | 99 |         if (device == AUDIO_DEVICE_OUT_SPEAKER_SAFE) | 
 | 100 |             device = AUDIO_DEVICE_OUT_SPEAKER; | 
 | 101 |  | 
 | 102 |         ALOGW_IF(popcount(device) != 1, | 
 | 103 |                  "getDeviceForVolume() invalid device combination: %08x", | 
 | 104 |                  device); | 
 | 105 |  | 
 | 106 |         return device; | 
 | 107 |     } | 
 | 108 |  | 
 | 109 |     /** | 
 | 110 |      * returns the category the device belongs to with regard to volume curve management | 
 | 111 |      * | 
 | 112 |      * @param[in] device to check upon the category to whom it belongs to. | 
 | 113 |      * | 
 | 114 |      * @return device category. | 
 | 115 |      */ | 
 | 116 |     static device_category getDeviceCategory(audio_devices_t device) | 
 | 117 |     { | 
 | 118 |         switch(getDeviceForVolume(device)) { | 
 | 119 |         case AUDIO_DEVICE_OUT_EARPIECE: | 
 | 120 |             return DEVICE_CATEGORY_EARPIECE; | 
 | 121 |         case AUDIO_DEVICE_OUT_WIRED_HEADSET: | 
 | 122 |         case AUDIO_DEVICE_OUT_WIRED_HEADPHONE: | 
 | 123 |         case AUDIO_DEVICE_OUT_BLUETOOTH_SCO: | 
 | 124 |         case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET: | 
 | 125 |         case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP: | 
 | 126 |         case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES: | 
 | 127 |             return DEVICE_CATEGORY_HEADSET; | 
 | 128 |         case AUDIO_DEVICE_OUT_LINE: | 
 | 129 |         case AUDIO_DEVICE_OUT_AUX_DIGITAL: | 
 | 130 |             /*USB?  Remote submix?*/ | 
 | 131 |             return DEVICE_CATEGORY_EXT_MEDIA; | 
 | 132 |         case AUDIO_DEVICE_OUT_SPEAKER: | 
 | 133 |         case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT: | 
 | 134 |         case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER: | 
 | 135 |         case AUDIO_DEVICE_OUT_USB_ACCESSORY: | 
 | 136 |         case AUDIO_DEVICE_OUT_USB_DEVICE: | 
 | 137 |         case AUDIO_DEVICE_OUT_REMOTE_SUBMIX: | 
 | 138 |         default: | 
 | 139 |             return DEVICE_CATEGORY_SPEAKER; | 
 | 140 |         } | 
 | 141 |     } | 
 | 142 |  | 
| Eric Laurent | ffbc80f | 2015-03-18 18:30:19 -0700 | [diff] [blame] | 143 |     static inline float DbToAmpl(float decibels) | 
 | 144 |     { | 
 | 145 |         if (decibels <= VOLUME_MIN_DB) { | 
 | 146 |             return 0.0f; | 
 | 147 |         } | 
 | 148 |         return exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 ) | 
 | 149 |     } | 
 | 150 |  | 
 | 151 |     static inline float AmplToDb(float amplification) | 
 | 152 |     { | 
 | 153 |         if (amplification == 0) { | 
 | 154 |             return VOLUME_MIN_DB; | 
 | 155 |         } | 
 | 156 |         return 20 * log10(amplification); | 
 | 157 |     } | 
 | 158 |  | 
| François Gaffie | dfd7409 | 2015-03-19 12:10:59 +0100 | [diff] [blame] | 159 | }; |