android10 audio音量曲线

一、音量曲线的加载流程

xref: /frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp

复制代码
AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface)
        : AudioPolicyManager(clientInterface, false /*forTesting*/)
{
    loadConfig();
    initialize();
}

在AudioPolicyManager.cpp的构造函数里面会执行loadConfig和initialize两个方法。

复制代码
void AudioPolicyManager::loadConfig() {
    if (deserializeAudioPolicyXmlConfig(getConfig()) != NO_ERROR) {
        ALOGE("could not load audio policy configuration file, setting defaults");
        getConfig().setDefault();
    }
}

在loadConfig方法中调用deserializeAudioPolicyXmlConfig方法去解析配置文件

复制代码
static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) {
    char audioPolicyXmlConfigFile[AUDIO_POLICY_XML_CONFIG_FILE_PATH_MAX_LENGTH];
    std::vector<const char*> fileNames;
    status_t ret;

    if (property_get_bool("ro.bluetooth.a2dp_offload.supported", false)) {
        if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.disabled", false) &&
            property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) {
            // Both BluetoothAudio@2.0 and BluetoothA2dp@1.0 (Offlaod) are disabled, and uses
            // the legacy hardware module for A2DP and hearing aid.
            fileNames.push_back(AUDIO_POLICY_BLUETOOTH_LEGACY_HAL_XML_CONFIG_FILE_NAME);
        } else if (property_get_bool("persist.bluetooth.a2dp_offload.disabled", false)) {
            // A2DP offload supported but disabled: try to use special XML file
            fileNames.push_back(AUDIO_POLICY_A2DP_OFFLOAD_DISABLED_XML_CONFIG_FILE_NAME);
        }
    } else if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.disabled", false)) {
        fileNames.push_back(AUDIO_POLICY_BLUETOOTH_LEGACY_HAL_XML_CONFIG_FILE_NAME);
    }
    fileNames.push_back(AUDIO_POLICY_XML_CONFIG_FILE_NAME);

    for (const char* fileName : fileNames) {
        for (int i = 0; i < kConfigLocationListSize; i++) {
            snprintf(audioPolicyXmlConfigFile, sizeof(audioPolicyXmlConfigFile),
                     "%s/%s", kConfigLocationList[i], fileName);
            ret = deserializeAudioPolicyFile(audioPolicyXmlConfigFile, &config);
            if (ret == NO_ERROR) {
                config.setSource(audioPolicyXmlConfigFile);
                return ret;
            }
        }
    }
    return ret;
}

AUDIO_POLICY_XML_CONFIG_FILE_NAME文件就是audio_policy_configuration.xml

复制代码
#define AUDIO_POLICY_XML_CONFIG_FILE_NAME "audio_policy_configuration.xml"

xref: /frameworks/av/services/audiopolicy/config/audio_policy_configuration.xml

复制代码
<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
   
   ............................................................................

    <xi:include href="audio_policy_volumes.xml"/>
    <xi:include href="default_volume_tables.xml"/>

    ............................................................................

</audioPolicyConfiguration>

在audio_policy_configuration.xml文件里面通过include包含了audio_policy_volumes.xml和default_volume_tables.xml

xref: /frameworks/av/services/audiopolicy/config/audio_policy_volumes.xml

复制代码
<volume stream="AUDIO_STREAM_RING" deviceCategory="DEVICE_CATEGORY_EARPIECE"
                                   ref="DEFAULT_DEVICE_CATEGORY_EARPIECE_VOLUME_CURVE"/>
<volume stream="AUDIO_STREAM_RING" deviceCategory="DEVICE_CATEGORY_EXT_MEDIA"
                                   ref="DEFAULT_DEVICE_CATEGORY_EXT_MEDIA_VOLUME_CURVE"/>
<volume stream="AUDIO_STREAM_RING" deviceCategory="DEVICE_CATEGORY_HEARING_AID"
                                   ref="DEFAULT_HEARING_AID_VOLUME_CURVE"/>
<volume stream="AUDIO_STREAM_MUSIC" deviceCategory="DEVICE_CATEGORY_HEADSET"
                                    ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
<volume stream="AUDIO_STREAM_MUSIC" deviceCategory="DEVICE_CATEGORY_SPEAKER"
                                    ref="DEFAULT_DEVICE_CATEGORY_SPEAKER_VOLUME_CURVE"/>
