源码分析基于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_CALLAudioSystem.STREAM_RING,            // STREAM_SYSTEMAudioSystem.STREAM_RING,            // STREAM_RINGAudioSystem.STREAM_MUSIC,           // STREAM_MUSICAudioSystem.STREAM_ALARM,           // STREAM_ALARMAudioSystem.STREAM_RING,            // STREAM_NOTIFICATIONAudioSystem.STREAM_BLUETOOTH_SCO,   // STREAM_BLUETOOTH_SCOAudioSystem.STREAM_RING,            // STREAM_SYSTEM_ENFORCEDAudioSystem.STREAM_RING,            // STREAM_DTMFAudioSystem.STREAM_MUSIC,           // STREAM_TTSAudioSystem.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 != -1streamType = 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 requestsif (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 deviceif ((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 commandsynchronized (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 rangeif (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 aliasstep = 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 mutedif (direction == AudioManager.ADJUST_RAISE) {// unmute immediately for volume upstreamState.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 mutedif (!mVolumeCurves->canBeMuted(stream)) index = mVolumeCurves->getVolumeIndexMax(stream);// update other private stream volumes which follow this onefor (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// enabledfloat 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 setif (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层

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

Android 音频源码分析——音量调节流程相关推荐

  1. Android 音频源码分析——AndroidRecord录音(一)

    Android 音频源码分析--AndroidRecord录音(一) Android 音频源码分析--AndroidRecord录音(二) Android 音频源码分析--AndroidRecord音 ...

  2. Android 音频源码分析——AudioTrack设备选择

    Android 音频源码分析--AndroidRecord录音(一) Android 音频源码分析--AndroidRecord录音(二) Android 音频源码分析--AndroidRecord音 ...

  3. Android 音频源码分析——音频设备切换(插入耳机)

    源码分析基于android9.0 通常带线耳机分类 模拟耳机 平时常用的3.5mm或6.3mm接口耳机,接收模拟信号(音频数据需要先处理,转码成pcm格式) Android中模拟耳机由WiredAcc ...

  4. 【Android SDM660源码分析】- 02 - UEFI XBL QcomChargerApp充电流程代码分析

    [Android SDM660源码分析]- 02 - UEFI XBL QcomChargerApp充电流程代码分析 一.加载 UEFI 默认应用程序 1.1 LaunchDefaultBDSApps ...

  5. 【Android SDM660源码分析】- 03 - UEFI XBL GraphicsOutput BMP图片显示流程

    [Android SDM660源码分析]- 03 - UEFI XBL GraphicsOutput BMP图片显示流程 1. GraphicsOutput.h 2. 显示驱动初化 DisplayDx ...

  6. 【Android SDM660源码分析】- 01 - 如何创建 UEFI XBL Protocol DXE_DRIVER 驱动及UEFI_APPLICATION 应用程序

    [Android SDM660源码分析]- 01 - 如何创建 UEFI XBL Protocol DXE_DRIVER 驱动及UEFI_APPLICATION 应用程序 一.创建DXE_DRIVER ...

  7. 【Android 10 源码】MediaRecorder 录像流程:MediaRecorder 初始化

    MediaRecorder 用于录制音频和视频,录制控制基于一个简单的状态机.下面是典型的使用 camera2 API 录制,需要使用到的 MediaRecorder API. MediaRecord ...

  8. 【Android 10 源码】MediaRecorder 录像流程:MediaRecorder 配置

    MediaRecorder 录像配置主要涉及输出文件路径.音频来源.视频来源.输出格式.音频编码格式.视频编码格式.比特率.帧率和视频尺寸等. 我们假设视频输入源来自 Camera,Camera2 A ...

  9. Android ADB 源码分析(三)

    前言 之前分析的两篇文章 Android Adb 源码分析(一) 嵌入式Linux:Android root破解原理(二) 写完之后,都没有写到相关的实现代码,这篇文章写下ADB的通信流程的一些细节 ...

最新文章

  1. 我们如此努力,也不过是个普通人
  2. Redis 宝典 | 基础、高级特性与性能调优
  3. 单线程下的生产者--消费者模式详解,wait和sleep的区别
  4. 电脑远程服务_电脑远程维修专家在线服务
  5. Docker安装稳定版及指定版本
  6. 在做模具设计过程中应注意哪些问题
  7. c语言二进制十进制十六进制之间的转化(详解,含源代码)
  8. 如何修改图片尺寸,分辨率大小,三种方式对比。
  9. 2019年小米软件开发《异数》笔试题
  10. 今天遇到安装CAD2014提示已安装磁盘空间显示0字节,无法下一步,已解决.#CAD2014提示已安装磁盘空间显示0字节无法下一步
  11. c#怎么设置winform中dgv的表头边框线
  12. 专利大战中 苹果被迫曝光的8个秘密
  13. Android:alpha换算表
  14. 使用Bootstrap做轮播图
  15. 把扫描文件转变为word文档的最实用的四款OCR识别软件
  16. 深度学习模型的构建三、优化函数optimizer
  17. 打造高效研发团队 (2) —— 研发流程篇
  18. centos编译Aegisub
  19. 单调时钟与墙上时钟的区别?ntp如何优雅校时?
  20. MIKE 21 教程 1.5 基于卫星影像绘制河道边界

热门文章

  1. OpenStack Dashboard
  2. Spark Scala当中reduceByKey(_+_) reduceByKey((x,y) => x+y)的用法
  3. Linux != Windows
  4. mac搭建python开发环境_Mac打造python2 python3开发环境
  5. html5 canvas代替flash,替代Flash技术最好的选择------HTML5
  6. FreeMind的常见问题和安装
  7. 使用Python,OpenCV构建移动文档扫描仪
  8. SAP OBY8 删除会计科目表报错 Message no. FC095
  9. Oracle--加锁的方法
  10. 判断小米 魅族 华为 系统 MIUI EMUI FLYME