OpenHarmony南向之Audio

news/2024/7/21 11:13:56 标签: 移动开发, 鸿蒙开发, harmonyos, openharmony, 鸿蒙

音频架构

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/audiodevice/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机制

所以,由此可以得到音频驱动适配的几种主流方案:

  1. 通过”adm adapter”对接自研ADM内核驱动,是目前社区主流方案
  2. 通过”alsa lib”对接ASLA,是针对已支持ASLA产品的友好适配方案
  3. 通过自己实现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启动流程

  1. 系统启动时Audio模块的Platform、Codec、Dsp、Dai各个驱动首先被加载,各驱动从各自私有配置文件中获取配置信息,并将获取的配置信息保存到各驱动的Data数据结构中。
  2. 各驱动模块调用ADM注册接口将自己添加到各驱动模块的链表中。
  3. ADM模块读取hdf_audio_driver_0和hdf_audio_driver_1配置信息,加载各模块的具体设备。
  4. ADM模块调用各模块的初始化函数对各模块设备进行初始化。
  5. 将初始化成功的音频设备添加到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

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

Code

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播放流程

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

ADM控制流程

  1. 设置音量,首先Interface Lib层通过控制服务下发获取音量范围指令,Control Dispatch控制服务收到指令后进行解析,并调用Codec模块Get函数,获取可设置音量的范围。
  2. Interface Lib层通过控制服务下发设置音量指令,Control Dispatch控制服务收到指令后进行解析,并调用Codec模块Set函数设置音量。

分布式音频组件

分布式音频是指多个设备之间音频外设跨设备协同使用的能力,如将设备A的音频通过设备B的Speaker进行播音,或者设备A使用设备B的Mic进行录音。
分布式音频不直接向应用提供接口,应用可以通过音频框架的接口来调用分布式音频能力,使用方式与本地音频一致。

概念说明

主控端(source) :分布式音频控制端设备,向被控端设备发送指令,实现在被控端设备上音频播放和录制的功能;

被控端(sink) :分布式音频被控制端设备,接收来自主控端设备的指令,使本地音频外设为主控端设备所用,用来播音或录音。


为了能让大家更好的学习
鸿蒙 (Harmony OS) 开发技术,这边特意整理了《鸿蒙 (Harmony OS)开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

鸿蒙 (Harmony OS)开发学习手册》

入门必看:https://qr21.cn/FV7h05

  1. 应用开发导读(ArkTS)
  2. 应用开发导读(Java)

HarmonyOS 概念:https://qr21.cn/FV7h05

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全

如何快速入门:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. 构建第一个JS应用
  4. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……


http://www.niftyadmin.cn/n/5283070.html

相关文章

032 - STM32学习笔记 - TIM基本定时器(一) - 定时器基本知识

032 - STM32学习笔记 - TIM定时器(一) - 基本定时器知识 这节开始学习一下TIM定时器功能,从字面意思上理解,定时器的基本功能就是用来定时,与定时器相结合,可以实现一些周期性的数据发送、采集等功能&#…

.net core webapi 自定义异常过滤器

1.定义统一返回格式 namespace webapi;/// <summary> /// 统一数据响应格式 /// </summary> public class Results<T> {/// <summary>/// 自定义的响应码&#xff0c;可以和http响应码一致&#xff0c;也可以不一致/// </summary>public int Co…

【量化金融】证券投资学

韭菜的自我修养 第一章&#xff1a; 基本框架和概念1.1 大盘底部形成的技术条件1.2 牛市与熊市1.3 交易系统1.3.1 树懒型交易系统1.3.2 止损止损的4个技术 第二章&#xff1a;证券家族4兄弟2.1 债券&#xff08;1&#xff09;债券&#xff0c;是伟大的创新&#xff08;2&#x…

力扣:93. 复原 IP 地址(回溯)

题目&#xff1a; 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 . 分隔。 例如&#xff1a;"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址&#xff0c;但是…

Java研学-Servlet 基础

一 概述 1 介绍 Servlet&#xff08;Server Applet&#xff09;是Java Servlet的简称&#xff0c;称为小服务程序或服务连接器&#xff0c;用Java编写的服务器端程序&#xff0c;具有独立于平台和协议的特性&#xff0c;主要功能在于交互式地浏览和生成数据&#xff0c;生成动…

华为vrrp+mstp+ospf+dhcp+dhcp relay配置案例

1、左边是vlan 10主桥&#xff0c;右边是vlan 20的主桥&#xff0c;并且互为备桥 2、 vlan 10 vrrp网关默认用左边&#xff0c;vlan 20的vrrp 网关默认用右边&#xff0c;对应mstp生成树 3、两边都track检测&#xff0c;不通就把vrrp减掉60&#xff0c;这样就会自动切另一边了 …

超维空间S2无人机使用说明书——32、使用yolov7进行目标识别

引言&#xff1a;为了提高yolo识别的质量&#xff0c;提高了yolo的版本&#xff0c;改用yolov7进行物体识别&#xff0c;同时系统兼容了低版本的yolo&#xff0c;包括基于C的yolov3和yolov4&#xff0c;也有更高版本的yolov8。 简介&#xff0c;为了提高识别速度&#xff0c;系…

web前端之拖拽API、vue3实现图片上传拖拽排序、拖放、投掷、复制、若依、vuedraggable

MENU vue2html5原生dom原生JavaScript实现跨区域拖放vue2实现跨区域拖放vue2mousedown实现全屏拖动&#xff0c;全屏投掷vue3element-plusvuedraggable实现图片上传拖拽排序vue2transition-group实现拖动排序原生拖拽排序 vue2html5原生dom原生JavaScript实现跨区域拖放 关键代…