<volume stream="AUDIO_STREAM_MUSIC" deviceCategory="DEVICE_CATEGORY_EARPIECE"
                                    ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
<volume stream="AUDIO_STREAM_MUSIC" deviceCategory="DEVICE_CATEGORY_EXT_MEDIA"
                                    ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
<volume stream="AUDIO_STREAM_MUSIC" deviceCategory="DEVICE_CATEGORY_HEARING_AID"
                                    ref="DEFAULT_HEARING_AID_VOLUME_CURVE"/>
<volume stream="AUDIO_STREAM_ALARM" deviceCategory="DEVICE_CATEGORY_HEADSET"
                                    ref="DEFAULT_NON_MUTABLE_HEADSET_VOLUME_CURVE"/>
<volume stream="AUDIO_STREAM_ALARM" deviceCategory="DEVICE_CATEGORY_SPEAKER">
    <point>0,-2970</point>
    <point>33,-2010</point>
    <point>66,-1020</point>
    <point>100,0</point>
</volume>

在audio_policy_volumes.xml中,规定了音频流类型(stream)、输出设备(deviceCategory)和音量曲线(ref)的关系,在default_volume_tables.xml中规定了具体的音量曲线的值

xref: /frameworks/av/services/audiopolicy/config/default_volume_tables.xml

复制代码
<reference name="DEFAULT_DEVICE_CATEGORY_EARPIECE_VOLUME_CURVE">
<!--Default Volume Curve -->
    <point>1,-4950</point>
    <point>33,-3350</point>
    <point>66,-1700</point>
    <point>100,0</point>
</reference>

例如DEFAULT_DEVICE_CATEGORY_EARPIECE_VOLUME_CURVE曲线上的(index、db)值。定义的index范围是1到100, 而db的范围为-4950 到0。音量等级分成4段为100、66、33、1就是音量百分比;0,-1700,-3350,-4950就是对应百分比时衰减的音量,代表衰减-17db,-33.50db,-49.50db。

如果想要修改音频曲线,只要修改default_volume_tables.xml文件就行了。

二、音量调节流程

xref: /frameworks/av/services/audiopolicy/managerdefault/AudioPolicyManager.cpp

复制代码
status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream,
                                                  int index,
                                                  audio_devices_t device)
{
    auto attributes = mEngine->getAttributesForStreamType(stream);
    ALOGV("%s: stream %s attributes=%s", __func__,
          toString(stream).c_str(), toString(attributes).c_str());
    return setVolumeIndexForAttributes(attributes, index, device);
}

调节音量的大小最终会调用到AudioPolicyManager的setStreamVolumeIndex方法。在setStreamVolumeIndex方法中会调用setVolumeIndexForAttributes方法

