AUDIO相关应用
📎 原始文档:https://www.kdocs.cn/l/cnOtPP7mbZrW AUDIO相关应用
一、通用函数
- 正弦波生成函数 a.固定数据
const s16 tsin_48k[48] = {
0, 2139, 4240, 6270, 8192, 9974, 11585, 12998,
14189, 15137, 15826, 16244, 16384, 16244, 15826, 15137,
14189, 12998, 11585, 9974, 8192, 6270, 4240, 2139,
0, -2139, -4240, -6270, -8192, -9974, -11585, -12998,
-14189, -15137, -15826, -16244, -16384, -16244, -15826, -15137,
-14189, -12998, -11585, -9974, -8192, -6270, -4240, -2139
};
int get_sine_data(u16 *s_cnt, s16 *data, u16 points, u8 ch)
{
while (points--) {
if (*s_cnt >= 48) {
*s_cnt = 0;
}
*data++ = tsin_48k[*s_cnt];
if (ch == 2) {
*data++ = tsin_48k[*s_cnt];
}
(*s_cnt)++;
}
return 0;
}
/* test */
static u16 user_cnt;
get_sine_data(&user_cnt,数据buff,长度,多少通道);
- 代码生成一个周期的频点数据
初始化接口:
- 如果只是一个正弦波数据使用generate_sine_wave
- 如果需要生成多个正弦波数据使用generate_sine_wave_ret接口并保存返回结构体
获取数据接口:
- 如果只是一个正弦波数据使用sine_data_get
- 如果需要获取多个正弦波数据使用generate_sine_wave_ret接口并保存返回结构体
#include <stdio.h>
#include <math.h>
typedef struct _SINE_WAVE_DATE_ {
s16 *data;//数据地址
int len;//数据长度
int offset;//取数的偏移量
}sine;
static sine wave={.data=NULL,.len=0,.offset=0};
/*
* 获取已知正弦波数据
返回:获取到的数据,每调用一次偏移地址加一
*/
s16 sine_data_addr_get(sine *addr){
if(!addr||!addr->data||addr->len<=0){
return 0;
}
addr->offset=(addr->offset+1)%addr->len;
return addr->data[addr->offset];
}
/*
* 生成正弦波数据
*/
sine generate_sine_wave_ret(double sample_rate/* 采样率 */, double frequency/* 正弦波频率 */) {
sine data={.data=NULL,.len=0,.offset=0};
// 时间轴增量
double t_increment = 1.0 / sample_rate;
// 每个周期的样本数
int num_samples=sample_rate/frequency;
data.data=zalloc(sizeof(s16)*num_samples);
if(!data.data)return data;
data.len = num_samples;
// 生成正弦波数据
for (int i = 0; i < num_samples; i++) {
double t = i * t_increment;
data.data[i] = (s16)(32767 *sin(2 * M_PI * frequency * t));
}
// 打印样本点作为验证
printf("样本点:\n");
for (int i = 0;i < num_samples; i++) {
// printf("sample %d: %f\n", i, data[i]);
printf("[i=%d %d]\r\n",i, data.data[i]);
}
return data;
}
void generate_sine_free_addr(sine *addr){
if(!addr||!addr->data){
return ;
}
addr->len=0;
free(addr->data);
addr->data=NULL;
return;
}
/*
* 获取已知正弦波数据
返回:获取到的数据,每调用一次偏移地址加一
*/
s16 sine_data_get(void){
return sine_data_addr_get(&wave);
}
/*
* 生成正弦波数据
*/
void generate_sine_wave(double sample_rate/* 采样率 */, double frequency/* 正弦波频率 */){
if(wave.data)return;
wave = generate_sine_wave_ret(sample_rate,frequency);
}
void generate_sine_free(void){
generate_sine_free_addr(&wave);
}
- 立体声的左右声道对调
void pcm_lr_exchange(void *data, u32 len)
{
s32 valuetemp;
s16 *buf;
buf = data;
len >>= 1; //byte to point
for (u32 i = 0; i < len; i += 2) {
valuetemp = buf[i];
buf[i] = buf[i+1];
buf[i+1] = valuetemp;
}
}
- 立体声的反相处理
void pcm_phase(void *data, u32 len)
{
s32 valuetemp;
s16 *buf;
buf = data;
len >>= 1; //byte to point
for (u32 i = 0; i < len; i += 2) {
buf[i] = (buf[i] == -32768) ? 32767 : -dat[i]; // 左声道
buf[i+1] = (buf[i+1] == -32768) ? 32767 : -data[i+1]; // 右声道
}
}
- 立体声的数字音量处理
16bit数据数字音量处理
//单/双声道数字音量处理。用于TWS时l_vol、r_vol设置一样即可
// vol:0~16384(大于16384会把数据放大,有可能出现失真)
int audio_digital_vol_run(void *data, u32 len, u32 l_vol,u32 r_vol)
{
s32 valuetemp;
s16 *buf;
buf = data;
len >>= 1; //byte to point
for (u32 i = 0; i < len; i += 2) {
///left channel
valuetemp = buf[i];
if (valuetemp < 0) {
valuetemp = -valuetemp;
valuetemp = (valuetemp * l_vol) >> 14 ;
valuetemp = -valuetemp;
} else {
valuetemp = (valuetemp * l_vol) >> 14 ;
}
if (valuetemp < -32768) {
valuetemp = -32768;
} else if (valuetemp > 32767) {
valuetemp = 32767;
}
buf[i] = (s16)valuetemp;
///right channel
valuetemp = buf[i + 1];
if (valuetemp < 0) {
valuetemp = -valuetemp;
valuetemp = (valuetemp * r_vol) >> 14 ;
valuetemp = -valuetemp;
} else {
valuetemp = (valuetemp * r_vol) >> 14 ;
}
if (valuetemp < -32768) {
valuetemp = -32768;
} else if (valuetemp > 32767) {
valuetemp = 32767;
}
buf[i + 1] = (s16)valuetemp;
}
return 0;
}
24bit数据数字音量处理
//单/双声道数字音量处理
// vol:0~16384(大于16384会把数据放大,有可能出现失真)
int audio_digital_vol_run32(void *data, u32 len, u32 l_vol,u32 r_vol)
{
s64 valuetemp;
s32 *buf;
buf = data;
len >>= 2; //byte to point
for (u32 i = 0; i < len; i += 2) {
///left channel
valuetemp = buf[i];
if (valuetemp < 0) {
valuetemp = -valuetemp;
valuetemp = (valuetemp * l_vol) >> 14 ;
valuetemp = -valuetemp;
} else {
valuetemp = (valuetemp * l_vol) >> 14 ;
}
if (valuetemp < -8388608) {
valuetemp = -8388608;
} else if (valuetemp > 8388607) {
valuetemp = 8388607;
}
buf[i] = (s32)valuetemp;
///right channel
valuetemp = buf[i + 1];
if (valuetemp < 0) {
valuetemp = -valuetemp;
valuetemp = (valuetemp * r_vol) >> 14 ;
valuetemp = -valuetemp;
} else {
valuetemp = (valuetemp * r_vol) >> 14 ;
}
if (valuetemp < -8388608) {
valuetemp = -8388608;
} else if (valuetemp > 8388607) {
valuetemp = 8388607;
}
buf[i + 1] = (s32)valuetemp;
}
return 0;
}
- 通用能量值计算(取平均值)
#define ABS(x) (x > 0 ? x : (-x))
int audio_output_data_db_calc_simple(short *data, unsigned short len,unsigned char channels)
{
//长度转换,如果长度是u8 需要/2
unsigned short points = len / 2;
unsigned short user_sample_rate = 16000;
signed int calc_point = user_sample_rate/50;
static signed int data_cnt = 0;
static signed int sum[4] = {0,0,0,0};
static int average_value;
int index = 0;
for(index = 0; index < points; index+=channels) {
//计算数值总和
switch (channels) {
case 4:
sum[3] += ABS(data[index+3]);
case 3:
sum[2] += ABS(data[index+2]);
case 2:
sum[1] += ABS(data[index+1]);
case 1:
sum[0] += ABS(data[index]);
break;
default:
sum[0] += ABS(data[index]);
break;
}
data_cnt++;
if(data_cnt>=calc_point) {
//计算各通道取平均
average_value= (sum[0]+sum[1]+sum[2]+sum[3])/(channels*calc_point);
//printf("average_value:%d\n",average_value);
sum[0] = 0;
sum[1] = 0;
sum[2] = 0;
sum[3] = 0;
data_cnt = 0;
}
}
return average_value;
}
- 通用能量值计算(取最大最小值)
//目前只实现计算单通道
int audio_data_automute(s16 *data, u16 len, u8 channels)
{
//参数定义
#define CALC_DATA_SUM 16000 //多长时间更新一次数据
u16 points = len / 2;
u16 index;
static s32 min = 0xfffff;
static s32 max = -100000;
static s32 cnt_sum = 0;
s32 db = 0;
for (index = 0; index < points; index += channels) {
db = data[index];
cnt_sum++;
if (cnt_sum > CALC_DATA_SUM) {
printf("update : %d\t%d\n",max,min);
cnt_sum = 0;
min = 0xfffff;
max = -100000;
}
if (min > db)
min = db;
if (max < db)
max = db;
}
return max;
}
- 立体声(双声道)音频混合
u32 pcm_mix(void *data, u32 len)
{
s32 valuetemp;
s16 *buf;
buf = data;
len >>= 2; //byte to point
for (u32 i = 0; i < len; i++) {
valuetemp = (buf[2*i] + buf[2*i+1])/2;
buf[i] = valuetemp;
}
return len*2;
}
二、频响(频谱)接口函数
频响:当前声音下每个频点的声音大小
- 开启板级配置文件的功能
- 频响配置后,开机就会打开,位置:
- 参数设置
- 数据打印处: 单位数字 db,最大值不到 90
注意点:以上获取频响都是获取DAC的数据去做的,在linein走模拟信号输入等情况时,会因为拿不到数据等情况,获取不了频响。
- 不同采样率对应第n个数据对应的频率 比如:48K采样率下第0个数据为123.686951hz的声音大小
【金山文档】 频谱显示中心频率点
频谱显示中心频率点.xlsx - 用户自定义数据流的频响获取可参考下文此处 【金山文档】 AUDIO相关应用
AUDIO相关应用.otl
三、音量控制
音量控制的核心原理
音量的本质是音频信号的幅度(电压 / 采样值)大小—— 信号幅度越大,音量越大。
- 一般我们音频信号流程:数字音频文件(MP3/蓝牙等) → 解码器解码 → 数字信号处理(音量控制、EQ、DRC等音效处理) → 硬件数字音量控制 → DAC数模转换 → 硬件模拟音量控制 → 功率放大器 → 喇叭 一般SDK 提供的两种音量类型(硬件 / 软件),对应不同的信号处理阶段和实现方式。
1. 硬件方式:硬件模拟音量(TYPE_DAC_AGAIN)
原理:模拟信号放大 / 衰减
核心实现:通过硬件内部的模拟运算放大器(运放) 调节增益。比如增益值 0 ~30,对应运放的放大倍数梯度。
2. 硬件方式:硬件数字音量(TYPE_DAC_DGAIN)
原理:数字信号幅度缩放(硬件模拟之前)
- 核心实现:通过硬件的数字信号处理器(DSP) 对解码后的数字采样值进行乘法运算。比如 14 位精度(0~16384),本质是 “采样值 × 缩放系数”(例:采样值 = 2048,缩放系数 = 0.5 → 输出 1024,音量减半)。
3. 软件方式:软件数字音量
原理:软件音量控制是解码后、硬件数字音量处理前的数字信号缩放运算,通过 CPU/DSP 的软件算法,直接修改音频 PCM 采样值的幅度,从而改变用户感知的音量大小。
核心实现方式:音频信号解码后输出PCM 采样值(如 16 位精度,范围 - 32768~32767),软件音量的核心是对该采样值做 “乘法运算”:
如前面的数字音量处理:
处理后采样值 = 原始采样值 × 软件音量缩放系数
- 缩放系数:映射用户感知的 “0~100% 音量”,例如:
- 音量 100% → 系数 = 1.0(采样值不变);
- 音量 50% → 系数 = 0.5(采样值减半,音量降低);
- 音量 0%(静音) → 系数 = 0.0(采样值置 0)。
软件数字音量与硬件数字音量的核心差异
| 对比维度 | 软件音量控制 | 硬件数字音量 |
|---|---|---|
| 实现载体 | CPU/DSP 软件算法(纯软件) | 硬件 DSP 模块(专用硬件) |
| 调节精度 | 理论无上限(支持浮点运算) | 硬件限制(如 14 位 = 16384 级) |
| 灵活度 | 极高(支持复杂算法、自定义逻辑) | 有限(仅支持基础缩放) |
| 资源占用 | 占用 CPU/DSP 算力 | 无额外算力消耗 |
| 延迟 | 轻微(软件运算耗时) | 无延迟(硬件并行处理) |
| 核心作用 | 用户交互适配、复杂逻辑实现 | 底层精准执行、独立通道硬件加速 |
1、音量调节接口
- 音箱SDK
- 硬件音量控制(硬件数字,硬件模拟)
// type: TYPE_DAC_AGAIN->模拟音量 TYPE_DAC_DGAIN->硬件数字音量
// ch : BIT(0):FL BIT(1):FR BIT(2):RL BIT(3):RR
// gain: 模拟音量:一般(0~30) 数字音量(0~16384)
int audio_dac_vol_set(u8 type, u32 ch, u16 gain, u8 fade_en);
//设置左声道 模拟音量
audio_dac_vol_set(TYPE_DAC_AGAIN, BIT(0), gain_l, fade);
//设置右声道 模拟音量
audio_dac_vol_set(TYPE_DAC_AGAIN, BIT(1), gain_r, fade);
//设置左声道 数字音量
audio_dac_vol_set(TYPE_DAC_DGAIN, BIT(0), gain_l ? DEFAULT_DIGTAL_VOLUME : 0, fade);
//设置右声道 数字音量
audio_dac_vol_set(TYPE_DAC_DGAIN, BIT(1), gain_r ? DEFAULT_DIGTAL_VOLUME : 0, fade);
- 软件数字音量控制(独立通道音量调节实现)
每个解码通道独立音量
//每个解码通道都开启数字音量管理,音量类型为VOL_TYPE_DIGGROUP时要使能
#define SYS_DIGVOL_GROUP_EN 1//DISABLE
#define SYS_VOL_TYPE VOL_TYPE_DIGGROUP
- 实际上使能了独立通道音量调节:会在开机的时候固定设置模拟音量跟数字音量
jl701n
音乐音量 观察每个通道的音量可以打开以下打印 - 实际应用
- 实现任意音量等级调节—— LZK 步骤1:board文件选择,独立通道数字音量。
步骤2:生成一个数字音量表替换默认的音量表
(生成音量表方法可以参考:“数字音量表生成方法” )
- 四声道音量独立调节
示范代码如下:
//变量
#ifdef USER_FOUR_DAC_VOL_CTL_EN
void *four_dac_digvol_last = NULL;
void *four_dac_digvol_last_entry = NULL;
#endif
//添加的节点
#ifdef USER_FOUR_DAC_VOL_CTL_EN
audio_dig_vol_param four_digvol_last_param = {
.vol_start = app_var.music_volume,
.vol_max = SYS_MAX_VOL,
.ch_total = ch_num,
.fade_en = 1,
.fade_points_step = 5,
.fade_gain_step = 10,
.vol_list = NULL,
};
four_dac_digvol_last = audio_dig_vol_open(&four_digvol_last_param);
four_dac_digvol_last_entry = audio_dig_vol_entry_get(four_dac_digvol_last);
entries[entry_cnt++] = four_dac_digvol_last_entry;
#endif
//测试代码
#ifdef USER_FOUR_DAC_VOL_CTL_EN
case KEY_VOL_RR_VOL_TEST:
{
static u8 vol = 0;
vol++;
if(vol>30)vol = 0;
printf("RR VOL %d\n",vol);
extern void *four_dac_digvol_last;
audio_dig_vol_set(four_dac_digvol_last, AUDIO_DIG_VOL_CH(3),vol);
// audio_dig_vol_set(audio_dig_vol_group_hdl_get(sys_digvol_group, "music_file"), AUDIO_DIG_VOL_CH(3), vol);
}
break;
case KEY_VOL_RL_VOL_TEST:
{
static u8 vol = 0;
vol++;
if(vol>30)vol = 0;
printf("RL VOL %d\n",vol);
extern void *four_dac_digvol_last;
audio_dig_vol_set(four_dac_digvol_last, AUDIO_DIG_VOL_CH(2),vol);
// audio_dig_vol_set(audio_dig_vol_group_hdl_get(sys_digvol_group, "music_file"), AUDIO_DIG_VOL_CH(2), vol);
}
break;
case KEY_VOL_FR_VOL_TEST:
{
static u8 vol = 0;
vol++;
if(vol>30)vol = 0;
printf("FR VOL %d\n",vol);
extern void *four_dac_digvol_last;
audio_dig_vol_set(four_dac_digvol_last, AUDIO_DIG_VOL_CH(1),vol);
// audio_dig_vol_set(audio_dig_vol_group_hdl_get(sys_digvol_group, "music_file"), AUDIO_DIG_VOL_CH(1), vol);
}
break;
case KEY_VOL_FL_VOL_TEST:
{
static u8 vol = 0;
vol++;
if(vol>30)vol = 0;
printf("FL VOL %d\n",vol);
extern void *four_dac_digvol_last;
audio_dig_vol_set(four_dac_digvol_last, AUDIO_DIG_VOL_CH(0),vol);
// audio_dig_vol_set(audio_dig_vol_group_hdl_get(sys_digvol_group, "music_file"), AUDIO_DIG_VOL_CH(0), vol);
}
break;
#endif
2、音量级db值修改 --WTS
如果使用硬件模拟音量调节,音量级对应DB值达不到需求。可以选择使用数字音量或联合音量调节,然后到配置工具中设置每个
音量等级的db值,生产的音量表替换程序中的表:

