BES (恒玄)HFP通话刨析以及调试笔记

 

目录

1. 前言

2. audio_develope通话调试

   2.1 通话调试前期准备和SDK 相关宏定义介绍

   2.2 通话调试原理 

   2.3 通话调试常见问题

3. BES (恒玄)HFP 通话代码部分说明

   3.1 HFP启动机制原理

   3.2 通话算法运行处理

   3.4 通话常见软件问题 分析解决

   3.5  BES 通话数据分析处理

   3.6  MIPS KEY的计算方式

4.总结


1.前言
    1.1 BES2300/BES2500/BES2600 audio_developer调试通话降噪 .

    1.2 本文主要介绍通话调试的机制原理 和常见问题解决对策。

2.audio_develope通话调试

 恒玄通话调试是需要用串口调试 ,本文以best1305 为例,简述下通话调试

2.1 通话调试前期准备和SDK 相关宏定义介绍:

 开发人员可以根据项目需要,开启相关宏定义,打开通话算法。

2.2 通话调试原理 :

 audio_developer 通过串口与耳机通讯,可以调试通话降噪算法参数,可以dump通话原始数据出来分析

原理:在通话运行状态下,刷新通话算法模块的参数 ,达到修改通话质量效果。

 通话调试需要修改 代码(hal_trace.h),使能ADUIO_DEBUG 开启后会自动打开 AUDIO_DUMP 

 开启通话调试后 ,系统会注册“Speech Tuning” ,根据收到的字符判断处理通话调试 同时波特率也会修改为“2000000

通话调试串口的数据格式为 “[Speech Tuning,\x01 ......  \x00]”

通话调试串口执行的 回调函数 :

 

 audio_developer 操作界面截图 :

 2.3 通话调试常见问题:

a.检查硬件连接 检查下波特率是否正确,串口调试必须使用UART0 连接 波特率为 200000

b. 写入参数无法生效

检查函数回调是否已经注册生效 命令"[1test,123]"

 检查发送数据长度 和代码本地长度是否匹配

如果长度不匹配,说明工具配置 和代码配置是不一样的 

log打印:[Speech Tuning] res : 1; info : Send len != config_size

3.BES (恒玄)HFP 通话代码部分说明

3.1 HFP启动机制原理

通话的启动在app_bt_stream.cpp 中,函数为

“int bt_sco_player(bool on, enum APP_SYSFREQ_FREQ_T freq)”

函数分为两个部分 一个是 上行处理  ,一部分是下行处理(连接codec输出)

上行:“bt_sco_codec_capture_data

下行:“bt_sco_codec_playback_data

 由于通话算法运行需要大量的数据处理运算,对于单核要求主频一般在104M 以上 双核大于52M

当耳机连接手机 ,并且手机启动通话状态后,会连接HFP_AUDIO,执行回调函数。

void app_hfp_event_callback(hf_chan_handle_t chan, struct hfp_context *ctx)

在函数 “hfp_audio_connected_handler(chan, ctx)” 会发送SCO 音频连接请求

然后这个最后会转到 并执行刚才上述介绍的“bt_sco_player” ,完成整个通话的启动。

3.2 通话算法运行处理

通话上行算法处理  函数接口:“int process_uplink_bt_voice_frames(uint8_t *in_buf, uint32_t in_len, uint8_t *ref_buf, uint32_t ref_len, uint8_t *out_buf,uint32_t out_len,int32_t codec_type)” 

 BES 默认通话链路:

 通话下行处理函数接口:

int process_downlink_bt_voice_frames(uint8_t *in_buf, uint32_t in_len, uint8_t *out_buf,uint32_t out_len,int32_t codec_type)

 值得说一下的是 不管是通话上行还是下行 数据都在走的DMA 通道 并且是在中断函数处理的 

3.3 第三方算法移植 :

这一块现在BES 代码有做规划,在makefile里面会根据宏定义编译不同的cpp和连接不同的.a静态链接库

 然后在代码里面也有不同的区分处理  ,bes 采用ARM 内核支持  硬浮点运算。

3.4 通话常见软件问题 分析解决:

3.4.1 通话无声音:

 a.检查mic通道是否匹配 ,运行时候是否有上电。

mic 的数据通道定义:

  

 b. MIC 的ADC gain值是否设置过小

 c.通话上行数据是否被阻断(mute 等)

在上行数据传输的处理函数(bt_sco_codec_capture_data)中有对 通话mute 的操作 :

3.5  BES 通话数据分析处理

3.5.1 上行数据常见处理:

