Android原生音量设置和计算

Android原生的音量调整都是调整软件音量,也就是对于解码后的pcm数据乘以一定的系数,范围0~1.0,常见的概念有:

Track volume : 单个AudioTrack的音量,使用时只影响该AudioTrack的音量,不会对其他AudioTrack造成影响,播完之后也就结束。

接口为:status_t AudioTrack::setVolume(float left, float right);
stream volume :设置某一stream的音量,这是系统中最常见的音量设置方式,通常都是设置AUDIO_STREAM_MUSIC类型即多媒体音量,而设置这项不会影响其他stream类型的音量,如按键音的AUDIO_STREAM_SYSTEM类型。

native的接口有:status_t AudioSystem::setStreamVolume(audio_stream_type_t stream, float value, audio_io_handle_t output);
master volume :设置它等于设置所有的stream volume和track volume。系统任何声音输出都会受到影响。而这个接口也保留了通过hal层直接设置硬件音量的可能性,直接设置硬件音量必然是会对系统所有声音有影响的

native的接口有: AudioSystem::setMasterVolume(float value);

所以一个输出的软件音量=master_volume * stream_volume * AudioTrack_volume,默认情况下master volume和Track volume都会是1.0,只会用到stream_volume。

master volume和Track volume的设置很简单,一般都是直接设置好自己想要设置的线性音量值,范围从0~1.0.

而stream volume涉及到音视频产品的音频曲线概念,有一套自己的规则,要分析stream volume的处理,AudioPolicyManager中的checkAndSetVolume函数是个高屋建瓴的入口,所有stream类型和不同的device的音量都是在这里进行计算并设置的。

Android P上的code如下,音量已经改成以db为单位计算了。

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();
    }

    float volumeDb = computeVolume(stream, index, device);
    if (outputDesc->isFixedVolume(device) ||
            // Force VoIP volume to max for bluetooth SCO
            ((stream == AUDIO_STREAM_VOICE_CALL || stream == AUDIO_STREAM_BLUETOOTH_SCO) &&
             (device & AUDIO_DEVICE_OUT_ALL_SCO) != 0)) {
        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(stream, index, device)

根据当前的index计算出stream和device的当前音量,如果系统设置了fixed模式,意为软件音量不可改变,将固定为0db输出

float AudioPolicyManager::computeVolume(audio_stream_type_t stream,
                                        int index,
                                        audio_devices_t device)
{
    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;
    }

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

}

将index转算出对应的db值

float VolumeCurve::volIndexToDb(int indexInUi, int volIndexMin, int volIndexMax) const
{
    ALOG_ASSERT(!mCurvePoints.isEmpty(), "Invalid volume curve");

    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;
    if (indexInUi < volIndexMin) {
        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;
    }
    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 mDeviceCategory %d, mStreamType %d vol index=[%d %d %d], dB=[%.1f %.1f %.1f]",
            mDeviceCategory, mStreamType,
            mCurvePoints[indexInUiPosition - 1].mIndex, volIdx,
            mCurvePoints[indexInUiPosition].mIndex,
            ((float)mCurvePoints[indexInUiPosition - 1].mAttenuationInMb / 100.0f), decibels,
            ((float)mCurvePoints[indexInUiPosition].mAttenuationInMb / 100.0f));

    return decibels;
}

利用audio_policy_volumes.xml中的音量点配置来计算对应音量,举例如下我们计算该类型index为70时候的音量值:

    <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>

nbCurvePoints = mCurvePoints.size();解析完之后为4

nbSteps = 1+ 100 - 0 = 101;

volIdx  = 70;

indexInUiPosition 表示当前index在表中的位置,70在66和100之间,那么计算就以66和100作为参考

音量计算公式为,:

    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)) );

mCurvePoints[0].mAttenuationInMb  = -2970db

mCurvePoints[1].mAttenuationInMb  = -2010db;

mCurvePoints[2].mAttenuationInMb  = -1020db;

mCurvePoints[3].mAttenuationInMb  = 0db;

70对应的db为 decibels = -10.2 + (70 -66)*(0-(-10.2))/(100-66) = -9.27db;

音量计算的逻辑分析完毕,而处理的地方在audiomixer里面,还涉及混音和重采样,有时间再独立去分析。

其实我们完全可以把100个点完全填写完毕也是可以的,这样使用公式平均化也是个粗略的估算,很多产品是有比较严格的音频曲线标准,我们可以在xml里将100个点的对应音量全部给出来。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值