当前位置: 首页 > article >正文

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里实现音量曲线的计算。


http://www.kler.cn/a/463771.html

相关文章:

  • MySQL8安装与卸载
  • 景区自助售卡机与定点酒店的合作双赢之策-景区酒店方案
  • 机器学习之正则化惩罚和K折交叉验证调整逻辑回归模型
  • wangEditor/editor自定义粘贴后续
  • 汇编语言:从键盘输入数字字符,(计算阶乘),以无符号十进制形式输出(分支、循环程序)
  • .NET 8 + Ocelot + Consul 实现代理网关、服务发现
  • SpringBoot 新特性
  • 使用Windows和FFmpeg 将https://xxx.com/xx.m3u8 推流到B站
  • 二十三种设计模式-建造者模式
  • 【2024年-12月-31日-开源社区openEuler实践记录】virtCCA_sdk:开启虚拟化安全增强的编程新钥
  • Maven 测试和单元测试介绍
  • 项目管理:用甘特图 “导航” 项目全程
  • tensorboard的界面参数与图像数据分析讲解
  • MYSQL 高阶语句
  • linux系统编程(七)管道和FIFO
  • 【MySQL】之Centos7.6 Yum方式安装部署安装Mysql 5.7版本
  • IPsec协议,网络安全的秘密
  • CentOS Stream 9 搭建单机版Clickhouse
  • Spring Boot 日志系统选择、日志系统选择、日志系统选择技术选型
  • 【GeekBand】C++设计模式笔记20_Composite_组合模式
  • 将广播发送和接收端实现一遍,完成一个发送端发送信息,对应多个接收端接收信息实验。
  • 怎么在 Linux 下运行 smart_rtmpd
  • 多模态论文笔记——CogVLM和CogVLM2(副)
  • [Linux]操作系统概念
  • Postman[4] 环境设置
  • ThinkPHP 8高效构建Web应用-第一个简单的MVC应用示例