Android8.0的音量曲线设置

本文详细探讨了在Android 8.0中如何修改音量曲线,尤其是针对蓝牙设备的情况。通过分析AudioPolicyManager.cpp、StreamDescriptor和VolumeCurvesCollection等关键组件,揭示了在代码和XML配置文件中调整音量曲线的方法,涉及到的路径包括框架、MK文件和XML配置。文章还展示了如何在不同场景下设置和计算音量,对于理解Android音量控制逻辑具有指导意义。
摘要由CSDN通过智能技术生成

最近Android8.0,遇到了一个问题。
由于我们开发的产品在对音量进行控制时通常只需要对speaker进行控制,并且音量控制逻辑是由我们自己的中间件进行控制的,所以音量曲线也是走的中间件的逻辑。
但是由于蓝牙设备又是走的安卓自己的流程,当需要修改蓝牙设备的音量曲线的时候去需要去修改安卓里的音量曲线。

通常在之前的版本中(4.x),是在代码中进行修改的,所以我也在类似的地方进行修改,结果没有效果,走了一些弯路,
所以又重新跟着音量设置的逻辑来查(关于音量设置的逻辑在另一篇文章中有写到),最终才发现是咋回事。

在AudioPolicyManager.cpp中

status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream,
                                                  int index,
                                                  audio_devices_t device)
{
    if ((index < mVolumeCurves->getVolumeIndexMin(stream)) ||
            (index > mVolumeCurves->getVolumeIndexMax(stream))) {
        return BAD_VALUE;
    }
    if (!audio_is_output_device(device)) {
        return BAD_VALUE;
    }

    // Force max volume if stream cannot be muted
    if (!mVolumeCurves->canBeMuted(stream)) index = mVolumeCurves->getVolumeIndexMax(stream);

    // update other private stream volumes which follow this one
    for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) {
        if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) {
            continue;
        }
        mVolumeCurves->addCurrentVolumeIndex((audio_stream_type_t)curStream, device, index);
    }

    // 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 strategy corresponding to 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 strategy
    // - For default requested device (AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME), apply volume only if
    // no specific device volume value exists for currently selected device.
    status_t status = NO_ERROR;
    for (size_t i = 0; i < mOutputs.size(); i++) {
        sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
        audio_devices_t curDevice = Volume::getDeviceForVolume(desc->device());
        for (int curStream = 0; curStream < AUDIO_STREAM_FOR_POLICY_CNT; curStream++) {
            if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) {
                continue;
            }
            if (!(desc->isStreamActive((audio_stream_type_t)curStream) ||
                    (isInCall() && (curStream == AUDIO_STREAM_VOICE_CALL)))) {
                continue;
            }
            routing_strategy curStrategy = getStrategy((audio_stream_type_t)curStream);
            audio_devices_t curStreamDevice = getDeviceForStrategy(curStrategy, false /*fromCache*/);
            if ((device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) &&
                    ((curStreamDevice & device) == 0)) {
                continue;
            }
            bool applyVolume;
            if (device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) {
                curStreamDevice |= device;
                applyVolume = (curDevice & curStreamDevice) != 0;
            } else {
                applyVolume = !mVolumeCurves->hasVolumeIndexForDevice(
                        stream, Volume::getDeviceForVolume(curStreamDevice));
            }

            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

                //调用checkAndSetVolume设置音量
                status_t volStatus =
                        checkAndSetVolume((audio_stream_type_t)curStream, index, desc, curDevice,
                            (stream == AUDIO_STREAM_SYSTEM) ? TOUCH_SOUND_FIXED_DELAY_MS : 0);
                if (volStatus != NO_ERROR) {
                    status = volStatus;
                }
            }
        }
    }
    return NO_ERROR;
}

这里调用了checkAndSetVolume继续设置音量

