请问大神,不使用audiorecord,怎样使用tinyalsa英文名来实现USB麦克风的录音?感谢!

S3C6410的IIS驱动录音噪音问题
[问题点数:40分,结帖人molabo]
本版专家分:115
结帖率 95.45%
CSDN今日推荐
本版专家分:70
本版专家分:501
本版专家分:3927
本版专家分:2428
本版专家分:115
本版专家分:3927
匿名用户不能发表回复!
其他相关推荐Android 关于蓝牙mic和speaker的输入输出
[问题点数:40分,无满意结帖,结帖人zhishui_Jolen]
本版专家分:0
结帖率 100%
CSDN今日推荐
本版专家分:47523
2011年3月 移动平台大版内专家分月排行榜第一2009年3月 移动平台大版内专家分月排行榜第一2009年2月 移动平台大版内专家分月排行榜第一
2012年2月 移动平台大版内专家分月排行榜第二2012年1月 移动平台大版内专家分月排行榜第二2011年5月 移动平台大版内专家分月排行榜第二2011年4月 移动平台大版内专家分月排行榜第二2009年1月 移动平台大版内专家分月排行榜第二
2011年12月 移动平台大版内专家分月排行榜第三2011年11月 移动平台大版内专家分月排行榜第三2011年9月 移动平台大版内专家分月排行榜第三2011年8月 移动平台大版内专家分月排行榜第三2010年3月 移动平台大版内专家分月排行榜第三2010年2月 移动平台大版内专家分月排行榜第三2009年4月 移动平台大版内专家分月排行榜第三2009年3月 硬件/嵌入开发大版内专家分月排行榜第三
本版专家分:47523
2011年3月 移动平台大版内专家分月排行榜第一2009年3月 移动平台大版内专家分月排行榜第一2009年2月 移动平台大版内专家分月排行榜第一
2012年2月 移动平台大版内专家分月排行榜第二2012年1月 移动平台大版内专家分月排行榜第二2011年5月 移动平台大版内专家分月排行榜第二2011年4月 移动平台大版内专家分月排行榜第二2009年1月 移动平台大版内专家分月排行榜第二
2011年12月 移动平台大版内专家分月排行榜第三2011年11月 移动平台大版内专家分月排行榜第三2011年9月 移动平台大版内专家分月排行榜第三2011年8月 移动平台大版内专家分月排行榜第三2010年3月 移动平台大版内专家分月排行榜第三2010年2月 移动平台大版内专家分月排行榜第三2009年4月 移动平台大版内专家分月排行榜第三2009年3月 硬件/嵌入开发大版内专家分月排行榜第三
本版专家分:0
本版专家分:540
本版专家分:42
本版专家分:95
本版专家分:0
匿名用户不能发表回复!
其他相关推荐/************************************ Author:刘江明 * Environment:MTK Android 6.0* Date:日***********************************/
& & & & Android音频系统是一套基于Linux ALSA上二次封装开发的一套音频系统,中间进行了很多的功能封装,但最终会用到Linux ALSA。所以在Hal层的类名都会包含ALSA。对于MTK的Android audio,MTK也有一定的介绍,先来大体了解一下:
& & & & & & & & &&
图1 &音频系统的Framework图
& & & & 提供给应用的功能接口有:AudioTrack与AudioRecord两个类,分别是播放与录音的功能接口。这两个功能接口会通过AudioSystem调用AudioFlinger。AudioFlinger对管控着所有的Buf与播放,同时AudioFlinger也受AudioSystem管理,其中的管理策略来自于AudioPolicy,AudioPolicy会决定这个是什么类型的音频以及音频的rounting,是音乐,还是打电话,音乐是能打开什么播放设备,是打开喇叭还是打开听筒播放,电话来了是否要把音乐关掉等等的音频策略,包括MTK的音频参数也会在这里加载进来
&图2 &音频系统的Hal图
& & & & 图一的AudioFlinger会进入到Hal层,Audio Hal层有几大重要的类:
& & & & AudioALSAStreamManager是入口管理下面的AudioALSAStreamIn和AudioALSAStreamOut
& & & &&AudioALSAStreamOut管理着AudioALSAPlaybackXXXX
& && &&&AudioALSAStreamIn管理着AudioALSACaptureXXXX,
& && & &AudioALSAPlaybackXXXX与AudioALSACaptureXXXX这两个类里面的主要函数是open(),read()和write(),主要是负责对PCM
buf的读写到Linux 的ALSA里面。
& && &&&AudioALSASpeechXXXX类是Aduio的一个算法处理。
& && &&&AudioALSAHardwareResourceManager这个类主要用于打开和关闭硬件设备,如MIC,喇叭等
& & & & AudioALSAVolumeController,这个类在上图没有体现,但是也很常用,主要用于Audio系统的音量控制,音量补偿,音频参数也在此得到应用
& & & &&HAL与ALSA对接使用了TinyALSA库,这个很重要。TinyALSA是一个轻量级的封装库,对ALSA接口进行了二次封装,简化了对ALSA的操作,具体源码目录在/external/tinyalsa。这个库衔接了Hal与Linux,这个是连接驱动的关键,一开始我针对Linux ALSA在HAL一顿狂找结果还是吃了闭门羹
& & & & 网上有个说就法,Google为了避免与Linux有版权的争议,自己能在源码上有更多的自己说话的权利,对Linux原生的ALSA进行了大量的修改,裁剪,去掉Linux GPL等协议。所有的Buf的处理交给了AudioTrack去处理,所以会有高延迟,低速率等问题,以至于Android手机无法做出高音质的手机。
二. 了解音频系统架构
& & & & 音频系统的AudioFlinger和AudioPolicy里有很复杂的Buf调动和Buf管理,也有很复杂的音频策略,里面考虑到了音频能遇到的各种状况。从这两个类下手很容易掉到坑里出不来。Loopback是MTK音频系统提供的一个工厂测试方法,其使用方法简单粗暴,运行过程也简单粗暴。使能它之后就可以任意的让主副MIC与喇叭听筒组合出声。它就相当于HAL层的一个直接使用HAL层API的应用。对我们了解Android
Audio系统代码的分布与功能有很大的帮助。从Loopback下手再回到AudioFlinger和AudioPolicy会更好。Loopback也分为两种模式一种是AFE模式,一种是Acoustic,通俗的说法就是前者是吹所模式,只能响应吹气声音,后者就是普通的声音输出。两者在流程上大体是一至的,声音模式会比吹气模式多出了一个Speech的控制,也就是多出了一个语音的算法处理。先从简单的下手,两种模式的比较也会让我们更容易了解代码。
(1)调用Loopback流程
& & & & 涉及到的文件:
& && & &frameworks/av/media/libmedia/AudioSystem.cpp
& & & & frameworks/av/services/audioflinger/AudioFlinger.cpp
& & & & vendor/mediatek/proprietary/hardware/audio/common/V3/aud_drv/AudioALSAHardware.cpp
& & & & vendor/mediatek/proprietary/hardware/audio/common/V3/aud_drv/LoopbackManager.cpp
& & & & vendor/mediatek/proprietary/hardware/audio/common/V3/aud_drv/AudioALSALoopbackController.cpp
& & & &&vendor/mediatek/proprietary/hardware/audio/common/V3/aud_drv/AudioALSADeviceConfigManager.cpp
& & & & vendor//mediatek/proprietary/hardware/audio/mt6735/aud_drv/AudioALSAHardwareResourceManager.cpp
& & & & vendor//mediatek/proprietary/hardware/audio/mt6735/aud_drv/AudioALSAVolumeController.cpp & &
& & & & Loopback使用方法是在APP中直接使用这个方法:AudioSystem.setParameters(“SET_LOOPBACK_TYPE=Type, OutputDevice”);。要启用主MIC进喇叭出的吹气模式Type为1,OutputDevice为3。这个参数会层层调用到HAL启动Loopback。我们可以跟踪这个参数的流向了解一下Audio系统是怎么分布的。
& & & & 前面一小段的流程大致是这样:AudioSystem.java--&android_media_AudioSystem.cpp--&AudioSystem.cpp
//AudioSystem.cppstatus_t AudioSystem::setParameters(const String8 &keyValuePairs){
//第一个参数为:AUDIO_IO_HANDLE_NONE
return setParameters(AUDIO_IO_HANDLE_NONE, keyValuePairs);}status_t AudioSystem::setParameters(audio_io_handle_t ioHandle, const String8 &keyValuePairs){
//省略掉无关逻辑
const sp&IAudioFlinger& &af = AudioSystem::get_audio_flinger();
if (af == 0) return PERMISSION_DENIED;
ret = af-&setParameters(ioHandle, keyValuePairs);}
& & & & 上面直接调用到AudioFlinger的setParameters()&&
//AudioFlinger.cppstatus_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs){
//上面传下来的ioHandle = AUDIO_IO_HANDLE_NONE
if (ioHandle == AUDIO_IO_HANDLE_NONE) {
mHardwareStatus = AUDIO_HW_SET_PARAMETER;
for (size_t i = 0; i & mAudioHwDevs.size(); i++) {
//mAudioHwDevs数组是HAL层“Dev”的集合,这里的Dev并不是指真正的具体设备
//而是把HAL的一个大模块抽象为一个“Dev”,例如,MTK音频HAL可以取名为“MTK Audio HAL”
//然后还有高通的“Qual Audio HAL”等等,还能细分为Wifi,USB,A2DP.....
audio_hw_device_t *dev = mAudioHwDevs.valueAt(i)-&hwDevice();
status_t result = dev-&set_parameters(dev, keyValuePairs.string());
& & & & mAudioHwDevs这个数组又是怎么来的呢?在AudioPolicyManager的构造函数里,会向本地文件加载一个audio_policy.conf。该config文件会决定音频系统有哪些通路,USB,A2DP等等,这些通路下面有哪些设备,还有一些设备的参数。大概摘抄一点
audio_hw_modules {
global_configuration {
attached_output_devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_EARPIECE
default_output_device AUDIO_DEVICE_OUT_SPEAKER
attached_input_devices AUDIO_DEVICE_IN_BUILTIN_MIC|AUDIO_DEVICE_IN_FM_TUNER|AUDIO_DEVICE_IN_VOICE_CALL
audio_hal_version 3.0
type AUDIO_DEVICE_OUT_WIRED_HEADSET
mode AUDIO_GAIN_MODE_JOINT
channel_mask AUDIO_CHANNEL_OUT_STEREO
min_value_mB -6400
max_value_mB 0
default_value_mB 0
step_value_mB 100
min_ramp_ms 0
max_ramp_ms 0
& & & &这些模块名字加载好后会跟据这些名字循环地去查找HAL模块,把找到的模块填入到mAudioHwDevs中。这段逻辑目前看得不是很仔细,有些逻辑不严谨
//AudioFlinger.cppaudio_module_handle_t AudioFlinger::loadHwModule_l(const char *name){
audio_hw_device_t *dev;
int rc = load_audio_interface(name, &dev);
mAudioHwDevs.add(handle, new AudioHwDevice(handle, name, dev, flags));}static int load_audio_interface(const char *if_name, audio_hw_device_t **dev){
rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod);}
& & & & &mAudioHwDevs数组是找到了如何初始化,setParameters()调用了该元素里的set_parameters()函数继续往下传参数。我还要找到具体的HAL模块才知道参数如何往下传。&hw_get_module_by_class()这个API会通过ID去找到HAL注册了哪些模块。我们搜一下上面的AUDIO_HARDWARE_MODULE_ID就可以找到具体是加载了哪些模块,在Vendor目录下现有两个HAL模块
(1)MTK自己实现的Audio HAL,名字和ID如下
AUDIO_HARDWARE_MODULE_ID,
name: &MTK Audio HW HAL&(2)还有一个是A2DP的
AUDIO_HARDWARE_MODULE_ID,
name: &A2DP Audio HW HAL&
& & & & 就这样把HAL模块加载出来了,所有的HAL模块都明非常标准和非常明确的接口定义,对于HAL以上的逻辑,只需找到ID和相应的名字即可找到需要使用的模块,即使你有100个厂商,100个厂商又有100个模块,还是依照明确的标准去走,这个就是面向对象编程中的一个核心理念,面向接口编程,不管你逻辑如何变,接口一定不能变!这样就能确保软件的低耦合,可移植。
& & & & 我们用的是“MTK Audio HW HAL”这个Audio HAL module。很容易地就在里面找到了set_parameters函数指针。指向adev_set_parameters()函数& & & &&
static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs){
struct legacy_audio_device *ladev = to_ladev(dev);
//结过转换,调用到的是AudioALSAHardware.cpp
return ladev-&hwif-&setParameters(String8(kvpairs));}
& & & & AudioALSAHardware::setParameters()是Audio setParameters()的最终执行函数,也是一个很有趣的函数。这个函数就像Audio系统的“后门”,里面可以粗暴有力的设置Audio参数,而影响整个Audio系统的运行。例如,可以调整音量,打开关闭MIC,FM的音频开关等等,知道这些暗门不知道能不能干一点坏事。就像我们强制打开Loopback,声音就开始从MIC进喇叭出,普通用户还没有办法关闭,除非重启平板,细思极恐。然后里面还保留着一些音频设备的测试方法和校准方法。
status_t AudioALSAHardware::setParameters(const String8 &keyValuePairs){
// Loopback
if (param.get(keySET_LOOPBACK_TYPE, value_str) == NO_ERROR)
if (loopback_type == NO_LOOPBACK) // close loopback
LoopbackManager::GetInstance()-&SetLoopbackOff();
else // open loopback
LoopbackManager::GetInstance()-&SetLoopbackOn(loopback_type, loopback_output_device);
& & & &&至此我们APP设备的参数就成功地传递到了HAL层,并开始操控Loopback。从下面的方法开始,正式进入到Loopback中
& && && &LoopbackManager::GetInstance()-&SetLoopbackOn(loopback_type, loopback_output_device);
(2)Loopback工作流程
& & & &(A)源码流程
& & & & 从SetLoopbackOn()开始,先看一下SetLoopbackOn的流程,再从此处展开
//LoopbackManager.cppstatus_t LoopbackManager::SetLoopbackOn(loopback_t loopback_type, loopback_output_device_t loopback_output_device){
//关闭所有的音频输出流,并重新开始准备 //也就是我们之前说的pcm_write,把数据定到ALSA中 //但是Loopback中并没有用到StreamManager
AudioALSAStreamManager::getInstance()-&setAllStreamsSuspend(true);
AudioALSAStreamManager::getInstance()-&standbyAllStreams();
// get loopback dev 获得Input dev为AUDIO_DEVICE_IN_BUILTIN_MIC,这个值后面有用
audio_devices_t input_device
= GetInputDeviceByLoopbackType(loopback_type);
audio_devices_t output_device = GetOutputDeviceByLoopbackType(loopback_type, loopback_output_device);
// set specific mic type
if (loopback_type == AP_MAIN_MIC_AFE_LOOPBACK || loopback_type == MD_MAIN_MIC_ACOUSTIC_LOOPBACK)
//设备主MIC,这函数仅是设备一个变量mBuiltInMicSpecificType,后面真正打开MIC的时候会用到这个参数
AudioALSAHardwareResourceManager::getInstance()-&setBuiltInMicSpecificType(BUILTIN_MIC_MIC1_ONLY);
//这里就是语音模式下的Modem与Speech算法,吹气模式并没有用到这里
if (CheckIsModemLoopback(loopback_type) == true)
SpeechDriverInterface *pSpeechDriver = SpeechDriverFactory::GetInstance()-&GetSpeechDriverByIndex(mWorkingModemIndex);
if (pSpeechDriver-&CheckModemIsReady() == false) // modem is sleep...
for (int modem_index = MODEM_1; modem_index & NUM_MODEM; modem_index++) // get working modem index
pSpeechDriver = SpeechDriverFactory::GetInstance()-&GetSpeechDriverByIndex((modem_index_t)modem_index);
if (pSpeechDriver != NULL && pSpeechDriver-&CheckModemIsReady() == true)
mWorkingModemIndex = (modem_index_t)modem_index;
SpeechDriverFactory::GetInstance()-&SetActiveModemIndex(mWorkingModemIndex);
// 打开关闭MIC降噪,吹气模式没有用到
if (loopback_type == MD_DUAL_MIC_ACOUSTIC_LOOPBACK_WITHOUT_DMNR ||
loopback_type == MD_DUAL_MIC_ACOUSTIC_LOOPBACK_WITH_DMNR)
mMaskCopy = SpeechEnhancementController::GetInstance()-&GetSpeechEnhancementMask(); // copy DMNR mask
sph_enh_mask_struct_t mask = mMaskCopy;
if (loopback_type == MD_DUAL_MIC_ACOUSTIC_LOOPBACK_WITHOUT_DMNR)
mask.dynamic_func &= (~SPH_ENH_DYNAMIC_MASK_DMNR);
else if (loopback_type == MD_DUAL_MIC_ACOUSTIC_LOOPBACK_WITH_DMNR)
mask.dynamic_func |= SPH_ENH_DYNAMIC_MASK_DMNR;
SpeechDriverFactory::GetInstance()-&GetSpeechDriverByIndex(mWorkingModemIndex)-&SetSpeechEnhancementMask(mask);
// Enable loopback function switch (loopback_type) {
case AP_MAIN_MIC_AFE_LOOPBACK:
//吹气模式,打开Loopback
AudioALSALoopbackController::getInstance()-&open(output_device, input_device);
case MD_MAIN_MIC_ACOUSTIC_LOOPBACK:
//语音模式,打开Loopback,同使全能Speech。吹气模式与语音模式最大的不同是Speech的介入#if defined(MTK_AUDIO_SPH_LPBK_PARAM)
AudioALSAStreamManager::getInstance()-&UpdateSpeechLpbkParams();#endif
AudioALSASpeechLoopbackController::getInstance()-&open(output_device, input_device);
// save opened loobpack type
mLoopbackType = loopback_type;
// VolumeController 对音量的总控制类
if ((loopback_type != AP_BT_LOOPBACK) && (loopback_type != AP_BT_LOOPBACK_NO_CODEC) && (loopback_type != MD_BT_LOOPBACK) && (loopback_type != MD_BT_LOOPBACK_NO_CODEC))
//语音模式音量
if (CheckIsModemLoopback(loopback_type) == true)
mVoiceVolumeCopy = mAudioALSAVolumeController-&getVoiceVolume();
mAudioALSAVolumeController-&setVoiceVolume(kVoiceVolumeForLoopback, AUDIO_MODE_IN_CALL, output_device);
//吹气模式音量
mMasterVolumeCopy = mAudioALSAVolumeController-&getMasterVolume();
mAudioALSAVolumeController-&setMasterVolume(kMasterVolumeForLoopback, AUDIO_MODE_NORMAL, output_device);
& & & & 我们去到吹气模式的Loopback打开AudioALSALoopbackController::getInstance()-&open(output_device, input_device);& & & &&
status_t AudioALSALoopbackController::open(const audio_devices_t output_devices, const audio_devices_t input_device){
// DL loopback setting
memset(&mConfig, 0, sizeof(mConfig));
mConfig.channels = 2;
mConfig.rate = 48000;
mConfig.period_size = 1024;
mConfig.period_count = 2;
mConfig.format = PCM_FORMAT_S16_LE;
mConfig.start_threshold = 0;
mConfig.stop_threshold = 0;
mConfig.silence_threshold = 0; //pcmInIdx
int pcmInIdx = AudioALSADeviceParser::getInstance()-&GetPcmIndexByString(keypcmUlDlLoopback);
int pcmOutIdx = AudioALSADeviceParser::getInstance()-&GetPcmIndexByString(keypcmUlDlLoopback);
int cardIndex = AudioALSADeviceParser::getInstance()-&GetCardIndexByString(keypcmUlDlLoopback);
mPcmUL = pcm_open(cardIndex, pcmInIdx, PCM_IN, &mConfig);
mPcmDL = pcm_open(cardIndex, pcmOutIdx, PCM_OUT, &mConfig); //调用TinyALSA接口打开播放录音的设备节点
pcm_start(mPcmUL);
pcm_start(mPcmDL); //打开输入输出设备
mHardwareResourceManager-&startInputDevice(input_device);
mHardwareResourceManager-&startOutputDevice(output_devices, mConfig.rate);}
& & & & 通过TinyALSA库调用底层设备节点的逻辑已经暴露,也就相当于打开我们常说的“声卡”,该节点在/dev/snd下面,我们来看看该目录&&
root@table:/ # ls dev/snd/adsp
comprC0D23controlC0
mixerpcmC0D0p
pcmC0D11ppcmC0D12c
pcmC0D14ppcmC0D15c
pcmC0D17cpcmC0D17p
pcmC0D19ppcmC0D1c
pcmC0D21cpcmC0D21p
pcmC0D22ppcmC0D24p
pcmC0D2ppcmC0D3c
pcmC0D4cpcmC0D4p
pcmC0D5ppcmC0D6c
pcmC0D7cpcmC0D7p
pcmC0D9cseq
sequencer2timer
& & & & 比较通用的设备节点有&
controlC0 --&
用于声卡的控制,例如通道选择,混音,麦克风的控制等midiC0D0
用于播放midi音频pcmC0D0c --〉
用于录音的pcm设备pcmC0D0p --〉
用于播放的pcm设备seq
音序器timer --〉
& & & & &pcm节点的介绍,我们Loopback吹气模式打开的是pcmC0D4c和pcmC0D4p
pcm设备,通过阅读tinyalsa的代码和查看Android下的音频设备节点,可知在Android中一个pcm设备最多可有一个mixer设备&/dev/snd/controlC%u&(一般是controlC0)和32个/dev/snd/pcmC%uD%uc(一般是pcmC0D0c)、/dev/snd/pcmC%uD%u%p(一般是pcmC0D0p),pcm设备中的C代表card,D代表device,c代表capture,p代表playback。当我们新增一个pcm声卡C的值会+1,D还是从0开始,可能只有c(pcmC1D0c 例如麦克风),可能只有p(pcmC1D0p 例如音响),可能同时存在c和p(pcmC1D0c pcmC1D0p )。
& & & & & 到AudioALSALoopbackController::open()执行完毕,Loopback就正式被打开完成,pcm_start()之后应该还会有一个pcm_write和pcm_read两个函数不断地从音频输入设备与音频输出设备读写buf,至少正常的播放与录音是如此的。但是从Loopback打开完成也没有看到在哪里pcm_read/write,有可能是哪算细节我遗漏掉了,或者还有一个猜想是在驱动的编解码芯片通过I2S就完成了这些简单的数据流读写,这里留个疑问,关于ALSA
PCM的讨论到此为止,再往下就是驱动层了,有空再继续深入。
& & & & (B)打开设备流程
& & & & 继续回到AudioALSALoopbackController::open(),里面还有输入(Mic)与输出(Speak)设备的打开,以打开MIC为例,我们来看一下是以一个怎么样的方式打开设备,打开设备的方法为:
mHardwareResourceManager-&startInputDevice(input_device);
mHardwareResourceManager-&startOutputDevice(output_devices, mConfig.rate);
& & & & 打开Input设备会复杂一些,我们以此为例。回忆一下我们之前的两个参数,一个是mBuiltInMicSpecificType=BUILTIN_MIC_MIC1_ONLY,另一个是input_device = AUDIO_DEVICE_IN_BUILTIN_MIC;&
status_t AudioALSAHardwareResourceManager::startInputDevice(const audio_devices_t new_device){ /*Audior打开和关闭设备用了很多的标志位,打开和关闭只是用了简单计数方式,
在一些高强度的测试的时候,这些计数就容易出问题*/
if (((mInputDevice & new_device) & ~AUDIO_DEVICE_BIT_IN) != 0)
if (new_device != AUDIO_DEVICE_IN_SPK_FEED)
mStartInputDeviceCount++;
if (new_device == AUDIO_DEVICE_IN_BUILTIN_MIC)
//设置MIC的模式
setMIC1Mode(false);
setMIC2Mode(false);
if (mBuiltInMicSpecificType == BUILTIN_MIC_MIC1_ONLY)
//主副MIC反转,我们并不反转
if (mMicInverse == true)
mDeviceConfigManager-&ApplyDeviceTurnonSequenceByName(AUDIO_DEVICE_BUILTIN_MIC_MIC1_INVERSE);
mDeviceConfigManager-&ApplyDeviceTurnonSequenceByName(AUDIO_DEVICE_BUILTIN_MIC_MIC1);
& & & & 先来看看,setMIC1Mode();
void AudioALSAHardwareResourceManager::setMIC1Mode(bool isphonemic){ //从Log知道,micmode = AUDIO_MIC_MODE_ACC
if (micmode == AUDIO_MIC_MODE_ACC)
mDeviceConfigManager-&ApplyDeviceSettingByName(AUDIOMIC1_TYPE_ACCMODE);
& & & & 这里主要调用了mDeviceConfigManager-&ApplyDeviceSettingByName(AUDIOMIC1_TYPE_ACCMODE);设置MIC1的工作模式。那个宏定义如下
#define AUDIOMIC1_TYPE_ACCMODE
&Mic1TypeACCMode&
& & & & 先不进入到ApplyDeviceSettingByName()函数中去,我们先记住上面的宏定义。然后再看一下mDeviceConfigManager-&ApplyDeviceTurnonSequenceByName(AUDIO_DEVICE_BUILTIN_MIC_MIC1);上面的是设置MIC的模式,在这里就真正地打开MIC。ApplyDeviceTurnonSequenceByName与刚才的ApplyDeviceSettingByName都是在AudioALSADeviceConfigManager中,他们的逻辑和工作方式都是基本是一至的。我们只需要看ApplyDeviceTurnonSequenceByName即可。同样地,记住他传入的参数
#define AUDIO_DEVICE_BUILTIN_MIC_MIC2
&builtin_Mic_Mic2&
& & & &ApplyDeviceTurnonSequenceByName的实现
status_t AudioALSADeviceConfigManager::ApplyDeviceTurnonSequenceByName(const char *DeviceName){ //通过名字查找设备描述符
DeviceCtlDescriptor *descriptor = GetDeviceDescriptorbyname(DeviceName);
if (descriptor-&DeviceStatusCounter == 0)
for (count = 0; count & descriptor-&mDeviceCltonVector.size(); count += 2)
String8 cltname = descriptor-&mDeviceCltonVector.itemAt(count);
String8 cltvalue = descriptor-&mDeviceCltonVector.itemAt(count + 1);
//调用TinyALSA把设置参数传入底层,以达到打开和设置的目地
mixer_ctl_set_enum_by_string(mixer_get_ctl_by_name(mMixer, cltname.string()), cltvalue.string());
descriptor-&DeviceStatusCounter++;}
& & & & 在设置的/system/etc/audio/audio_device.xml里对所有的设备,所有设置参数有一个详细的记录。ApplyDeviceTurnonSequenceByName()的原理就是查找这个文件里与传入参数匹配的节点,然后把该节点下面的子节点里面的参数通过TinyALSA设置到底层驱动。我们把上述的参数在该文件里查找一下便知。还是以打开MIC为例,传入的参数为&builtin_Mic_Mic2,在audio_device.xml里找到如下字段
&path name=&builtin_Mic_Mic2& value=&turnon&&
&kctl name=&Audio_MicSource1_Setting& value=&ADC1& /&
&kctl name=&Audio_ADC_1_Switch& value=&On& /&
&kctl name=&Audio_ADC_2_Switch& value=&On& /&
&kctl name=&Audio_Preamp1_Switch& value=&IN_ADC3& /&
&kctl name=&Audio_Preamp2_Switch& value=&IN_ADC3& /&
&path name=&builtin_Mic_Mic2& value=&turnoff&&
&kctl name=&Audio_Preamp1_Switch& value=&OPEN& /&
&kctl name=&Audio_Preamp2_Switch& value=&OPEN& /&
&kctl name=&Audio_ADC_1_Switch& value=&Off& /&
&kctl name=&Audio_ADC_2_Switch& value=&Off& /&
& & & & 可以很清晰地见到 name为builtin_Mic_Mic2的value=turnon,下面对应着要打开该MIC对应的动作,打开ADC1,打开ADC2等等。ApplyDeviceSettingByName()也是同样的原理。至此,打开流程完毕
(C)音量调节流程
& & & & 回到LoopbackManager,我们用的是吹气模式,设置音量调用的是这条方法:mAudioALSAVolumeController-&setMasterVolume(kMasterVolumeForLoopback, AUDIO_MODE_NORMAL, output_device);
status_t AudioALSAVolumeController::setMasterVolume(float v, audio_mode_t mode, uint32_t devices){
int MapVolume = AudioALSAVolumeController::logToLinear(v);
mMasterVolume = v;
mMode = mode;
mOutputDevices = devices;
switch (mode)
//传入的模式是volume
case AUDIO_MODE_NORMAL :
// normal mode
switch (devices)
//输出设置是喇叭
case (AUDIO_DEVICE_OUT_SPEAKER) :
ApplyExtAmpHeadPhoneGain(MapVolume,
mode, Audio_Speaker);
//来电铃声,通话声音大小的音量设置
case AUDIO_MODE_RINGTONE :
case AUDIO_MODE_IN_CALL :
case AUDIO_MODE_IN_CALL_2 :
case AUDIO_MODE_IN_CALL_EXTERNAL:
& && & &ApplyExtAmpHeadPhoneGain()实现。
void AudioALSAVolumeController::ApplyExtAmpHeadPhoneGain(int Gain, uint32_t mode, uint32_t device){ //从音频参数中获取增益值
int DegradedBGain = mVolumeRange[device];
DegradedBGain = DegradedBGain + (DEVICE_VOLUME_RANGE - DegradedBGain) * ((VOLUME_MAPPING_STEP - Gain) / VOLUME_MAPPING_STEP);
//通过TinyALSA设置喇叭增益
SetLinoutLGain(DegradedBGain);
SetLinoutRGain(DegradedBGain);
//通过TinyALSA设置耳机增益
SetHeadPhoneLGain(DegradedBGain);
SetHeadPhoneRGain(DegradedBGain);}
& & & & 吹气模式下的问题的音量设置比语音loopback和普通的播放录制的音量设置都要简单得好多,没有计算耳机阻抗大小,再根据得出的阻抗再匹配相应的增益补偿。上面的音量设置也只是动用到了音频参数文件,在initVolumeController()初始化了音频参数
status_t AudioALSAVolumeController::initVolumeController(){ //从NVRAM里读取音频参数
GetVolumeVer1ParamFromNV(&mVolumeParam);
GetNBSpeechParamFromNVRam(&mSphParamNB);
GetWBSpeechParamFromNVRam(&mSphParamWB); ...
for (int i = 0 ; i & SPEAKER_VOLUME_TYPE_MAX ; i++)
ALOGD(&speakeraudiovolume %d = %d&, i, mVolumeParam.speakeraudiovolume[i]);
.... //把音频参数数据设置到mVolumeRange数组中
float MaxdB = 0.0, MindB = 0.0, micgain = 0.0;
int degradegain = 0;
degradegain = (unsigned char)MampVoiceBufferVolume(mVolumeParam.normalaudiovolume[NORMAL_AUDIO_BUFFER]);
SetVolumeRange(Audio_Earpiece, DEVICE_VOICE_MAX_VOLUME
, DEVICE_VOICE_MIN_VOLUME, degradegain);
degradegain = (unsigned char)MampAudioBufferVolume(mVolumeParam.headsetaudiovolume[HEADSET_AUDIO_BUFFER]);
SetVolumeRange(Audio_Headset, DEVICE_MAX_VOLUME
, DEVICE_MIN_VOLUME, degradegain);
SetVolumeRange(Audio_Headphone, DEVICE_MAX_VOLUME
, DEVICE_MIN_VOLUME, degradegain); }
& & & & Loopback的工作流程大致就是这样子了。其中贯穿了一些功能类,了解了音频工作内容的分布,还留下两个问题,
& & & & 1. 有了pcm_start(),为什么没有pcm_read()/pcm_write(),有两种可能,不是我看漏了,就是在驱动层完成了数据流
& & & & 2. speech用了些共享内存和线程,工作方式有些复杂,它的工作原理是怎么样子的,是通过pcm_read()读出Buf吗?
------------------------------------------------------
Android 5.1 Audio HAL分析
android usb解析(二)UsbHostManager(and6.0)
android开发笔记之开发规范
Android客制化------设置MTP存储模式
获取Android设备上的所有存储设备
没有更多推荐了,

我要回帖

更多关于 alsa英文名 的文章

 

随机推荐