/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "APM::AudioPort" //#define LOG_NDEBUG 0 #include "TypeConverter.h" #include "AudioPort.h" #include "HwModule.h" #include "AudioGain.h" #include #ifndef ARRAY_SIZE #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) #endif namespace android { int32_t volatile AudioPort::mNextUniqueId = 1; // --- AudioPort class implementation void AudioPort::attach(const sp& module) { mModule = module; } // Note that is a different namespace than AudioFlinger unique IDs audio_port_handle_t AudioPort::getNextUniqueId() { return static_cast(android_atomic_inc(&mNextUniqueId)); } audio_module_handle_t AudioPort::getModuleHandle() const { if (mModule == 0) { return AUDIO_MODULE_HANDLE_NONE; } return mModule->mHandle; } uint32_t AudioPort::getModuleVersion() const { if (mModule == 0) { return 0; } return mModule->getHalVersion(); } const char *AudioPort::getModuleName() const { if (mModule == 0) { return "invalid module"; } return mModule->getName(); } void AudioPort::toAudioPort(struct audio_port *port) const { // TODO: update this function once audio_port structure reflects the new profile definition. // For compatibility reason: flatening the AudioProfile into audio_port structure. SortedVector flatenedFormats; SampleRateVector flatenedRates; ChannelsVector flatenedChannels; for (size_t profileIndex = 0; profileIndex < mProfiles.size(); profileIndex++) { if (mProfiles[profileIndex]->isValid()) { audio_format_t formatToExport = mProfiles[profileIndex]->getFormat(); const SampleRateVector &ratesToExport = mProfiles[profileIndex]->getSampleRates(); const ChannelsVector &channelsToExport = mProfiles[profileIndex]->getChannels(); if (flatenedFormats.indexOf(formatToExport) < 0) { flatenedFormats.add(formatToExport); } for (size_t rateIndex = 0; rateIndex < ratesToExport.size(); rateIndex++) { uint32_t rate = ratesToExport[rateIndex]; if (flatenedRates.indexOf(rate) < 0) { flatenedRates.add(rate); } } for (size_t chanIndex = 0; chanIndex < channelsToExport.size(); chanIndex++) { audio_channel_mask_t channels = channelsToExport[chanIndex]; if (flatenedChannels.indexOf(channels) < 0) { flatenedChannels.add(channels); } } if (flatenedRates.size() > AUDIO_PORT_MAX_SAMPLING_RATES || flatenedChannels.size() > AUDIO_PORT_MAX_CHANNEL_MASKS || flatenedFormats.size() > AUDIO_PORT_MAX_FORMATS) { ALOGE("%s: bailing out: cannot export profiles to port config", __FUNCTION__); return; } } } port->role = mRole; port->type = mType; strlcpy(port->name, mName, AUDIO_PORT_MAX_NAME_LEN); port->num_sample_rates = flatenedRates.size(); port->num_channel_masks = flatenedChannels.size(); port->num_formats = flatenedFormats.size(); for (size_t i = 0; i < flatenedRates.size(); i++) { port->sample_rates[i] = flatenedRates[i]; } for (size_t i = 0; i < flatenedChannels.size(); i++) { port->channel_masks[i] = flatenedChannels[i]; } for (size_t i = 0; i < flatenedFormats.size(); i++) { port->formats[i] = flatenedFormats[i]; } ALOGV("AudioPort::toAudioPort() num gains %zu", mGains.size()); uint32_t i; for (i = 0; i < mGains.size() && i < AUDIO_PORT_MAX_GAINS; i++) { port->gains[i] = mGains[i]->getGain(); } port->num_gains = i; } void AudioPort::importAudioPort(const sp port) { size_t indexToImport; for (indexToImport = 0; indexToImport < port->mProfiles.size(); indexToImport++) { const sp &profileToImport = port->mProfiles[indexToImport]; if (profileToImport->isValid()) { // Import only valid port, i.e. valid format, non empty rates and channels masks bool hasSameProfile = false; for (size_t profileIndex = 0; profileIndex < mProfiles.size(); profileIndex++) { if (*mProfiles[profileIndex] == *profileToImport) { // never import a profile twice hasSameProfile = true; break; } } if (hasSameProfile) { // never import a same profile twice continue; } addAudioProfile(profileToImport); } } } void AudioPort::pickSamplingRate(uint32_t &pickedRate,const SampleRateVector &samplingRates) const { pickedRate = 0; // For direct outputs, pick minimum sampling rate: this helps ensuring that the // channel count / sampling rate combination chosen will be supported by the connected // sink if (isDirectOutput()) { uint32_t samplingRate = UINT_MAX; for (size_t i = 0; i < samplingRates.size(); i ++) { if ((samplingRates[i] < samplingRate) && (samplingRates[i] > 0)) { samplingRate = samplingRates[i]; } } pickedRate = (samplingRate == UINT_MAX) ? 0 : samplingRate; } else { uint32_t maxRate = SAMPLE_RATE_HZ_MAX; // For mixed output and inputs, use max mixer sampling rates. Do not // limit sampling rate otherwise // For inputs, also see checkCompatibleSamplingRate(). if (mType != AUDIO_PORT_TYPE_MIX) { maxRate = UINT_MAX; } // TODO: should mSamplingRates[] be ordered in terms of our preference // and we return the first (and hence most preferred) match? This is of concern if // we want to choose 96kHz over 192kHz for USB driver stability or resource constraints. for (size_t i = 0; i < samplingRates.size(); i ++) { if ((samplingRates[i] > pickedRate) && (samplingRates[i] <= maxRate)) { pickedRate = samplingRates[i]; } } } } void AudioPort::pickChannelMask(audio_channel_mask_t &pickedChannelMask, const ChannelsVector &channelMasks) const { pickedChannelMask = AUDIO_CHANNEL_NONE; // For direct outputs, pick minimum channel count: this helps ensuring that the // channel count / sampling rate combination chosen will be supported by the connected // sink if (isDirectOutput()) { uint32_t channelCount = UINT_MAX; for (size_t i = 0; i < channelMasks.size(); i ++) { uint32_t cnlCount; if (useInputChannelMask()) { cnlCount = audio_channel_count_from_in_mask(channelMasks[i]); } else { cnlCount = audio_channel_count_from_out_mask(channelMasks[i]); } if ((cnlCount < channelCount) && (cnlCount > 0)) { pickedChannelMask = channelMasks[i]; channelCount = cnlCount; } } } else { uint32_t channelCount = 0; uint32_t maxCount = MAX_MIXER_CHANNEL_COUNT; // For mixed output and inputs, use max mixer channel count. Do not // limit channel count otherwise if (mType != AUDIO_PORT_TYPE_MIX) { maxCount = UINT_MAX; } for (size_t i = 0; i < channelMasks.size(); i ++) { uint32_t cnlCount; if (useInputChannelMask()) { cnlCount = audio_channel_count_from_in_mask(channelMasks[i]); } else { cnlCount = audio_channel_count_from_out_mask(channelMasks[i]); } if ((cnlCount > channelCount) && (cnlCount <= maxCount)) { pickedChannelMask = channelMasks[i]; channelCount = cnlCount; } } } } /* format in order of increasing preference */ const audio_format_t AudioPort::sPcmFormatCompareTable[] = { AUDIO_FORMAT_DEFAULT, AUDIO_FORMAT_PCM_16_BIT, AUDIO_FORMAT_PCM_8_24_BIT, AUDIO_FORMAT_PCM_24_BIT_PACKED, AUDIO_FORMAT_PCM_32_BIT, AUDIO_FORMAT_PCM_FLOAT, }; int AudioPort::compareFormats(audio_format_t format1, audio_format_t format2) { // NOTE: AUDIO_FORMAT_INVALID is also considered not PCM and will be compared equal to any // compressed format and better than any PCM format. This is by design of pickFormat() if (!audio_is_linear_pcm(format1)) { if (!audio_is_linear_pcm(format2)) { return 0; } return 1; } if (!audio_is_linear_pcm(format2)) { return -1; } int index1 = -1, index2 = -1; for (size_t i = 0; (i < ARRAY_SIZE(sPcmFormatCompareTable)) && ((index1 == -1) || (index2 == -1)); i ++) { if (sPcmFormatCompareTable[i] == format1) { index1 = i; } if (sPcmFormatCompareTable[i] == format2) { index2 = i; } } // format1 not found => index1 < 0 => format2 > format1 // format2 not found => index2 < 0 => format2 < format1 return index1 - index2; } bool AudioPort::isBetterFormatMatch(audio_format_t newFormat, audio_format_t currentFormat, audio_format_t targetFormat) { if (newFormat == currentFormat) { return false; } if (currentFormat == AUDIO_FORMAT_INVALID) { return true; } if (newFormat == targetFormat) { return true; } int currentDiffBytes = (int)audio_bytes_per_sample(targetFormat) - audio_bytes_per_sample(currentFormat); int newDiffBytes = (int)audio_bytes_per_sample(targetFormat) - audio_bytes_per_sample(newFormat); if (abs(newDiffBytes) < abs(currentDiffBytes)) { return true; } else if (abs(newDiffBytes) == abs(currentDiffBytes)) { return (newDiffBytes >= 0); } return false; } void AudioPort::pickAudioProfile(uint32_t &samplingRate, audio_channel_mask_t &channelMask, audio_format_t &format) const { format = AUDIO_FORMAT_DEFAULT; samplingRate = 0; channelMask = AUDIO_CHANNEL_NONE; // special case for uninitialized dynamic profile if (!mProfiles.hasValidProfile()) { return; } audio_format_t bestFormat = sPcmFormatCompareTable[ARRAY_SIZE(sPcmFormatCompareTable) - 1]; // For mixed output and inputs, use best mixer output format. // Do not limit format otherwise if ((mType != AUDIO_PORT_TYPE_MIX) || isDirectOutput()) { bestFormat = AUDIO_FORMAT_INVALID; } for (size_t i = 0; i < mProfiles.size(); i ++) { if (!mProfiles[i]->isValid()) { continue; } audio_format_t formatToCompare = mProfiles[i]->getFormat(); if ((compareFormats(formatToCompare, format) > 0) && (compareFormats(formatToCompare, bestFormat) <= 0)) { uint32_t pickedSamplingRate = 0; audio_channel_mask_t pickedChannelMask = AUDIO_CHANNEL_NONE; pickChannelMask(pickedChannelMask, mProfiles[i]->getChannels()); pickSamplingRate(pickedSamplingRate, mProfiles[i]->getSampleRates()); if (formatToCompare != AUDIO_FORMAT_DEFAULT && pickedChannelMask != AUDIO_CHANNEL_NONE && pickedSamplingRate != 0) { format = formatToCompare; channelMask = pickedChannelMask; samplingRate = pickedSamplingRate; // TODO: shall we return on the first one or still trying to pick a better Profile? } } } ALOGV("%s Port[nm:%s] profile rate=%d, format=%d, channels=%d", __FUNCTION__, mName.string(), samplingRate, channelMask, format); } status_t AudioPort::checkGain(const struct audio_gain_config *gainConfig, int index) const { if (index < 0 || (size_t)index >= mGains.size()) { return BAD_VALUE; } return mGains[index]->checkConfig(gainConfig); } void AudioPort::dump(int fd, int spaces, bool verbose) const { const size_t SIZE = 256; char buffer[SIZE]; String8 result; if (!mName.isEmpty()) { snprintf(buffer, SIZE, "%*s- name: %s\n", spaces, "", mName.string()); result.append(buffer); write(fd, result.string(), result.size()); } if (verbose) { mProfiles.dump(fd, spaces); if (mGains.size() != 0) { snprintf(buffer, SIZE, "%*s- gains:\n", spaces, ""); result = buffer; write(fd, result.string(), result.size()); for (size_t i = 0; i < mGains.size(); i++) { mGains[i]->dump(fd, spaces + 2, i); } } } } void AudioPort::log(const char* indent) const { ALOGI("%s Port[nm:%s, type:%d, role:%d]", indent, mName.string(), mType, mRole); } // --- AudioPortConfig class implementation AudioPortConfig::AudioPortConfig() { mSamplingRate = 0; mChannelMask = AUDIO_CHANNEL_NONE; mFormat = AUDIO_FORMAT_INVALID; mGain.index = -1; } status_t AudioPortConfig::applyAudioPortConfig(const struct audio_port_config *config, struct audio_port_config *backupConfig) { struct audio_port_config localBackupConfig; status_t status = NO_ERROR; localBackupConfig.config_mask = config->config_mask; toAudioPortConfig(&localBackupConfig); sp audioport = getAudioPort(); if (audioport == 0) { status = NO_INIT; goto exit; } status = audioport->checkExactAudioProfile(config->sample_rate, config->channel_mask, config->format); if (status != NO_ERROR) { goto exit; } if (config->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { mSamplingRate = config->sample_rate; } if (config->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { mChannelMask = config->channel_mask; } if (config->config_mask & AUDIO_PORT_CONFIG_FORMAT) { mFormat = config->format; } if (config->config_mask & AUDIO_PORT_CONFIG_GAIN) { status = audioport->checkGain(&config->gain, config->gain.index); if (status != NO_ERROR) { goto exit; } mGain = config->gain; } exit: if (status != NO_ERROR) { applyAudioPortConfig(&localBackupConfig); } if (backupConfig != NULL) { *backupConfig = localBackupConfig; } return status; } void AudioPortConfig::toAudioPortConfig(struct audio_port_config *dstConfig, const struct audio_port_config *srcConfig) const { if (dstConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { dstConfig->sample_rate = mSamplingRate; if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE)) { dstConfig->sample_rate = srcConfig->sample_rate; } } else { dstConfig->sample_rate = 0; } if (dstConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { dstConfig->channel_mask = mChannelMask; if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK)) { dstConfig->channel_mask = srcConfig->channel_mask; } } else { dstConfig->channel_mask = AUDIO_CHANNEL_NONE; } if (dstConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT) { dstConfig->format = mFormat; if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT)) { dstConfig->format = srcConfig->format; } } else { dstConfig->format = AUDIO_FORMAT_INVALID; } sp audioport = getAudioPort(); if ((dstConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) && audioport != NULL) { dstConfig->gain = mGain; if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) && audioport->checkGain(&srcConfig->gain, srcConfig->gain.index) == OK) { dstConfig->gain = srcConfig->gain; } } else { dstConfig->gain.index = -1; } if (dstConfig->gain.index != -1) { dstConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN; } else { dstConfig->config_mask &= ~AUDIO_PORT_CONFIG_GAIN; } } }; // namespace android