复制代码
status_t AudioPolicyManager::setVolumeIndexForAttributes(const audio_attributes_t &attributes,
                                                         int index,
                                                         audio_devices_t device)
{
    // Get Volume group matching the Audio Attributes
    auto group = mEngine->getVolumeGroupForAttributes(attributes);
    if (group == VOLUME_GROUP_NONE) {
        ALOGD("%s: no group matching with %s", __FUNCTION__, toString(attributes).c_str());
        return BAD_VALUE;
    }
    ALOGV("%s: group %d matching with %s", __FUNCTION__, group, toString(attributes).c_str());
    status_t status = NO_ERROR;
    IVolumeCurves &curves = getVolumeCurves(attributes);
    VolumeSource vs = toVolumeSource(group);
    product_strategy_t strategy = mEngine->getProductStrategyForAttributes(attributes);

    status = setVolumeCurveIndex(index, device, curves);
    if (status != NO_ERROR) {
        ALOGE("%s failed to set curve index for group %d device 0x%X", __func__, group, device);
        return status;
    }

    audio_devices_t curSrcDevice;
    auto curCurvAttrs = curves.getAttributes();
    if (!curCurvAttrs.empty() && curCurvAttrs.front() != defaultAttr) {
        auto attr = curCurvAttrs.front();
        curSrcDevice = mEngine->getOutputDevicesForAttributes(attr, nullptr, false).types();
    } else if (!curves.getStreamTypes().empty()) {
        auto stream = curves.getStreamTypes().front();
        curSrcDevice = mEngine->getOutputDevicesForStream(stream, false).types();
    } else {
        ALOGE("%s: Invalid src %d: no valid attributes nor stream",__func__, vs);
        return BAD_VALUE;
    }
    curSrcDevice = Volume::getDeviceForVolume(curSrcDevice);

    // update volume on all outputs and streams matching the following:
    // - The requested stream (or a stream matching for volume control) is active on the output
    // - The device (or devices) selected by the engine for this stream includes
    // the requested device
    // - For non default requested device, currently selected device on the output is either the
    // requested device or one of the devices selected by the engine for this stream
    // - For default requested device (AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME), apply volume only if
    // no specific device volume value exists for currently selected device.
    for (size_t i = 0; i < mOutputs.size(); i++) {
        sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
        audio_devices_t curDevice = desc->devices().types();

        if (curDevice & AUDIO_DEVICE_OUT_SPEAKER_SAFE) {
            curDevice |= AUDIO_DEVICE_OUT_SPEAKER;
            curDevice &= ~AUDIO_DEVICE_OUT_SPEAKER_SAFE;
        }

        // Inter / intra volume group priority management: Loop on strategies arranged by priority
        // If a higher priority strategy is active, and the output is routed to a device with a
        // HW Gain management, do not change the volume
        bool applyVolume = false;
        if (desc->useHwGain()) {
            if (!(desc->isActive(toVolumeSource(group)) || isInCall())) {
                continue;
            }
            for (const auto &productStrategy : mEngine->getOrderedProductStrategies()) {
                auto activeClients = desc->clientsList(true /*activeOnly*/, productStrategy,
                                                       false /*preferredDevice*/);
                if (activeClients.empty()) {
                    continue;
                }
                bool isPreempted = false;
                bool isHigherPriority = productStrategy < strategy;
                for (const auto &client : activeClients) {
                    if (isHigherPriority && (client->volumeSource() != vs)) {
                        ALOGV("%s: Strategy=%d (\nrequester:\n"
                              " group %d, volumeGroup=%d attributes=%s)\n"
                              " higher priority source active:\n"
                              " volumeGroup=%d attributes=%s) \n"
                              " on output %zu, bailing out", __func__, productStrategy,
                              group, group, toString(attributes).c_str(),
                              client->volumeSource(), toString(client->attributes()).c_str(), i);
                        applyVolume = false;
                        isPreempted = true;
                        break;
                    }
                    // However, continue for loop to ensure no higher prio clients running on output
                    if (client->volumeSource() == vs) {
                        applyVolume = true;
                    }
                }
                if (isPreempted || applyVolume) {
                    break;
                }
            }
            if (!applyVolume) {
                continue; // next output
            }
            status_t volStatus = checkAndSetVolume(curves, vs, index, desc, curDevice,
                                                   (vs == toVolumeSource(AUDIO_STREAM_SYSTEM)?
                                                        TOUCH_SOUND_FIXED_DELAY_MS : 0));
            if (volStatus != NO_ERROR) {
                status = volStatus;
            }
            continue;
        }
        if (!(desc->isActive(vs) || isInCall())) {
            continue;
        }
        if ((device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) && ((curDevice & device) == 0)) {
            continue;
        }
        if (device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) {
            curSrcDevice |= device;
            applyVolume = (Volume::getDeviceForVolume(curDevice) & curSrcDevice) != 0;
        } else {
            applyVolume = !curves.hasVolumeIndexForDevice(curSrcDevice);
        }
        if (applyVolume) {
            //FIXME: workaround for truncated touch sounds
            // delayed volume change for system stream to be removed when the problem is
            // handled by system UI
            status_t volStatus = checkAndSetVolume(
                        curves, vs, index, desc, curDevice,
                        ((vs == toVolumeSource(AUDIO_STREAM_SYSTEM))?
                             TOUCH_SOUND_FIXED_DELAY_MS : 0));
            if (volStatus != NO_ERROR) {
                status = volStatus;
            }
        }
    }
    mpClientInterface->onAudioVolumeGroupChanged(group, 0 /*flags*/);
    return status;
}

在setVolumeIndexForAttributes方法中会调用checkAndSetVolume方法

