blob: fac335084d013e0438be22274b9a74e9535f80d3 [file] [log] [blame]
Yao Chenc4d442f2016-04-08 11:33:47 -07001/*
2 * Copyright (C) 2016 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
17package com.android.car;
18
Yao Chen2612bb42016-08-19 16:19:17 -070019import android.car.settings.CarSettings;
Yao Chenc4d442f2016-04-08 11:33:47 -070020import android.media.AudioAttributes;
21import android.media.AudioManager;
22import android.util.Log;
23import android.util.SparseArray;
24
25import com.android.car.vehiclenetwork.VehicleNetworkConsts.VehicleAudioContextFlag;
26
27import java.util.ArrayList;
28import java.util.Arrays;
29import java.util.List;
30
31public class VolumeUtils {
32 private static final String TAG = "VolumeUtils";
33
34 public static final int[] LOGICAL_STREAMS = {
35 AudioManager.STREAM_VOICE_CALL,
36 AudioManager.STREAM_SYSTEM,
37 AudioManager.STREAM_RING,
38 AudioManager.STREAM_MUSIC,
39 AudioManager.STREAM_ALARM,
40 AudioManager.STREAM_NOTIFICATION,
41 AudioManager.STREAM_DTMF,
42 };
43
44 public static final int[] CAR_AUDIO_CONTEXT = {
45 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG,
46 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG,
47 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG,
48 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_CALL_FLAG,
49 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_ALARM_FLAG,
50 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG,
51 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG,
52 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG,
53 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_CD_ROM_FLAG,
54 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_AUX_AUDIO_FLAG,
55 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG,
56 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_RADIO_FLAG
57 };
58
Yao Chen2612bb42016-08-19 16:19:17 -070059 public static final SparseArray<String> CAR_AUDIO_CONTEXT_SETTINGS = new SparseArray<>();
60 static {
61 CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG,
62 CarSettings.Global.KEY_VOLUME_MUSIC);
63 CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG,
64 CarSettings.Global.KEY_VOLUME_MUSIC);
65 CAR_AUDIO_CONTEXT_SETTINGS.put(
66 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NAVIGATION_FLAG,
67 CarSettings.Global.KEY_VOLUME_NAVIGATION);
68 CAR_AUDIO_CONTEXT_SETTINGS.put(
69 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_VOICE_COMMAND_FLAG,
70 CarSettings.Global.KEY_VOLUME_VOICE_COMMAND);
71 CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_CALL_FLAG,
72 CarSettings.Global.KEY_VOLUME_CALL);
73 CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_ALARM_FLAG,
74 CarSettings.Global.KEY_VOLUME_ALARM);
75 CAR_AUDIO_CONTEXT_SETTINGS.put(
76 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG,
77 CarSettings.Global.KEY_VOLUME_NOTIFICATION);
78 CAR_AUDIO_CONTEXT_SETTINGS.put(
79 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SAFETY_ALERT_FLAG,
80 CarSettings.Global.KEY_VOLUME_SAFETY_ALERT);
81 CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_CD_ROM_FLAG,
82 CarSettings.Global.KEY_VOLUME_CD_ROM);
83 CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_AUX_AUDIO_FLAG,
84 CarSettings.Global.KEY_VOLUME_AUX);
85 CAR_AUDIO_CONTEXT_SETTINGS.put(
86 VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG,
87 CarSettings.Global.KEY_VOLUME_SYSTEM_SOUND);
88 CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_RADIO_FLAG,
89 CarSettings.Global.KEY_VOLUME_RADIO);
90 }
91
Yao Chenc4d442f2016-04-08 11:33:47 -070092 public static String streamToName(int stream) {
93 switch (stream) {
94 case AudioManager.STREAM_ALARM: return "Alarm";
95 case AudioManager.STREAM_MUSIC: return "Music";
96 case AudioManager.STREAM_NOTIFICATION: return "Notification";
97 case AudioManager.STREAM_RING: return "Ring";
98 case AudioManager.STREAM_VOICE_CALL: return "Call";
99 case AudioManager.STREAM_SYSTEM: return "System";
100 case AudioManager.STREAM_DTMF: return "DTMF";
101 default: return "Unknown";
102 }
103 }
104
105 public static int androidStreamToCarContext(int logicalAndroidStream) {
106 switch (logicalAndroidStream) {
107 case AudioManager.STREAM_VOICE_CALL:
108 return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_CALL_FLAG;
109 case AudioManager.STREAM_SYSTEM:
110 return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG;
111 case AudioManager.STREAM_RING:
112 return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG;
113 case AudioManager.STREAM_MUSIC:
114 return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG;
115 case AudioManager.STREAM_ALARM:
116 return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_ALARM_FLAG;
117 case AudioManager.STREAM_NOTIFICATION:
118 return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG;
119 case AudioManager.STREAM_DTMF:
120 return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG;
121 default:
122 return VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_UNKNOWN_FLAG;
123 }
124 }
125
126 public static int carContextToAndroidStream(int carContext) {
127 switch (carContext) {
128 case VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_CALL_FLAG:
129 return AudioManager.STREAM_VOICE_CALL;
130 case VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_SYSTEM_SOUND_FLAG:
131 return AudioManager.STREAM_SYSTEM;
132 case VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_NOTIFICATION_FLAG:
133 return AudioManager.STREAM_NOTIFICATION;
134 case VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_MUSIC_FLAG:
135 return AudioManager.STREAM_MUSIC;
136 case VehicleAudioContextFlag.VEHICLE_AUDIO_CONTEXT_ALARM_FLAG:
137 return AudioManager.STREAM_ALARM;
138 default:
139 return AudioManager.STREAM_MUSIC;
140 }
141 }
142
143 public static int androidStreamToCarUsage(int logicalAndroidStream) {
144 return CarAudioAttributesUtil.getCarUsageFromAudioAttributes(
145 new AudioAttributes.Builder()
146 .setLegacyStreamType(logicalAndroidStream).build());
147 }
148
149 private final SparseArray<Float[]> mStreamAmplLookup = new SparseArray<>(7);
150
151 private static final float LN_10 = 2.302585093f;
152 // From cs/#android/frameworks/av/media/libmedia/AudioSystem.cpp
153 private static final float DB_PER_STEP = -.5f;
154
155 private final AudioManager mAudioManager;
156
157 public VolumeUtils(AudioManager audioManager) {
158 mAudioManager = audioManager;
159 for(int i : LOGICAL_STREAMS) {
160 initStreamLookup(i);
161 }
162 }
163
164 private void initStreamLookup(int streamType) {
165 int maxIndex = mAudioManager.getStreamMaxVolume(streamType);
166 Float[] amplList = new Float[maxIndex + 1];
167
168 for (int i = 0; i <= maxIndex; i++) {
169 amplList[i] = volIndexToAmpl(i, maxIndex);
170 }
171 Log.d(TAG, streamToName(streamType) + ": " + Arrays.toString(amplList));
172 mStreamAmplLookup.put(streamType, amplList);
173 }
174
175
176 public static int closestIndex(float desired, Float[] list) {
177 float min = Float.MAX_VALUE;
178 int closestIndex = 0;
179
180 for (int i = 0; i < list.length; i++) {
181 float diff = Math.abs(list[i] - desired);
182 if (diff < min) {
183 min = diff;
184 closestIndex = i;
185 }
186 }
187 return closestIndex;
188 }
189
190 public void adjustStreamVol(int stream, int desired, int actual, int maxIndex) {
191 float gain = getTrackGain(desired, actual, maxIndex);
192 int index = closestIndex(gain, mStreamAmplLookup.get(stream));
193 if (index == mAudioManager.getStreamVolume(stream)) {
194 return;
195 } else {
196 mAudioManager.setStreamVolume(stream, index, 0 /*don't show UI*/);
197 }
198 }
199
200 /**
201 * Returns the gain which, when applied to an a stream with volume
202 * actualVolIndex, will make the output volume equivalent to a stream with a gain of
203 * 1.0 playing on a stream with volume desiredVolIndex.
204 *
205 * Computing this is non-trivial because the gain is applied on a linear scale while the volume
206 * indices map to a log (dB) scale.
207 *
208 * The computation is copied from cs/#android/frameworks/av/media/libmedia/AudioSystem.cpp
209 */
210 float getTrackGain(int desiredVolIndex, int actualVolIndex, int maxIndex) {
211 if (desiredVolIndex == actualVolIndex) {
212 return 1.0f;
213 }
214 return volIndexToAmpl(desiredVolIndex, maxIndex)
215 / volIndexToAmpl(actualVolIndex, maxIndex);
216 }
217
218 /**
219 * Returns the amplitude corresponding to volIndex. Guaranteed to return a non-negative value.
220 */
221 private float volIndexToAmpl(int volIndex, int maxIndex) {
222 // Normalize volIndex to be in the range [0, 100].
223 int volume = (int) ((float) volIndex / maxIndex * 100.0f);
224 return logToLinear(volumeToDecibels(volume));
225 }
226
227 /**
228 * volume is in the range [0, 100].
229 */
230 private static float volumeToDecibels(int volume) {
231 return (100 - volume) * DB_PER_STEP;
232 }
233
234 /**
235 * Corresponds to the function linearToLog in AudioSystem.cpp.
236 */
237 private static float logToLinear(float decibels) {
238 return decibels < 0.0f ? (float) Math.exp(decibels * LN_10 / 20.0f) : 1.0f;
239 }
Yao Chen2612bb42016-08-19 16:19:17 -0700240}