为了让CSR867x的开发更容易,现与思度科技联合推出CSR867x学习板【淘宝链接:思度科技CSR开发板】。
技术交流QQ群号:743434463
开发板会员QQ群号:725398389(凭订单号入群,赠PPT、项目源码、视频教程)
——————————正文分割线———————————–
1. 引言
熟悉CSR867X芯片方案的开发人员一定对UFE工具印象深刻。UFE(universal front end)工具是ADK自带的声学调音工具,能够满足音乐播放、免提通话等场景的声学调音的一般需求。其音乐播放调音界面如下:
从上图可以看出,音频数据从右侧天线输入,其音频编码格式为APTX_HD,采样率44.1k,采样精度24位。在经过Decoder解码器解码后转成双声道的PCM格式音频数据,经过Compander(压扩器)、User EQ(用户均衡器)、Bass Enhancement(低音增强)、Speaker EQ(扬声器均衡器)、Stereo Enhancement(立体声增强)、Crossover(分频器)、Main Volume(主音量控制)等模块的处理后通过模拟通道输出。其中除了Main Volume和Crossover模块以外,所有模块对音频信号的处理都是不区分左右通道的,例如Speaker EQ模块:
可以看到Speaker EQ模块有10个点的滤波器,每个滤波器的参数有Gain(增益)、Freq(频率)、Q值(滤波器品质因数)和带宽(一个计算值,不能设置)。这10个滤波器的参数会同时作用在左右声道。
本文给出了一种支持左右声道各10个Speaker EQ的方案,并给出一个基于matlab GUI的调音工具设计。
2. 主要功能
- 左右通道各10个Speaker EQ(支持任何蓝牙音频解码格式、任何音源)
- 可在VM层读写每个Speaker EQ的每个参数(滤波器类型、增益、频率、品质因数)
- 可通过matlab GUI实时调节每个Speaker EQ的每个参数
修改后的系统框图如下:
3. 项目难点
3.1. 改造音频链路
基本思路是,在现有音频链路的基础上,将整个Speaker EQ模块改为左通道独享,然后拷贝一个完整的Speaker EQ模块单独给右通道使用。首先创建修改原有的speaker eq的数组定义,使其只处理左通道数据:
// music_example_config.asm
.VAR SpkrEqDefnTable[$user_eq.DEFINITION_TABLE_SIZE] =
MAX_NUM_SPKR_EQ_BANKS,
MAX_NUM_SPKR_EQ_STAGES,
&spkr_eq_left_dm2,
&spkr_eq_left_dm2,
&SpkrEqCoefsA,
&SpkrEqCoefsB,
&$audio_proc.hq_peq.initialize,
&$audio_proc.hq_peq.zero_delay_data,
&$audio_proc.hq_peq.process;
接着创建一个新的R speaker eq数组定义,其只处理右通道的数据:
// music_example_config.asm
.VAR SpkrEqDefnTable2[$user_eq.DEFINITION_TABLE_SIZE] =
MAX_NUM_SPKR_EQ_BANKS,
MAX_NUM_SPKR_EQ_STAGES,
&spkr_eq_right_dm2,
&spkr_eq_right_dm2,
&SpkrEqCoefsC,
&SpkrEqCoefsD,
&$audio_proc.hq_peq.initialize,
&$audio_proc.hq_peq.zero_delay_data,
&$audio_proc.hq_peq.process;
然后为R speaker eq创建滤波器系数存储空间,并将此系数关联到右通道的音频数据流:
// music_example_config.asm
.VAR/DM2 spkr_eq_right_dm2[MAX_SPKR_EQ_OBJECT_SIZE] =
&stream_map_right_in, // PTR_INPUT_DATA_BUFF_FIELD
&stream_map_right_in, // PTR_OUTPUT_DATA_BUFF_FIELD
MAX_NUM_SPKR_EQ_STAGES, // MAX_STAGES_FIELD
&SpkrEqCoefsC, // PARAM_PTR_FIELD
0 ...;
.VAR SpkrEqCoefsC[3+6*MAX_NUM_SPKR_EQ_STAGES] =
$spkrEq.Fs44.NumBands,
$spkrEq.Fs44.GainExp,
$spkrEq.Fs44.GainMant,
$spkrEq.Fs44.Stage1.b2, $spkrEq.Fs44.Stage1.b1, $spkrEq.Fs44.Stage1.b0, $spkrEq.Fs44.Stage1.a2, $spkrEq.Fs44.Stage1.a1,
$spkrEq.Fs44.Stage2.b2, $spkrEq.Fs44.Stage2.b1, $spkrEq.Fs44.Stage2.b0, $spkrEq.Fs44.Stage2.a2, $spkrEq.Fs44.Stage2.a1,
$spkrEq.Fs44.Stage3.b2, $spkrEq.Fs44.Stage3.b1, $spkrEq.Fs44.Stage3.b0, $spkrEq.Fs44.Stage3.a2, $spkrEq.Fs44.Stage3.a1,
$spkrEq.Fs44.Stage4.b2, $spkrEq.Fs44.Stage4.b1, $spkrEq.Fs44.Stage4.b0, $spkrEq.Fs44.Stage4.a2, $spkrEq.Fs44.Stage4.a1,
$spkrEq.Fs44.Stage5.b2, $spkrEq.Fs44.Stage5.b1, $spkrEq.Fs44.Stage5.b0, $spkrEq.Fs44.Stage5.a2, $spkrEq.Fs44.Stage5.a1,
$spkrEq.Fs44.Stage6.b2, $spkrEq.Fs44.Stage6.b1, $spkrEq.Fs44.Stage6.b0, $spkrEq.Fs44.Stage6.a2, $spkrEq.Fs44.Stage6.a1,
$spkrEq.Fs44.Stage7.b2, $spkrEq.Fs44.Stage7.b1, $spkrEq.Fs44.Stage7.b0, $spkrEq.Fs44.Stage7.a2, $spkrEq.Fs44.Stage7.a1,
$spkrEq.Fs44.Stage8.b2, $spkrEq.Fs44.Stage8.b1, $spkrEq.Fs44.Stage8.b0, $spkrEq.Fs44.Stage8.a2, $spkrEq.Fs44.Stage8.a1,
$spkrEq.Fs44.Stage9.b2, $spkrEq.Fs44.Stage9.b1, $spkrEq.Fs44.Stage9.b0, $spkrEq.Fs44.Stage9.a2, $spkrEq.Fs44.Stage9.a1,
$spkrEq.Fs44.Stage10.b2, $spkrEq.Fs44.Stage10.b1, $spkrEq.Fs44.Stage10.b0, $spkrEq.Fs44.Stage10.a2, $spkrEq.Fs44.Stage10.a1,
$spkrEq.Fs44.Stage1.scale, $spkrEq.Fs44.Stage2.scale, $spkrEq.Fs44.Stage3.scale, $spkrEq.Fs44.Stage4.scale, $spkrEq.Fs44.Stage5.scale,
$spkrEq.Fs44.Stage6.scale, $spkrEq.Fs44.Stage7.scale, $spkrEq.Fs44.Stage8.scale, $spkrEq.Fs44.Stage9.scale, $spkrEq.Fs44.Stage10.scale;
// 48 kHz coefficients if not using coefficient calculation routines
.VAR SpkrEqCoefsD[3+6*MAX_NUM_SPKR_EQ_STAGES] =
$spkrEq.Fs48.NumBands,
$spkrEq.Fs48.GainExp,
$spkrEq.Fs48.GainMant,
$spkrEq.Fs48.Stage1.b2, $spkrEq.Fs48.Stage1.b1, $spkrEq.Fs48.Stage1.b0, $spkrEq.Fs48.Stage1.a2, $spkrEq.Fs48.Stage1.a1,
$spkrEq.Fs48