Home | History | Annotate | Download | only in car
      1 /*
      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 
     17 package com.android.car;
     18 
     19 import android.car.settings.CarSettings;
     20 import android.hardware.automotive.vehicle.V2_0.VehicleAudioContextFlag;
     21 import android.media.AudioAttributes;
     22 import android.media.AudioManager;
     23 import android.util.Log;
     24 import android.util.SparseArray;
     25 
     26 import java.util.Arrays;
     27 
     28 public 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 = {
     42             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
     54     };
     55 
     56     public static final SparseArray<String> CAR_AUDIO_CONTEXT_SETTINGS = new SparseArray<>();
     57     static {
     58         CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.UNKNOWN_FLAG,
     59                 CarSettings.Global.KEY_VOLUME_MUSIC);
     60         CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.MUSIC_FLAG,
     61                 CarSettings.Global.KEY_VOLUME_MUSIC);
     62         CAR_AUDIO_CONTEXT_SETTINGS.put(
     63                 VehicleAudioContextFlag.NAVIGATION_FLAG,
     64                 CarSettings.Global.KEY_VOLUME_NAVIGATION);
     65         CAR_AUDIO_CONTEXT_SETTINGS.put(
     66                 VehicleAudioContextFlag.VOICE_COMMAND_FLAG,
     67                 CarSettings.Global.KEY_VOLUME_VOICE_COMMAND);
     68         CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.CALL_FLAG,
     69                 CarSettings.Global.KEY_VOLUME_CALL);
     70         CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.ALARM_FLAG,
     71                 CarSettings.Global.KEY_VOLUME_ALARM);
     72         CAR_AUDIO_CONTEXT_SETTINGS.put(
     73                 VehicleAudioContextFlag.NOTIFICATION_FLAG,
     74                 CarSettings.Global.KEY_VOLUME_NOTIFICATION);
     75         CAR_AUDIO_CONTEXT_SETTINGS.put(
     76                 VehicleAudioContextFlag.SAFETY_ALERT_FLAG,
     77                 CarSettings.Global.KEY_VOLUME_SAFETY_ALERT);
     78         CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.CD_ROM_FLAG,
     79                 CarSettings.Global.KEY_VOLUME_CD_ROM);
     80         CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.AUX_AUDIO_FLAG,
     81                 CarSettings.Global.KEY_VOLUME_AUX);
     82         CAR_AUDIO_CONTEXT_SETTINGS.put(
     83                 VehicleAudioContextFlag.SYSTEM_SOUND_FLAG,
     84                 CarSettings.Global.KEY_VOLUME_SYSTEM_SOUND);
     85         CAR_AUDIO_CONTEXT_SETTINGS.put(VehicleAudioContextFlag.RADIO_FLAG,
     86                 CarSettings.Global.KEY_VOLUME_RADIO);
     87     }
     88 
     89     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:
    105                 return VehicleAudioContextFlag.CALL_FLAG;
    106             case AudioManager.STREAM_SYSTEM:
    107                 return VehicleAudioContextFlag.SYSTEM_SOUND_FLAG;
    108             case AudioManager.STREAM_RING:
    109                 return VehicleAudioContextFlag.NOTIFICATION_FLAG;
    110             case AudioManager.STREAM_MUSIC:
    111                 return VehicleAudioContextFlag.MUSIC_FLAG;
    112             case AudioManager.STREAM_ALARM:
    113                 return VehicleAudioContextFlag.ALARM_FLAG;
    114             case AudioManager.STREAM_NOTIFICATION:
    115                 return VehicleAudioContextFlag.NOTIFICATION_FLAG;
    116             case AudioManager.STREAM_DTMF:
    117                 return VehicleAudioContextFlag.SYSTEM_SOUND_FLAG;
    118             default:
    119                 return VehicleAudioContextFlag.UNKNOWN_FLAG;
    120         }
    121     }
    122 
    123     public static int carContextToAndroidStream(int carContext) {
    124         switch (carContext) {
    125             case VehicleAudioContextFlag.CALL_FLAG:
    126                 return AudioManager.STREAM_VOICE_CALL;
    127             case VehicleAudioContextFlag.SYSTEM_SOUND_FLAG:
    128                 return AudioManager.STREAM_SYSTEM;
    129             case VehicleAudioContextFlag.NOTIFICATION_FLAG:
    130                 return AudioManager.STREAM_NOTIFICATION;
    131             case VehicleAudioContextFlag.MUSIC_FLAG:
    132                 return AudioManager.STREAM_MUSIC;
    133             case VehicleAudioContextFlag.ALARM_FLAG:
    134                 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     }
    237 }
    238