高通ADSP音频算法集成(二)CAPI v2算法模块和测试单元 您所在的位置:网站首页 面具v2模块怎么用 高通ADSP音频算法集成(二)CAPI v2算法模块和测试单元

高通ADSP音频算法集成(二)CAPI v2算法模块和测试单元

2023-06-05 14:53| 来源: 网络整理| 查看: 265

      

目录

一  双声道数据交换算法

1. capi_v2_channelswap_t 数据结构

2. capi_v2_channelswap_get_static_properties

3. capi_v2_channelswap_init

4. capi_v2_vtbl_t vtbl

5 capi_v2_channelswap.h头文件

6 capi_v2_channelswap编译

二 编写测试单元

1. 测试音频和配置文件

2. 测试主函数入口

3 打开算法库、加载函数

4 调用处理函数

三 算法效果验证

         CAPI v2 (Common Audio Processor Interface v2) 是 ADSP 框架中使用的通用音频处理接口。它的设计目的是将底层算法细节进行封装,并抽象成通用的接口形式。通过 CAPI v2, 控制层代码能够方便地调用算法模块,实现音频编解码、混音、回声消除、降噪等功能。这种接口的设计使得开发人员可以更轻松地利用底层算法模块,而无需关注底层实现的细节。

一  双声道数据交换算法

        定义一个简单的算法:capi_v2_channelswap,将双声道音频数据进行左右声道交换。

        场景:在耳机的左右声道上,通过PA后分别接一个喇叭,从而实现立体声。但是硬件在电路进行设计的时候将左右声道DAC输出设计反了。

如上图所示,CAPI v2 module主要分为下面的三个部分:

1. capi_v2_channelswap_t 数据结构

        每个算法模块都需要定义一个内部的数据结构体,并且这个结构体的内存空间需要控制层申请,在算法初始化的时候传入。capi_v2_channelswap数据结构定义如下:

typedef struct _capi_v2_channelswap_t { // CAPI_V2 interface const capi_v2_vtbl_t* vtbl_ptr; //必须结构体的第一个成员 // Module calibration parameters uint32_t channelswap_enable; // CAPI_V2 Properties int heap_id; capi_v2_event_callback_info_t event_callback; capi_v2_port_num_info_t ports; // Media formats: media_fmt_t input_media_format; media_fmt_t output_media_format; } capi_v2_channelswap_t; CAPI_V2 interface

capi_v2_vtbl_t结构包含以下函数,用于暴露给控制层调用。

capi_v2_vtbl_t::process() //算法处理,核心代码!!

capi_v2_vtbl_t::end() //算法处理结束

//说明:在设置和获取参数需要定义参数的ID,同时这个ID后面还将用于ACDB,算法导入。

capi_v2_vtbl_t::set_param() //设置参数,算法校准参数,使能开关等

capi_v2_vtbl_t::get_param()//获取参数,算法校准参数,使能开关等

capi_v2_vtbl_t::set_properties()//设置音频特性,输入输出格式、回调函数等

capi_v2_vtbl_t::get_properties()//获取音频特性,输入输出格式、回调函数等

Module calibration parameters

算法校准参数,使能开关等

CAPI_V2 Properties

输入和输出的ports,控制层传入的回调函数等

Media formats

音频格式

2. capi_v2_channelswap_get_static_properties

        在初始化CAPI v2 module之前,capi_v2_channelswap_get_static_properties被控制层先调用,以获取当前模块的静态特征。例如 capi_v2_channelswap_t数据结构占用内存的大小,用于控制层申请并在初始化传入合适的内存;CAPI v2 module处理函数消耗栈空间的大小,以便ADSP 在创建SVC时,分配好栈空间。

        实现如下,最后调用了capi_v2_vtbl_t::get_properties()的实现,即capi_v2_utils_props_process_properties。

capi_v2_err_t capi_v2_channelswap_get_static_properties( capi_v2_proplist_t* init_set_properties, capi_v2_proplist_t* static_properties) { if (NULL == static_properties) { FARF(ERROR, "static_properties is NULL"); return CAPI_V2_EFAILED; } return capi_v2_utils_props_process_properties(static_properties, capi_v2_channelswap_set_props, 0); return CAPI_V2_EOK; } 3. capi_v2_channelswap_init

通过capi_v2_t* _pif参数传入了控制层为算法内部数据结构申请的内存,即capi_v2_channelswap_t。对capi_v2_channelswap_t清零后,将模块内部算法操作函数表赋值给vtbl_ptr,使控制层获得一个capi_v2_t结构,获得参数传递和调用算法处理的能力,对capi_v2_channelswap_t其他数据成员进行初始化。

通过init_set_properties参数传递进来处理当前数据流的特性,如端口数、输入数据流格式等。最后调用了capi_v2_vtbl_t::set_properties()的实现,即capi_v2_channelswap_set_properties。

capi_v2_err_t capi_v2_channelswap_init(capi_v2_t* _pif, capi_v2_proplist_t* init_set_properties) { capi_v2_channelswap_t *me = (capi_v2_channelswap_t *)_pif; capi_v2_prop_t *props; uint32_t i; capi_v2_err_t result = CAPI_V2_EOK; if (NULL == _pif) { FARF(ERROR, "_pif is NULL."); return CAPI_V2_EBADPARAM; } memset(me, 0, sizeof(capi_v2_channelswap_t)); me->vtbl_ptr = &vtbl; me->ports.num_input_ports =1; me->ports.num_output_ports=1; //disable default me->channelswap_enable = 0; me->event_callback.event_cb =NULL; me->event_callback.event_context = NULL ; props = init_set_properties->prop_ptr; for(i = 0; i < init_set_properties->props_num; i++){ switch (props->id) { case CAPI_V2_INPUT_MEDIA_FORMAT: case CAPI_V2_EVENT_CALLBACK_INFO: case CAPI_V2_HEAP_ID: case CAPI_V2_PORT_NUM_INFO: break; default: FARF(HIGH, "This property with id is not supported at initialization of module", props->id); break; } props++; } FARF(HIGH, "num of init set props %ld", init_set_properties->props_num); // Setting parameters now if(init_set_properties->props_num) { result = capi_v2_channelswap_set_properties(_pif, init_set_properties); } return result; } 4. capi_v2_vtbl_t vtbl static const capi_v2_vtbl_t vtbl = { // add function pointers to CAPI_V2 functions capi_v2_channelswap_process, capi_v2_channelswap_end, capi_v2_channelswap_set_param, capi_v2_channelswap_get_param, capi_v2_channelswap_set_properties, capi_v2_channelswap_get_properties };

process实现如下,其他函数与SDK样例类似。

static capi_v2_err_t capi_v2_channelswap_process( capi_v2_t* _pif, capi_v2_stream_data_t* input[], capi_v2_stream_data_t* output[]) { capi_v2_err_t result = CAPI_V2_EOK; capi_v2_channelswap_t* me = (capi_v2_channelswap_t*)_pif; capi_v2_buf_t *in_buf_ptr, *out_buf_ptr; uint32_t i, k; FARF(ERROR,"process"); for(k=0; kports.num_input_ports; k++) { in_buf_ptr = input[k]->buf_ptr; out_buf_ptr = output[k]->buf_ptr; if(input[k]->bufs_num == 2 && me->channelswap_enable == 1) //swap left and right channel { capi_v2_buf_t *in_buf_ptr_r = in_buf_ptr++; capi_v2_buf_t *out_buf_ptr_r = out_buf_ptr++; memcpy(out_buf_ptr->data_ptr, in_buf_ptr_r->data_ptr, in_buf_ptr_r->actual_data_len); out_buf_ptr->actual_data_len = in_buf_ptr_r->actual_data_len; memcpy(out_buf_ptr_r->data_ptr, in_buf_ptr->data_ptr, in_buf_ptr->actual_data_len); out_buf_ptr_r->actual_data_len = in_buf_ptr->actual_data_len; }else{ //pass through for(i=0; ibufs_num; i++) { memcpy(out_buf_ptr->data_ptr, in_buf_ptr->data_ptr, in_buf_ptr->actual_data_len); out_buf_ptr->actual_data_len = in_buf_ptr->actual_data_len; in_buf_ptr++; out_buf_ptr++; } } } return result; }

参数设置:capi_v2_channelswap只有使能参数。

static capi_v2_err_t capi_v2_channelswap_set_param( capi_v2_t* _pif, uint32_t param_id, const capi_v2_port_info_t* port_info_ptr, capi_v2_buf_t* params_ptr) { capi_v2_err_t result = CAPI_V2_EOK; capi_v2_channelswap_t* me = (capi_v2_channelswap_t*)_pif; //Validate pointers if (NULL == _pif || NULL == params_ptr) { FARF(ERROR, "CAPI V2 CHANNELSWAP: set param received NULL pointer"); return CAPI_V2_EBADPARAM; } switch(param_id) { case CAPI_V2_PARAM_CHANNELSWAP_MODULE_ENABLE: ... break; default: FARF(HIGH, "CAPI V2 CHANNELSWAP: set_param received unsupported parameter(0x%x)", param_id); result = CAPI_V2_EBADPARAM; break; } return result; } 5 capi_v2_channelswap.h头文件 #ifndef __CHANNELSWAP_V2_CALIB_H__ #define __CHANNELSWAP_V2_CALIB_H__ #define CAPI_V2_CHANNELSWAP_STACK_SIZE 2048 // Passthru module calibration parameters #define CAPI_V2_PARAM_CHANNELSWAP_MODULE_ENABLE 0x1111FF01 #ifdef __cplusplus extern "C" { #endif capi_v2_err_t capi_v2_channelswap_get_static_properties( capi_v2_proplist_t* init_set_properties, capi_v2_proplist_t* static_properties); capi_v2_err_t capi_v2_channelswap_init(capi_v2_t* _pif, capi_v2_proplist_t* init_set_properties); #ifdef __cplusplus } #endif #endif 6 capi_v2_channelswap编译

~/Qualcomm/Hexagon_SDK/3.5.4/examples/audio/capi_v2_channelswap

make tree V=hexagon_Debug_dynamic_toolv83_v66

cd hexagon_Debug_dynamic_toolv83_v66/ship/

tree

.

├── capi_v2_channelswap.a

├── capi_v2_channelswap.h

└── capi_v2_channelswap.so

0 directories, 3 files

如上,capi_v2_channelswap最终的产出是算法库和算法头文件。

二 编写测试单元

Hexagon SDK 提供了一个可执行测试框架,用于测试开发的算法库。

1. 测试音频和配置文件

capi_v2_channelswap/data

.

├── channelswap.cfg

├── test_audio_in.raw //双声道16bit PCM 48k

channelswap.cfg文件内容如下:

###################################################################### # Configuration file for Hexagon Access Program Example Passthru unit test # # This config file contains the commands that will be run sequentially by the # test framework. # # Specifically it: # - Sets media format, sampling rate, num channels, bytes per sample # - Sets Enable module parameters # - Processes buffers as specified by NumBuffers ###################################################################### # Set Media Format SetMediaFormat SetBitstreamFormat 69029 SetDataFormat 0 SetNumChannelsAndMapping 2 0 1 SetBitsPerSample 16 QFactor 0 SetSamplingRate 48000 SetIsSigned 1 SetInterleaving 2 SetOutputMediaFormat SetBitstreamFormat 69029 SetDataFormat 0 SetNumChannelsAndMapping 2 0 1 SetBitsPerSample 16 QFactor 0 SetSamplingRate 48000 SetIsSigned 1 SetInterleaving 2 # Enable Passthru module SetParamInband PayloadSizeInBytes 32 00 00 00 00 # Data Payload address 00 00 00 00 # Data Payload address 00 00 00 00 # mem_map_handle 18 00 00 00 # Data payload size 00 FF 11 11 # module_id 0x11111200 PASSTHRU_V2_MODULE 01 FF 11 11 # param_id 0x11111201 PASSTHRU_PARAM_MOD_ENABLE 04 00 00 00 # param_size 01 00 00 00 # payload (Enable/Disable) ProcessData NumBuffers 300 2. 测试主函数入口 int test_main_start(int argc, char* argv[]) { int err = TEST_SUCCESS; args_t* input_args = 0; FARF(HIGH, "test_main_start \n"); #ifdef ENABLE_COMMAND_LINE_PARAMS if (NULL == (input_args = (args_t*)malloc(sizeof(args_t)))) { FARF(HIGH, "%s: ERROR CODE 1 - Cannot malloc args\n", argv[0]); exit(-1); } get_eargs(argc, argv, input_args); #endif //1. 定义capi_v2_proplist_t和capi_v2_prop_t capi_v2_proplist_t init_set_properties; capi_v2_proplist_t static_properties; capi_v2_prop_t props[5]; capi_v2_init_memory_requirement_t init_memory_requirement; capi_v2_stack_size_t stack_size; capi_v2_max_metadata_size_t max_metadata_size; capi_v2_is_inplace_t is_inplace; capi_v2_requires_data_buffering_t requires_data_buffering; init_set_properties.props_num = 0; static_properties.props_num = 5; static_properties.prop_ptr = props; //2. 对props初始化,用于获取算法模块的特征 props[0].id = CAPI_V2_INIT_MEMORY_REQUIREMENT; props[0].payload.data_ptr = (int8_t*)&init_memory_requirement; props[0].payload.max_data_len = sizeof(init_memory_requirement); props[1].id = CAPI_V2_STACK_SIZE; props[1].payload.data_ptr = (int8_t*)&stack_size; props[1].payload.max_data_len = sizeof(stack_size); props[2].id = CAPI_V2_MAX_METADATA_SIZE; props[2].payload.data_ptr = (int8_t*)&max_metadata_size; props[2].payload.max_data_len = sizeof(max_metadata_size); props[3].id = CAPI_V2_IS_INPLACE; props[3].payload.data_ptr = (int8_t*)&is_inplace; props[3].payload.max_data_len = sizeof(is_inplace); props[4].id = CAPI_V2_REQUIRES_DATA_BUFFERING; props[4].payload.data_ptr = (int8_t*)&requires_data_buffering; props[4].payload.max_data_len = sizeof(requires_data_buffering); #ifdef __V_DYNAMIC__ TRY(err, dll_test(input_args, &init_set_properties, &static_properties)); #else TRY(err, lib_test(input_args, &init_set_properties, &static_properties)); #endif if (0 == input_args) { free(input_args); } CATCH(err){}; return err; } 3 打开算法库、加载函数 int dll_test(args_t* input_args, capi_v2_proplist_t* init_set_properties, capi_v2_proplist_t* static_properties) { void* h = 0; const char* cpszmodname = "capi_v2_channelswap.so"; const char* cpszget_static_properties = "capi_v2_channelswap_get_static_properties"; const char* cpszinit = "capi_v2_channelswap_init"; capi_v2_get_static_properties_f get_static_properties_f = 0; capi_v2_init_f init_f = 0; int err = TEST_SUCCESS; FARF(HIGH, "-- start dll test -- "); //1. 打开动态库capi_v2_channelswap.so FARF(HIGH, "attempt to load %s ", cpszmodname); h = dlopen(cpszmodname, RTLD_NOW); if (0 == h) { FARF(HIGH, "dlopen %s failed %s ", cpszmodname, dlerror()); THROW(err, TEST_FAILURE); } //2.获得动态库中get_static_properties_f函数 get_static_properties_f = (capi_v2_get_static_properties_f)dlsym(h, cpszget_static_properties); if (0 == get_static_properties_f) { FARF(HIGH, "dlsym %s failed %s ", cpszget_static_properties, dlerror()); THROW(err, TEST_FAILURE); } //3.获得动态库中capi_v2_init_f函数 init_f = (capi_v2_init_f)dlsym(h, cpszinit); if (0 == init_f) { FARF(HIGH, "dlsym %s failed %s ", cpszinit, dlerror()); THROW(err, TEST_FAILURE); } TRY(err, test_capi_v2_main(get_static_properties_f, init_f, init_set_properties, static_properties, "../data/test_audio_in.raw", //输入音频文件 "../data/test_audio_out.raw", //输出音频文件 "../data/channelswap.cfg")); //配置文件 ......... } 4 调用处理函数

test_capi_v2_main是测试框架提供的函数。

capi_v2_err_t test_capi_v2_main(capi_v2_get_static_properties_f get_static_properties_f, capi_v2_init_f init_f, capi_v2_proplist_t* init_set_properties, capi_v2_proplist_t* static_properties, const char* filename_in, const char* filename_out, const char* filename_config) { module_info_t module; uint32_t malloc_size = 0; uint8_t* ptr = 0; capi_v2_err_t result = CAPI_V2_EOK; // ------------------------ // Initializations // ------------------------ // TODO init_profiling(); memset(&module, 0, sizeof(module)); if (filename_in != NULL) { if ((module.finp = fopen(filename_in, "rb")) == NULL) { FARF(ERROR, "test_capi_v2: Cannot open input file "); THROW(result, CAPI_V2_EFAILED); } } if (filename_out != NULL) { if ((module.fout = fopen(filename_out, "wb")) == NULL) { FARF(ERROR, "test_capi_v2: Cannot open output file "); THROW(result, CAPI_V2_EFAILED); } } if ((module.fCfg = fopen(filename_config, "rb")) == NULL) { FARF(ERROR, "test_capi_v2: Cannot open config file "); THROW(result, CAPI_V2_EFAILED); } // ------------------------ // STEP 1: Get size requirements of CAPI_V2 // ------------------------ FARF(HIGH, "MAIN: ---------------- "); FARF(HIGH, "MAIN: Initialize module "); FARF(HIGH, "MAIN: ---------------- "); result = get_static_properties_f(init_set_properties, static_properties); if (CAPI_V2_FAILED(result)) { FARF(ERROR, "MAIN: get_static_properties error "); THROW(result, result); } capi_v2_utils_props_process_properties(static_properties, test_capi_v2_get_props, &malloc_size); malloc_size = align_to_8_byte(malloc_size); // ------------------------ // STEP 2: Allocate memory // ------------------------ ptr = (uint8_t*)malloc(malloc_size); if (!ptr) { FARF(ERROR, "MAIN: Memory allocation error "); THROW(result, CAPI_V2_ENOMEMORY); } module.module_ptr = (capi_v2_t*)ptr; FARF(HIGH, "allocated %6lu bytes of memory at location 0x%08p ", malloc_size, ptr); module.out_format.max_data_len = sizeof(module.out_format_buf); module.out_format.data_ptr = module.out_format_buf; // ------------------------ // STEP 3: Initialize module // ------------------------ result = init_f((capi_v2_t*)(ptr), init_set_properties); if (CAPI_V2_FAILED(result)) { FARF(ERROR, "MAIN: Initialization error "); THROW(result, result); } { // Set event callback information capi_v2_event_callback_info_t event_cb_info; event_cb_info = capi_v2_tst_get_cb_info(&module); capi_v2_proplist_t proplist; capi_v2_prop_t props[1]; proplist.props_num = 1; proplist.prop_ptr = props; props[0].id = CAPI_V2_EVENT_CALLBACK_INFO; props[0].payload.data_ptr = (int8_t*)(&event_cb_info); props[0].payload.actual_data_len = sizeof(event_cb_info); props[0].payload.max_data_len = sizeof(event_cb_info); module.module_ptr->vtbl_ptr->set_properties(module.module_ptr, &proplist); } module.requires_data_buffering = (bool_t)*(static_properties->prop_ptr[CAPI_V2_REQUIRES_DATA_BUFFERING].payload.data_ptr); // ------------------------ // Run config file // ------------------------ FARF(HIGH, "MAIN: ---------------- "); FARF(HIGH, "MAIN: Run config file "); FARF(HIGH, "MAIN: ---------------- "); // Added new Input/Output commands for test script support testCommand inputCmd[2]; strncpy(inputCmd[0].opCode, "Input", 6); strncpy(inputCmd[1].opCode, "Output", 7); inputCmd[0].pFunction = &Inputfile; inputCmd[1].pFunction = &Outputfile; result = RunTest(&module, inputCmd, 2); if (CAPI_V2_FAILED(result)) { FARF(ERROR, "MAIN: Error in RunTest "); THROW(result, result); } // ------------------------ // Destroy CAPI V2 and free memory // ------------------------ result = module.module_ptr->vtbl_ptr->end(module.module_ptr); if (CAPI_V2_FAILED(result)) { FARF(ERROR, "MAIN: Error in call to end "); THROW(result, result); } CATCH(result){}; if (ptr) { free(ptr); } // TODO deinit_profiling(); if (module.finp) { fclose(module.finp); } if (module.fout) { fclose(module.fout); } if (module.fCfg) { fclose(module.fCfg); } FARF(HIGH, "MAIN: Done "); return result; } 三 算法效果验证

执行如下测试命令:

~/Qualcomm/Hexagon_SDK/3.5.4/tools/HEXAGON_Tools/8.3.07/Tools/bin/hexagon-sim -mv66g_1024 --simulated_returnval --usefs hexagon_Debug_dynamic_toolv83_v66 hexagon_Debug_dynamic_toolv83_v66/capi_v2_channelswap_q

在data目录生成 test_audio_out.raw文件。

打开test_audio_in.raw和test_audio_out.raw文件对比如下,算法生效!!



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有