sp2cw3 bare SDK快速开发指南

本文介绍珠海普林芯驰sp2cw3平台芯片bare的SDK使用,包括编译环境搭建、下载、驱动及应用开发等,使用SDK进行开发之前,请阅读本文。

1、SOC简介

sp2cw3系列是珠海普林芯驰(www.spacetouch.co)推出的通用音频处理SOC,采用CPU+NPU+uDSP三核异构框架。CPU使用RISC-V架构,支持单精度浮点运算,uDSP能够完成音频信号的特征提取及计算,通过NPU实现神经网络训练和学习,能够实现AI降噪、AEC、语音识别等音频算法应用。 同时内置高性能音频专用 codec,丰富的外设资源,能够应用于各种音频应用领域。

spv40

2、SDK框架说明

bare SDK提供了AI降噪、传统(MMSE)降噪、回声消除、啸叫抑制等多种算法,工程模版已经集成最新的算法库。开发人员只需要根据自己的需求,完成业务逻辑部分即可。

2.1、文件夹目录结构

SDK根目录主控包括如下文件夹及内容:

文件夹描述
oss链接脚本、启动文件、寄存器封装等公共文件用,用户一般不需要改动,如需要改动,请联系原厂技术支持
project该文件下放置了demo工程,用于用户的需求开发

spv40

 

 

project是SDK源码目录,开发需要进入该目录进行:

文件夹描述
sp2cw3_demo_for_iis_ai_denoise_new_tool_v2.23A2A和IIS集成的标准降噪参考工程

 

工程文件夹目录结构:

文件夹描述
algo算法静态库和算法接口调用逻辑代码
board硬件平台、系统状态机
codecaudio adc(录音),audio dac(播放) ,src(采样率转换)、 iis、usb录音等等相关配置代码
image算法模型(.mod)、参数配置文件(.a)、提示音文件(.dat)
src用户业务逻辑代码相关
system系统运行、算法执行调用文件,udsp、npu、DMA等
debug编译过程中间文件
output编译后输出的烧录文件(.bin)
makefile编译构建脚本,负责文件的依赖关系等等

1722505678538

2.2、降噪工程使用说明

2.2.1首次编译说明

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

1722505678538 1722505678538

2.2.2模型配置

导入降噪工程(工程名: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_216K采样率、6ms延时的啸叫抑制模型
AHS_DENOISE_16K_6MS_26022016K采样率、6ms延时的啸叫抑制模型
AHS_DENOISE_32K_6MS_25102932K采样率、6ms延时的啸叫抑制模型
48k_15ms_7_1_Algorithm48K采样率、15ms延时的7.1声道算法模型
32k_48ms_strongdenoise32K采样率、48ms延时的强降噪模型
16k_48ms_stronger_denoise16K采样率、48ms延时的强降噪模型
16k_48ms_lowpower_dualmic16K采样率、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工程并重新编译即可。

2.2.3模型配置参考示例

参考示例:用户得到了一个"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工程重新编译即可添加成功。

2.3、系统状态机说明

函数名位置描述
void user_init(void)main.c用于上电后的外设初始化
void user_2ms_handler(void)main.c2ms执行周期的状态机回调函数,用户可以根据需求在这里调用业务逻辑代码
void user_10ms_handler(void)main.c10ms执行周期的状态机回调函数,用户可以根据需求在这里调用业务逻辑代码
void user_100ms_handler(void)main.c100ms执行周期的状态机回调函数,用户可以根据需求在这里调用业务逻辑代码
__IRAM void tick_user_handler(void)main.c500us执行周期的状态机回调函数,用户可以根据需求在这里调用业务逻辑代码

注释:在增加业务逻辑代码时候,应该评估其执行周期,尽可能不要频繁执行,为CPU跑语音算法预留足够的CPU算力。

2.4、音频核心代码解析

音频相关代码文件存放在"\codec"路径下。

2.4.1 audio adc

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;

 

2.4.2 audio dac

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;

2.4.3 audio i2s

/**
 * 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外设 */
}

 

2.5、音频算法接口说明

1722512619376

/**
 * 音频算法初始化
 * @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播出
}



2.6、通路配置说明

2.6.1 工程宏定义说明

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_PINIIS时钟引脚
IIS_WS_PINIIS声道选择引脚
IIS_SDO_PINIIS数据输出引脚
IIS_SDI_PINIIS数据输入引脚

在 SDK下的codec目录下提供了”codec_param.c“文件,该文件定义了一个名为"ENABLE_PARAM_SET_CODE"的宏,使能这个宏定义,即可使用代码进行配置ADC、DAC、I2S以及算法的相关参数(注:代码配置会覆盖上位机的配置)。

宏定义功能说明
ENABLE_PARAM_SET_CODE通过该宏开启和关闭代码配置codec和算法参数

 

2.6.2 配置示例

下面将进行"使用啸叫抑制模型,模拟进(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文件,回读芯片参数确保修改成功。

1722512619376

1722512619376

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引脚即可。

硬件通路大致如下:

1722512619376

 

 

2.7、用户调音算法配置说明(NOISE_GATE、EQ、DRC、SRC)

在 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使能配置*/

2.7.1 NOISE_GATE

通过音量阈值判断,自动抑制低于门限的稳态背景噪声、保留高于门限的有效人声,用户可根据需求在上位机进行配置。

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);/*初始化静噪门*/
}

2.7.2 SOFT_EQ

通过调节不同频率段的音量增益 / 衰减,改变音频的频率响应,用户可根据需求在上位机进行配置。

/**
 * 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滤波处理 */
		}
	}
}

2.7.3 SOFT_DRC

自动调节音频信号的动态范围,把大音量压小、把小音量放大,用户可根据需求在上位机进行配置。

/**
 * 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初始化函数,传入采样率与配置参数 */
}

2.7.4 SOFT_SRC

把音频信号从一个采样率转换成另一个采样率的,解决不同设备之间采样率不匹配问题,用户可根据需求在代码中进行配置。

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); /* 获取重采样后数据,参数单位为字节 */
}

2.8、UAC音频功能

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

2.8.1 USB录音函数

该函数位于"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)

2.8.2 USB播放函数

该函数位于"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);

2.8.3 混音函数

该函数位于"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);