OpenHarmony南向之Audio
OpenHarmony南向之Audio
音频架构
Audio驱动框架基于HDF驱动框架实现,包含内核态(KHDF),和用户态(UHDF), 对北向提供音频HDI接口
音频框架图

驱动架构主要由以下几部分组成。
- HDI adapter:实现Audio HAL层驱动(HDI接口适配),给Audio服务(frameworks)提供所需的音频硬件驱动能力接口。包含 Audio Manager、Audio Adapter、Audio Control、Audio Capture、Audio Render等接口对象。
 - Audio Interface Lib:向下配合内核中的Audio Driver Model使用,实现音频硬件的控制、录音数据的读取、播放数据的写入,向上和上层的Audio HDI Adapter层进行对接。
 - ADM(Audio Driver Model):音频驱动框架模型,向上服务于多媒体音频子系统,向下统一接口适配各自的驱动代码。
 
主要代码目录
drivers/hdf_core/framework/model/audio及device/board/xxx/yyy/audio_drivers等: ADM相关驱动,KHDF部分drivers/peripheral/audio: audio HAL 实现,UHDF部分foundation/multimedia/audio_framework: audio framework 实现
调用流程

Audio Service
目录:foundation/multimedia/audio_framework/services/
client:
通过 Remote()->SendRequest,和服务端进行通信(IPC),binder
server:
server端向上通过IPC与client交互,向下与HAL交互
使用PulseAudio进行音频流管理foundation/multimedia/audio_framework/services/audio_service/client/src/audio_service_client.cpp
HAL
HAL简单架构

- alsa adapter(
drivers/peripheral/audio/supportlibs/alsa_adapter):
基于alsa lib(alsa用户态接口库),对alsa接口的封装,向下驱动基于alsa - adm adapter(
drivers/peripheral/audio/supportlibs/adm_adapter):
基于amate(ADM用户态接口库),对ADM接口的封装,向下驱动基于alsa - supportlib
屏蔽声卡访问控制的差异,在用户态实现一套符合 hdi-adapter 规范的访问控制 - hdi-passthrough
将声卡(片内声卡、usb 声卡、HDMI 声卡等)抽象成 adapter,每个 adapter 都包含 supportlibs 抽象的 audioRender 和 audiocapture,最后通过 audiomanager 管理 adapters。进一步将音频 HDI 接口规范化封装 - hdi-binder
HDF音频驱动框架最上层的封装,基于C/S的IPC机制 
所以,由此可以得到音频驱动适配的几种主流方案:
- 通过”adm adapter”对接自研ADM内核驱动,是目前社区主流方案
 - 通过”alsa lib”对接ASLA,是针对已支持ASLA产品的友好适配方案
 - 通过自己实现Vendor HAL来对接音频HDI接口,主要针对厂商有自己成熟的音频中间件的情况
 
目录结构
从下面代码目录结构可以很容易和上面的架构图一一对应
zdd@zdd-PC:~/WorkSpace/OHOS/oh-v3.2.2/drivers/peripheral/audio$ tree -L 2
.
├── audio.gni
├── BUILD.gn
├── bundle.json
├── config
├── hal
│   ├── hdi_binder
│   │   ├── proxy
│   │   └── server
│   ├── hdi_passthrough
│   └── pathselect
├── hdi_service
│   ├── binder
│   ├── BUILD.gn
│   ├── passthrough
│   ├── pathselect
│   └── supportlibs
├── interfaces
│   ├── 2.0
│   └── include
├── supportlibs
│   ├── adm_adapter
│   ├── alsa_adapter
│   ├── BUILD.gn
│   └── interfaces
└── test
    ├── BUILD.gn
    ├── fuzztest
    ├── resource
    ├── sample
    ├── systemtest
    └── unittest
HAL流程

ADM流程
ADM流程基本上都是基于驱动消息机制(dispatch)实现:drivers/peripheral/audio/supportlibs/adm_adapter/src/audio_interface_lib_common.c
下面看看几种基本的ADM流程:
ADM启动流程

- 系统启动时Audio模块的Platform、Codec、Dsp、Dai各个驱动首先被加载,各驱动从各自私有配置文件中获取配置信息,并将获取的配置信息保存到各驱动的Data数据结构中。
 - 各驱动模块调用ADM注册接口将自己添加到各驱动模块的链表中。
 - ADM模块读取hdf_audio_driver_0和hdf_audio_driver_1配置信息,加载各模块的具体设备。
 - ADM模块调用各模块的初始化函数对各模块设备进行初始化。
 - 将初始化成功的音频设备添加到cardManager链表。
 