这个问题我们需要先回到关于 mic channel的定义处,这个定义需要和硬件匹配:

 mic所采集的数据会根据通道数排列依次存放在PCM_BUF 里,比如上面这样的定义 ,在处理函数中:

 BES 默认 通话主mic处理在最小的通道(一般是channel 0 ,有定义的话)   所以 这样的硬件需要在软件上修改 来适应硬件搭配,可以插入以下代码对两个通道数据做交换(如果是3通道也是采用同样的方法处理) :

int process_uplink_bt_voice_frames(uint8_t *in_buf, uint32_t in_len, uint8_t *ref_buf, uint32_t ref_len, uint8_t *out_buf,uint32_t out_len,int32_t codec_type){

#if defined(SPEECH_TX_24BIT)
    int32_t *pcm_buf = (int32_t *)in_buf;
    int pcm_len = in_len / sizeof(int32_t);  
#else
    int16_t *pcm_buf = (int16_t *)in_buf;
    int pcm_len = in_len / sizeof(int16_t);
#endif
     .........
     .........

#if defined(SPEECH_TX_2MIC_SWAP_CHANNELS)
	  //TRACE(2,"pcm f %d,%d",pcm_buf[0],pcm_buf[1]);
	  for (int i = 0; i < (pcm_len-1); i += 2) {        
		pcm_buf[i]=pcm_buf[i]^pcm_buf[i + 1];
		pcm_buf[i + 1]=pcm_buf[i + 1]^pcm_buf[i];
		pcm_buf[i]=pcm_buf[i]^pcm_buf[i + 1];   
	}  
	 //TRACE(2,"pcm h %d,%d",pcm_buf[0],pcm_buf[1]);
#endif
   speech_tx_process(pcm_buf, aec_echo_buf, &pcm_len);
    ..........
    ..........
}

知晓通话上行数据处理机制后 ,可以简单修改 达到测试单个mic性能的软件 :

/**Modify the uplink handler**/

int process_uplink_bt_voice_frames(uint8_t *in_buf, uint32_t in_len, uint8_t *ref_buf, uint32_t ref_len, uint8_t *out_buf,uint32_t out_len,int32_t codec_type){
    #if defined(SPEECH_TX_24BIT)
    int32_t *pcm_buf = (int32_t *)in_buf;
    int pcm_len = in_len / sizeof(int32_t);  
#else
    int16_t *pcm_buf = (int16_t *)in_buf;
    int pcm_len = in_len / sizeof(int16_t);
#endif

#if defined(SPEECH_TX_AEC_CODEC_REF)
    ASSERT(pcm_len % (SPEECH_CODEC_CAPTURE_CHANNEL_NUM + 1) == 0, "[%s] pcm_len(%d) should be divided by %d", __FUNCTION__, pcm_len, SPEECH_CODEC_CAPTURE_CHANNEL_NUM + 1);
    // copy reference buffer
#if defined(SPEECH_TX_AEC) || defined(SPEECH_TX_AEC2) || defined(SPEECH_TX_AEC3) || defined(SPEECH_TX_AEC2FLOAT) || defined(SPEECH_TX_THIRDPARTY)
    for (int i = SPEECH_CODEC_CAPTURE_CHANNEL_NUM, j = 0; i < pcm_len; i += SPEECH_CODEC_CAPTURE_CHANNEL_NUM + 1, j++) {
        aec_echo_buf[j] = pcm_buf[i];
    }
#endif
    for (int i = 0, j = 0; i < pcm_len; i += SPEECH_CODEC_CAPTURE_CHANNEL_NUM + 1, j += SPEECH_CODEC_CAPTURE_CHANNEL_NUM) {
        for (int k = 0; k < SPEECH_CODEC_CAPTURE_CHANNEL_NUM; k++)
            pcm_buf[j + k] = pcm_buf[i + k];
    }
    pcm_len = pcm_len / (SPEECH_CODEC_CAPTURE_CHANNEL_NUM + 1) * SPEECH_CODEC_CAPTURE_CHANNEL_NUM;
#endif

#if defined(__PRODUCT_LINE_TEST__)
    if(app_test_mic_channel)   /**Define variables. When app_test_mic_channel =0, it is in normal mode and all mics are in normal working state  **/
    {
        uint8_t k = app_test_mic_channel - 1;
        pcm_len = pcm_len>>1;  /**After the two-channel data processing is completed, only the data length of mic needed to be tested needs to be uploaded, so the length needs to be halved,If it's three channels, there need pcm_len/3.**/
        for(int i=0;i<pcm_len;i++){
            pcm_buf[i] = pcm_buf[2*i+k];  /*CH0 FF_ref_MIC, CH2 TALK mic*/
        }
    }
    else
#endif		
    {  /**normal handler**/
#if defined(SPEECH_TX_2MIC_SWAP_CHANNELS)
	  //TRACE(2,"pcm f %d,%d",pcm_buf[0],pcm_buf[1]);
	  for (int i = 0; i < (pcm_len-1); i += 2) {        
		pcm_buf[i]=pcm_buf[i]^pcm_buf[i + 1];
		pcm_buf[i + 1]=pcm_buf[i + 1]^pcm_buf[i];
		pcm_buf[i]=pcm_buf[i]^pcm_buf[i + 1];   
	}  
	 //TRACE(2,"pcm dat %d,%d",pcm_buf[0],pcm_buf[1]);
#endif
        speech_tx_process(pcm_buf, aec_echo_buf, &pcm_len);
    }
     ..............
     ..............
}

