Volume.h revision ffbc80f5908eaf67a033c6e93a343c39dd6894eb
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
26class VolumeCurvePoint
27{
28public:
29    int mIndex;
30    float mDBAttenuation;
31};
32
33class Volume
34{
35public:
36    /**
37     * 4 points to define the volume attenuation curve, each characterized by the volume
38     * index (from 0 to 100) at which they apply, and the attenuation in dB at that index.
39     * we use 100 steps to avoid rounding errors when computing the volume in volIndexToDb()
40     *
41     * @todo shall become configurable
42     */
43    enum {
44        VOLMIN = 0,
45        VOLKNEE1 = 1,
46        VOLKNEE2 = 2,
47        VOLMAX = 3,
48
49        VOLCNT = 4
50    };
51
52    /**
53     * device categories used for volume curve management.
54     */
55    enum device_category {
56        DEVICE_CATEGORY_HEADSET,
57        DEVICE_CATEGORY_SPEAKER,
58        DEVICE_CATEGORY_EARPIECE,
59        DEVICE_CATEGORY_EXT_MEDIA,
60        DEVICE_CATEGORY_CNT
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     */
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_HDMI_ARC) {
86                device = AUDIO_DEVICE_OUT_HDMI_ARC;
87            } else if (device & AUDIO_DEVICE_OUT_AUX_LINE) {
88                device = AUDIO_DEVICE_OUT_AUX_LINE;
89            } else if (device & AUDIO_DEVICE_OUT_SPDIF) {
90                device = AUDIO_DEVICE_OUT_SPDIF;
91            } else {
92                device = (audio_devices_t)(device & AUDIO_DEVICE_OUT_ALL_A2DP);
93            }
94        }
95
96        /*SPEAKER_SAFE is an alias of SPEAKER for purposes of volume control*/
97        if (device == AUDIO_DEVICE_OUT_SPEAKER_SAFE)
98            device = AUDIO_DEVICE_OUT_SPEAKER;
99
100        ALOGW_IF(popcount(device) != 1,
101                 "getDeviceForVolume() invalid device combination: %08x",
102                 device);
103
104        return device;
105    }
106
107    /**
108     * returns the category the device belongs to with regard to volume curve management
109     *
110     * @param[in] device to check upon the category to whom it belongs to.
111     *
112     * @return device category.
113     */
114    static device_category getDeviceCategory(audio_devices_t device)
115    {
116        switch(getDeviceForVolume(device)) {
117        case AUDIO_DEVICE_OUT_EARPIECE:
118            return DEVICE_CATEGORY_EARPIECE;
119        case AUDIO_DEVICE_OUT_WIRED_HEADSET:
120        case AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
121        case AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
122        case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
123        case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP:
124        case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES:
125            return DEVICE_CATEGORY_HEADSET;
126        case AUDIO_DEVICE_OUT_LINE:
127        case AUDIO_DEVICE_OUT_AUX_DIGITAL:
128            /*USB?  Remote submix?*/
129            return DEVICE_CATEGORY_EXT_MEDIA;
130        case AUDIO_DEVICE_OUT_SPEAKER:
131        case AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
132        case AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER:
133        case AUDIO_DEVICE_OUT_USB_ACCESSORY:
134        case AUDIO_DEVICE_OUT_USB_DEVICE:
135        case AUDIO_DEVICE_OUT_REMOTE_SUBMIX:
136        default:
137            return DEVICE_CATEGORY_SPEAKER;
138        }
139    }
140
141    static inline float DbToAmpl(float decibels)
142    {
143        if (decibels <= VOLUME_MIN_DB) {
144            return 0.0f;
145        }
146        return exp( decibels * 0.115129f); // exp( dB * ln(10) / 20 )
147    }
148
149    static inline float AmplToDb(float amplification)
150    {
151        if (amplification == 0) {
152            return VOLUME_MIN_DB;
153        }
154        return 20 * log10(amplification);
155    }
156
157};
158