• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
CarAudioDeviceInfo(AudioDeviceInfo audioDeviceInfo)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 
getAudioDeviceInfo()71     AudioDeviceInfo getAudioDeviceInfo() {
72         return mAudioDeviceInfo;
73     }
74 
getAudioDevicePort()75     AudioDevicePort getAudioDevicePort() {
76         return mAudioDeviceInfo.getPort();
77     }
78 
getBusNumber()79     int getBusNumber() {
80         return mBusNumber;
81     }
82 
getDefaultGain()83     int getDefaultGain() {
84         return mDefaultGain;
85     }
86 
getMaxGain()87     int getMaxGain() {
88         return mMaxGain;
89     }
90 
getMinGain()91     int getMinGain() {
92         return mMinGain;
93     }
94 
getSampleRate()95     int getSampleRate() {
96         return mSampleRate;
97     }
98 
getEncodingFormat()99     int getEncodingFormat() {
100         return mEncodingFormat;
101     }
102 
getChannelCount()103     int getChannelCount() {
104         return mChannelCount;
105     }
106 
107     // Input is in millibels
setCurrentGain(int gainInMillibels)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      */
parseDeviceAddress(String address)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 
getMaxSampleRate(AudioDeviceInfo info)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 */
getEncodingFormat(AudioDeviceInfo info)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      */
getMaxChannels(AudioDeviceInfo info)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      */
getAudioGain()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      */
checkAudioGainConfiguration(AudioGain audioGain)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
toString()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 
dump(PrintWriter writer)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