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 音量方案

  1. BT、USB 外接设备,走 AOSP 原生音量方案;speaker、headphone、spdif 走私有通路音量方案。

  2. 所有私有通路使用 mixer control 接口完成音量调整。

  3. 私有通路音量作用在 interface 相连接的 DPGA 上

    Playback:
     _________      ____________     |\               _______
    |  MIU    | -> |  RDMA 0    | -> |  - DPGA   ->  |  DAC  | -> Speaker
     —————————      ————————————     |/               ———————
    
    Capture:
     _________      ____________      _______            /|     ________
    |  MIU    | <- |  WDMA 0    | <- |  MUX  | <- DPGA -  | <- |  ADC   | <- AMIC
     —————————      ————————————      ———————            \|     ————————
    
  4. 支持私有通路音量调节的 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)
    ... ...
    
  5. 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
  1. 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:/ #
  1. 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 调试

  1. 客制化音效支持共 7 段模块:

  2. AEC(Acoustic Echo Cancellation)

  3. ANR(Audio Noise Reduction)

  4. EQ(Equalizer Config、High Pass Filter Config )

  5. SONIC(multiple times playback speed,仅 tunnel mode 下生效)

  6. AGC(Automatic Gain Control Config)

  7. CUS1、CUS2(客制化扩展,暂未启用)

        +_______      ______      _______      _______      ________      ________+
        |  ANR  | -> |  EQ  | -> | SONIC | -> |  AGC  | -> |  CUS1  | -> |  CUS2  |
        +———————      ——————      ———————      ———————      ————————      ————————+
    
  8. 支持音效单模块自定义处理顺序

    <!-- 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>
    
  9. 支持单模块开关配置

    <!-- 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 输入输出操作流程

  1. 播放过程中,动态调整输出设备流程:

    +______________________      ____________      ____________
    |  attach (speaker)    | -> |  open      | -> |  playback  | ->  (开始动态切换) ->
    +——————————————————————      ————————————      ————————————
    
    +______________________      ____________________      ____________        _____________________+
    |  detach (speaker)    | -> |  attach (spdif)    | -> |  close     |   -> |  detach (spdif)     |
    +——————————————————————      ————————————————————      ————————————        —————————————————————+
    
    (因为是 attach/detach 不同 interface, 所以切换过程中会存在断音)
    
  2. 静置状态下,调整外接设备流程:

    注意:有 close 的话,都需要重新 detach 再 attach
                                           ____________
                                       -> |  playback  | -
     ____________      ____________  /     ————————————    \     ____________      ____________
    |  attach    | -> |  open      |                         -> |  close     | -> |  detach    |
     ————————————      ————————————  \     ____________    /     ————————————      ————————————
                                       -> |  capture   | -
                                           ————————————
    

4.1.2 连接(attach)通路

  1. 单独或同时连接 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,
    ... ...
    
  2. 单独或同时连接 AMIC + DMIC 输入

  3. 单声道 mono 的处理:

        连接操作:
        console:/ # tinymix2 -D 0 set 34 1   // multichannel 的 0、1 声道 attach 到 AMIC 的 0、1
    

    注意:capture 取数据的时候,即 mono 默认取 multichannel 的 0 声道

  4. 多声道: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,
        ... ...
    
  5. 连接直通(passthrough)通路

例如:ADC 到 DAC 的直通

    连接操作:
    console:/ # tinymix2 -D 0 set 42 1
    console:/ # tinymix2 -D 0 set 43 1
  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 层调试

  1. 保存 hal 层收到的 MixerOutput PCM 数据,APP 播放的数据:
    console:/ # setprop debug.vendor.audio.dump_out 1
    

生成目标文件目录:/data/vendor/audio/dump_out.pcm

  1. 保存 hal 层 MixerOutput PCM 数据经过客制化音效后,写入kernel的数据:
    console:/ # setprop debug.vendor.audio.dump_out_aef 1
    

生成目标文件目录:/data/vendor/audio/dump_out_aef.pcm

  1. 保存 hal 层 PrimaryInput PCM 数据,从 kernel 取到的数据:
    console:/ # setprop debug.vendor.audio.dump_in 1
    

生成目标文件目录:/data/vendor/audio/dump_in.pcm

  1. 保存 hal 层 PrimaryInput PCM 数据经过客制化音效后,返回给 APP 的数据:
    console:/ # setprop debug.vendor.audio.dump_in_aef 1
    

生成目标文件目录:/data/vendor/audio/dump_in_aef.pcm

  1. 调整 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 服务提供标准调试信息,可以查看到当前系统各种设备连接情况,用户选择的输出设备以及实际输出的设备等用户系统信息。

  1. User select device: speaker(用户设置中选中的输出设备)

  2. Active output device: speaker(系统当前的输出设备)

  3. Current volume: 33(系统当前的音量大小)

  4. Audio balance: 0(用户设置的平衡值)

  5. Sound Mode: 0(用户设置的声音模式值)

  6. Spdif Mode: 0(用户设置的SPDIF模式值)

  7. 其他状态查询:

BT Sco:off

BT A2dp:off

WiredHeadset:off

Microphone:off

Speakerphone:off

命令:

    console:/ # dumpsys activity service SystemControlService