Android ALSA音频系统架构分析(1)----从Loopback了解Audio

     
     
     
     
  1. /***********************************
  2. * Author:刘江明
  3. * Environment:MTK Android 6.0
  4. * Date:2017年05月25日
  5. ***********************************/

一.  前述

        Android音频系统是一套基于Linux ALSA上二次封装开发的一套音频系统,中间进行了很多的功能封装,但最终会用到Linux ALSA。所以在Hal层的类名都会包含ALSA。对于MTK的Android audio,MTK也有一定的介绍,先来大体了解一下:
                  Android ALSA音频系统架构分析(1)----从Loopback了解Audio_第1张图片
图1  音频系统的Framework图
        提供给应用的功能接口有:AudioTrack与AudioRecord两个类,分别是播放与录音的功能接口。这两个功能接口会通过AudioSystem调用AudioFlinger。AudioFlinger对管控着所有的Buf与播放,同时AudioFlinger也受AudioSystem管理,其中的管理策略来自于AudioPolicy,AudioPolicy会决定这个是什么类型的音频以及音频的rounting,是音乐,还是打电话,音乐是能打开什么播放设备,是打开喇叭还是打开听筒播放,电话来了是否要把音乐关掉等等的音频策略,包括MTK的音频参数也会在这里加载进来