复制代码
status_t AudioPolicyManager::checkAndSetVolume(IVolumeCurves &curves,
                                               VolumeSource volumeSource,
                                               int index,
                                               const sp<AudioOutputDescriptor>& outputDesc,
                                               audio_devices_t device,
                                               int delayMs,
                                               bool force)
{
    // do not change actual attributes volume if the attributes is muted
    if (outputDesc->isMuted(volumeSource)) {
        ALOGVV("%s: volume source %d muted count %d active=%d", __func__, volumeSource,
               outputDesc->getMuteCount(volumeSource), outputDesc->isActive(volumeSource));
        return NO_ERROR;
    }
    VolumeSource callVolSrc = toVolumeSource(AUDIO_STREAM_VOICE_CALL);
    VolumeSource btScoVolSrc = toVolumeSource(AUDIO_STREAM_BLUETOOTH_SCO);
    bool isVoiceVolSrc = callVolSrc == volumeSource;
    bool isBtScoVolSrc = btScoVolSrc == volumeSource;

    audio_policy_forced_cfg_t forceUseForComm =
            mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_COMMUNICATION);
    // do not change in call volume if bluetooth is connected and vice versa
    // if sco and call follow same curves, bypass forceUseForComm
    if ((callVolSrc != btScoVolSrc) &&
            ((isVoiceVolSrc && forceUseForComm == AUDIO_POLICY_FORCE_BT_SCO) ||
             (isBtScoVolSrc && forceUseForComm != AUDIO_POLICY_FORCE_BT_SCO))) {
        ALOGV("%s cannot set volume group %d volume with force use = %d for comm", __func__,
             volumeSource, forceUseForComm);
        return INVALID_OPERATION;
    }
    if (device == AUDIO_DEVICE_NONE) {
        device = outputDesc->devices().types();
    }

    float volumeDb = computeVolume(curves, volumeSource, index, device);
    if (outputDesc->isFixedVolume(device) ||
            // Force VoIP volume to max for bluetooth SCO
            ((isVoiceVolSrc || isBtScoVolSrc) && (device & AUDIO_DEVICE_OUT_ALL_SCO) != 0)) {
        volumeDb = 0.0f;
    }
    outputDesc->setVolume(volumeDb, volumeSource, curves.getStreamTypes(), device, delayMs, force);

    if (isVoiceVolSrc || isBtScoVolSrc) {
        float voiceVolume;
        // Force voice volume to max or mute for Bluetooth SCO as other attenuations are managed by the headset
        if (isVoiceVolSrc) {
            voiceVolume = (float)index/(float)curves.getVolumeIndexMax();
        } else {
            voiceVolume = index == 0 ? 0.0 : 1.0;
        }
        if (voiceVolume != mLastVoiceVolume) {
            mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
            mLastVoiceVolume = voiceVolume;
        }
    }
    return NO_ERROR;
}

在checkAndSetVolume方法中调用computeVolume方法