(操作可以看以下第三点:“数字音量表生成方法”参考)
替换到程序中数字音量表:
替换到程序中的联合音量表:

3、数字音量表生成方法
4、进FM模式不改变当前系统音量等级的情况下降低整体音量
此方法适用的前提条件:
1. 使用的是模拟音量 2. FM模式下没有eq 原理:通过降低数字音量实现整体音量的降低
demo:http://192.168.1.245/soundbox/ac695n-soundbox-sdk/-/tree/FM_DOW_VOL
四、IIS音频接口
五、HDMI ARC
六、光纤同轴 S/PDIF
测试设备:
笔记本电脑台式机外置USB声卡转数字同轴光纤5.1输出DTS杜比AC3 黑色【图片 价格 品牌 报价】-京东 (jd.com)
七、DAC
- DAC 参数配置
struct dac_platform_data {
u32 output : 4; //DAC输出模式
u32 ldo_volt : 4; //DACVDD_LDO电压档选择
u32 ldo_isel : 4; //LDO偏置电流选择档位, 0:5u, 1:10u, 2:15u, 3:20u, 4:25u, 5:30u, 6:35u, 7:40u
u32 lpf_isel : 4; //LPF bias电流选择, 0:无, 1:0.3125u, 2:0.625u, 3:0.9375, 4:1.25u, 5:1.5625, 6:1.875u, 7:2.1875u, 8:2.5u, 9:2.8125u, 10:3.125u, 11:3.4375u, 12:3.75u, 13:4.0625u, 14:4.375u, 15:4.6875u
u32 ldo_fb_isel : 2; //LDO负载电流选择, 0:15u, 1:48u, 2:81u, 3:114u
u32 vcmo_en : 1; //VCMO直推使能
u32 keep_vcmo : 1;
u32 dsm_clk : 1;
u32 vcm_risetime : 1; //VCM上电快慢选择
u32 zero_cross_detect : 1; //模拟增益过零检测配置
u32 vdd_setting_enable : 1; //通过接口设置DACVDD电压的使能
};
//****************
//SDK参数 参考配置
//****************
struct dac_platform_data dac_data = {
.ldo_volt = TCFG_AUDIO_DAC_LDO_VOLT, //DACVDD等级.需要根据具体硬件来设置(高低压)可选:1.2V/1.3V/2.35V/2.5V/2.65V/2.8V/2.95V/3.1V
#if ((TCFG_AUDIO_DAC_CONNECT_MODE == DAC_OUTPUT_FRONT_LR_REAR_LR) || (TCFG_AUDIO_DAC_CONNECT_MODE == DAC_OUTPUT_DUAL_LR_DIFF))
.vcmo_en = 0, //四声道与双声道差分关闭VCOMO
#else
.vcmo_en = 1, //是否打开VCOMO
#endif
.output = TCFG_AUDIO_DAC_CONNECT_MODE, //DAC输出配置,和具体硬件连接有关,需根据硬件来设置
.ldo_isel = 3,
.ldo_fb_isel = 2,
.lpf_isel = 0x8,
};
当需要修改DAC的驱动电流时,建议是修改lpf_isel 这个参数
- output:DAC输出模式
#define DAC_OUTPUT_MONO_L 0 //左声道
#define DAC_OUTPUT_MONO_R 1 //右声道
#define DAC_OUTPUT_LR 2 //立体声
#define DAC_OUTPUT_MONO_LR_DIFF 3 //单声道差分输出
#define DAC_OUTPUT_DUAL_LR_DIFF 6 //双声道差分
#define DAC_OUTPUT_FRONT_LR_REAR_LR 9 //四声道单端输出(不可设置vcmo公共端)
- 差分输出:有两个输出端,输出的信号是两输出端之间的电压差;
- 单端输出:只有一个输出端,输出的信号是输出端对地的电压。
- DAC 当IO使用 AC696:DAC输出高低电平_ac696n_soundbox_sdk_v1.2.2
AC695: DAC输出高低电平_AC695N_soundbox_sdk_release_3.1.0 - 拦截DAC数据做运算
- AC696 耳机SDK(0.3.1SDK)
void dac_data_write_handler(u8 channel, void *buf, int len)
{
//DAC 的数据接口
}
- AC695 /AC696 音箱SDK 具体可以参考本文档的 《九 音频流 —— 2 左右声道数据互换》 这一节

- AC695 数据直接推到DAC
- AC695N_HCI_SDK_release_v0.0.5 添加 DAC直接推数据的函数
cbp_out\apps\dongle\audio_dev.c
#include "media/includes.h"
s16 dac_io_data[400];
void audio_dac_mute_ch(u8 ch, u8 mute)
{
if (mute) {
JL_ANA->DAA_CON1 &= ~((ch & 0xF) << 20);
JL_ANA->DAA_CON1 &= ~((ch & 0xF) << 24);
} else {
JL_ANA->DAA_CON1 |= ((ch & 0xF) << 20);
JL_ANA->DAA_CON1 |= ((ch & 0xF) << 24);
}
}
const s16 tsin_48k[48] = {
0, 2139, 4240, 6270, 8192, 9974, 11585, 12998,
14189, 15137, 15826, 16244, 16384, 16244, 15826, 15137,
14189, 12998, 11585, 9974, 8192, 6270, 4240, 2139,
0, -2139, -4240, -6270, -8192, -9974, -11585, -12998,
-14189, -15137, -15826, -16244, -16384, -16244, -15826, -15137,
-14189, -12998, -11585, -9974, -8192, -6270, -4240, -2139
};
int get_sine_data(u16 *s_cnt, s16 *data, u16 points, u8 ch)
{
while (points--) {
if (*s_cnt >= 48) {
*s_cnt = 0;
}
*data++ = tsin_48k[*s_cnt];
if (ch == 2) {
*data++ = tsin_48k[*s_cnt];提个
}
(*s_cnt)++;
}
return 0;
}
#define USER_IO_DEBUG_0(i,x) {JL_PORT##i->DIR &= ~BIT(x), JL_PORT##i->OUT &= ~BIT(x);}
#define USER_IO_DEBUG_1(i,x) {JL_PORT##i->DIR &= ~BIT(x), JL_PORT##i->OUT |= BIT(x);}
u16 delay_test_cnt = 0;
u16 sin_cnt = 0;
s16 dual_ch_test_buf[1024 * 2];
void dac_io_test(void *priv)
{
u16 s_cnt = 0;
u16 len = 640;
delay_test_cnt++;
if(delay_test_cnt<200){
USER_IO_DEBUG_0(A,6);
return;
}else
{
USER_IO_DEBUG_1(A,6);
if(delay_test_cnt>400)delay_test_cnt = 0;
}
get_sine_data(&sin_cnt,dual_ch_test_buf,len/2/2,2);
//ch 0:FL 1:FR 2:RL 3:RR value (-32768) ~ (32767)
int wlen = audio_dac_write(&dac_hdl, dual_ch_test_buf, len);
if (wlen != len) {
/*dac buf写满了*/
printf("dac full,wlen:%d-%d", wlen, len);
}
putchar('#');
}
void dac_output_test(void *priv)
{
int i;
audio_dac_set_delay_time(&dac_hdl, 1, 50);
audio_output_start(8000, 1);
audio_dac_start(&dac_hdl);
s16 dac_data[128];
for (i=0; i<128; i++) {
dac_data[i] = 32767; // 开机默认 低
//dac_data[i] = -32768; // 开机默认 高
}
audio_dac_mute_ch(0xf, 1);
audio_dac_write(&dac_hdl, dac_data, 256);
os_time_dly(50);
audio_dac_set_L_digital_vol(&dac_hdl, 16384);
audio_dac_set_R_digital_vol(&dac_hdl, 16384);
audio_dac_set_RL_digital_vol(&dac_hdl, 16384);
audio_dac_set_RR_digital_vol(&dac_hdl, 16384);
#if (TCFG_AUDIO_DAC_CONNECT_MODE == DAC_OUTPUT_FRONT_LR_REAR_LR)
int err = audio_dac_set_analog_vol_support4way(&dac_hdl, 31, 31, 31, 31);
#else
int err = audio_dac_set_analog_vol(&dac_hdl, 31);
#endif
JL_AUDIO->DAC_CON &= ~(0xF << 12);
audio_dac_mute_ch(0xf, 0);
sys_timer_add(NULL, dac_io_test, 1);
}
5. DAC极性修改
- 数字反相器,用来矫正DAC的输出相位 新的SDK已经有实现该功能,以前的SDK要实现可以参考自行添加,宏定义如下:
#define TCFG_DIG_PHASE_INVERTER_EN 1 //数字反相器,用来矫正DAC的输出相位
实现代码:
#if TCFG_DIG_PHASE_INVERTER_EN
audio_phase_inver_hdl.entry.data_process_len = audio_phase_inver_output_data_process_len;
audio_phase_inver_hdl.entry.data_handler = audio_phase_inver_data_handler;
entries[entry_cnt++] = &(audio_phase_inver_hdl.entry);
#endif/*TCFG_DIG_PHASE_INVERTER_EN*/
- AC698X DAC极性修改

- AC697X DAC极性修改 先检查代码里面有没有TCFG_DIG_PHASE_INVERTER_EN这个宏,以及下面的代码

有的话直接代码改TCFG_DIG_PHASE_INVERTER_EN宏的值,1是反的话就改成0,0是反的话就改成1 。
没有TCFG_DIG_PHASE_INVERTER_EN宏就按照上面698的添加既可。 - 可视化SDK极性修改 需要加个节点,在节点处理中添加上面698的代码


