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

1 篇文章 6 订阅
订阅专栏

   
   
  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也有一定的介绍,先来大体了解一下:
                  
图1  音频系统的Framework图
        提供给应用的功能接口有:AudioTrack与AudioRecord两个类,分别是播放与录音的功能接口。这两个功能接口会通过AudioSystem调用AudioFlinger。AudioFlinger对管控着所有的Buf与播放,同时AudioFlinger也受AudioSystem管理,其中的管理策略来自于AudioPolicy,AudioPolicy会决定这个是什么类型的音频以及音频的rounting,是音乐,还是打电话,音乐是能打开什么播放设备,是打开喇叭还是打开听筒播放,电话来了是否要把音乐关掉等等的音频策略,包括MTK的音频参数也会在这里加载进来
        
  图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吗?
------------------------------------------------------
参考文章


        


Android loopback(三)
Android系统攻城狮
12-25 1477
CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长!人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.更多原创,欢迎关注:Android系统攻城狮。
alsa-utils alsa-utils alsa-utils
11-27
alsa-utils
linux利用alsa loopback录制应用声音
u012906122的专栏
07-28 2905
1 插入黑莓耳机 查看耳机设备: arecord -L 2启用snd-aloop模块 sudo modprobe snd-aloop 3 注意检查和修改声音设置 (1) 声音设置,右键,高级设置,输出选择模拟输出Loopback todo (2) 声音设置,扬声器声音增大到100%,否则录音时声音很小 4 应用放音乐 例如,浏览器播放视频: https://haokan.baidu.com/v?pd=wisenatural&vid=1175464000978651618 5 录制...
android Audio ALSA框架分析
02-14
从框图中可以看出 android 对于 java 层一共提供 3 个接口,分别 MedialPlayer、MediaREcorder 以及 AudioServier,通过 JNI 则调用到 AudioTrack(放 AudioRecord(录音)以及(AudioSystem)主要音频参数设定。通过 Android Binder 机 AudioFlinger 层相同步,之后调用到 AudioHardware ,其中提供的接口主要 AudioStreamOut以及AudioStreamin。最终将进入Linux内核调用到ALSA
Android/Linux音频架构开发ALSA-篇3
最新发布
weixin_46453743的博客
08-02 612
文章介绍了ALSA架构知识,讲述如何创建一个声卡snd_card,如何理解声卡。
Android系统架构
数据库天地
09-30 352
Android系统架构采用了分层架构的思想,如图1所示。从上层到底层共包括四层,分别是应用程序程序层、应用框架层、系统库和Android运行时和Linux内核。 图1:Android系统架构图 每层功能简要介绍如下: 一 应用程序层 该层提供一些核心应用程序包,例如电子邮件、短信、日历、地图、浏览器和联系人管理等。同时,开发者可以利用Java语言设计和编写属于自己的应用程序,...
【多图】Google工程师解析Android系统架构
zhjp4295216的专栏
11-25 871
<br /><br />Sans Serif是Google的一位工程师,近日发布了一篇博文非常清楚的描述了Android系统架构,中国移动通信研究院院长黄晓庆在新浪微博上推荐了该文,并认为文中对Android的介绍很好,如下是CSDN对文章的简单编译:<br />Andriod是什么?<br />首先,就像Android开源和兼容性技术负责人Dan Morrill在Android开发手册兼容性部分所解释的,“Android并不是传统的Linux风格的一个规范或分发版本,也不是一系列可重用的组件集成,Andr
Android中使用ALSA声卡
yihui8的专栏
07-04 8683
Android中使用ALSA声卡首先,cd到Android源码树根目录下:cd /home/figo/android/Android-2.0从Android主页下载ALSA声卡的相关源码:git clone git://android.git.kernel.org/platform/external/alsa-lib.gitgit clone git://android.git.kernel.org/platform/external/alsa-utils.gitgit clone git://androi
alsa-lib-1.0.25.rar_alsa-lib_alsa-lib-1_alsa-lib-1.0_alsa-lib1.0
09-23
alsa-lib-1.0.25.rar是一个包含alsa-lib库源代码的压缩包,它在Linux音频生态系统中扮演着至关重要的角色。Alsa(Advanced Linux Sound Architecture,高级Linux声音架构)是Linux内核的一个组件,为操作系统提供了...
alsa-lib-1.0.14.rar_alsa_alsa lib_alsa-lib_alsa-lib download_als
09-19
Alsa-lib-1.0.14 是一个重要的音频库,它是 ALSA(Advanced Linux Sound Architecture)的一部分,专为Linux操作系统设计,用于管理和控制音频硬件。这个版本的 alsa-lib 提供了与各种声卡设备交互所需的接口和功能...
alsa-plugins-pulseaudio-1.1.9-1.el8.x86_64.rpm
12-06
离线安装包,亲测可用
ALSA Doc.rar_ALSA sound driver_alsa-lib_audio Alsa_oss wm89_声卡 l
09-19
在Linux操作系统中,声音系统是实现音频输入输出的关键部分,而ALSA(Advanced Linux Sound Architecture,高级Linux声音架构)就是Linux内核中的一个核心组件,为开发者提供了丰富的接口来处理声音相关的任务。...
Android 音频子系统(1) ---- 架构分析
yong_i7的博客
07-13 1179
本系列文章基于AAOS14源码进行分析解读,部分框图直接使用了原作者的图片,侵权必删。音频子系统Android框架中一个较为复杂的系统,涉及到较多的模块。一个应用涉及到的API层面包括APP层,FrameWork层,Native层,HAL层,Driver层。APP应用本身进程,使用应用层API:AudioManager,AudioTrack,AudioRecord;SystemServer进程,使用框架层的API:AudioService。
Android Audio 入门 一】--- Audio ALSA Driver
|~~~热爱生活、努力学习的小伙汁~~~|
09-18 7677
Audio System 01】--- Audio Driver一、 ALSA 音频体系介绍1. ASoC介绍(Machine 、Platform 和 Codec)2. AFE介绍(Audio Front-End 前端后端)3. snd_soc_dai_link (Platform 和 Codec 联系在一起的结构体) 一、 ALSA 音频体系介绍 ALSA 是 Advanced Linux ...
Audio Loopback Dongle
weixin_34407348的博客
04-19 652
Audio Loopback Dongle The diagram and photo below show an audio loopback dongle for the headset connector that we call the "Dr. Rick O'Rang audio loopback dongle." The Chrome hardware team designed t...
Android下很牛逼的Alsa的全面解析
工匠
08-21 815
http://blogold.chinaunix.net/u3/111072/article_131315.html
android使用Alsa Aloop录制系统内部声音
和大伙儿去乘凉
04-24 4329
soc硬件上如果支持loopback功能,可以直接使用audiorecord或tinyalsa的接口以标准的形式去录音 2.使用remote submix 使用REMOTE_SUBMIX形式在录音的时候的时候 ...
Android 使用 ALSA
cahu的专栏
06-09 1719
   android 系统中带有了对alsa的支持,但在默认的情况下并不被下载和编译。可以通过下面步骤在android中使用alsa.1、 使用git 获取到下面三个alsa 相关的包,并放在对应的位置。   git clone git://android.git.kernel.org/platform/external/alsa-lib.git   git clone git://andro
Android Audio代码分析8 - AudioHardwareALSA::openOutputStream函数
mmdev
10-11 411
发现以前写的东西,对调用函数的展开放在了函数的前面,导致不方便找到原来代码及设置的函数参数。 以后打算稍作改动,把对被调函数的展开放在原代码的后面,这样看起来应该方便些。 闲言少叙,跳入代码。 前两天看AudioTrack创建的时候,我们看到了AudioHardwareALSA::openOutputStream,并没有继续往下看。 今天就看看函数AudioHardwareALSA::op...
linux alsa-lib读取音频
05-09
Linux ALSA-Lib库是用于读取和处理音频的开源库。它提供了一套API,可以让开发者通过 C/C++ 编程语言访问 Linux 系统中的音频设备。 ALSA-Lib 可以实现多种音频设备的读写,包括内置音频硬件,外部 USB 音频设备以及蓝牙音频。 ALSA-Lib 提供了一个叫做alsa-lib.h的头文件,这个头文件包含了常用的 ALSA-Lib API 函数。开发者可以根据具体需求来选择合适的函数,最常用的是snd_pcm_open()、snd_pcm_hw_params_set_xxx()、snd_pcm_writei()和snd_pcm_close(),这些函数分别用于打开、设置参数、写数据和关闭音频设备。 ALSA-Lib 提供的多种API函数使得开发者可以对音频进行多种高级操作。比如,开发者可以通过snd_pcm_drop()中止当前播放操作,通过snd_pcm_pause()暂停播放,通过snd_pcm_prepare()准备播放,还可以通过调用snd_pcm_avail_update()获取当前音频设备的缓冲区状态。 读取音频数据可以通过snd_pcm_readi()函数实现,这个函数会一次性从设备中读取指定数量的音频采样,并将其存储在一个指定的缓冲区中。开发者还可以选择使用snd_pcm_mmap_readi()和snd_pcm_mmap_begin()来读取音频采样,这两个函数可以实现更高效的读取。 在开发 Linux 音频应用程序时,ALSA-Lib 是非常重要的组件。通过掌握 ALSA-Lib 的 API 函数,开发者可以实现快速、高效地读取和处理音频数据。因此,熟悉 ALSA-Lib 是 Linux 音频开发工程师的必备技能之一。
写文章

热门文章

  • Android ALSA音频系统架构分析(1)----从Loopback了解Audio 29770
  • 正确解决 Invalid module format 21984
  • Android Camera 系统架构源码分析(1)---->Camera的初始化 14390
  • SELinux/SEAndroid 实例简述(三)实例看SELinux/SEAndroid 9980
  • Android Camera 系统架构源码分析(3)---->Camera的显示流程 7612

分类专栏

  • Gin 2篇
  • Android源码分析 Camera 5篇
  • Android源码分析 Sensor 2篇
  • Android系统编程 6篇
  • Android系统编程 Jni编程及实例 4篇
  • Android系统编程 Android.mk
  • Android APP编程 2篇
  • Android应用编程 UI 4篇
  • Linux驱动 出现问题总结 2篇
  • C++ 2篇
  • 穿戴与物联网系列
  • Android ALSA Audio 1篇

最新评论

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

    xly120404126: 这部分有什么相关书籍的推荐么

  • SELinux/SEAndroid 实例简述(二) TE语言规则

    leo1_yuan: Hi 大佬! 正在找 -- 的含义,感谢! 问一下 `/sys/power/wake_lock -- u:object_r:sysfs_wake_lock:s0` 中,如果不加 -- 会有什么影响吗?

  • Android Camera 系统架构源码分析(3)---->Camera的显示流程

    weixin_60333065: 60帧怎么改,请教

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

    a312983516: 这个alsa能实现接听电话后给对方播放一段固定音频吗。

  • SELinux/SEAndroid 实例简述(三)实例看SELinux/SEAndroid

    edaplayer: 目前为止看到最完整的添加流程,感谢博主解开我心中困惑

最新文章

  • gin源码分析(2)gin启动http服务
  • gin源码分析(1)--初始化中间件,路由组与路由树
  • SELinux/SEAndroid 实例简述(三)实例看SELinux/SEAndroid
2024年2篇
2017年4篇
2015年24篇
2014年2篇

目录

目录

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值

玻璃钢生产厂家商场春夏季美陈湖南仿铜玻璃钢雕塑抚州公园玻璃钢雕塑贵州玻璃钢卡通雕塑定制梅州发光小品玻璃钢雕塑工艺河南特色商场美陈研发公司古代玻璃钢雕塑动物玻璃钢雕塑品牌好玻璃钢大型雕塑寿命淮安东安玻璃钢雕塑厂家玻璃钢抻像雕塑铜陵户外玻璃钢雕塑定制无锡专业玻璃钢雕塑服务介绍河南装饰商场美陈销售企业长沙玻璃钢浮雕不锈钢树叶雕塑潮州玻璃钢动物雕塑供应商宜兴商场主题美陈玻璃钢人物雕塑怎么样黄岛岛玻璃钢雕塑云南公园玻璃钢雕塑厂家宣城欧式玻璃钢雕塑生产厂家泰州白色玻璃钢花盆邯郸标识玻璃钢彩绘雕塑常州球形玻璃钢花盆德阳玻璃钢仿真水果雕塑深圳欧式玻璃钢雕塑定做圣诞商场门头美陈福建欧式玻璃钢雕塑优势玻璃钢抽象雕塑图片南阳肖像玻璃钢人物雕塑公司香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化