Android ALSA音频系统架构分析(1)----从Loopback了解Audio_第2张图片        
  图2  音频系统的Hal图

        图一的AudioFlinger会进入到Hal层,Audio Hal层有几大重要的类:
        AudioALSAStreamManager是入口管理下面的AudioALSAStreamIn和AudioALSAStreamOut
         AudioALSAStream Out管理着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
     
     
     
     
  1. //AudioSystem.cpp
  2. status_t AudioSystem::setParameters(const String8 &keyValuePairs)
  3. {
  4. //第一个参数为:AUDIO_IO_HANDLE_NONE
  5. return setParameters(AUDIO_IO_HANDLE_NONE, keyValuePairs);
  6. }
  7. status_t AudioSystem::setParameters(audio_io_handle_t ioHandle, const String8 &keyValuePairs)
  8. {
  9. //省略掉无关逻辑
  10. const sp<IAudioFlinger> &af = AudioSystem::get_audio_flinger();
  11. if (af == 0) return PERMISSION_DENIED;
  12. ret = af->setParameters(ioHandle, keyValuePairs);
  13. }
        上面直接调用到AudioFlinger的setParameters()   
     
     
     
     
  1. //AudioFlinger.cpp
  2. status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs)
  3. {
  4. //上面传下来的ioHandle = AUDIO_IO_HANDLE_NONE
  5. if (ioHandle == AUDIO_IO_HANDLE_NONE) {
  6. mHardwareStatus = AUDIO_HW_SET_PARAMETER;
  7. for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
  8. //mAudioHwDevs数组是HAL层“Dev”的集合,这里的Dev并不是指真正的具体设备
  9. //而是把HAL的一个大模块抽象为一个“Dev”,例如,MTK音频HAL可以取名为“MTK Audio HAL”
  10. //然后还有高通的“Qual Audio HAL”等等,还能细分为Wifi,USB,A2DP.....
  11. audio_hw_device_t *dev = mAudioHwDevs.valueAt(i)->hwDevice();
  12. status_t result = dev->set_parameters(dev, keyValuePairs.string());
  13. }
  14. }
  15. }
        mAudioHwDevs这个数组又是怎么来的呢?在AudioPolicyManager的构造函数里,会向本地文件加载一个audio_policy.conf。该config文件会决定音频系统有哪些通路,USB,A2DP等等,这些通路下面有哪些设备,还有一些设备的参数。大概摘抄一点
      
      
      
      
  1. audio_hw_modules {
  2. primary {
  3. global_configuration {
  4. attached_output_devices AUDIO_DEVICE_OUT_SPEAKER|AUDIO_DEVICE_OUT_EARPIECE
  5. default_output_device AUDIO_DEVICE_OUT_SPEAKER
  6. attached_input_devices AUDIO_DEVICE_IN_BUILTIN_MIC|AUDIO_DEVICE_IN_FM_TUNER|AUDIO_DEVICE_IN_VOICE_CALL
  7. audio_hal_version 3.0
  8. }
  9. devices {
  10. headset {
  11. type AUDIO_DEVICE_OUT_WIRED_HEADSET
  12. gains {
  13. gain_1 {
  14. mode AUDIO_GAIN_MODE_JOINT
  15. channel_mask AUDIO_CHANNEL_OUT_STEREO
  16. min_value_mB -6400
  17. max_value_mB 0
  18. default_value_mB 0
  19. step_value_mB 100
  20. min_ramp_ms 0
  21. max_ramp_ms 0
  22. }
  23. }
  24. }
  25. ......
       这些模块名字加载好后会跟据这些名字循环地去查找HAL模块,把找到的模块填入到 mAudioHwDevs中。这段逻辑目前看得不是很仔细,有些逻辑不严谨
      
      
      
      
  1. //AudioFlinger.cpp
  2. audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name)
  3. {
  4. audio_hw_device_t *dev;
  5. int rc = load_audio_interface(name, &dev);
  6. mAudioHwDevs.add(handle, new AudioHwDevice(handle, name, dev, flags));
  7. }
  8. static int load_audio_interface(const char *if_name, audio_hw_device_t **dev)
  9. {
  10. rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, &mod);
  11. }
          mAudioHwDevs数组是找到了如何初始化,setParameters()调用了该元素里的set_parameters()函数继续往下传参数。我还要找到具体的HAL模块才知道参数如何往下传。  hw_get_module_by_class()这个API会通过ID去找到HAL注册了哪些模块。我们搜一下上面的AUDIO_HARDWARE_MODULE_ID就可以找到具体是加载了哪些模块,在Vendor目录下现有两个HAL模块
       
       
       
       
  1. 1MTK自己实现的Audio HAL,名字和ID如下
  2. id: AUDIO_HARDWARE_MODULE_ID,
  3. name: "MTK Audio HW HAL"
  4. 2)还有一个是A2DP
  5. id: AUDIO_HARDWARE_MODULE_ID,
  6. name: "A2DP Audio HW HAL"
        就这样把HAL模块加载出来了,所有的HAL模块都明非常标准和非常明确的接口定义,对于HAL以上的逻辑,只需找到ID和相应的名字即可找到需要使用的模块,即使你有100个厂商,100个厂商又有100个模块,还是依照明确的标准去走,这个就是面向对象编程中的一个核心理念,面向接口编程,不管你逻辑如何变,接口一定不能变!这样就能确保软件的低耦合,可移植。
        我们用的是“MTK Audio HW HAL”这个Audio HAL module。很容易地就在里面找到了set_parameters函数指针。指向adev_set_parameters()函数         
     
     
     
     
  1. static int adev_set_parameters(struct audio_hw_device *dev, const char *kvpairs)
  2. {
  3. struct legacy_audio_device *ladev = to_ladev(dev);
  4. //结过转换,调用到的是AudioALSAHardware.cpp
  5. return ladev->hwif->setParameters(String8(kvpairs));
  6. }
        AudioALSAHardware::setParameters()是Audio setParameters()的最终执行函数,也是一个很有趣的函数。这个函数就像Audio系统的“后门”,里面可以粗暴有力的设置Audio参数,而影响整个Audio系统的运行。例如,可以调整音量,打开关闭MIC,FM的音频开关等等,知道这些暗门不知道能不能干一点坏事。就像我们强制打开Loopback,声音就开始从MIC进喇叭出,普通用户还没有办法关闭,除非重启平板,细思极恐。然后里面还保留着一些音频设备的测试方法和校准方法。
      
      
      
      
  1. status_t AudioALSAHardware::setParameters(const String8 &keyValuePairs)
  2. {
  3. // Loopback
  4. if (param.get(keySET_LOOPBACK_TYPE, value_str) == NO_ERROR)
  5. {
  6. if (loopback_type == NO_LOOPBACK) // close loopback
  7. {
  8. LoopbackManager::GetInstance()->SetLoopbackOff();
  9. }
  10. else // open loopback
  11. {
  12. LoopbackManager::GetInstance()->SetLoopbackOn(loopback_type, loopback_output_device);
  13. }
  14. }
  15. }
         至此我们APP设备的参数就成功地传递到了HAL层,并开始操控Loopback。从下面的方法开始,正式进入到Loopback中
            LoopbackManager::GetInstance()->SetLoopbackOn(loopback_type, loopback_output_device);

