本文介绍珠海普林芯驰sp2cw3平台芯片bare的SDK使用,包括编译环境搭建、下载、驱动及应用开发等,使用SDK进行开发之前,请阅读本文。
sp2cw3系列是珠海普林芯驰(www.spacetouch.co)推出的通用音频处理SOC,采用CPU+NPU+uDSP三核异构框架。CPU使用RISC-V架构,支持单精度浮点运算,uDSP能够完成音频信号的特征提取及计算,通过NPU实现神经网络训练和学习,能够实现AI降噪、AEC、语音识别等音频算法应用。 同时内置高性能音频专用 codec,丰富的外设资源,能够应用于各种音频应用领域。

bare SDK提供了AI降噪、传统(MMSE)降噪、回声消除、啸叫抑制等多种算法,工程模版已经集成最新的算法库。开发人员只需要根据自己的需求,完成业务逻辑部分即可。
SDK根目录主控包括如下文件夹及内容:
| 文件夹 | 描述 |
|---|---|
| oss | 链接脚本、启动文件、寄存器封装等公共文件用,用户一般不需要改动,如需要改动,请联系原厂技术支持 |
| project | 该文件下放置了demo工程,用于用户的需求开发 |

project是SDK源码目录,开发需要进入该目录进行:
| 文件夹 | 描述 |
|---|---|
| sp2cw3_demo_for_iis_ai_denoise_new_tool_v2.23 | A2A和IIS集成的标准降噪参考工程 |
工程文件夹目录结构:
| 文件夹 | 描述 |
|---|---|
| algo | 算法静态库和算法接口调用逻辑代码 |
| board | 硬件平台、系统状态机 |
| codec | audio adc(录音),audio dac(播放) ,src(采样率转换)、 iis、usb录音等等相关配置代码 |
| image | 算法模型(.mod)、参数配置文件(.a)、提示音文件(.dat) |
| src | 用户业务逻辑代码相关 |
| system | 系统运行、算法执行调用文件,udsp、npu、DMA等 |
| debug | 编译过程中间文件 |
| output | 编译后输出的烧录文件(.bin) |
| makefile | 编译构建脚本,负责文件的依赖关系等等 |

导入降噪工程(工程名:sp2cw3_demo_for_iis_ai_denoise_new_tool_v2.23)后,首次编译工程时,需要在工程目录下右击"cfg.exe",选择[属性]-[兼容性],勾选“以管理员身份运行此程序”,点击确定后再进行编译。