- DAC高阻态
- AC695 系列设置 DAC 管脚高阻态 注意:下面的接口需要DAVDD电压(2.8V)打开后,才能够实际配置成为高阻态。可以在app_main()调用以下代码段。
extern struct audio_dac_hdl dac_hdl;
audio_dac_vdd_power_on(&dac_hdl);
delay(1000);
JL_ANA->DAA_CON3 |= BIT(16);
delay(1000);
JL_ANA->DAA_CON0 |= BIT(23);
delay(100);
JL_ANA->DAA_CON0 |= BIT(0);
delay(100);
JL_ANA->DAA_CON0 |= BIT(1);
delay(100);
audio_dac_mute_ch(BIT(0)|BIT(1),1);
os_time_dly(1);
/*
ch: FL:BIT(0) FR:BIT(1) RL:BIT(2) RR:BIT(3)
例如:BIT(0)是 FL 左声道, BIT(1)是 FR 右声道, FLR BIT(0)|bit(1)是立体声
MUTE:1 MUTE 0UNMUTE
*/
void audio_dac_mute_ch(u8 ch, u8 mute)
{
if (mute) {
JL_ANA->DAA_CON1 &= ~((ch & 0xf) << 20);
JL_ANA->DAA_CON1 &= ~((ch & 0xf) << 24);
} else {
JL_ANA->DAA_CON1 |= ((ch & 0xf) << 20);
JL_ANA->DAA_CON1 |= ((ch & 0xf) << 24);
}
}
- AC696N 系列设置 DAC 管脚高阻态
/*
ch: FL:BIT(0) FR:BIT(1) RL:BIT(2) RR:BIT(3)
例如:BIT(0)是 FL 左声道, BIT(1)是 FR 右声道, FLR BIT(0)|bit(1)是立体声
MUTE:1:MUTE 0:UNMUTE
*/
void audio_dac_mute_ch(u8 ch, u8 mute)
{
if (mute) {
JL_ANA->DAA_CON1 &= ~((ch & 0x3) << 10);
JL_ANA->DAA_CON1 &= ~((ch & 0x3) << 13);
} else {
JL_ANA->DAA_CON1 |= ((ch & 0x3) << 10);
JL_ANA->DAA_CON1 |= ((ch & 0x3) << 13);
}
}
- 706N 系列设置 DAC 管脚高阻态 AC706N音箱问题整理.otl
//DAC高阻态
SFR(JL_ADDA->DAA_CON2, 23, 1, 1); //SDDAC_PA_TRIM_EN_11v
DAC tirm值
AC697X_AC698X_AC700X DAC左右声道trim 值获取
备注:DAC左右声道trim值相差在正负150左右正常。如果相差很大,在播放提示音或蓝牙音乐前会听到一个啵声。这个是由第一次上电时DAC trim时,供电不稳引起。需要确保供电稳定,可以用强升工具重新下载程序,擦掉所有VM数据,然后再重新上电测试。
- AC695X /AC696X DAC trim值获取


- 697N音量可调参数
/*
*系统音量类型选择
*软件数字音量是指纯软件对声音进行运算后得到的
*硬件数字音量是指dac内部数字模块对声音进行运算后输出
*/
#define VOL_TYPE_DIGITAL 0 //软件数字音量
#define VOL_TYPE_ANALOG 1 //硬件模拟音量
#define VOL_TYPE_AD 2 //联合音量(模拟数字混合调节)
#define VOL_TYPE_DIGITAL_HW 3 //硬件数字音量
#define SYS_VOL_TYPE VOL_TYPE_AD
#define MAX_ANA_VOL (10) //系统最大模拟音量0~15
- 使用软件数字音量时,音量表在apps\common\audio\audio_digital_vol.c中的const u16 dig_vol_table[DIGITAL_VOL_MAX + 1]。
- 不建议使用硬件模拟音量
- 使用联合音量(模拟数字混合调节)时:
- 如果使能USE_CONFIG_COMBINE_VOL_SETTING,从配置工具中修改音量
- 否则修改combined_vol_list和call_combined_vol_list
- 使用硬件数字音量时,可在audio_fade_in_fade_out函数中修改或添加自定义数字音量表,如下图位置:

- 其他影响音量的功能模块:EQ、DRC、ANC,测试时,要关掉EQ、DRC、ANC测试,或者EQ的曲线拉平和总增益置0,ANC的dac增益配置配成15,如图:

- 700N音量可调参数
普通SDK:
#define TCFG_AUDIO_DAC_LDO_VOLT DACVDD_LDO_1_80V //普通模式 DACVDD有1.8V到2.5V可选,最大音量空载1K 0DB峰峰值1.3V~1.8V,需要更大时TCFG_AUDIO_DAC_LDO_VOLT 可以定义为28,但是VDDIO要提高到3.2V
/*
*系统音量类型选择
*软件数字音量是指纯软件对声音进行运算后得到的
*硬件数字音量是指dac内部数字模块对声音进行运算后输出
*/
#define VOL_TYPE_DIGITAL 0 //软件数字音量(调节解码输出数据的音量)
#define VOL_TYPE_ANALOG 1 //(暂未支持)硬件模拟音量
#define VOL_TYPE_AD 2 //(暂未支持)联合音量(模拟数字混合调节)
#define VOL_TYPE_DIGITAL_HW 3 //硬件数字音量(调节DAC模块的硬件音量)
#define SYS_VOL_TYPE VOL_TYPE_DIGITAL_HW
#define MAX_ANA_VOL (15) // 系统最大模拟音量,范围: 0 ~ 15
#define DEFAULT_DIGITAL_VOLUME 16384 // 硬件数字音量0~16384
- 700N仅支持硬件数字音量和软件数字音量
- 硬件数字音量表hw_dig_vol_table,软件数字音量表是dig_vol_table,可以通过配置工具-->数字音量配置功能得到想要DB值的音量表,然后替换到hw_dig_vol_table或者dig_vol_table。


- 其他影响音量的功能模块:EQ、DRC、ANC 测试最大音量时,要关掉EQ、DRC、ANC测试,或者EQ的曲线拉平和总增益置0,ANC的dac增益配置配成15,如图:

可视化SDK:



自定义数字音量的方法

按照普通sdk的方式修改下面的表
- 701N音量可调参数
普通SDK:
硬件上支持多种DAC电压配置,有HPVDD邦出来的,可外接电压最大3V,没有HPVDD这个IO口的,内部默认接VDDIO。
/*
DAC模式选择
#define DAC_MODE_L_DIFF (0) // 低压差分模式 , 适用于低功率差分耳机 , 输出幅度 0~2Vpp
#define DAC_MODE_L_SINGLE (1) // 低压单端模式 , 主要用于左右差分模式, 差分输出幅度 0~2Vpp
#define DAC_MODE_H1_DIFF (2) // 高压1档差分模式, 适用于高功率差分耳机 , 输出幅度 0~3Vpp , VDDIO >= 3.0V
#define DAC_MODE_H1_SINGLE (3) // 高压1档单端模式, 适用于高功率单端PA音箱, 输出幅度 0~1.5Vpp, VDDIO >= 3.0V
#define DAC_MODE_H2_DIFF (4) // 高压2档差分模式, 适用于高功率差分PA音箱, 输出幅度 0~5Vpp , VDDIO >= 3.3V
#define DAC_MODE_H2_SINGLE (5) // 高压2档单端模式, 适用于高功率单端PA音箱, 输出幅度 0~2.5Vpp, VDDIO >= 3.3V
*/
#define TCFG_AUDIO_DAC_MODE DAC_MODE_L_DIFF // DAC_MODE_L_DIFF 低压, DAC_MODE_H1_DIFF 高压
//HPVDD 1.85V,选择DAC_MODE_H1_DIFF,关掉EQ、DRC、ANC测试,空载1K峰峰值2.7V左右
/*
*系统音量类型选择
*软件数字音量是指纯软件对声音进行运算后得到的
*硬件数字音量是指dac内部数字模块对声音进行运算后输出
*选择软件数字音量或者硬件数字音量会固定模拟音量,
*固定的模拟音量值由 MAX_ANA_VOL 决定
*/
#define VOL_TYPE_DIGITAL 0 //软件数字音量
#define VOL_TYPE_AD 2 //联合音量(模拟数字混合调节)
#define VOL_TYPE_DIGITAL_HW 3 //硬件数字音量
/*注意:ANC使能情况下使用软件数字音量*/
#if (TCFG_AUDIO_ANC_ENABLE || TCFG_AD2DA_LOW_LATENCY_ENABLE || TCFG_AUDIO_HEARING_AID_ENABLE)
#define SYS_VOL_TYPE VOL_TYPE_DIGITAL
#else
#define SYS_VOL_TYPE VOL_TYPE_DIGITAL_HW
#endif/*TCFG_AUDIO_ANC_ENABLE*/
#define MAX_ANA_VOL (3) //系统最大模拟音量 (0-3)
#if TCFG_AUDIO_HEARING_AID_ENABLE
#define DIG_VOL_MAX_VALUE (0.0f) // 数字音量最大值(单位:dB),最大0
#elif (TCFG_AUDIO_DAC_MODE == DAC_MODE_L_DIFF)
#define DIG_VOL_MAX_VALUE (-5.0f) // 数字音量最大值(单位:dB),最大0
#elif (TCFG_AUDIO_DAC_MODE == DAC_MODE_H1_DIFF)
#define DIG_VOL_MAX_VALUE (-8.0f) // 数字音量最大值(单位:dB),最大0
#else
#define DIG_VOL_MAX_VALUE (0.0f) // 数字音量最大值(单位:dB),最大0
#endif
#if (TCFG_AUDIO_ANC_ENABLE)
#define ANC_MODE_DIG_VOL_LIMIT (-6.0f) // 限制数字音量,最大0
#endif/*TCFG_AUDIO_ANC_ENABLE*/
/*数字音量最大值定义 最大16384*/
#define DEFAULT_DIGITAL_VOLUME 16384
- 使用软件数字音量时:
- SW_DIG_VOL_TAB_USER_DEFINED打开时,由SDK流程生成数字音量表,大小受上面的DIG_VOL_MAX_VALUE参数限制,每个等级差异由DIG_VOL_STEP决定。
- SW_DIG_VOL_TAB_USER_DEFINED关闭时,数字音量表使用default_dig_vol_table。
- 使用硬件数字音量时:
- 默认由SDK流程生成数字音量表,大小受上面的DIG_VOL_MAX_VALUE参数限制,每个等级差异由BT_MUSIC_VOL_STEP(音乐)或者BT_CALL_VOL_STEP(通话)决定。
- 可通过修改下图改为使用数字音量表,数字音量表使用hw_dig_vol_table。

