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
33/**
34 * device categories used for volume curve management.
35 */
36enum 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
44class Volume
45{
46public:
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     */
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     */
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
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
159};
160