Audio系统介绍
1. 概述¶
本章指导客户了解 Sigmastar android 音频系统架构和常用定位点,达到初步定位音频问题的目的。
2. 功能描述¶
2.1 音频系统框图¶
原生音频解耦方案框图如下。在维持原生系统架构不变的前提下,将私有业务行为融合并同步于原生系统,主要涉及 2 个方面,音量控制方案 & 私有通路外设播放。
新增 AudioPolicyEngineCustom 和 AudioPolicyManagerCustom 模块实现解耦,也是解耦方案的核心所在。向上对接 Framework 的接口进行切换输出设备,向下调用 Audio HAL 标准接口实现音量、设备切换等功能。涉及主要方案如下:
—————————————————————————————————————————————————————————————————————————————— Application _____________________ _____________________ | play | | record | ————————————————————— ————————————————————— --------------------------------------------------- Framework _______________________________________________ | AudioPolicyService | | ______________________________________ | | | AudioPolicyManagerCustom | | | | __________________________ | | | | | AudioPolicyEngineCustom | | | | | —————————————————————————— | | | —————————————————————————————————————— | ——————————————————————————————————————————————— _______________________________________________ | audio flinger | ——————————————————————————————————————————————— --------------------------------------------------- _______________________________________________ | audio hal | ——————————————————————————————————————————————— --------------------------------------------------- _______________________________________________ | tinyalsa | User Space | (utils: tinyplay tinycap tinymix) | ——————————————————————————————————————————————— ------------------------------------------------------------------ _______________________________________________ Kernel Space | alsa | ——————————————————————————————————————————————— ---------------------------------------------------- _______________________________________________ | sound driver | ——————————————————————————————————————————————— ——————————————————————————————————————————————————————————————————————————————
2.2 音量方案¶
-
BT、USB 外接设备,走 AOSP 原生音量方案;speaker、headphone、spdif 走私有通路音量方案。
-
所有私有通路使用 mixer control 接口完成音量调整。
-
私有通路音量作用在 interface 相连接的 DPGA 上
Playback: _________ ____________ |\ _______ | MIU | -> | RDMA 0 | -> | - DPGA -> | DAC | -> Speaker ————————— ———————————— |/ ——————— Capture: _________ ____________ _______ /| ________ | MIU | <- | WDMA 0 | <- | MUX | <- DPGA - | <- | ADC | <- AMIC ————————— ———————————— ——————— \| ————————
-
支持私有通路音量调节的 control 节点
console:/ # tinymix2 -D 0 contents Number of controls: 50 ctl type num name value 0 INT 1 DAC_0 Playback Volume 535 (range 0->1023) 1 INT 1 DAC_1 Playback Volume 535 (range 0->1023) 2 INT 1 I2S_TXA_0 Playback Volume 511 (range 0->1023) 3 INT 1 I2S_TXA_1 Playback Volume 511 (range 0->1023) 4 INT 1 I2S_TXB_0 Playback Volume 511 (range 0->1023) 5 INT 1 I2S_TXB_1 Playback Volume 511 (range 0->1023) 6 INT 1 SPDIF_TX_0 Playback Volume 511 (range 0->1023) 7 INT 1 SPDIF_TX_1 Playback Volume 511 (range 0->1023) 8 INT 1 ADC_A_0 Capture Volume 700 (range 0->1023) 9 INT 1 ADC_A_1 Capture Volume 700 (range 0->1023) 10 INT 1 ADC_B_0 Capture Volume 700 (range 0->1023) 11 INT 1 ADC_B_1 Capture Volume 700 (range 0->1023) 12 INT 1 I2S_RXA_0 Capture Volume 511 (range 0->1023) 13 INT 1 I2S_RXA_1 Capture Volume 511 (range 0->1023) 14 INT 1 I2S_RXA_2 Capture Volume 511 (range 0->1023) 15 INT 1 I2S_RXA_3 Capture Volume 511 (range 0->1023) 16 INT 1 I2S_RXA_4 Capture Volume 511 (range 0->1023) 17 INT 1 I2S_RXA_5 Capture Volume 511 (range 0->1023) 18 INT 1 I2S_RXA_6 Capture Volume 511 (range 0->1023) 19 INT 1 I2S_RXA_7 Capture Volume 511 (range 0->1023) 20 INT 1 I2S_RXB_0 Capture Volume 511 (range 0->1023) 21 INT 1 I2S_RXB_1 Capture Volume 511 (range 0->1023) 22 INT 1 HDMI_RX_0 Capture Volume 511 (range 0->1023) 23 INT 1 HDMI_RX_1 Capture Volume 511 (range 0->1023) 24 INT 1 DMIC_0 Capture Volume 511 (range 0->1023) 25 INT 1 DMIC_1 Capture Volume 511 (range 0->1023) 26 INT 1 DMIC_2 Capture Volume 511 (range 0->1023) 27 INT 1 DMIC_3 Capture Volume 511 (range 0->1023) 28 INT 1 DMIC_4 Capture Volume 511 (range 0->1023) 29 INT 1 DMIC_5 Capture Volume 511 (range 0->1023) 30 INT 1 DMIC_6 Capture Volume 511 (range 0->1023) 31 INT 1 DMIC_7 Capture Volume 511 (range 0->1023) 32 INT 2 ECHO Capture Volume 511, 511 (range 0->1023) ... ...
-
AOSP 原生音量结构
track volume:单个 APP 通过 track 接口设置音量是设置的是这个,它只影响本 APP 的音量
stream volume:设置某一类型 stream 的音量
stream volume alias:设置的是同一组 stream 的音量,比如使用某个音量调节滑动条设置的音量,比如设置媒体音量,所有 APP 的媒体音量都会受到影响(但是电话通话音量、闹钟音量不受影响)
master volume:设置它等于设置所有的 stream volume 和 track volume,作为一个乘数因子来影响所有的音量。
在 audioFlinger 的 thread 中,各个音量乘积后再归一化处理到 [0,1] 区间,作用于数据上。
增益大小 = 音频数据增益 * master_volume * stream_volume * track_volume
- AOSP 原生音量保存
使用 SettingProvider 来保存,Settings 有 system、global、secure 三个表格,
AudioService 通过 persistVolume 接口将 volume 保存在 system 表格中。
console:/ # settings list system | grep -i volume volume_alarm=6 volume_bluetooth_sco=7 volume_music=5 volume_music_headphone=10 volume_music_headset=10 volume_music_spdif=89 volume_music_speaker=23 volume_music_usb_headset=10 volume_notification=5 volume_ring=5 volume_system=7 volume_voice=4 console:/ #
- AOSP 原生 master_volume
原生系统中,只有 audioService 和 audioFlinger 在使用 master volume,
audioService :如果系统使用了固定音量选项配置,则调用 setMasterVolume(1.0f) 设置为 full scale volume,否则不设置。
audioFlinger :初始化 load audio hal 的时候,先 getMasterVolume 然后再 setMasterVolume;以 hal 提供的值来做初始化的值,否则初始化设置为 1.0f;在创建 thread 以及 thread 设置 setMasterVolume 的时候,如果 hal 支持 setMasterVolume 接口,则 framework 中 MasterVolume 为 1.0f
setMasterVolume 可以往下对接到声卡中去,控制所有声音的音量。
但是此方案需要考虑如何客制化 SystemUI 让音量按键调整逻辑对接上 setMasterVolume
2.3 关键字说明¶
-
device(音频输入/输出设备)
audio output 的 device 指的是 audio codec 的 RDMA。device 是 ASOC 对 audio codec 中 DMA 的抽象,1个card 下只挂载 1 个device,audio codec 中的 RDMA 和 output device 为 一一对应的关系。如 card 0、device 0 对应 audio codec 中的 RDMA0,card 1、device 0 对应于audio codec 中的 RDMA1,以此类推。WDMA 和 input device 也是一一对应的关系。如 card 0、device 0 对应 audio codec 中的 WDMA0,card 1、device 0 对应于audio codec 中的 WDMA1。ASOC 的数据流均以 DMA 为中心进行串接。
ASOC 中还包括另一类 device,即 input 设备不经过 DMA,直接将数据流送至 output 设备,例如(AMIC -> DAC)。这一类 device 称为直通通路 device,即 passthrough ,目前仅 AI_MCH_VIR_SEL/AO_VIR_MUX_SEL 节点支持。
-
interface(音频输出外设)
output 的 interface 指的是 audio codec 的音频输出外设接口的抽象,如 DAC/SPDIF/I2S_TX/HDMI_TX 等接口。
input 的 interface 指的是 audio codec 的音频输入外设接口的抽象,如 AMIC/DMIC/I2S_RX/HDMI_RX 等接口。
-
output attach
output 的 attach 指的是将 interface 挂载到 RDMA 上,对于 output device 而言,attach 是将 RDMA 的输出信号连接到具体的 interface,使外设输出音频信号。output 支持动态 attach。
硬件混音:即当同一个 output interface 被 attach 到 2 个 output device 时候,output interface 的输出为 2 个 output device 输出混音后的结果。
直通通路:当使用场景中包含 passthrough 时候,需要先 attach input,再 attach output,否则将不能正常使用。
分发:即当多个 output interface 被 attach 到 1 个 output device 时候,从 RDMA 输出的信号经过 DPGA 做增益调节后,分成多个支路分别到达各个 interface,各个外设同时输出声音。
-
input attach
input 的 attach 指的是将 interface 挂载到 WDMA 对应的 MUX 上,即将 device 和 interface 关联起来。对于 input device 而言,attach 是设定 WDMA 对应的MUX,选择哪些 interface 的数据可以通过 MUX ,由 WDMA 做后续的数据搬移。input 不支持动态 attach。
-
detach
detach 指的是 将 interface 和 RDMA/WDMA 的连接断开。
-
output echo
output 的 echo 指的是 AEC 的回采参考数据。由 audio codec 框图可以看出,SRC 的输入是 RDMA 输出并经过 DPGA 放大的信号,SRC 的输出则是对输入进行重采样后的信号,可通过 multi channel 送进 WDMA,作为 AEC 算法的回声参考数据。
-
input echo
input 的 echo 指的是 audio codec 提供的 AEC 参考数据。
对于 input device 而言,echo 表示 audio codec 框图中 SRC 的 输出信号。而对于 output device 而言,echo 表示将 output device 的输出连接到 SRC 的输入。下图以简单框图表示 input device 和 output device 同时使用 echo 的 数据流情况,应用即可获取到已经对齐的 AEC far end 和 near end 的数据。
_________ ___| ADC | <- AMIC _________ ____________ _______ | ————————— | APP | <- | WDMA 0 | <- | MCH | <--| ————————— ———————————— ——————— | ________ |___ | SRC | ———————— ^ | _________ ____________ |\ | _______ | APP | -> | RDMA 0 | -> | - DPGA ---------------> | DAC | -> Speaker ————————— ———————————— |/ ———————
-
GAIN
gain 即 DPGA gain。
-
format
即表示用什么数据形式来表示一个音频采样样本,目前仅支持 S16_LE 格式(PCM linear 16 bit little endian)
-
sample rate
对于 input 表示 录音设备在单位时间内对模拟信号采样的次数,采样频率越高,机械波的波形越真实越自然。目前 ADC 支持 8000/16000/32000/48000 采样率。
对于 output 表示播放采样率。
-
period size
对于 output,period size 表示默认的起播条件(缓存中的样本数大于period size,才会起播)
-
I2S Mode
决定了 I2S 的工作模式,是标准的 I2S 模式还是 TDM I2S 模式(2 channel 或 多 channel),是 Master 还是 Slave (Master 提供同步时钟,Slave 接收同步时钟)。一般来说,工作模式没有什么限制,能与外接的 codec 时钟匹配上即可。
-
I2S BitWidth
收发数据的位宽,目前仅支持 16bit,硬件只能处理 16 bit。
-
I2S format
即 I2S 的对齐方式,目前仅支持 I2S Philips 以及 Left-justified 对齐。
I2S Philips 的对齐格式,样本数据的第一个数据位出现在 WCLK(左右通道切换时钟)跳变的第一个 BCLK(串行时钟)后。
Left-justified 对齐格式,样本数据的第一个数据位出现在 WCLK(左右通道切换时钟)跳变的第一个 BCLK(串行时钟)内,且 WCLK 的极性与 I2S Philips 的对齐格式相反。
-
MCLK
称为主时钟,也叫系统时钟(system clock),一般是采样频率的 256 或 384 倍,作用是为了使系统间能够更好的同步,并不是必须的。
目前仅支持 11.2896M、12.288M、16.384M、19.2M、24M、48M 等。
-
4-wire/6-wire Mode
Sigmastar 的 I2S 有 2 种接线方式。
一种是 4-wire 模式,包括 RX_WCK、RX_BCK、RX_SDI、TX_SDO 四根线,此模式下,TX 没有独立的 clock,所有的 clock 均有 RX 提供,故在此模式下 TX 需要依赖 RX 来使用,没法单独使用 TX,且 I2S TX 的参数需要与 I2S RX 一致。
另一种是 6-wire 模式,包括 RX_WCK、RX_BCK、RX_SDI、TX_WCK、TX_BCK、TX_SDO 六根线,此模式下 RX 和 TX 各自独立,没有关联。
4-wire/6-wire Mode 的选择需要根据具体场景来决定。属于同一组 I2S 的 RX/TX 不能一边设置成 4-wire Mode,另一边设置成 6-wire Mode。
-
slot
表示 I2S 传输的声道数,目前 I2S 模式下支持 2 slot,TDM 模式下支持 4/8/16 slot,但是 I2S TX 的有效数据为 2 slot,当对 I2S TX 设置大于 2 的 slot 个数时候,除了 slot 0 和 slot 1,其他的均为无效数据。
-
passthrough (直通通道)
passthrough 意思指 input 设备直接将数据流送至 output 设备,不经过 DMA,以 ADC -> DAC 为例,
/| _________ ------- <- DPGA - | <- | ADC | <- AMIC | \| ————————— | | V |\ _________ ---> | - DPGA -> | | |/ | HW | _________ | Mixer | -> | DAC | -> Speaker _________ ____________ |\ | | ————————— | MIU | -> | RDMA 0 | -> | - DPGA -> | | ————————— ———————————— |/ —————————
-
input 的 attach 节点
AI_MCH_01_SEL:即 MUX 的 0/1 声道
目前 AMIC 最大支持 3 channel,DMIC 最大支持 8 channel。Input 最大支持 16 channel attach。
-
output 的 attach 节点
DMA 是给非硬件混音使用,DMA+SRC 是给硬件混音使用
-
spdif 的 attach 节点
LDMA 是给 lpcm 格式使用,LDMA+SRC 是给 lpcm 做硬件混音使用,NLDMA 是给 nlpcm 格式播放 compress 使用
-
CHANNEL_MODE
STEREO:正常的立体声模式
DOUBLE_MONO:左右 2 个声道输出为同样的单声道数据
DOUBLE_LEFT:左右 2 个声道输出为左声道数据
DOUBLE_RIGHT:左右 2 个声道输出为右声道数据
EXCHANGE:左右 2 个声道输出为左右声道互换数据
ONLY_LEFT:只有左声道输出为单声道数据
ONLY_RIGHT:只有右声道输出为单声道数据
2.4 音频输入输出通路¶
系统提供 HDMI、SStarExoPlayer 本地媒体的私有音频通路音频输入输出。
系统提供 audiotrack、ijkplayer 原生媒体框架、mediacodec 播放的音频输出。
支持喇叭、耳机、SPDIF、I2S_TX、USB声卡、蓝牙的设备输出。支持设备间的切换。
__________ ------------------------------------------------------> | | (NLPCM) | | SPDIF_TX | -> spdif | _________ (LPCM) |\ | | _________ ____________ | | | ---> | - VOL -> | | | HDMI_RX | --> | LPCM/ | | | | | |/ —————————— ————————— --> | NLPCM |------> | | | --> Headphone | ———————————— (LPCM) | | ______________ | |\ _____ _____ | (NLPCM) | | |---> | primary hal |--+--> | - VOL -> | DAC | -> | Amp |----> Speaker | _____________ | | |AEC/ANR/EQ/AGC| | |/ ————— ————— ___________| | | | | —————————————— | | MediaPlayer|-> | | | | | |\ ________ _____ ———————————— | Audio Track | ---> | Mixer | ---> | - VOL -> | I2S_TX | -> | Amp | -> Speaker ____________ | | | | |/ ———————— ————— | key sound |-> | | | | ———————————— | | | | _____________ _____________ ————————————— | |---> | a2dp hal | -> | BT Driver | | | ————————————— ————————————— ____________ _____________ | | | USB / MIC |-> | AudioRecord | ---> | | _____________ _____________ ———————————— ————————————— | |---> | usb hal | -> | USB / 2.4G | | | ————————————— ————————————— —————————
2.5 音频输出设备策略¶
在应用层上,app 会根据不用场景,传入不同的 stream_type 类型,
例如 mediaplayer 播放一段视频:
media_player.reset(); media_player.setDataSource(afd); media_player.setAudioStreamType(AudioManager.STREAM_MUSIC); media_player.setLooping(false); media_player.setDisplay(holder); media_player.prepare(); media_player.start();
或者 AudioTrack播放一段音频:
int audioBufSize = AudioTrack.getMinBufferSize(48000, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT); AudioAttributes attr = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setLegacyStreamType(AudioManager.STREAM_MUSIC) .build(); AudioFormat format = new AudioFormat.Builder() .setSampleRate(48000) .setEncoding(AudioFormat.ENCODING_PCM_16BIT) .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO) .build(); int sessionID = audioManager.generateAudioSessionId(); AudioTrack mAudioTrack = new AudioTrack(attr, format, audioBufSize, AudioTrack.MODE_STREAM, sessionID);
stream_type 与 routing_strategy 枚举列表:
typedef enum { AUDIO_STREAM_DEFAULT =-1, AUDIO_STREAM_VOICE_CALL = 0, // 电话 AUDIO_STREAM_SYSTEM = 1, // 系统声音 AUDIO_STREAM_RING = 2, // 铃声 AUDIO_STREAM_MUSIC = 3, // 音乐声 AUDIO_STREAM_ALARM = 4, // 警告 AUDIO_STREAM_NOTIFICATION = 5, // 通知 AUDIO_STREAM_BLUETOOTH_SCO = 6, // 蓝牙耳机接通 AUDIO_STREAM_ENFORCED_AUDIBLE = 7, // Sounds that cannot be muted by user and must be routed to speaker AUDIO_STREAM_DTMF = 8, // 双音多频 AUDIO_STREAM_TTS = 9, // 文本合成语音 AUDIO_STREAM_ACCESSIBILITY = 10, // For accessibility talk back prompts AUDIO_STREAM_ASSISTANT = 11, // 虚拟的语音助手 NUM_STREAM_TYPES } audio_stream_type_t; enum routing_strategy { STRATEGY_MEDIA, STRATEGY_PHONE, STRATEGY_SONIFICATION, STRATEGY_SONIFICATION_RESPECTFUL, STRATEGY_DTMF, STRATEGY_ENFORCED_AUDIBLE, NUM_STRATEGIES };
在 framework 层,根据 stream_type 映射到 routing_strategy
具体代码(/frameworks/av/services/audiopolicy/engine/common/src/EngineDefaultConfig.h)如下:
/** * @brief AudioProductStrategies hard coded array of strategies to fill new engine API contract. */ const engineConfig::ProductStrategies gOrderedStrategies = { {"STRATEGY_PHONE", { {"phone", AUDIO_STREAM_VOICE_CALL, "AUDIO_STREAM_VOICE_CALL", {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_VOICE_COMMUNICATION, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}}, }, {"sco", AUDIO_STREAM_BLUETOOTH_SCO, "AUDIO_STREAM_BLUETOOTH_SCO", {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_SCO, ""}}, } }, }, {"STRATEGY_SONIFICATION", { {"ring", AUDIO_STREAM_RING, "AUDIO_STREAM_RING", {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}} }, {"alarm", AUDIO_STREAM_ALARM, "AUDIO_STREAM_ALARM", {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ALARM, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}}, } }, }, {"STRATEGY_ENFORCED_AUDIBLE", { {"", AUDIO_STREAM_ENFORCED_AUDIBLE, "AUDIO_STREAM_ENFORCED_AUDIBLE", {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_AUDIBILITY_ENFORCED, ""}} } }, }, {"STRATEGY_ACCESSIBILITY", { {"", AUDIO_STREAM_ACCESSIBILITY, "AUDIO_STREAM_ACCESSIBILITY", {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}} } }, }, {"STRATEGY_SONIFICATION_RESPECTFUL", { {"", AUDIO_STREAM_NOTIFICATION, "AUDIO_STREAM_NOTIFICATION", { {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}, {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}, {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}, {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}, {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_NOTIFICATION_EVENT, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""} } } }, }, {"STRATEGY_MEDIA", { {"assistant", AUDIO_STREAM_ASSISTANT, "AUDIO_STREAM_ASSISTANT", {{AUDIO_CONTENT_TYPE_SPEECH, AUDIO_USAGE_ASSISTANT, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}} }, {"music", AUDIO_STREAM_MUSIC, "AUDIO_STREAM_MUSIC", { {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_MEDIA, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}, {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_GAME, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}, {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANT, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}, {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}, {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""} }, }, {"system", AUDIO_STREAM_SYSTEM, "AUDIO_STREAM_SYSTEM", {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_ASSISTANCE_SONIFICATION, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}} } }, }, {"STRATEGY_DTMF", { {"", AUDIO_STREAM_DTMF, "AUDIO_STREAM_DTMF", { {AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""} } } }, }, {"STRATEGY_CALL_ASSISTANT", { {"", AUDIO_STREAM_CALL_ASSISTANT, "AUDIO_STREAM_CALL_ASSISTANT", {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_CALL_ASSISTANT, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_NONE, ""}} } }, }, {"STRATEGY_TRANSMITTED_THROUGH_SPEAKER", { {"", AUDIO_STREAM_TTS, "AUDIO_STREAM_TTS", {{AUDIO_CONTENT_TYPE_UNKNOWN, AUDIO_USAGE_UNKNOWN, AUDIO_SOURCE_DEFAULT, AUDIO_FLAG_BEACON, ""}} } }, } };
同时也支持客制化 xml 配置,需要覆盖到 /vendor/etc/ 目录下。
AOSP 参考目录:(/frameworks/av/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_product_strategies.xml)
客制化目录:(device/sigmastar/pioneer5/audio_config/audio_policy_engine_product_strategies.xml)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <!-- Copyright (C) 2018 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <ProductStrategies> <!-- "hidden strategies" like TTS, enforced audible: Shall we expose them here or keep it hard coded --> <!-- Used to identify the volume of audio streams for enforced system sounds in certain countries (e.g. camera in Japan) This strategy will only have higher priority than phone if force for system is set to enforced. --> <ProductStrategy name="STRATEGY_PHONE"> <AttributesGroup streamType="AUDIO_STREAM_VOICE_CALL" volumeGroup="voice_call"> <Attributes> <Usage value="AUDIO_USAGE_VOICE_COMMUNICATION"/> </Attributes> </AttributesGroup> <AttributesGroup streamType="AUDIO_STREAM_BLUETOOTH_SCO" volumeGroup="bluetooth_sco"> <Attributes> <Flags value="AUDIO_FLAG_SCO"/> </Attributes> </AttributesGroup> </ProductStrategy> <ProductStrategy name="STRATEGY_SONIFICATION"> <AttributesGroup streamType="AUDIO_STREAM_RING" volumeGroup="ring"> <Attributes> <Usage value="AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE"/> </Attributes> </AttributesGroup> <AttributesGroup streamType="AUDIO_STREAM_ALARM" volumeGroup="alarm"> <Attributes> <Usage value="AUDIO_USAGE_ALARM"/> </Attributes> </AttributesGroup> </ProductStrategy> <ProductStrategy name="STRATEGY_ENFORCED_AUDIBLE"> <AttributesGroup streamType="AUDIO_STREAM_ENFORCED_AUDIBLE" volumeGroup="enforced_audible"> <Attributes> <Flags value="AUDIO_FLAG_AUDIBILITY_ENFORCED"/> </Attributes> </AttributesGroup> </ProductStrategy> <ProductStrategy name="STRATEGY_ACCESSIBILITY"> <AttributesGroup streamType="AUDIO_STREAM_ACCESSIBILITY" volumeGroup="accessibility"> <Attributes> <Usage value="AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY"/> </Attributes> </AttributesGroup> </ProductStrategy> <ProductStrategy name="STRATEGY_SONIFICATION_RESPECTFUL"> <AttributesGroup streamType="AUDIO_STREAM_NOTIFICATION" volumeGroup="notification"> <Attributes> <Usage value="AUDIO_USAGE_NOTIFICATION"/> </Attributes> <Attributes> <Usage value="AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST"/> </Attributes> <Attributes> <Usage value="AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT"/> </Attributes> <Attributes> <Usage value="AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED"/> </Attributes> <Attributes> <Usage value="AUDIO_USAGE_NOTIFICATION_EVENT"/> </Attributes> </AttributesGroup> </ProductStrategy> <ProductStrategy name="STRATEGY_MEDIA"> <AttributesGroup streamType="AUDIO_STREAM_ASSISTANT" volumeGroup="assistant"> <Attributes> <ContentType value="AUDIO_CONTENT_TYPE_SPEECH"/> <Usage value="AUDIO_USAGE_ASSISTANT"/> </Attributes> </AttributesGroup> <AttributesGroup streamType="AUDIO_STREAM_MUSIC" volumeGroup="music"> <Attributes> <Usage value="AUDIO_USAGE_MEDIA"/> </Attributes> <Attributes> <Usage value="AUDIO_USAGE_GAME"/> </Attributes> <Attributes> <Usage value="AUDIO_USAGE_ASSISTANT"/> </Attributes> <Attributes> <Usage value="AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE"/> </Attributes> <Attributes></Attributes> </AttributesGroup> <AttributesGroup streamType="AUDIO_STREAM_SYSTEM" volumeGroup="system"> <Attributes> <Usage value="AUDIO_USAGE_ASSISTANCE_SONIFICATION"/> </Attributes> </AttributesGroup> </ProductStrategy> <ProductStrategy name="STRATEGY_DTMF"> <AttributesGroup streamType="AUDIO_STREAM_DTMF" volumeGroup="dtmf"> <Attributes> <Usage value="AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING"/> </Attributes> </AttributesGroup> </ProductStrategy> <!-- Used to identify the volume of audio streams exclusively transmitted through the speaker (TTS) of the device --> <ProductStrategy name="STRATEGY_TRANSMITTED_THROUGH_SPEAKER"> <AttributesGroup streamType="AUDIO_STREAM_TTS" volumeGroup="tts"> <Attributes> <Flags value="AUDIO_FLAG_BEACON"/> </Attributes> </AttributesGroup> </ProductStrategy> </ProductStrategies>
audioPolicyEngine 会根据 routing_strategy 调用 Engine::getDevicesForStrategyInt 接口映射到具体的 audio_device,
设备策略由 2 部分构成(原生策略部分 +UI 策略部分 )
- 原生策略(高优先级)
- 高优先级 Bluetooth,如果有连接蓝牙,声音就从蓝牙设备出来;如果没有连接蓝牙设备,但是有连接 USB 设备,声音就从 USB 出来;如果没有连接蓝牙/USB 设备,但是有连接耳机,声音就从耳机出来;如果三者都没有连接,声音遵从 UI 策略输出。
- 次优先级 USB Headset,如果有连接 USB 设备,声音就从 USB 设备出来;如果没有连接 USB 设备,但是有连接耳机,声音就从耳机出来;如果两者都没有连接,声音遵从 UI 策略输出。
-
低优先级 耳机,如果有连接耳机,声音就从耳机出来;如果没有连接耳机,声音遵从 UI 策略输出。
-
UI 策略(低优先级)
- UI 选择 Spdif,如果 Spdif 有连接,那就声音从 Spdif 连接的设备出来;如果没有连接就无声。
- UI 选择 Speaker,声音从喇叭输出。
3. 开发指引¶
3.1 设备策略修改¶
AOSP 原生系统中在开关机的时候,audio manager 没有保存关机前的输出 device 选择,这部分是由 SStarSystemControl 来保存、初始化。
@Override public void onCreate() { super.onCreate(); ... ... initAudioConfiguration(); ... ... } private void initAudioConfiguration() { try { ... ... int outputDevice = mServiceStub.getAudioOutputDevice(); ... ... applyAudioOutputPolicy(outputDevice); ... ... } catch (Exception e) { e.printStackTrace(); } }
保存文件目录(vendor\sigmastar\hardware\systemcontrol\system_control_user_configuration.xml)
... ... <sound_mode>STANDARD</sound_mode> <audio_output_device>SPEAKER</audio_output_device> <audio_balance>0</audio_balance> <spdif_mode>PCM</spdif_mode> ... ...
设备策略的定制修改,需要进行相应模块的代码逻辑修改。
- 原生策略部分
此部分因 AOSP 原生 engine 逻辑中,支持 speaker 与 spdif 同时输出,故结合 UI 设置需求,需要客制化调整这一部分逻辑,采用 SSTAR_CUSTOMIZED 宏定义区分开。
由 AudioPolicyEngineCustom 模块(vendor\sigmastar\common\audio\audiopolicyenginecustom)提供 Engine::getDevicesForStrategyInt 接口实现。
具体代码(audiopolicyenginecustom\src\Engine.cpp)如下:
DeviceVector Engine::getDevicesForStrategyInt(legacy_strategy strategy, DeviceVector availableOutputDevices, const SwAudioOutputCollection &outputs) const { DeviceVector devices; switch (strategy) { case STRATEGY_TRANSMITTED_THROUGH_SPEAKER: devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER); break; case STRATEGY_PHONE: { devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_HEARING_AID); if (!devices.isEmpty()) break; devices = availableOutputDevices.getFirstDevicesFromTypes( getLastRemovableMediaDevices()); if (!devices.isEmpty()) break; devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_EARPIECE); } break; case STRATEGY_SONIFICATION: case STRATEGY_ENFORCED_AUDIBLE: // strategy STRATEGY_ENFORCED_AUDIBLE uses same routing policy as STRATEGY_SONIFICATION // except: // - when in call where it doesn't default to STRATEGY_PHONE behavior // - in countries where not enforced in which case it follows STRATEGY_MEDIA if ((strategy == STRATEGY_SONIFICATION) || (getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED)) { devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER); } // if SCO headset is connected and we are told to use it, play ringtone over // speaker and BT SCO if (!availableOutputDevices.getDevicesFromTypes(getAudioDeviceOutAllScoSet()).isEmpty()) { DeviceVector devices2; devices2 = availableOutputDevices.getFirstDevicesFromTypes({ AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT, AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, AUDIO_DEVICE_OUT_BLUETOOTH_SCO}); // Use ONLY Bluetooth SCO output when ringing in vibration mode if (!((getForceUse(AUDIO_POLICY_FORCE_FOR_SYSTEM) == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) && (strategy == STRATEGY_ENFORCED_AUDIBLE))) { if (getForceUse(AUDIO_POLICY_FORCE_FOR_VIBRATE_RINGING) == AUDIO_POLICY_FORCE_BT_SCO) { if (!devices2.isEmpty()) { devices = devices2; break; } } } // Use both Bluetooth SCO and phone default output when ringing in normal mode if (audio_is_bluetooth_out_sco_device(getPreferredDeviceTypeForLegacyStrategy( availableOutputDevices, STRATEGY_PHONE))) { if (strategy == STRATEGY_SONIFICATION) { devices.replaceDevicesByType( AUDIO_DEVICE_OUT_SPEAKER, availableOutputDevices.getDevicesFromType( AUDIO_DEVICE_OUT_SPEAKER_SAFE)); } if (!devices2.isEmpty()) { devices.add(devices2); break; } } } // The second device used for sonification is the same as the device used by media strategy FALLTHROUGH_INTENDED; case STRATEGY_DTMF: case STRATEGY_ACCESSIBILITY: case STRATEGY_SONIFICATION_RESPECTFUL: case STRATEGY_REROUTING: case STRATEGY_MEDIA: { DeviceVector devices2; if (strategy != STRATEGY_SONIFICATION) { // no sonification on remote submix (e.g. WFD) sp<DeviceDescriptor> remoteSubmix; if ((remoteSubmix = availableOutputDevices.getDevice( AUDIO_DEVICE_OUT_REMOTE_SUBMIX, String8("0"), AUDIO_FORMAT_DEFAULT)) != nullptr) { devices2.add(remoteSubmix); } } if ((devices2.isEmpty()) && (getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) == AUDIO_POLICY_FORCE_SPEAKER)) { devices2 = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER); } if (devices2.isEmpty() && (getLastRemovableMediaDevices().size() > 0)) { if ((getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP)) { // Get the last connected device of wired and bluetooth a2dp devices2 = availableOutputDevices.getFirstDevicesFromTypes( getLastRemovableMediaDevices()); } else { // Get the last connected device of wired except bluetooth a2dp devices2 = availableOutputDevices.getFirstDevicesFromTypes( getLastRemovableMediaDevices(GROUP_WIRED)); } } if ((devices2.isEmpty()) && (strategy != STRATEGY_SONIFICATION)) { // no sonification on aux digital (e.g. HDMI) devices2 = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_AUX_DIGITAL); } if ((devices2.isEmpty()) && (getForceUse(AUDIO_POLICY_FORCE_FOR_DOCK) == AUDIO_POLICY_FORCE_ANALOG_DOCK)) { devices2 = availableOutputDevices.getDevicesFromType( AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET); } #ifdef SSTAR_CUSTOMIZED if ((devices2.isEmpty()) && (getForceUse(AUDIO_POLICY_FORCE_FOR_DOCK) == AUDIO_POLICY_FORCE_DIGITAL_DOCK)) { devices2 = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPDIF); } #endif /*SSTAR_CUSTOMIZED*/ if (devices2.isEmpty()) { devices2 = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER); } DeviceVector devices3; if (strategy == STRATEGY_MEDIA) { // ARC, SPDIF and AUX_LINE can co-exist with others. devices3 = availableOutputDevices.getDevicesFromTypes({ AUDIO_DEVICE_OUT_HDMI_ARC, AUDIO_DEVICE_OUT_HDMI_EARC, #ifndef SSTAR_CUSTOMIZED AUDIO_DEVICE_OUT_SPDIF, #endif /*SSTAR_CUSTOMIZED*/ AUDIO_DEVICE_OUT_AUX_LINE, }); } devices2.add(devices3); // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION or // STRATEGY_ENFORCED_AUDIBLE, AUDIO_DEVICE_NONE otherwise devices.add(devices2); // If hdmi system audio mode is on, remove speaker out of output list. if ((strategy == STRATEGY_MEDIA) && (getForceUse(AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO) == AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED)) { devices.remove(devices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER)); } bool mediaActiveLocally = outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_MUSIC), SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY) || outputs.isActiveLocally( toVolumeSource(AUDIO_STREAM_ACCESSIBILITY), SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY); bool ringActiveLocally = outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_RING), 0); // - for STRATEGY_SONIFICATION and ringtone active: // if SPEAKER was selected, and SPEAKER_SAFE is available, use SPEAKER_SAFE instead // - for STRATEGY_SONIFICATION_RESPECTFUL: // if no media is playing on the device, check for mandatory use of "safe" speaker // when media would have played on speaker, and the safe speaker path is available if (strategy == STRATEGY_SONIFICATION || ringActiveLocally || (strategy == STRATEGY_SONIFICATION_RESPECTFUL && !mediaActiveLocally)) { devices.replaceDevicesByType( AUDIO_DEVICE_OUT_SPEAKER, availableOutputDevices.getDevicesFromType( AUDIO_DEVICE_OUT_SPEAKER_SAFE)); } } break; case STRATEGY_CALL_ASSISTANT: devices = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_TELEPHONY_TX); break; case STRATEGY_NONE: // Happens when internal strategies are processed ("rerouting", "patch"...) break; default: ALOGW("%s unknown strategy: %d", __func__, strategy); break; } if (devices.isEmpty()) { ALOGV("%s no device found for strategy %d", __func__, strategy); sp<DeviceDescriptor> defaultOutputDevice = getApmObserver()->getDefaultOutputDevice(); if (defaultOutputDevice != nullptr) { devices.add(defaultOutputDevice); } ALOGE_IF(devices.isEmpty(), "%s no default device defined", __func__); } ALOGVV("%s strategy %d, device %s", __func__, strategy, dumpDeviceTypes(devices.types()).c_str()); return devices; }
例如:修改 spdif 的优先级高于蓝牙,即当选择 spdif 输出的时候,同时连接了蓝牙输出,优先从 spdif 输出。
修改参考示例如下:(audiopolicyenginecustom\src\Engine.cpp)
case STRATEGY_MEDIA: { DeviceVector devices2; if (strategy != STRATEGY_SONIFICATION) { // no sonification on remote submix (e.g. WFD) sp<DeviceDescriptor> remoteSubmix; if ((remoteSubmix = availableOutputDevices.getDevice( AUDIO_DEVICE_OUT_REMOTE_SUBMIX, String8("0"), AUDIO_FORMAT_DEFAULT)) != nullptr) { devices2.add(remoteSubmix); } } #ifdef SSTAR_CUSTOMIZED if ((devices2.isEmpty()) && (getForceUse(AUDIO_POLICY_FORCE_FOR_DOCK) == AUDIO_POLICY_FORCE_DIGITAL_DOCK)) { devices2 = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPDIF); } #endif /*SSTAR_CUSTOMIZED*/ if ((devices2.isEmpty()) && (getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) == AUDIO_POLICY_FORCE_SPEAKER)) { devices2 = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER); } if (devices2.isEmpty() && (getLastRemovableMediaDevices().size() > 0)) { if ((getForceUse(AUDIO_POLICY_FORCE_FOR_MEDIA) != AUDIO_POLICY_FORCE_NO_BT_A2DP)) { // Get the last connected device of wired and bluetooth a2dp devices2 = availableOutputDevices.getFirstDevicesFromTypes( getLastRemovableMediaDevices()); } else { // Get the last connected device of wired except bluetooth a2dp devices2 = availableOutputDevices.getFirstDevicesFromTypes( getLastRemovableMediaDevices(GROUP_WIRED)); } } if ((devices2.isEmpty()) && (strategy != STRATEGY_SONIFICATION)) { // no sonification on aux digital (e.g. HDMI) devices2 = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_AUX_DIGITAL); } if ((devices2.isEmpty()) && (getForceUse(AUDIO_POLICY_FORCE_FOR_DOCK) == AUDIO_POLICY_FORCE_ANALOG_DOCK)) { devices2 = availableOutputDevices.getDevicesFromType( AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET); } if (devices2.isEmpty()) { devices2 = availableOutputDevices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER); } DeviceVector devices3; if (strategy == STRATEGY_MEDIA) { // ARC, SPDIF and AUX_LINE can co-exist with others. devices3 = availableOutputDevices.getDevicesFromTypes({ AUDIO_DEVICE_OUT_HDMI_ARC, AUDIO_DEVICE_OUT_HDMI_EARC, #ifndef SSTAR_CUSTOMIZED AUDIO_DEVICE_OUT_SPDIF, #endif /*SSTAR_CUSTOMIZED*/ AUDIO_DEVICE_OUT_AUX_LINE, }); } devices2.add(devices3); // device is DEVICE_OUT_SPEAKER if we come from case STRATEGY_SONIFICATION or // STRATEGY_ENFORCED_AUDIBLE, AUDIO_DEVICE_NONE otherwise devices.add(devices2); // If hdmi system audio mode is on, remove speaker out of output list. if ((strategy == STRATEGY_MEDIA) && (getForceUse(AUDIO_POLICY_FORCE_FOR_HDMI_SYSTEM_AUDIO) == AUDIO_POLICY_FORCE_HDMI_SYSTEM_AUDIO_ENFORCED)) { devices.remove(devices.getDevicesFromType(AUDIO_DEVICE_OUT_SPEAKER)); } bool mediaActiveLocally = outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_MUSIC), SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY) || outputs.isActiveLocally( toVolumeSource(AUDIO_STREAM_ACCESSIBILITY), SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY); bool ringActiveLocally = outputs.isActiveLocally(toVolumeSource(AUDIO_STREAM_RING), 0); // - for STRATEGY_SONIFICATION and ringtone active: // if SPEAKER was selected, and SPEAKER_SAFE is available, use SPEAKER_SAFE instead // - for STRATEGY_SONIFICATION_RESPECTFUL: // if no media is playing on the device, check for mandatory use of "safe" speaker // when media would have played on speaker, and the safe speaker path is available if (strategy == STRATEGY_SONIFICATION || ringActiveLocally || (strategy == STRATEGY_SONIFICATION_RESPECTFUL && !mediaActiveLocally)) { devices.replaceDevicesByType( AUDIO_DEVICE_OUT_SPEAKER, availableOutputDevices.getDevicesFromType( AUDIO_DEVICE_OUT_SPEAKER_SAFE)); } } break;
- UI 策略部分由 SystemControl 模块(vendor\sigmastar\common\apps\SStarSystemControl)提供 setAudioOutputDevice 接口实现。
具体代码(SStarSystemControl\src\com\sstar\systemcontrol\service\SystemControlService.java)如下:
private int getOutputDeviceForUIDevice(int dev) { switch (dev) { case 0: return AudioSystem.DEVICE_OUT_SPEAKER; case 1: return AudioSystem.DEVICE_OUT_SPDIF; default: return AudioSystem.DEVICE_NONE; } } private void applyAudioOutputPolicy(int dev) { int audioDevice = getOutputDeviceForUIDevice(dev); switch (audioDevice) { case AudioSystem.DEVICE_OUT_SPDIF: AudioSystem.setForceUse(AudioSystem.FOR_DOCK, AudioSystem.FORCE_DIGITAL_DOCK); break; case AudioSystem.DEVICE_OUT_SPEAKER: default: AudioSystem.setForceUse(AudioSystem.FOR_DOCK, AudioSystem.FORCE_NONE); break; } } @Override public void setAudioOutputDevice(int dev) { Log.d(TAG, "setAudioOutputDevice---dev: " + dev); try { applyAudioOutputPolicy(dev); mSystemControl.setAudioOutputDevice(dev); } catch (Exception e) { e.printStackTrace(); } }
3.2 音量的修改¶
- 音量范围(最小、最大音量)修改
AOSP 参考目录:(/frameworks/av/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_stream_volumes.xml)
客制化目录:(device/sigmastar/pioneer5/audio_config/audio_policy_engine_stream_volumes.xml)
<volumeGroup> <name>music</name> <indexMin>0</indexMin> <indexMax>99</indexMax> <volume deviceCategory="DEVICE_CATEGORY_HEADSET" ref="DEFAULT_MEDIA_VOLUME_CURVE"/> <volume deviceCategory="DEVICE_CATEGORY_SPEAKER" ref="DEFAULT_DEVICE_CATEGORY_SPEAKER_VOLUME_CURVE"/> <volume deviceCategory="DEVICE_CATEGORY_EARPIECE" ref="DEFAULT_MEDIA_VOLUME_CURVE"/> <volume deviceCategory="DEVICE_CATEGORY_EXT_MEDIA" ref="DEFAULT_MEDIA_VOLUME_CURVE"/> <volume deviceCategory="DEVICE_CATEGORY_HEARING_AID" ref="DEFAULT_HEARING_AID_VOLUME_CURVE"/> </volumeGroup>
- 音量曲线修改
BT、USB 外接设备,走 AOSP 原生音量方案:
AOSP 参考目录:(/frameworks/av/services/audiopolicy/enginedefault/config/example/phone/audio_policy_engine_default_stream_volumes.xml)
客制化目录:(device/sigmastar/pioneer5/audio_config/audio_policy_engine_default_stream_volumes.xml)
<reference name="DEFAULT_DEVICE_CATEGORY_SPEAKER_VOLUME_CURVE"> <!-- Default is Speaker Media Volume Curve --> <point>1,-5800</point> <point>20,-4000</point> <point>60,-1700</point> <point>100,0</point> </reference>
speaker、headphone 走私有通路音量方案:
客制化目录:(device\sigmastar\pioneer5\factory\system_control_factory_configuration.xml)
... ... <audio_quality_nonlinearity_item> <nonlinearity_type type="SPEAKER"> <!-- the value corresponds to the audio quality nonlinearity point and value : --> <nonlinearity_index index="INDEX_0">0,0</nonlinearity_index> <nonlinearity_index index="INDEX_1">25,270</nonlinearity_index> <nonlinearity_index index="INDEX_2">50,380</nonlinearity_index> <nonlinearity_index index="INDEX_3">75,462</nonlinearity_index> <nonlinearity_index index="INDEX_4">100,510</nonlinearity_index> </nonlinearity_type> <nonlinearity_type type="HEADPHONE"> <nonlinearity_index index="INDEX_0">0,0</nonlinearity_index> <nonlinearity_index index="INDEX_1">25,270</nonlinearity_index> <nonlinearity_index index="INDEX_2">50,380</nonlinearity_index> <nonlinearity_index index="INDEX_3">75,462</nonlinearity_index> <nonlinearity_index index="INDEX_4">100,510</nonlinearity_index> </nonlinearity_type> </audio_quality_nonlinearity_item> ... ...
- 原生音量方案和私有通路音量方案判定逻辑
由 AudioPolicyManagerCustom 模块(vendor\sigmastar\common\audio\audiopolicymanagercustom)提供 AudioPolicyManagerCustom::checkAndSetHalVolume 接口实现。
具体代码(audiopolicymanagercustom\AudioPolicyManager.cpp)如下:
在 APP 设置音量到 audioPolicyManager 的时候,会调用接口 setStreamVolumeIndex,其中增加客制化逻辑 checkAndSetHalVolume 接口对接到 audio hal,把对应 device 的音量同步到 interface 的 DPGA gain 上。
void AudioPolicyManagerCustom::checkAndSetHalVolume(audio_stream_type_t stream, int index, audio_devices_t device) { ALOGD("%s: stream(%d), index(%d), device(0x%x, %s)", __FUNCTION__, stream, index, device, audio_device_to_string(device)); if (stream == AUDIO_STREAM_MUSIC) { String8 keyValuePairs; if ((device & AUDIO_DEVICE_OUT_SPEAKER) != 0) { keyValuePairs = String8::format("spk_volume=%d", index); } else if ((device & AUDIO_DEVICE_OUT_WIRED_HEADSET) != 0 || (device & AUDIO_DEVICE_OUT_WIRED_HEADPHONE) != 0) { keyValuePairs = String8::format("headphone_volume=%d", index); } else if ((device & AUDIO_DEVICE_OUT_SPDIF) != 0) { keyValuePairs = String8::format("spdif_volume=%d", index); } mpClientInterface->setParameters(AUDIO_IO_HANDLE_NONE, keyValuePairs); } } status_t AudioPolicyManagerCustom::setStreamVolumeIndex(audio_stream_type_t stream, int index, audio_devices_t device) { checkAndSetHalVolume(stream, index, device); return AudioPolicyManager::setStreamVolumeIndex(stream, index, device); }
同步 audio hal 后,会调用接口 computeVolume 获取同步给 audioFlinger 的音量缩放值大小。
float AudioPolicyManagerCustom::computeVolume(IVolumeCurves& curves, VolumeSource volumeSource, int index, const DeviceTypeSet& deviceTypes) { // Set full scale volume for stream music and stream system. if (volumeSource == toVolumeSource(AUDIO_STREAM_MUSIC) || volumeSource == toVolumeSource(AUDIO_STREAM_SYSTEM)) { if (Volume::getDeviceForVolume(deviceTypes) == AUDIO_DEVICE_OUT_SPEAKER || Volume::getDeviceForVolume(deviceTypes) == AUDIO_DEVICE_OUT_WIRED_HEADSET || Volume::getDeviceForVolume(deviceTypes) == AUDIO_DEVICE_OUT_WIRED_HEADPHONE || Volume::getDeviceForVolume(deviceTypes) == AUDIO_DEVICE_OUT_HDMI_ARC || Volume::getDeviceForVolume(deviceTypes) == AUDIO_DEVICE_OUT_SPDIF) { return 0.0f; } } return AudioPolicyManager::computeVolume(curves, volumeSource, index, deviceTypes); }
返回 return 0.0f; 表示 MUSIC 和 SYSTEM 的 stream 在播放到 speaker/headphone/spdif 设备的时候,在 audioFlinger 中 mixer thread 中不进行缩放。
3.3 音效的修改¶
客制化目录:(device/sigmastar/pioneer5/factory/system_control_factory_configuration.xml),
支持 adb push 到 /vendor/etc/ 目录替换后,重启进行 debug 调试
-
客制化音效支持共 7 段模块:
-
AEC(Acoustic Echo Cancellation)
-
ANR(Audio Noise Reduction)
-
EQ(Equalizer Config、High Pass Filter Config )
-
SONIC(multiple times playback speed,仅 tunnel mode 下生效)
-
AGC(Automatic Gain Control Config)
-
CUS1、CUS2(客制化扩展,暂未启用)
+_______ ______ _______ _______ ________ ________+ | ANR | -> | EQ | -> | SONIC | -> | AGC | -> | CUS1 | -> | CUS2 | +——————— —————— ——————— ——————— ———————— ————————+
-
支持音效单模块自定义处理顺序
<!-- Apc Sequence --> <!-- * NONE: 0 --> <!-- * AEC: 1 --> <!-- * ANR: 2 --> <!-- * EQ: 3 --> <!-- * SONIC: 4 --> <!-- * AGC: 5 --> <!-- * CUS1: 6 --> <!-- * CUS2: 7 --> <apcSequences> <apcSequence name="IN">1,0,0,0,0,0,0</apcSequence> <apcSequence name="OUT">2,3,4,5,0,0,0</apcSequence> </apcSequences>
-
支持单模块开关配置
<!-- Apc Switch Configuration Decalaration --> <!-- * apc buffer config --> <!-- * 1 = Enable --> <!-- * 0 = Disable --> <apcSwitchs> <apcSwitch> <name>IN</name> <aecEnabled>1</aecEnabled> <anrEnabled>0</anrEnabled> <eqEnabled>0</eqEnabled> <drEnabled>0</drEnabled> <vadEnabled>0</vadEnabled> <agcEnabled>0</agcEnabled> </apcSwitch> <apcSwitch> <name>OUT</name> <aecEnabled>0</aecEnabled> <anrEnabled>1</anrEnabled> <eqEnabled>1</eqEnabled> <drEnabled>0</drEnabled> <vadEnabled>0</vadEnabled> <agcEnabled>1</agcEnabled> </apcSwitch> </apcSwitchs>
4. 调试指引¶
4.1 kernel 层调试¶
使用 tinyalsa utils 中的 tinycap2、tinymix2、tinypcminfo2、tinyplay2 来 debug 调试确认信息。
音频输入输出通路操作参考示例:
-
capture:tinycap2 录音操作
console:/ # tinycap2 /data/mic.wav -D 0 -d 0 -c 2 -b 16 -r 16000 -p 512 -n 4 -t 15
-
playback:tinyplay2 播放操作
console:/ # tinyplay2 /sdcard/Music/8K_16bit_STEREO_60s.wav -D 0 -d 0 -p 512 -n 4
-
attach/detach:tinymix2 通路连通、断开控制,音量调整控制
console:/ # tinymix2 -D 0 contents Number of controls: 50 ctl type num name value ... ... 33 ENUM 1 CHANNEL_MODE_PLAYBACK > STEREO, DOUBLE_MONO, DOUBLE_LEFT, DOUBLE_RIGHT, EXCHANGE, ONLY_LEFT, ONLY_RIGHT, 34 ENUM 1 AI_MCH_01_SEL > AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 35 ENUM 1 AI_MCH_23_SEL > AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 36 ENUM 1 AI_MCH_45_SEL > AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 37 ENUM 1 AI_MCH_67_SEL > AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 38 ENUM 1 AI_MCH_89_SEL > AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 39 ENUM 1 AI_MCH_AB_SEL > AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 40 ENUM 1 AI_MCH_CD_SEL > AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 41 ENUM 1 AI_MCH_EF_SEL > AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 42 ENUM 1 AI_MCH_VIR_SEL > AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 43 ENUM 1 AO_VIR_MUX_SEL > VIR_DETACH, DAC0, SPDIF_LPCM_TX, SPDIF_NLPCM_TX, I2S_TXA, I2S_TXB, ECHO_0, 44 ENUM 1 HDMI_TX_SEL > AO_DETACH, DMA, DMA+SRC, 45 ENUM 1 DAC_SEL > AO_DETACH, DMA, DMA+SRC, 46 ENUM 1 ECHO_SEL > AO_DETACH, DMA, DMA+SRC, 47 ENUM 1 SPDIF_TX_SEL > AO_DETACH, LDMA, LDMA+SRC, NLDMA, 48 ENUM 1 I2S_TXA_SEL > AO_DETACH, DMA, DMA+SRC, 49 ENUM 1 I2S_TXB_SEL > AO_DETACH, DMA, DMA+SRC, console:/ # console:/ # tinymix2 -D 1 contents Number of controls: 17 ctl type num name value 0 ENUM 1 CHANNEL_MODE_PLAYBACK > STEREO, DOUBLE_MONO, DOUBLE_LEFT, DOUBLE_RIGHT, EXCHANGE, ONLY_LEFT, ONLY_RIGHT, 1 ENUM 1 AI_MCH_01_SEL > AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 2 ENUM 1 AI_MCH_23_SEL > AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 3 ENUM 1 AI_MCH_45_SEL > AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 4 ENUM 1 AI_MCH_67_SEL > AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 5 ENUM 1 AI_MCH_89_SEL > AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 6 ENUM 1 AI_MCH_AB_SEL > AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 7 ENUM 1 AI_MCH_CD_SEL > AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 8 ENUM 1 AI_MCH_EF_SEL > AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 9 ENUM 1 AI_MCH_VIR_SEL > AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 10 ENUM 1 AO_VIR_MUX_SEL > VIR_DETACH, DAC0, SPDIF_LPCM_TX, SPDIF_NLPCM_TX, I2S_TXA, I2S_TXB, ECHO_0, 11 ENUM 1 HDMI_TX_SEL > AO_DETACH, DMA, DMA+SRC, 12 ENUM 1 DAC_SEL > AO_DETACH, DMA, DMA+SRC, 13 ENUM 1 ECHO_SEL > AO_DETACH, DMA, DMA+SRC, 14 ENUM 1 SPDIF_TX_SEL > AO_DETACH, LDMA, LDMA+SRC, NLDMA, 15 ENUM 1 I2S_TXA_SEL > AO_DETACH, DMA, DMA+SRC, 16 ENUM 1 I2S_TXB_SEL > AO_DETACH, DMA, DMA+SRC, console:/ #
4.1.1 输入输出操作流程¶
-
播放过程中,动态调整输出设备流程:
+______________________ ____________ ____________ | attach (speaker) | -> | open | -> | playback | -> (开始动态切换) -> +—————————————————————— ———————————— ———————————— +______________________ ____________________ ____________ _____________________+ | detach (speaker) | -> | attach (spdif) | -> | close | -> | detach (spdif) | +—————————————————————— ———————————————————— ———————————— —————————————————————+ (因为是 attach/detach 不同 interface, 所以切换过程中会存在断音)
-
静置状态下,调整外接设备流程:
注意:有 close 的话,都需要重新 detach 再 attach ____________ -> | playback | - ____________ ____________ / ———————————— \ ____________ ____________ | attach | -> | open | -> | close | -> | detach | ———————————— ———————————— \ ____________ / ———————————— ———————————— -> | capture | - ————————————
4.1.2 连接(attach)通路¶
-
单独或同时连接 DAC + SPDIF 输出
连接操作: console:/ # tinymix2 -D 0 set 45 1 // R_DMA attach 到 DAC console:/ # tinymix2 -D 0 set 47 1 // R_DMA attach 到 SPDIF 查看状态: console:/ # tinymix2 -D 0 contents ... ... 45 ENUM 1 DAC_SEL AO_DETACH, > DMA, DMA+SRC, 46 ENUM 1 ECHO_SEL > AO_DETACH, DMA, DMA+SRC, 47 ENUM 1 SPDIF_TX_SEL AO_DETACH, > LDMA, LDMA+SRC, NLDMA, ... ...
-
单独或同时连接 AMIC + DMIC 输入
-
单声道 mono 的处理:
连接操作: console:/ # tinymix2 -D 0 set 34 1 // multichannel 的 0、1 声道 attach 到 AMIC 的 0、1
注意:capture 取数据的时候,即 mono 默认取 multichannel 的 0 声道
-
多声道:AMIC + DMIC 同时输入 8 声道数据
例如:AMIC 数据存放在 0、1 声道,DMIC 数据存放在 2、3、4、5、6、7 声道
连接操作: console:/ # tinymix2 -D 0 set 34 1 // multichannel 的 0、1 声道 attach 到 AMIC 的 0、1 console:/ # tinymix2 -D 0 set 35 3 // multichannel 的 2、3 声道 attach 到 DMIC 的 0、1 console:/ # tinymix2 -D 0 set 36 4 // multichannel 的 4、5 声道 attach 到 DMIC 的 2、3 console:/ # tinymix2 -D 0 set 37 5 // multichannel 的 6、7 声道 attach 到 DMIC 的 4、5 查看状态: console:/ # tinymix2 -D 0 contents ... ... 34 ENUM 1 AI_MCH_01_SEL AI_DETACH, > ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 35 ENUM 1 AI_MCH_23_SEL AI_DETACH, ADC_A, ADC_B, > DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 36 ENUM 1 AI_MCH_45_SEL AI_DETACH, ADC_A, ADC_B, DMIC_A_01, > DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 37 ENUM 1 AI_MCH_67_SEL AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, > DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 38 ENUM 1 AI_MCH_89_SEL > AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 39 ENUM 1 AI_MCH_AB_SEL > AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, 40 ENUM 1 AI_MCH_CD_SEL > AI_DETACH, ADC_A, ADC_B, DMIC_A_01, DMIC_A_23, DMIC_A_45, DMIC_A_67, ECHO_01, HDMI_01, I2S_RXA_0_1, I2S_RXA_2_3, I2S_RXA_4_5, I2S_RXA_6_7, I2S_RXB_0_1, ... ...
-
连接直通(passthrough)通路
例如:ADC 到 DAC 的直通
连接操作: console:/ # tinymix2 -D 0 set 42 1 console:/ # tinymix2 -D 0 set 43 1
- I2S 通路的连接
I2S_TX、I2S_RX 的 主从模式以及时钟信号等相关配置,请在 kernel 的 dtsi 文件中配置。
连接操作: console:/ # tinymix2 -D 0 set 48 1
4.1.3 断开(detach)通路¶
断开通路的时候,只需要把对应通路切换到 DETACH 状态(AI_DETACH / AO_DETACH / VIR_DETACH)即可,例如:detach 通路 DAC
console:/ # tinymix2 -D 0 set 45 0
4.1.4 Compress 的播放¶
compress 目前只支持在外接功放上进行播放
连接操作: console:/ # tinymix2 -D 0 set 47 3 // NLDMA attach 到 SPDIF 播放操作: console:/ # tinyplay2 demo.sdf -D 0 -d 0 -c 2 -r 48000 -p 512 -n 4
4.1.5 XRUN 的处理¶
XRUN 包含 playback 的 under_run 和 capture 的 over_run,当出现 XRUN 的时候,需要改变每次 read/write data 的大小,一般是加大即可。
例如:使用 buffer size 的方式 代替 period size 的方式读写。(tinyplay 中默认是每次写 period size 大小)
注意:设置的 period size 大小要是采样率的整数倍
4.2 audio hal 层调试¶
- 保存 hal 层收到的 MixerOutput PCM 数据,APP 播放的数据:
console:/ # setprop debug.vendor.audio.dump_out 1
生成目标文件目录:/data/vendor/audio/dump_out.pcm
- 保存 hal 层 MixerOutput PCM 数据经过客制化音效后,写入kernel的数据:
console:/ # setprop debug.vendor.audio.dump_out_aef 1
生成目标文件目录:/data/vendor/audio/dump_out_aef.pcm
- 保存 hal 层 PrimaryInput PCM 数据,从 kernel 取到的数据:
console:/ # setprop debug.vendor.audio.dump_in 1
生成目标文件目录:/data/vendor/audio/dump_in.pcm
- 保存 hal 层 PrimaryInput PCM 数据经过客制化音效后,返回给 APP 的数据:
console:/ # setprop debug.vendor.audio.dump_in_aef 1
生成目标文件目录:/data/vendor/audio/dump_in_aef.pcm
- 调整 hal 层打印 log 等级 :
console:/ # setprop log.tag.audio_hw_primary D
打印级别:V > D > I > W > E(级别越低,打印 log 越少, 默认 I)
各个节点数据
Playback: _____ ___________ __________ ___________ ______________ ________ | app | -> | framework | -> | dump_out | -> | audio hal | -> | dump_out_aef | -> | kernel | -> Speaker ————— ——————————— —————————— ——————————— —————————————— ———————— Capture: _____ ___________ _____________ ___________ _________ ________ | app | <- | framework | <- | dump_in_aef | <- | audio hal | <- | dump_in | <- | kernel | <- MIC ————— ——————————— ————————————— ——————————— ————————— ————————
4.3 原生 framework 音频服务状态查询¶
-
AudioFlinger 原生数据服务,提供不仅限于以下信息:
-
每一路 AudioTrack 播放状态,比如 ShareBuffer 大小,Write 计数,欠载(underrun)发生次数等。
-
Hal 层输出设备的配置信息,比如:采样率、位宽、声道数、BufferSize 等等。
-
Mixer 线程等其他信息。
命令:
console:/ # dumpsys media.audio_flinger
-
AudioPolicy 原生设备策略服务,提供不仅限于以下信息:
-
系统已经识别到的设备以及设备的能力集。
-
当前系统 ForceUse 设备情况。
命令:
console:/ # dumpsys media.audio_policy
-
Audio 音频类信息:
命令:
console:/ # dumpsys audio
4.4 SStarSystemControlService 服务调试¶
SStarSystemControlService 服务提供标准调试信息,可以查看到当前系统各种设备连接情况,用户选择的输出设备以及实际输出的设备等用户系统信息。
-
User select device: speaker(用户设置中选中的输出设备)
-
Active output device: speaker(系统当前的输出设备)
-
Current volume: 33(系统当前的音量大小)
-
Audio balance: 0(用户设置的平衡值)
-
Sound Mode: 0(用户设置的声音模式值)
-
Spdif Mode: 0(用户设置的SPDIF模式值)
-
其他状态查询:
BT Sco:off
BT A2dp:off
WiredHeadset:off
Microphone:off
Speakerphone:off
命令:
console:/ # dumpsys activity service SystemControlService