欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-05-12 09:17:32
...

源码分析基于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. 应用层通过AudioManager接口调节;
    adjustStreamVolume 和setStreamVolume等
  2. 系统音量按键调节,framework调用AudioService接口

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

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;
                }
            }
        }
    }
    return status;
}

对以上符合调节的输出和流进行调用checkAndSetVolume;

AudioPolicyManager::checkAndSetVolume ——》SwAudioOutputDescriptor::setVolume

bool SwAudioOutputDescriptor::setVolume(float volume,
                                        audio_stream_type_t stream,
                                        audio_devices_t device,
                                        uint32_t delayMs,
                                        bool force)
{
    bool changed = AudioOutputDescriptor::setVolume(volume, stream, device, delayMs, force);

    if (changed) {
        // Force VOICE_CALL to track BLUETOOTH_SCO stream volume when bluetooth audio is
        // enabled
        float volume = Volume::DbToAmpl(mCurVolume[stream]);
        if (stream == AUDIO_STREAM_BLUETOOTH_SCO) {
            mClientInterface->setStreamVolume(
                    AUDIO_STREAM_VOICE_CALL, volume, mIoHandle, delayMs);
        }
        mClientInterface->setStreamVolume(stream, volume, mIoHandle, delayMs);
    }
    return changed;
}

bool AudioOutputDescriptor::setVolume(float volume,
                                      audio_stream_type_t stream,
                                      audio_devices_t device __unused,
                                      uint32_t delayMs,
                                      bool force)
{
    // We actually change the volume if:
    // - the float value returned by computeVolume() changed
    // - the force flag is set
    if (volume != mCurVolume[stream] || force) {
        ALOGV("setVolume() for stream %d, volume %f, delay %d", stream, volume, delayMs);
        mCurVolume[stream] = volume;
        return true;
    }
    return false;
}

首先检查是否需要真正修改音量:

  • 对比volume值是否有变化
  • force 是否为true;

如果符合条件,则change为ture,调用mClientInterface->setStreamVolume

流程如下:

mClientInterface->setStreamVolume(stream, volume, mIoHandle, delayMs);
>>AudioPolicyService::AudioPolicyClient::setStreamVolume;
>>mAudioCommandThread->volumeCommand;
>>AudioSystem::setStreamVolume;
...  
>>AudioFlinger::setStreamVolume;
>>volumeInterface->setStreamVolume(stream, value);

这里volumeInterface对应为PlaybackThread。

void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
{
    Mutex::Autolock _l(mLock);
    mStreamTypes[stream].volume = value;
    broadcast_l();
}

mStreamTypes保存该流的音量值。

这里只追到将音量值保存下来,推测其使用如下

  • AudioFlinger::MixerThread::prepareTracks_l

  • AudioFlinger::DirectOutputThread::prepareTracks_l

对于mixer类的音频通路,将音量值交给AudioMixer处理。

AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
        Vector< sp<Track> > *tracksToRemove)
{
    //........
    const float vh = track->getVolumeHandler()->getVolume(
        proxy->framesReleased()).first;
    float volume = masterVolume
        * mStreamTypes[track->streamType()].volume
        * vh;
    track->mCachedVolume = volume;
    gain_minifloat_packed_t vlr = proxy->getVolumeLR();
    float vlf = volume * float_from_gain(gain_minifloat_unpack_left(vlr));
    float vrf = volume * float_from_gain(gain_minifloat_unpack_right(vlr));
    track->setFinalVolume((vlf + vrf) / 2.f);
    //......
    mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, &vlf);
    mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, &vrf);   
}

对于Direct类音频通路,将音量值设置到底层设备中。

void AudioFlinger::DirectOutputThread::processVolume_l(Track *track, bool lastTrack)
{
	//.....

    if (lastTrack) {
        track->setFinalVolume((left + right) / 2.f);
        if (left != mLeftVolFloat || right != mRightVolFloat) {
            mLeftVolFloat = left;
            mRightVolFloat = right;

            uint32_t vl = (uint32_t)(left * (1 << 24));
            uint32_t vr = (uint32_t)(right * (1 << 24));

            if (!mEffectChains.isEmpty()) {
                mEffectChains[0]->setVolume_l(&vl, &vr);
                left = (float)vl / (1 << 24);
                right = (float)vr / (1 << 24);
            }
            status_t result = mOutput->stream->setVolume(left, right);
        }
    }
}

mOutput->stream->setVolume将音量设置到hal层

到这里基本分析完毕了,欢迎大家进行讨论、评论、指正。