- 其他影响音量的功能模块:EQ、DRC、ANC 测试最大音量时,要关掉EQ、DRC、ANC测试,或者EQ的曲线拉平和总增益置0,ANC的dac增益配置配成15,如图:
开了ANC后输出不够时,可以加大ANC_MODE_DIG_VOL_LIMIT,最大为0。
公版SDK使用如下配置,最大音量空载1K 0DB输出峰峰值2.7V左右:- HPVDD接1.85V;
- TCFG_AUDIO_DAC_MODE选择DAC_MODE_H1_DIFF;
- DIG_VOL_MAX_VALUE配置为(0.0f);
- 关掉EQ、DRC、ANC。
可视化SDK:
11. 打印所有adc,dac的增益
AC696n
void audio_adda_gain_dump(void)//打印所有adc,dac的增益
{
u8 dac_again_fl = JL_ANA->DAA_CON1 & 0x1F;
u8 dac_again_fr = (JL_ANA->DAA_CON1 >> 5) & 0x1F;
u32 dac_dgain_fl = JL_AUDIO->DAC_VL0 & 0xFFFF;
u32 dac_dgain_fr = (JL_AUDIO->DAC_VL0 >> 16) & 0xFFFF;
u8 mic0_gain = (JL_ANA->ADA_CON1) & 0x1F;
u8 mic0_db = (JL_ANA->ADA_CON1 >> 16) & 0x1; //0db还是6db
u8 linein_l_gain = (JL_ANA->ADA_CON1 >> 5) & 0xF;
printf("MIC_G:%d,MIC_0_6:%d, linein_gain:%d,DAC_AG:%d,%d,DAC_DG:%d,%d\n", mic0_gain, mic0_db, linein_l_gain, dac_again_fl, dac_again_fr, dac_dgain_fl, dac_dgain_fr);
}
ac695 n
void audio_adda_gain_dump(void)//打印所有adc,dac的增益
{
u8 dac_again_fl = JL_ANA->DAA_CON1 & 0x1F;
u8 dac_again_fr = (JL_ANA->DAA_CON1 >> 5) & 0x1F;
u8 dac_again_rl = (JL_ANA->DAA_CON1 >> 10) & 0x1F;
u8 dac_again_rr = (JL_ANA->DAA_CON1 >> 15) & 0x1F;
u32 dac_dgain_fl = JL_AUDIO->DAC_VL0 & 0xFFFF;
u32 dac_dgain_fr = (JL_AUDIO->DAC_VL0 >> 16) & 0xFFFF;
u32 dac_dgain_rl = JL_AUDIO->DAC_VL1 & 0xFFFF;
u32 dac_dgain_rr = (JL_AUDIO->DAC_VL1 >> 16) & 0xFFFF;
u8 mic0_gain = (JL_ANA->ADA_CON0 >> 8) & 0x1F;
u8 mic0_db = (JL_ANA->ADA_CON4 >> 29) & 0x1; //0db还是6db
u8 linein_r_gain = (JL_ANA->ADA_CON0 >> 4) & 0xF;
u8 linein_l_gain = JL_ANA->ADA_CON0 & 0xF;
printf("MIC_G:%d,MIC_0_6:%d, linein_gain:%d,%d,DAC_AG:%d,%d,%d,%d,DAC_DG:%d,%d,%d,%d\n", mic0_gain, mic0_db, linein_l_gain, linein_r_gain, dac_again_fl, dac_again_fr, dac_again_rl, dac_again_rr, dac_dgain_fl, dac_dgain_fr, dac_dgain_rl, dac_dgain_rr);
}
八、MIC
- 打开MIC数据采样 示范程序
AC696N_soundbox_sdk_release_1.5.0
struct adc_mic_ch user_mic;
struct audio_adc_output_hdl user_mic_output;
/*----------------------------------------------------------------------------*/
/**@brief MIC数据的回调
@param
@return
@note : 默认统计最大/最小值/最大最小的差
*/
/*----------------------------------------------------------------------------*/
static void my_adc_output_to_enc(void *priv, s16 *data, int len)
{
//参数定义
#define CALC_DATA_SUM 16000 //多长时间更新一次数据
u16 points = len / 2;
u16 index;
static s32 min = 0xfffff;
static s32 max = -100000;
static s32 cnt_sum = 0;
s32 db = 0;
for (index = 0; index < points; index ++) {
db = data[index];
cnt_sum++;
if (cnt_sum > CALC_DATA_SUM) {
printf("MAX : %d\tMIN : %d \t: %d\n",max,min,max-min);
cnt_sum = 0;
min = 0xfffff;
max = -100000;
}
if (min > db)
min = db;
if (max < db)
max = db;
}
}
/*----------------------------------------------------------------------------*/
/**@brief 打开MIC
@param
@return
@note : audio_mic_open 另外两个传参为 采样率 和 MIC增益
*/
/*----------------------------------------------------------------------------*/
void user_mic_open(void)
{
user_mic_output.handler = my_adc_output_to_enc;
user_mic_output.priv = NULL;
if (audio_mic_open(&user_mic, 16000,0) == 0) {
audio_mic_add_output(&user_mic_output);
audio_mic_start(&user_mic);
}
}
/*----------------------------------------------------------------------------*/
/**@brief 关闭MIC
@param
@return
@note
*/
/*----------------------------------------------------------------------------*/
void user_mic_close(void)
{
audio_mic_close(&user_mic, &user_mic_output);
}
- 打开mic,获取mic数据,分析mic频谱
//使用头文件
#include "spectrum/Spectrum_fft.h"
//数据定义
#define mic_spectrum_fft 1
int get_spectrum;
spectrum_fft_hdl *fft_hdl = NULL;
//开mic,获取mic数据:
extern void mic_spectrum_get_demo(void *p);
int audio_adc_mic_open_demo(u16 sr)
{ printf("KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKL\n");
u8 ladc_mic_gain = 5;
ASSERT(ladc_mic == NULL);
ladc_mic = zalloc(sizeof(struct ladc_mic_demo));
if (ladc_mic) {
audio_adc_mic_open(&ladc_mic->mic_ch, AUDIO_ADC_MIC_CH, &adc_hdl);
audio_adc_mic_set_sample_rate(&ladc_mic->mic_ch, sr);
audio_adc_mic_set_gain(&ladc_mic->mic_ch, ladc_mic_gain);
audio_adc_mic_set_buffs(&ladc_mic->mic_ch, ladc_mic->adc_buf, LADC_MIC_IRQ_POINTS * 2, LADC_MIC_BUF_NUM);
audio_adc_mic_start(&ladc_mic->mic_ch);
ladc_mic->adc_output.handler = adc_mic_open_demo_output; //回调函数
ladc_mic->adc_output.priv = &adc_hdl;
audio_adc_add_output_handler(&adc_hdl, &ladc_mic->adc_output);
#if mic_spectrum_fft //频谱部分
spectrum_fft_open_parm parm = {0};
parm.sr = sr;
parm.channel = 1;
parm.attackFactor = 0.9;
parm.releaseFactor = 0.9;
parm.mode = 2;
fft_hdl = audio_spectrum_fft_open(&parm);
get_spectrum = sys_timer_add(fft_hdl, mic_spectrum_get_demo, 500);//频谱值获取测试 */
printf("spectrum_open_demo\n");
clock_add(SPECTRUM_CLK);
#endif
return 0;
} else {
return -1;
}
}
////MIC回调函数:
extern int audio_spectrum_fft_run(spectrum_fft_hdl *hdl, s16 *data, int len);
static void adc_mic_open_demo_output(void *priv, s16 *data, int len)
{
struct audio_adc_hdl *hdl = priv;
//putchar('o');
if (ladc_mic == NULL) {
return;
}
#if mic_spectrum_fft //频谱部分
audio_spectrum_fft_run(fft_hdl,data,len);
#endif
// printf("ppppp:%d,%d\n",data[2],len); //可在此处拿到MIC采集的数据,对所有数据做绝对值累加则可以作为声音能量参考依据
}
///关MIC函数
void audio_adc_mic_close_demo(void)
{
if (ladc_mic){
#if mic_spectrum_fft //频谱部分
audio_spectrum_fft_close(fft_hdl);
clock_remove(SPECTRUM_CLK);
sys_timer_del(get_spectrum);
#endif
audio_adc_mic_close(&ladc_mic->mic_ch);
audio_adc_del_output_handler(&adc_hdl, &ladc_mic->adc_output);
free(ladc_mic);
ladc_mic = NULL;
}
}
//频谱数据处理函数涉及的头文件及参数:
#include "spectrum/spectrum_fft.h"
#include "audio_splicing.h"
#define spectrum_fft_points_per_ch (512)
//频谱部分:
//上文获取频谱输出函数:
void mic_spectrum_get_demo(void *p)
{
spectrum_fft_hdl *hdl = p;
printf("spectrum_open_demo : %d\n",hdl);
if (hdl) {
u8 db_num = audio_spectrum_fft_get_num(hdl);//获取频谱个数
short *db_data = audio_spectrum_fft_get_val(hdl);//获取存储频谱值得地址
if (!db_data) {
return;
}
for (int i = 0; i < db_num; i++) {
//输出db_num个 db值
printf("db_data db[%d] %d\n", i, db_data[i]);
}
}
}
//频谱数据处理函数:
/*----------------------------------------------------------------------------*/
/**@brief audio_spectrum_fft_run 同步处理,每次run都会把输入buf消耗完,才会往下走
@param _hdl:句柄
@param data:输入数据
@param len:输入数据长度
@return len
@note 频谱计算处理,只获取输入的数据,不改变输入的数据
*/
/*----------------------------------------------------------------------------*/
int audio_spectrum_fft_run(spectrum_fft_hdl *hdl, s16 *data, int len)
{
if (!hdl) {
return len;
}
if (!len) {
return len;
}
if (!hdl->run_en) {
hdl->offset = 0;
return len;
}
u8 channel = (hdl->parm.channel & 0x3);
s16 *indata_tmp = data;
u32 in_remain = len;
u32 tlen;
__here:
indata_tmp = (s16 *)((int)data + (len - in_remain));
if (hdl->offset < hdl->out_buf_size) {
tlen = hdl->out_buf_size - hdl->offset;
if (channel == 2 && (hdl->parm.channel & BIT(4))) {
if (tlen > in_remain / 2) {
tlen = in_remain / 2;
}
pcm_qual_to_dual((void *)((int)hdl->out_buf + hdl->offset), indata_tmp, tlen * 2);
hdl->offset += tlen;
in_remain -= tlen * 2;
} else {
if (tlen > in_remain) {
tlen = in_remain;
}
memcpy((void *)((int)hdl->out_buf + hdl->offset), indata_tmp, tlen);
hdl->offset += tlen;
in_remain -= tlen;
}
if (in_remain && (hdl->offset != hdl->out_buf_size)) {
goto __here;
}
}
if (hdl->offset == hdl->out_buf_size) {
hdl->offset = 0;
SpectrumShowRun(hdl->work_buf, hdl->out_buf, spectrum_fft_points_per_ch);
if (in_remain) {
goto __here;
}
}
return len;
}
参考按键使用例程:
extern int audio_adc_mic_open_demo(u16 sr);
extern void audio_adc_mic_close_demo(void);
case KEY_OPEN_ADC:
static int i = 0;
if(i == 0){
printf("audio_adc_open_demo");
audio_adc_mic_open_demo(44100);
i = 1;
}
else{
printf("audio_adc_close_demo");
audio_adc_mic_close_demo();
i = 0;
}
break;
上文主要实现功能:当audio_adc_mic_open_demo函数执行后,将开启MIC,并采集MIC数据在adc_mic_open_demo_output回调函数处可以对收集到的数据做处理。同时如果将mic_spectrum_fft宏定义为1时,可以同时得到频谱的输出。
频谱数据解答:
此图是在上述程序执行后,mic输入2984hz的音频后,频谱输出结果:
2984hz对应采样率为44100的频谱显示中心频率点第16点,此时在频谱输出图中对应的db[16]为最大值。简单来说:就是输入频率的声音越大,频谱对应的数据就越大。
备注:频率中心附近几个频点的数据也比较大,属于正常现象。
完整采样率对应频谱显示中心频率点如下图:
【金山文档】 频谱显示中心频率点
频谱显示中心频率点.xlsx
695_3.0.1_sdk_add_mic_spectrum_demo
链接:https://pan.baidu.com/s/13tHCwnd055UTSQn2a1ZFWg
提取码:8888
- ac696n_earphone_v0.3.0获取mic采样数据 (696其他耳机SDK可尝试共用)
- 只需要获取mic的采样数据 添加如下代码,调用audio_adc_demo()启动采样,调用audio_adc_demo_stop()结束采样
struct adc_stream_ops {
void (*buf_reset)(void *priv);
void *(*alloc_space)(void *priv, u32 *len);
void (*output)(void *priv, void *buf, u32 len);
};
struct audio_adc_attr {
u8 gain;
u16 sample_rate;
u16 irq_time;
u16 irq_points;
};
int audio_adc_sample_start(void *adc);
int audio_adc_sample_stop(void *adc);
int audio_adc_get_attr(void *adc, struct audio_adc_attr *attr);
int audio_adc_set_attr(void *adc, struct audio_adc_attr *attr);
int audio_adc_close(void *adc);
void audio_adc_set_irq_handler(void *adc, void *priv, void (*handler)(void *, void *, u32));
void audio_adc_set_buf_ops(void *adc, void *priv, struct adc_stream_ops *ops);
int audio_adc_set_buffer(void *adc, void *buf, int len);
int audio_adc_set_sample_rate(void *adc, u32 sample_rate);
void audio_adc_set_mic_gain(void *adc, u8 gain);
static void *adc_buf;
static int adc_bufsize = 2048;
static void *adc;
#define DEFAULT_REC_POINTS 64
#define ADC_SR 16000
extern struct adc_platform_data adc_data;
static void *test_adc_stream_alloc(void *priv, u32 * len) {
*len = DEFAULT_REC_POINTS;
return (void *)adc_buf;
}
//mic数据输出
static void test_adc_stream_finish(void *priv, void *data, u32 len) {
put_buf((u8*)data,8);
}
const struct adc_stream_ops output_ops = {
.alloc_space = test_adc_stream_alloc,
.output = test_adc_stream_finish,
};
void audio_adc_demo()
{
if(adc){
return;
}
adc_buf = zalloc(adc_bufsize);
adc = audio_adc_open(&adc_data, "mic");
audio_adc_set_buf_ops(adc, adc, &output_ops);
audio_adc_set_buffer(adc, adc_buf, adc_bufsize);
struct audio_adc_attr adc_attr;
audio_adc_get_attr(adc, &adc_attr);
adc_attr.gain = 14;
adc_attr.sample_rate = ADC_SR;
adc_attr.irq_points = DEFAULT_REC_POINTS;
audio_adc_set_attr(adc, &adc_attr);
audio_adc_sample_start(adc);
//audio_adc_sample_stop(adc_mc);
//audio_adc_close(adc_mc);
}
void audio_adc_demo_stop()
{
if(adc){
audio_adc_sample_stop(adc);
audio_adc_close(adc);
free(adc_buf);
adc_buf = NULL;
adc = NULL;
}
}
2. mic输出到dac功能
定义宏AUDIO_MIC_TEST
#define AUDIO_MIC_TEST
调用mic_rec_play_start()启动,启动后不要再播提示音和蓝牙音乐。
调用mic_rec_play_stop()停止。
同时修改如下截图内容,也可以在截图位置获取mic的采样数据
- USB通话、智能语音使用数字MIC
- usb通话 USB通话修改audio_usb_mic.c文件
添加库和宏
- usb通话 USB通话修改audio_usb_mic.c文件
#include "pdm_link.h"
static audio_plnk_t *audio_plnk_mic = NULL;
#define ESCO_PLNK_SCLK 2400000 /*1M-4M,SCLK/SR需为整数且在1-4096范围*/
#define ESCO_PLNK_CH_NUM 1 /*支持的最大通道(max = 2)*/
#define ESCO_PLNK_IRQ_POINTS 256 /*采样中断点数*/
添加输出函数
void plink_data_to_usb(s16 *data, int len)
{
adc_output_to_cbuf(NULL, data, len << 1);
}
添加数字mic流程
#if (TCFG_AUDIO_ADC_MIC_CHA == PLNK_MIC)
printf("pink_2_adc open\n");
audio_plnk_mic = zalloc(sizeof(audio_plnk_t));
//audio_mic_pwr_ctl(MIC_PWR_ON);
if (audio_plnk_mic) {
audio_plnk_mic->ch_num = ESCO_PLNK_CH_NUM;//PLNK_MIC_CH;
/*SCLK:1M-4M,SCLK/SR需为整数且在1-4096范围*/
audio_plnk_mic->sr = sample_rate;
audio_plnk_mic->sclk_fre = ESCO_PLNK_SCLK;
if (audio_plnk_mic->sclk_fre % audio_plnk_mic->sr) {
r_printf("[warn]SCLK/SR需为整数且在1-4096范围\n");
}
#if (PLNK_CH_EN == PLNK_CH0_EN)
audio_plnk_mic->ch0_mode = CH0MD_CH0_SCLK_RISING_EDGE;
audio_plnk_mic->ch1_mode = CH1MD_CH0_SCLK_FALLING_EDGE;
#elif (PLNK_CH_EN == PLNK_CH1_EN)
audio_plnk_mic->ch0_mode = CH0MD_CH1_SCLK_FALLING_EDGE;
audio_plnk_mic->ch1_mode = CH1MD_CH1_SCLK_RISING_EDGE;
#else
audio_plnk_mic->ch0_mode = CH0MD_CH0_SCLK_RISING_EDGE;
audio_plnk_mic->ch1_mode = CH1MD_CH1_SCLK_RISING_EDGE;
#endif
audio_plnk_mic->output = plink_data_to_usb;//*adc_output_to_cbuf;
audio_plnk_mic->buf_len = ESCO_PLNK_IRQ_POINTS;
audio_plnk_mic->buf=zalloc(audio_plnk_mic->buf_len * audio_plnk_mic->ch_num * 2 * 2);
ASSERT(audio_plnk_mic->buf);
audio_plnk_mic->sclk_io = TCFG_AUDIO_PLNK_SCLK_PIN;
audio_plnk_mic->ch0_io = TCFG_AUDIO_PLNK_DAT0_PIN;
audio_plnk_mic->ch1_io = TCFG_AUDIO_PLNK_DAT1_PIN;
audio_plnk_open(audio_plnk_mic);
audio_plnk_start(audio_plnk_mic);
}
#else
注意补全#endif
添加关MIC流程
#if (TCFG_AUDIO_ADC_MIC_CHA == PLNK_MIC)
printf("pink_2_adc close\n");
audio_plnk_close();
audio_mic_pwr_ctl(MIC_PWR_OFF);
if (audio_plnk_mic->buf) {
free(audio_plnk_mic->buf);
}
if (audio_plnk_mic) {
free(audio_plnk_mic);
audio_plnk_mic = NULL;
}
#else
2. 智能语音
智能语音修改voice_mic_data.c文件
添加相应库和宏
#include "pdm_link.h"
static audio_plnk_t *audio_plnk_mic = NULL;
#define ESCO_PLNK_SCLK 2400000 /*1M-4M,SCLK/SR需为整数且在1-4096范围*/
#define ESCO_PLNK_CH_NUM 1 /*支持的最大通道(max = 2)*/
#define ESCO_PLNK_IRQ_POINTS 256 /*采样中断点数*/
添加输出函数
void plink_data_to_kws(s16 *data, int len)
{
audio_main_adc_dma_data_handler(NULL, data, len << 1);
}
添加数字mic流程
#if (TCFG_AUDIO_ADC_MIC_CHA == PLNK_MIC)
if (1) {
audio_plnk_mic = zalloc(sizeof(audio_plnk_t));
audio_mic_pwr_ctl(MIC_PWR_ON);
if (audio_plnk_mic) {
audio_plnk_mic->ch_num = ESCO_PLNK_CH_NUM;//PLNK_MIC_CH;
/*SCLK:1M-4M,SCLK/SR需为整数且在1-4096范围*/
audio_plnk_mic->sr = 16000;//*sample_rate;
audio_plnk_mic->sclk_fre = ESCO_PLNK_SCLK;
if (audio_plnk_mic->sclk_fre % audio_plnk_mic->sr) {
r_printf("[warn]SCLK/SR需为整数且在1-4096范围\n");
}
#if (PLNK_CH_EN == PLNK_CH0_EN)
audio_plnk_mic->ch0_mode = CH0MD_CH0_SCLK_RISING_EDGE;
audio_plnk_mic->ch1_mode = CH1MD_CH0_SCLK_FALLING_EDGE;
#elif (PLNK_CH_EN == PLNK_CH1_EN)
audio_plnk_mic->ch0_mode = CH0MD_CH1_SCLK_FALLING_EDGE;
audio_plnk_mic->ch1_mode = CH1MD_CH1_SCLK_RISING_EDGE;
#else
audio_plnk_mic->ch0_mode = CH0MD_CH0_SCLK_RISING_EDGE;
audio_plnk_mic->ch1_mode = CH1MD_CH1_SCLK_RISING_EDGE;
#endif
audio_plnk_mic->output = plink_data_to_kws;
audio_plnk_mic->buf_len = ESCO_PLNK_IRQ_POINTS;
audio_plnk_mic->buf = zalloc(audio_plnk_mic->buf_len * audio_plnk_mic->ch_num * 2 * 2);
ASSERT(audio_plnk_mic->buf);
audio_plnk_mic->sclk_io = TCFG_AUDIO_PLNK_SCLK_PIN;
audio_plnk_mic->ch0_io = TCFG_AUDIO_PLNK_DAT0_PIN;
audio_plnk_mic->ch1_io = TCFG_AUDIO_PLNK_DAT1_PIN;
audio_plnk_open(audio_plnk_mic);
audio_plnk_start(audio_plnk_mic);
}
}
#else
添加关MIC流程
#if (TCFG_AUDIO_ADC_MIC_CHA == PLNK_MIC)
audio_plnk_close();
audio_mic_pwr_ctl(MIC_PWR_OFF);
if (audio_plnk_mic->buf) {
free(audio_plnk_mic->buf);
}
if (audio_plnk_mic) {
free(audio_plnk_mic);
audio_plnk_mic = NULL;
}
#else
3. 701N数字MIC使用补充
701系列使用数字MIC时需要做如下添加
SFR(JL_ASS->CLK_CON, 0, 2, 3);
4. 使用数字mic做USB mic声音不够大 可以使用aec模块做AGC放大,或者自行处理采样数据放大
5. 打开MIC偏置接口 AC695N
audio_adc.h
int audio_adc_mic_open(struct adc_mic_ch *mic, int ch, struct audio_adc_hdl *adc);
- AD698蓝牙音乐和mic同时输出 【金山文档】 AC698V164 MIC与蓝牙音乐共同输出DAC demo
AC698V164 MIC与蓝牙音乐共同输出DAC demo.otl - 其他与MIC相关的demo 翻译耳机、语音识别
- 700N/701N话务耳机单mic和双mic互相切换的接口 ENC相关应用入口汇总.otl
九、音频流
- 人声消除
- 目前有以下情况目前不支持人声消除(可能会出现无声的情况):
- 左右声道数据基本一致的音频文件。
- 程序宏定义开关
//*********************************************************************************//
// 人声消除使能
//*********************************************************************************//
#define AUDIO_VOCAL_REMOVE_EN 0
//*********************************************************************************//
// 相关函数
//*********************************************************************************//
/*----------------------------------------------------------------------------*/
/**@brief audio_vocal_remove_sw 运行过程开关处理
@param _hdl:句柄
@param dis: 0:人声消除有效 1:人声消除无效
@return 0:成功 -1:失败
@note
*/
/*----------------------------------------------------------------------------*/
int audio_vocal_remove_sw(vocal_remove_hdl *_hdl, u32 dis);
- 人声消除额外保留部分频率声音办法(695_3.1.0_sdk) 公版对比此压缩包内文件将修改点到修改到程序上
【金山文档】 AC695X 人声消除增加了低音保留处理方法
https://kdocs.cn/l/cu2XQVI8FRnS
AC696N_SOUNDBOX_SDK_1.7.0 人声消除补丁.zip
原理:
将原始声音分为两份,一份走原先的人声消除,另一份走EQ调节
最后输出声音 = 原先人声消除效果(左-右)+ EQ调节后声音
EQ调节处:
- 左右声道数据互换
2.1 695sdk参考
- AC695N_soundbox_sdk_release_3.0.4 添加以下节点及函数
2.
#include "stream_entry.h"
u8 pcm_lr_exchange_flag = 1;
int pcm_lr_exchange_sw(u8 sw)
{
pcm_lr_exchange_flag = sw;
}
int pcm_lr_exchange_deal(void *priv, struct audio_data_frame *in)
{
s16 *f_lr=in->data;
s16 tmp_l,tmp_r;
u16 rlen;
if(pcm_lr_exchange_flag == 0)
return in->data_len;
rlen = in->data_len>>2; ////u8*4
//printf("%d\n",in->offset);
if(in->channel == 2){
if(in->offset==0){
for(u16 i=0; i<rlen; i++) ///lrlrlr......
{
tmp_l = f_lr[i*2];
tmp_r = f_lr[i*2+1];
f_lr[i*2] = tmp_r;
f_lr[i*2+1] = tmp_l;
}
}
}
return in->data_len;
}
2.2 608 sdk参考
- ac608n_soundbox_sdk_v1.2.2 添加以下节点及函数
- 在audio_dec.c中的audio_dec_init();函数中如下位置增加左右声道转换节点