HCS
以rk3568平台,结合上面的启动流程看看audio相关的hcs文件
device_info.hcs:
...
audio :: host {
            hostName = "audio_host";
            priority = 110;
            device_dai0 :: device {
                device0 :: deviceNode {
                    policy = 1;
                    priority = 50;
                    preload = 0;
                    permission = 0666;
                    moduleName = "DAI_RK3568";
                    serviceName = "dai_service";
                    deviceMatchAttr = "hdf_dai_driver";
                }
            }
            device_codec_0 :: device {
                device0 :: deviceNode {
                    policy = 1;
                    priority = 50;
                    preload = 0;
                    permission = 0666;
                    moduleName = "CODEC_RK809";
                    serviceName = "codec_service_0";
                    deviceMatchAttr = "hdf_codec_driver_0";
                }
            }
            device_codec_1 :: device {
                device0 :: deviceNode {
                    policy = 1;
                    priority = 50;
                    preload = 0;
                    permission = 0666;
                    moduleName = "CODEC_RK817";
                    serviceName = "codec_service_1";
                    deviceMatchAttr = "hdf_codec_driver_1";
                }
            }
            device_dsp :: device {
                device0 :: deviceNode {
                    policy = 1;
                    priority = 50;
                    preload = 0;
                    permission = 0666;
                    moduleName = "DSP_RK3568";
                    serviceName = "dsp_service_0";
                    deviceMatchAttr = "hdf_dsp_driver";
                }
            }
            device_dma :: device {
                device0 :: deviceNode {
                    policy = 1;
                    priority = 50;
                    preload = 0;
                    permission = 0666;
                    moduleName = "DMA_RK3568";
                    serviceName = "dma_service_0";
                    deviceMatchAttr = "hdf_dma_driver";
                }
            }
            device_audio :: device {
                device0 :: deviceNode {
                    policy = 2;
                    priority = 60;
                    preload = 0;
                    permission = 0666;
                    moduleName = "HDF_AUDIO";
                    deviceMatchAttr = "hdf_audio_driver_0";
                    serviceName = "hdf_audio_codec_primary_dev0";
                }
                device1 :: deviceNode {
                    policy = 2;
                    priority = 60;
                    preload = 0;
                    permission = 0666;
                    moduleName = "HDF_AUDIO";
                    deviceMatchAttr = "hdf_audio_driver_1";
                    serviceName = "hdf_audio_codec_primary_dev11";
                }
            }
            device_stream :: device {
                device0 :: deviceNode {
                    policy = 2;
                    priority = 80;
                    preload = 0;
                    permission = 0666;
                    moduleName = "HDF_AUDIO_STREAM";
                    serviceName = "hdf_audio_render";
                }
                device1 :: deviceNode {
                    policy = 2;
                    priority = 80;
                    preload = 0;
                    permission = 0666;
                    moduleName = "HDF_AUDIO_STREAM";
                    serviceName = "hdf_audio_capture";
                }
            }
            device_control :: device {
                device0 :: deviceNode {
                    policy = 2;
                    priority = 80;
                    preload = 0;
                    permission = 0666;
                    moduleName = "HDF_AUDIO_CONTROL";
                    serviceName = "hdf_audio_control";
                }
            }
            device_analog_headset :: device {
                device0 :: deviceNode {
                    policy = 1;
                    priority = 90;
                    preload = 0;
                    permission = 0666;
                    moduleName = "AUDIO_ANALOG_HEADSET";
                    serviceName = "analog_headset_service";
                    deviceMatchAttr = "analog_headset_attr";
                }
            }
        }
