Android R- AudioManager之音量调节(一)

前言

关于音量调节我们知道有AudioManager的软件调节和CarAudioManager的硬件调节,今天先聊聊AudioManager的软件音量调节。

正文

关于AudioManager中音量调节的API主要有如下两个:

  1. adjustVolume
  2. setStreamVolume

adjustVolume偏向一些按键的音量调节,比如手机上音量+-的硬按键控制的音量调节。
setStreamVolume更像是settings中的音量bar进行的音量调节。
说这俩函数前先看下volume几个相关的数组。

  • Max Volume
    所有streamType对应的最大音量
  /** Maximum volume index values for audio streams */
    protected static int[] MAX_STREAM_VOLUME = new int[] {
        5,  // STREAM_VOICE_CALL
        7,  // STREAM_SYSTEM
        7,  // STREAM_RING
        15, // STREAM_MUSIC
        7,  // STREAM_ALARM
        7,  // STREAM_NOTIFICATION
        15, // STREAM_BLUETOOTH_SCO
        7,  // STREAM_SYSTEM_ENFORCED
        15, // STREAM_DTMF
        15, // STREAM_TTS
        15, // STREAM_ACCESSIBILITY
        15  // STREAM_ASSISTANT
    };
  • Min Volume
    所有streamType对应的最小音量
    /** Minimum volume index values for audio streams */
    protected static int[] MIN_STREAM_VOLUME = new int[] {
        1,  // STREAM_VOICE_CALL
        0,  // STREAM_SYSTEM
        0,  // STREAM_RING
        0,  // STREAM_MUSIC
        1,  // STREAM_ALARM
        0,  // STREAM_NOTIFICATION
        0,  // STREAM_BLUETOOTH_SCO
        0,  // STREAM_SYSTEM_ENFORCED
        0,  // STREAM_DTMF
        0,  // STREAM_TTS
        1,  // STREAM_ACCESSIBILITY
        0   // STREAM_ASSISTANT
    };
  • Default Volume
    注意默认音量定义在AudioSystem中
    public static int[] DEFAULT_STREAM_VOLUME = new int[] {
        4,  // STREAM_VOICE_CALL
        7,  // STREAM_SYSTEM
        5,  // STREAM_RING
        5, // STREAM_MUSIC
        6,  // STREAM_ALARM
        5,  // STREAM_NOTIFICATION
        7,  // STREAM_BLUETOOTH_SCO
        7,  // STREAM_SYSTEM_ENFORCED
        5, // STREAM_DTMF
        5, // STREAM_TTS
        5, // STREAM_ACCESSIBILITY
        5, // STREAM_ASSISTANT
    };

我们知道了每个streamType对应的最大、最小以及默认的音量,还有个很重的数组

  • Volume Alias
    private final int[] STREAM_VOLUME_ALIAS_VOICE = new int[] {
        AudioSystem.STREAM_VOICE_CALL,      // STREAM_VOICE_CALL
        AudioSystem.STREAM_RING,            // STREAM_SYSTEM
        AudioSystem.STREAM_RING,            // STREAM_RING
        AudioSystem.STREAM_MUSIC,           // STREAM_MUSIC
        AudioSystem.STREAM_ALARM,           // STREAM_ALARM
        AudioSystem.STREAM_RING,            // STREAM_NOTIFICATION
        AudioSystem.STREAM_BLUETOOTH_SCO,   // STREAM_BLUETOOTH_SCO
        AudioSystem.STREAM_RING,            // STREAM_SYSTEM_ENFORCED
        AudioSystem.STREAM_RING,            // STREAM_DTMF
        AudioSystem.STREAM_MUSIC,           // STREAM_TTS
        AudioSystem.STREAM_MUSIC,           // STREAM_ACCESSIBILITY
        AudioSystem.STREAM_MUSIC            // STREAM_ASSISTANT
    };

ALIAS别名,这个主要是将streamType进行了分组,相同别名的音量一组。如streamType是TTS和Music的就是一组,同一组的音源音量一起调节,我们调节了STREAM_MUSIC的音量,那么STREAM_TTS的音量也会同步变更。
接下来就看下具体音量调节的流程吧。

adjustVolume

调节当前音源的音量,AudioService中实现adjustStreamVolume的逻辑比较复杂,因为随着Android版本的不断迭代,功能的不断增多。因此逻辑也是越来越复杂,比如震动下的铃声调节、勿扰模式下的铃声调节,以及特殊StreamType的特殊处理,这里只说下大体的流程吧。
首先是step的计算,即每次调节音量的步长。

 step = rescaleStep(10, streamType, streamTypeAlias);

看下rescaleStep这个函数:

    private int rescaleStep(int step, int srcStream, int dstStream) {
        int srcRange = getIndexRange(srcStream);
        int dstRange = getIndexRange(dstStream);
        if (srcRange == 0) {
            Log.e(TAG, "rescaleStep : index range should not be zero");
            return 0;
        }

        return ((step * dstRange + srcRange / 2) / srcRange);
    }