struct __audio_test_hdl audio_test_hdl;
memset(&audio_test_hdl, 0x00, sizeof(struct __audio_test_hdl));
audio_test_hdl.entry.data_process_len = audio_test_output_data_process_len;
audio_test_hdl.entry.data_handler = audio_test_data_handler;
// 加入数据流
entries[entry_cnt++] = &(audio_test_hdl.entry);
其中用到的相关接口如下:
extern struct audio_dac_hdl dac_hdl;
struct __audio_test_hdl {
struct audio_stream_entry entry; // 音频流入口
}audio_test_hdl;
// 处理函数
int audio_test_run(void *hdl, s16 *data, u32 len)
{
// 在这里可以处理数据,或者拿走数据
s16 *ddata = data;
u32 points = len/2;
for(int i = 0; i < points; i+=2){
s16 tmp = ddata[i];
ddata[i] = ddata[i+1];
ddata[i+1] = tmp;
}
return 0;
}
static void audio_test_output_data_process_len(struct audio_stream_entry *entry, int len)
{
// 不需要做处理,空着就行
}
static int audio_test_data_handler(struct audio_stream_entry *entry,
struct audio_data_frame *in,
struct audio_data_frame *out)
{
struct __audio_test_hdl *hdl = container_of(entry, struct __audio_test_hdl, entry);
out->data = in->data;
out->data_len = in->data_len;
if (in->data_len - in->offset > 0) {
audio_test_run(hdl, in->data + in->offset / 2, in->data_len - in->offset);
}
return in->data_len;
}
- 数字反相器,用来矫正DAC的输出相位 新的SDK已经有实现该功能,以前的SDK要实现可以参考自行添加,宏定义如下:
#define TCFG_DIG_PHASE_INVERTER_EN 1 //数字反相器,用来矫正DAC的输出相位
实现代码:
#if TCFG_DIG_PHASE_INVERTER_EN
audio_phase_inver_hdl.entry.data_process_len = audio_phase_inver_output_data_process_len;
audio_phase_inver_hdl.entry.data_handler = audio_phase_inver_data_handler;
entries[entry_cnt++] = &(audio_phase_inver_hdl.entry);
#endif/*TCFG_DIG_PHASE_INVERTER_EN*/
- DAC能量值计算
AC696 耳机SDK(0.3.1SDK) 参考上面 拦截DAC数据做运行及通用能量值计算 去实现该功能。
AC696 音箱SDK 2M包应用 蓝牙模式下(参考例子)
拿到蓝牙解码数据后,对数据进行累加取平均值计算。得出能量值。(修改如下图)
int audio_output_data_db_calc_simple(short *data, unsigned short len,unsigned char channels) 函数看 第一点 的第 4 小点 :通用能量值计算(取平均值)AC696 非2M包应用 开 AUDIO_OUTPUT_AUTOMUTE 调用 audio_dac_energy_get()函数;
- 自定义音频流(适用于音箱SDK添加第三方算法/数据处理) 下面以AC695N_soundbox_sdk_release_3.0.1 数据除以2作为例子
主要修改 SDK\cpu\br23\audio_dec\audio_dec.c 文件 - 添加数据节点 int audio_dec_init()函数
截图中的代码
#if AUDIO_VOCAL_REMOVE_EN
struct __stream_entry *l_r_entry = NULL;
l_r_entry = stream_entry_open(&mixer,pcm_lr_exchange_deal,0);
entries[entry_cnt++] = &(l_r_entry->entry);
if (mix_vocal_remove_hdl) {
entries[entry_cnt++] = &mix_vocal_remove_hdl->entry;
}
if (vocal_remove_mix_ch_switch) {
entries[entry_cnt++] = &vocal_remove_mix_ch_switch->entry;
}
#endif
- 数据处理
截图中的代码
#include "stream_entry.h"
u8 pcm_lr_exchange_flag = 1;
int pcm_lr_exchange_sw(u8 sw)
{
pcm_lr_exchange_flag = sw;
}
//任意长度
int pcm_lr_exchange_deal(void *priv, struct audio_data_frame *in)
{
s16 *f_lr=in->data;
s16 tmp_l,tmp_r;
u16 rlen;
if(pcm_lr_exchange_flag == 0)
return in->data_len;
rlen = in->data_len>>2; ////u8*4
//printf("%d\n",in->offset);
if(in->channel == 2){
if(in->offset==0){
for(u16 i=0; i<rlen; i++) ///lrlrlr......
{
// tmp_l = f_lr[i*2];
// tmp_r = f_lr[i*2+1];
f_lr[i*2] = f_lr[i*2]/2;
f_lr[i*2+1] = f_lr[i*2+1]/2;
}
}
}
return in->data_len;
}
- 如果客户数据处理需要定长,可参考以下做法添加以下流程处理

