本文介绍珠海普林芯驰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_release_for_a2a_ai_aec_v1.0 | 回声消除(AEC)A2A标准参考工程 |
sp2cw3_release_for_a2a_ai_denoise_v1.0 | AI降噪A2A标准参考工程 |
sp2cw3_release_for_a2a_stationary_denoise_v1.0 | 稳态噪声降噪A2A标准参考工程 |
sp2cw3_demo_for_iis_ai_denoise_v1.0 | AI降噪IIS标准参考工程 |
sp2cw3_release_algo_for_a2a_ai_howling_suppression_v1.0 | AI啸叫抑制A2A标准参考工程 |
工程文件夹目录结构:
文件夹 | 描述 |
---|---|
algo | 算法静态库和算法接口调用逻辑代码 |
board | 硬件平台、系统状态机 |
codec | audio adc(录音),audio dac(播放) ,src(采样率转换)、 IIS等等相关配置代码 |
image | 算法模型(.mod)、参数配置文件(.a)、提示音文件(.dat) |
src | 用户业务逻辑代码相关 |
system | 系统运行、算法执行调用文件,udsp、npu、DMA等 |
debug | 编译过程中间文件 |
output | 编译后输出的烧录文件(.bin) |
makefile | 编译构建脚本,负责文件的依赖关系等等 |
导入AI降噪工程(工程名:sp2cw3_release_for_a2a_ai_denoise_v1.0)后,打开makefile文件,可以对降噪模型的延时和MIC通道做配置,如下:
宏 | 配置值 | 功能说明 |
---|---|---|
DENOISE_DELAY_TIME_MS | 24 | 对应ADC采样率为8k,降噪模型延时为24ms |
DENOISE_DELAY_TIME_MS | 37 | 对应ADC采样率为16k,降噪模型延时为37.5ms |
DENOISE_DELAY_TIME_MS | 48 | 对应ADC采样率为16k,降噪模型延时为48ms |
宏 | 配置值 | 功能说明 |
---|---|---|
DENOISE_MIC_NUMBER | 1 | 单麦降噪 |
DENOISE_MIC_NUMBER | 2 | 双麦降噪 |
导入稳态降噪工程(工程名:sp2cw3_release_for_a2a_stationary_denoise_v1.0)后,打开makefile文件,可以对降噪库的采样率、延时、声道数做修改,如下:
宏 | 配置值 | 功能说明 |
---|---|---|
DENOISE_TYPE | mono_16k_48ms | 对应ADC采样率为16k,降噪库延时为24ms,单声道 |
DENOISE_TYPE | mono_48k_32ms | 对应ADC采样率为48k,降噪库延时为32ms,单声道 |
DENOISE_TYPE | mono_8k_48ms | 对应ADC采样率为8k,降噪库延时为48ms,单声道 |
DENOISE_TYPE | stereo_16k_48ms | 对应ADC采样率为16k,降噪库延时为48ms,立体声 |
DENOISE_TYPE | stereo_48k_32ms | 对应ADC采样率为48k,降噪库延时为32ms,立体声 |
DENOISE_TYPE | stereo_8k_48ms | 对应ADC采样率为8k,降噪库延时为48ms,立体声 |
导入AEC工程(工程名:sp2cw3_release_for_a2a_ai_aec_v1.0)后,打开makefile文件,可以对AEC库的采样率、延时做修改,如下:
宏 | 配置值 | 功能说明 |
---|---|---|
AEC_TYPE | AEC_8K_48MS | 对应ADC采样率为8k,AEC库延时为48ms |
AEC_TYPE | AEC_16K_48MS | 对应ADC采样率为16k,AEC库延时为48ms |
AEC_TYPE | AEC_DENOISE_8K_48MS | 对应ADC采样率为8k,降噪和AEC库延时为48ms |
导入啸叫抑制工程(工程名:sp2cw3_release_algo_for_a2a_ai_howling_suppression_v1.0)后,打开makefile文件,可以对啸叫抑制库的采样率、延时做修改,如下:
宏 | 配置值 | 功能说明 |
---|---|---|
AHS_TYPE | AHS_16K_18MS | 对应ADC采样率为16k,啸叫抑制库延时为18ms |
函数名 | 位置 | 描述 |
---|---|---|
void user_init(void) | main.c | 用于上电后的外设初始化 |
void user_2ms_handler(void) | mainc | 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算力。
void audio_adc_init(void)
{
audio_adc_ch_cfg_typedef cfg;
cfg.sample_rate = SAMPLE_RATE_16K; /*配置采样率*/
cfg.digit_volume = VOLUME_01X; /*数字音量*/
cfg.hw_pin = AMIC_CH0; /*模拟通道*/
cfg.amic.mode = different; /*差分模式*/
cfg.amic.mic_gain = denoise_param.mic_gain; /*从配置文件获取MIC增益*/
cfg.amic.mix_gain = MADC_MIX_GAIN_N0D0B; /*设置MIX增益*/
cfg.amic.vmic = VOLT_2o59; /*设置VMIC电压*/
hal_audio_adc_ch_cfg(&audio_adc, cfg);
cfg.hw_pin = AMIC_CH1;
hal_audio_adc_ch_cfg(&audio_adc, cfg);
audio_adc.init.data_width = WIDTH_16BIT; /*数据宽度*/
#ifndef ADC_USE_SRC1
audio_adc.init.src_sel = AHB;
#else
audio_adc.init.src_sel = SRC;
debug("in audio_adc_init, src take effect \n\r");
#endif
audio_adc.init.channel_id = CHN0 | CHN1;
audio_adc.dma_handle = &audio_dma;/*注册DMA句柄*/
hal_audio_init(&audio_adc);
#ifndef ADC_USE_SRC1
adc_dma_init();
#endif
//adc_mic0_AGC_init();
audio_adc_cmd(&audio_adc, ENABLE);
}
主要通过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];
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;
audio_dac.init.sample_rate = DAC_SAMPLE_RATE_16K;
audio_dac.init.outfrom = HEADPHONE;
#ifndef DAC_USE_SRC0
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 = denoise_param.hp_volume;
audio_dac.init.eq_type = EQ_NORMAL_TYPE;
audio_dac.init.inverse = denoise_param.dac_output_inverse;
audio_dac.dma_handle = &dac_dma;
hal_audio_dac_init(&audio_dac);
audio_dac_EQ_init();
audio_dac_drc_init();
dac_dma_cfg();
audio_dac_cmd(&audio_dac, ENABLE);
}
主要通过结构体传入参数,结构体的赋值通过结构体枚举变量成员传入:
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;
/**
* 音频算法初始化
* @param: none
* @return: none.
*/
void denoise_init(void)
{
plxc_set_model_addr((uint32_t)(FW_HEADER->BIN[1].ADDR + 0x4000000));// set ai denoise mod addr
plxc_algo_init();//init ai denoise algo
plxc_set_denoise_level(250); // set ai denoise level
plxc_set_audio_callback(getaudio);// set callback function
}
/**
* 向算法输入原始音频数据
*
* @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播出
}