blob: 4205589270b0f565ea715d26ec496ae99935f628 [file] [log] [blame]
François Gaffiedfd74092015-03-19 12:10:59 +01001/*
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 Laurentffbc80f2015-03-18 18:30:19 -070021#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 Gaffiedfd74092015-03-19 12:10:59 +010025
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.
Eric Laurentffbc80f2015-03-18 18:30:19 -070039 * we use 100 steps to avoid rounding errors when computing the volume in volIndexToDb()
François Gaffiedfd74092015-03-19 12:10:59 +010040 *
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
Eric Laurentffbc80f2015-03-18 18:30:19 -0700141 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
François Gaffiedfd74092015-03-19 12:10:59 +0100157};