导入降噪工程(工程名:sp2cw3_demo_for_iis_ai_denoise_new_tool_v2.23)后,在”\algo“目录下存放了一个makefile文件,该文件用于用户进行模型替换和添加。现有模型库如下:
导入降噪工程(工程名:sp2cw3_demo_for_iis_ai_denoise_new_tool_v2.23)后,在工程目录下存放了一个makefile文件,该文件用于用户进行模型替换和添加。现有模型库如下:
| 模型名称 | 描述 |
|---|---|
| AHS_DENOISE_16K_6MS_260322_2 | 16K采样率、6ms延时的啸叫抑制模型 |
| AHS_DENOISE_16K_6MS_260220 | 16K采样率、6ms延时的啸叫抑制模型 |
| AHS_DENOISE_32K_6MS_251029 | 32K采样率、6ms延时的啸叫抑制模型 |
| 48k_15ms_7_1_Algorithm | 48K采样率、15ms延时的7.1声道算法模型 |
| 32k_48ms_strongdenoise | 32K采样率、48ms延时的强降噪模型 |
| 16k_48ms_stronger_denoise | 16K采样率、48ms延时的强降噪模型 |
| 16k_48ms_lowpower_dualmic | 16K采样率、48ms延时的双麦降噪模型 |
如果用户需要更换模型,在"\algo\makefile"文件里有一个定义"SDK_MOD_SET",在该定义后面修改不同模型名称即可更换(现有模型名称如上所示),更换后需要“clear”工程并重新编译。
如果用户需要更换模型,在"makefile"文件里有一个定义"SDK_MOD_SET",在该定义后面修改不同模型名称即可更换(现有模型名称如上所示),更换后需要“clear”工程并重新编译。
SDK_MOD_SET :=AHS_DENOISE_16K_6MS_260322_2
如果用户需要新增模型,需要".a"和".mod"文件。具体操作步骤如下
(1)用户需要将新的".mod"文件复制到"\image\ahs_mod(或者denoise_mod和7_1文件夹下,3个文件夹分别对应啸叫抑制,降噪和7.1声道模型,以免混淆)"目录下,将".a"文件复制到"\algo\lib\ahs(或者denoise和7_1Algorithm文件夹下,3个文件夹分别对应啸叫抑制,降噪和7.1声道,以免混淆)"目录下,右键项目clear重新编译一下,保证文件正确添加。
(2)重新复制粘贴一份模型模板代码进行修改,模板如下:
ifeq ($(SDK_MOD_SET),32k_48ms_strongdenoise)
MOD_FILENAME := denoise_mod/32k_48ms_strong_denoise.mod
LIBS := -L"./algo/lib/denoise" -lalgo_denoise_32k_48ms
MACRO_ARGS ?= "DENOISE_FRAME_32k_48MS_DEFINE"
endif
需要修改的地方如下:
ifeq ($(SDK_MOD_SET),参数1) 中的“参数1”替换为用户自定义的宏名称,该名称为用户替换模型时需要修改的参数。例如:ifeq ($(SDK_MOD_SET),16k_48ms_denoise)
MOD_FILENAME := 参数2 中的“参数2” 替换为第一步添加的.mod模型文件路径。例如:MOD_FILENAME :=denoise_mod/16k_48ms_denoise.mod
LIBS :=参数3 中的”参数3“替换为第一步添加的.a文件,但要根据格式严格替换,具体为”-L"./algo/lib/.a文件所在的文件夹" -.a文件名称“。要注意.a文件的前缀”libalgo“需要修改为“lalgo”。例如:-L"./algo/lib/denoise" -lalgo_denoise_16k_48ms。
MACRO_ARGS ?= "DENOISE_FRAME_32k_48MS_DEFINE"用户可根据模型名称进行修改,例如:MACRO_ARGS ?= "DENOISE_FRAME_16k_48MS_DEFINE
(4)在"\algo\process"路径下存放了"algo_cfg.h"文件,参照文件中的定义进行配置。配置参数如下:
#ifdef DENOISE_FRAME_48k_32MS_1216_DEFINE //第二步中配置的“MACRO_ARGS ?= DENOISE_FRAME_32k_48MS_DEFINE"模型宏定义
#define AUDIO_CALLBACK_LEN (512)//采样点数(采样率x采样时间(模型延迟)/3)
#define ADC_SAMPLERATE 48000//ADC采样率
#define DAC_SAMPLERATE 48000//DAC采样率
#define ALGO_DENOISE_APP 1//开启降噪算法(保存开启),如果是其它模型,请参照示例选择其它定义,例如:ALGO_AHS_APP(啸叫抑制)
#endif
(3)修改完成后clear工程并重新编译即可。
参考示例:用户得到了一个"ahs_16k_48ms_260401.mod"的啸叫抑制模型文件和一个”libalgo_ahs_16k_48ms_260401.a“的链接文件。首先需要把”ahs_16k_48ms_260401.mod“文件存放到”\image\ahs_mod“路径下,把“libalgo_ahs_16k_48ms_260401.a”文件存放在”\algo\lib\ahs“路径下,clear工程重新编译确保添加成功。接着在makefile文件和algo_cfg.h文件里复制粘贴模板代码进行修改,修改完成后代码如下:
makefile:
SDK_MOD_SET:=16k_48ms_ahs
ifeq ($(SDK_MOD_SET),16k_48ms_ahs)
MOD_FILENAME := ahs_mod/16k_48ms_260401.mod
LIBS := -L"./algo/lib/ahs" -lalgo_ahs_16k_48ms_260401
MACRO_ARGS ?= "AHS_FRAME_16k_48MS_260401_DEFINE"
endif
algo_cfg.h:
#ifdef AHS_FRAME_16k_48MS_260401_DEFINE
#define AUDIO_CALLBACK_LEN (256)
#define ADC_SAMPLERATE 16000
#define DAC_SAMPLERATE 16000
#define ALGO_AHS_APP 1 (啸叫抑制)
#endif
最后clear工程重新编译即可添加成功。
| 函数名 | 位置 | 描述 |
|---|---|---|
| void user_init(void) | main.c | 用于上电后的外设初始化 |
| void user_2ms_handler(void) | main.c | 2ms执行周期的状态机回调函数,用户可以根据需求在这里调用业务逻辑代码 |
| void user_10ms_handler(void) | main.c | 10ms执行周期的状态机回调函数,用户可以根据需求在这里调用业务逻辑代码 |
| void user_100ms_handler(void) | main.c | 100ms执行周期的状态机回调函数,用户可以根据需求在这里调用业务逻辑代码 |
| __IRAM void tick_user_handler(void) | main.c | 500us执行周期的状态机回调函数,用户可以根据需求在这里调用业务逻辑代码 |
注释:在增加业务逻辑代码时候,应该评估其执行周期,尽可能不要频繁执行,为CPU跑语音算法预留足够的CPU算力。
音频相关代码文件存放在"\codec"路径下。
void audio_adc_init(void)
{
audio_adc_ch_cfg_typedef cfg;
cfg.sample_rate = ADC_SAMPLERATE;/*配置采样率*/
cfg.digit_volume = VOLUME_01X;/*配置数字音量*/
cfg.hw_pin = AMIC_CH0;/*配置ADC通道*/
cfg.amic.mode = codec_param.mic0_mic_mode;/*配置mic模式:差分或单端*/
cfg.amic.mic_gain = codec_param.mic0_pga_gain;/*配置adc模拟增益*/
cfg.amic.mix_gain = codec_param.mic0_mix_gain;/*配置adc混音增益*/
cfg.amic.vmic = VOLT_2o59;/*配置VMIC电压*/
hal_audio_adc_ch_cfg(&audio_adc, cfg);/*使能ADC通道*/
cfg.sample_rate = ADC_SAMPLERATE;
cfg.digit_volume = VOLUME_01X;
cfg.hw_pin = AMIC_CH1;
cfg.amic.mode = codec_param.mic1_mic_mode;
cfg.amic.mic_gain = codec_param.mic1_pga_gain;
cfg.amic.mix_gain = codec_param.mic1_mix_gain;
cfg.amic.vmic = VOLT_2o59;
hal_audio_adc_ch_cfg(&audio_adc, cfg);
#ifdef IIS_USE_OUTSIDE_MCLK
set_audio_adc_clk_source(CMU_Clock_EXT_DEV,IIS_MCLK_FREQ);/*设置ADC时钟源为IIS_MCLK时钟*/
#elif AUDIO_PLL_FREQ
set_audio_adc_clk_source(CMU_Clock_PLL_AUDIO,AUDIO_PLL_FREQ);/*设置ADC时钟源为音频专用时钟*/
#else
set_audio_adc_clk_source(CMU_Clock_OSC_XTAL,24000000);/*设置ADC时钟源为晶振时钟*/
#endif
audio_adc.init.data_width = WIDTH_24BIT;/*数据宽度*/
audio_adc.init.src_sel = AHB;
audio_adc.init.channel_id = CHN0 | CHN1;/*ADC通道对应MIC0和MIC1*/
audio_adc.dma_handle = &audio_dma;/*注册DMA句柄*/
hal_audio_init(&audio_adc);/*初始化ADC*/
adc_dma_init();/*初始化ADC的DMA*/
audio_adc_cmd(&audio_adc, ENABLE);/*使能ADC*/
if(!codec_param.mic0_enable)
{
hal_audio_adc_option(MIC_0,DISABLE);
}
if(!codec_param.mic1_enable)
{
hal_audio_adc_option(MIC_1,DISABLE);
}
}
主要通过2个结构体传入参数,结构体的赋值通过结构体枚举变量成员传入:
typedef struct _audio_adc_ch_cfg_typedef
{
sample_rate_type_def sample_rate; /*采样率*/
volume_type_def digit_volume; /*数字音量*/
microphone_type_def hw_pin; /*pin脚选择*/
audio_adc_dmic_typedef dmic; /*DMIC配置*/
audio_adc_amic_typedef amic; /*AMIC配置*/
} audio_adc_ch_cfg_typedef;
typedef struct _audio_adc_amic_typedef
{
amic_mode_type_def mode; /*输入方式*/
amic_mic_gain_type_def mic_gain; /*mic增益*/
amic_mix_gain_type_def mix_gain; /*mix增益*/
vmic_vol_type_def vmic; /*VMIC电压*/
} audio_adc_amic_typedef;
void audio_dac_init(void)
{
volatile __ALIGNED(4) static uint16_t dac_buff[2][DAC_FRAM_SIZE * 2];/*DAC数据数组*/
audio_dac_buff[0].addr = (uint8_t *)dac_buff[0];
audio_dac_buff[0].status = BUFF_FREE;
audio_dac_buff[1].addr = (uint8_t *)dac_buff[1];
audio_dac_buff[1].status = BUFF_FREE;
audio_dac.init.track = DAC_STEREO;/*DAC输出方式选择:单声道或立体声*/
audio_dac.init.sample_rate = DAC_SAMPLERATE;/*DAC输出采样率*/
audio_dac.init.outfrom = HEADPHONE;
#ifndef DAC_USE_SRC1
audio_dac.init.src_sel = AHB;
audio_dac.init.data_format = width_16bit_useful_16bit;/*位宽*/
#else
audio_dac.init.src_sel = SRC;
audio_dac.init.data_format = width_32bit_useful_low_16bit;
#endif
audio_dac.init.mix_source = MIX_SOURCE_DAC_DETACHED;
audio_dac.init.vol = codec_param.dac_hpl_volume;/*上位机设置DAC输出音量*/
audio_dac.init.eq_type = EQ_NORMAL_TYPE;
audio_dac.init.inverse = codec_param.dac_output_inverse;
#ifdef IIS_USE_OUTSIDE_MCLK
set_audio_dac_clk_source(CMU_Clock_EXT_DEV,IIS_MCLK_FREQ);
#elif AUDIO_PLL_FREQ
set_audio_dac_clk_source(CMU_Clock_PLL_AUDIO,AUDIO_PLL_FREQ);
#else
set_audio_dac_clk_source(CMU_Clock_OSC_XTAL,24576000);
#endif
audio_dac.dma_handle = &dac_dma;
hal_audio_dac_init(&audio_dac);/*DAC初始化*/
audio_dac_EQ_init();/*EQ初始化*/
audio_dac_drc_init();/*DRC初始化*/
dac_dma_cfg();/*DAC_DMA配置*/
audio_dac_cmd(&audio_dac, ENABLE);/*DAC使能*/
audio_dac_option_cmd(DAC_HPL,codec_param.dac_enable & 0X01);
audio_dac_option_cmd(DAC_HPR,(codec_param.dac_enable>>1) & 0X01);
dac_set_hpl_volume(codec_param.dac_hpl_volume);/*设置左声道音量*/
dac_set_hpr_volume(codec_param.dac_hpr_volume);/*设置右声道音量*/
}
主要通过结构体传入参数,结构体的赋值通过结构体枚举变量成员传入:
typedef struct dac_init_typedef
{
adc_track_typedef track; /*声道数*/
dac_sample_rate_typedef sample_rate; /*采样率*/
dac_outfrom_typedef outfrom; /*输出类型*/
src_sel_type_def src_sel; /*AHB或SRC*/
dac_data_format_typedef data_format; /*数据对齐格式*/
dac_mix_sorce_def mix_source; /*mix设置*/
uint32_t vol; /*音量设置*/
eq_param_class_type eq_type; /*EQ类型*/
dac_param_inverse_type inverse; /*输出为差分或正常*/
} dac_init_typedef;
/**
* IIS配置函数
* @param: none
* @return: none.
*/
void audio_iis_init(void)
{
asi_pins_type_t pins; /* IIS引脚配置结构体 */
asi_config_type_t asi_config_struct; /* IIS核心配置结构体 */
asi_control_type_t asi_control_struct; /* IIS控制结构体 */
asi_interrupt_enable_type_t enable_type; /* IIS中断使能结构体 */
pins.sck = IIS_SCK_PIN;
pins.ws = IIS_WS_PIN;
pins.sdo = IIS_SDO_PIN;
pins.sdi = IIS_SDI_PIN;
hal_asi_pins_init(&pins);
#ifdef IIS_USE_OUTSIDE_MCLK
asi_config_struct.clk_source = ASI_CLK_EXT_CLK; /* 时钟源选择外部MCLK */
asi_config_struct.clk_rate = IIS_MCLK_FREQ; /* 外部MCLK频率(由宏定义指定) */
#else
#ifdef AUDIO_PLL_FREQ
asi_config_struct.clk_source = ASI_CLK_PLL_AUDIO; /* 优先使用音频专用PLL时钟 */
asi_config_struct.clk_rate = AUDIO_PLL_FREQ; /* PLL频率 */
#else
asi_config_struct.clk_source = ASI_CLK_OSC_XTAL; /* 未配置PLL时,使用晶振时钟(默认24MHz) */
asi_config_struct.clk_rate = 24000000; /* 晶振频率24MHz */
#endif
#endif
#ifdef IIS_OUTPUT_MCLK
dac_chip_mclk_init(IIS_MCLK_OUTPUT_PIN, IIS_MCLK_OUTPUT_FREQ, ENABLE); /* 初始化MCLK输出引脚、频率,并使能输出 */
#endif
asi_config_struct.samplerate = dual_denoise_param.iis_smplerate; /* 采样率 */
asi_config_struct.sample_bit = ASI_SAMPLE_BIT_16BITS; /* 采样位宽:16bit */
asi_config_struct.data_format = ASI_DAT_FMT_LOW_16BITS; /* 数据格式:低16位有效 */
asi_config_struct.data_mode = ASI_I2S_STANDARD; /* 工作模式:标准I2S协议 */
asi_config_struct.mode = ASI_MASTER_MODE; /* 主模式 */
// asi_config_struct.mode = ASI_SLAVE_MODE; /* 从模式 */
asi_config_struct.chn_nums = ASI_TWO_CHANNEL; /* 声道数:双声道 */
// asi_config_struct.chn_width = ASI_CHANNEL_WIDTH_32BITS; /* 通道宽度:32bit */
asi_config_struct.chn_width = ASI_CHANNEL_WIDTH_16BITS; /* 通道宽度:16bit */
asi_control_struct.tx_enable = ASI_TX_ENABLE; /* 使能IIS发送功能 */
asi_control_struct.rx_enable = ASI_RX_ENABLE; /* 使能IIS接收功能 */
#ifdef ENABLE_IIS_OUT
#ifdef IIS_TX_USE_SRC
asi_control_struct.tx_stc_t = ASI_TX_SRC; /* 启用SRC */
#else
asi_control_struct.tx_dma_enable = ASI_TX_DMA_ENABLE;/* 禁用SRC,启用TX DMA传输*/
asi_control_struct.tx_stc_t = ASI_TX_AHB; /* TX数据通过AHB总线传输 */
#endif
#else
asi_control_struct.tx_dma_enable = ASI_TX_DMA_DISABLE;/* 禁用IIS发送功能,关闭TX DMA */
asi_control_struct.tx_stc_t = ASI_TX_AHB;
#endif
#ifdef ENABLE_IIS_IN
#ifdef IIS_RX_USE_SRC
asi_control_struct.rx_stc_t = ASI_RX_SRC; /* 启用SRC(采样率转换),RX数据经SRC后传输 */
#else
asi_control_struct.rx_dma_enable = ASI_RX_DMA_ENABLE; /* 禁用SRC,启用RX DMA传输 */
asi_control_struct.rx_stc_t = ASI_RX_AHB; /* RX数据通过AHB总线传输 */
#endif
#else
asi_control_struct.rx_dma_enable = ASI_RX_DMA_DISABLE; /* 禁用IIS接收功能,关闭RX DMA */
asi_control_struct.rx_stc_t = ASI_RX_AHB;
#endif
asi_control_struct.slot_enable = ASI_2_CHANNEL_ENABLE; /* 启用双声道时隙 */
asi_control_struct.io_sta = ASI_IO_STA_HIGH_IMPEDANCE; /* IIS引脚状态配置为高阻态 */
enable_type.asi_rx_fifo_of_enable = ASI_DISABLE; /* 禁用RX FIFO溢出中断 */
enable_type.asi_rx_fifo_uf_enable = ASI_DISABLE; /* 禁用RX FIFO下溢中断 */
enable_type.asi_tx_fifo_of_enable = ASI_DISABLE; /* 禁用TX FIFO溢出中断 */
enable_type.asi_tx_fifo_uf_enable = ASI_DISABLE; /* 禁用TX FIFO下溢中断 */
enable_type.asi_rx_irq_enable = ASI_DISABLE; /* 禁用RX总中断 */
enable_type.asi_tx_irq_enable = ASI_DISABLE; /* 禁用TX总中断 */
if((asi_control_struct.tx_stc_t == ASI_TX_AHB)&&(asi_control_struct.tx_dma_enable == ASI_TX_DMA_ENABLE)&&(dual_denoise_param.iis_tx_function == ENABLE))
{
iis_tx_dma_cfg();
}
if((asi_control_struct.rx_stc_t == ASI_RX_AHB)&&(asi_control_struct.rx_dma_enable == ASI_RX_DMA_ENABLE)&&(dual_denoise_param.iis_rx_function == ENABLE))
{
iis_rx_dma_cfg();
}
hal_asi_init(ASI0, &asi_config_struct, (asi_pins_type_t *)&pins, &asi_control_struct, &enable_type); /* 初始化IIS外设 */
}