- 初始化CBUFF:用于数据缓存
#define USER_DATA_LEN 128
static cbuffer_t user_dac_cbuf_in;
static cbuffer_t user_dac_cbuf_out;
static u8 user_dac_buffer_in[1024];
static u8 user_dac_buffer_out[1024];
cbuf_init(&user_dac_cbuf_in,user_dac_buffer_in, sizeof(user_dac_buffer_in));
cbuf_init(&user_dac_cbuf_out,user_dac_buffer_out, sizeof(user_dac_buffer_out));
- 音频数据添加缓存处理(数据处理在:数据处理部分定长为:USER_DATA_LEN)
int pcm_lr_exchange_deal(void *priv, struct audio_data_frame *in)
{
u8 user_buffer[USER_DATA_LEN*2];
u16 rlen;
if(pcm_lr_exchange_flag == 0)
return in->data_len;
if(in->offset==0){
//写入读缓存
cbuf_write(&user_dac_cbuf_in,in->data,in->data_len);
//处理缓存数据
while (cbuf_get_data_size(&user_dac_cbuf_in) >= USER_DATA_LEN) {
cbuf_read(&user_dac_cbuf_in,user_buffer,USER_DATA_LEN);
s16 *f_lr=user_buffer;
rlen = USER_DATA_LEN>>2;
//数据处理部分
for(u16 i=0; i<rlen; i++) ///lrlrlr......
{
// tmp_l = f_lr[i*2];
// tmp_r = f_lr[i*2+1];
f_lr[i*2] = f_lr[i*2]/2;
f_lr[i*2+1] = f_lr[i*2+1]/2;
}
cbuf_write(&user_dac_cbuf_out,user_buffer,USER_DATA_LEN);
}
//printf("len %d - %d - %d\n",in->data_len,cbuf_get_data_size(&user_dac_cbuf_in),cbuf_get_data_size(&user_dac_cbuf_out));
if(cbuf_get_data_size(&user_dac_cbuf_out)>=in->data_len){
// putchar('*');
cbuf_read(&user_dac_cbuf_out,in->data,in->data_len);
}else{
memset(in->data,0,in->data_len);
}
}
return in->data_len;
}
每次音频解码开始清楚之前的buff
参考应用:清零数据流方式(音乐模式,其他模式参照修改)




int zero_entry_handle(void *priv, struct audio_data_frame *in)
{
memset(in->data,0x00,in->data_len);
return in->data_len;
}
struct __stream_entry *zero_entry = NULL;
/****************************************************/
zero_entry = stream_entry_open(&mixer,zero_entry_handle,0);
entries[entry_cnt++] = &(zero_entry->entry);
/****************************************************/
if(zero_entry){
stream_entry_close(&zero_entry);
zero_entry = NULL;
}
淡出淡入方式:
//淡入:
dvol_handle fade_in_dvol = {
.toggle = 1,
.fade = 1,
.vol = 0,
.vol_max = 16384,
.vol_fade = 0,
.fade_step = 5,
.vol_target = 16384,
};
//数据流加入此数据处理后,声音会从0淡入到16384(0db增益)结束
//一般是16bit数据流处理,位宽注意对齐
audio_digital_vol_run(&fade_in_dvol,in->data,in->data_len);
fade_in_dvol.vol_fade = 0; //淡入前设置,当这个值是16384的时候淡入结束
//淡出
dvol_handle fade_out_dvol = {
.toggle = 1,
.fade = 1,
.vol = 16384,
.vol_max = 16384,
.vol_fade = 16384,
.fade_step = 6,
.vol_target = 0,
};
//数据流加入此数据处理后,声音会从16384(0db)淡入到0(静音)结束
//一般是16bit数据流处理,位宽注意对齐
audio_digital_vol_run(&fade_out_dvol,in->data,in->data_len);
fade_in_dvol.vol_fade = 16384; //淡出前设置,当这个值是0的时候淡入结束
//淡出需要特别注最后的音量是0,如果下次数据流开启时还继续执行的话,就会没有声音!!!!!!
- 700NSDK\697NSDK添加A2DP第三方音频算法 700N\697N耳机的SDK需要放在a2dp_dec_output_handler内的remain部分进行缓存,如图:
注意:我们自带的EQ和DRC等音效算法要关闭,若遇到打印'F'等字眼,很可能是跑不过来。此时需要屏蔽掉第三方算法,先验证不跑第三方需要多大时钟,再将时钟提高到128M验证能否跑得过来。如果提高到128M还跑不动算法,则该算法需要的算力为:128M-无算法的时钟大小。可以处理算法前后拉IO口抓波形看一次处理需要多长时间,若要求算力太高,则优先优化算法运行速度。 - AC695X 多路WAV叠加播放 【金山文档】 AC695X多路wav叠加参考资料
AC695X多路wav叠加参考资料.zip - 可视化耳机SDK,音频数据淡入淡出 音频PCM数据淡入淡出(可视化).otl
十、混响
1、在线调试方法
2、混响高低音调节
对应修改修改方法:【金山文档】 混响高低音接口_AC695N_soundbox_sdk_release_3.1.0 ->仓库链接
3、相关补丁
- AC695N_soundbox_sdk_release_3.1.0 混响接口 mic_effect_set_echo_delay等用不了 解决方法:对比替换下面的文件
【金山文档】 AC695N_soundbox_sdk_release_3.1.0(修复混响接口用不了问题)
3、混响数据流叠加linein解码
AC695N_soundbox_sdk_release_3.1.0_混响叠加linein通道.zip
十一、EQ
在线调试方法 串口:【金山文档】 新Tools的使用
蓝牙:
701N 普通SDK:

可视化SDK:
离线调试方法 与在线调试方法不一样的地方是,需要注意SDK的EQ版本,工具上要对应选择。


实现等响功能 原理:在每一路解码通道添加一个EQ处理的节点去实现等响音量调节功能
实现:
具体添加可以参考DEMO里面宏定义实现内容
///*********************************************************************************//
// 等响功能
//1. 通过EQ去实现该功能
//*********************************************************************************//
#define AUDIO_EQUAL_LOUDNESS_EQ_EN
AC695N_soundbox_sdk_release_3.1.0
【金山文档】 EQ_等响度_AC695N_soundbox_sdk_release_3.1.0
AC700N_earphone_release_V1.3.5
AC700N_earphone_release_V1.3.5_bt_多带wdrc
demo实现的曲线类似这种,音源音量变化较大的情况下,低频增益变化不大(实际曲线和效果取决于参数、喇叭、结构)
demo代码默认开启了此功能,默认使能了EQ在线调试,可以借用辅听EQ来调试
在线调试时使用辅听EQ调试,没有辅听功能时,调试好可以直接使用辅听EQ的DRC参数,只需要将READ_HEARING_AID_PARAM置1,不用修改其他代码。
如果有辅听功能,用辅听EQ调完参数后要把参数填到mix_out_wdrc_get_filter_info函数中,并将READ_HEARING_AID_PARAM置0。
其中mix_out_wdrc_p._p.wdrc[x].threshold_num取决于下图位置,有4个就是4,参数填到mix_out_wdrc_p._p.wdrc[x].threshold[x]中
int mix_out_wdrc_get_filter_info(void *drc, struct audio_drc_filter_info *info)
{
//多带
mix_out_wdrc_p.nband = 2; //max<=3,1:全带 2:两段 3:三段
//WDRC
mix_out_wdrc_p.type = 3; //0:没有使能限幅和压缩器,1:限幅器 2:压缩器 3:wdrc(wdrc不支持3带)
mix_out_wdrc_p.low_freq = 200; //中低频分频点
mix_out_wdrc_p.order = 2; //分频器阶数, 2或者4
//低频参数
mix_out_wdrc_p._p.wdrc[0].attacktime = 100;
mix_out_wdrc_p._p.wdrc[0].releasetime = 200;
mix_out_wdrc_p._p.wdrc[0].inputgain = 0;
mix_out_wdrc_p._p.wdrc[0].outputgain = 0;
mix_out_wdrc_p._p.wdrc[0].threshold_num = 4; //点数,也就是限幅器、压缩器的总数
mix_out_wdrc_p._p.wdrc[0].rms_time = 25; //RMS时间
mix_out_wdrc_p._p.wdrc[0].algorithm = 0; //detect mode
mix_out_wdrc_p._p.wdrc[0].mode = 0;
//第1个点[0, 0]
mix_out_wdrc_p._p.wdrc[0].threshold[0].in_threshold = 0;
mix_out_wdrc_p._p.wdrc[0].threshold[0].out_threshold = 0;
//第2个点[40, 50]
mix_out_wdrc_p._p.wdrc[0].threshold[1].in_threshold = 40;
mix_out_wdrc_p._p.wdrc[0].threshold[1].out_threshold = 40 + 10;
//第3个点[65, 85]
mix_out_wdrc_p._p.wdrc[0].threshold[2].in_threshold = 65;
mix_out_wdrc_p._p.wdrc[0].threshold[2].out_threshold = 65 + 20;
//第4个点[90, 90]
mix_out_wdrc_p._p.wdrc[0].threshold[3].in_threshold = 90;
mix_out_wdrc_p._p.wdrc[0].threshold[3].out_threshold = 90 + 0;
//高频参数
mix_out_wdrc_p._p.wdrc[1].attacktime = 100;
mix_out_wdrc_p._p.wdrc[1].releasetime = 200;
mix_out_wdrc_p._p.wdrc[1].inputgain = 0;
mix_out_wdrc_p._p.wdrc[1].outputgain = 0;
mix_out_wdrc_p._p.wdrc[1].threshold_num = 2;
mix_out_wdrc_p._p.wdrc[1].rms_time = 25;
mix_out_wdrc_p._p.wdrc[1].algorithm = 0;
mix_out_wdrc_p._p.wdrc[1].mode = 0;
//第1个点[0, 0]
mix_out_wdrc_p._p.wdrc[1].threshold[0].in_threshold = 0;
mix_out_wdrc_p._p.wdrc[1].threshold[0].out_threshold = 0;
//第2个点[90, 90]
mix_out_wdrc_p._p.wdrc[1].threshold[1].in_threshold = 90;
mix_out_wdrc_p._p.wdrc[1].threshold[1].out_threshold = 90;
info->R_pch = info->pch = &mix_out_wdrc_p;
return 0;
}
- EQ切换
AC695X&AC701N
AC701N:
测试代码
#define your_EQ_FILE_NAME SDFILE_RES_ROOT_PATH"eq_cfg_h2.bin"
#define your_EQ_FILE_NAME1 SDFILE_RES_ROOT_PATH"eq_cfg_h1.bin"
void user_eq_change(void)
{
s32 ret = 0;
static u8 flag = 0;
if(flag){
flag = 0;
eff_file_switch(your_EQ_FILE_NAME);
}
else{
flag = 1;
eff_file_switch(your_EQ_FILE_NAME1);
}
}
701音箱SDK需要注意音效文件命名长度要一致,或者自行修改此处逻辑:
3.1.0_SDK_EQ文件切换
effect_adj.c文件改动
//更改
//#define EFF_CFG_FILE_NAME SDFILE_RES_ROOT_PATH"eq_cfg_hw.bin"
u8 EFF_CFG_FILE_NAME[48] = SDFILE_RES_ROOT_PATH"eq_cfg_hw.bin";
//添加
/*
*设置eq更新标志,eq运行时会重新调用系数回调,获取系数
* */
static void cur_eq_update(u32 eq_name)
{
struct audio_eq *eq = get_cur_eq_hdl_by_name(eq_name);
local_irq_disable();
if (eq) {
eq->mask[0] = (u32)-1;
eq->mask[1] = (u32)-1;
eq->updata = 1;
}
local_irq_enable();
}
/*
*设置drc更新标志,drc运行时会重新调用系数回调,获取系数
* */
static void cur_drc_update(u32 drc_name)
{
struct audio_drc *drc = get_cur_drc_hdl_by_name(drc_name);
local_irq_disable();
if (drc) {
drc->updata = 1;
}
local_irq_enable();
}
/*
*效果文件切换
*path:效果文件路径
* */
void eff_file_switch(u8 *path)
{
//更新效果文件路径
memcpy(EFF_CFG_FILE_NAME, path, strlen(path));
//重新解析效果文件
phone_eff_analyze_data();
music_eff_analyze_data();
#if (TCFG_EQ_ENABLE != 0)
cp_eq_file_seg_to_custom_tab();
#endif
#if defined(LINEIN_MODE_SOLE_EQ_EN) && LINEIN_MODE_SOLE_EQ_EN
linein_eff_analyze_data();
#endif
#if defined(TCFG_MIC_EFFECT_ENABLE) && TCFG_MIC_EFFECT_ENABLE
for (int i = 0 ; i < EFFECT_REVERB_PARM_MAX; i++) {
mic_eff_analyze_data(i);
}
#endif
//设置需要更新的模块
audio_vbass_update_demo(AEID_MUSIC_VBASS, &vbass_parm.parm, vbass_parm.is_bypass);
int i = 0;
for(i=0;i<4;i++){
cur_drc_set_update(AEID_MUSIC_DRC, i, &music_mode.drc_parm.wdrc_parm[i]);
cur_drc_set_bypass(AEID_MUSIC_DRC, i,&music_mode.drc_parm.wdrc_parm[i].is_bypass);
}
eq_mode_set(EQ_MODE_CUSTOM);
mic_effect_change_mode(0);//这里的0是使用0号的话筒音效,默认还有1、2、3、4号
}
eq_config.c文件改动
//更改
#define EQ_FILE_CP_TO_CUSTOM 1
void cp_eq_file_seg_to_custom_tab()
{
printf("[%s]------------Row[%d]",__FUNCTION__,__LINE__);
#if EQ_FILE_CP_TO_CUSTOM
u8 nsection = music_mode.eq_parm.seg_num;
struct eq_seg_info *seg = eq_tab_custom;
if (nsection > ARRAY_SIZE(eq_tab_custom)) {
log_e("music nsection:%d > custom nsection:%d\n", nsection, ARRAY_SIZE(eq_tab_custom));
return ;
}
printf("music_mode.eq_parm.global_gain == %d\n",(int)((int)(music_mode.eq_parm.global_gain*100)/100));
globa_gain_tab[EQ_MODE_CUSTOM] = music_mode.eq_parm.global_gain;
memcpy(seg, music_mode.eq_parm.seg, sizeof(struct eq_seg_info)*nsection);
eq_mode=EQ_MODE_CUSTOM;
#endif
}
测试代码
#define your_EQ_FILE_NAME SDFILE_RES_ROOT_PATH"eq_cfg_h2.bin"
#define your_EQ_FILE_NAME1 SDFILE_RES_ROOT_PATH"eq_cfg_h1.bin"
void user_eq_change(void)
{
s32 ret = 0;
static u8 flag = 0;
if(flag){
flag = 0;
eff_file_switch(your_EQ_FILE_NAME);
}
else{
flag = 1;
eff_file_switch(your_EQ_FILE_NAME1);
}
}
注意点
1、必须必须必须使用新版EQ工具生成的EQ文件
2、文件放在SDK\cpu\br23\tools\download\standard
3、在SDK\cpu\br23\tools\download\standard\download.bat文件此处后面添加文件,比如:-res tone.cfg eq_cfg_h1.bin eq_cfg_h2.bin
demo程序下载:
696N_170
695_3.1.0_change_eq_demo
链接:https://pan.baidu.com/s/1REnAwIE8yVIqB_U0uA0rQg
提取码:8888
V3.0.6及以下旧版本&以及696 V160SDK版本 多外部EQ文件切换修改方法
#define TCFG_USE_EQ_FILE 1 //离线eq使用配置文件还是默认系数表 1:使用文件 0 使用默认系数表
注意:过老的版本可以无法使用该方法。
cpu\br25\audio_effect\eq_config.c 修改:
EQ_CFG *user_eq_cfg;
/////////////////////////////////////////////
extern s32 eq_file_get_cfg(EQ_CFG * eq_cfg,u8 * path);
#define EQ_FILE_NAME SDFILE_RES_ROOT_PATH"eq_cfg_hw.bin"
#define EQ_FILE1_NAME SDFILE_RES_ROOT_PATH"eq_cfg_hw1.bin"
#define EQ_FILE2_NAME SDFILE_RES_ROOT_PATH"eq_cfg_hwja.bin"
#define EQ_FILE3_NAME SDFILE_RES_ROOT_PATH"eq_cfg_hwco.bin"
#define EQ_FILE4_NAME SDFILE_RES_ROOT_PATH"eq_cfg_hwcl.bin"
#define EQ_FILE5_NAME SDFILE_RES_ROOT_PATH"eq_cfg_hwpo.bin"
#define EQ_FILE6_NAME SDFILE_RES_ROOT_PATH"eq_cfg_hwro.bin"
#define EQ_FILE7_NAME SDFILE_RES_ROOT_PATH"eq_cfg_hwno.bin"
void set_user_eq_change(u8 flag)
{
s32 ret =0;
if(user_eq_cfg){
if(flag==0){
ret = eq_file_get_cfg(user_eq_cfg,EQ_FILE_NAME);
}
else if(flag==1){
ret = eq_file_get_cfg(user_eq_cfg,EQ_FILE1_NAME);
}
else if(flag==2){
ret = eq_file_get_cfg(user_eq_cfg,EQ_FILE2_NAME);
}
else if(flag==3){
ret = eq_file_get_cfg(user_eq_cfg,EQ_FILE3_NAME);
}
else if(flag==4){
ret = eq_file_get_cfg(user_eq_cfg,EQ_FILE4_NAME);
}
else if(flag==5){
ret = eq_file_get_cfg(user_eq_cfg,EQ_FILE5_NAME);
}
else if(flag==6){
ret = eq_file_get_cfg(user_eq_cfg,EQ_FILE6_NAME);
}
else if(flag==7){
ret = eq_file_get_cfg(user_eq_cfg,EQ_FILE7_NAME);
}
}
}
//////////////////////////////////////////////
int eq_init(void)
{
audio_eq_init();
eq_adjust_parm parm = {0};
#if TCFG_EQ_ONLINE_ENABLE
parm.online_en = 1;
#endif
if (config_filter_coeff_fade_en) {
parm.fade_en = 1;
}
#if TCFG_USE_EQ_FILE
parm.file_en = 1;
#endif
#if EQ_FILE_CP_TO_CUSTOM
parm.file_en = 1;
parm.type_gain_tab = type_gain_tab;
#endif
#if TCFG_DRC_ENABLE
parm.drc = 1;
#endif
#if TCFG_USER_TWS_ENABLE
parm.tws = 1;
#endif
#if APP_ONLINE_DEBUG
parm.app = 1;
#endif
#if (RCSP_ADV_EN)&&(JL_EARPHONE_APP_EN)&&(TCFG_DRC_ENABLE == 0)
parm.limit_zero = 1;
#endif
#if TCFG_EQ_DIVIDE_ENABLE
parm.stero = 1;
parm.mode_num = 8;
#endif
if (!parm.stero) {
parm.mode_num = 5;// 一共有多少个模式
/* #ifdef DAC_OUTPUT_FRONT_LR_REAR_LR */
/* #if (TCFG_AUDIO_DAC_CONNECT_MODE == DAC_OUTPUT_FRONT_LR_REAR_LR) */
/* parm.mode_num = 6; */
/* #endif */
/* #endif */
}
#if !TCFG_USE_EQ_FILE
#if TCFG_PHONE_EQ_ENABLE
parm.phone_eq_tab = (void *)phone_eq_tab_normal;
parm.phone_eq_tab_size = ARRAY_SIZE(phone_eq_tab_normal);
#endif
parm.ul_eq_tab = (void *)ul_eq_tab_normal;
parm.ul_eq_tab_size = ARRAY_SIZE(ul_eq_tab_normal);
#endif
parm.eq_tool_tab = eq_tool_tab;
parm.eq_mode_use_idx = (u8 *)eq_mode_use_idx;
parm.eq_type_tab = (void *)get_eq_mode_tab();
#if TCFG_USER_EQ_MODE_NUM
parm.type_num = TCFG_USER_EQ_MODE_NUM;//EQ_MODE_MAX;
#endif
parm.section_max = EQ_SECTION_MAX;
EQ_CFG *eq_cfg = eq_cfg_open(&parm);
user_eq_cfg = eq_cfg;
if (eq_cfg) {
eq_cfg->priv = eq_cfg;
eq_cfg->send_cmd = ci_send_cmd;
#if APP_ONLINE_DEBUG
if (eq_cfg->app) {
app_online_db_register_handle(DB_PKT_TYPE_EQ, eq_app_online_parse);
}
#endif
#if EQ_FILE_CP_TO_CUSTOM
eq_file_cp_to_custon_mode_fun(eq_cfg);
#else
for (int i = 0; i < eq_cfg->mode_num; i++) {
if (eq_cfg->eq_type == EQ_TYPE_MODE_TAB) {
set_global_gain(eq_cfg, i, 0);
drc_default_init(eq_cfg, i);
}
}
#endif
}
return 0;
}
__initcall(eq_init);
download.bat中添加外部EQ bin文件

