Integração de algoritmo de áudio Qualcomm ADSP (2) módulo de algoritmo CAPI v2 e unidade de teste

        CAPI v2 (Common Audio Processor Interface v2) é uma interface de processamento de áudio comum usada na estrutura ADSP. Seu objetivo de design é encapsular os detalhes do algoritmo subjacente e abstraí-lo em um formato de interface comum. Através do CAPI v2, o código da camada de controle pode facilmente chamar o módulo de algoritmo para realizar codec de áudio, mixagem de áudio, cancelamento de eco, redução de ruído e outras funções. O design desta interface torna mais fácil para os desenvolvedores utilizarem os módulos de algoritmo subjacentes sem ter que prestar atenção aos detalhes da implementação subjacente.

Troca de dados em um canal

Defina um algoritmo simples: capi_v2_channelswap, que troca os canais esquerdo e direito dos dados de áudio de dois canais.

Cenário: Nos canais esquerdo e direito dos fones de ouvido, um alto-falante é conectado após passar pelo PA para obter som estéreo. No entanto, quando o hardware está projetando o circuito, o design da saída DAC dos canais esquerdo e direito é invertido.

Conforme mostrado na figura acima, o módulo CAPI v2 é dividido principalmente nas três partes a seguir:

1. Estrutura de dados capi_v2_channelswap_t

Cada módulo de algoritmo precisa definir uma estrutura de dados interna, e o espaço de memória dessa estrutura precisa ser aplicado pela camada de controle e repassado quando o algoritmo é inicializado. A estrutura de dados capi_v2_channelswap é definida da seguinte forma:

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;

  • Interface CAPI_V2

A estrutura capi_v2_vtbl_t contém as seguintes funções para exposição a chamadas de plano de controle.

capi_v2_vtbl_t::process() //processamento de algoritmo, código principal! !

capi_v2_vtbl_t::end() //Fim do processamento do algoritmo

//Descrição: Ao definir e obter parâmetros, você precisa definir o ID do parâmetro, e este ID também será usado para ACDB e importação de algoritmo posteriormente.

capi_v2_vtbl_t::set_param() //Definir parâmetros, parâmetros de calibração de algoritmo, habilitar chaves, etc.

capi_v2_vtbl_t::get_param()//Obtém parâmetros, parâmetros de calibração de algoritmo, habilita chaves, etc.

capi_v2_vtbl_t::set_properties()//Definir características de áudio, formatos de entrada e saída, funções de retorno de chamada, etc.

capi_v2_vtbl_t::get_properties()//Obtém características de áudio, formatos de entrada e saída, funções de retorno de chamada, etc.

  • Parâmetros de calibração do módulo

Parâmetros de calibração de algoritmo, chaves de habilitação, etc.

  • Propriedades CAPI_V2

Portas de entrada e saída, funções de retorno de chamada transmitidas pela camada de controle, etc.

  • Formatos de mídia

formato de áudio

2.capi_v2_channelswap_get_static_properties

Antes de inicializar o módulo CAPI v2, capi_v2_channelswap_get_static_properties é chamado pela camada de controle para obter as características estáticas do módulo atual. Por exemplo, o tamanho da memória ocupada pela estrutura de dados capi_v2_channelswap_t é usado para aplicar à camada de controle e passar a memória apropriada durante a inicialização; a função de processamento do módulo CAPI v2 consome o tamanho do espaço de pilha para que o ADSP possa alocar o espaço de pilha ao criar o SVC.

A implementação é a seguinte e, finalmente, a implementação de capi_v2_vtbl_t::get_properties() é chamada, ou seja, 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

A memória solicitada pela camada de controle para a estrutura de dados interna do algoritmo é passada através do parâmetro capi_v2_t* _pif, ou seja, capi_v2_channelswap_t. Depois de limpar capi_v2_channelswap_t, atribua a tabela de funções de operação do algoritmo interno do módulo a vtbl_ptr, para que a camada de controle obtenha uma estrutura capi_v2_t, obtenha a capacidade de passar parâmetros e chamar o processamento do algoritmo e inicialize outros membros de dados de capi_v2_channelswap_t.

As características do fluxo de dados atual, como o número de portas e o formato do fluxo de dados de entrada, são transmitidas por meio do parâmetro init_set_properties. Finalmente, a implementação de capi_v2_vtbl_t::set_properties() é chamada, nomeadamente 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 <%d> 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
};

O processo é implementado da seguinte maneira e outras funções são semelhantes às amostras do 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; k<me->ports.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; i<input[k]->bufs_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;

}

Configurações de parâmetros: capi_v2_channelswap possui apenas parâmetros de ativação.

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 arquivos de cabeçalho

#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 compilando capi_v2_channelswap

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

fazer árvore V=hexagon_Debug_dynamic_toolv83_v66

cd hexágono_Debug_dynamic_toolv83_v66/ship/

árvore

.

├── capi_v2_channelswap.a

├── capi_v2_channelswap.h

└── capi_v2_channelswap.so

0 diretórios, 3 arquivos

Como acima, a saída final de capi_v2_channelswap é a biblioteca de algoritmos e os arquivos de cabeçalho do algoritmo.

Duas unidades de teste de escrita

O Hexagon SDK fornece uma estrutura de teste executável para testar a biblioteca de algoritmos desenvolvida.

1. Teste áudio e perfis

capi_v2_channelswap/dados

.

├── canaiswap.cfg

├── test_audio_in.raw // caminho de voz duplo 16 bits PCM 48k

O conteúdo do arquivo canaiswap.cfg é o seguinte:

######################################################################
# 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 <msw>
   00 00 00 00    # Data Payload address <lsw> 
   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. Teste a entrada da função principal

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 Abra a biblioteca de algoritmos e carregue a função

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 Chame a função de processamento

test_capi_v2_main é uma função fornecida pela estrutura de teste.

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;
}

Verificação de efeito de três algoritmos

Execute o seguinte comando de teste:

~/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

Gere o arquivo test_audio_out.raw no diretório de dados.

Abra os arquivos test_audio_in.raw e test_audio_out.raw e compare-os da seguinte forma, o algoritmo entra em vigor! !

Acho que você gosta

Origin blog.csdn.net/Q_Lee/article/details/131219170
Recomendado
Clasificación