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