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

Android 音频 专栏收录该内容
16 篇文章 7 订阅

源码分析基于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;
                }
            }
        }
    }
    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层

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

  • 2
    点赞
  • 9
    评论
  • 3
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值