(2)Loopback工作流程

       (A)源码流程

        从 SetLoopbackOn()开始,先看一下SetLoopbackOn的流程,再从此处展开
     
     
     
     
  1. //LoopbackManager.cpp
  2. status_t LoopbackManager::SetLoopbackOn(loopback_t loopback_type, loopback_output_device_t loopback_output_device)
  3. {
  4. //关闭所有的音频输出流,并重新开始准备
  5. //也就是我们之前说的pcm_write,把数据定到ALSA中
  6. //但是Loopback中并没有用到StreamManager
  7. AudioALSAStreamManager::getInstance()->setAllStreamsSuspend(true);
  8. AudioALSAStreamManager::getInstance()->standbyAllStreams();
  9. // get loopback dev 获得Input dev为AUDIO_DEVICE_IN_BUILTIN_MIC,这个值后面有用
  10. audio_devices_t input_device = GetInputDeviceByLoopbackType(loopback_type);
  11. audio_devices_t output_device = GetOutputDeviceByLoopbackType(loopback_type, loopback_output_device);
  12. // set specific mic type
  13. if (loopback_type == AP_MAIN_MIC_AFE_LOOPBACK || loopback_type == MD_MAIN_MIC_ACOUSTIC_LOOPBACK)
  14. {
  15. //设备主MIC,这函数仅是设备一个变量mBuiltInMicSpecificType,后面真正打开MIC的时候会用到这个参数
  16. AudioALSAHardwareResourceManager::getInstance()->setBuiltInMicSpecificType(BUILTIN_MIC_MIC1_ONLY);
  17. }
  18. ....
  19. //这里就是语音模式下的Modem与Speech算法,吹气模式并没有用到这里
  20. if (CheckIsModemLoopback(loopback_type) == true)
  21. {
  22. SpeechDriverInterface *pSpeechDriver = SpeechDriverFactory::GetInstance()->GetSpeechDriverByIndex(mWorkingModemIndex);
  23. if (pSpeechDriver->CheckModemIsReady() == false) // modem is sleep...
  24. {
  25. for (int modem_index = MODEM_1; modem_index < NUM_MODEM; modem_index++) // get working modem index
  26. {
  27. pSpeechDriver = SpeechDriverFactory::GetInstance()->GetSpeechDriverByIndex((modem_index_t)modem_index);
  28. if (pSpeechDriver != NULL && pSpeechDriver->CheckModemIsReady() == true)
  29. {
  30. mWorkingModemIndex = (modem_index_t)modem_index;
  31. SpeechDriverFactory::GetInstance()->SetActiveModemIndex(mWorkingModemIndex);
  32. break;
  33. }
  34. }
  35. }
  36. }
  37. ......
  38. // 打开关闭MIC降噪,吹气模式没有用到
  39. if (loopback_type == MD_DUAL_MIC_ACOUSTIC_LOOPBACK_WITHOUT_DMNR ||
  40. loopback_type == MD_DUAL_MIC_ACOUSTIC_LOOPBACK_WITH_DMNR)
  41. {
  42. mMaskCopy = SpeechEnhancementController::GetInstance()->GetSpeechEnhancementMask(); // copy DMNR mask
  43. sph_enh_mask_struct_t mask = mMaskCopy;
  44. if (loopback_type == MD_DUAL_MIC_ACOUSTIC_LOOPBACK_WITHOUT_DMNR)
  45. {
  46. mask.dynamic_func &= (~SPH_ENH_DYNAMIC_MASK_DMNR);
  47. }
  48. else if (loopback_type == MD_DUAL_MIC_ACOUSTIC_LOOPBACK_WITH_DMNR)
  49. {
  50. mask.dynamic_func |= SPH_ENH_DYNAMIC_MASK_DMNR;
  51. }
  52. SpeechDriverFactory::GetInstance()->GetSpeechDriverByIndex(mWorkingModemIndex)->SetSpeechEnhancementMask(mask);
  53. }
  54. ......
  55. // Enable loopback function
  56. switch (loopback_type)
  57. {
  58. case AP_MAIN_MIC_AFE_LOOPBACK:
  59. {
  60. //吹气模式,打开Loopback
  61. AudioALSALoopbackController::getInstance()->open(output_device, input_device);
  62. break;
  63. }
  64. case MD_MAIN_MIC_ACOUSTIC_LOOPBACK:
  65. {
  66. //语音模式,打开Loopback,同使全能Speech。吹气模式与语音模式最大的不同是Speech的介入
  67. #if defined(MTK_AUDIO_SPH_LPBK_PARAM)
  68. AudioALSAStreamManager::getInstance()->UpdateSpeechLpbkParams();
  69. #endif
  70. AudioALSASpeechLoopbackController::getInstance()->open(output_device, input_device);
  71. break;
  72. }
  73. }
  74. // save opened loobpack type
  75. mLoopbackType = loopback_type;
  76. // VolumeController 对音量的总控制类
  77. 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))
  78. {
  79. //语音模式音量
  80. if (CheckIsModemLoopback(loopback_type) == true)
  81. {
  82. mVoiceVolumeCopy = mAudioALSAVolumeController->getVoiceVolume();
  83. mAudioALSAVolumeController->setVoiceVolume(kVoiceVolumeForLoopback, AUDIO_MODE_IN_CALL, output_device);
  84. }
  85. else
  86. {
  87. //吹气模式音量
  88. mMasterVolumeCopy = mAudioALSAVolumeController->getMasterVolume();
  89. mAudioALSAVolumeController->setMasterVolume(kMasterVolumeForLoopback, AUDIO_MODE_NORMAL, output_device);
  90. }
  91. }
  92. }
        我们去到吹气模式的Loopback打开AudioALSALoopbackController::getInstance()->open(output_device, input_device);         
      
      
      
      
  1. status_t AudioALSALoopbackController::open(const audio_devices_t output_devices, const audio_devices_t input_device)
  2. {
  3. // DL loopback setting
  4. memset(&mConfig, 0, sizeof(mConfig));
  5. mConfig.channels = 2;
  6. mConfig.rate = 48000;
  7. mConfig.period_size = 1024;
  8. mConfig.period_count = 2;
  9. mConfig.format = PCM_FORMAT_S16_LE;
  10. mConfig.start_threshold = 0;
  11. mConfig.stop_threshold = 0;
  12. mConfig.silence_threshold = 0;
  13. //pcmInIdx pcmOutIdx cardIndex
  14. int pcmInIdx = AudioALSADeviceParser::getInstance()->GetPcmIndexByString(keypcmUlDlLoopback);
  15. int pcmOutIdx = AudioALSADeviceParser::getInstance()->GetPcmIndexByString(keypcmUlDlLoopback);
  16. int cardIndex = AudioALSADeviceParser::getInstance()->GetCardIndexByString(keypcmUlDlLoopback);
  17. mPcmUL = pcm_open(cardIndex, pcmInIdx, PCM_IN, &mConfig);
  18. mPcmDL = pcm_open(cardIndex, pcmOutIdx, PCM_OUT, &mConfig);
  19. //调用TinyALSA接口打开播放录音的设备节点
  20. pcm_start(mPcmUL);
  21. pcm_start(mPcmDL);
  22. //打开输入输出设备
  23. mHardwareResourceManager->startInputDevice(input_device);
  24. mHardwareResourceManager->startOutputDevice(output_devices, mConfig.rate);
  25. }
        通过TinyALSA库调用底层设备节点的逻辑已经暴露,也就相当于打开我们常说的“声卡”,该节点在/dev/snd下面,我们来看看该目录   
      
      
      
      
  1. root@table:/ # ls dev/snd/
  2. adsp audio comprC0D23
  3. controlC0 dsp mixer
  4. pcmC0D0p pcmC0D10p pcmC0D11p
  5. pcmC0D12c pcmC0D13c pcmC0D14p
  6. pcmC0D15c pcmC0D16c pcmC0D17c
  7. pcmC0D17p pcmC0D18p pcmC0D19p
  8. pcmC0D1c pcmC0D20p pcmC0D21c
  9. pcmC0D21p pcmC0D22c pcmC0D22p
  10. pcmC0D24p pcmC0D2c pcmC0D2p
  11. pcmC0D3c pcmC0D3p pcmC0D4c
  12. pcmC0D4p pcmC0D5c pcmC0D5p
  13. pcmC0D6c pcmC0D6p pcmC0D7c
  14. pcmC0D7p pcmC0D8p pcmC0D9c
  15. seq sequencer sequencer2
  16. timer
        比较通用的设备节点有  
       
       
       
       
  1. controlC0 --> 用于声卡的控制,例如通道选择,混音,麦克风的控制等
  2. midiC0D0 --> 用于播放midi音频
  3. pcmC0D0c --〉 用于录音的pcm设备
  4. pcmC0D0p --〉 用于播放的pcm设备
  5. seq --〉 音序器
  6. timer --〉 定时器
         pcm节点的介绍,我们Loopback吹气模式打开的是pcmC0D4c和pcmC0D4p
        
        
        
        
  1. pcm设备,通过阅读tinyalsa的代码和查看Android下的音频设备节点,
  2. 可知在Android中一个pcm设备最多可有一个mixer设备"/dev/snd/controlC%u"
  3. (一般是controlC0)和32个/dev/snd/pcmC%uD%uc(一般是pcmC0D0c)、/dev/snd/pcmC%uD%u%p(一般是pcmC0D0p),
  4. pcm设备中的C代表cardD代表devicec代表capturep代表playback
  5. 当我们新增一个pcm声卡C的值会+1D还是从0开始,
  6. 可能只有cpcmC1D0c 例如麦克风),可能只有ppcmC1D0p 例如音响),
  7. 可能同时存在cppcmC1D0c pcmC1D0p )。
          到 AudioALSALoopbackController::open()执行完毕,Loopback就正式被打开完成,pcm_start()之后应该还会有一个pcm_write和pcm_read两个函数不断地从音频输入设备与音频输出设备读写buf,至少正常的播放与录音是如此的。但是从Loopback打开完成也没有看到在哪里pcm_read/write,有可能是哪算细节我遗漏掉了,或者还有一个猜想是在驱动的编解码芯片通过I2S就完成了这些简单的数据流读写,这里留个疑问, 关于ALSA PCM的讨论到此为止,再往下就是驱动层了,有空再继续深入。

        (B)打开设备流程

        继续回到 AudioALSALoopbackController::open(),里面还有输入(Mic)与输出(Speak)设备的打开,以打开MIC为例,我们来看一下是以一个怎么样的方式打开设备,打开设备的方法为:
        
        
        
        
  1. mHardwareResourceManager->startInputDevice(input_device);
  2. mHardwareResourceManager->startOutputDevice(output_devices, mConfig.rate);
        打开Input设备会复杂一些,我们以此为例。回忆一下我们之前的两个参数,一个是mBuiltInMicSpecificType=BUILTIN_MIC_MIC1_ONLY,另一个是input_device = AUDIO_DEVICE_IN_BUILTIN_MIC; 
       
       
       
       
  1. status_t AudioALSAHardwareResourceManager::startInputDevice(const audio_devices_t new_device)
  2. {
  3. /*Audior打开和关闭设备用了很多的标志位,打开和关闭只是用了简单计数方式,
  4. 在一些高强度的测试的时候,这些计数就容易出问题*/
  5. if (((mInputDevice & new_device) & ~AUDIO_DEVICE_BIT_IN) != 0)
  6. {
  7. if (new_device != AUDIO_DEVICE_IN_SPK_FEED)
  8. {
  9. mStartInputDeviceCount++;
  10. }
  11. }
  12. if (new_device == AUDIO_DEVICE_IN_BUILTIN_MIC)
  13. {
  14. //设置MIC的模式
  15. setMIC1Mode(false);
  16. setMIC2Mode(false);
  17. //打开MIC
  18. if (mBuiltInMicSpecificType == BUILTIN_MIC_MIC1_ONLY)
  19. {
  20. //主副MIC反转,我们并不反转
  21. if (mMicInverse == true)
  22. {
  23. mDeviceConfigManager->ApplyDeviceTurnonSequenceByName(AUDIO_DEVICE_BUILTIN_MIC_MIC1_INVERSE);
  24. }
  25. else
  26. {
  27. mDeviceConfigManager->ApplyDeviceTurnonSequenceByName(AUDIO_DEVICE_BUILTIN_MIC_MIC1);
  28. }
  29. }
  30. }
  31. }
        先来看看,setMIC1Mode();
        
        
        
        
  1. void AudioALSAHardwareResourceManager::setMIC1Mode(bool isphonemic)
  2. {
  3. //从Log知道,micmode = AUDIO_MIC_MODE_ACC
  4. if (micmode == AUDIO_MIC_MODE_ACC)
  5. {
  6. mDeviceConfigManager->ApplyDeviceSettingByName(AUDIOMIC1_TYPE_ACCMODE);
  7. }
  8. }
        这里主要调用了mDeviceConfigManager->ApplyDeviceSettingByName(AUDIOMIC1_TYPE_ACCMODE);设置MIC1的工作模式。那个宏定义如下
        
        
        
        
  1. #define AUDIOMIC1_TYPE_ACCMODE "Mic1TypeACCMode"
        先不进入到ApplyDeviceSettingByName()函数中去,我们先记住上面的宏定义。然后再看一下mDeviceConfigManager->ApplyDeviceTurnonSequenceByName(AUDIO_DEVICE_BUILTIN_MIC_MIC1);上面的是设置MIC的模式,在这里就真正地打开MIC。 ApplyDeviceTurnonSequenceByName与刚才的 ApplyDeviceSettingByName都是在AudioALSADeviceConfigManager中,他们的逻辑和工作方式都是基本是一至的。我们只需要看 ApplyDeviceTurnonSequenceByName即可。同样地,记住他传入的参数
        
        
        
        
  1. #define AUDIO_DEVICE_BUILTIN_MIC_MIC2 "builtin_Mic_Mic2"
       ApplyDeviceTurnonSequenceByName的实现
         
         
         
         
  1. status_t AudioALSADeviceConfigManager::ApplyDeviceTurnonSequenceByName(const char *DeviceName)
  2. {
  3. //通过名字查找设备描述符
  4. DeviceCtlDescriptor *descriptor = GetDeviceDescriptorbyname(DeviceName);
  5. if (descriptor->DeviceStatusCounter == 0)
  6. {
  7. for (count = 0; count < descriptor->mDeviceCltonVector.size(); count += 2)
  8. {
  9. String8 cltname = descriptor->mDeviceCltonVector.itemAt(count);
  10. String8 cltvalue = descriptor->mDeviceCltonVector.itemAt(count + 1);
  11. //调用TinyALSA把设置参数传入底层,以达到打开和设置的目地
  12. mixer_ctl_set_enum_by_string(mixer_get_ctl_by_name(mMixer, cltname.string()), cltvalue.string());
  13. }
  14. }
  15. descriptor->DeviceStatusCounter++;
  16. }
        在设置的/system/etc/audio/audio_device.xml里对所有的设备,所有设置参数有一个详细的记录。ApplyDeviceTurnonSequenceByName()的原理就是查找这个文件里与传入参数匹配的节点,然后把该节点下面的子节点里面的参数通过TinyALSA设置到底层驱动。我们把上述的参数在该文件里查找一下便知。还是以打开MIC为例,传入的参数为 builtin_Mic_Mic2,在audio_device.xml里找到如下字段
        
        
        
        
  1. <path name="builtin_Mic_Mic2" value="turnon">
  2. <kctl name="Audio_MicSource1_Setting" value="ADC1" />
  3. <kctl name="Audio_ADC_1_Switch" value="On" />
  4. <kctl name="Audio_ADC_2_Switch" value="On" />
  5. <kctl name="Audio_Preamp1_Switch" value="IN_ADC3" />
  6. <kctl name="Audio_Preamp2_Switch" value="IN_ADC3" />
  7. path>
  8. <path name="builtin_Mic_Mic2" value="turnoff">
  9. <kctl name="Audio_Preamp1_Switch" value="OPEN" />
  10. <kctl name="Audio_Preamp2_Switch" value="OPEN" />
  11. <kctl name="Audio_ADC_1_Switch" value="Off" />
  12. <kctl name="Audio_ADC_2_Switch" value="Off" />
  13. path>
        可以很清晰地见到 name为builtin_Mic_Mic2的value=turnon,下面对应着要打开该MIC对应的动作,打开ADC1,打开ADC2等等。 ApplyDeviceSettingByName()也是同样的原理。至此,打开流程完毕
      