/**
* 音频算法初始化
* @param: none
* @return: none.
*/
void denoise_init(void)
{
#if NOISE_GATE_ENABLE == 1
noise_gate_cfg();/*静噪门初始化*/
#endif
#ifdef ENABLE_DRC
drc_init();
#endif
static uint32_t algo_heap[ALGO_HEAP_SIZE/4];/*算法内存堆初始化*/
algo_dma_init();/*算法用DMA初始化*/
//配置mod地址
#if 0
extern int8_t __mod_file_addr;
plxc_set_model_addr(((((uint32_t)&__mod_file_addr)&0x00ffffff)|0x4c000000));// set ai denoise mod addr
#else
plxc_set_model_addr((uint32_t)(FW_HEADER->BIN[1].ADDR + 0x4000000));// set ai denoise mod addr
#endif
//算法初始化
plxc_algo_heap_init(algo_heap,sizeof(algo_heap));
plxc_algo_init();//init ai denoise algo
#ifdef ALGO_DENOISE
plxc_set_process_level(algo_ai_denoise_param.denoise_level,ALGO_DENOISE); //set ai denoise level
plxc_set_denoise_dualmic_weight((float)algo_ai_denoise_param.dualmic_weight/100.00);/*双麦降噪权重配置*/
#endif
#ifdef ALGO_AHS_APP
plxc_set_process_level((algo_ai_ahs_param.enc_level*2)|(1<<15),ALGO_AHS);//降噪
plxc_set_process_level((algo_ai_ahs_param.ahs_level*2)|(0<<15),ALGO_AHS);//啸叫抑制
plxc_set_process_level(algo_ai_ahs_param.aec_level|(1<<14),ALGO_AHS);//AEC
ahs_set_freq_cpst(algo_ai_ahs_param.freq_level);
#endif
plxc_set_audio_callback(get_audio);//降噪算法处理完后后回调函数
#ifdef ALGO_TYPE_7_1_RITHM //7.1声道音效
audioDrax_cfg();
#endif
#if defined(ENABLE_IIS_IN) || defined(ENABLE_IIS_OUT)
audio_iis_init();//IIS_init
#endif
#ifdef ENABLE_IIS_IN
#ifdef IIS_RX_USE_SRC
src0_init();//IIS->SRC->MEM
#endif
#endif
#ifdef ENABLE_IIS_OUT
#ifdef IIS_TX_USE_SRC
src1_init();//MEM->SRC->IIS
#endif
#endif
#ifdef ENABLE_AUDIO_DAC
audio_dac_init();//init dac
#endif
#if defined(ENABLE_IIS_IN) && !defined(ENABLE_AUDIO_ADC)
// 这里只走 IIS 麦克风输入,才初始化iis_rx,否则需要在adc回调中开启iis_rx,保证数据同步
audio_iis_rx_ctrl(ENABLE);
#endif
#ifdef ENABLE_AUDIO_ADC
audio_adc_init();//init adc
#endif
}
/**
* 向算法输入原始音频数据
*
* @param: audio:原始音频数据的缓存地址
* num_samples:音频数据的点数
(uint32_t)audio_buffer[0]:主通道
(uint32_t)audio_buffer[1]:参考通道
* @return: none.
*/
void push_primitive_audio_data(int16_t *audio, uint32_t num_samples)
{
...
if ((gAudioPrimitive != (void *)0) && (gAudioPrimitive->audioListener != (void *)0))
{
gAudioPrimitive->audioListener(0, (uint32_t)audio_buffer[0], (uint32_t)audio_buffer[1]);
}
...
}
/**
* 音频算法计算完成后回调函数,从该函数获取数据
* @param: audio0:音频数据0buff地址
* audio1:音频数据1buff地址
* num_samples:音频数据的点数
* @return: none.
*/
void get_audio(int16_t *audio0, int16_t *audio1, uint32_t num_samples)
{
uint16_t temp_buff[AUDIO_CALLBACK_LEN * 2];
...
for (uint16_t i = 0; i < num_samples; i++)/*输出1路降噪前,1路降噪后的数据*/
{
temp_buff[2 * i + 0] = audio1[i];
temp_buff[2 * i + 1] = audio0[i];
}
...
src1_write_data((uint8_t *)temp_buff, AUDIO_CALLBACK_LEN);//将降噪后的数据,通过SRC后由DAC播出
}
SDK 提供多种宏定义配置,分别对应 信号通路、时钟 / 引脚、音频参数,集中在 3 个核心文件中,修改后即可快速适配场景:
在 SDK下的algo目录下提供了”analog_iis_option.h“文件,通过修改该配置文件下的宏定义,即可配置模拟进-模拟出、I2S进-I2S出、模拟进-I2S出、I2S进-模拟出等多种音频信号通路和降噪模型,用户可根据使用场景和需求自行选择。
| 宏定义 | 功能说明 |
|---|---|
| ENABLE_AUDIO_ADC | 使能数据从ADC输入 |
| ENABLE_IIS_IN | 使能数据从I2S输入 |
| ENABLE_AUDIO_DAC | 使能数据从DAC输出 |
| ENABLE_IIS_OUT | 使能数据从I2S输出 |
| DENOISE_ADC | 启用ADC数据源的降噪处理 |
| DENOISE_IIS_IN | 启用IIS输入的降噪处理 |
| DENOISE_ADC_IIS_IN | 同时启用ADC和IIS输入降噪 |
在 SDK下的”board/include/“目录下提供了”board.h“文件,该文件包括了大部分引脚定义宏和功能定义宏,通过修改该配置文件下的宏定义,即可配置I2S的时钟源和I2S的功能引脚。
| 宏定义 | 功能说明 |
|---|---|
| IIS_USE_OUTSIDE_MCLK | 使能使用外部设备提供的主时钟 |
| AUDIO_PLL_FREQ | 使能使用内部 PLL 生成的核心时钟 |
| IIS_OUTPUT_MCLK | 使能芯片向外部设备输出主时钟 |
| IIS_SCK_PIN | IIS时钟引脚 |
| IIS_WS_PIN | IIS声道选择引脚 |
| IIS_SDO_PIN | IIS数据输出引脚 |
| IIS_SDI_PIN | IIS数据输入引脚 |
在 SDK下的codec目录下提供了”codec_param.c“文件,该文件定义了一个名为"ENABLE_PARAM_SET_CODE"的宏,使能这个宏定义,即可使用代码进行配置ADC、DAC、I2S以及算法的相关参数(注:代码配置会覆盖上位机的配置)。
| 宏定义 | 功能说明 |
|---|---|
| ENABLE_PARAM_SET_CODE | 通过该宏开启和关闭代码配置codec和算法参数 |
下面将进行"使用啸叫抑制模型,模拟进(mic->adc),I2S出、采样率16k、数据宽度16位、双声道,主时钟频率49152000HZ“的主机通路和"I2S进,模拟出、采样率16k、数据宽度16位、双声道,主时钟频率49152000HZ“的从机配置示例操作。
1、主机配置
(1)在头文件“analog_iis_option.h”中打开"ENABLE_AUDIO_ADC 、 ENABLE_IIS_OUT " 这两个宏。
(2)在“board.h”中屏蔽掉”** IIS_USE_OUTSIDE_MCLK**"和" IIS_OUTPUT_MCLK"这两个宏, 使用PLL音频时钟"AUDIO_PLL_FREQ 49152000" 。
(3)在“board.h”中配置IIS引脚。
(4)在SDK的codec文件夹下找到"codec_iis.c"文件,对函数”audio_iis_init "中的部分参数进行配置,主要配置如下:
asi_config_struct.samplerate = codec_param.iis_smplerate; // 1. 采样率配置:通过上位机或代码进行配置
asi_config_struct.sample_bit = ASI_SAMPLE_BIT_16BITS;// 2. 采样位宽:配置为16bit
asi_config_struct.data_format = ASI_DAT_FMT_LOW_16BITS;// 3. 数据格式:低16位有效
asi_config_struct.data_mode = ASI_I2S_STANDARD;// 4. 工作协议:标准I2S协议
asi_config_struct.mode = ASI_MASTER_MODE;// 5. 主从模式:配置为主模式
asi_config_struct.chn_nums = ASI_TWO_CHANNEL;// 6. 声道数:双声道
asi_config_struct.chn_width = ASI_CHANNEL_WIDTH_16BITS;// 7. 通道宽度:16bit(与采样位宽、数据格式保持一致)
asi_control_struct.tx_enable = ASI_TX_ENABLE;// 8. 发送使能:启用IIS发送功能
asi_control_struct.rx_enable = ASI_RX_DISABLE;// 9. 接收使能:禁用IIS接收功能
(5)根据2.2.2和2.2.3章节的模型更换方法替换需要更新的模型。
(6)在工程代码中配置完成后,需要在上位机中进行配置,具体步骤如下:
1.编译工程后,在上位机导入当前工程的"ext.a"文件,选择左上角新建一个配置文件,根据工程具体配置选择需要更新的功能配置(例如从A2A更换到A2IIS,模型从降噪模型更换到啸叫抑制模型,就需要在新建的算法通路中选择“模拟输入_IIS_TX输出”,更换算法选择为“啸叫抑制参数”,点击确定,然后在上位机配置具体的codec参数)。
2.在设置好各个参数后,点击上位机左上角保存选项,替换掉工程里旧的ext.a文件。然后右击工程,选择“clear”清空一下工程,再重新编译工程即可完成修改。修改完成后,可以连接设备,在上位机重新导入ext.a文件,回读芯片参数确保修改成功。


