为了方便大家学习,现与思度科技联合推出QCC300x/CSR867x/QCC30xx/QCC51xx学习板【思度科技蓝牙开发板】。
技术交流QQ群号:743434463
开发板会员QQ群号:725398389(凭订单号入群,赠PPT、项目源码、视频教程)
——————————正文分割线———————————–
高通以CSR867X系列为基础,开发了下一代高性能蓝牙SOC系列QCC30XX/QCC51XX。新系列整体的硬软件能力都有了显著的提升,包括全新的音频仿真环境KSE。
从开始阅读KSE描述文档,到第一次跑通例程,再到分析定位仿真误差,经历了漫长的三个月。为了让大家少走一些弯路,将过程中遇到的问题和解决方案分享出来(文中例程和文档都可以在思度开发板QCC512X资料库中下载)。
官方介绍文档如下文档:
详细内容建议读者阅读原版手册,在此只抓几个重点概念简单讲一下。
首先我们需要了解KSE的系统框架,示意图如下:
对于算法移植工程师来说,KSE中的kymera API、Python工具集API和文件接口是需要重点关注的,kalsim对我们来说是个黑盒,不用过多关注。软件开发需要掌握C语言、汇编语言,熟悉matlab脚本、python脚本和json格式。
高通官方提供了loopback_download_test例程作为最简的算法移植验证环境,其音频链路框图如下:
官方文档给出了各个部分的详细描述,可以参考如下文档:
每个模块都有对应的文档,可见高通为了让开发者掌握这套新架构是投入了很多精力。建议多花时间在阅读上述文档,对理解和运用kymera有很大帮助。
跑通此例程的方法在文档“80-cg335-1_aa_kymera_simulation_environment_(kse)_training.pdf”中有详细描述,这里给出仿真的结果示意:
matlab脚本读取仿真的输入输出文件后,以坐标图形式显示,代码如下:
system('run_pass_through.bat > run_log.txt');
figure(1);
fd = fopen('.\resource\153_Prompts_176.4_kHz_Music_Detected_8k.raw','r');
data1 = fread(fd, 'int16');
subplot(2,1,1);plot(data1);xlabel('aud in');
fclose(fd);
fd = fopen('.\tmp\153_Prompts_176.4_kHz_Music_Detected_8k.raw','r');
data2 = fread(fd, 'int16');
subplot(2,1,2);plot(data2);xlabel('aud out');
fclose(fd);
官方例程中需要用MDE开发环境运行KSE,每次仿真都需手动执行连接仿真器等操作。在此给出一种运用windows bat脚本结合python脚本实现一键自动化仿真的方法。
首先需要按照官方文档运行一次MDE的例程loopback_download_test,目的是用MDE模板工程创建基础仿真工程。
然后我们可以抓取MDE部署仿真工程的命令行,以windows bat脚本的方式启动python KSE shell:
cmd.exe /C "D:\qtil\ADK_QCC512X_QCC302X_WIN_6.3.2.24\tools\python27\python.exe -m kse.kalsim.kalsim_shell --ks_path D:\qtil\ADK_QCC512X_QCC302X_WIN_6.3.2.24\tools\kalsim\kalsim_qcc512x_audio.exe --log_level 20 --ks_firmware E:\___Product\0_INP\TWSearphone\target\qcc512x\loopback_download_test\audio\kalimba\kymera\output\stre_rom_kalsim_kalcmd2_release\build\debugbin\kymera_stre_audio --acat_path D:\qtil\ADK_QCC512X_QCC302X_WIN_6.3.2.24\audio\kalimba\kymera\tools\ACAT\ACAT --acat_use --acat_bundle E:\___Product\0_INP\TWSearphone\target\qcc512x\loopback_download_test\audio\kalimba\kymera\output_bundles\stre_rom_kalsim_kalcmd2_release\self_test_and_passthrough\self_test_and_passthrough --ka_path D:\qtil\ADK_QCC512X_QCC302X_WIN_6.3.2.24\tools\pythontools --platform stre --hydra_ftp_server_directory E:\___Product\0_INP\TWSearphone\target\qcc512x\loopback_download_test\audio\kalimba\kymera\output\stre_rom_kalsim_kalcmd2_release\build\patchbin --script "script/kalsim/pass_through.py script/kalsim/self_test_and_passthrough.dkcs""
windows bat脚本的最后一行指明了KSE shell启动时自动加载的仿真脚本pass_through.py,其描述了kymera的输入输出关系、内部音频链路、各capability的设置等,代码如下:
import os
import time
import sys
import argparse
if __name__ == '__main__':
from kats.kalsim.stream.stream_base import CALLBACK_EOF
INPUT_FILE_RAW = 'resource/153_Prompts_176.4_kHz_Music_Detected_8k.raw'
OUTPUT_FILE_RAW = 'tmp/153_Prompts_176.4_kHz_Music_Detected_8k.raw'
print('removing output files')
try:
os.remove(OUTPUT_FILE_RAW)
except Exception:
pass
print('creating source stream')
config = {
'hydra_type': 'audio_slot',
'hydra_bac_handle': 0,
'hydra_audioslot': 0,
'stream_flow_control_drive': 'kalsim',
'stream_flow_control_block_size': 1,
'stream_format': 16,
'stream_backing': 'file',
'stream_filename': INPUT_FILE_RAW,
'stream_flow_control_rate': 8000,
}
st_source = stream.get_instance('hydra', 'source', **config)
st_source.create()
st_source.config()
print('creating sink stream')
config = {
'hydra_type': 'audio_slot',
'hydra_bac_handle': 0,
'hydra_audioslot': 0,
'stream_flow_control_drive': 'kalsim',
'stream_flow_control_block_size': 1,
'stream_format': 16,
'stream_backing': 'file',
'stream_filename': OUTPUT_FILE_RAW,
'stream_flow_control_rate': 8000,
}
st_sink = stream.get_instance('hydra', 'sink', **config)
st_sink.create()
st_sink.config()
print('creating endpoints')
ep_source = kymera.stream_if_get_source('pcm', [0, 0])
kymera.stream_if_configure_sid(ep_source, 'pcm_sample_rising_edge', 0)
kymera.stream_if_configure_sid(ep_source, 'pcm_master_clock_rate', 512000)
kymera.stream_if_configure_sid(ep_source, 'pcm_master_mode', 1)
kymera.stream_if_configure_sid(ep_source, 'pcm_sample_format', 1)
kymera.stream_if_configure_sid(ep_source, 'pcm_sync_rate', 8000)
kymera.stream_if_configure_sid(ep_source, 'pcm_slot_count', 4)
ep_sink = kymera.stream_if_get_sink('pcm', [0, 0])
print('creating pass through operator')
op = kymera.opmgr_create_operator(1)
kymera.opmgr_operator_message(op, [10, 1])
kymera.opmgr_operator_message(op, [11, 1])
print('connecting endpoints')
tr1 = kymera.stream_if_connect(ep_source, op | 0xa000)
tr2 = kymera.stream_if_connect(op | 0x2000, ep_sink)
print('start streaming')
st_sink.start()
kymera.opmgr_start_operators([op])
st_source.start()
for _ in range(12500):
if not st_source.check_active():
print('end of file detected')
break
time.sleep(0.005)
else:
raise RuntimeError('timeout streaming the file')
print('stop streaming')
st_source.stop()
st_source.destroy()
st_sink.stop()
st_sink.destroy()
print('release resources')
kymera.opmgr_stop_operators([op])
kymera.stream_if_transform_disconnect([tr1, tr2])
kymera.opmgr_destroy_operators([op])
kymera.stream_if_close_source(ep_source)
kymera.stream_if_close_sink(ep_sink)
exit()
kymera的音频链路可以图形化显示,需要安装python环境下的graphviz插件。安装完插件后,在上述py脚本中的print(‘start streaming’)语句前插入acat.run(),执行脚本后会需要提示输入指令,输入stream.create_graph_img():
生成html格式的graph描述文件,可以在浏览器打开,这里给出音频链路示意图:
loopback_download_test例程跑通后,我们需要验证KSE的仿真结果是否符合预期。此例程中的basic_pass_through capability可以看做简单的数据搬运,因此输入输出应该是bit-by-bit对应的。
回顾3.1章中给出的坐标图,肉眼可见的差异是输出相对于输入有很大的延迟。这里我的判断是KSE环境在运行时引入的延迟,即从打开输出文件,到写入仿真数据,这两个时间点之间有仿真环境的延迟。
为了能较方便地去除延迟对仿真结果结果判定的影响,我们改用白噪声作为输入信号,用find函数查询非0点的起始位置,使输入输出按样点对齐。
matlab脚本代码如下:
figure(1);
fd = fopen('.\resource\audio_in.raw','r');
data1 = fread(fd, 'int16');
data1 = data1(1:5000);
subplot(3,1,1);plot(data1);xlabel('aud in');
fclose(fd);
fd = fopen('.\tmp\audio_out.raw','r');
data2 = fread(fd, 'int16');
offset = find(data2~=0);
data2 = data2(1+offset(1)-1:5000+offset(1)-1);
subplot(3,1,2);plot(data2);xlabel('aud out');
fclose(fd);
data3=data1-data2;
subplot(3,1,3);plot(data3);xlabel('audio in - audio out');
对齐后的输入、输出、误差结果:
结果出乎意料,可以看到输入输出之间是有误差存在的。对误差信号进行频谱估计,可见其频率成分主要集中在低频:
这个问题困扰了我有近一个月,终于在文档“80-cg887-1_ab_kymera_simulation_environment_(kse)_user_guide.pdf”中找到了答案:
3.4.1 Hydra PCM Endpoints
On the data that is filled in their buffer PCM endpoints carry out two operations:
■ DC removal: To remove the DC component from the data on a moving window basis.
■ Rate matching: To adjust the incoming sample rate to the rate that is specified in the endpoint
configuration.
These two operations can be controlled by a configuration key named
audio_disable_endpoint_processing. A value of 0x0000 disables both functionalities and
0x0001 disables DC removal.
原来hydra PCM类型的endpoint本身隐含了DC removal和Rate matching两个功能,即去除信号中的直流成分和采样率转换。对照上文中提到的误差信号的频率成分主要是低频,猜想消除误差的方式应是关闭DC removal功能。
按照文档给出的方法在pass_through.py脚本中的"create endpoint"段插入如下代码:
kymera.stream_if_configure_sid(ep_source, 'audio_disable_endpoint_processing', 1)
ep_sink = kymera.stream_if_get_sink('pcm', [0, 0])
kymera.stream_if_configure_sid(ep_sink, 'audio_disable_endpoint_processing', 1)
修改accmd.py脚本,添加“audio_disable_endpoint_processing”命令:
# available to all interfaces
'audio_channel_mute_enable': 0x0700,
'audio_sample_size': 0x0701,
'audio_disable_endpoint_processing': 0x0710,
重新运行KSE,再用matlab脚本比对结果,误差消除:
至此一个高效并可靠的仿真环境已搭建完毕,可以在此基础上开始算法移植工作。