status_t AudioPolicyManager::checkAndSetVolume(audio_stream_type_t stream,
                                                   int index,
                                                   const sp<AudioOutputDescriptor>& outputDesc,
                                                   audio_devices_t device,
                                                   int delayMs,
                                                   bool force)
{
    // do not change actual stream volume if the stream is muted
    if (outputDesc->mMuteCount[stream] != 0) {
        ALOGVV("checkAndSetVolume() stream %d muted count %d",
              stream, outputDesc->mMuteCount[stream]);
        return NO_ERROR;
    }
    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 ((stream == AUDIO_STREAM_VOICE_CALL && forceUseForComm == AUDIO_POLICY_FORCE_BT_SCO) ||
        (stream == AUDIO_STREAM_BLUETOOTH_SCO && forceUseForComm != AUDIO_POLICY_FORCE_BT_SCO)) {
        ALOGV("checkAndSetVolume() cannot set stream %d volume with force use = %d for comm",
             stream, forceUseForComm);
        return INVALID_OPERATION;
    }

    if (device == AUDIO_DEVICE_NONE) {
        device = outputDesc->device();
    }
    //调用computeVolume将index值转化成db值
    float volumeDb = computeVolume(stream, index, device);
    if (outputDesc->isFixedVolume(device)) {
        volumeDb = 0.0f;
    }
    outputDesc->setVolume(volumeDb, stream, device, delayMs, force);

    if (stream == AUDIO_STREAM_VOICE_CALL ||
        stream == AUDIO_STREAM_BLUETOOTH_SCO) {
        float voiceVolume;
        // Force voice volume to max for bluetooth SCO as volume is managed by the headset
        if (stream == AUDIO_STREAM_VOICE_CALL) {
            voiceVolume = (float)index/(float)mVolumeCurves->getVolumeIndexMax(stream);
        } else {
            voiceVolume = 1.0;
        }

        if (voiceVolume != mLastVoiceVolume) {
            mpClientInterface->setVoiceVolume(voiceVolume, delayMs);
            mLastVoiceVolume = voiceVolume;
        }
    }

    return NO_ERROR;
}

这里调用了computeVolume将index值转化成db值

进入computeVolume

float AudioPolicyManager::computeVolume(audio_stream_type_t stream,
                                        int index,
                                        audio_devices_t device)
{
    //调用mVolumeCurves->volIndexToDb
    float volumeDB = mVolumeCurves->volIndexToDb(stream, 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
    if ((stream == AUDIO_STREAM_ACCESSIBILITY)
            && (AUDIO_MODE_RINGTONE == mEngine->getPhoneState())
            && isStreamActive(AUDIO_STREAM_RING, 0)) {
        const float ringVolumeDB = computeVolume(AUDIO_STREAM_RING, index, device);
        return ringVolumeDB - 4 > volumeDB ? ringVolumeDB - 4 : volumeDB;
    }

    // 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.
    const routing_strategy stream_strategy = getStrategy(stream);
    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)) &&
        ((stream_strategy == STRATEGY_SONIFICATION)
                || (stream_strategy == STRATEGY_SONIFICATION_RESPECTFUL)
                || (stream == AUDIO_STREAM_SYSTEM)
                || ((stream_strategy == STRATEGY_ENFORCED_AUDIBLE) &&
                    (mEngine->getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_NONE))) &&
            mVolumeCurves->canBeMuted(stream)) {
        // 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 = getDeviceForStrategy(STRATEGY_MEDIA, true /*fromCache*/);
            float musicVolDB = computeVolume(AUDIO_STREAM_MUSIC,
                                             mVolumeCurves->getVolumeIndex(AUDIO_STREAM_MUSIC,
                                                                              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("computeVolume increasing volume for stream=%d device=0x%X from %f to %f",
                            stream, 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) ||
                stream_strategy != STRATEGY_SONIFICATION) {
            volumeDB += SONIFICATION_HEADSET_VOLUME_FACTOR_DB;
        }
    }

    return volumeDB;
}

继续调用mVolumeCurves->volIndexToDb

!!!!在这里就有一个非常重要的点,也是我花了一些时间去踩坑的点。
先去看frameworks\av\services\audiopolicy\common\managerdefinitions下的
Android.mk文件,其中有一段

ifeq ($(USE_XML_AUDIO_POLICY_CONF), 1)

LOCAL_SRC_FILES += src/Serializer.cpp

LOCAL_SHARED_LIBRARIES += libicuuc libxml2

LOCAL_C_INCLUDES += \
    $(TOPDIR)external/libxml2/include \
    $(TOPDIR)external/icu/icu4c/source/common