2、从机配置
(1)在头文件“analog_iis_option.h”中打开"define ENABLE_IIS_IN 、 define ENABLE_AUDIO_DAC" 这两个宏。
(2)在“board.h”中屏蔽掉”define IIS_USE_OUTSIDE_MCLK"和"define IIS_OUTPUT_MCLK"这两个宏, 使用PLL音频时钟 #define AUDIO_PLL_FREQ 49152000。
(3)在“board.h”中配置IIS引脚。
(4)在SDK的codec文件夹下找到"codec_iis.c"文件,对函数”audio_iis_init "中的部分参数进行配置,主要配置如下:
asi_config_struct.samplerate = dual_denoise_param.iis_smplerate; // 1. 采样率配置:通过上位机或代码进行配置
asi_config_struct.sample_bit = ASI_SAMPLE_BIT_16BITS;// 2. 采样位宽:配置为16bit
asi_config_struct.data_format = ASI_DAT_FMT_LOW_16BITS;// 3. 数据格式:低16位有效
asi_config_struct.data_mode = ASI_I2S_STANDARD;// 4. 工作协议:标准I2S协议
asi_config_struct.mode = ASI_SLAVE_MODE;// 5. 主从模式:配置为从模式
asi_config_struct.chn_nums = ASI_TWO_CHANNEL;// 6. 声道数:双声道
asi_config_struct.chn_width = ASI_CHANNEL_WIDTH_16BITS;// 7. 通道宽度:16bit
asi_control_struct.tx_enable = ASI_TX_DISABLE;// 8. 发送使能:禁用IIS发送功能
asi_control_struct.rx_enable = ASI_RX_ENABLE;// 9. 接收使能:启用IIS接收功能
(5)上位机配置方法和主机相同。
配置完成,将主机的SCK、WS引脚分别与从机相连,主机的SDO引脚连接从机的SDI引脚即可。
硬件通路大致如下:

在 SDK下的algo目录下提供了”drc、noise_gate、soft_eq和soft_src“文件夹,里面提供了对应软件算法的源码。同时algo目录下提供了”analog_iis_option.h“文件,该文件里包含了用户可选择的音频算法,通过修改宏定义的值(0关闭,1开启)即可使能或关闭对应音频算法。
/****************音频算法使能配置*****************************/
#define NOISE_GATE_ENABLE 0 /*静噪门使能配置*/
#define ADC_SOFT_EQ_ENABLE 0 /*使能adc软件EQ(进算法之前)*/
#define SOFT_DRC_ENABLE 0 /*DRC使能*/
#define SOFT_RESAMPLER 0 /*软件SRC使能配置*/
通过音量阈值判断,自动抑制低于门限的稳态背景噪声、保留高于门限的有效人声,用户可根据需求在上位机进行配置。
void noise_gate_cfg(void)
{
audio_noise_gate_def noise_gate;
noise_gate.enable = noise_gate_param.NoiseGate_enable; /*静噪门状态使能*/
noise_gate.audio_fram_len = AUDIO_CALLBACK_LEN;/*音频数据帧长*/
noise_gate.up_threshold = noise_gate_param.up_threshold; /* 上位机配置噪声门开启阈值 */
noise_gate.down_threshold = noise_gate_param.down_threshold; /* 上位机配置噪声门关闭阈值 */
noise_gate_init(noise_gate);/*初始化静噪门*/
}
通过调节不同频率段的音量增益 / 衰减,改变音频的频率响应,用户可根据需求在上位机进行配置。
/**
* EQ配置函数
* @param: audio 需要处理的音频buffer
* @param: sample_len 需要处理的音频buffer长度
* @return: none.
*/
static int32_t filter_status[8][10] = {0}; /* 8个EQ滤波器的状态缓存数组 */
void eq_soft_process(int16_t* audio, uint32_t sample_len)
{
for(uint8_t i=0;i<8;i++) /* 遍历8路软件EQ滤波器 */
{
if(soft_eq_param.filter_type[i] != 0) /* 判断当前EQ滤波器是否启用(非0表示启用) */
{
eq_soft_update_data_q15_int((int16_t*)audio,sample_len,(int*)&soft_eq_param.param[i].param[0],(int32_t*)(&filter_status[i][0])); /* 执行Q15格式EQ滤波处理 */
}
}
}
自动调节音频信号的动态范围,把大音量压小、把小音量放大,用户可根据需求在上位机进行配置。
/**
* DRC配置函数
* @param:drc_init_struct Drc配置结构体
* @return: none.
*/
void drc_set_parameters(DRC_init_typedef * drc_init_struct)
{
// ========================
// DRC模块开关配置
// ========================
drc_init_struct->Limiter = DRC_LIMITER_EN; /* 限幅器开关(1=开启, 0=关闭) */
drc_init_struct->Compressor = DRC_COMPRESSOR_EN; /* 压缩器开关(1=开启, 0=关闭) */
drc_init_struct->Expander = DRC_EXPANDER_EN; /* 扩展器开关(1=开启, 0=关闭) */
drc_init_struct->NoiseGate = DRC_NOISEGATE_EN; /* 噪声门开关(1=开启, 0=关闭) */
// ========================
// 时间参数配置(单位: ms或s)
// ========================
drc_init_struct->ATtd = DRC_ATTD_MS; /* 主攻击时间(3ms) - 增益下降速度 */
drc_init_struct->RTtd = DRC_RTTD_MS; /* 主释放时间(150ms) - 增益恢复速度 */
drc_init_struct->AVt = DRC_AVT_MS; /* RMS平均时间(1ms) - 能量检测窗口 */
drc_init_struct->ATt = DRC_ATT_MS; /* 峰值攻击时间(1ms) - 瞬态响应速度 */
drc_init_struct->RTt = DRC_RTT_MS; /* 峰值释放时间(5s) - 峰值保持时间 -推荐 1-10s 单位ms */
// ========================
// 动态范围阈值(单位: dB)
// ========================
drc_init_struct->NT = DRC_NT_DB; /* 噪声门阈值(-35dB) */
drc_init_struct->ET = DRC_ET_DB; /* 扩展器阈值(-30dB) */
drc_init_struct->CT = DRC_CT_DB; /* 压缩器阈值(-6dB) */
drc_init_struct->LT = DRC_LT_DB; /* 限幅器阈值(-3dB) */
// ========================
// 动态范围比率
// ========================
drc_init_struct->NR = DRC_NR_SLE; /* 噪声门比率(0.1:1) */
drc_init_struct->ER = DRC_ER_SLE; /* 扩展器比率(0.75:1) */
drc_init_struct->CR = DRC_CR_SLE; /* 压缩器比率(2:1) */
drc_init_struct->LR = DRC_LR_SLE; /* 限幅器比率(∞:1) */
// ========================
// 高级参数
// ========================
drc_init_struct->hyst = DRC_HYST; /* 滞后曲线宽度(0.1) - 防止阈值附近振荡 */
}
void drc_init(void)
{
DRC_init_typedef drc_init_struct; /* 定义DRC初始化配置结构体 */
#ifdef EXTER_PARAM/* 如果定义该宏,即通过外部ext.a文件导入默认值 */
drc_set_exter_parameters(&drc_init_struct); /* 使用外部配置参数初始化DRC结构体 */
#else
drc_set_parameters(&drc_init_struct); /* 使用内部默认参数初始化DRC结构体 */
#endif
drcinit(AUDIO_SAMPLERATE,drc_init_struct); /* 调用底层DRC初始化函数,传入采样率与配置参数 */
}
把音频信号从一个采样率转换成另一个采样率的,解决不同设备之间采样率不匹配问题,用户可根据需求在代码中进行配置。
void resample_init(void)
{
static int16_t in_buff[AUDIO_CALLBACK_LEN]; /* 定义重采样输入缓存数组 */
resampler = Creat_Resampler(); /* 创建重采样器实例 */
ResCfg resCfg; /* 定义重采样配置结构体 */
resCfg.inbuf = in_buff; /* 数据输入数组 */
resCfg.inch = 1; /* 要处理的通道数 */
resCfg.insrt = 16000; /* SRC处理之前的输入采样率 */
resCfg.outsrt = 48000; /* SRC处理之前的输出采样率 */
resCfg.samples = AUDIO_CALLBACK_LEN; /* 要处理的点个数,双声道时记得*2 */
resCfg.BPC=5; /* 滤波器相数上限 */
resCfg.FILTERTAP=31; /* 滤波器阶数 */
resCfg.BPCINT=3; /* 滤波器内部参数 */
resampler->prepare(resampler,&resCfg); /* 配置重采样器参数 */
}
/**
* SRC处理函数
* @param:in_buff 音频数据输入buffer
@param:out_buff 音频数据输出buffer
* @return: none.
*/
void resample_deal(int16_t *in_buff,int16_t *out_buff)/* 执行重采样处理,返回处理点数 */
{
int dac_size = resampler->process(resampler,in_buff); /* 执行重采样处理,返回处理点数 */
resampler->getData(resampler,out_buff,dac_size * 2); /* 获取重采样后数据,参数单位为字节 */
}
SDK的"board/include"路径下存放了board.h文件,该文件包含了一个宏“#define USING_UAC_10”,启用该宏定义后,设备连接电脑时将自动枚举为名为 test_audio 的 USB 音频设备。在电脑端的音频输入 / 输出设置中选择该设备,即可通过 USB 录音函数与 USB 播放函数实现音频数据的双向传输。同时在“/algo/process/algo_process.c"文件的"get_audio**"函数中会打开录音和播放功能,此时,使用SPV6040B设备可以支持实时耳返监听,可通过耳机即时听到采集到的音频反馈并支持 USB 音频的录制与播放,满足双向音频交互需求。
#ifdef USING_UAC_10
usb_recorder_post_data_to_host(temp_buff,2,AUDIO_CALLBACK_LEN);
get_usb_host_speaker_data(temp_buff_speak,2,AUDIO_CALLBACK_LEN);
audio_mix_int16(temp_buff,temp_buff_speak,temp_buff,AUDIO_CALLBACK_LEN*2);
#endif
该函数位于"codec/codec_usb_recorder.c"文件中,在应用层调这个函数,USB会自动读取传入的buff数据实现录音功能。
/**
* @brief 两个单声道int16音频数据混音(饱和混音,防止溢出)
* @param addr 音频数据缓冲区buffer
* @param channel_count 音频声道数
* @param sample_count 音频样本数量(不是字节数)
*/
void usb_recorder_post_data_to_host(uint16_t* addr,uint32_t channel_count,uint32_t sample_points)
该函数位于"codec/codec_usb_speaker.c"文件中,在应用层调这个函数,即可把USB的音频数据通过DAC实现播放功能。
/**
* @brief 两个单声道int16音频数据混音(饱和混音,防止溢出)
* @param addr 音频数据缓冲区buffer
* @param channel_count 音频声道数
* @param sample_count 音频样本数量(不是字节数)
*/
uint32_t get_usb_host_speaker_data(uint16_t* addr,uint32_t channel_count,uint32_t sample_points);
该函数位于"algo/process/algo_process.c"文件中,在应用层调这个函数,即可把两个单声道int16音频数据混音。
/**
* @brief 两个单声道int16音频数据混音(饱和混音,防止溢出)
* @param in1 第一个输入音频数据缓冲区
* @param in2 第二个输入音频数据缓冲区
* @param out 混音后的输出音频数据缓冲区(需提前分配足够内存)
* @param sample_count 音频样本数量(不是字节数)
*/
void audio_mix_int16(int16_t *in1,int16_t *in2,int16_t *out,uint32_t sample_count);