/**Add mic test entrance of production line **/
#if defined(__PRODUCT_LINE_TEST__)
void app_cmd_mic_lookback_on(u8 channel_num)
{
	switch(channel_num){
		case CH0_CHANNEL:  /* mic1 test  FF*/
		cfg_audio_input_path_cfg[0].cfg = (AUD_CHANNEL_MAP_CH0 | AUD_CHANNEL_MAP_CH2|AUD_CHANNEL_MAP_ECMIC_CH0 | AUD_VMIC_MAP_VMIC1);        
		app_test_mic_channel = 1;    
		break;

		case CH1_CHANNEL:  /* mic2 test  FB*/
		cfg_audio_input_path_cfg[0].cfg = (AUD_CHANNEL_MAP_CH1 | AUD_CHANNEL_MAP_CH2 | AUD_CHANNEL_MAP_ECMIC_CH0|AUD_VMIC_MAP_VMIC1);        //ch1
		app_test_mic_channel = 1;    
		break;

		case CH2_CHANNEL:/* mic3 test  TALK */
		cfg_audio_input_path_cfg[0].cfg = (AUD_CHANNEL_MAP_CH1  |AUD_CHANNEL_MAP_CH2|AUD_CHANNEL_MAP_ECMIC_CH0|AUD_VMIC_MAP_VMIC2);       //ch2 
		app_test_mic_channel = 2;    
		break;

		default:
		cfg_audio_input_path_cfg[0].cfg = (AUD_CHANNEL_MAP_CH0 | AUD_CHANNEL_MAP_CH2 |AUD_CHANNEL_MAP_ECMIC_CH0| AUD_VMIC_MAP_VMIC1|AUD_VMIC_MAP_VMIC2);        
		app_test_mic_channel = 0;  
		break;
	}
	TRACE(0,"app_cmd_mic_lookback_on,channel_num=%d",channel_num);
	app_audio_sendrequest(APP_BT_STREAM_HFP_PCM, (uint8_t)APP_BT_SETTING_RESTART, 0);
}
#endif

 3.5.2 下行通话数据处理

    下行数据就是speak输出,常见两种处理 就是mute 或者 改变speak的增益。通常情况是需要减少增益 压制通话回声问题。

Mute操作:

修改speak 输出增益:

a. 利用BES资源 ,打开 SPEECH_RX_POST_GAIN:=1 然后在代码里面修改增益

b.对于增益要求不是很严格的 采用下面的方式可以节省资源:

 c. 也可以设置不同的音量表在 codec 端处理 : 这些方式都可以实现speak端通话音量的改变。

3.6  MIPS KEY的计算方式

在移植通话算法 或者开启自带通话算法时候,需要评估mips ,从而得出合理的运算主频 

 

  4  总结

1.audio_developer 调试: 

SDK 需要打开某个算法,需要原厂打开对应宏定义编译出新的SDK,只要涉及到lib库里的功能都需要原厂手动修改编译。开发商和原厂FAE做起来都会比较累。如果没有原厂支持根本没法做,很少文档,SDK也很多问题,没法开箱即用
如果要支持audio_developer上位机调试,还需改代码,如果对整个原理不清楚就没法做,没文档只能啃代码
调试工具设计的也有待加强,串口通讯调试对耳机这类产品非常麻烦,本来板子就不大,还要焊接一堆线,调试还要拖着串口线。其他蓝牙芯片早就是SPP/BLE 无线调试了,直接装好成品调试

由于上述缺陷和原厂支援问题 ,在通话算法方面,原厂也会推荐使用第三方算法 (声加/大象/思必驰 等)。

2. BES HFP 通话机制和原理 

 这方面也需要实际做项目 才能发现更多的问题,了解机制的情况下 ,能够帮助工程师快速定位问题 ,简化开发流程。
 

  • 12
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值