else

LOCAL_SRC_FILES += \
    src/ConfigParsingUtils.cpp \
    src/StreamDescriptor.cpp \
    src/Gains.cpp

根据命名规则来看USE_XML_AUDIO_POLICY_CONF也就是说是否使用XML的意思。
我之前没有关注到这个mk所以自然而然的去和老版本一样去代码里面改,而在编译之后生成的.so替换进去完全没有效果,后来修改代码发现压根没有编译进去,所以才来找mk才发现有两种方法。
我发现第二种(else下的,也就是和老版本一样在代码里面改)没有效果,连USE_XML_AUDIO_POLICY_CONF的值都不用去查了,就知道是用的xml的方式,就根据ifeq下的套路来改就好了。

但是我们现在还是先从比较好理解的else下的老版本方法来讲。

回到我们的mVolumeCurves->volIndexToDb,那么就去看mVolumeCurves是个什么对象。

在AudioPolicyManager的构造函数

AudioPolicyManager::AudioPolicyManager(AudioPolicyClientInterface *clientInterface)

中有这么一段


#ifdef USE_XML_AUDIO_POLICY_CONF
    mVolumeCurves = new VolumeCurvesCollection();
    AudioPolicyConfig config(mHwModules, mAvailableOutputDevices, mAvailableInputDevices,
                             mDefaultOutputDevice, speakerDrcEnabled,
                             static_cast<VolumeCurvesCollection *>(mVolumeCurves));
    if (deserializeAudioPolicyXmlConfig(config) != NO_ERROR) {
#else
    mVolumeCurves = new StreamDescriptorCollection();
    AudioPolicyConfig config(mHwModules, mAvailableOutputDevices, mAvailableInputDevices,
                             mDefaultOutputDevice, speakerDrcEnabled);
    if ((ConfigParsingUtils::loadConfig(AUDIO_POLICY_VENDOR_CONFIG_FILE, config) != NO_ERROR) &&
            (ConfigParsingUtils::loadConfig(AUDIO_POLICY_CONFIG_FILE, config) != NO_ERROR)) {
  

这里也牵涉到了USE_XML_AUDIO_POLICY_CONF,我们已经知道这个的值是1,但是我们还是先看看else下面。mVolumeCurves = new StreamDescriptorCollection();

StreamDescriptorCollection()的定义在StreamDescriptor.h和StreamDescriptor.cpp中
那么去StreamDescriptor.h和StreamDescriptor.cpp看看

class StreamDescriptorCollection : public DefaultKeyedVector<audio_stream_type_t, StreamDescriptor>,
                                   public IVolumeCurvesCollection
StreamDescriptorCollection::StreamDescriptorCollection()
{
    for (size_t stream = 0 ; stream < AUDIO_STREAM_CNT; stream++) {
        add(static_cast<audio_stream_type_t>(stream), StreamDescriptor());
    }
}

发现是用了DefaultKeyedVector把StreamDescriptor()和stream(也就是音量的类型)对应起来。

再去看看StreamDescriptor(),这个StreamDescriptor()也是在StreamDescriptor.cpp中定义的。

StreamDescriptor::StreamDescriptor()
    :   mIndexMin(0), mIndexMax(1), mCanBeMuted(true)
{
    // Initialize the current stream's index to mIndexMax so volume isn't 0 in
    // cases where the Java layer doesn't call into the audio policy service to
    // set the default volume.
    mIndexCur.add(AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME, mIndexMax);
}

这里做了一个简单的初始化,暂时不用管。

再去看StreamDescriptorCollection::volIndexToDb

float StreamDescriptorCollection::volIndexToDb(audio_stream_type_t stream, device_category category,
                                               int indexInUi) const
{
                    (int)stream, index, (int)device);
    const StreamDescriptor &streamDesc = valueAt(stream);
    return Gains::volIndexToDb(streamDesc.getVolumeCurvePoint(category),
                               streamDesc.getVolumeIndexMin(), streamDesc.getVolumeIndexMax(),
                               indexInUi);
}

调用Gains::volIndexToDb

那么进入Gains.cpp.

//static
float Gains::volIndexToDb(const VolumeCurvePoint *curve, int indexMin, int indexMax, int indexInUi)
{
    // the volume index in the UI is relative to the min and max volume indices for this stream type
    int nbSteps = 1 + curve[Volume::VOLMAX].mIndex - curve[Volume::VOLMIN].mIndex;
    int volIdx = (nbSteps * (indexInUi - indexMin)) / (indexMax - indexMin);

    // find what part of the curve this index volume belongs to, or if it's out of bounds
    int segment = 0;
    if (volIdx < curve[Volume::VOLMIN].mIndex) {         // out of bounds
        return VOLUME_MIN_DB;
    } else if (volIdx < curve[Volume::VOLKNEE1].mIndex) {
        segment = 0;
    } else if (volIdx < curve[Volume::VOLKNEE2].mIndex) {
        segment = 1;
    } else if (volIdx <= curve[Volume::VOLMAX].mIndex) {
        segment = 2;
    } else {                                                               // out of bounds
        return 0.0f;
    }

    // linear interpolation in the attenuation table in dB
    float decibels = curve[segment].mDBAttenuation +
            ((float)(volIdx - curve[segment].mIndex)) *
                ( (curve[segment+1].mDBAttenuation -
                        curve[segment].mDBAttenuation) /
                    ((float)(curve[segment+1].mIndex -
                            curve[segment].mIndex)) );

    ALOGVV("VOLUME vol index=[%d %d %d], dB=[%.1f %.1f %.1f]",
            curve[segment].mIndex, volIdx,
            curve[segment+1].mIndex,
            curve[segment].mDBAttenuation,
            decibels,
            curve[segment+1].mDBAttenuation);

    return decibels;
}
};