(C)音量调节流程
        回到LoopbackManager,我们用的是吹气模式,设置音量调用的是这条方法:mAudioALSAVolumeController->setMasterVolume(kMasterVolumeForLoopback, AUDIO_MODE_NORMAL, output_device);
     
     
     
     
  1. status_t AudioALSAVolumeController::setMasterVolume(float v, audio_mode_t mode, uint32_t devices)
  2. {
  3. int MapVolume = AudioALSAVolumeController::logToLinear(v);
  4. mMasterVolume = v;
  5. mMode = mode;
  6. mOutputDevices = devices;
  7. switch (mode)
  8. {
  9. //传入的模式是volume
  10. case AUDIO_MODE_NORMAL : // normal mode
  11. {
  12. switch (devices)
  13. {
  14. //输出设置是喇叭
  15. case (AUDIO_DEVICE_OUT_SPEAKER) :
  16. {
  17. ApplyExtAmpHeadPhoneGain(MapVolume, mode, Audio_Speaker);
  18. }
  19. }
  20. break;
  21. }
  22. //来电铃声,通话声音大小的音量设置
  23. case AUDIO_MODE_RINGTONE :
  24. case AUDIO_MODE_IN_CALL :
  25. case AUDIO_MODE_IN_CALL_2 :
  26. case AUDIO_MODE_IN_CALL_EXTERNAL:
  27. }
  28. }
  29. }
        ApplyExtAmpHeadPhoneGain()实现。
      
      
      
      
  1. void AudioALSAVolumeController::ApplyExtAmpHeadPhoneGain(int Gain, uint32_t mode, uint32_t device)
  2. {
  3. //从音频参数中获取增益值
  4. int DegradedBGain = mVolumeRange[device];
  5. DegradedBGain = DegradedBGain + (DEVICE_VOLUME_RANGE - DegradedBGain) * ((VOLUME_MAPPING_STEP - Gain) / VOLUME_MAPPING_STEP);
  6. //通过TinyALSA设置喇叭增益
  7. SetLinoutLGain(DegradedBGain);
  8. SetLinoutRGain(DegradedBGain);
  9. //通过TinyALSA设置耳机增益
  10. SetHeadPhoneLGain(DegradedBGain);
  11. SetHeadPhoneRGain(DegradedBGain);
  12. }
        吹气模式下的问题的音量设置比语音loopback和普通的播放录制的音量设置都要简单得好多,没有计算耳机阻抗大小,再根据得出的阻抗再匹配相应的增益补偿。上面的音量设置也只是动用到了音频参数文件,在initVolumeController()初始化了音频参数
      
      
      
      
  1. status_t AudioALSAVolumeController::initVolumeController()
  2. {
  3. //从NVRAM里读取音频参数
  4. GetVolumeVer1ParamFromNV(&mVolumeParam);
  5. GetNBSpeechParamFromNVRam(&mSphParamNB);
  6. GetWBSpeechParamFromNVRam(&mSphParamWB);
  7. ...
  8. for (int i = 0 ; i < SPEAKER_VOLUME_TYPE_MAX ; i++)
  9. {
  10. ALOGD("speakeraudiovolume %d = %d", i, mVolumeParam.speakeraudiovolume[i]);
  11. }
  12. ....
  13. //把音频参数数据设置到mVolumeRange数组中
  14. float MaxdB = 0.0, MindB = 0.0, micgain = 0.0;
  15. int degradegain = 0;
  16. degradegain = (unsigned char)MampVoiceBufferVolume(mVolumeParam.normalaudiovolume[NORMAL_AUDIO_BUFFER]);
  17. SetVolumeRange(Audio_Earpiece, DEVICE_VOICE_MAX_VOLUME , DEVICE_VOICE_MIN_VOLUME, degradegain);
  18. degradegain = (unsigned char)MampAudioBufferVolume(mVolumeParam.headsetaudiovolume[HEADSET_AUDIO_BUFFER]);
  19. SetVolumeRange(Audio_Headset, DEVICE_MAX_VOLUME , DEVICE_MIN_VOLUME, degradegain);
  20. SetVolumeRange(Audio_Headphone, DEVICE_MAX_VOLUME , DEVICE_MIN_VOLUME, degradegain);
  21. }
        
        Loopback的工作流程大致就是这样子了。其中贯穿了一些功能类,了解了音频工作内容的分布,还留下两个问题,
        1. 有了pcm_start(),为什么没有pcm_read()/pcm_write(),有两种可能,不是我看漏了,就是在驱动层完成了数据流
        2. speech用了些共享内存和线程,工作方式有些复杂,它的工作原理是怎么样子的,是通过pcm_read()读出Buf吗?
------------------------------------------------------
参考文章
http://www.2cto.com/kf/201505/401051.html


        


你可能感兴趣的:(Android,ALSA,Audio)