blob: 3164aea9dcc947e55a237cf6aeac905c8d44b13f [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;
Pavel Maltsevcfe93102017-02-02 12:38:08 -080020import android.hardware.automotive.vehicle.V2_0.VehicleAudioContextFlag;
Yao Chenc4d442f2016-04-08 11:33:47 -070021import android.media.AudioAttributes;
22import android.media.AudioManager;
23import android.util.Log;
24import android.util.SparseArray;
25
Yao Chenc4d442f2016-04-08 11:33:47 -070026import java.util.Arrays;
Yao Chenc4d442f2016-04-08 11:33:47 -070027
28public class VolumeUtils {
29 private static final String TAG = "VolumeUtils";
30
31 public static final int[] LOGICAL_STREAMS = {
32 AudioManager.STREAM_VOICE_CALL,
33 AudioManager.STREAM_SYSTEM,
34 AudioManager.STREAM_RING,
35 AudioManager.STREAM_MUSIC,
36 AudioManager.STREAM_ALARM,
37 AudioManager.STREAM_NOTIFICATION,
38 AudioManager.STREAM_DTMF,
39 };
40
41 public static final int[] CAR_AUDIO_CONTEXT = {
Pavel Maltsev0d07c762016-11-03 16:40:15 -070042 VehicleAudioContextFlag.MUSIC_FLAG,
43 VehicleAudioContextFlag.NAVIGATION_FLAG,
44 VehicleAudioContextFlag.VOICE_COMMAND_FLAG,
45 VehicleAudioContextFlag.CALL_FLAG,
46 VehicleAudioContextFlag.ALARM_FLAG,
47 VehicleAudioContextFlag.NOTIFICATION_FLAG,
48 VehicleAudioContextFlag.UNKNOWN_FLAG,
49 VehicleAudioContextFlag.SAFETY_ALERT_FLAG,
50 VehicleAudioContextFlag.CD_ROM_FLAG,
51 VehicleAudioContextFlag.AUX_AUDIO_FLAG,
52 VehicleAudioContextFlag.SYSTEM_SOUND_FLAG,
53 VehicleAudioContextFlag.RADIO_FLAG
Yao Chenc4d442f2016-04-08 11:33:47 -070054 };
55
Yao Chen2612bb42016-08-19 16:19:17 -070056 public static final SparseArray<String> CAR_AUDIO_CONTEXT_SETTINGS = new SparseArray<>();
57 static {
Pavel Maltsev0d07c762016-11-03 16:40:15 -070058 CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.UNKNOWN_FLAG,
Yao Chen2612bb42016-08-19 16:19:17 -070059 CarSettings.Global.KEY_VOLUME_MUSIC);
Pavel Maltsev0d07c762016-11-03 16:40:15 -070060 CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.MUSIC_FLAG,
Yao Chen2612bb42016-08-19 16:19:17 -070061 CarSettings.Global.KEY_VOLUME_MUSIC);
62 CAR_AUDIO_CONTEXT_SETTINGS.put(
Pavel Maltsev0d07c762016-11-03 16:40:15 -070063 VehicleAudioContextFlag.NAVIGATION_FLAG,
Yao Chen2612bb42016-08-19 16:19:17 -070064 CarSettings.Global.KEY_VOLUME_NAVIGATION);
65 CAR_AUDIO_CONTEXT_SETTINGS.put(
Pavel Maltsev0d07c762016-11-03 16:40:15 -070066 VehicleAudioContextFlag.VOICE_COMMAND_FLAG,
Yao Chen2612bb42016-08-19 16:19:17 -070067 CarSettings.Global.KEY_VOLUME_VOICE_COMMAND);
Pavel Maltsev0d07c762016-11-03 16:40:15 -070068 CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.CALL_FLAG,
Yao Chen2612bb42016-08-19 16:19:17 -070069 CarSettings.Global.KEY_VOLUME_CALL);
Pavel Maltsev0d07c762016-11-03 16:40:15 -070070 CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.ALARM_FLAG,
Yao Chen2612bb42016-08-19 16:19:17 -070071 CarSettings.Global.KEY_VOLUME_ALARM);
72 CAR_AUDIO_CONTEXT_SETTINGS.put(
Pavel Maltsev0d07c762016-11-03 16:40:15 -070073 VehicleAudioContextFlag.NOTIFICATION_FLAG,
Yao Chen2612bb42016-08-19 16:19:17 -070074 CarSettings.Global.KEY_VOLUME_NOTIFICATION);
75 CAR_AUDIO_CONTEXT_SETTINGS.put(
Pavel Maltsev0d07c762016-11-03 16:40:15 -070076 VehicleAudioContextFlag.SAFETY_ALERT_FLAG,
Yao Chen2612bb42016-08-19 16:19:17 -070077 CarSettings.Global.KEY_VOLUME_SAFETY_ALERT);
Pavel Maltsev0d07c762016-11-03 16:40:15 -070078 CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.CD_ROM_FLAG,
Yao Chen2612bb42016-08-19 16:19:17 -070079 CarSettings.Global.KEY_VOLUME_CD_ROM);
Pavel Maltsev0d07c762016-11-03 16:40:15 -070080 CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.AUX_AUDIO_FLAG,
Yao Chen2612bb42016-08-19 16:19:17 -070081 CarSettings.Global.KEY_VOLUME_AUX);
82 CAR_AUDIO_CONTEXT_SETTINGS.put(
Pavel Maltsev0d07c762016-11-03 16:40:15 -070083 VehicleAudioContextFlag.SYSTEM_SOUND_FLAG,
Yao Chen2612bb42016-08-19 16:19:17 -070084 CarSettings.Global.KEY_VOLUME_SYSTEM_SOUND);
Pavel Maltsev0d07c762016-11-03 16:40:15 -070085 CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.RADIO_FLAG,
Yao Chen2612bb42016-08-19 16:19:17 -070086 CarSettings.Global.KEY_VOLUME_RADIO);
87 }
88
Yao Chenc4d442f2016-04-08 11:33:47 -070089 public static String streamToName(int stream) {
90 switch (stream) {
91 case AudioManager.STREAM_ALARM: return "Alarm";
92 case AudioManager.STREAM_MUSIC: return "Music";
93 case AudioManager.STREAM_NOTIFICATION: return "Notification";
94 case AudioManager.STREAM_RING: return "Ring";
95 case AudioManager.STREAM_VOICE_CALL: return "Call";
96 case AudioManager.STREAM_SYSTEM: return "System";
97 case AudioManager.STREAM_DTMF: return "DTMF";
98 default: return "Unknown";
99 }
100 }
101
102 public static int androidStreamToCarContext(int logicalAndroidStream) {
103 switch (logicalAndroidStream) {
104 case AudioManager.STREAM_VOICE_CALL:
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700105 return VehicleAudioContextFlag.CALL_FLAG;
Yao Chenc4d442f2016-04-08 11:33:47 -0700106 case AudioManager.STREAM_SYSTEM:
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700107 return VehicleAudioContextFlag.SYSTEM_SOUND_FLAG;
Yao Chenc4d442f2016-04-08 11:33:47 -0700108 case AudioManager.STREAM_RING:
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700109 return VehicleAudioContextFlag.NOTIFICATION_FLAG;
Yao Chenc4d442f2016-04-08 11:33:47 -0700110 case AudioManager.STREAM_MUSIC:
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700111 return VehicleAudioContextFlag.MUSIC_FLAG;
Yao Chenc4d442f2016-04-08 11:33:47 -0700112 case AudioManager.STREAM_ALARM:
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700113 return VehicleAudioContextFlag.ALARM_FLAG;
Yao Chenc4d442f2016-04-08 11:33:47 -0700114 case AudioManager.STREAM_NOTIFICATION:
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700115 return VehicleAudioContextFlag.NOTIFICATION_FLAG;
Yao Chenc4d442f2016-04-08 11:33:47 -0700116 case AudioManager.STREAM_DTMF:
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700117 return VehicleAudioContextFlag.SYSTEM_SOUND_FLAG;
Yao Chenc4d442f2016-04-08 11:33:47 -0700118 default:
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700119 return VehicleAudioContextFlag.UNKNOWN_FLAG;
Yao Chenc4d442f2016-04-08 11:33:47 -0700120 }
121 }
122
123 public static int carContextToAndroidStream(int carContext) {
124 switch (carContext) {
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700125 case VehicleAudioContextFlag.CALL_FLAG:
Yao Chenc4d442f2016-04-08 11:33:47 -0700126 return AudioManager.STREAM_VOICE_CALL;
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700127 case VehicleAudioContextFlag.SYSTEM_SOUND_FLAG:
Yao Chenc4d442f2016-04-08 11:33:47 -0700128 return AudioManager.STREAM_SYSTEM;
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700129 case VehicleAudioContextFlag.NOTIFICATION_FLAG:
Yao Chenc4d442f2016-04-08 11:33:47 -0700130 return AudioManager.STREAM_NOTIFICATION;
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700131 case VehicleAudioContextFlag.MUSIC_FLAG:
Yao Chenc4d442f2016-04-08 11:33:47 -0700132 return AudioManager.STREAM_MUSIC;
Pavel Maltsev0d07c762016-11-03 16:40:15 -0700133 case VehicleAudioContextFlag.ALARM_FLAG:
Yao Chenc4d442f2016-04-08 11:33:47 -0700134 return AudioManager.STREAM_ALARM;
135 default:
136 return AudioManager.STREAM_MUSIC;
137 }
138 }
139
140 public static int androidStreamToCarUsage(int logicalAndroidStream) {
141 return CarAudioAttributesUtil.getCarUsageFromAudioAttributes(
142 new AudioAttributes.Builder()
143 .setLegacyStreamType(logicalAndroidStream).build());
144 }
145
146 private final SparseArray<Float[]> mStreamAmplLookup = new SparseArray<>(7);
147
148 private static final float LN_10 = 2.302585093f;
149 // From cs/#android/frameworks/av/media/libmedia/AudioSystem.cpp
150 private static final float DB_PER_STEP = -.5f;
151
152 private final AudioManager mAudioManager;
153
154 public VolumeUtils(AudioManager audioManager) {
155 mAudioManager = audioManager;
156 for(int i : LOGICAL_STREAMS) {
157 initStreamLookup(i);
158 }
159 }
160
161 private void initStreamLookup(int streamType) {
162 int maxIndex = mAudioManager.getStreamMaxVolume(streamType);
163 Float[] amplList = new Float[maxIndex + 1];
164
165 for (int i = 0; i <= maxIndex; i++) {
166 amplList[i] = volIndexToAmpl(i, maxIndex);
167 }
168 Log.d(TAG, streamToName(streamType) + ": " + Arrays.toString(amplList));
169 mStreamAmplLookup.put(streamType, amplList);
170 }
171
172
173 public static int closestIndex(float desired, Float[] list) {
174 float min = Float.MAX_VALUE;
175 int closestIndex = 0;
176
177 for (int i = 0; i < list.length; i++) {
178 float diff = Math.abs(list[i] - desired);
179 if (diff < min) {
180 min = diff;
181 closestIndex = i;
182 }
183 }
184 return closestIndex;
185 }
186
187 public void adjustStreamVol(int stream, int desired, int actual, int maxIndex) {
188 float gain = getTrackGain(desired, actual, maxIndex);
189 int index = closestIndex(gain, mStreamAmplLookup.get(stream));
190 if (index == mAudioManager.getStreamVolume(stream)) {
191 return;
192 } else {
193 mAudioManager.setStreamVolume(stream, index, 0 /*don't show UI*/);
194 }
195 }
196
197 /**
198 * Returns the gain which, when applied to an a stream with volume
199 * actualVolIndex, will make the output volume equivalent to a stream with a gain of
200 * 1.0 playing on a stream with volume desiredVolIndex.
201 *
202 * Computing this is non-trivial because the gain is applied on a linear scale while the volume
203 * indices map to a log (dB) scale.
204 *
205 * The computation is copied from cs/#android/frameworks/av/media/libmedia/AudioSystem.cpp
206 */
207 float getTrackGain(int desiredVolIndex, int actualVolIndex, int maxIndex) {
208 if (desiredVolIndex == actualVolIndex) {
209 return 1.0f;
210 }
211 return volIndexToAmpl(desiredVolIndex, maxIndex)
212 / volIndexToAmpl(actualVolIndex, maxIndex);
213 }
214
215 /**
216 * Returns the amplitude corresponding to volIndex. Guaranteed to return a non-negative value.
217 */
218 private float volIndexToAmpl(int volIndex, int maxIndex) {
219 // Normalize volIndex to be in the range [0, 100].
220 int volume = (int) ((float) volIndex / maxIndex * 100.0f);
221 return logToLinear(volumeToDecibels(volume));
222 }
223
224 /**
225 * volume is in the range [0, 100].
226 */
227 private static float volumeToDecibels(int volume) {
228 return (100 - volume) * DB_PER_STEP;
229 }
230
231 /**
232 * Corresponds to the function linearToLog in AudioSystem.cpp.
233 */
234 private static float logToLinear(float decibels) {
235 return decibels < 0.0f ? (float) Math.exp(decibels * LN_10 / 20.0f) : 1.0f;
236 }
Yao Chen2612bb42016-08-19 16:19:17 -0700237}