在这里真正地进行了计算并且返回了一个decibels,这个值就是我们调用
float volumeDB = mVolumeCurves->volIndexToDb(stream, Volume::getDeviceCategory(device), index);
返回的值。

如果有兴趣的话可以仔细看一看是怎么计算的(其实也很简单,就是根据传入的Index再匹配这个index两边的point进行计算)。

此时我们发现是根据我们传入的几个值的数据来进行计算的。所以再看看传入的东西都是些什么。
回到StreamDescriptorCollection::volIndexToDb

float StreamDescriptorCollection::volIndexToDb(audio_stream_type_t stream, device_category category,
                                               int indexInUi) const
{
                    (int)stream, index, (int)device);
    const StreamDescriptor &streamDesc = valueAt(stream);
    return Gains::volIndexToDb(streamDesc.getVolumeCurvePoint(category),
                               streamDesc.getVolumeIndexMin(), streamDesc.getVolumeIndexMax(),
                               indexInUi);
}

看到传入的是streamDesc, 而streamDesc是一个StreamDescriptor对象。
原来如此,这就是我们之前看到的一个stream对应一个StreamDescriptor对象,
streamDesc = valueAt(stream)就是去获取当前stream所对应的StreamDescriptor对象。

我们到StreamDescriptor.h中StreamDescriptor的定义中有以下几个参数。

private:
    const VolumeCurvePoint *mVolumeCurve[DEVICE_CATEGORY_CNT];
    KeyedVector<audio_devices_t, int> mIndexCur; /**< current volume index per device. */
    int mIndexMin; /**< min volume index. */
    int mIndexMax; /**< max volume index. */
    bool mCanBeMuted; /**< true is the stream can be muted. */

   int getVolumeIndexMin() const { return mIndexMin; }
    int getVolumeIndexMax() const { return mIndexMax; }
    const VolumeCurvePoint *getVolumeCurvePoint(device_category deviceCategory) const
    {
        return mVolumeCurve[deviceCategory];
    }

这个也仅仅是返回这几个参数。那么我们先看看mVolumeCurve,从它的定义看他是和
deviceCategory相绑定的,大概可以看出,音量曲线的参数是根据不同device和stream组合来定义的,简单的说就是同一个steam不同的设备的音量曲线也不同。

既然有getVolumeCurvePoint,那么也肯定有setVolumeCurvePoint

void StreamDescriptor::setVolumeCurvePoint(device_category deviceCategory,
                                           const VolumeCurvePoint *point)
{
    mVolumeCurve[deviceCategory] = point;
}

