Home | History | Annotate | Download | only in car
      1 /*
      2  * Copyright (C) 2018 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 package com.android.car;
     17 
     18 import android.media.AudioDeviceInfo;
     19 import android.media.AudioDevicePort;
     20 import android.media.AudioFormat;
     21 import android.media.AudioGain;
     22 import android.media.AudioGainConfig;
     23 import android.media.AudioManager;
     24 import android.media.AudioPort;
     25 import android.util.Log;
     26 
     27 import com.android.internal.util.Preconditions;
     28 
     29 import java.io.PrintWriter;
     30 
     31 /**
     32  * A helper class wraps {@link AudioDeviceInfo}, and helps get/set the gain on a specific port
     33  * in terms of millibels.
     34  * Note to the reader. For whatever reason, it seems that AudioGain contains only configuration
     35  * information (min/max/step, etc) while the AudioGainConfig class contains the
     36  * actual currently active gain value(s).
     37  */
     38 /* package */ class CarAudioDeviceInfo {
     39 
     40     private final AudioDeviceInfo mAudioDeviceInfo;
     41     private final int mBusNumber;
     42     private final int mSampleRate;
     43     private final int mEncodingFormat;
     44     private final int mChannelCount;
     45     private final int mDefaultGain;
     46     private final int mMaxGain;
     47     private final int mMinGain;
     48 
     49     /**
     50      * We need to store the current gain because it is not accessible from the current
     51      * audio engine implementation. It would be nice if AudioPort#activeConfig() would return it,
     52      * but in the current implementation, that function actually works only for mixer ports.
     53      */
     54     private int mCurrentGain;
     55 
     56     CarAudioDeviceInfo(AudioDeviceInfo audioDeviceInfo) {
     57         mAudioDeviceInfo = audioDeviceInfo;
     58         mBusNumber = parseDeviceAddress(audioDeviceInfo.getAddress());
     59         mSampleRate = getMaxSampleRate(audioDeviceInfo);
     60         mEncodingFormat = getEncodingFormat(audioDeviceInfo);
     61         mChannelCount = getMaxChannels(audioDeviceInfo);
     62         final AudioGain audioGain = Preconditions.checkNotNull(
     63                 getAudioGain(), "No audio gain on device port " + audioDeviceInfo);
     64         mDefaultGain = audioGain.defaultValue();
     65         mMaxGain = audioGain.maxValue();
     66         mMinGain = audioGain.minValue();
     67 
     68         mCurrentGain = -1; // Not initialized till explicitly set
     69     }
     70 
     71     AudioDeviceInfo getAudioDeviceInfo() {
     72         return mAudioDeviceInfo;
     73     }
     74 
     75     AudioDevicePort getAudioDevicePort() {
     76         return mAudioDeviceInfo.getPort();
     77     }
     78 
     79     int getBusNumber() {
     80         return mBusNumber;
     81     }
     82 
     83     int getDefaultGain() {
     84         return mDefaultGain;
     85     }
     86 
     87     int getMaxGain() {
     88         return mMaxGain;
     89     }
     90 
     91     int getMinGain() {
     92         return mMinGain;
     93     }
     94 
     95     int getSampleRate() {
     96         return mSampleRate;
     97     }
     98 
     99     int getEncodingFormat() {
    100         return mEncodingFormat;
    101     }
    102 
    103     int getChannelCount() {
    104         return mChannelCount;
    105     }
    106 
    107     // Input is in millibels
    108     void setCurrentGain(int gainInMillibels) {
    109         // Clamp the incoming value to our valid range.  Out of range values ARE legal input
    110         if (gainInMillibels < mMinGain) {
    111             gainInMillibels = mMinGain;
    112         } else if (gainInMillibels > mMaxGain) {
    113             gainInMillibels = mMaxGain;
    114         }
    115 
    116         // Push the new gain value down to our underlying port which will cause it to show up
    117         // at the HAL.
    118         AudioGain audioGain = getAudioGain();
    119         if (audioGain == null) {
    120             Log.e(CarLog.TAG_AUDIO, "getAudioGain() returned null.");
    121             return;
    122         }
    123 
    124         // size of gain values is 1 in MODE_JOINT
    125         AudioGainConfig audioGainConfig = audioGain.buildConfig(
    126                 AudioGain.MODE_JOINT,
    127                 audioGain.channelMask(),
    128                 new int[] { gainInMillibels },
    129                 0);
    130         if (audioGainConfig == null) {
    131             Log.e(CarLog.TAG_AUDIO, "Failed to construct AudioGainConfig");
    132             return;
    133         }
    134 
    135         int r = AudioManager.setAudioPortGain(getAudioDevicePort(), audioGainConfig);
    136         if (r == AudioManager.SUCCESS) {
    137             // Since we can't query for the gain on a device port later,
    138             // we have to remember what we asked for
    139             mCurrentGain = gainInMillibels;
    140         } else {
    141             Log.e(CarLog.TAG_AUDIO, "Failed to setAudioPortGain: " + r);
    142         }
    143     }
    144 
    145     /**
    146      * Parse device address. Expected format is BUS%d_%s, address, usage hint
    147      * @return valid address (from 0 to positive) or -1 for invalid address.
    148      */
    149     private int parseDeviceAddress(String address) {
    150         String[] words = address.split("_");
    151         int addressParsed = -1;
    152         if (words[0].toLowerCase().startsWith("bus")) {
    153             try {
    154                 addressParsed = Integer.parseInt(words[0].substring(3));
    155             } catch (NumberFormatException e) {
    156                 //ignore
    157             }
    158         }
    159         if (addressParsed < 0) {
    160             return -1;
    161         }
    162         return addressParsed;
    163     }
    164 
    165     private int getMaxSampleRate(AudioDeviceInfo info) {
    166         int[] sampleRates = info.getSampleRates();
    167         if (sampleRates == null || sampleRates.length == 0) {
    168             return 48000;
    169         }
    170         int sampleRate = sampleRates[0];
    171         for (int i = 1; i < sampleRates.length; i++) {
    172             if (sampleRates[i] > sampleRate) {
    173                 sampleRate = sampleRates[i];
    174             }
    175         }
    176         return sampleRate;
    177     }
    178 
    179     /** Always returns {@link AudioFormat#ENCODING_PCM_16BIT} as for now */
    180     private int getEncodingFormat(AudioDeviceInfo info) {
    181         return AudioFormat.ENCODING_PCM_16BIT;
    182     }
    183 
    184     /**
    185      * Gets the maximum channel count for a given {@link AudioDeviceInfo}
    186      *
    187      * @param info {@link AudioDeviceInfo} instance to get maximum channel count for
    188      * @return Maximum channel count for a given {@link AudioDeviceInfo},
    189      * 1 (mono) if there is no channel masks configured
    190      */
    191     private int getMaxChannels(AudioDeviceInfo info) {
    192         int numChannels = 1;
    193         int[] channelMasks = info.getChannelMasks();
    194         if (channelMasks == null) {
    195             return numChannels;
    196         }
    197         for (int channelMask : channelMasks) {
    198             int currentNumChannels = Integer.bitCount(channelMask);
    199             if (currentNumChannels > numChannels) {
    200                 numChannels = currentNumChannels;
    201             }
    202         }
    203         return numChannels;
    204     }
    205 
    206     /**
    207      * @return {@link AudioGain} with {@link AudioGain#MODE_JOINT} on a given {@link AudioPort}.
    208      * This is useful for inspecting the configuration data associated with this gain controller
    209      * (min/max/step/default).
    210      */
    211     AudioGain getAudioGain() {
    212         final AudioDevicePort audioPort = getAudioDevicePort();
    213         if (audioPort != null && audioPort.gains().length > 0) {
    214             for (AudioGain audioGain : audioPort.gains()) {
    215                 if ((audioGain.mode() & AudioGain.MODE_JOINT) != 0) {
    216                     return checkAudioGainConfiguration(audioGain);
    217                 }
    218             }
    219         }
    220         return null;
    221     }
    222 
    223     /**
    224      * Constraints applied to gain configuration, see also audio_policy_configuration.xml
    225      */
    226     private AudioGain checkAudioGainConfiguration(AudioGain audioGain) {
    227         Preconditions.checkArgument(audioGain.maxValue() >= audioGain.minValue());
    228         Preconditions.checkArgument((audioGain.defaultValue() >= audioGain.minValue())
    229                 && (audioGain.defaultValue() <= audioGain.maxValue()));
    230         Preconditions.checkArgument(
    231                 ((audioGain.maxValue() - audioGain.minValue()) % audioGain.stepValue()) == 0);
    232         Preconditions.checkArgument(
    233                 ((audioGain.defaultValue() - audioGain.minValue()) % audioGain.stepValue()) == 0);
    234         return audioGain;
    235     }
    236 
    237     @Override
    238     public String toString() {
    239         return "bus number: " + mBusNumber
    240                 + " address: " + mAudioDeviceInfo.getAddress()
    241                 + " sampleRate: " + getSampleRate()
    242                 + " encodingFormat: " + getEncodingFormat()
    243                 + " channelCount: " + getChannelCount()
    244                 + " currentGain: " + mCurrentGain
    245                 + " maxGain: " + mMaxGain
    246                 + " minGain: " + mMinGain;
    247     }
    248 
    249     void dump(PrintWriter writer) {
    250         writer.printf("Bus Number (%d) / address (%s)\n ",
    251                 mBusNumber, mAudioDeviceInfo.getAddress());
    252         writer.printf("\tsample rate / encoding format / channel count: %d %d %d\n",
    253                 getSampleRate(), getEncodingFormat(), getChannelCount());
    254         writer.printf("\tGain in millibel (min / max / default/ current): %d %d %d %d\n",
    255                 mMinGain, mMaxGain, mDefaultGain, mCurrentGain);
    256     }
    257 }
    258