1 /*
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
17 #include <algorithm>
18 #include <set>
19 #include <string>
20
21 #define LOG_TAG "APM::AudioProfile"
22 //#define LOG_NDEBUG 0
23
24 #include <media/AudioResamplerPublic.h>
25 #include <utils/Errors.h>
26
27 #include "AudioGain.h"
28 #include "AudioPort.h"
29 #include "AudioProfile.h"
30 #include "HwModule.h"
31 #include "TypeConverter.h"
32
33 namespace android {
34
asInMask() const35 ChannelsVector ChannelsVector::asInMask() const
36 {
37 ChannelsVector inMaskVector;
38 for (const auto& channel : *this) {
39 if (audio_channel_mask_out_to_in(channel) != AUDIO_CHANNEL_INVALID) {
40 inMaskVector.add(audio_channel_mask_out_to_in(channel));
41 }
42 }
43 return inMaskVector;
44 }
45
asOutMask() const46 ChannelsVector ChannelsVector::asOutMask() const
47 {
48 ChannelsVector outMaskVector;
49 for (const auto& channel : *this) {
50 if (audio_channel_mask_in_to_out(channel) != AUDIO_CHANNEL_INVALID) {
51 outMaskVector.add(audio_channel_mask_in_to_out(channel));
52 }
53 }
54 return outMaskVector;
55 }
56
operator ==(const AudioProfile & left,const AudioProfile & compareTo)57 bool operator == (const AudioProfile &left, const AudioProfile &compareTo)
58 {
59 return (left.getFormat() == compareTo.getFormat()) &&
60 (left.getChannels() == compareTo.getChannels()) &&
61 (left.getSampleRates() == compareTo.getSampleRates());
62 }
63
createFullDynamicImpl()64 static AudioProfile* createFullDynamicImpl()
65 {
66 AudioProfile* dynamicProfile = new AudioProfile(gDynamicFormat,
67 ChannelsVector(), SampleRateVector());
68 dynamicProfile->setDynamicFormat(true);
69 dynamicProfile->setDynamicChannels(true);
70 dynamicProfile->setDynamicRate(true);
71 return dynamicProfile;
72 }
73
74 // static
createFullDynamic()75 sp<AudioProfile> AudioProfile::createFullDynamic()
76 {
77 static sp<AudioProfile> dynamicProfile = createFullDynamicImpl();
78 return dynamicProfile;
79 }
80
AudioProfile(audio_format_t format,audio_channel_mask_t channelMasks,uint32_t samplingRate)81 AudioProfile::AudioProfile(audio_format_t format,
82 audio_channel_mask_t channelMasks,
83 uint32_t samplingRate) :
84 mName(String8("")),
85 mFormat(format)
86 {
87 mChannelMasks.add(channelMasks);
88 mSamplingRates.add(samplingRate);
89 }
90
AudioProfile(audio_format_t format,const ChannelsVector & channelMasks,const SampleRateVector & samplingRateCollection)91 AudioProfile::AudioProfile(audio_format_t format,
92 const ChannelsVector &channelMasks,
93 const SampleRateVector &samplingRateCollection) :
94 mName(String8("")),
95 mFormat(format),
96 mChannelMasks(channelMasks),
97 mSamplingRates(samplingRateCollection) {}
98
setChannels(const ChannelsVector & channelMasks)99 void AudioProfile::setChannels(const ChannelsVector &channelMasks)
100 {
101 if (mIsDynamicChannels) {
102 mChannelMasks = channelMasks;
103 }
104 }
105
setSampleRates(const SampleRateVector & sampleRates)106 void AudioProfile::setSampleRates(const SampleRateVector &sampleRates)
107 {
108 if (mIsDynamicRate) {
109 mSamplingRates = sampleRates;
110 }
111 }
112
clear()113 void AudioProfile::clear()
114 {
115 if (mIsDynamicChannels) {
116 mChannelMasks.clear();
117 }
118 if (mIsDynamicRate) {
119 mSamplingRates.clear();
120 }
121 }
122
checkExact(uint32_t samplingRate,audio_channel_mask_t channelMask,audio_format_t format) const123 status_t AudioProfile::checkExact(uint32_t samplingRate, audio_channel_mask_t channelMask,
124 audio_format_t format) const
125 {
126 if (audio_formats_match(format, mFormat) &&
127 supportsChannels(channelMask) &&
128 supportsRate(samplingRate)) {
129 return NO_ERROR;
130 }
131 return BAD_VALUE;
132 }
133
checkCompatibleSamplingRate(uint32_t samplingRate,uint32_t & updatedSamplingRate) const134 status_t AudioProfile::checkCompatibleSamplingRate(uint32_t samplingRate,
135 uint32_t &updatedSamplingRate) const
136 {
137 ALOG_ASSERT(samplingRate > 0);
138
139 if (mSamplingRates.isEmpty()) {
140 updatedSamplingRate = samplingRate;
141 return NO_ERROR;
142 }
143
144 // Search for the closest supported sampling rate that is above (preferred)
145 // or below (acceptable) the desired sampling rate, within a permitted ratio.
146 // The sampling rates are sorted in ascending order.
147 size_t orderOfDesiredRate = mSamplingRates.orderOf(samplingRate);
148
149 // Prefer to down-sample from a higher sampling rate, as we get the desired frequency spectrum.
150 if (orderOfDesiredRate < mSamplingRates.size()) {
151 uint32_t candidate = mSamplingRates[orderOfDesiredRate];
152 if (candidate / AUDIO_RESAMPLER_DOWN_RATIO_MAX <= samplingRate) {
153 updatedSamplingRate = candidate;
154 return NO_ERROR;
155 }
156 }
157 // But if we have to up-sample from a lower sampling rate, that's OK.
158 if (orderOfDesiredRate != 0) {
159 uint32_t candidate = mSamplingRates[orderOfDesiredRate - 1];
160 if (candidate * AUDIO_RESAMPLER_UP_RATIO_MAX >= samplingRate) {
161 updatedSamplingRate = candidate;
162 return NO_ERROR;
163 }
164 }
165 // leave updatedSamplingRate unmodified
166 return BAD_VALUE;
167 }
168
checkCompatibleChannelMask(audio_channel_mask_t channelMask,audio_channel_mask_t & updatedChannelMask,audio_port_type_t portType,audio_port_role_t portRole) const169 status_t AudioProfile::checkCompatibleChannelMask(audio_channel_mask_t channelMask,
170 audio_channel_mask_t &updatedChannelMask,
171 audio_port_type_t portType,
172 audio_port_role_t portRole) const
173 {
174 if (mChannelMasks.isEmpty()) {
175 updatedChannelMask = channelMask;
176 return NO_ERROR;
177 }
178 const bool isRecordThread = portType == AUDIO_PORT_TYPE_MIX && portRole == AUDIO_PORT_ROLE_SINK;
179 const bool isIndex = audio_channel_mask_get_representation(channelMask)
180 == AUDIO_CHANNEL_REPRESENTATION_INDEX;
181 const uint32_t channelCount = audio_channel_count_from_in_mask(channelMask);
182 int bestMatch = 0;
183 for (size_t i = 0; i < mChannelMasks.size(); i ++) {
184 audio_channel_mask_t supported = mChannelMasks[i];
185 if (supported == channelMask) {
186 // Exact matches always taken.
187 updatedChannelMask = channelMask;
188 return NO_ERROR;
189 }
190
191 // AUDIO_CHANNEL_NONE (value: 0) is used for dynamic channel support
192 if (isRecordThread && supported != AUDIO_CHANNEL_NONE) {
193 // Approximate (best) match:
194 // The match score measures how well the supported channel mask matches the
195 // desired mask, where increasing-is-better.
196 //
197 // TODO: Some tweaks may be needed.
198 // Should be a static function of the data processing library.
199 //
200 // In priority:
201 // match score = 1000 if legacy channel conversion equivalent (always prefer this)
202 // OR
203 // match score += 100 if the channel mask representations match
204 // match score += number of channels matched.
205 // match score += 100 if the channel mask representations DO NOT match
206 // but the profile has positional channel mask and less than 2 channels.
207 // This is for audio HAL convention to not list index masks for less than 2 channels
208 //
209 // If there are no matched channels, the mask may still be accepted
210 // but the playback or record will be silent.
211 const bool isSupportedIndex = (audio_channel_mask_get_representation(supported)
212 == AUDIO_CHANNEL_REPRESENTATION_INDEX);
213 const uint32_t supportedChannelCount = audio_channel_count_from_in_mask(supported);
214 int match;
215 if (isIndex && isSupportedIndex) {
216 // index equivalence
217 match = 100 + __builtin_popcount(
218 audio_channel_mask_get_bits(channelMask)
219 & audio_channel_mask_get_bits(supported));
220 } else if (isIndex && !isSupportedIndex) {
221 const uint32_t equivalentBits = (1 << supportedChannelCount) - 1 ;
222 match = __builtin_popcount(
223 audio_channel_mask_get_bits(channelMask) & equivalentBits);
224 if (supportedChannelCount <= FCC_2) {
225 match += 100;
226 }
227 } else if (!isIndex && isSupportedIndex) {
228 const uint32_t equivalentBits = (1 << channelCount) - 1;
229 match = __builtin_popcount(
230 equivalentBits & audio_channel_mask_get_bits(supported));
231 } else {
232 // positional equivalence
233 match = 100 + __builtin_popcount(
234 audio_channel_mask_get_bits(channelMask)
235 & audio_channel_mask_get_bits(supported));
236 switch (supported) {
237 case AUDIO_CHANNEL_IN_FRONT_BACK:
238 case AUDIO_CHANNEL_IN_STEREO:
239 if (channelMask == AUDIO_CHANNEL_IN_MONO) {
240 match = 1000;
241 }
242 break;
243 case AUDIO_CHANNEL_IN_MONO:
244 if (channelMask == AUDIO_CHANNEL_IN_FRONT_BACK
245 || channelMask == AUDIO_CHANNEL_IN_STEREO) {
246 match = 1000;
247 }
248 break;
249 default:
250 break;
251 }
252 }
253 if (match > bestMatch) {
254 bestMatch = match;
255 updatedChannelMask = supported;
256 }
257 }
258 }
259 return bestMatch > 0 ? NO_ERROR : BAD_VALUE;
260 }
261
dump(String8 * dst,int spaces) const262 void AudioProfile::dump(String8 *dst, int spaces) const
263 {
264 dst->appendFormat("%s%s%s\n", mIsDynamicFormat ? "[dynamic format]" : "",
265 mIsDynamicChannels ? "[dynamic channels]" : "",
266 mIsDynamicRate ? "[dynamic rates]" : "");
267 if (mName.length() != 0) {
268 dst->appendFormat("%*s- name: %s\n", spaces, "", mName.string());
269 }
270 std::string formatLiteral;
271 if (FormatConverter::toString(mFormat, formatLiteral)) {
272 dst->appendFormat("%*s- format: %s\n", spaces, "", formatLiteral.c_str());
273 }
274 if (!mSamplingRates.isEmpty()) {
275 dst->appendFormat("%*s- sampling rates:", spaces, "");
276 for (size_t i = 0; i < mSamplingRates.size(); i++) {
277 dst->appendFormat("%d", mSamplingRates[i]);
278 dst->append(i == (mSamplingRates.size() - 1) ? "" : ", ");
279 }
280 dst->append("\n");
281 }
282
283 if (!mChannelMasks.isEmpty()) {
284 dst->appendFormat("%*s- channel masks:", spaces, "");
285 for (size_t i = 0; i < mChannelMasks.size(); i++) {
286 dst->appendFormat("0x%04x", mChannelMasks[i]);
287 dst->append(i == (mChannelMasks.size() - 1) ? "" : ", ");
288 }
289 dst->append("\n");
290 }
291 }
292
add(const sp<AudioProfile> & profile)293 ssize_t AudioProfileVector::add(const sp<AudioProfile> &profile)
294 {
295 ssize_t index = Vector::add(profile);
296 // we sort from worst to best, so that AUDIO_FORMAT_DEFAULT is always the first entry.
297 // TODO: compareFormats could be a lambda to convert between pointer-to-format to format:
298 // [](const audio_format_t *format1, const audio_format_t *format2) {
299 // return compareFormats(*format1, *format2);
300 // }
301 sort(compareFormats);
302 return index;
303 }
304
addProfileFromHal(const sp<AudioProfile> & profileToAdd)305 ssize_t AudioProfileVector::addProfileFromHal(const sp<AudioProfile> &profileToAdd)
306 {
307 // Check valid profile to add:
308 if (!profileToAdd->hasValidFormat()) {
309 return -1;
310 }
311 if (!profileToAdd->hasValidChannels() && !profileToAdd->hasValidRates()) {
312 FormatVector formats;
313 formats.add(profileToAdd->getFormat());
314 setFormats(FormatVector(formats));
315 return 0;
316 }
317 if (!profileToAdd->hasValidChannels() && profileToAdd->hasValidRates()) {
318 setSampleRatesFor(profileToAdd->getSampleRates(), profileToAdd->getFormat());
319 return 0;
320 }
321 if (profileToAdd->hasValidChannels() && !profileToAdd->hasValidRates()) {
322 setChannelsFor(profileToAdd->getChannels(), profileToAdd->getFormat());
323 return 0;
324 }
325 // Go through the list of profile to avoid duplicates
326 for (size_t profileIndex = 0; profileIndex < size(); profileIndex++) {
327 const sp<AudioProfile> &profile = itemAt(profileIndex);
328 if (profile->isValid() && profile == profileToAdd) {
329 // Nothing to do
330 return profileIndex;
331 }
332 }
333 profileToAdd->setDynamicFormat(true); // set the format as dynamic to allow removal
334 return add(profileToAdd);
335 }
336
checkExactProfile(uint32_t samplingRate,audio_channel_mask_t channelMask,audio_format_t format) const337 status_t AudioProfileVector::checkExactProfile(uint32_t samplingRate,
338 audio_channel_mask_t channelMask,
339 audio_format_t format) const
340 {
341 if (isEmpty()) {
342 return NO_ERROR;
343 }
344
345 for (const auto& profile : *this) {
346 if (profile->checkExact(samplingRate, channelMask, format) == NO_ERROR) {
347 return NO_ERROR;
348 }
349 }
350 return BAD_VALUE;
351 }
352
checkCompatibleProfile(uint32_t & samplingRate,audio_channel_mask_t & channelMask,audio_format_t & format,audio_port_type_t portType,audio_port_role_t portRole) const353 status_t AudioProfileVector::checkCompatibleProfile(uint32_t &samplingRate,
354 audio_channel_mask_t &channelMask,
355 audio_format_t &format,
356 audio_port_type_t portType,
357 audio_port_role_t portRole) const
358 {
359 if (isEmpty()) {
360 return NO_ERROR;
361 }
362
363 const bool checkInexact = // when port is input and format is linear pcm
364 portType == AUDIO_PORT_TYPE_MIX && portRole == AUDIO_PORT_ROLE_SINK
365 && audio_is_linear_pcm(format);
366
367 // iterate from best format to worst format (reverse order)
368 for (ssize_t i = size() - 1; i >= 0 ; --i) {
369 const sp<AudioProfile> profile = itemAt(i);
370 audio_format_t formatToCompare = profile->getFormat();
371 if (formatToCompare == format ||
372 (checkInexact
373 && formatToCompare != AUDIO_FORMAT_DEFAULT
374 && audio_is_linear_pcm(formatToCompare))) {
375 // Compatible profile has been found, checks if this profile has compatible
376 // rate and channels as well
377 audio_channel_mask_t updatedChannels;
378 uint32_t updatedRate;
379 if (profile->checkCompatibleChannelMask(channelMask, updatedChannels,
380 portType, portRole) == NO_ERROR &&
381 profile->checkCompatibleSamplingRate(samplingRate, updatedRate) == NO_ERROR) {
382 // for inexact checks we take the first linear pcm format due to sorting.
383 format = formatToCompare;
384 channelMask = updatedChannels;
385 samplingRate = updatedRate;
386 return NO_ERROR;
387 }
388 }
389 }
390 return BAD_VALUE;
391 }
392
clearProfiles()393 void AudioProfileVector::clearProfiles()
394 {
395 for (size_t i = size(); i != 0; ) {
396 sp<AudioProfile> profile = itemAt(--i);
397 if (profile->isDynamicFormat() && profile->hasValidFormat()) {
398 removeAt(i);
399 continue;
400 }
401 profile->clear();
402 }
403 }
404
405 // Returns an intersection between two possibly unsorted vectors and the contents of 'order'.
406 // The result is ordered according to 'order'.
407 template<typename T, typename Order>
intersectFilterAndOrder(const T & input1,const T & input2,const Order & order)408 std::vector<typename T::value_type> intersectFilterAndOrder(
409 const T& input1, const T& input2, const Order& order)
410 {
411 std::set<typename T::value_type> set1{input1.begin(), input1.end()};
412 std::set<typename T::value_type> set2{input2.begin(), input2.end()};
413 std::set<typename T::value_type> common;
414 std::set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(),
415 std::inserter(common, common.begin()));
416 std::vector<typename T::value_type> result;
417 for (const auto& e : order) {
418 if (common.find(e) != common.end()) result.push_back(e);
419 }
420 return result;
421 }
422
423 // Intersect two possibly unsorted vectors, return common elements according to 'comp' ordering.
424 // 'comp' is a comparator function.
425 template<typename T, typename Compare>
intersectAndOrder(const T & input1,const T & input2,Compare comp)426 std::vector<typename T::value_type> intersectAndOrder(
427 const T& input1, const T& input2, Compare comp)
428 {
429 std::set<typename T::value_type, Compare> set1{input1.begin(), input1.end(), comp};
430 std::set<typename T::value_type, Compare> set2{input2.begin(), input2.end(), comp};
431 std::vector<typename T::value_type> result;
432 std::set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(),
433 std::back_inserter(result), comp);
434 return result;
435 }
436
findBestMatchingOutputConfig(const AudioProfileVector & outputProfiles,const std::vector<audio_format_t> & preferredFormats,const std::vector<audio_channel_mask_t> & preferredOutputChannels,bool preferHigherSamplingRates,audio_config_base * bestOutputConfig) const437 status_t AudioProfileVector::findBestMatchingOutputConfig(const AudioProfileVector& outputProfiles,
438 const std::vector<audio_format_t>& preferredFormats,
439 const std::vector<audio_channel_mask_t>& preferredOutputChannels,
440 bool preferHigherSamplingRates,
441 audio_config_base *bestOutputConfig) const
442 {
443 auto formats = intersectFilterAndOrder(getSupportedFormats(),
444 outputProfiles.getSupportedFormats(), preferredFormats);
445 // Pick the best compatible profile.
446 for (const auto& f : formats) {
447 sp<AudioProfile> inputProfile = getFirstValidProfileFor(f);
448 sp<AudioProfile> outputProfile = outputProfiles.getFirstValidProfileFor(f);
449 if (inputProfile == nullptr || outputProfile == nullptr) {
450 continue;
451 }
452 auto channels = intersectFilterAndOrder(inputProfile->getChannels().asOutMask(),
453 outputProfile->getChannels(), preferredOutputChannels);
454 if (channels.empty()) {
455 continue;
456 }
457 auto sampleRates = preferHigherSamplingRates ?
458 intersectAndOrder(inputProfile->getSampleRates(), outputProfile->getSampleRates(),
459 std::greater<typename SampleRateVector::value_type>()) :
460 intersectAndOrder(inputProfile->getSampleRates(), outputProfile->getSampleRates(),
461 std::less<typename SampleRateVector::value_type>());
462 if (sampleRates.empty()) {
463 continue;
464 }
465 ALOGD("%s() found channel mask %#x and sample rate %d for format %#x.",
466 __func__, *channels.begin(), *sampleRates.begin(), f);
467 bestOutputConfig->format = f;
468 bestOutputConfig->sample_rate = *sampleRates.begin();
469 bestOutputConfig->channel_mask = *channels.begin();
470 return NO_ERROR;
471 }
472 return BAD_VALUE;
473 }
474
getFirstValidProfile() const475 sp<AudioProfile> AudioProfileVector::getFirstValidProfile() const
476 {
477 for (size_t i = 0; i < size(); i++) {
478 if (itemAt(i)->isValid()) {
479 return itemAt(i);
480 }
481 }
482 return 0;
483 }
484
getFirstValidProfileFor(audio_format_t format) const485 sp<AudioProfile> AudioProfileVector::getFirstValidProfileFor(audio_format_t format) const
486 {
487 for (size_t i = 0; i < size(); i++) {
488 if (itemAt(i)->isValid() && itemAt(i)->getFormat() == format) {
489 return itemAt(i);
490 }
491 }
492 return 0;
493 }
494
getSupportedFormats() const495 FormatVector AudioProfileVector::getSupportedFormats() const
496 {
497 FormatVector supportedFormats;
498 for (size_t i = 0; i < size(); i++) {
499 if (itemAt(i)->hasValidFormat()) {
500 supportedFormats.add(itemAt(i)->getFormat());
501 }
502 }
503 return supportedFormats;
504 }
505
hasDynamicChannelsFor(audio_format_t format) const506 bool AudioProfileVector::hasDynamicChannelsFor(audio_format_t format) const
507 {
508 for (size_t i = 0; i < size(); i++) {
509 sp<AudioProfile> profile = itemAt(i);
510 if (profile->getFormat() == format && profile->isDynamicChannels()) {
511 return true;
512 }
513 }
514 return false;
515 }
516
hasDynamicProfile() const517 bool AudioProfileVector::hasDynamicProfile() const
518 {
519 for (size_t i = 0; i < size(); i++) {
520 if (itemAt(i)->isDynamic()) {
521 return true;
522 }
523 }
524 return false;
525 }
526
hasDynamicRateFor(audio_format_t format) const527 bool AudioProfileVector::hasDynamicRateFor(audio_format_t format) const
528 {
529 for (size_t i = 0; i < size(); i++) {
530 sp<AudioProfile> profile = itemAt(i);
531 if (profile->getFormat() == format && profile->isDynamicRate()) {
532 return true;
533 }
534 }
535 return false;
536 }
537
setFormats(const FormatVector & formats)538 void AudioProfileVector::setFormats(const FormatVector &formats)
539 {
540 // Only allow to change the format of dynamic profile
541 sp<AudioProfile> dynamicFormatProfile = getProfileFor(gDynamicFormat);
542 if (dynamicFormatProfile == 0) {
543 return;
544 }
545 for (size_t i = 0; i < formats.size(); i++) {
546 sp<AudioProfile> profile = new AudioProfile(formats[i],
547 dynamicFormatProfile->getChannels(),
548 dynamicFormatProfile->getSampleRates());
549 profile->setDynamicFormat(true);
550 profile->setDynamicChannels(dynamicFormatProfile->isDynamicChannels());
551 profile->setDynamicRate(dynamicFormatProfile->isDynamicRate());
552 add(profile);
553 }
554 }
555
dump(String8 * dst,int spaces) const556 void AudioProfileVector::dump(String8 *dst, int spaces) const
557 {
558 dst->appendFormat("%*s- Profiles:\n", spaces, "");
559 for (size_t i = 0; i < size(); i++) {
560 dst->appendFormat("%*sProfile %zu:", spaces + 4, "", i);
561 itemAt(i)->dump(dst, spaces + 8);
562 }
563 }
564
getProfileFor(audio_format_t format) const565 sp<AudioProfile> AudioProfileVector::getProfileFor(audio_format_t format) const
566 {
567 for (size_t i = 0; i < size(); i++) {
568 if (itemAt(i)->getFormat() == format) {
569 return itemAt(i);
570 }
571 }
572 return 0;
573 }
574
setSampleRatesFor(const SampleRateVector & sampleRates,audio_format_t format)575 void AudioProfileVector::setSampleRatesFor(
576 const SampleRateVector &sampleRates, audio_format_t format)
577 {
578 for (size_t i = 0; i < size(); i++) {
579 sp<AudioProfile> profile = itemAt(i);
580 if (profile->getFormat() == format && profile->isDynamicRate()) {
581 if (profile->hasValidRates()) {
582 // Need to create a new profile with same format
583 sp<AudioProfile> profileToAdd = new AudioProfile(format, profile->getChannels(),
584 sampleRates);
585 profileToAdd->setDynamicFormat(true); // need to set to allow cleaning
586 add(profileToAdd);
587 } else {
588 profile->setSampleRates(sampleRates);
589 }
590 return;
591 }
592 }
593 }
594
setChannelsFor(const ChannelsVector & channelMasks,audio_format_t format)595 void AudioProfileVector::setChannelsFor(const ChannelsVector &channelMasks, audio_format_t format)
596 {
597 for (size_t i = 0; i < size(); i++) {
598 sp<AudioProfile> profile = itemAt(i);
599 if (profile->getFormat() == format && profile->isDynamicChannels()) {
600 if (profile->hasValidChannels()) {
601 // Need to create a new profile with same format
602 sp<AudioProfile> profileToAdd = new AudioProfile(format, channelMasks,
603 profile->getSampleRates());
604 profileToAdd->setDynamicFormat(true); // need to set to allow cleaning
605 add(profileToAdd);
606 } else {
607 profile->setChannels(channelMasks);
608 }
609 return;
610 }
611 }
612 }
613
614 // static
compareFormats(const sp<AudioProfile> * profile1,const sp<AudioProfile> * profile2)615 int AudioProfileVector::compareFormats(const sp<AudioProfile> *profile1,
616 const sp<AudioProfile> *profile2)
617 {
618 return AudioPort::compareFormats((*profile1)->getFormat(), (*profile2)->getFormat());
619 }
620
621 } // namespace android
622