srcStream表示调节的streamType,dstStream表示当前音量组的streamType,举个例子如果当前调节的srcStream是STREAM_TTS那么通过STREAM_VOLUME_ALIAS_VOICE 我们可以知道dstStream是STREAM_MUSIC。

    private int getIndexRange(int streamType) {
        return (mStreamStates[streamType].getMaxIndex() - mStreamStates[streamType].getMinIndex());
    }

如果srcStream音量最大值减去最小值不是0,那么就说明这个streamType的音量是可以调节的,那么它的步长就是((step * dstRange + srcRange / 2) / srcRange)根据上面的数组就很容易算出step来了。
拿到step,在根据direction然后调用VolumeStreamState的adjustIndex,而adjustIndex又会调用setIndex

        public boolean adjustIndex(int deltaIndex, int device, String caller,
                boolean hasModifyAudioSettings) {
            return setIndex(getIndex(device) + deltaIndex, device, caller,
                    hasModifyAudioSettings);
        }

setIndex中主要做音量处理,以及发送音量变化的广播。这里多说一句音量变更后存储在mIndexMap中。

mIndexMap.put(device, index)

可以看到音量的存储并不是根据streamType来的,而是根据device存储的,这就解释了我们在播放音乐的时候外放和插入耳机播放时音量不一致的原因了。
因为一个streamType可能对应多个Device即,一个streamType可能存在多个音量。

音量更新完后便是给子线程发 MSG_SET_DEVICE_VOLUME消息来调节音量。 子线程收到音量调节消息后调用VolumeStreamState的setDeviceVolume

    /*package*/ void setDeviceVolume(VolumeStreamState streamState, int device) {

        synchronized (VolumeStreamState.class) {
            // Apply volume
            streamState.applyDeviceVolume_syncVSS(device);

            // Apply change to all streams using this one as alias
            int numStreamTypes = AudioSystem.getNumStreamTypes();
            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
                if (streamType != streamState.mStreamType &&
                        mStreamVolumeAlias[streamType] == streamState.mStreamType) {
                    // Make sure volume is also maxed out on A2DP device for aliased stream
                    // that may have a different device selected
                    int streamDevice = getDeviceForStream(streamType);
                    if ((device != streamDevice) && mAvrcpAbsVolSupported
                            && AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)) {
                        mStreamStates[streamType].applyDeviceVolume_syncVSS(device);
                    }
                    mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice);
                }
            }
        }
        // Post a persist volume msg
        sendMsg(mAudioHandler,
                MSG_PERSIST_VOLUME,
                SENDMSG_QUEUE,
                device,
                0,
                streamState,
                PERSIST_DELAY);

    }

这里主要做了三步,先看第一步即设置音量applyDeviceVolume_syncVSS将音量通过AudioSystem设置到native的AudioPolicyManager中;第二步是 mStreamStates[streamType].applyDeviceVolume_syncVSS(streamDevice)即设置相同组别的其他streamType的音量;最后一步发送MSG_PERSIST_VOLUME消息。将音量保存起来。
这样adjustVolume的流程就走完了。

setStreamVolume

设置指定streamType的音量。和adjustVolume的流程类似,但是相对adjustVolume而言setStreamVolume直接在AudioManager中调用到AudioService中的setStreamVolume。然后一些特殊的判断后会调用到onSetStreamVolume

    private void onSetStreamVolume(int streamType, int index, int flags, int device,
            String caller, boolean hasModifyAudioSettings) {
        final int stream = mStreamVolumeAlias[streamType];
        setStreamVolumeInt(stream, index, device, false, caller, hasModifyAudioSettings);
        // setting volume on ui sounds stream type also controls silent mode
        if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
                (stream == getUiSoundsStreamType())) {
            setRingerMode(getNewRingerMode(stream, index, flags),
                    TAG + ".onSetStreamVolume", false /*external*/);
        }
        // setting non-zero volume for a muted stream unmutes the stream and vice versa,
        // except for BT SCO stream where only explicit mute is allowed to comply to BT requirements
        if (streamType != AudioSystem.STREAM_BLUETOOTH_SCO) {
            mStreamStates[stream].mute(index == 0);
        }
    }

然后是setStreamVolumeInt,然后streamState.setIndex到这里就和adjustVolume的逻辑相同了。

总结

AudioManager的音量调节无论是adjustVolume还是setStreamVolume其实核心逻辑都是一样的,

  • 更新VolumeStreamState中index,并存入mIndexMap中(主要为了getStreamVolume使用),将index通过AudioSystem设置下去
  • 更新同组别的其他streamType的音量,音量组别可以通过int[] STREAM_VOLUME_ALIAS_VOICE查看
  • 音量存储到数据库中
    以上,欢迎大家一起沟通讨论喜欢就点下关注吧
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

轻量级LZ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值