Android 音频源码分析——音量调节流程

Posted VNanyesheshou

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 音频源码分析——音量调节流程相关的知识,希望对你有一定的参考价值。

源码分析基于android9.0

一、声音类型

对于大多数手机用户来说,操作手机音量按键可以看到,声音类型分为四种:媒体、铃声、闹钟、通话,但是其系统内部则分为十几种类型。

声⾳类型用来区分不同播放用途及播放设备,包含11中类型

定义在 frameworks/base/media/java/android/media/Audiosystem.java中

/** Used to identify the default audio stream volume */
public static final int STREAM_DEFAULT = -1;
/** Used to identify the volume of audio streams for phone calls */
public static final int STREAM_VOICE_CALL = 0;
/** Used to identify the volume of audio streams for system sounds */
public static final int STREAM_SYSTEM = 1;
/** Used to identify the volume of audio streams for the phone ring and message alerts */
public static final int STREAM_RING = 2;
/** Used to identify the volume of audio streams for music playback */
public static final int STREAM_MUSIC = 3;
/** Used to identify the volume of audio streams for alarms */
public static final int STREAM_ALARM = 4;
/** Used to identify the volume of audio streams for notifications */
public static final int STREAM_NOTIFICATION = 5;
/** Used to identify the volume of audio streams for phone calls when connected on bluetooth */
public static final int STREAM_BLUETOOTH_SCO = 6;
/** Used to identify the volume of audio streams for enforced system sounds in certain
 * countries (e.g camera in Japan) */
public static final int STREAM_SYSTEM_ENFORCED = 7;
/** Used to identify the volume of audio streams for DTMF tones */
public static final int STREAM_DTMF = 8;
/** Used to identify the volume of audio streams exclusively transmitted through the
 *  speaker (TTS) of the device */
public static final int STREAM_TTS = 9;
/** Used to identify the volume of audio streams for accessibility prompts */
public static final int STREAM_ACCESSIBILITY = 10;

虽然是11种类型,但许多是使用相同的音量配置。

在 frameworks/base/services/core/java/com/android/server/audio/AudioService.java

定义声音类型的具体使用类型,通过调节几个类型,从而控制所有的声音的音量。

private final int[] STREAM_VOLUME_ALIAS_DEFAULT = 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
;

二、音量调节

Android调节音量主要有三种方式:

  1. 系统音量按键调节,framework调用AudioService接口
  2. 应用层通过AudioManager接口调节;
    adjustStreamVolume 和setStreamVolume等
  3. AudioTrack调节音量,使用setVolume接口;

流程图如下:

这里分析下系统音量按键调节流程

framework层调节

PhoneWindowManager可以管理系统的一些按键事件:home键、返回键、Menu键、音量键等。

它接收到音量加减事件,如下函数:

