| /* |
| * 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::Ports" |
| //#define LOG_NDEBUG 0 |
| |
| #include "Ports.h" |
| #include "HwModule.h" |
| #include "Gains.h" |
| #include "ConfigParsingUtils.h" |
| #include "audio_policy_conf.h" |
| |
| namespace android { |
| |
| int32_t volatile AudioPort::mNextUniqueId = 1; |
| |
| // --- AudioPort class implementation |
| |
| AudioPort::AudioPort(const String8& name, audio_port_type_t type, |
| audio_port_role_t role, const sp<HwModule>& module) : |
| mName(name), mType(type), mRole(role), mModule(module), mFlags(0), mId(0) |
| { |
| mUseInChannelMask = ((type == AUDIO_PORT_TYPE_DEVICE) && (role == AUDIO_PORT_ROLE_SOURCE)) || |
| ((type == AUDIO_PORT_TYPE_MIX) && (role == AUDIO_PORT_ROLE_SINK)); |
| } |
| |
| void AudioPort::attach(const sp<HwModule>& module) { |
| mId = android_atomic_inc(&mNextUniqueId); |
| mModule = module; |
| } |
| |
| void AudioPort::toAudioPort(struct audio_port *port) const |
| { |
| port->role = mRole; |
| port->type = mType; |
| strlcpy(port->name, mName, AUDIO_PORT_MAX_NAME_LEN); |
| unsigned int i; |
| for (i = 0; i < mSamplingRates.size() && i < AUDIO_PORT_MAX_SAMPLING_RATES; i++) { |
| if (mSamplingRates[i] != 0) { |
| port->sample_rates[i] = mSamplingRates[i]; |
| } |
| } |
| port->num_sample_rates = i; |
| for (i = 0; i < mChannelMasks.size() && i < AUDIO_PORT_MAX_CHANNEL_MASKS; i++) { |
| if (mChannelMasks[i] != 0) { |
| port->channel_masks[i] = mChannelMasks[i]; |
| } |
| } |
| port->num_channel_masks = i; |
| for (i = 0; i < mFormats.size() && i < AUDIO_PORT_MAX_FORMATS; i++) { |
| if (mFormats[i] != 0) { |
| port->formats[i] = mFormats[i]; |
| } |
| } |
| port->num_formats = i; |
| |
| ALOGV("AudioPort::toAudioPort() num gains %zu", mGains.size()); |
| |
| for (i = 0; i < mGains.size() && i < AUDIO_PORT_MAX_GAINS; i++) { |
| port->gains[i] = mGains[i]->mGain; |
| } |
| port->num_gains = i; |
| } |
| |
| void AudioPort::importAudioPort(const sp<AudioPort> port) { |
| for (size_t k = 0 ; k < port->mSamplingRates.size() ; k++) { |
| const uint32_t rate = port->mSamplingRates.itemAt(k); |
| if (rate != 0) { // skip "dynamic" rates |
| bool hasRate = false; |
| for (size_t l = 0 ; l < mSamplingRates.size() ; l++) { |
| if (rate == mSamplingRates.itemAt(l)) { |
| hasRate = true; |
| break; |
| } |
| } |
| if (!hasRate) { // never import a sampling rate twice |
| mSamplingRates.add(rate); |
| } |
| } |
| } |
| for (size_t k = 0 ; k < port->mChannelMasks.size() ; k++) { |
| const audio_channel_mask_t mask = port->mChannelMasks.itemAt(k); |
| if (mask != 0) { // skip "dynamic" masks |
| bool hasMask = false; |
| for (size_t l = 0 ; l < mChannelMasks.size() ; l++) { |
| if (mask == mChannelMasks.itemAt(l)) { |
| hasMask = true; |
| break; |
| } |
| } |
| if (!hasMask) { // never import a channel mask twice |
| mChannelMasks.add(mask); |
| } |
| } |
| } |
| for (size_t k = 0 ; k < port->mFormats.size() ; k++) { |
| const audio_format_t format = port->mFormats.itemAt(k); |
| if (format != 0) { // skip "dynamic" formats |
| bool hasFormat = false; |
| for (size_t l = 0 ; l < mFormats.size() ; l++) { |
| if (format == mFormats.itemAt(l)) { |
| hasFormat = true; |
| break; |
| } |
| } |
| if (!hasFormat) { // never import a channel mask twice |
| mFormats.add(format); |
| } |
| } |
| } |
| for (size_t k = 0 ; k < port->mGains.size() ; k++) { |
| sp<AudioGain> gain = port->mGains.itemAt(k); |
| if (gain != 0) { |
| bool hasGain = false; |
| for (size_t l = 0 ; l < mGains.size() ; l++) { |
| if (gain == mGains.itemAt(l)) { |
| hasGain = true; |
| break; |
| } |
| } |
| if (!hasGain) { // never import a gain twice |
| mGains.add(gain); |
| } |
| } |
| } |
| } |
| |
| void AudioPort::clearCapabilities() { |
| mChannelMasks.clear(); |
| mFormats.clear(); |
| mSamplingRates.clear(); |
| mGains.clear(); |
| } |
| |
| void AudioPort::loadSamplingRates(char *name) |
| { |
| char *str = strtok(name, "|"); |
| |
| // by convention, "0' in the first entry in mSamplingRates indicates the supported sampling |
| // rates should be read from the output stream after it is opened for the first time |
| if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { |
| mSamplingRates.add(0); |
| return; |
| } |
| |
| while (str != NULL) { |
| uint32_t rate = atoi(str); |
| if (rate != 0) { |
| ALOGV("loadSamplingRates() adding rate %d", rate); |
| mSamplingRates.add(rate); |
| } |
| str = strtok(NULL, "|"); |
| } |
| } |
| |
| void AudioPort::loadFormats(char *name) |
| { |
| char *str = strtok(name, "|"); |
| |
| // by convention, "0' in the first entry in mFormats indicates the supported formats |
| // should be read from the output stream after it is opened for the first time |
| if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { |
| mFormats.add(AUDIO_FORMAT_DEFAULT); |
| return; |
| } |
| |
| while (str != NULL) { |
| audio_format_t format = (audio_format_t)ConfigParsingUtils::stringToEnum(sFormatNameToEnumTable, |
| ARRAY_SIZE(sFormatNameToEnumTable), |
| str); |
| if (format != AUDIO_FORMAT_DEFAULT) { |
| mFormats.add(format); |
| } |
| str = strtok(NULL, "|"); |
| } |
| } |
| |
| void AudioPort::loadInChannels(char *name) |
| { |
| const char *str = strtok(name, "|"); |
| |
| ALOGV("loadInChannels() %s", name); |
| |
| if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { |
| mChannelMasks.add(0); |
| return; |
| } |
| |
| while (str != NULL) { |
| audio_channel_mask_t channelMask = |
| (audio_channel_mask_t)ConfigParsingUtils::stringToEnum(sInChannelsNameToEnumTable, |
| ARRAY_SIZE(sInChannelsNameToEnumTable), |
| str); |
| if (channelMask != 0) { |
| ALOGV("loadInChannels() adding channelMask %04x", channelMask); |
| mChannelMasks.add(channelMask); |
| } |
| str = strtok(NULL, "|"); |
| } |
| } |
| |
| void AudioPort::loadOutChannels(char *name) |
| { |
| const char *str = strtok(name, "|"); |
| |
| ALOGV("loadOutChannels() %s", name); |
| |
| // by convention, "0' in the first entry in mChannelMasks indicates the supported channel |
| // masks should be read from the output stream after it is opened for the first time |
| if (str != NULL && strcmp(str, DYNAMIC_VALUE_TAG) == 0) { |
| mChannelMasks.add(0); |
| return; |
| } |
| |
| while (str != NULL) { |
| audio_channel_mask_t channelMask = |
| (audio_channel_mask_t)ConfigParsingUtils::stringToEnum(sOutChannelsNameToEnumTable, |
| ARRAY_SIZE(sOutChannelsNameToEnumTable), |
| str); |
| if (channelMask != 0) { |
| mChannelMasks.add(channelMask); |
| } |
| str = strtok(NULL, "|"); |
| } |
| return; |
| } |
| |
| audio_gain_mode_t AudioPort::loadGainMode(char *name) |
| { |
| const char *str = strtok(name, "|"); |
| |
| ALOGV("loadGainMode() %s", name); |
| audio_gain_mode_t mode = 0; |
| while (str != NULL) { |
| mode |= (audio_gain_mode_t)ConfigParsingUtils::stringToEnum(sGainModeNameToEnumTable, |
| ARRAY_SIZE(sGainModeNameToEnumTable), |
| str); |
| str = strtok(NULL, "|"); |
| } |
| return mode; |
| } |
| |
| void AudioPort::loadGain(cnode *root, int index) |
| { |
| cnode *node = root->first_child; |
| |
| sp<AudioGain> gain = new AudioGain(index, mUseInChannelMask); |
| |
| while (node) { |
| if (strcmp(node->name, GAIN_MODE) == 0) { |
| gain->mGain.mode = loadGainMode((char *)node->value); |
| } else if (strcmp(node->name, GAIN_CHANNELS) == 0) { |
| if (mUseInChannelMask) { |
| gain->mGain.channel_mask = |
| (audio_channel_mask_t)ConfigParsingUtils::stringToEnum(sInChannelsNameToEnumTable, |
| ARRAY_SIZE(sInChannelsNameToEnumTable), |
| (char *)node->value); |
| } else { |
| gain->mGain.channel_mask = |
| (audio_channel_mask_t)ConfigParsingUtils::stringToEnum(sOutChannelsNameToEnumTable, |
| ARRAY_SIZE(sOutChannelsNameToEnumTable), |
| (char *)node->value); |
| } |
| } else if (strcmp(node->name, GAIN_MIN_VALUE) == 0) { |
| gain->mGain.min_value = atoi((char *)node->value); |
| } else if (strcmp(node->name, GAIN_MAX_VALUE) == 0) { |
| gain->mGain.max_value = atoi((char *)node->value); |
| } else if (strcmp(node->name, GAIN_DEFAULT_VALUE) == 0) { |
| gain->mGain.default_value = atoi((char *)node->value); |
| } else if (strcmp(node->name, GAIN_STEP_VALUE) == 0) { |
| gain->mGain.step_value = atoi((char *)node->value); |
| } else if (strcmp(node->name, GAIN_MIN_RAMP_MS) == 0) { |
| gain->mGain.min_ramp_ms = atoi((char *)node->value); |
| } else if (strcmp(node->name, GAIN_MAX_RAMP_MS) == 0) { |
| gain->mGain.max_ramp_ms = atoi((char *)node->value); |
| } |
| node = node->next; |
| } |
| |
| ALOGV("loadGain() adding new gain mode %08x channel mask %08x min mB %d max mB %d", |
| gain->mGain.mode, gain->mGain.channel_mask, gain->mGain.min_value, gain->mGain.max_value); |
| |
| if (gain->mGain.mode == 0) { |
| return; |
| } |
| mGains.add(gain); |
| } |
| |
| void AudioPort::loadGains(cnode *root) |
| { |
| cnode *node = root->first_child; |
| int index = 0; |
| while (node) { |
| ALOGV("loadGains() loading gain %s", node->name); |
| loadGain(node, index++); |
| node = node->next; |
| } |
| } |
| |
| status_t AudioPort::checkExactSamplingRate(uint32_t samplingRate) const |
| { |
| if (mSamplingRates.isEmpty()) { |
| return NO_ERROR; |
| } |
| |
| for (size_t i = 0; i < mSamplingRates.size(); i ++) { |
| if (mSamplingRates[i] == samplingRate) { |
| return NO_ERROR; |
| } |
| } |
| return BAD_VALUE; |
| } |
| |
| status_t AudioPort::checkCompatibleSamplingRate(uint32_t samplingRate, |
| uint32_t *updatedSamplingRate) const |
| { |
| if (mSamplingRates.isEmpty()) { |
| return NO_ERROR; |
| } |
| |
| // Search for the closest supported sampling rate that is above (preferred) |
| // or below (acceptable) the desired sampling rate, within a permitted ratio. |
| // The sampling rates do not need to be sorted in ascending order. |
| ssize_t maxBelow = -1; |
| ssize_t minAbove = -1; |
| uint32_t candidate; |
| for (size_t i = 0; i < mSamplingRates.size(); i++) { |
| candidate = mSamplingRates[i]; |
| if (candidate == samplingRate) { |
| if (updatedSamplingRate != NULL) { |
| *updatedSamplingRate = candidate; |
| } |
| return NO_ERROR; |
| } |
| // candidate < desired |
| if (candidate < samplingRate) { |
| if (maxBelow < 0 || candidate > mSamplingRates[maxBelow]) { |
| maxBelow = i; |
| } |
| // candidate > desired |
| } else { |
| if (minAbove < 0 || candidate < mSamplingRates[minAbove]) { |
| minAbove = i; |
| } |
| } |
| } |
| // This uses hard-coded knowledge about AudioFlinger resampling ratios. |
| // TODO Move these assumptions out. |
| static const uint32_t kMaxDownSampleRatio = 6; // beyond this aliasing occurs |
| static const uint32_t kMaxUpSampleRatio = 256; // beyond this sample rate inaccuracies occur |
| // due to approximation by an int32_t of the |
| // phase increments |
| // Prefer to down-sample from a higher sampling rate, as we get the desired frequency spectrum. |
| if (minAbove >= 0) { |
| candidate = mSamplingRates[minAbove]; |
| if (candidate / kMaxDownSampleRatio <= samplingRate) { |
| if (updatedSamplingRate != NULL) { |
| *updatedSamplingRate = candidate; |
| } |
| return NO_ERROR; |
| } |
| } |
| // But if we have to up-sample from a lower sampling rate, that's OK. |
| if (maxBelow >= 0) { |
| candidate = mSamplingRates[maxBelow]; |
| if (candidate * kMaxUpSampleRatio >= samplingRate) { |
| if (updatedSamplingRate != NULL) { |
| *updatedSamplingRate = candidate; |
| } |
| return NO_ERROR; |
| } |
| } |
| // leave updatedSamplingRate unmodified |
| return BAD_VALUE; |
| } |
| |
| status_t AudioPort::checkExactChannelMask(audio_channel_mask_t channelMask) const |
| { |
| if (mChannelMasks.isEmpty()) { |
| return NO_ERROR; |
| } |
| |
| for (size_t i = 0; i < mChannelMasks.size(); i++) { |
| if (mChannelMasks[i] == channelMask) { |
| return NO_ERROR; |
| } |
| } |
| return BAD_VALUE; |
| } |
| |
| status_t AudioPort::checkCompatibleChannelMask(audio_channel_mask_t channelMask) |
| const |
| { |
| if (mChannelMasks.isEmpty()) { |
| return NO_ERROR; |
| } |
| |
| const bool isRecordThread = mType == AUDIO_PORT_TYPE_MIX && mRole == AUDIO_PORT_ROLE_SINK; |
| for (size_t i = 0; i < mChannelMasks.size(); i ++) { |
| // FIXME Does not handle multi-channel automatic conversions yet |
| audio_channel_mask_t supported = mChannelMasks[i]; |
| if (supported == channelMask) { |
| return NO_ERROR; |
| } |
| if (isRecordThread) { |
| // This uses hard-coded knowledge that AudioFlinger can silently down-mix and up-mix. |
| // FIXME Abstract this out to a table. |
| if (((supported == AUDIO_CHANNEL_IN_FRONT_BACK || supported == AUDIO_CHANNEL_IN_STEREO) |
| && channelMask == AUDIO_CHANNEL_IN_MONO) || |
| (supported == AUDIO_CHANNEL_IN_MONO && (channelMask == AUDIO_CHANNEL_IN_FRONT_BACK |
| || channelMask == AUDIO_CHANNEL_IN_STEREO))) { |
| return NO_ERROR; |
| } |
| } |
| } |
| return BAD_VALUE; |
| } |
| |
| status_t AudioPort::checkFormat(audio_format_t format) const |
| { |
| if (mFormats.isEmpty()) { |
| return NO_ERROR; |
| } |
| |
| for (size_t i = 0; i < mFormats.size(); i ++) { |
| if (mFormats[i] == format) { |
| return NO_ERROR; |
| } |
| } |
| return BAD_VALUE; |
| } |
| |
| |
| uint32_t AudioPort::pickSamplingRate() const |
| { |
| // special case for uninitialized dynamic profile |
| if (mSamplingRates.size() == 1 && mSamplingRates[0] == 0) { |
| return 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 ((mType == AUDIO_PORT_TYPE_MIX) && (mRole == AUDIO_PORT_ROLE_SOURCE) && |
| (mFlags & (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD))) { |
| uint32_t samplingRate = UINT_MAX; |
| for (size_t i = 0; i < mSamplingRates.size(); i ++) { |
| if ((mSamplingRates[i] < samplingRate) && (mSamplingRates[i] > 0)) { |
| samplingRate = mSamplingRates[i]; |
| } |
| } |
| return (samplingRate == UINT_MAX) ? 0 : samplingRate; |
| } |
| |
| uint32_t samplingRate = 0; |
| uint32_t maxRate = MAX_MIXER_SAMPLING_RATE; |
| |
| // For mixed output and inputs, use max mixer sampling rates. Do not |
| // limit sampling rate otherwise |
| if (mType != AUDIO_PORT_TYPE_MIX) { |
| maxRate = UINT_MAX; |
| } |
| for (size_t i = 0; i < mSamplingRates.size(); i ++) { |
| if ((mSamplingRates[i] > samplingRate) && (mSamplingRates[i] <= maxRate)) { |
| samplingRate = mSamplingRates[i]; |
| } |
| } |
| return samplingRate; |
| } |
| |
| audio_channel_mask_t AudioPort::pickChannelMask() const |
| { |
| // special case for uninitialized dynamic profile |
| if (mChannelMasks.size() == 1 && mChannelMasks[0] == 0) { |
| return AUDIO_CHANNEL_NONE; |
| } |
| audio_channel_mask_t channelMask = 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 ((mType == AUDIO_PORT_TYPE_MIX) && (mRole == AUDIO_PORT_ROLE_SOURCE) && |
| (mFlags & (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD))) { |
| uint32_t channelCount = UINT_MAX; |
| for (size_t i = 0; i < mChannelMasks.size(); i ++) { |
| uint32_t cnlCount; |
| if (mUseInChannelMask) { |
| cnlCount = audio_channel_count_from_in_mask(mChannelMasks[i]); |
| } else { |
| cnlCount = audio_channel_count_from_out_mask(mChannelMasks[i]); |
| } |
| if ((cnlCount < channelCount) && (cnlCount > 0)) { |
| channelMask = mChannelMasks[i]; |
| channelCount = cnlCount; |
| } |
| } |
| return channelMask; |
| } |
| |
| 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 < mChannelMasks.size(); i ++) { |
| uint32_t cnlCount; |
| if (mUseInChannelMask) { |
| cnlCount = audio_channel_count_from_in_mask(mChannelMasks[i]); |
| } else { |
| cnlCount = audio_channel_count_from_out_mask(mChannelMasks[i]); |
| } |
| if ((cnlCount > channelCount) && (cnlCount <= maxCount)) { |
| channelMask = mChannelMasks[i]; |
| channelCount = cnlCount; |
| } |
| } |
| return channelMask; |
| } |
| |
| /* 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; |
| } |
| |
| audio_format_t AudioPort::pickFormat() const |
| { |
| // special case for uninitialized dynamic profile |
| if (mFormats.size() == 1 && mFormats[0] == 0) { |
| return AUDIO_FORMAT_DEFAULT; |
| } |
| |
| audio_format_t format = AUDIO_FORMAT_DEFAULT; |
| audio_format_t bestFormat = |
| AudioPort::sPcmFormatCompareTable[ |
| ARRAY_SIZE(AudioPort::sPcmFormatCompareTable) - 1]; |
| // For mixed output and inputs, use best mixer output format. Do not |
| // limit format otherwise |
| if ((mType != AUDIO_PORT_TYPE_MIX) || |
| ((mRole == AUDIO_PORT_ROLE_SOURCE) && |
| (((mFlags & (AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)) != 0)))) { |
| bestFormat = AUDIO_FORMAT_INVALID; |
| } |
| |
| for (size_t i = 0; i < mFormats.size(); i ++) { |
| if ((compareFormats(mFormats[i], format) > 0) && |
| (compareFormats(mFormats[i], bestFormat) <= 0)) { |
| format = mFormats[i]; |
| } |
| } |
| return 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) const |
| { |
| const size_t SIZE = 256; |
| char buffer[SIZE]; |
| String8 result; |
| |
| if (mName.size() != 0) { |
| snprintf(buffer, SIZE, "%*s- name: %s\n", spaces, "", mName.string()); |
| result.append(buffer); |
| } |
| |
| if (mSamplingRates.size() != 0) { |
| snprintf(buffer, SIZE, "%*s- sampling rates: ", spaces, ""); |
| result.append(buffer); |
| for (size_t i = 0; i < mSamplingRates.size(); i++) { |
| if (i == 0 && mSamplingRates[i] == 0) { |
| snprintf(buffer, SIZE, "Dynamic"); |
| } else { |
| snprintf(buffer, SIZE, "%d", mSamplingRates[i]); |
| } |
| result.append(buffer); |
| result.append(i == (mSamplingRates.size() - 1) ? "" : ", "); |
| } |
| result.append("\n"); |
| } |
| |
| if (mChannelMasks.size() != 0) { |
| snprintf(buffer, SIZE, "%*s- channel masks: ", spaces, ""); |
| result.append(buffer); |
| for (size_t i = 0; i < mChannelMasks.size(); i++) { |
| ALOGV("AudioPort::dump mChannelMasks %zu %08x", i, mChannelMasks[i]); |
| |
| if (i == 0 && mChannelMasks[i] == 0) { |
| snprintf(buffer, SIZE, "Dynamic"); |
| } else { |
| snprintf(buffer, SIZE, "0x%04x", mChannelMasks[i]); |
| } |
| result.append(buffer); |
| result.append(i == (mChannelMasks.size() - 1) ? "" : ", "); |
| } |
| result.append("\n"); |
| } |
| |
| if (mFormats.size() != 0) { |
| snprintf(buffer, SIZE, "%*s- formats: ", spaces, ""); |
| result.append(buffer); |
| for (size_t i = 0; i < mFormats.size(); i++) { |
| const char *formatStr = ConfigParsingUtils::enumToString(sFormatNameToEnumTable, |
| ARRAY_SIZE(sFormatNameToEnumTable), |
| mFormats[i]); |
| if (i == 0 && strcmp(formatStr, "") == 0) { |
| snprintf(buffer, SIZE, "Dynamic"); |
| } else { |
| snprintf(buffer, SIZE, "%s", formatStr); |
| } |
| result.append(buffer); |
| result.append(i == (mFormats.size() - 1) ? "" : ", "); |
| } |
| result.append("\n"); |
| } |
| write(fd, result.string(), result.size()); |
| if (mGains.size() != 0) { |
| snprintf(buffer, SIZE, "%*s- gains:\n", spaces, ""); |
| write(fd, buffer, strlen(buffer) + 1); |
| result.append(buffer); |
| for (size_t i = 0; i < mGains.size(); i++) { |
| mGains[i]->dump(fd, spaces + 2, i); |
| } |
| } |
| } |
| |
| |
| // --- 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> audioport = getAudioPort(); |
| if (audioport == 0) { |
| status = NO_INIT; |
| goto exit; |
| } |
| if (config->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) { |
| status = audioport->checkExactSamplingRate(config->sample_rate); |
| if (status != NO_ERROR) { |
| goto exit; |
| } |
| mSamplingRate = config->sample_rate; |
| } |
| if (config->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) { |
| status = audioport->checkExactChannelMask(config->channel_mask); |
| if (status != NO_ERROR) { |
| goto exit; |
| } |
| mChannelMask = config->channel_mask; |
| } |
| if (config->config_mask & AUDIO_PORT_CONFIG_FORMAT) { |
| status = audioport->checkFormat(config->format); |
| if (status != NO_ERROR) { |
| goto exit; |
| } |
| 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; |
| } |
| if (dstConfig->config_mask & AUDIO_PORT_CONFIG_GAIN) { |
| dstConfig->gain = mGain; |
| if ((srcConfig != NULL) && (srcConfig->config_mask & AUDIO_PORT_CONFIG_GAIN)) { |
| 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; |
| } |
| } |
| |
| |
| // --- AudioPatch class implementation |
| |
| AudioPatch::AudioPatch(audio_patch_handle_t handle, |
| const struct audio_patch *patch, uid_t uid) : |
| mHandle(handle), mPatch(*patch), mUid(uid), mAfPatchHandle(0) |
| {} |
| |
| status_t AudioPatch::dump(int fd, int spaces, int index) const |
| { |
| const size_t SIZE = 256; |
| char buffer[SIZE]; |
| String8 result; |
| |
| snprintf(buffer, SIZE, "%*sAudio patch %d:\n", spaces, "", index+1); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "%*s- handle: %2d\n", spaces, "", mHandle); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "%*s- audio flinger handle: %2d\n", spaces, "", mAfPatchHandle); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "%*s- owner uid: %2d\n", spaces, "", mUid); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "%*s- %d sources:\n", spaces, "", mPatch.num_sources); |
| result.append(buffer); |
| for (size_t i = 0; i < mPatch.num_sources; i++) { |
| if (mPatch.sources[i].type == AUDIO_PORT_TYPE_DEVICE) { |
| snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "", |
| mPatch.sources[i].id, ConfigParsingUtils::enumToString(sDeviceNameToEnumTable, |
| ARRAY_SIZE(sDeviceNameToEnumTable), |
| mPatch.sources[i].ext.device.type)); |
| } else { |
| snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "", |
| mPatch.sources[i].id, mPatch.sources[i].ext.mix.handle); |
| } |
| result.append(buffer); |
| } |
| snprintf(buffer, SIZE, "%*s- %d sinks:\n", spaces, "", mPatch.num_sinks); |
| result.append(buffer); |
| for (size_t i = 0; i < mPatch.num_sinks; i++) { |
| if (mPatch.sinks[i].type == AUDIO_PORT_TYPE_DEVICE) { |
| snprintf(buffer, SIZE, "%*s- Device ID %d %s\n", spaces + 2, "", |
| mPatch.sinks[i].id, ConfigParsingUtils::enumToString(sDeviceNameToEnumTable, |
| ARRAY_SIZE(sDeviceNameToEnumTable), |
| mPatch.sinks[i].ext.device.type)); |
| } else { |
| snprintf(buffer, SIZE, "%*s- Mix ID %d I/O handle %d\n", spaces + 2, "", |
| mPatch.sinks[i].id, mPatch.sinks[i].ext.mix.handle); |
| } |
| result.append(buffer); |
| } |
| |
| write(fd, result.string(), result.size()); |
| return NO_ERROR; |
| } |
| |
| |
| }; // namespace android |