最后在需要的地方切换EQ使用函数:
void set_user_eq_change(u8 flag);
注意:如果程序还打开 “#define TCFG_AUDIO_OUT_EQ_ENABLE 1 //mix_out后高低音EQ”还需要做以下修改:
AC697X
文件eq效果当成自定义模式参与效果切换
原理:把外部EQ读取到系数表,参与到效果切换
实现:具体可参考demo
应用:当存在外部EQ文件需要使用到,同时程序需要切换EQ模式时
【金山文档】 AC697X_eq效果文件参与效果切换使用说明 (1)
https://kdocs.cn/l/cb9fqYSBFRTj
697N_V2.3.x版本SDK多个EQ文件切换
添加如下代码到eq_config.c文件中,download.bat中请自行添加eq bin文件
void eq_cfg_open(void);
#define EQ_FILE_0 SDFILE_RES_ROOT_PATH"eq_cfg_hw.bin"
#define EQ_FILE_1 SDFILE_RES_ROOT_PATH"eq_cfg_hw1.bin"
#define EQ_FILE_2 SDFILE_RES_ROOT_PATH"eq_cfg_hw2.bin"
const u8 *eq_file_list[] = { //需要更多EQ文件,将路径填到这个数组中
EQ_FILE_0,
EQ_FILE_1,
EQ_FILE_2,
};
static u8 file_index = 0; //当前eq文件的序号
/**
* @brief 切换EQ文件
*
* @param file 0xff:按照eq_file_list的顺序循环切换,n(0~(ARRAY_SIZE(eq_file_list)-1)):切换到eq_file_list中的第n个文件
*/
void eq_file_switch(u8 file)
{
if(file == 0xff){
file_index++;
}
else{
file_index = file;
}
if(file_index >= ARRAY_SIZE(eq_file_list)){
file_index = 0;
}
eq_cfg_open();
if(p_eq_cfg){
p_eq_cfg->mode_updata = 1;
}
}
修改void eq_cfg_open(void)函数
if (eq_file_get_cfg(p_eq_cfg, eq_file_list[file_index]))
调用方法:
void eq_file_switch(u8 file);
eq_file_switch(0xff);//循环切换数组中的文件
// eq_file_switch(0);//切换到数组中的第1个文件

AC698X
多EQ文件切换
#define EQ_FILE_0 SDFILE_RES_ROOT_PATH"eq_cfg_hw.bin"
#define EQ_FILE_1 SDFILE_RES_ROOT_PATH"eq_cfg_hw1.bin"
#define EQ_FILE_2 SDFILE_RES_ROOT_PATH"eq_cfg_hw2.bin"
const u8 *eq_file_list[] = { //需要更多EQ文件,将路径填到这个数组中
EQ_FILE_0,
EQ_FILE_1,
EQ_FILE_2,
};
static u8 file_index = 0; //当前eq文件的序号
/**
* @brief 切换EQ文件
*
* @param file 0xff:按照eq_file_list的顺序循环切换,n(0~(ARRAY_SIZE(eq_file_list)-1)):切换到eq_file_list中的第n个文件
*/
void eq_file_switch(u8 file)
{
if(file == 0xff){
file_index++;
}
else{
file_index = file;
}
if(file_index >= ARRAY_SIZE(eq_file_list)){
file_index = 0;
}
EQ_CFG *eq_cfg = get_eq_cfg_hdl();
eq_cfg->eq_type = EQ_TYPE_MODE_TAB;
eq_cfg_default_init(get_eq_cfg_hdl());
drc_default_init(get_eq_cfg_hdl(), song_eq_mode);
if (!eq_file_get_cfg(get_eq_cfg_hdl(), eq_file_list[file_index])) {
eq_cfg->eq_type = EQ_TYPE_FILE;
eq_cfg->mode_updata = 1;
}
}
698在EQ初始化重设EQ
eq_file_switch(file_index);
调用方法:
void eq_file_switch(u8 file);
eq_file_switch(0xff);//循环切换数组中的文件
// eq_file_switch(0);//切换到数组中的第1个文件

2M包开SPP在线调试EQ和TWS在线EQ调试同步
2M包btstack.a没有开启SPP协议不能在线调EQ,需要按照698问题处理文档说明修改btstack.a改为4M库。
如果空间不足,V125版本也可以修改下面的库:
【金山文档】 btstack_2M_flash
TWS在线调试EQ2M包未同步参数到从机:默认未开启主从传参数同步,需按照下图修改。
AC692X
两个外部EQ文件切换demo:
链接:https://pan.baidu.com/s/1yKRqpBB124b7ugiLM8AF7w
提取码:8888
AC700N/JL701N
u8 curr_eq_index = 0;
const u8 *eq_file_list[] = {
(u8 *)SDFILE_RES_ROOT_PATH"norm.bin",
(u8 *)SDFILE_RES_ROOT_PATH"norm_p.bin",
(u8 *)SDFILE_RES_ROOT_PATH"bass.bin",
(u8 *)SDFILE_RES_ROOT_PATH"bass_p.bin",
(u8 *)SDFILE_RES_ROOT_PATH"light.bin",
(u8 *)SDFILE_RES_ROOT_PATH"light_p.bin",
};
/**
* @brief 切换EQ效果文件的接口
*
* @param index 对应eq_file_list效果,0xff:循环切换
*/
void eq_effect_switch(u8 index)
{
if(index == 0xff){
curr_eq_index++;
if(curr_eq_index >= ARRAY_SIZE(eq_file_list)){
curr_eq_index = 0;
}
}
else{
if(index >= ARRAY_SIZE(eq_file_list)){
return;
}
curr_eq_index = index;
}
#if (!TCFG_EQ_ONLINE_ENABLE)
EQ_CFG *eq_cfg = get_eq_cfg_hdl();
if(!eq_cfg){
return;
}
eq_cfg->eq_type = EQ_TYPE_FILE;
int ret = eq_file_get_cfg(eq_cfg, eq_file_list[curr_eq_index]);
printf("eq_effect_switch %d ret %d\n", curr_eq_index, ret);
#endif
}