private void dispatchDirectAudioEvent(KeyEvent event) 
    if (event.getAction() != KeyEvent.ACTION_DOWN) 
        return;
    
    int keyCode = event.getKeyCode();
    int flags = AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_PLAY_SOUND
            | AudioManager.FLAG_FROM_KEY;
    String pkgName = mContext.getOpPackageName();
    switch (keyCode) 
        case KeyEvent.KEYCODE_VOLUME_UP:
            try 
                getAudioService().adjustSuggestedStreamVolume(AudioManager.ADJUST_RAISE,
                        AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
             catch (Exception e) 
                Log.e(TAG, "Error dispatching volume up in dispatchTvAudioEvent.", e);
            
            break;
        case KeyEvent.KEYCODE_VOLUME_DOWN:
            try 
                getAudioService().adjustSuggestedStreamVolume(AudioManager.ADJUST_LOWER,
                        AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
             catch (Exception e) 
                Log.e(TAG, "Error dispatching volume down in dispatchTvAudioEvent.", e);
            
            break;
        case KeyEvent.KEYCODE_VOLUME_MUTE:
            try 
                if (event.getRepeatCount() == 0) 
                    getAudioService().adjustSuggestedStreamVolume(
                            AudioManager.ADJUST_TOGGLE_MUTE,
                            AudioManager.USE_DEFAULT_STREAM_TYPE, flags, pkgName, TAG);
                
             catch (Exception e) 
                Log.e(TAG, "Error dispatching mute in dispatchTvAudioEvent.", e);
            
            break;
    

对于音量加减都会调用getAudioService().adjustSuggestedStreamVolume,只是参数不同区分是加还是减。getAudioService获取AudioService,调用其接口调节。

AudioService

public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
        String callingPackage, String caller) 
    final IAudioPolicyCallback extVolCtlr;
    synchronized (mExtVolumeControllerLock) 
        extVolCtlr = mExtVolumeController;
    
    if (extVolCtlr != null) 
        sendMsg(mAudioHandler, MSG_NOTIFY_VOL_EVENT, SENDMSG_QUEUE,
                direction, 0 /*ignored*/,
                extVolCtlr, 0 /*delay*/);
     else 
        adjustSuggestedStreamVolume(direction, suggestedStreamType, flags, callingPackage,
                caller, Binder.getCallingUid());
    

adjustSuggestedStreamVolume调用重载函数,参数不一致,新加了调用uid参数

private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
        String callingPackage, String caller, int uid) 
    final int streamType;
    synchronized (mForceControlStreamLock) 
        // Request lock in case mVolumeControlStream is changed by other thread.
        if (mUserSelectedVolumeControlStream)  // implies mVolumeControlStream != -1
            streamType = mVolumeControlStream;
         else 
            final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType);
            final boolean activeForReal;
            if (maybeActiveStreamType == AudioSystem.STREAM_RING
                    || maybeActiveStreamType == AudioSystem.STREAM_NOTIFICATION) 
                activeForReal = wasStreamActiveRecently(maybeActiveStreamType, 0);
             else 
                activeForReal = AudioSystem.isStreamActive(maybeActiveStreamType, 0);
            
            if (activeForReal || mVolumeControlStream == -1) 
                streamType = maybeActiveStreamType;
             else 
                streamType = mVolumeControlStream;
            
        
    

    final boolean isMute = isMuteAdjust(direction);

    ensureValidStreamType(streamType);
    final int resolvedStream = mStreamVolumeAlias[streamType];

    // Play sounds on STREAM_RING only.
    if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
            resolvedStream != AudioSystem.STREAM_RING) 
        flags &= ~AudioManager.FLAG_PLAY_SOUND;
    

    // For notifications/ring, show the ui before making any adjustments
    // Don't suppress mute/unmute requests
    if (mVolumeController.suppressAdjustment(resolvedStream, flags, isMute)) 
        direction = 0;
        flags &= ~AudioManager.FLAG_PLAY_SOUND;
        flags &= ~AudioManager.FLAG_VIBRATE;
        if (DEBUG_VOL) Log.d(TAG, "Volume controller suppressed adjustment");
    

    adjustStreamVolume(streamType, direction, flags, callingPackage, caller, uid);

  • streamType 获取当前要控制的流类型;
  • resolvedStream 通过streamType转换为其对于的音频流类型别名;
  • 对于通知和铃声类型,显示调节音量ui;
  • 调用adjustStreamVolume;

接着看adjustStreamVolume

