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