Home | History | Annotate | Download | only in src
      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 
     35 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 
     46 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 
     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 
     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
     75 sp<AudioProfile> AudioProfile::createFullDynamic()
     76 {
     77     static sp<AudioProfile> dynamicProfile = createFullDynamicImpl();
     78     return dynamicProfile;
     79 }
     80 
     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 
     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 
     99 void AudioProfile::setChannels(const ChannelsVector &channelMasks)
    100 {
    101     if (mIsDynamicChannels) {
    102         mChannelMasks = channelMasks;
    103     }
    104 }
    105 
    106 void AudioProfile::setSampleRates(const SampleRateVector &sampleRates)
    107 {
    108     if (mIsDynamicRate) {
    109         mSamplingRates = sampleRates;
    110     }
    111 }
    112 
    113 void AudioProfile::clear()
    114 {
    115     if (mIsDynamicChannels) {
    116         mChannelMasks.clear();
    117     }
    118     if (mIsDynamicRate) {
    119         mSamplingRates.clear();
    120     }
    121 }
    122 
    123 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 
    134 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 
    169 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 
    262 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 
    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 
    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 
    337 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 
    353 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 
    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>
    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>
    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 
    437 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 
    475 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 
    485 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 
    495 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 
    506 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 
    517 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 
    527 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 
    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 
    556 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 
    565 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 
    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 
    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
    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