mVolumeCurve在这里进行了赋值。我们再看看这个setVolumeCurvePoint在哪里调用的。
发现StreamDescriptorCollection::setVolumeCurvePoint调用了StreamDescriptor的
setVolumeCurvePoint

void StreamDescriptorCollection::setVolumeCurvePoint(audio_stream_type_t stream,
                                                     device_category deviceCategory,
                                                     const VolumeCurvePoint *point)
{
    editValueAt(stream).setVolumeCurvePoint(deviceCategory, point);
}

现在明白了,是在StreamDescriptorCollection中调用steam对应的StreamDescriptor对象的setVolumeCurvePoint,并且把传入的deviceCategory和point传入StreamDescriptor的setVolumeCurvePoint,我们需要知道这个point是哪里来的。

再看是谁调用了StreamDescriptorCollection的setVolumeCurvePoint
查找到在StreamDescriptorCollection::initializeVolumeCurves中

void StreamDescriptorCollection::initializeVolumeCurves(bool isSpeakerDrcEnabled)
{
    for (int i = 0; i < AUDIO_STREAM_CNT; i++) {
        for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) {
            setVolumeCurvePoint(static_cast<audio_stream_type_t>(i),
                                static_cast<device_category>(j),
                                Gains::sVolumeProfiles[i][j]);
        }
    }

    // Check availability of DRC on speaker path: if available, override some of the speaker curves
    if (isSpeakerDrcEnabled) {
        setVolumeCurvePoint(AUDIO_STREAM_SYSTEM, DEVICE_CATEGORY_SPEAKER,
                            Gains::sDefaultSystemVolumeCurveDrc);
        setVolumeCurvePoint(AUDIO_STREAM_RING, DEVICE_CATEGORY_SPEAKER,
                            Gains::sSpeakerSonificationVolumeCurveDrc);
        setVolumeCurvePoint(AUDIO_STREAM_ALARM, DEVICE_CATEGORY_SPEAKER,
                            Gains::sSpeakerSonificationVolumeCurveDrc);
        setVolumeCurvePoint(AUDIO_STREAM_NOTIFICATION, DEVICE_CATEGORY_SPEAKER,
                            Gains::sSpeakerSonificationVolumeCurveDrc);
        setVolumeCurvePoint(AUDIO_STREAM_MUSIC, DEVICE_CATEGORY_SPEAKER,
                            Gains::sSpeakerMediaVolumeCurveDrc);
        setVolumeCurvePoint(AUDIO_STREAM_ACCESSIBILITY, DEVICE_CATEGORY_SPEAKER,
                            Gains::sSpeakerMediaVolumeCurveDrc);
    }
}

下面if的内容先不看,我们看这个循环

for (int i = 0; i < AUDIO_STREAM_CNT; i++) {
        for (int j = 0; j < DEVICE_CATEGORY_CNT; j++) {
            setVolumeCurvePoint(static_cast<audio_stream_type_t>(i),
                                static_cast<device_category>(j),
                                Gains::sVolumeProfiles[i][j]);
        }
    }

这个双循环吧所有的steam和device对应的点都传了进去。看看这个point是哪里来的。
在Gains.cpp中

const VolumeCurvePoint *Gains::sVolumeProfiles[AUDIO_STREAM_CNT]
                                                  [DEVICE_CATEGORY_CNT] = {
    { // AUDIO_STREAM_VOICE_CALL
        Gains::sDefaultVoiceVolumeCurve, // DEVICE_CATEGORY_HEADSET
        Gains::sSpeakerVoiceVolumeCurve, // DEVICE_CATEGORY_SPEAKER
        Gains::sSpeakerVoiceVolumeCurve, // DEVICE_CATEGORY_EARPIECE
        Gains::sDefaultMediaVolumeCurve  // DEVICE_CATEGORY_EXT_MEDIA
    },
    { // AUDIO_STREAM_SYSTEM
        Gains::sHeadsetSystemVolumeCurve, // DEVICE_CATEGORY_HEADSET
        Gains::sDefaultSystemVolumeCurve, // DEVICE_CATEGORY_SPEAKER
        Gains::sDefaultSystemVolumeCurve,  <
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值