AudSys ========== 概述 -------------- AudSys 是一个管理音频外设的系统. 它提供了一种基于命令的调用方式, 操作用户创建的音频流. 抽象出3种音频模块: record 上行模块, playbk 下行模块以及ANC模块. **功能简介:** - 驱动管理 - 资源分配 - 时序控制 - 同步操作 - 杂音消除 - 转采样 - 频偏校准 - 啸叫检测 - 能量估计 - 饱和检测 - 增益控制 - 主动降噪 - 均衡器调整 - 语音检测等 模块功能特性 -------------- **record 模块支持以下功能:** - 支持 ADC PDM_rx I2S_rx VAD DUMP 等外设采样率配置, 频率率转换功能 - 支持 -95dB~95dB 数字音量调整, 支持 -6dB~-12dB 模拟 gain 调整 - 支持 Amic, Dmic 支持16bit和24bit位宽, 支持I2S_rx UAC_rx 16bit 24bit 位宽 - 支持 Amic, Dmic I2S_rx数据啸叫检测 - 支持 audio timer 定时触发 Amic, Dmic I2S_rx 数据启动, 精度 0.5us - 支持 Amic 和 Dmic 混合开启 **playbk 模块支持以下功能:** - 支持 DAC, PDM_tx, I2S_tx通过ASRC转采样,调频偏 - 支持 -95dB~95dB 数字音量调整, 支持-19dB~-3dB模拟gain调整 - 支持 DAC, PDM_tx, 16bit,24bit位宽, 支持I2S_tx UAC_tx 16bit 24bit 位宽 - 支持 DAC, PDM_tx, I2S_tx 数据啸叫检测 - 支持 DAC, PDM_tx, I2S_tx 能量估计和饱和检测 - 支持 audio timer 定时触发DAC, I2S_tx数据启动, 精度 0.5us - 支持 DAC spk PA(Power Amplifier)动态开关 - 支持 DAC spk DRE(Dynamic range enhancer)动态开关 - 支持 DAC, PDM_tx, I2S_tx 主辅两路数据硬件混合, 如 tone 和音乐混合 - 支持 DAC, PDM_tx, I2S_tx 混音主辅两路音量占比设置 - 支持 DAC, PDM_tx, I2S_tx 硬件EQ(均衡器)切换,每路数据支持28个 biquad 滤波器 **ANC 模块支持以下功能:** - 支持输入外设选择 Amic, Dmic 或混合Amic Dmic - 支持输出外设选择 DAC spk - 支持 -19dB~-3dB DAC spk 模拟 gain, 支持 -95dB~95dB 数字音量调整 - 支持 -6dB~-12dB Amic模拟 gain 固定调整 - 支持输入输出硬件数据啸叫检测 - 支持输入输出能量估计和饱和检测 - 支持输出 spk PA(Power Amplifier)动态开关 - 支持输出 spk DRE(Dynamic range enhancer)动态开关 - 支持灵活可配的 biquad 滤波器系数操控 - 支持无杂音的切换 ANC 滤波器系数 - 支持最短 200ms 左右的场景系数切换 **Audsys框图** - 以对象的方式操作 AudSys, 提供的3种音频流硬件资源, 使用简易. - 内部外设模块化, 方便定制和裁剪. - 不同产品的音频流控制和数据传输保持一致, 代码继承性强. - 增加新的音频外设, 不影响原有产品代码, 扩展容易. - 音频流控制和传输以命令方式, 可读性强. .. image:: ../../../_static/audsys_1.png :align: center .. centered:: AudSys包括接口层和设备层 资源依赖 -------------- - AUDIO_ADC - AUDIO_DAC - PDM - I2S - ANC - ASRC - AUDIO_INTERFACE - AUDIO_DUMP - DMA - USB_LIB AudSys用法流程 ----------------- **用法流程简介** - 初始化 :cpp:func:`audsys_init` - 创建音频流 :cpp:func:`audsys_record_create` :cpp:func:`audsys_playbk_create` :cpp:func:`audsys_anc_create` - 发送命令 - 销毁音频流 :cpp:func:`audsys_record_destroy` :cpp:func:`audsys_playbk_destroy` :cpp:func:`audsys_anc_destroy` - 注销 :cpp:func:`audsys_deinit` **AudSys 创建销毁接口** - audsys对外提供init,deinit和3个create和destroy接口. - init和deinit只初始化软件状态, 包括创建audsys task, 创建锁, 队列等全局变量.不直接操作寄存器. - 可多次创建销毁, 每次创建表示开启一个stream流, 如录音, 播放, 语音检测, ANC等stream. .. code-block:: c :linenos: WQ_RET audsys_record_create(audsys_record_info_t *info, audsys_record_obj_t **obj); WQ_RET audsys_record_destroy(audsys_record_obj_t *obj); WQ_RET audsys_playbk_create(audsys_playbk_info_t *info, audsys_playbk_obj_t **obj); WQ_RET audsys_playbk_destroy(audsys_playbk_obj_t *obj); WQ_RET audsys_anc_create(audsys_anc_info_t *info, audsys_anc_obj_t **obj); WQ_RET audsys_anc_destroy(audsys_anc_obj_t *obj); **AudSys 创建参数** - 每次创建传入资源信息和 object 指针的指针, 会获取 object 对应的外设信息和操作函数指针 - 用户通过 obj 下发命令和操作audio功能模块 .. code-block:: c :linenos: typedef struct audsys_record_obj { audsys_record_t *record; audsys_record_cmd_fn cmd_fn; } audsys_record_obj_t; typedef struct audsys_playbk_obj { audsys_playbk_t *playback; audsys_playbk_cmd_fn cmd_fn; } audsys_playbk_obj_t; typedef struct audsys_anc_obj { audsys_anc_t *anc; audsys_anc_cmd_fn cmd_fn; } audsys_anc_obj_t; **AudSys 命令枚举** - 以 recorder 为例, 用户通过传入命令 ID 操作功能对应的函数,命令 ID 如下 .. code-block:: c :linenos: enum { AUDSYS_RECORD_CMD_OPEN, AUDSYS_RECORD_CMD_CLOSE, AUDSYS_RECORD_CMD_START, AUDSYS_RECORD_CMD_STOP, AUDSYS_RECORD_CMD_GAIN, AUDSYS_RECORD_CMD_HOWL_DET, AUDSYS_RECORD_CMD_PM, AUDSYS_RECORD_CMD_VAD, AUDSYS_RECORD_CMD_DUMP, AUDSYS_RECORD_CMD_TIMER, AUDSYS_RECORD_CMD_PREPARE_DATA, AUDSYS_RECORD_CMD_GET_INFO, AUDSYS_RECORD_CMD_TYPE_MAX, }; typedef uint8_t audsys_record_cmd_t; **AudSys 命令函数** - 以 recorder 为例, 命令对应函数如下. .. code-block:: c :linenos: static audsys_record_cmd_ops_t g_record_cmd_ops_tbl[AUDSYS_RECORD_CMD_TYPE_MAX] = { [AUDSYS_RECORD_CMD_OPEN] = record_cmd_open, [AUDSYS_RECORD_CMD_CLOSE] = record_cmd_close, [AUDSYS_RECORD_CMD_START] = record_cmd_start, [AUDSYS_RECORD_CMD_STOP] = record_cmd_stop, [AUDSYS_RECORD_CMD_GAIN] = record_cmd_gain, [AUDSYS_RECORD_CMD_HOWL_DET] = record_cmd_howl_det, [AUDSYS_RECORD_CMD_PM] = record_cmd_power_monitor, [AUDSYS_RECORD_CMD_VAD] = record_cmd_vad, [AUDSYS_RECORD_CMD_DUMP] = record_cmd_dump, [AUDSYS_RECORD_CMD_TIMER] = record_cmd_timer, [AUDSYS_RECORD_CMD_PREPARE_DATA] = record_cmd_prepare_data, [AUDSYS_RECORD_CMD_GET_INFO] = record_cmd_get_info, }; **AudSys 命令参数** - 以 recorder 为例, 每条命令都要对应参数, 用户通过参数传递音频外设信息 .. code-block:: c :linenos: typedef union audsys_record_msg { record_cfg_t record_cfg; record_dump_cfg_t record_dump; record_gain_t record_gain; audsys_record_get_info_t record_info; record_data_t record_data; aud_howl_detect_cfg_t howl_detect; aud_power_monitor_cfg_t power_monitor; aud_timer_cfg_t aud_timer; } audsys_record_msg_u; **AudSys 注册外设** - 以 recorder 为例, stream创建时, 设备层会注册外设函数表,用户通过创建时传入的device type决定使用哪些外设. .. code-block:: c :linenos: aud_record_dev_ops_t *aud_dev_register_record_ops(void) { aud_record_dev_ops_t *ops = wq_heap_caps_malloc(sizeof(aud_record_dev_ops_t), MALLOC_CAP_NONE); ops->amic_ops = aud_adc_get_ops(); ops->dmic_ops = aud_pdm_get_rx_ops(); ops->i2s_ops = aud_i2s_get_rx_ops(); #if is_defined(CONFIG_AUDIO_UAC_RX_ENABLE) ops->uac_ops = aud_uac_get_rx_ops(); #endif ops->vad_ops = aud_vad_get_ops(); ops->dump_ops = aud_dump_get_ops(); ops->gain_ops = aud_gain_rx_get_ops(); #ifdef CONFIG_AUDIO_HOWL_DETECT_ENABLE ops->howl_det_ops = aud_howl_det_get_ops(); #endif ops->pm_ops = aud_pm_get_ops(); ops->timer_ops = aud_timer_get_ops(); return ops; } **音频功能模块宏控制使能** - 可通过配置宏, 控制device或功能模块是否使能和编译. .. code-block:: c :linenos: CONFIG_AUDIO_CLOCK_15_36M=y /* CONFIG_AUDIO_CLOCK_16M is not set */ /* CONFIG_AUDIO_CLOCK_32M is not set */ CONFIG_AUDIO_CLOCK=15360000 /* CONFIG_AUDIO_PDM_RX_ENABLE is not set */ /* CONFIG_AUDIO_PDM_TX_ENABLE is not set */ /* CONFIG_AUDIO_DAC_ENABLE is not set */ /* CONFIG_AUDIO_ANC_ENABLE is not set */ CONFIG_AUDIO_I2S_TX_ENABLE=y /* CONFIG_AUDIO_EQ_ENABLE is not set */ /* CONFIG_AUDIO_HOWL_DETECT_ENABLE is not set */ /* CONFIG_AUDIO_POWER_MONITOR_ENABLE is not set */ /* CONFIG_AUDIO_DRE_USER_CTL is not set */ /* CONFIG_LOOPBACK_ENABLE is not set */ CONFIG_AUDIO_UAC_RX_ENABLE=y **开I2S TX stream** - 以I2S TX stream为例介绍配置流程 .. code-block:: c :linenos: /** 该参数为外设固定参数,每次创建stream只需传入一次 */ static aud_dev_out_map_t g_playbk_i2s_map = { /** 选择外设类型,如I2S,PDM,ADC,DAC,USB... */ .dev_type = AUD_DEV_TYPE_I2S, /** 选择使用i2s port0或其他i2s模块 */ .port_id = 0, /** 设置音频通路个数,0:通道0, 1:通道1, 2:双通道,4:4通道,8:8通道 */ .out_chns = 2, /** 音频外设的固定属性参数 */ .param = i2s_out_param, }; /** 声明 i2s out stream 的对象指针,和参数变量 */ audsys_playbk_obj_t *playbk_i2s_obj; audsys_playbk_msg_u playbk_msg; g_playbk_i2s_info.map = &g_playbk_i2s_map; /** 创建i2s out 音频流,申请必要内存,音存储频信息 */ audsys_playbk_create(&g_playbk_i2s_info, &playbk_i2s_obj); /** 下发启动 i2s tx外设的必要信息 */ playbk_msg.playbk_cfg.sample_rate = 48000; playbk_msg.playbk_cfg.bit_width = AUDSYS_PLAYBK_BIT_WIDTH_24_L; playbk_msg.playbk_cfg.trigger_mode = AUDSYS_PLAYBK_TIMER_TRIGGER; playbk_msg.playbk_cfg.timer_id = 0; playbk_msg.playbk_cfg.path = AUDSYS_PLAYBK_PATH_MAIN; playbk_msg.playbk_cfg.done_cb = playbk_i2s_complete_callback; /** 通过命令下发启动参数,等待complete callback回调,启动完成 */ playbk_i2s_obj->cmd_fn(playbk_i2s_obj, AUDSYS_PLAYBK_CMD_OPEN, &playbk_msg); playbk_i2s_obj->cmd_fn(playbk_i2s_obj, AUDSYS_PLAYBK_CMD_START, &playbk_msg); /** 通过命令下发数据参数,上传dma数据信息,等待dma 传输完成中断 callback*/ g_playbk_i2s_obj->cmd_fn(g_playbk_i2s_obj, AUDSYS_PLAYBK_CMD_PREPARE_DATA, &playbk_msg); /** 通过命令下发关闭参数,等待complete callback回调,关闭完成 */ playbk_i2s_obj->cmd_fn(playbk_i2s_obj, AUDSYS_PLAYBK_CMD_STOP, &playbk_msg); playbk_i2s_obj->cmd_fn(playbk_i2s_obj, AUDSYS_PLAYBK_CMD_CLOSE, &playbk_msg); /** 销毁i2s out音频流,释放内存,恢复存储信息为初始值 */ audsys_record_destroy(playbk_i2s_obj); 参考示例 ----------- :: - examples/audio_sys_loopback_demo - examples/usb_demo/uac_speaker API 介绍 ----------- .. doxygenfile:: audsys.h .. doxygenfile:: audsys_playbk.h .. doxygenfile:: audsys_record.h .. doxygenfile:: audsys_anc.h