blob: d1082e8a773770a0af4f7edbd776f0a82305b1aa [file] [log] [blame]
François Gaffie112b0af2015-11-19 16:13:25 +01001/*
2 * Copyright (C) 2015 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
Mikhail Naganovfa69dc62018-07-27 09:58:58 -070017#include <algorithm>
18#include <set>
Mikhail Naganove50f6282018-07-26 16:20:43 -070019#include <string>
20
François Gaffie112b0af2015-11-19 16:13:25 +010021#define LOG_TAG "APM::AudioProfile"
22//#define LOG_NDEBUG 0
23
jiabin4562b3b2019-07-29 10:13:34 -070024#include <media/AudioContainers.h>
François Gaffie112b0af2015-11-19 16:13:25 +010025#include <media/AudioResamplerPublic.h>
Mikhail Naganove50f6282018-07-26 16:20:43 -070026#include <utils/Errors.h>
27
Mikhail Naganove50f6282018-07-26 16:20:43 -070028#include "AudioPort.h"
29#include "AudioProfile.h"
30#include "HwModule.h"
31#include "TypeConverter.h"
François Gaffie112b0af2015-11-19 16:13:25 +010032
33namespace android {
34
Mikhail Naganove50f6282018-07-26 16:20:43 -070035bool operator == (const AudioProfile &left, const AudioProfile &compareTo)
36{
37 return (left.getFormat() == compareTo.getFormat()) &&
38 (left.getChannels() == compareTo.getChannels()) &&
39 (left.getSampleRates() == compareTo.getSampleRates());
40}
41
Mikhail Naganov21b43362018-06-04 10:37:09 -070042static AudioProfile* createFullDynamicImpl()
43{
44 AudioProfile* dynamicProfile = new AudioProfile(gDynamicFormat,
jiabin4562b3b2019-07-29 10:13:34 -070045 ChannelMaskSet(), SampleRateSet());
Mikhail Naganov21b43362018-06-04 10:37:09 -070046 dynamicProfile->setDynamicFormat(true);
47 dynamicProfile->setDynamicChannels(true);
48 dynamicProfile->setDynamicRate(true);
49 return dynamicProfile;
50}
51
52// static
53sp<AudioProfile> AudioProfile::createFullDynamic()
54{
55 static sp<AudioProfile> dynamicProfile = createFullDynamicImpl();
56 return dynamicProfile;
57}
58
Mikhail Naganove50f6282018-07-26 16:20:43 -070059AudioProfile::AudioProfile(audio_format_t format,
60 audio_channel_mask_t channelMasks,
61 uint32_t samplingRate) :
62 mName(String8("")),
63 mFormat(format)
64{
jiabin4562b3b2019-07-29 10:13:34 -070065 mChannelMasks.insert(channelMasks);
66 mSamplingRates.insert(samplingRate);
Mikhail Naganove50f6282018-07-26 16:20:43 -070067}
68
69AudioProfile::AudioProfile(audio_format_t format,
jiabin4562b3b2019-07-29 10:13:34 -070070 const ChannelMaskSet &channelMasks,
71 const SampleRateSet &samplingRateCollection) :
Mikhail Naganove50f6282018-07-26 16:20:43 -070072 mName(String8("")),
73 mFormat(format),
74 mChannelMasks(channelMasks),
75 mSamplingRates(samplingRateCollection) {}
76
jiabin4562b3b2019-07-29 10:13:34 -070077void AudioProfile::setChannels(const ChannelMaskSet &channelMasks)
Mikhail Naganove50f6282018-07-26 16:20:43 -070078{
79 if (mIsDynamicChannels) {
80 mChannelMasks = channelMasks;
81 }
82}
83
jiabin4562b3b2019-07-29 10:13:34 -070084void AudioProfile::setSampleRates(const SampleRateSet &sampleRates)
Mikhail Naganove50f6282018-07-26 16:20:43 -070085{
86 if (mIsDynamicRate) {
87 mSamplingRates = sampleRates;
88 }
89}
90
91void AudioProfile::clear()
92{
93 if (mIsDynamicChannels) {
94 mChannelMasks.clear();
95 }
96 if (mIsDynamicRate) {
97 mSamplingRates.clear();
98 }
99}
100
François Gaffie112b0af2015-11-19 16:13:25 +0100101status_t AudioProfile::checkExact(uint32_t samplingRate, audio_channel_mask_t channelMask,
102 audio_format_t format) const
103{
Eric Laurente6930022016-02-11 10:20:40 -0800104 if (audio_formats_match(format, mFormat) &&
Phil Burk2cc2a032016-04-06 16:41:39 -0700105 supportsChannels(channelMask) &&
106 supportsRate(samplingRate)) {
François Gaffie112b0af2015-11-19 16:13:25 +0100107 return NO_ERROR;
108 }
109 return BAD_VALUE;
110}
111
François Gaffie112b0af2015-11-19 16:13:25 +0100112status_t AudioProfile::checkCompatibleSamplingRate(uint32_t samplingRate,
113 uint32_t &updatedSamplingRate) const
114{
Glenn Kasten05ddca52016-02-11 08:17:12 -0800115 ALOG_ASSERT(samplingRate > 0);
116
jiabin4562b3b2019-07-29 10:13:34 -0700117 if (mSamplingRates.empty()) {
François Gaffie112b0af2015-11-19 16:13:25 +0100118 updatedSamplingRate = samplingRate;
119 return NO_ERROR;
120 }
Glenn Kasten05ddca52016-02-11 08:17:12 -0800121
François Gaffie112b0af2015-11-19 16:13:25 +0100122 // Search for the closest supported sampling rate that is above (preferred)
123 // or below (acceptable) the desired sampling rate, within a permitted ratio.
124 // The sampling rates are sorted in ascending order.
jiabin4562b3b2019-07-29 10:13:34 -0700125 auto desiredRate = mSamplingRates.lower_bound(samplingRate);
François Gaffie112b0af2015-11-19 16:13:25 +0100126
127 // Prefer to down-sample from a higher sampling rate, as we get the desired frequency spectrum.
jiabin4562b3b2019-07-29 10:13:34 -0700128 if (desiredRate != mSamplingRates.end()) {
129 if (*desiredRate / AUDIO_RESAMPLER_DOWN_RATIO_MAX <= samplingRate) {
130 updatedSamplingRate = *desiredRate;
François Gaffie112b0af2015-11-19 16:13:25 +0100131 return NO_ERROR;
132 }
133 }
134 // But if we have to up-sample from a lower sampling rate, that's OK.
jiabin4562b3b2019-07-29 10:13:34 -0700135 if (desiredRate != mSamplingRates.begin()) {
136 uint32_t candidate = *(--desiredRate);
François Gaffie112b0af2015-11-19 16:13:25 +0100137 if (candidate * AUDIO_RESAMPLER_UP_RATIO_MAX >= samplingRate) {
138 updatedSamplingRate = candidate;
139 return NO_ERROR;
140 }
141 }
142 // leave updatedSamplingRate unmodified
143 return BAD_VALUE;
144}
145
146status_t AudioProfile::checkCompatibleChannelMask(audio_channel_mask_t channelMask,
147 audio_channel_mask_t &updatedChannelMask,
148 audio_port_type_t portType,
149 audio_port_role_t portRole) const
150{
jiabin4562b3b2019-07-29 10:13:34 -0700151 if (mChannelMasks.empty()) {
François Gaffie112b0af2015-11-19 16:13:25 +0100152 updatedChannelMask = channelMask;
153 return NO_ERROR;
154 }
155 const bool isRecordThread = portType == AUDIO_PORT_TYPE_MIX && portRole == AUDIO_PORT_ROLE_SINK;
156 const bool isIndex = audio_channel_mask_get_representation(channelMask)
157 == AUDIO_CHANNEL_REPRESENTATION_INDEX;
Eric Laurent6e161572019-05-10 15:53:08 -0700158 const uint32_t channelCount = audio_channel_count_from_in_mask(channelMask);
François Gaffie112b0af2015-11-19 16:13:25 +0100159 int bestMatch = 0;
jiabin4562b3b2019-07-29 10:13:34 -0700160 for (const auto &supported : mChannelMasks) {
François Gaffie112b0af2015-11-19 16:13:25 +0100161 if (supported == channelMask) {
162 // Exact matches always taken.
163 updatedChannelMask = channelMask;
164 return NO_ERROR;
165 }
166
167 // AUDIO_CHANNEL_NONE (value: 0) is used for dynamic channel support
168 if (isRecordThread && supported != AUDIO_CHANNEL_NONE) {
169 // Approximate (best) match:
170 // The match score measures how well the supported channel mask matches the
171 // desired mask, where increasing-is-better.
172 //
173 // TODO: Some tweaks may be needed.
174 // Should be a static function of the data processing library.
175 //
176 // In priority:
177 // match score = 1000 if legacy channel conversion equivalent (always prefer this)
178 // OR
179 // match score += 100 if the channel mask representations match
180 // match score += number of channels matched.
Eric Laurent6e161572019-05-10 15:53:08 -0700181 // match score += 100 if the channel mask representations DO NOT match
182 // but the profile has positional channel mask and less than 2 channels.
183 // This is for audio HAL convention to not list index masks for less than 2 channels
François Gaffie112b0af2015-11-19 16:13:25 +0100184 //
185 // If there are no matched channels, the mask may still be accepted
186 // but the playback or record will be silent.
187 const bool isSupportedIndex = (audio_channel_mask_get_representation(supported)
188 == AUDIO_CHANNEL_REPRESENTATION_INDEX);
Eric Laurent6e161572019-05-10 15:53:08 -0700189 const uint32_t supportedChannelCount = audio_channel_count_from_in_mask(supported);
François Gaffie112b0af2015-11-19 16:13:25 +0100190 int match;
191 if (isIndex && isSupportedIndex) {
192 // index equivalence
193 match = 100 + __builtin_popcount(
194 audio_channel_mask_get_bits(channelMask)
195 & audio_channel_mask_get_bits(supported));
196 } else if (isIndex && !isSupportedIndex) {
Eric Laurent6e161572019-05-10 15:53:08 -0700197 const uint32_t equivalentBits = (1 << supportedChannelCount) - 1 ;
François Gaffie112b0af2015-11-19 16:13:25 +0100198 match = __builtin_popcount(
199 audio_channel_mask_get_bits(channelMask) & equivalentBits);
Eric Laurent6e161572019-05-10 15:53:08 -0700200 if (supportedChannelCount <= FCC_2) {
201 match += 100;
202 }
François Gaffie112b0af2015-11-19 16:13:25 +0100203 } else if (!isIndex && isSupportedIndex) {
Eric Laurent6e161572019-05-10 15:53:08 -0700204 const uint32_t equivalentBits = (1 << channelCount) - 1;
François Gaffie112b0af2015-11-19 16:13:25 +0100205 match = __builtin_popcount(
206 equivalentBits & audio_channel_mask_get_bits(supported));
207 } else {
208 // positional equivalence
209 match = 100 + __builtin_popcount(
210 audio_channel_mask_get_bits(channelMask)
211 & audio_channel_mask_get_bits(supported));
212 switch (supported) {
213 case AUDIO_CHANNEL_IN_FRONT_BACK:
214 case AUDIO_CHANNEL_IN_STEREO:
215 if (channelMask == AUDIO_CHANNEL_IN_MONO) {
216 match = 1000;
217 }
218 break;
219 case AUDIO_CHANNEL_IN_MONO:
220 if (channelMask == AUDIO_CHANNEL_IN_FRONT_BACK
221 || channelMask == AUDIO_CHANNEL_IN_STEREO) {
222 match = 1000;
223 }
224 break;
225 default:
226 break;
227 }
228 }
229 if (match > bestMatch) {
230 bestMatch = match;
231 updatedChannelMask = supported;
232 }
233 }
234 }
235 return bestMatch > 0 ? NO_ERROR : BAD_VALUE;
236}
237
Andy Hungbb54e202018-10-05 11:42:02 -0700238void AudioProfile::dump(String8 *dst, int spaces) const
François Gaffie112b0af2015-11-19 16:13:25 +0100239{
Andy Hungbb54e202018-10-05 11:42:02 -0700240 dst->appendFormat("%s%s%s\n", mIsDynamicFormat ? "[dynamic format]" : "",
François Gaffie112b0af2015-11-19 16:13:25 +0100241 mIsDynamicChannels ? "[dynamic channels]" : "",
242 mIsDynamicRate ? "[dynamic rates]" : "");
François Gaffie112b0af2015-11-19 16:13:25 +0100243 if (mName.length() != 0) {
Andy Hungbb54e202018-10-05 11:42:02 -0700244 dst->appendFormat("%*s- name: %s\n", spaces, "", mName.string());
François Gaffie112b0af2015-11-19 16:13:25 +0100245 }
246 std::string formatLiteral;
247 if (FormatConverter::toString(mFormat, formatLiteral)) {
Andy Hungbb54e202018-10-05 11:42:02 -0700248 dst->appendFormat("%*s- format: %s\n", spaces, "", formatLiteral.c_str());
François Gaffie112b0af2015-11-19 16:13:25 +0100249 }
jiabin4562b3b2019-07-29 10:13:34 -0700250 if (!mSamplingRates.empty()) {
Andy Hungbb54e202018-10-05 11:42:02 -0700251 dst->appendFormat("%*s- sampling rates:", spaces, "");
jiabin4562b3b2019-07-29 10:13:34 -0700252 for (auto it = mSamplingRates.begin(); it != mSamplingRates.end();) {
253 dst->appendFormat("%d", *it);
254 dst->append(++it == mSamplingRates.end() ? "" : ", ");
François Gaffie112b0af2015-11-19 16:13:25 +0100255 }
Andy Hungbb54e202018-10-05 11:42:02 -0700256 dst->append("\n");
François Gaffie112b0af2015-11-19 16:13:25 +0100257 }
258
jiabin4562b3b2019-07-29 10:13:34 -0700259 if (!mChannelMasks.empty()) {
Andy Hungbb54e202018-10-05 11:42:02 -0700260 dst->appendFormat("%*s- channel masks:", spaces, "");
jiabin4562b3b2019-07-29 10:13:34 -0700261 for (auto it = mChannelMasks.begin(); it != mChannelMasks.end();) {
262 dst->appendFormat("0x%04x", *it);
263 dst->append(++it == mChannelMasks.end() ? "" : ", ");
François Gaffie112b0af2015-11-19 16:13:25 +0100264 }
Andy Hungbb54e202018-10-05 11:42:02 -0700265 dst->append("\n");
François Gaffie112b0af2015-11-19 16:13:25 +0100266 }
François Gaffie112b0af2015-11-19 16:13:25 +0100267}
268
Mikhail Naganove50f6282018-07-26 16:20:43 -0700269ssize_t AudioProfileVector::add(const sp<AudioProfile> &profile)
270{
jiabin4562b3b2019-07-29 10:13:34 -0700271 ssize_t index = size();
272 push_back(profile);
Mikhail Naganove50f6282018-07-26 16:20:43 -0700273 // we sort from worst to best, so that AUDIO_FORMAT_DEFAULT is always the first entry.
jiabin4562b3b2019-07-29 10:13:34 -0700274 std::sort(begin(), end(),
275 [](const sp<AudioProfile> & a, const sp<AudioProfile> & b)
276 {
277 return AudioPort::compareFormats(a->getFormat(), b->getFormat()) < 0;
278 });
Mikhail Naganove50f6282018-07-26 16:20:43 -0700279 return index;
280}
281
282ssize_t AudioProfileVector::addProfileFromHal(const sp<AudioProfile> &profileToAdd)
283{
284 // Check valid profile to add:
285 if (!profileToAdd->hasValidFormat()) {
286 return -1;
287 }
288 if (!profileToAdd->hasValidChannels() && !profileToAdd->hasValidRates()) {
289 FormatVector formats;
jiabin4562b3b2019-07-29 10:13:34 -0700290 formats.push_back(profileToAdd->getFormat());
Mikhail Naganove50f6282018-07-26 16:20:43 -0700291 setFormats(FormatVector(formats));
292 return 0;
293 }
294 if (!profileToAdd->hasValidChannels() && profileToAdd->hasValidRates()) {
295 setSampleRatesFor(profileToAdd->getSampleRates(), profileToAdd->getFormat());
296 return 0;
297 }
298 if (profileToAdd->hasValidChannels() && !profileToAdd->hasValidRates()) {
299 setChannelsFor(profileToAdd->getChannels(), profileToAdd->getFormat());
300 return 0;
301 }
302 // Go through the list of profile to avoid duplicates
303 for (size_t profileIndex = 0; profileIndex < size(); profileIndex++) {
jiabin4562b3b2019-07-29 10:13:34 -0700304 const sp<AudioProfile> &profile = at(profileIndex);
Mikhail Naganove50f6282018-07-26 16:20:43 -0700305 if (profile->isValid() && profile == profileToAdd) {
306 // Nothing to do
307 return profileIndex;
308 }
309 }
310 profileToAdd->setDynamicFormat(true); // set the format as dynamic to allow removal
311 return add(profileToAdd);
312}
313
François Gaffie112b0af2015-11-19 16:13:25 +0100314status_t AudioProfileVector::checkExactProfile(uint32_t samplingRate,
315 audio_channel_mask_t channelMask,
316 audio_format_t format) const
317{
jiabin4562b3b2019-07-29 10:13:34 -0700318 if (empty()) {
François Gaffie112b0af2015-11-19 16:13:25 +0100319 return NO_ERROR;
320 }
321
Mikhail Naganov2478d522017-12-07 13:02:28 -0800322 for (const auto& profile : *this) {
François Gaffie112b0af2015-11-19 16:13:25 +0100323 if (profile->checkExact(samplingRate, channelMask, format) == NO_ERROR) {
324 return NO_ERROR;
325 }
326 }
327 return BAD_VALUE;
328}
329
330status_t AudioProfileVector::checkCompatibleProfile(uint32_t &samplingRate,
331 audio_channel_mask_t &channelMask,
332 audio_format_t &format,
333 audio_port_type_t portType,
334 audio_port_role_t portRole) const
335{
jiabin4562b3b2019-07-29 10:13:34 -0700336 if (empty()) {
François Gaffie112b0af2015-11-19 16:13:25 +0100337 return NO_ERROR;
338 }
339
340 const bool checkInexact = // when port is input and format is linear pcm
341 portType == AUDIO_PORT_TYPE_MIX && portRole == AUDIO_PORT_ROLE_SINK
342 && audio_is_linear_pcm(format);
343
344 // iterate from best format to worst format (reverse order)
345 for (ssize_t i = size() - 1; i >= 0 ; --i) {
jiabin4562b3b2019-07-29 10:13:34 -0700346 const sp<AudioProfile> profile = at(i);
François Gaffie112b0af2015-11-19 16:13:25 +0100347 audio_format_t formatToCompare = profile->getFormat();
348 if (formatToCompare == format ||
349 (checkInexact
350 && formatToCompare != AUDIO_FORMAT_DEFAULT
351 && audio_is_linear_pcm(formatToCompare))) {
352 // Compatible profile has been found, checks if this profile has compatible
353 // rate and channels as well
354 audio_channel_mask_t updatedChannels;
355 uint32_t updatedRate;
356 if (profile->checkCompatibleChannelMask(channelMask, updatedChannels,
357 portType, portRole) == NO_ERROR &&
358 profile->checkCompatibleSamplingRate(samplingRate, updatedRate) == NO_ERROR) {
359 // for inexact checks we take the first linear pcm format due to sorting.
360 format = formatToCompare;
361 channelMask = updatedChannels;
362 samplingRate = updatedRate;
363 return NO_ERROR;
364 }
365 }
366 }
367 return BAD_VALUE;
368}
369
Mikhail Naganove50f6282018-07-26 16:20:43 -0700370void AudioProfileVector::clearProfiles()
371{
jiabin4562b3b2019-07-29 10:13:34 -0700372 for (auto it = begin(); it != end();) {
373 if ((*it)->isDynamicFormat() && (*it)->hasValidFormat()) {
374 it = erase(it);
375 } else {
376 (*it)->clear();
377 ++it;
Mikhail Naganove50f6282018-07-26 16:20:43 -0700378 }
Mikhail Naganove50f6282018-07-26 16:20:43 -0700379 }
380}
381
Mikhail Naganovfa69dc62018-07-27 09:58:58 -0700382// Returns an intersection between two possibly unsorted vectors and the contents of 'order'.
383// The result is ordered according to 'order'.
384template<typename T, typename Order>
385std::vector<typename T::value_type> intersectFilterAndOrder(
386 const T& input1, const T& input2, const Order& order)
387{
388 std::set<typename T::value_type> set1{input1.begin(), input1.end()};
389 std::set<typename T::value_type> set2{input2.begin(), input2.end()};
390 std::set<typename T::value_type> common;
391 std::set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(),
392 std::inserter(common, common.begin()));
393 std::vector<typename T::value_type> result;
394 for (const auto& e : order) {
395 if (common.find(e) != common.end()) result.push_back(e);
396 }
397 return result;
398}
399
400// Intersect two possibly unsorted vectors, return common elements according to 'comp' ordering.
401// 'comp' is a comparator function.
402template<typename T, typename Compare>
403std::vector<typename T::value_type> intersectAndOrder(
404 const T& input1, const T& input2, Compare comp)
405{
406 std::set<typename T::value_type, Compare> set1{input1.begin(), input1.end(), comp};
407 std::set<typename T::value_type, Compare> set2{input2.begin(), input2.end(), comp};
408 std::vector<typename T::value_type> result;
409 std::set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(),
410 std::back_inserter(result), comp);
411 return result;
412}
413
414status_t AudioProfileVector::findBestMatchingOutputConfig(const AudioProfileVector& outputProfiles,
415 const std::vector<audio_format_t>& preferredFormats,
416 const std::vector<audio_channel_mask_t>& preferredOutputChannels,
417 bool preferHigherSamplingRates,
418 audio_config_base *bestOutputConfig) const
419{
420 auto formats = intersectFilterAndOrder(getSupportedFormats(),
421 outputProfiles.getSupportedFormats(), preferredFormats);
422 // Pick the best compatible profile.
423 for (const auto& f : formats) {
424 sp<AudioProfile> inputProfile = getFirstValidProfileFor(f);
425 sp<AudioProfile> outputProfile = outputProfiles.getFirstValidProfileFor(f);
426 if (inputProfile == nullptr || outputProfile == nullptr) {
427 continue;
428 }
jiabin4562b3b2019-07-29 10:13:34 -0700429 auto channels = intersectFilterAndOrder(asOutMask(inputProfile->getChannels()),
Mikhail Naganovfa69dc62018-07-27 09:58:58 -0700430 outputProfile->getChannels(), preferredOutputChannels);
431 if (channels.empty()) {
432 continue;
433 }
434 auto sampleRates = preferHigherSamplingRates ?
435 intersectAndOrder(inputProfile->getSampleRates(), outputProfile->getSampleRates(),
jiabin4562b3b2019-07-29 10:13:34 -0700436 std::greater<typename SampleRateSet::value_type>()) :
Mikhail Naganovfa69dc62018-07-27 09:58:58 -0700437 intersectAndOrder(inputProfile->getSampleRates(), outputProfile->getSampleRates(),
jiabin4562b3b2019-07-29 10:13:34 -0700438 std::less<typename SampleRateSet::value_type>());
Mikhail Naganovfa69dc62018-07-27 09:58:58 -0700439 if (sampleRates.empty()) {
440 continue;
441 }
442 ALOGD("%s() found channel mask %#x and sample rate %d for format %#x.",
443 __func__, *channels.begin(), *sampleRates.begin(), f);
444 bestOutputConfig->format = f;
445 bestOutputConfig->sample_rate = *sampleRates.begin();
446 bestOutputConfig->channel_mask = *channels.begin();
447 return NO_ERROR;
448 }
449 return BAD_VALUE;
450}
451
Mikhail Naganove50f6282018-07-26 16:20:43 -0700452sp<AudioProfile> AudioProfileVector::getFirstValidProfile() const
453{
jiabin4562b3b2019-07-29 10:13:34 -0700454 for (const auto &profile : *this) {
455 if (profile->isValid()) {
456 return profile;
Mikhail Naganove50f6282018-07-26 16:20:43 -0700457 }
458 }
jiabin4562b3b2019-07-29 10:13:34 -0700459 return nullptr;
Mikhail Naganove50f6282018-07-26 16:20:43 -0700460}
461
462sp<AudioProfile> AudioProfileVector::getFirstValidProfileFor(audio_format_t format) const
463{
jiabin4562b3b2019-07-29 10:13:34 -0700464 for (const auto &profile : *this) {
465 if (profile->isValid() && profile->getFormat() == format) {
466 return profile;
Mikhail Naganove50f6282018-07-26 16:20:43 -0700467 }
468 }
jiabin4562b3b2019-07-29 10:13:34 -0700469 return nullptr;
Mikhail Naganove50f6282018-07-26 16:20:43 -0700470}
471
472FormatVector AudioProfileVector::getSupportedFormats() const
473{
474 FormatVector supportedFormats;
jiabin4562b3b2019-07-29 10:13:34 -0700475 for (const auto &profile : *this) {
476 if (profile->hasValidFormat()) {
477 supportedFormats.push_back(profile->getFormat());
Mikhail Naganove50f6282018-07-26 16:20:43 -0700478 }
479 }
480 return supportedFormats;
481}
482
483bool AudioProfileVector::hasDynamicChannelsFor(audio_format_t format) const
484{
jiabin4562b3b2019-07-29 10:13:34 -0700485 for (const auto &profile : *this) {
Mikhail Naganove50f6282018-07-26 16:20:43 -0700486 if (profile->getFormat() == format && profile->isDynamicChannels()) {
487 return true;
488 }
489 }
490 return false;
491}
492
493bool AudioProfileVector::hasDynamicProfile() const
494{
jiabin4562b3b2019-07-29 10:13:34 -0700495 for (const auto &profile : *this) {
496 if (profile->isDynamic()) {
Mikhail Naganove50f6282018-07-26 16:20:43 -0700497 return true;
498 }
499 }
500 return false;
501}
502
503bool AudioProfileVector::hasDynamicRateFor(audio_format_t format) const
504{
jiabin4562b3b2019-07-29 10:13:34 -0700505 for (const auto &profile : *this) {
Mikhail Naganove50f6282018-07-26 16:20:43 -0700506 if (profile->getFormat() == format && profile->isDynamicRate()) {
507 return true;
508 }
509 }
510 return false;
511}
512
513void AudioProfileVector::setFormats(const FormatVector &formats)
514{
515 // Only allow to change the format of dynamic profile
516 sp<AudioProfile> dynamicFormatProfile = getProfileFor(gDynamicFormat);
517 if (dynamicFormatProfile == 0) {
518 return;
519 }
jiabin4562b3b2019-07-29 10:13:34 -0700520 for (const auto &format : formats) {
521 sp<AudioProfile> profile = new AudioProfile(format,
Mikhail Naganove50f6282018-07-26 16:20:43 -0700522 dynamicFormatProfile->getChannels(),
523 dynamicFormatProfile->getSampleRates());
524 profile->setDynamicFormat(true);
525 profile->setDynamicChannels(dynamicFormatProfile->isDynamicChannels());
526 profile->setDynamicRate(dynamicFormatProfile->isDynamicRate());
527 add(profile);
528 }
529}
530
Andy Hungbb54e202018-10-05 11:42:02 -0700531void AudioProfileVector::dump(String8 *dst, int spaces) const
Mikhail Naganove50f6282018-07-26 16:20:43 -0700532{
Andy Hungbb54e202018-10-05 11:42:02 -0700533 dst->appendFormat("%*s- Profiles:\n", spaces, "");
Mikhail Naganove50f6282018-07-26 16:20:43 -0700534 for (size_t i = 0; i < size(); i++) {
Andy Hungbb54e202018-10-05 11:42:02 -0700535 dst->appendFormat("%*sProfile %zu:", spaces + 4, "", i);
jiabin4562b3b2019-07-29 10:13:34 -0700536 at(i)->dump(dst, spaces + 8);
Mikhail Naganove50f6282018-07-26 16:20:43 -0700537 }
538}
539
540sp<AudioProfile> AudioProfileVector::getProfileFor(audio_format_t format) const
541{
jiabin4562b3b2019-07-29 10:13:34 -0700542 for (const auto &profile : *this) {
543 if (profile->getFormat() == format) {
544 return profile;
Mikhail Naganove50f6282018-07-26 16:20:43 -0700545 }
546 }
jiabin4562b3b2019-07-29 10:13:34 -0700547 return nullptr;
Mikhail Naganove50f6282018-07-26 16:20:43 -0700548}
549
550void AudioProfileVector::setSampleRatesFor(
jiabin4562b3b2019-07-29 10:13:34 -0700551 const SampleRateSet &sampleRates, audio_format_t format)
Mikhail Naganove50f6282018-07-26 16:20:43 -0700552{
jiabin4562b3b2019-07-29 10:13:34 -0700553 for (const auto &profile : *this) {
Mikhail Naganove50f6282018-07-26 16:20:43 -0700554 if (profile->getFormat() == format && profile->isDynamicRate()) {
555 if (profile->hasValidRates()) {
556 // Need to create a new profile with same format
557 sp<AudioProfile> profileToAdd = new AudioProfile(format, profile->getChannels(),
558 sampleRates);
559 profileToAdd->setDynamicFormat(true); // need to set to allow cleaning
560 add(profileToAdd);
561 } else {
562 profile->setSampleRates(sampleRates);
563 }
564 return;
565 }
566 }
567}
568
jiabin4562b3b2019-07-29 10:13:34 -0700569void AudioProfileVector::setChannelsFor(const ChannelMaskSet &channelMasks, audio_format_t format)
Mikhail Naganove50f6282018-07-26 16:20:43 -0700570{
jiabin4562b3b2019-07-29 10:13:34 -0700571 for (const auto &profile : *this) {
Mikhail Naganove50f6282018-07-26 16:20:43 -0700572 if (profile->getFormat() == format && profile->isDynamicChannels()) {
573 if (profile->hasValidChannels()) {
574 // Need to create a new profile with same format
575 sp<AudioProfile> profileToAdd = new AudioProfile(format, channelMasks,
576 profile->getSampleRates());
577 profileToAdd->setDynamicFormat(true); // need to set to allow cleaning
578 add(profileToAdd);
579 } else {
580 profile->setChannels(channelMasks);
581 }
582 return;
583 }
584 }
585}
586
Mikhail Naganov1b2a7942017-12-08 10:18:09 -0800587} // namespace android