...
audio_config.hcs:
root {
    platform {
        template card_controller {
            match_attr = "";
            serviceName = "";
            codecName = "";
            platformName = "";
            cpuDaiName = "";
            codecDaiName = "";
            dspName = "";
            dspDaiName = "";
        }
        controller_0x120c1000 :: card_controller {
            match_attr = "hdf_audio_driver_0";
            serviceName = "hdf_audio_codec_primary_dev0";
            codecName = "codec_service_0";
            platformName = "dma_service_0";
            cpuDaiName = "dai_service";
            codecDaiName = "codec_dai";
            dspName = "dsp_service_0";
            dspDaiName = "dsp_dai";
        }
        controller_0x120c1001 :: card_controller {
            match_attr = "hdf_audio_driver_1";
            serviceName = "hdf_audio_codec_primary_dev11";
            codecName = "codec_service_1";
            platformName = "dma_service_0";
            cpuDaiName = "dai_service";
            codecDaiName = "rk817_dai";
            dspName = "dsp_service_0";
            dspDaiName = "dsp_dai";
        }
    }
}
codec_config.hcs
root {
    platform {
        template codec_controller {
            match_attr = "";
            serviceName = "";
            codecDaiName = "";
        }
        controller_0x120c1030 :: codec_controller {
            match_attr = "hdf_codec_driver_0";
            serviceName = "codec_service_0";
            codecDaiName = "codec_dai";
            regConfig {
                /* reg, value */
                initSeqConfig = [
                    0x13,    0xf4,
                    ...
                ];
                controlsConfig = [
                    /*array index, iface, mixer/mux, enable,*/
                    0,  2,  0,  1,
                    ...
                ];
                /* reg, rreg, shift, rshift, min, max, mask, invert, value */
                ctrlParamsSeqConfig = [
                    0x31,    0x32,    0,    0,    0x00,    0xFF,   0xFF,   1,    0x00, // DACL/R Playback Volume
                   ...
                ];
                /* reg, rreg, shift, rshift, min, max, mask, invert, value */
                daiParamsSeqConfig = [
                    0x45,    0x45,    0,     0,    0x0,   0xFF,    0xFF,   0,     0x0C, // PLL_PREDIV_BIT
                    ...
                ];
                ctrlSapmParamsSeqConfig = [
                    0x27,    0x27,    5,     5,    0x00,    0x1,    0x1,    1,    0x00,     //LPGA MIC  -- connect MIC1
                    ...
                ];
                /*
                 sapm
                 reg is 0xFFFF: component has no sapm register bit
                 sapmType, compNameIndex, reg, mask, shift, invert, kcontrolNews, kcontrolsNum
                */
                sapmComponent = [
                    10,      0,       0x18,       0x1,     7,     1,     0,     0,  //ADCL
                    ...
                ];
                /*array index, iface, mixer/mux, enable*/
                sapmConfig = [
                    0,     2,    0,    1,
                    ...
                ];
            }
        }
        controller_0x120c1031 :: codec_controller {
            match_attr = "hdf_codec_driver_1";
            serviceName = "codec_service_1";
            codecDaiName = "rk817_dai";
        }
    }
}
ADM播放流程

- 播放音频时,Interface Lib层通过播放流服务下发Render Open指令,Audio Stream Dispatch服务收到指令后分别调用各模块的函数接口对指令进行下发。
 - Interface Lib层通过控制服务下发通路选择指令,Control Dispatch控制服务收到指令后调用Dai模块接口设置通路。
 - Interface Lib层通过播放流服务下发硬件参数,Audio Stream Dispatch服务收到参数后分别调用各模块参数设置接口,对硬件参数进行设置。
 - Interface Lib层通过播放流服务下发播放启动指令,Audio Stream Dispatch服务收到指令后分别调用各模块启动接口,对各模块进行启动设置。
 - Interface Lib层通过播放流服务下发音频数据,Audio Stream Dispatch服务收到数据后调用Platform AudioPcmWrite接口将音频数据传给Dma。
 - Interface Lib层通过播放流服务下发播放停止指令,Audio Stream Dispatch服务收到指令后分别调用各模块停止接口,对各模块进行停止设置。
 - Interface Lib层通过播放流服务下发Render Close指令,Audio Stream Dispatch服务收到指令后调用Platform AudioRenderClose对已申请资源进行释放。
 
ADM控制流程

- 设置音量,首先Interface Lib层通过控制服务下发获取音量范围指令,Control Dispatch控制服务收到指令后进行解析,并调用Codec模块Get函数,获取可设置音量的范围。
 - Interface Lib层通过控制服务下发设置音量指令,Control Dispatch控制服务收到指令后进行解析,并调用Codec模块Set函数设置音量。
 
分布式音频组件
分布式音频是指多个设备之间音频外设跨设备协同使用的能力,如将设备A的音频通过设备B的Speaker进行播音,或者设备A使用设备B的Mic进行录音。
分布式音频不直接向应用提供接口,应用可以通过音频框架的接口来调用分布式音频能力,使用方式与本地音频一致。
概念说明
主控端(source) :分布式音频控制端设备,向被控端设备发送指令,实现在被控端设备上音频播放和录制的功能;
被控端(sink) :分布式音频被控制端设备,接收来自主控端设备的指令,使本地音频外设为主控端设备所用,用来播音或录音。

参考
- https://laval.csdn.net/64eef49d6ffa502025762163.html
 - https://blog.csdn.net/qq_37596943/article/details/128616800
 - https://docs.openharmony.cn/pages/v3.2/zh-cn/device-dev/driver/driver-peripherals-audio-des.md/
 - 分布式音频部件官方文档
 




 