可视化工具AC700+1T2
u8 curr_eq_index = 0;
const u8 *eq_file_list[] = {
(u8 *)FLASH_RES_PATH "eq_cfg_hw.bin",
(u8 *)FLASH_RES_PATH "eq_cfg_hw1.bin",
(u8 *)FLASH_RES_PATH "eq_cfg_hw2.bin",
};
/**
* @brief 切换EQ效果文件的接口
*
* @param index 对应eq_file_list效果,0xff:循环切换
*/
void eq_effect_switch(u8 index)
{
if(index == 0xff){
curr_eq_index++;
if(curr_eq_index >= ARRAY_SIZE(eq_file_list)){
curr_eq_index = 0;
}
}
else{
if(index >= ARRAY_SIZE(eq_file_list)){
return;
}
curr_eq_index = index;
}
#if (!TCFG_EQ_ONLINE_ENABLE)
EQ_CFG *eq_cfg = get_eq_cfg_hdl();
if(!eq_cfg){
return;
}
eq_cfg->eq_type = EQ_TYPE_FILE;
int ret = eq_file_get_cfg(eq_cfg, eq_file_list[curr_eq_index]);
y_printf("eq_effect_switch %d ret %d\n", curr_eq_index, ret);
// syscfg_write(CFG_USER_DEFINE_EQ_INDEX, &curr_eq_index, sizeof(curr_eq_index));
#endif
}
①找个地方放上面代码
②这里打开
③注意EQ版本和工具生产的EQ文件版本要对应
⑤在下载目录的download.bat文件里加入要添加的.bin文件
⑥测试用例,音乐播放暂时键触发
case API_MUSIC_PP:
y_printf("API_MUSIC_PP");
eq_effect_switch(0xFF);
// if (state != BT_CALL_HANGUP) {
// break;
// }
// user_send_cmd_prepare_for_addr(bt_addr, USER_CTRL_AVCTP_OPID_PLAY, 0, NULL);
break;
⑦如果需要关机保存切换的效果,存一下VM(注意不要在中断里读写VM,VM的ID不要与其他的冲突)
#define CFG_USER_DEFINE_EQ_INDEX 10
//************************************************//
syscfg_write(CFG_USER_DEFINE_EQ_INDEX, &curr_eq_index, sizeof(curr_eq_index));
//************************************************//
#if (!TCFG_EQ_ONLINE_ENABLE)
u8 user_eq_index = 0;
int ret = syscfg_read(CFG_USER_DEFINE_EQ_INDEX, &user_eq_index, sizeof(user_eq_index));
y_printf("ret = %d, user_eq_index = %d", ret, user_eq_index);
if ((ret>0) && (user_eq_index >= 0 && user_eq_index <= ARRAY_SIZE(eq_file_list))) {
eq_effect_switch(user_eq_index);
}
#endif



最新可视化工具切换EQ文件方法
先添加EQ效果

使用如下接口切换添加好的多个EQ
#define CFG_FILE_MAX_EQ_NUM 3//有多少个EQ效果这里就填多少
static u8 curr_eq_index = 0;
void eq_effect_switch(u8 index)
{
if(index == 0xff){
curr_eq_index++;
}
else{
curr_eq_index = index;
}
if(curr_eq_index >= CFG_FILE_MAX_EQ_NUM){
curr_eq_index = 0;
}
eq_file_cfg_switch("MusicEqBt", curr_eq_index);
}
- 自定义系数表动态更新
//系数切换
void eq_sw_demo()
{
eq_mode_sw();//7种默认系数切换
}
//获取当前eq系数表类型
void eq_mode_get_demo()
{
u8 mode ;
mode = eq_mode_get_cur();
}
//宏TCFG_USE_EQ_FILE配0
//自定义系数表动态更新
//本demo 示意更新中心截止频率,增益,总增益,如需设置更多参数,请查看eq_config.h头文件的demo
void eq_update_demo()
{
eq_mode_set_custom_info(0, 200, 2);//第0段,200Hz中心截止频率,2db
eq_mode_set_custom_info(5, 2000, 2);//第5段,2000Hz中心截止频率,2db
set_global_gain(get_eq_cfg_hdl(), song_eq_mode, -2);//设置普通音乐eq 总增益 -2db(可避免最大增益大于0db,导致失真)
eq_mode_set(EQ_MODE_CUSTOM);//设置系数、总增益更新
}
- 697N加载了eq_cfg_hw.bin后切换EQ无效果---调通话EQ必须加载EQ文件
int eq_mode_set(u8 mode)
{
#if !TCFG_USE_EQ_FILE
if (mode >= get_eq_mode_max()) {
mode = 0;
}
eq_mode = mode;
if (p_eq_cfg && ((p_eq_cfg->eq_type == EQ_TYPE_MODE_TAB) || (p_eq_cfg->eq_type == EQ_TYPE_FILE))) {
p_eq_cfg->eq_type = EQ_TYPE_MODE_TAB;
p_eq_cfg->mode_updata = 1;
}
#endif
return 0;
}
- SDK-eq内部系数表切换修改为淡入淡出配置
AC695X
- AC695N_soundbox_sdk_release_3.1.0 310SDK底层已添加了eq系数表切换淡入淡出处理 695n310sdk在eq节点open处添加如下修改:

- AC695N_soundbox_其他系列sdk 可以参考以下的修改demo,demo程序在695n200sdk上修改 (客户自行获取AC695N_soundbox_sdk_release_2.0.0公版程序与demo程序进行对比)
AC695N_soundbox_sdk_release_2.0.0_eq系数表切换添加淡入淡出demo
链接:https://pan.baidu.com/s/1cnc8Ib3SnJS_Z-dE0sIoKQ
提取码:8888
AD697X
AC897N_AD697N_earphone_release_V2.4.0_EQ_fade
AC897N_AD697N_earphone_release_V2.4.0_EQ_fade.zip
支持EQ默认系数和EQ文件淡入淡出
可调参数
AC700N
br36_V138_eq_xfade.zip -- 适用于AC700N_earphone_release_V1.3.8
8. 可视化高低音使用方法 添加高低音节点
要更改节点名称为下图
修改编译问题


__attribute__((weak))
u8 get_mic_current_scene()
{
return 0;
}
使用demo
十二、虚拟低音

700N参数调节

701N参数调节

十三、按键提示音
1、AC697X 按键音添加demo程序
demo提取:
链接:https://pan.baidu.com/s/1yvFNLORiDYy5s4a5Vi3Xew
提取码:8888
跟以下宏KEY_TONE_EN 看:
2、AC698X 按键音添加demo程序
demo提取:
链接:https://pan.baidu.com/s/19NqqYFUsyFX2TYsKDFsmpg
提取码:8888
跟以下宏KEY_TONE_EN 看:

十四、MP3文件无缝播放
- AD697x提示音无缝播放 【金山文档】 AD697_MP3文件无缝播放demo_20221213
https://kdocs.cn/l/cdcw4JGspyfQ
主要修改了audio_dec_file.h和audio_dec_file.c
要使用该功能确保以下宏有打开
#define FILE_DEC_REPEAT_EN 1//0 // 无缝循环播放
#define TCFG_DEC_MP3_ENABLE 1//0
需要保证播放时不能进入低功耗,最好取消进入低功耗的配置宏和蓝牙没有连接自动关机的宏(当然也可以自行根据需求去控制)
#define TCFG_LOWPOWER_LOWPOWER_SEL 0//1 //芯片是否进入powerdown
#define TCFG_AUTO_SHUT_DOWN_TIME 0//180 //没有蓝牙连接自动关机时间
在函数 gapless_playback_open 中,将对应的路径换成自己mp3文件的路径,或者将提示音0的文件替换为对应的mp3文件
/**************************************************************
*
* mp3文件循环播放demo
* 调用该demo需要保证不会进入低功耗功能, 也需关闭蓝牙超时关机
*
*************************************************************/
void gapless_playback_open(void)
{
printf("--- tone gapless playback ---\n");
if (!close_gapless_playback_timer_id) {
u32 time_sec = 120; //120s
printf("\n>>> time : %d sec\n\n", time_sec);
close_gapless_playback_timer_id = sys_timer_add(NULL, gapless_playback_close, 120 * 1000);
}
if (!file_sound) {
file_sound = fopen("mnt/sdfile/res/tone/noise.*", "r"); //mp3文件路径
if (!file_sound) {
printf("file_sound open failed!!\n");
}
}
/* 打开循环播放, 需要关闭蓝牙超时关机的功能。也需要避免进入低功耗 */
file_dec_gapless_playback_open(file_sound);
}
time_sec是定时时间,根据需求修改
十五、简易数据音频解码实现
1. 该工程在原来的基础上实现 PCM数据解码及文件数据解码。
2. 使用注意点如下
a. user_decoder.c 主要添加的文件
b. void user_dec_test(void) 函数为提供的测试函数
c. 用户主要自行修改文件接口如下(读数据接口):
PCM 解码接口 :user_dec_read
FILE 解码接口:file_input
d. FILE 解码 提供一个 测试示范:USER_DEC_TEST //使用默认SD0 里面的 文件名进行测试
PCM 解码 : 默认播放正弦波
主要文件:user_decoder.c https://kdocs.cn/l/crIImErNZ6DU
/*----------------------------------------------------------------------------*/
/**@brief 测试自定义解码函数
@param
@return
@note
*/
/*----------------------------------------------------------------------------*/
void user_dec_test(void)
{
static u8 flag = 0;
// //* FILE
if(flag){
user_file_dec_open(USER_DEC_TEST__FILE,AUDIO_CODING_M4A);
}else{
//user_file_dec_open(USER_DEC_TEST2__FILE,AUDIO_CODING_MP3);
audio_dec_file_app_close(user_file_dec);
}
flag = !flag;
}
SDK版本_AC695N_soundbox_sdk_release_3.1.0 :https://kdocs.cn/l/ccmSKEeSCXY2
6082_1.2.2_sdk接收SPI数据解码demo
链接:https://pan.baidu.com/s/16My0ixCQPIEgTVxFXbQKjQ
提取码:8888
JL701N 参考接口:

十六、辅听耳机
- JL701N立体声耳机使用两个mic分别输出到左右声道 【金山文档】 ac701n_earphone_release_v1.6.0_立体声双mic辅听,gitlab链接
ac701n_earphone_release_v1.6.0_立体声双mic辅听.zipV140旧版本:ac701n_earphone_release_v1.4.0_立体声双mic辅听.zip
以上demo实现立体声耳机的mic0输出到左声道,mic1输出到右声道
可修改mic的通道
如果左右mic反了,在下面这个位置对调左右声道的数据,点击查看对调方法
- JL700V1.3.8版本移植--why 20260729
AC700N_earphone_release_V1.3.8_立体声双mic辅听.zip
十七、第三方算法添加
- 第三方算法添加参考文档 第三方音效算法接入介绍_20230712.01.pdf
2. PCM DELAY用第三方算法实现demo
AC701N patch06之前的版本用第三方算法节点实现新版本的pcm delay节点功能.otl
十八、变速变调
宏定义:
//696/695/701
#define TCFG_SPEED_PITCH_ENABLE DISABLE//
b

变速有具体的换算方法
static int get_speedV(float speed)
{
int speedV;
if (speed >= 1) {
//变快
speedV = (256 - 256 / speed) * 80 / 140 + 80;
} else {
//变慢
speedV = 80 - (256 / speed) * 80 / 450;
}
return speedV;
}
FAQ
1、AC696N_soundbox_sdk_release_1.6.0
【音箱SDK1.6.0】修复开混响EQ和蓝牙EQ同时开导致蓝牙播歌卡顿
替换一个media_app.a文件
media_app(SDK1.6.0_修复开混响EQ和蓝牙EQ同时开导致蓝牙播歌卡顿)
2、AC700X 双声道差分和单声道差分动态切换例子
1、程序默认设置为双声道差分输出 DAC_OUTPUT_LR

- 当要动态切换到单声道差分输出时,直接拿左右声道数据相加除2处理,以达到单声道差分输出效果(左右声道耦合输出):

s16 *buff = data;
u16 temp_len = len/2;
u16 i = 0;
if(typeC_insert == 0) //切换条件,自己根据应用定义使用
{
while(i<temp_len)
{
s16 temp = (buff[i]+buff[i+1])/2; //左右声道相加除以2
buff[i] = temp; //把叠加后的数据分别赋予左右声道
buff[i+1] = temp; //把叠加后的数据分别赋予左右声道
i+=2;
}
}
- 二代音效算法音效文件汇总
20段EQ
【金山文档】 music_base20
https://kdocs.cn/l/ceJB5IRSuM7k
虚拟低音
【金山文档】 music_base_vbass
https://kdocs.cn/l/cooqAsLBgN2r
DRC
1、增加2带drc和3带drc的音效文件到effects_file目录(music_base_mdrc.bin、music_base_mdrc_3b.bin)
2、使能宏#define TCFG_AUDIO_MDRC_ENABLE 1
3、在download.c内添加copy .\effect_file\music_base_mdrc.bin eq_cfg_hw.bin
例如:
2段DRC
【金山文档】 music_base_mdrc
https://kdocs.cn/l/cdoeLsUYdEXS
3段DRC
【金山文档】 music_base_mdrc_3b
https://kdocs.cn/l/ctTaycra29eT
3段DRC+虚拟低音
【金山文档】 music_base_mdrc_3b_vbass
https://kdocs.cn/l/cqd9Zv5omOQa
3段DRC+虚拟低音+后级EQ

【金山文档】 music_base_mdrc_3b_vbass_eq2
https://kdocs.cn/l/cnLgQTJVotLC
3段DRC+全带DRC
【金山文档】 music_base_mdrc_3b_and_wholedrc
https://kdocs.cn/l/cpQ13exjIwzV
带混响的音效
【金山文档】 mic_effect_echo_mdrc_3b
https://kdocs.cn/l/ca3bMNuo3UXW

JL701N 带混响的音效文件


【金山文档】 JL701N_mic_effect_2023年7月6日
https://kdocs.cn/l/caM5PivD6hVg
如果在使用695N音效文件中话筒的EQ段从5段改为10段,替换以下文件和修改以下代码
1、代码修改
2、替换音效文件
【金山文档】 mic_effect_full
https://kdocs.cn/l/csXacB9BZcg1



