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>
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)
25 
26 class VolumeCurvePoint
27 {
28 public:
29     int mIndex;
30     float mDBAttenuation;
31 };
32 
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 
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.
50      * we use 100 steps to avoid rounding errors when computing the volume in volIndexToDb()
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     /**
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      */
getDeviceForVolume(audio_devices_t device)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;
85             } else if (device & AUDIO_DEVICE_OUT_SPEAKER_SAFE) {
86                 device = AUDIO_DEVICE_OUT_SPEAKER_SAFE;
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      */
getDeviceCategory(audio_devices_t device)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 
DbToAmpl(float decibels)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 
AmplToDb(float amplification)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 
159 };
160