当前位置:   article > 正文

Android Audio Driver基础_audio_platform_info

audio_platform_info

         Android Audio底层使用的是liunx alsa驱动,录制或播放或声音所需的基本硬件是音频芯片或声卡(Sound cards), alsa声卡设备如下图所示:

1 Control      // 监听声卡上的一些音频流状态

2 Mixer          // 负责路由或混合声卡上的各种模拟信号

3 Pulse Code Modulation (PCM)         //负责音频流的录制及播放

  1. //获取系统支持的声卡
  2. msmnile_gvmq:/ # cat /proc/asound/cards
  3. // 获取系统支持的pcm设备节点
  4. msmnile_gvmq:/ # lsof /dev/snd/pcmC0D
  5. 定义说明例如:pcmC0D16c
  6. C0:card 0 ,声卡ID
  7. D16:device 16 :音频设备 ID
  8. c:capture 支持录音
  9. p:playback 支持播音

  1. // 获取音频PCM设备详细描述
  2. msmnile_gvmq:/ # cat /proc/asound/pcm
  3. 00-00: MultiMedia1 (*) : : playback 1 : capture 1
  4. 00-01: MultiMedia2 (*) : : playback 1 : capture 1
  5. 00-02: VoiceMMode1 (*) : : playback 1 : capture 1
  6. 定义说明例如:00-00: MultiMedia1 (*) : : playback 1 : capture 1
  7. 00:声卡ID
  8. 00:设备ID
  9. MultiMedia1 :别称
  10. playback 1 :播音
  11. capture 1 : 录音

  1. // 查看当前占用snd相关音频设备
  2. msmnile_gvmq:/ # lsof | grep snd
  3. audio@2.0-servi 17112 audioserve mem REG 259,45 23948 1294 /vendor/lib/libsndmonitor.so
  4. audio@2.0-servi 17112 audioserve 7u CHR 116,2 0t0 8060 /dev/snd/controlC0 audio@2.0-servi 17112 audioserve 8u CHR 116,2 0t0 8060 /dev/snd/controlC0
  5. audio@2.0-servi 17112 audioserve 54u CHR 116,2 0t0 8060 /dev/snd/controlC0 audio@2.0-servi 17112 audioserve 60u CHR 116,3 0t0 14418 /dev/snd/pcmC0D0p

通道配置----映射usecase与pcm的关系

  1. 比如蓝牙电话录音,是走id为29的pcm
  2. // hardware/qcom/audio/configs/msmnile_au/audio_platform_info.xml
  3. <usecase name="USECASE_AUDIO_HFP_SCO" type="in" id="29" />
  4. <usecase name="USECASE_AUDIO_HFP_SCO" type="out" id="29" />
  5. <usecase name="USECASE_AUDIO_HFP_SCO_WB" type="in" id="29" />
  6. <usecase name="USECASE_AUDIO_HFP_SCO_WB" type="out" id="29" />
  7. 除了audio_platform_info.xml,pcm和usecase的默认映射是在
  8. vendor/qcom/opensource/audio-hal/primary-hal/hal/msm8974/platform.c#368

Mixer---用于 Audio route (FE to BE)

  1. // hardware/qcom/audio/configs/msmnile_au/mixer_paths_adp.xml
  2. <path name="hfp-sco">
  3. <ctl name="AUX_PCM_RX Audio Mixer MultiMedia6" value="1" /> //value = 1 含义是开启
  4. <ctl name="MultiMedia6 Mixer TERT_TDM_TX_0" value="1" />
  5. </path>
  6. 或mixer配置
  7. <!-- These are actual sound device specific mixer settings -->
  8. <path name="adc1">
  9. <ctl name="AIF1_CAP Mixer SLIM TX7" value="1"/>
  10. <ctl name="SLIM_0_TX Channels" value="One" />
  11. <ctl name="SLIM TX7 MUX" value="DEC6" />
  12. <ctl name="DEC6 MUX" value="ADC1" />
  13. <ctl name="IIR1 INP1 MUX" value="DEC6" />
  14. </path>
  15. 或音量调节
  16. <ctl name="DEC1 Volume" value="84" />

高通音频通路设置

简单描述下高通 HAL 层音频通路的连接流程,音频通路分为三大块:FE PCMs、BE DAIs、Devices,这三块均需要打开并串联起来才能完成一个音频通路的设置。

| Front End PCMs | SoC DSP | Back End DAIs | Audio devices |

*************
PCM0 <------------> * * <----DAI0-----> Codec Headset
* *
PCM1 <------------> * * <----DAI1-----> Codec Speakers/Earpiece
* DSP *
PCM2 <------------> * * <----DAI2-----> MODEM
* *
PCM3 <------------> * * <----DAI3-----> BT
* *
* * <----DAI4-----> DMIC
* *
* * <----DAI5-----> FM
*************

