• 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 android.media.AudioFormat.ENCODING_PCM_16BIT;
19 
20 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE;
21 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
22 
23 import android.car.builtin.media.AudioManagerHelper;
24 import android.car.builtin.media.AudioManagerHelper.AudioGainInfo;
25 import android.car.builtin.util.Slogf;
26 import android.media.AudioDeviceInfo;
27 import android.media.AudioManager;
28 
29 import com.android.car.CarLog;
30 import com.android.car.audio.hal.HalAudioDeviceInfo;
31 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
32 import com.android.car.internal.util.IndentingPrintWriter;
33 import com.android.internal.annotations.GuardedBy;
34 
35 /**
36  * A helper class wraps {@link AudioDeviceInfo}, and helps get/set the gain on a specific port
37  * in terms of millibels.
38  * Note to the reader. For whatever reason, it seems that AudioGain contains only configuration
39  * information (min/max/step, etc) while the AudioGainConfig class contains the
40  * actual currently active gain value(s).
41  */
42 /* package */ class CarAudioDeviceInfo {
43 
44     public static final int DEFAULT_SAMPLE_RATE = 48000;
45     private final AudioDeviceInfo mAudioDeviceInfo;
46     private final int mSampleRate;
47     private final int mEncodingFormat;
48     private final int mChannelCount;
49     private final AudioManager mAudioManager;
50 
51     private final Object mLock = new Object();
52     @GuardedBy("mLock")
53     private int mDefaultGain;
54     @GuardedBy("mLock")
55     private int mMaxGain;
56     @GuardedBy("mLock")
57     private int mMinGain;
58     @GuardedBy("mLock")
59     private int mStepValue;
60     @GuardedBy("mLock")
61     private boolean mCanBeRoutedWithDynamicPolicyMixRule = true;
62 
63 
64     /**
65      * We need to store the current gain because it is not accessible from the current
66      * audio engine implementation. It would be nice if AudioPort#activeConfig() would return it,
67      * but in the current implementation, that function actually works only for mixer ports.
68      */
69     private int mCurrentGain;
70 
CarAudioDeviceInfo(AudioManager audioManager, AudioDeviceInfo audioDeviceInfo)71     CarAudioDeviceInfo(AudioManager audioManager, AudioDeviceInfo audioDeviceInfo) {
72         mAudioManager = audioManager;
73         mAudioDeviceInfo = audioDeviceInfo;
74         mSampleRate = getMaxSampleRate(audioDeviceInfo);
75         mEncodingFormat = ENCODING_PCM_16BIT;
76         mChannelCount = getMaxChannels(audioDeviceInfo);
77         AudioGainInfo audioGainInfo = AudioManagerHelper.getAudioGainInfo(audioDeviceInfo);
78         mDefaultGain = audioGainInfo.getDefaultGain();
79         mMaxGain = audioGainInfo.getMaxGain();
80         mMinGain = audioGainInfo.getMinGain();
81         mStepValue = audioGainInfo.getStepValue();
82 
83         mCurrentGain = -1; // Not initialized till explicitly set
84     }
85 
getAudioDeviceInfo()86     AudioDeviceInfo getAudioDeviceInfo() {
87         return mAudioDeviceInfo;
88     }
89 
90     /**
91      * By default, considers all AudioDevice can be used to establish dynamic policy mixing rules.
92      * until validation state is performed.
93      * Once called, the device is marked definitively as "connot be routed with dynamic mixes".
94      */
resetCanBeRoutedWithDynamicPolicyMix()95     void resetCanBeRoutedWithDynamicPolicyMix() {
96         synchronized (mLock) {
97             mCanBeRoutedWithDynamicPolicyMixRule = false;
98         }
99     }
100 
canBeRoutedWithDynamicPolicyMix()101     boolean canBeRoutedWithDynamicPolicyMix() {
102         synchronized (mLock) {
103             return mCanBeRoutedWithDynamicPolicyMixRule;
104         }
105     }
106 
getAddress()107     String getAddress() {
108         return mAudioDeviceInfo.getAddress();
109     }
110 
getDefaultGain()111     int getDefaultGain() {
112         synchronized (mLock) {
113             return mDefaultGain;
114         }
115     }
116 
getMaxGain()117     int getMaxGain() {
118         synchronized (mLock) {
119             return mMaxGain;
120         }
121     }
122 
getMinGain()123     int getMinGain() {
124         synchronized (mLock) {
125             return mMinGain;
126         }
127     }
128 
getSampleRate()129     int getSampleRate() {
130         return mSampleRate;
131     }
132 
getEncodingFormat()133     int getEncodingFormat() {
134         return mEncodingFormat;
135     }
136 
getChannelCount()137     int getChannelCount() {
138         return mChannelCount;
139     }
140 
getStepValue()141     int getStepValue() {
142         synchronized (mLock) {
143             return mStepValue;
144         }
145     }
146 
147 
148     // Input is in millibels
setCurrentGain(int gainInMillibels)149     void setCurrentGain(int gainInMillibels) {
150         int gain = gainInMillibels;
151         // Clamp the incoming value to our valid range.  Out of range values ARE legal input
152         synchronized (mLock) {
153             if (gain < mMinGain) {
154                 gain = mMinGain;
155             } else if (gain > mMaxGain) {
156                 gain = mMaxGain;
157             }
158         }
159 
160         if (AudioManagerHelper.setAudioDeviceGain(mAudioManager,
161                 getAddress(), gain, true)) {
162             // Since we can't query for the gain on a device port later,
163             // we have to remember what we asked for
164             mCurrentGain = gain;
165         } else {
166             Slogf.e(CarLog.TAG_AUDIO, "Failed to setAudioPortGain " + gain
167                     + " for output device " + getAddress());
168         }
169     }
170 
171     // Updates audio device info for dynamic gain stage configurations
updateAudioDeviceInfo(HalAudioDeviceInfo halDeviceInfo)172     void updateAudioDeviceInfo(HalAudioDeviceInfo halDeviceInfo) {
173         synchronized (mLock) {
174             mMinGain = halDeviceInfo.getGainMinValue();
175             mMaxGain = halDeviceInfo.getGainMaxValue();
176             mStepValue = halDeviceInfo.getGainStepValue();
177             mDefaultGain = halDeviceInfo.getGainDefaultValue();
178         }
179     }
180 
getMaxSampleRate(AudioDeviceInfo info)181     private static int getMaxSampleRate(AudioDeviceInfo info) {
182         int[] sampleRates = info.getSampleRates();
183         if (sampleRates == null || sampleRates.length == 0) {
184             return DEFAULT_SAMPLE_RATE;
185         }
186         int sampleRate = sampleRates[0];
187         for (int i = 1; i < sampleRates.length; i++) {
188             if (sampleRates[i] > sampleRate) {
189                 sampleRate = sampleRates[i];
190             }
191         }
192         return sampleRate;
193     }
194 
getMaxChannels(AudioDeviceInfo info)195     private static int getMaxChannels(AudioDeviceInfo info) {
196         int numChannels = 1;
197         int[] channelMasks = info.getChannelMasks();
198         if (channelMasks == null) {
199             return numChannels;
200         }
201         for (int channelMask : channelMasks) {
202             int currentNumChannels = Integer.bitCount(channelMask);
203             if (currentNumChannels > numChannels) {
204                 numChannels = currentNumChannels;
205             }
206         }
207         return numChannels;
208     }
209 
210     @Override
211     @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE)
toString()212     public String toString() {
213         return "address: " + mAudioDeviceInfo.getAddress()
214                 + " sampleRate: " + getSampleRate()
215                 + " encodingFormat: " + getEncodingFormat()
216                 + " channelCount: " + getChannelCount()
217                 + " currentGain: " + mCurrentGain
218                 + " maxGain: " + getMaxGain()
219                 + " minGain: " + getMinGain();
220     }
221 
222     @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
dump(IndentingPrintWriter writer)223     void dump(IndentingPrintWriter writer) {
224         synchronized (mLock) {
225             writer.printf("CarAudioDeviceInfo Device(%s)\n", mAudioDeviceInfo.getAddress());
226             writer.increaseIndent();
227             writer.printf("Routing with Dynamic Mix enabled (%b)\n",
228                     mCanBeRoutedWithDynamicPolicyMixRule);
229             writer.printf("sample rate / encoding format / channel count: %d %d %d\n",
230                     getSampleRate(), getEncodingFormat(), getChannelCount());
231             writer.printf("Gain values (min / max / default/ current): %d %d %d %d\n",
232                     mMinGain, mMaxGain, mDefaultGain, mCurrentGain);
233             writer.decreaseIndent();
234         }
235     }
236 }
237