protected void adjustStreamVolume(int streamType, int direction, int flags,
        String callingPackage, String caller, int uid) 
	//...

    int streamTypeAlias = mStreamVolumeAlias[streamType];

    VolumeStreamState streamState = mStreamStates[streamTypeAlias];

    final int device = getDeviceForStream(streamTypeAlias);

    int aliasIndex = streamState.getIndex(device);
    boolean adjustVolume = true;
    int step;

    // skip a2dp absolute volume control request when the device
    // is not an a2dp device
    if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) == 0 &&
        (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) != 0) 
        return;
    

    // If we are being called by the system (e.g. hardware keys) check for current user
    // so we handle user restrictions correctly.
    if (uid == android.os.Process.SYSTEM_UID) 
        uid = UserHandle.getUid(getCurrentUserId(), UserHandle.getAppId(uid));
    
    if (mAppOps.noteOp(STREAM_VOLUME_OPS[streamTypeAlias], uid, callingPackage)
            != AppOpsManager.MODE_ALLOWED) 
        return;
    

    // reset any pending volume command
    synchronized (mSafeMediaVolumeState) 
        mPendingVolumeCommand = null;
    

    flags &= ~AudioManager.FLAG_FIXED_VOLUME;
    if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) &&
           ((device & mFixedVolumeDevices) != 0)) 
        flags |= AudioManager.FLAG_FIXED_VOLUME;

        // Always toggle between max safe volume and 0 for fixed volume devices where safe
        // volume is enforced, and max and 0 for the others.
        // This is simulated by stepping by the full allowed volume range
        if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&
                (device & mSafeMediaVolumeDevices) != 0) 
            step = safeMediaVolumeIndex(device);
         else 
            step = streamState.getMaxIndex();
        
        if (aliasIndex != 0) 
            aliasIndex = step;
        
     else 
        // convert one UI step (+/-1) into a number of internal units on the stream alias
        step = rescaleIndex(10, streamType, streamTypeAlias);
    

   	if (streamState.adjustIndex(direction * step, device, caller)
                || streamState.mIsMuted) 
            // Post message to set system volume (it in turn will post a
            // message to persist).
            if (streamState.mIsMuted) 
                // Unmute the stream if it was previously muted
                if (direction == AudioManager.ADJUST_RAISE) 
                    // unmute immediately for volume up
                    streamState.mute(false);
                 else if (direction == AudioManager.ADJUST_LOWER) 
                    if (mIsSingleVolume) 
                        sendMsg(mAudioHandler, MSG_UNMUTE_STREAM, SENDMSG_QUEUE,
                                streamTypeAlias, flags, null, UNMUTE_STREAM_DELAY);
                    
                
            
            sendMsg(mAudioHandler,
                    MSG_SET_DEVICE_VOLUME,
                    SENDMSG_QUEUE,
                    device,
                    0,
                    streamState,
                    0);
        
        
    //.......
    int index = mStreamStates[streamType].getIndex(device);
    sendVolumeUpdate(streamType, oldIndex, index, flags);

  • streamTypeAlias 获取音频流类型别名;
  • streamState获取该音频流 状态;device 获取对应音频设备;
  • step 转换为音频流调节的单位;
  • streamState.adjustIndex 调节音量值保存在VolumeStreamState中;
  • 发送MSG_SET_DEVICE_VOLUME 消息,调节音量

MSG_SET_DEVICE_VOLUME 调用流程

AudioService.setDeviceVolume()
>>streamState.applyDeviceVolume_syncVSS(device);
>>AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
>>android_media_AudioSystem_setStreamVolumeIndex(JNIEnv *env,
                                               jobject thiz,
                                               jint stream,
                                               jint index,
                                               jint device);
>>AudioSystem::setStreamVolumeIndex(static_cast <audio_stream_type_t>(stream),
                                              index,
                                              (audio_devices_t)device);
>>aps->setStreamVolumeIndex(stream, index, device);
>>AudioPolicyService::setStreamVolumeIndex(audio_stream_type_t stream,
                                                  int index,
                                                  audio_devices_t device);
>>AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream,
                                                  int index,
                                                  audio_devices_t device)

audioserver层调节

这里接着看 AudioPolicyManager::setStreamVolumeIndex

status_t AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t stream,
                                                  int index,
                                                  audio_devices_t device)


    // VOICE_CALL stream has minVolumeIndex > 0  but can be muted directly by an
    // app that has MODIFY_PHONE_STATE permission.
    if (((index < mVolumeCurves->getVolumeIndexMin(stream)) &&
            !(stream == AUDIO_STREAM_VOICE_CALL && index == 0)) ||
            (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);
    

    // 更新符合以下条件的所有输出和流的音量:
    // -请求的流(或与音量控制匹配的流)在输出中处于活动状态
    // -与该流相对应的策略选择的设备(一个或多个),包括请求的设备
    // -对于非默认请求的设备,输出中当前选择的设备是请求的设备或策略选择的设备之一
    // - 对于默认的请求设备(AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME),
    // 仅当当前所选设备不存在特定设备音量值时,才应用音量。
    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 = Volume::getDeviceForVolume(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, curStreamDevice);
            

            if (applyVolume) 
                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;
                Android 音频源码分析——音量调节流程

Android 音频源码分析——音量调节流程

Android App开发音量调节中实现拖动条和滑动条和音频管理器AudioManager讲解及实战(超详细 附源码和演示视频)

Android6.0 源码修改之Settings音量调节界面增加通话音量调节

android音频系统音量控制探讨

Html 音频音量调节