Front End PCMs:音频前端,一个前端对应着一个 PCM 设备。

FE PCMs 是在音频流打开时设置的,我们首先要了解一个音频流对应着一个 usecase,具体细节请参考:Android 音频系统:AudioTrack、AudioFlinger Threads、AudioHAL Usecases、AudioDriver PCMs

  1. usecase 通俗表示音频场景,对应着音频前端FE,比如:
  2. low_latency:按键音、触摸音、游戏背景音等低延时的放音场景
  3. deep_buffer:音乐、视频等对时延要求不高的放音场景
  4. compress_offload:mp3、flac、aac等格式的音源播放场景,这种音源不需要软件解码,直接把数据送到硬件解码器(aDSP),由硬件解码器(aDSP)进行解码
  5. record:普通录音场景
  6. record_low_latency:低延时的录音场景
  7. voice_call:语音通话场景
  8. voip_call:网络通话场景

  1. start_output_stream() 代码分析:
  2. // 根据 usecase 找到对应 FE PCM id
  3. int platform_get_pcm_device_id(audio_usecase_t usecase, int device_type)
  4. {
  5. int device_id = -1;
  6. if (device_type == PCM_PLAYBACK)
  7. device_id = pcm_device_table[usecase][0];
  8. else
  9. device_id = pcm_device_table[usecase][1];
  10. return device_id;
  11. }
  12. int start_output_stream(struct stream_out *out)
  13. {
  14. int ret = 0;
  15. struct audio_usecase *uc_info;
  16. struct audio_device *adev = out->dev;
  17. // 根据 usecase 找到对应 FE PCM id
  18. out->pcm_device_id = platform_get_pcm_device_id(out->usecase, PCM_PLAYBACK);
  19. if (out->pcm_device_id < 0) {
  20. ALOGE("%s: Invalid PCM device id(%d) for the usecase(%d)",
  21. __func__, out->pcm_device_id, out->usecase);
  22. ret = -EINVAL;
  23. goto error_open;
  24. }
  25. // 为这个音频流新建一个 usecase 实例
  26. uc_info = (struct audio_usecase *)calloc(1, sizeof(struct audio_usecase));
  27. if (!uc_info) {
  28. ret = -ENOMEM;
  29. goto error_config;
  30. }
  31. uc_info->id = out->usecase; // 音频流对应的 usecase
  32. uc_info->type = PCM_PLAYBACK; // 音频流的流向
  33. uc_info->stream.out = out;
  34. uc_info->devices = out->devices; // 音频流的初始设备
  35. uc_info->in_snd_device = SND_DEVICE_NONE;
  36. uc_info->out_snd_device = SND_DEVICE_NONE;
  37. list_add_tail(&adev->usecase_list, &uc_info->list); // 把新建的 usecase 实例添加到链表中
  38. // 根据 usecase、out->devices,为音频流选择相应的音频设备
  39. select_devices(adev, out->usecase);
  40. ALOGV("%s: Opening PCM device card_id(%d) device_id(%d) format(%#x)",
  41. __func__, adev->snd_card, out->pcm_device_id, out->config.format);
  42. if (!is_offload_usecase(out->usecase)) {
  43. unsigned int flags = PCM_OUT;
  44. unsigned int pcm_open_retry_count = 0;
  45. if (out->usecase == USECASE_AUDIO_PLAYBACK_AFE_PROXY) {
  46. flags |= PCM_MMAP | PCM_NOIRQ;
  47. pcm_open_retry_count = PROXY_OPEN_RETRY_COUNT;
  48. } else if (out->realtime) {
  49. flags |= PCM_MMAP | PCM_NOIRQ;
  50. } else
  51. flags |= PCM_MONOTONIC;
  52. while (1) {
  53. // 打开 FE PCM
  54. out->pcm = pcm_open(adev->snd_card, out->pcm_device_id,
  55. flags, &out->config);
  56. if (out->pcm == NULL || !pcm_is_ready(out->pcm)) {
  57. ALOGE("%s: %s", __func__, pcm_get_error(out->pcm));
  58. if (out->pcm != NULL) {
  59. pcm_close(out->pcm);
  60. out->pcm = NULL;
  61. }
  62. if (pcm_open_retry_count-- == 0) {
  63. ret = -EIO;
  64. goto error_open;
  65. }
  66. usleep(PROXY_OPEN_WAIT_TIME * 1000);
  67. continue;
  68. }
  69. break;
  70. }
  71. 语音通话的情景有所不同,它不是传统意义的音频流,流程大概是这样的:
  72. 进入通话时,上层会先设置音频模式为 AUDIO_MODE_IN_CALL(HAL 接口是 adev_set_mode()),再传入音频设备 routing=$device(HAL 接口是 out_set_parameters())
  73. out_set_parameters() 中检查音频模式是否为 AUDIO_MODE_IN_CALL,是则调用 voice_start_call() 打开语音通话的 FE_PCM


Back End DAIs:音频后端,一个后端对应着一个 DAI 接口,一个 FE PCM 能够连接到一个或多个 BE DAI

  1. SLIM_BUS
  2. Aux_PCM
  3. Primary_MI2S
  4. Secondary_MI2S
  5. Tertiary_MI2S
  6. Quatermary_MI2S


Audio Device:有 headset、speaker、earpiece、mic、bt、modem 等;不同的设备可能与不同的 DAI 接口连接,也可能与同一个 DAI 接口连接

  1. device 表示音频端点设备,包括输出端点(如 speaker、headphone、earpiece)和输入端点(如 headset-mic、builtin-mic)。高通 HAL 对音频设备做了扩展,比如 speaker 分为:
  2. SND_DEVICE_OUT_SPEAKER:普通的外放设备
  3. SND_DEVICE_OUT_SPEAKER_PROTECTED:带保护的外放设备
  4. SND_DEVICE_OUT_VOICE_SPEAKER:普通的通话免提设备
  5. SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED:带保护的通话免提设备

  1. 详见 platform.h 音频设备定义,下面仅列举一部分:
  2. /* Sound devices specific to the platform
  3. * The DEVICE_OUT_* and DEVICE_IN_* should be mapped to these sound
  4. * devices to enable corresponding mixer paths
  5. */
  6. enum {
  7. SND_DEVICE_NONE = 0,
  8. /* Playback devices */
  9. SND_DEVICE_MIN,
  10. SND_DEVICE_OUT_BEGIN = SND_DEVICE_MIN,
  11. SND_DEVICE_OUT_HANDSET = SND_DEVICE_OUT_BEGIN,
  12. SND_DEVICE_OUT_SPEAKER,
  13. SND_DEVICE_OUT_HEADPHONES,
  14. SND_DEVICE_OUT_HEADPHONES_DSD,
  15. SND_DEVICE_OUT_SPEAKER_AND_HEADPHONES,
  16. SND_DEVICE_OUT_SPEAKER_AND_LINE,
  17. SND_DEVICE_OUT_VOICE_HANDSET,
  18. SND_DEVICE_OUT_VOICE_SPEAKER,
  19. SND_DEVICE_OUT_VOICE_HEADPHONES,
  20. SND_DEVICE_OUT_VOICE_LINE,
  21. SND_DEVICE_OUT_HDMI,
  22. SND_DEVICE_OUT_DISPLAY_PORT,
  23. SND_DEVICE_OUT_BT_SCO,
  24. SND_DEVICE_OUT_BT_A2DP,
  25. SND_DEVICE_OUT_SPEAKER_AND_BT_A2DP,
  26. SND_DEVICE_OUT_AFE_PROXY,
  27. SND_DEVICE_OUT_USB_HEADSET,
  28. SND_DEVICE_OUT_USB_HEADPHONES,
  29. SND_DEVICE_OUT_SPEAKER_AND_USB_HEADSET,
  30. SND_DEVICE_OUT_SPEAKER_PROTECTED,
  31. SND_DEVICE_OUT_VOICE_SPEAKER_PROTECTED,
  32. SND_DEVICE_OUT_END,
  33. /* Capture devices */
  34. SND_DEVICE_IN_BEGIN = SND_DEVICE_OUT_END,
  35. SND_DEVICE_IN_HANDSET_MIC = SND_DEVICE_IN_BEGIN, // 58
  36. SND_DEVICE_IN_SPEAKER_MIC,
  37. SND_DEVICE_IN_HEADSET_MIC,
  38. SND_DEVICE_IN_VOICE_SPEAKER_MIC,
  39. SND_DEVICE_IN_VOICE_HEADSET_MIC,
  40. SND_DEVICE_IN_BT_SCO_MIC,
  41. SND_DEVICE_IN_CAMCORDER_MIC,
  42. SND_DEVICE_IN_END,
  43. SND_DEVICE_MAX = SND_DEVICE_IN_END,
  44. };
  45. 扩展这么多是为了方便设置 acdb id,比如外放和通话免提虽然都用了同样的喇叭设备,
  46. 但是这两种情景会使用不同的算法,因此需要设置不同的 acdb id 到 aDSP,
  47. 区分 SND_DEVICE_OUT_SPEAKER 和 SND_DEVICE_OUT_VOICE_SPEAKER 是为了匹配到各自的 acdb id。

由于高通 HAL 定义的音频设备与 Android Framework 定义的不一致,所以在高通 HAL 中会根据音频场景对框架层传入的音频设备进行转换,详见:

platform_get_output_snd_device()
platform_get_input_snd_device()
在高通 HAL 中,我们只看到 usecase(即 FE PCM)和 device,device 和 BE DAI 是“多对一”的关系,device 连接着唯一的 BE DAI(反过来就不成立了,BE DAI 可能连接着多个 device),所以确定了 device 也就能确定所连接的 BE DAI。

路由选择

  1. 我们在 mixer_pahts.xml 中看到 usecase 相关的通路:
  2. <path name="deep-buffer-playback speaker">
  3. <ctl name="QUAT_MI2S_RX Audio Mixer MultiMedia1" value="1" />
  4. </path>
  5. <path name="deep-buffer-playback headphones">
  6. <ctl name="TERT_MI2S_RX Audio Mixer MultiMedia1" value="1" />
  7. </path>
  8. <path name="deep-buffer-playback earphones">
  9. <ctl name="QUAT_MI2S_RX Audio Mixer MultiMedia1" value="1" />
  10. </path>
  11. <path name="low-latency-playback speaker">
  12. <ctl name="QUAT_MI2S_RX Audio Mixer MultiMedia5" value="1" />
  13. </path>
  14. <path name="low-latency-playback headphones">
  15. <ctl name="TERT_MI2S_RX Audio Mixer MultiMedia5" value="1" />
  16. </path>
  17. <path name="low-latency-playback earphones">
  18. <ctl name="QUAT_MI2S_RX Audio Mixer MultiMedia5" value="1" />
  19. </path>

这些通路其实就是连接 usecase、device 之间的路由。比如 “deep-buffer-playback speaker” 是连接 deep-buffer-playback FE PCM、speaker Device 之间的路由,打开 “deep-buffer-playback speaker”,则把 deep-buffer-playback FE PCM 和 speaker Device 连接起来;关闭 “deep-buffer-playback speaker”,则断开 deep-buffer-playback FE PCM 和 speaker Device 的连接。

之前提到“device 连接着唯一的 BE DAI,确定了 device 也就能确定所连接的 BE DAI”,因此这些路由通路其实都隐含着 BE DAI 的连接:FE PCM 并非直接到 device 的,而是 FE PCM 先连接到 BE DAI,BE DAI 再连接到 device。这点有助于理解路由控件,路由控件面向的是 FE PCM 和 BE DAI 之间的连接,回放类型的路由控件名称一般是: $BE_DAI Audio Mixer $FE_PCM,录制类型的路由控件名称一般是:$FE_PCM Audio Mixer $BE_DAI,这很容易分辨。

例如 “deep-buffer-playback speaker” 通路中的路由控件:

<ctl name="QUAT_MI2S_RX Audio Mixer MultiMedia1" value="1" />
MultiMedia1:deep_buffer usacase 对应的 FE PCM
QUAT_MI2S_RX:speaker device 所连接的 BE DAI
Audio Mixer:表示 DSP 路由功能
value:1 表示连接,0 表示断开连接
这个控件的意思是:把 MultiMedia1 PCM 与 QUAT_MI2S_RX DAI 连接起来。这个控件并没有指明 QUAT_MI2S_RX DAI 与 speaker device 之间的连接,因为 BE DAIs 与 Devices 之间并不需要路由控件,如之前所强调”device 连接着唯一的 BE DAI,确定了 device 也就能确定所连接的 BE DAI“。

路由控件的开关不仅仅影响 FE PCMs、BE DAIs 的连接或断开,同时会使能或禁用 BE DAIs,要深入理解这点的话需要去研究 ALSA DPCM(Dynamic PCM) 机制,这里稍作了解即可。

路由操作函数是 enable_audio_route()/disable_audio_route(),这两个函数名称很贴合,控制 FE PCMs 与 BE DAIs 的连接或断开。

alsa调试命令

tinymix

可进行通道切换或配置

tinycap

录音命令,使用命令前先用 tinymix 切换到音频通道

  1. tinycap /sdcard/test.pcm -D 0 -d 0 -c 4 -r 48000 -b 32 -p 768 -n 10
  2. -D card 声卡
  3. -d device 设备
  4. -c channels 通道
  5. -r rate 采样率
  6. -b bits pcm 位宽
  7. -p period_size 一次中断的帧数
  8. -n n_periods 周期数

使用示例

  1. //要先使用tinymix进行通道配置
  2. tinymix "MultiMedia2 Mixer QUAT_TDM_TX_0" 1
  3. tinycap /data/test.wav -c 8 -d 1

tinyplay

播音命令,使用示例

  1. //要先使用tinymix进行通道配置
  2. tinymix "QUAT_TDM_RX_0 Channels" "Two"
  3. tinymix "QUAT_TDM_RX_0 Audio Mixer MultiMedia1" "1"
  4. tinyplay /sdcard/Music/LoveYou.wav

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号