复制代码
float AudioPolicyManager::computeVolume(IVolumeCurves &curves,
                                        VolumeSource volumeSource,
                                        int index,
                                        audio_devices_t device)
{
    float volumeDb = curves.volIndexToDb(Volume::getDeviceCategory(device), index);

    // handle the case of accessibility active while a ringtone is playing: if the ringtone is much
    // louder than the accessibility prompt, the prompt cannot be heard, thus masking the touch
    // exploration of the dialer UI. In this situation, bring the accessibility volume closer to
    // the ringtone volume
    const auto callVolumeSrc = toVolumeSource(AUDIO_STREAM_VOICE_CALL);
    const auto ringVolumeSrc = toVolumeSource(AUDIO_STREAM_RING);
    const auto musicVolumeSrc = toVolumeSource(AUDIO_STREAM_MUSIC);
    const auto alarmVolumeSrc = toVolumeSource(AUDIO_STREAM_ALARM);

    if (volumeSource == toVolumeSource(AUDIO_STREAM_ACCESSIBILITY)
            && (AUDIO_MODE_RINGTONE == mEngine->getPhoneState()) &&
            mOutputs.isActive(ringVolumeSrc, 0)) {
        auto &ringCurves = getVolumeCurves(AUDIO_STREAM_RING);
        const float ringVolumeDb = computeVolume(ringCurves, ringVolumeSrc, index, device);
        return ringVolumeDb - 4 > volumeDb ? ringVolumeDb - 4 : volumeDb;
    }

    // in-call: always cap volume by voice volume + some low headroom
    if ((volumeSource != callVolumeSrc && (isInCall() ||
                                           mOutputs.isActiveLocally(callVolumeSrc))) &&
            (volumeSource == toVolumeSource(AUDIO_STREAM_SYSTEM) ||
             volumeSource == ringVolumeSrc || volumeSource == musicVolumeSrc ||
             volumeSource == alarmVolumeSrc ||
             volumeSource == toVolumeSource(AUDIO_STREAM_NOTIFICATION) ||
             volumeSource == toVolumeSource(AUDIO_STREAM_ENFORCED_AUDIBLE) ||
             volumeSource == toVolumeSource(AUDIO_STREAM_DTMF) ||
             volumeSource == toVolumeSource(AUDIO_STREAM_ACCESSIBILITY))) {
        auto &voiceCurves = getVolumeCurves(callVolumeSrc);
        int voiceVolumeIndex = voiceCurves.getVolumeIndex(device);
        const float maxVoiceVolDb =
                computeVolume(voiceCurves, callVolumeSrc, voiceVolumeIndex, device)
                + IN_CALL_EARPIECE_HEADROOM_DB;
        // FIXME: Workaround for call screening applications until a proper audio mode is defined
        // to support this scenario : Exempt the RING stream from the audio cap if the audio was
        // programmatically muted.
        // VOICE_CALL stream has minVolumeIndex > 0 : Users cannot set the volume of voice calls to
        // 0. We don't want to cap volume when the system has programmatically muted the voice call
        // stream. See setVolumeCurveIndex() for more information.
        bool exemptFromCapping = (volumeSource == ringVolumeSrc) && (voiceVolumeIndex == 0);
        ALOGV_IF(exemptFromCapping, "%s volume source %d at vol=%f not capped", __func__,
                 volumeSource, volumeDb);
        if ((volumeDb > maxVoiceVolDb) && !exemptFromCapping) {
            ALOGV("%s volume source %d at vol=%f overriden by volume group %d at vol=%f", __func__,
                  volumeSource, volumeDb, callVolumeSrc, maxVoiceVolDb);
            volumeDb = maxVoiceVolDb;
        }
    }
    // if a headset is connected, apply the following rules to ring tones and notifications
    // to avoid sound level bursts in user's ears:
    // - always attenuate notifications volume by 6dB
    // - attenuate ring tones volume by 6dB unless music is not playing and
    // speaker is part of the select devices
    // - if music is playing, always limit the volume to current music volume,
    // with a minimum threshold at -36dB so that notification is always perceived.
    if ((device & (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP | AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
                   AUDIO_DEVICE_OUT_WIRED_HEADSET | AUDIO_DEVICE_OUT_WIRED_HEADPHONE |
                   AUDIO_DEVICE_OUT_USB_HEADSET | AUDIO_DEVICE_OUT_HEARING_AID)) &&
            ((volumeSource == alarmVolumeSrc ||
              volumeSource == ringVolumeSrc) ||
             (volumeSource == toVolumeSource(AUDIO_STREAM_NOTIFICATION)) ||
             (volumeSource == toVolumeSource(AUDIO_STREAM_SYSTEM)) ||
             ((volumeSource == toVolumeSource(AUDIO_STREAM_ENFORCED_AUDIBLE)) &&
              (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) &&
            curves.canBeMuted()) {

        // when the phone is ringing we must consider that music could have been paused just before
        // by the music application and behave as if music was active if the last music track was
        // just stopped
        if (isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY) ||
                mLimitRingtoneVolume) {
            volumeDb += SONIFICATION_HEADSET_VOLUME_FACTOR_DB;
            audio_devices_t musicDevice =
                    mEngine->getOutputDevicesForAttributes(attributes_initializer(AUDIO_USAGE_MEDIA),
                                                           nullptr, true /*fromCache*/).types();
            auto &musicCurves = getVolumeCurves(AUDIO_STREAM_MUSIC);
            float musicVolDb = computeVolume(musicCurves, musicVolumeSrc,
                                             musicCurves.getVolumeIndex(musicDevice), musicDevice);
            float minVolDb = (musicVolDb > SONIFICATION_HEADSET_VOLUME_MIN_DB) ?
                        musicVolDb : SONIFICATION_HEADSET_VOLUME_MIN_DB;
            if (volumeDb > minVolDb) {
                volumeDb = minVolDb;
                ALOGV("computeVolume limiting volume to %f musicVol %f", minVolDb, musicVolDb);
            }
            if (device & (AUDIO_DEVICE_OUT_BLUETOOTH_A2DP |
                          AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES)) {
                // on A2DP, also ensure notification volume is not too low compared to media when
                // intended to be played
                if ((volumeDb > -96.0f) &&
                        (musicVolDb - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB > volumeDb)) {
                    ALOGV("%s increasing volume for volume source=%d device=0x%X from %f to %f",
                          __func__, volumeSource, device, volumeDb,
                          musicVolDb - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB);
                    volumeDb = musicVolDb - SONIFICATION_A2DP_MAX_MEDIA_DIFF_DB;
                }
            }
        } else if ((Volume::getDeviceForVolume(device) != AUDIO_DEVICE_OUT_SPEAKER) ||
                   (!(volumeSource == alarmVolumeSrc || volumeSource == ringVolumeSrc))) {
            volumeDb += SONIFICATION_HEADSET_VOLUME_FACTOR_DB;
        }
    }

    return volumeDb;
}

在computeVolume方法中调用volIndexToDb方法,volIndexToDb方法定义在VolumeCurve.h头文件中。

xref: /frameworks/av/services/audiopolicy/engine/common/include/VolumeCurve.h

复制代码
virtual float volIndexToDb(device_category deviceCat, int indexInUi) const
{
    sp<VolumeCurve> vc = getCurvesFor(deviceCat);
    if (vc != 0) {
        return vc->volIndexToDb(indexInUi, mIndexMin, mIndexMax);
    } else {
        ALOGE("Invalid device category %d for Volume Curve", deviceCat);
        return 0.0f;
    }
}

具体实现类在VolumeCurve.cpp类中

ef: /frameworks/av/services/audiopolicy/engine/common/src/VolumeCurve.cpp

复制代码
float VolumeCurve::volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) const
{
    ALOG_ASSERT(!mCurvePoints.isEmpty(), "Invalid volume curve");
    if (volIndexMin < 0 || volIndexMax < 0) {
        // In order to let AudioService initialize the min and max, convention is to use -1
        return NAN;
    }
    if (indexInUi < volIndexMin) {
        // an index of 0 means mute request when volIndexMin > 0
        if (indexInUi == 0) {
            ALOGV("VOLUME forcing mute for index 0 with min index %d", volIndexMin);
            return VOLUME_MIN_DB;
        }
        ALOGV("VOLUME remapping index from %d to min index %d", indexInUi, volIndexMin);
        indexInUi = volIndexMin;
    } else if (indexInUi > volIndexMax) {
        ALOGV("VOLUME remapping index from %d to max index %d", indexInUi, volIndexMax);
        indexInUi = volIndexMax;
    }

    size_t nbCurvePoints = mCurvePoints.size();
    // the volume index in the UI is relative to the min and max volume indices for this stream
    int nbSteps = 1 + mCurvePoints[nbCurvePoints - 1].mIndex - mCurvePoints[0].mIndex;
    int volIdx = (nbSteps * (indexInUi - volIndexMin)) / (volIndexMax - volIndexMin);

    // Where would this volume index been inserted in the curve point
    size_t indexInUiPosition = mCurvePoints.orderOf(CurvePoint(volIdx, 0));
    if (indexInUiPosition >= nbCurvePoints) {
        //use last point of table
        return mCurvePoints[nbCurvePoints - 1].mAttenuationInMb / 100.0f;
    }
    if (indexInUiPosition == 0) {
        if (indexInUiPosition != mCurvePoints[0].mIndex) {
            return VOLUME_MIN_DB; // out of bounds
        }
        return mCurvePoints[0].mAttenuationInMb / 100.0f;
    }
    // linear interpolation in the attenuation table in dB
    float decibels = (mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f) +
            ((float)(volIdx - mCurvePoints[indexInUiPosition - 1].mIndex)) *
                ( ((mCurvePoints[indexInUiPosition].mAttenuationInMb / 100.0f) -
                        (mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f)) /
                    ((float)(mCurvePoints[indexInUiPosition].mIndex -
                            mCurvePoints[indexInUiPosition - 1].mIndex)) );

    ALOGV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f]",
            mCurvePoints[indexInUiPosition - 1].mIndex, volIdx,
            mCurvePoints[indexInUiPosition].mIndex,
            ((float)mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f), decibels,
            ((float)mCurvePoints[indexInUiPosition].mAttenuationInMb / 100.0f));

    return decibels;
}

在VolumeCurve::volIndexToDb里实现音量曲线的计算。

相关推荐
阿巴斯甜18 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker18 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952719 小时前
Andorid Google 登录接入文档
android
黄林晴21 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇2 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android