翻译耳机、语音识别应用demo
📎 原始文档:https://www.kdocs.cn/l/csk1MNwvvo3R 翻译耳机、语音识别应用demo
新文档已转移至:翻译耳机、语音识别,本链接不再更新维护
关于翻译耳机和语音识别
耳机只需要上传语音数据给APP,翻译和识别由APP实现
耳机可实现解码功能,播放app下发的音频(比如翻译好的语音)
翻译耳机常见形态/功能
普通录音
普通录音只需要耳机将mic采到的音频数据发给手机APP,手机APP负责对音频数据进行存储、识别、翻译。
通话录音
通话录音需要耳机将mic采到的音频数据和手机通话下行数据发给手机APP,手机APP负责对音频数据进行存储、识别、翻译。
音频翻译
音频翻译应用于mic、音频和视频的声音翻译,耳机将音频数据传给app,app翻译后再发给耳机播放
通话翻译
通话翻译的两种应用:翻译通话下行音频,翻译通话上行音频
翻译通话下行音频,耳机将通话下行数据发给app,app将翻译好的音频发回给耳机播放,替代原始下行音频
翻译通话上行音频,耳机将mic数据发给app,app将翻译好的音频发回给耳机,替代上行数据发给手机通话
同声翻译
两个不同语言的人对话,一人一个耳机,耳机采集佩戴人说话的声音,发给app翻译后在对方的耳机播出来
方案芯片选型
普通录音
支持的语音数据通信方式:BLE、SPP、ESCO
支持的芯片系列:
BLE、SPP:697N、700N、701N、708N、709N、689N、710N。
ESCO:走HFP,所有支持蓝牙通话的芯片都支持(实现原理同手机语音助手,需要APP获得手机权限)
通话录音
支持的语音数据通信方式:BLE、SPP
支持的芯片系列:
BLE、SPP:701N、708N、709N、689N。
音频翻译
支持的语音数据通信方式:BLE、SPP、ESCO
支持的芯片系列:
BLE、SPP:701N、708N、709N、689N。
ESCO:仅支持mic翻译,走HFP,所有支持蓝牙通话的芯片都支持(实现原理同手机语音助手,需要APP获得手机权限)
通话翻译
支持的语音数据通信方式:BLE、SPP
支持的芯片系列:
BLE、SPP:701N。
同声翻译
支持的语音数据通信方式:BLE、SPP、ESCO
支持的芯片系列:
BLE、SPP:701N、708N、709N、689N。
ESCO:走HFP,所有支持蓝牙通话的芯片都支持(实现原理同手机语音助手,需要APP获得手机权限),需要手机同时连接两个耳机的经典蓝牙,目前还没有APP用这种方案做同声翻译
开mic(不带编码),然后通过SPP或者BLE发出去
- SDK demo 【金山文档】 AC897N_AD697N_earphone_release_V2.4.0_mic_tx
https://kdocs.cn/l/ctxL3VWuXMo5
【金山文档】 AC700N_earphone_release_V1.3.7_mic_tx
https://kdocs.cn/l/ckNSBHOeqx7w - demo更新记录
2023年8月19日:
- 使用杰理之家的发数接口,自动切换SPP、BLE发数(但是杰理之家不支持SPP和BLE同时连接,需要同时连接的请改用数传,自行修改发数接口u32 send_data(u8 *data, int len))
- 优化BLE发数速率(详见:关于提升BLE传输速率说明.pdf,https://gitee.com/Jieli-Tech/fw-AC63_BT_SDK/issues/I59IL3)
- 移植到其他SDK 添加如下缓存和发数的代码,按照注释添加到代码中
#define SEND_PK_LEN 512
OS_SEM mic_sem;//通知发数任务用的信号量
cbuffer_t mic_cbuf_hd;
u8 *mic_buff = NULL;
u8 spp_send_busy = 0;//spp发数忙状态(没开APP功能才用)
u8 temp[SEND_PK_LEN];//SPP的发数缓存
u32 total_send = 0;//已发数据长度,用于计算发数速率
u32 start_point = 0;//用于计算发数速率
static volatile u8 task_release = 0;
/*
* adc采集到数据之后,通过此接口将数据缓存并通知任务发数
*/
void adc_output_2_mic_task(u8 *buff, u16 len)
{
u32 wlen = cbuf_write(&mic_cbuf_hd, buff, len);
if(wlen != len){
putchar('m');
}
os_sem_post(&mic_sem);
}
#include "spp_user.h"
#include "btstack/avctp_user.h"
void spp_get_operation_table(struct spp_operation_t **interface_pt);
/*
* 发数接口,这里是通过SPP发,需要通过BLE发的话自行在这里调用BLE发数接口发数
* 只需要改里面的发数接口,不需要主动调用
*/
u32 send_data(u8 *data, int len)
{
#ifdef CONFIG_APP_BT_ENABLE
struct spp_operation_t *interface_pt;
spp_get_operation_table(&interface_pt);
u32 ret = interface_pt ? interface_pt->send_data(NULL, data, len) : 0;
#else
spp_send_busy = 1;
u32 ret = user_send_cmd_prepare(USER_CTRL_SPP_SEND_DATA, len, data);
#endif
if(ret){
// printf("send_data err %d\n", ret);
}
return ret ? 0 : len;
}
/*
* 发数繁忙判断,这里只判断了SPP的状态,需要判断BLE状态的话自行在这里调用BLE接口判断
* 只需要改里面的判断接口,不需要主动调用
*/
u8 is_send_busy(void)
{
#ifdef CONFIG_APP_BT_ENABLE
struct spp_operation_t *interface_pt;
spp_get_operation_table(&interface_pt);
return interface_pt ? interface_pt->busy_state() : 0;
#else
return spp_send_busy;
#endif
}
/*
* 尝试发数接口
* 这个函数会判断有数据、且发数不繁忙才会发数,此接口不需要主动调用,由task执行
*/
void try_send(void)
{
if(cbuf_get_data_len(&mic_cbuf_hd) && !is_send_busy()){
putchar('S');
int len = cbuf_get_data_len(&mic_cbuf_hd);
if(len > SEND_PK_LEN){
len = SEND_PK_LEN;
}
OS_ENTER_CRITICAL();
cbuf_read(&mic_cbuf_hd, temp, len);
OS_EXIT_CRITICAL();
u32 wlen = send_data(temp, len);
if(wlen != len){
putchar('E');
//跑到这里说明发数失败了
}
total_send += wlen;
if(sys_timer_get_ms() - start_point >= 2000){
printf("send speed %d Bps\n", 1000 * total_send / (sys_timer_get_ms() - start_point));
start_point = sys_timer_get_ms();
total_send = 0;
}
}
}
/*
* SPP发完回调接口,SPP发完数的时候调用一下
* 这个函数要在SPP发数完毕唤醒接口中调用,需要配合spp_user.h中的接口使用
* 可参考SDK demo中的用法
*/
void spp_wakeup_send_data(void)
{
putchar('K');
spp_send_busy = 0;
if(task_release == 0){
os_sem_post(&mic_sem);
}
}
/*
* 清除SPP繁忙状态,停止发数的时候调用一下,spp断开的时候调用一下
*/
void clr_send_busy(void)
{
spp_send_busy = 0;
}
/*
* 发数任务
*/
void mic_tx_task(void *p)
{
printf("mic_data_task start\n");
while(1){
os_sem_pend(&mic_sem, 0);
if(task_release){
task_release = 2;
continue;
}
try_send();
}
}
#include "bt_tws.h"
void sys_auto_sniff_controle(u8 enable, u8 *addr);
/*
* 创建发数的任务,要在adc采集开始之前执行一下,例如:
mic_data_task_create();
audio_adc_mic_start(&mic_demo->mic_ch);
*/
void mic_data_task_create(void)
{
mic_buff = malloc(ADC_DEMO_BUFS_SIZE*4);
ASSERT(mic_buff);
start_point = sys_timer_get_ms();
printf("mic_buff %x\n", mic_buff);
cbuf_init(&mic_cbuf_hd, mic_buff, ADC_DEMO_BUFS_SIZE*4);
os_sem_create(&mic_sem, 0);
sys_auto_sniff_controle(0, NULL);//关sniff
user_send_cmd_prepare(USER_CTRL_ALL_SNIFF_EXIT, 0, NULL);//退sniff
tws_api_auto_role_switch_disable();//关自动主从切换
clk_set("sys", 64000000);//提高系统时钟
task_release = 0;
int ret = os_task_create(mic_tx_task, NULL, 2, (SEND_PK_LEN * 2 / 128) * 128, 256, "mic_task");
if(ret == 0){
printf("mic_data_task_create succ\n");
return;
}
printf("mic_data_task_create failed\n");
}
/*
* 删除发数的任务,要在停止adc采集之后执行一下,例如:
audio_adc_del_output_handler(&adc_hdl, &mic_demo->adc_output);
audio_adc_mic_close(&mic_demo->mic_ch);
OS_ENTER_CRITICAL();
free(mic_demo);
mic_demo = NULL;
OS_EXIT_CRITICAL();
void mic_data_task_del(void);
mic_data_task_del();
*/
void mic_data_task_del(void)
{
printf("mic_data_task_del\n");
task_release = 1;
while (task_release == 1)
{
os_sem_post(&mic_sem);
os_time_dly(10);
}
os_task_del("mic_task");
os_sem_del(&mic_sem, 0);
printf("mic_data_task_del ok\n");
OS_ENTER_CRITICAL();
free(mic_buff);
mic_buff = NULL;
OS_EXIT_CRITICAL();
clr_send_busy();
sys_auto_sniff_controle(1, NULL);//开sniff
tws_api_auto_role_switch_enable();//开自动主从切换
}
开mic(带编码),然后通过SPP或者BLE发出去
- 697N SDK已支持MIC编码为OPUS或者SPEEX的流程
音频编码选择二选一:
#define TCFG_ENC_OPUS_ENABLE 0
#define TCFG_ENC_SPEEX_ENABLE 0
配置发数接口
#define AUDIO_SEND_MIN_BUF_LEN 200
#define AUDIO_SPEECH_BUF_LEN (3*1024 + 512)
#define AI_PL_DATA_SEND gma_opus_voice_mic_send
AI_ENC_INFO_REGISTER(ai_enc_info, AUDIO_CODING_OPUS, STANDARD_OPUS);
打开宏:
#define BT_MIC_EN 1
获取编码数据:
static int rec_enc_output(void *priv, void *buf, int len)
启动编码:
int ai_mic_rec_start(void)
结束编码:
int ai_mic_rec_close(void)
参考流程:
XM_MMA_EN
2. 700N SDK已支持MIC编码为OPUS或者SPEEX的流程
音频编码选择二选一:
#define TCFG_ENC_OPUS_ENABLE 0
#define TCFG_ENC_SPEEX_ENABLE 0
配置发数接口:
int mic_rec_pram_init(u32 enc_type, u8 opus_type, u16(*speech_send)(u8 *buf, u16 len), u16 frame_num, u16 cbuf_size)
//mic_rec_pram_init(AUDIO_CODING_SPEEX, 0, app_protocol_send_voice_data, 5, 1024);
打开宏:
#define BT_MIC_EN 1
获取编码数据:
static int rec_enc_output(void *priv, void *buf, int len)
启动编码:
int ai_mic_rec_start(void)
结束编码:
int ai_mic_rec_close(void)
参考流程:
AI_APP_PROTOCOL下的APP_PROTOCOL_TME_CODE,APP_PROTOCOL_SPEECH_EN
翻译耳机(仅支持可视化SDK)
- demo JL701N_翻译耳机_V3.0.0.zip-- JL701N_翻译耳机_V3.0.0_patch_01.10
JL708N_翻译耳机_V3.0.0.zip
注意:以下demo需要关闭随机核,改回固定核。修改如下
JL701N_翻译耳机.zip-- 701N V1.0.0 patch_06_01JL701N_翻译耳机_2Amic.rar -- 701N V1.0.0 patch_06_01原立体声MIC+DAC改为MIC+MICJL701N_翻译耳机_1Amic_1Dmic.rar -- 701N V1.0.0 patch_06_01原立体声MIC+DAC改为MIC+数字MIC【金山文档】 701N V1.0.0 patch_04 翻译耳机流程701N V1.0.0 patch_04 翻译耳机流程.zip
AC701_100_1T2_patch04.03-debuaA-fix-siri.zip(修复siri状态异常问题)
更新记录:
2025年3月11日:
支持非通话下使用通话算法节点(打开CONST_REF_SRC,自行修改数据流节点)
2025年3月6日:
处理agc模块死机问题
2025年3月3日:
1、处理通话结束时死机问题
2、处理开机没播提示音的时候,开opus解码没声音的问题
2025年1月7日:
1、升级SDK到V3.0.0大版本
2024年11月20日:
1、buff改为malloc
2、支持录音和anc一起开
修改了以下文件:
2024年8月8日:
1、添加缺失文件处理编译错误
2024年8月6日:
1、升级到SDK版本701N V1.0.0 patch_06_01
2024年6月14日:
1、升级到SDK版本701N V1.0.0 patch_05.32
2、支持单声道和立体声编码切换,支持单声道解码
2024年5月27日:
1、提高BLE发数速率
2024年5月22日:
1、修复SPP发数据时opus帧被拆开导致帧不完整问题
2024年5月8日:
1、修复通话时开启opus解码会卡顿问题
2、修复通话时开启opus解码,解码声音会变调问题
2023年11月8日:
1、恢复公版SDK 的ble配置,不需要指令,ble连上直接发opus数据
2023年11月2日:
- 优化关解码会复位问题
- 优化启动解码的卡顿问题
- 修复定时器重复创建问题 2023年10月30日:
- 修复解码卡顿问题
- 修复解码没数据会导致音乐没声音问题 2023年10月16日:
- 实现音乐和通话喇叭声音上传
最新版本实现的功能:
PB1按键,单击打开mic进行opus编码并通过BLE发送,同时打开opus解码;双击将音乐或者通话远端声音进行opus编码并通过BLE发送,同时打开opus解码;三击将mic和dac(音乐或者通话远端)声音进行opus编码并通过BLE发送。
接口使用说明
typedef enum {
MIC_TO_MONO_OPUS = (1 << 4) | MIC,//mic输出到单声道opus编码器(单声道)
DAC_TO_MONO_OPUS = (1 << 4) | DAC,//dac输出到单声道opus编码器(单声道)
MIC_TO_STERO_OPUS = (2 << 4) | MIC,//mic输出到立体声opus编码器(单声道)
DAC_TO_STERO_OPUS = (2 << 4) | DAC,//dac输出到立体声opus编码器(单声道)
MIC_DAC_TO_STERO_OPUS = (2 << 4) | MIC | DAC,//mic和dac输出到立体声opus编码器(立体声)
} stream_type;
/**
* @brief 打开opus编码
*
* @param ch_mode 见stream_type
* @return int 0:成功;其他:失败
*/
int translation_ear_recoder_open_all(u8 ch_mode);
//关闭编码
void translation_ear_recoder_close_all(void);
//打开解码,ch_num暂时只支持填1,暂时只支持解码单声道opus编码器的编码数据
//source_uuid固定填NODE_UUID_SOURCE_DEV0
int dev_flow_player_open(u8 ch_num, u16 source_uuid);
//关闭解码
void dev_flow_player_close(void);
编码数据输出,可以在这个地方将数据发出去
自编自解:仅支持单声道opus编码器的编码数据(MIC_TO_MONO_OPUS、DAC_TO_MONO_OPUS)
需要调用dev_flow_player_open打开解码,然后把编码输出的数据填到解码缓存
实现方法
新建两个音频流程,分别处理单声道opus编码和立体声opus编码
单声道opus编码用的是公版SDK的opus编码器(silk-only 库,仅支持单声道,16k采样率 ,20ms帧编码,CBR编码,支持码率16/32/64kpbs,对应帧长40/80/160字节)
立体声opus编码使用独立的立体声编码器(celt-only库,支持8000/16000/24000/48000采样率单双声道编码,支持封装格式: 0:百度无头. 1:size+rangeFinal. 源码可兼容版本. 2:ogg封装,pc软件可播放)
立体声编码参数修改(建议默认),需要不同的帧长可通过调整码率(br)实现
音乐场景和通话场景使用中间节点收集数据

关于移植
按照上面的截图做好数据流,对比demo和公版701N V1.0.0 patch_06_01以下文件的修改
(作废)701N V1.0.0 patch_04实现的功能:
- 此demo开的mic是mic1、mic2、mic3,详见ADC节点配置(可自行修改,但需要注意下面第2点也要同步改)


- 三个mic的数据在effect dev0节点混合输出(开发者可以自行添加算法)(可自行修改)

- PB1按键,单击打开mic进行opus编码并通过BLE发送,同时打开opus解码;三击将音乐或者通话远端声音进行opus编码并通过BLE发送,同时打开opus解码;双击结束编码和停止发送,同时结束解码。

- demo默认打开自编自解功能,把发送出去的opus数据传给解码播放,从喇叭输出。详见本地自编自解流程实现。
- (作废)更新可视化SDK到最新版本以及最新补丁
- (作废)mic数据上传以及语音下发实现方式
- 添加音频流程



- 节点介绍
- AI_TX:将数据通过BLE或者SPP发出去的节点,对应代码文件:audio\framework\nodes\ai_tx_node.c
- Source_Dev0/Source_Dev1:可自定义实现的节点,用在音频流的起始,用于第三方来源的音频数据接入SDK音频流,本demo将来自ble的或者来自AI_TX的opus编码数据输入到Source_Dev0,然后经过解码器输出到dac播放出来。Source_Dev1则用于音乐和通话数据的编码,给到AI_TX发送出去。对应代码文件: audio\framework\plugs\source\source_dev0_file.c
audio\framework\plugs\source\source_dev1_file.c - EffectDev0/1/2/3/4:可自定义实现的节点,用在音频流的中间流程,可用于接入第三方算法,例如第三方双mic、三mic算法等。本demo的EffectDev0用来做mic数据混合(或者开发第三方算法),EffectDev1用于获取音乐和通话的pcm数据给Source_Dev1编码。对应代码文件: audio\framework\nodes\effect_dev0_node.c
audio\framework\nodes\effect_dev1_node.c
audio\framework\nodes\effect_dev2_node.c
audio\framework\nodes\effect_dev3_node.c
audio\framework\nodes\effect_dev4_node.c
- 发数实现方法 打开数传或者RCSP后,在AI_TX节点中调用数传或者rcsp的发数接口
因不同方案使用的APP可能不一样,BLE那边的profile和uuid等不做详细描述,但开启数传功能和rcsp功能的方法就是在可视化工具中打开对应功能。提升发数速率的方法见发数速率提升方法 - APP语音以及解码播放实现方法
- 创建解码数据缓存
#include "circular_buf.h"
static cbuffer_t output_cbuf_h;
static u8 *output_buff = NULL;
#define OUTPUT_BUFF_SIZE (2048)
static void output_buff_init(void)
{
if(output_buff == NULL){
output_buff = malloc(OUTPUT_BUFF_SIZE);
ASSERT(output_buff);
cbuf_init(&output_cbuf_h, output_buff, OUTPUT_BUFF_SIZE);
}
}
static void output_buff_free(void)
{
if(output_buff){
OS_ENTER_CRITICAL();
free(output_buff);
output_buff = NULL;
OS_EXIT_CRITICAL();
}
}
//输出到解码
static u32 source_input_write(u8 *data, u16 len)
{
if(output_buff == NULL){
return 0;
}
return cbuf_write(&output_cbuf_h, data, len);
}
static u32 source_input_read(u8 *data, u16 len)
{
if(output_buff == NULL){
return 0;
}
if(len > cbuf_get_data_len(&output_cbuf_h)){
len = (cbuf_get_data_len(&output_cbuf_h) / 40) * 40;//对齐到40字节,因为默认opus是40字节一帧
}
return cbuf_read(&output_cbuf_h, data, len);
}
u32 source_dev0_input_write(u8 *data, u16 len)
{
return source_input_write(data, len);
}
2. BLE收数或者SPP收数后调用source_dev0_input_write写数据到缓存 3. Source_Dev0解码流程运行时,从缓存中拿数据解码
static u8 output[40];//数据大小根据实际数据格式调整。opus用40,pcm一般用512
5. 本地自编自解流程实现 在AI_TX调用source_dev0_input_write将编码数据写入解码缓存
6. 数据流程的启动和停止方式
//启动,先启动上传,再启动解码
/**
* @brief 启动数据上传
*
* @param pipeline_name 启动的场景,固定为"ai_voice"
* @param source_uuid 上传的数据源,根据需要选择,上传mic选NODE_UUID_ADC,上传dac选NODE_UUID_SOURCE_DEV1
* @param code_type 编码格式,见audio_base.h
* @param ai_type 固定为0
* @return int
*/
int ai_voice_recoder_open(u8 *pipeline_name, u16 source_uuid, u32 code_type, u8 ai_type);
ai_voice_recoder_open("ai_voice", NODE_UUID_ADC, 0x00100000, 0);//mic数据上传启动
/**
* @brief 自定义数据流播放器 启动
*
* @param pipeline_name 启动的场景,固定为"ai_voice"
* @param source_uuid 播放数据源,固定为NODE_UUID_SOURCE_DEV0
* @return int
*/
int dev_flow_player_open(u8 *pipeline_name, u16 source_uuid);
dev_flow_player_open("ai_voice", NODE_UUID_SOURCE_DEV0);//app语音数据下发解码启动
//停止,先停止解码,再停止上传
void dev_flow_player_close(u8 *pipeline_name);
dev_flow_player_close("ai_voice");//app语音数据下发解码关闭
void ai_voice_recoder_close(u8 *pipeline_name);
ai_voice_recoder_close("ai_voice");//mic数据上传关闭
- (作废)本地dac数据上传以及语音下发实现方式
- 添加音频流程

- 节点介绍 同节点介绍
- dac数据变采样、缓存 dac音频编码上传,使用source dev1节点作为源节点,effect dev1将音乐的数据变采样后再给到source dev1节点,同样的effect dev1也是通过缓存的方式把数据给到source dev1
struct source_dev1_file_hdl *hdl_p;
static u16 input_sr = 44100;
static s16 output[256];
#include "circular_buf.h"
static cbuffer_t output_cbuf_h;
static u8 *output_buff = NULL;
#define OUTPUT_BUFF_SIZE (2048)
static void output_buff_init(void)
{
if(output_buff == NULL){
output_buff = malloc(OUTPUT_BUFF_SIZE);
ASSERT(output_buff);
cbuf_init(&output_cbuf_h, output_buff, OUTPUT_BUFF_SIZE);
}
}
static void output_buff_free(void)
{
if(output_buff){
OS_ENTER_CRITICAL();
free(output_buff);
output_buff = NULL;
OS_EXIT_CRITICAL();
}
}
static void source_dev1_packet_rx_notify(void *_hdl);
static u32 source_input_read(u8 *data, u16 len);
//输出到解码
static u32 source_input_write(u8 *data, u16 len)
{
struct stream_frame *frame;
struct source_dev1_file_hdl *hdl = hdl_p;
if(output_buff == NULL || hdl == NULL){
return 0;
}
u16 data_len = len;
if(hdl->sw_src_api && hdl->sw_src_buf && len){//变采样流程
if(hdl->src_in_sr != input_sr){
hdl->src_in_sr = input_sr;
hdl->sw_src_api->set_sr(hdl->sw_src_buf, input_sr);//更新采样率
}
len = hdl->sw_src_api->run(hdl->sw_src_buf, data, len >> 1, output);
len <<= 1;
data = output;
}
cbuf_write(&output_cbuf_h, data, len);//变采样后写缓存
if(cbuf_get_data_len(&output_cbuf_h) >= 512){
source_dev1_packet_rx_notify(hdl);//缓存够512字节再通知编码
}
return data_len;
}
static u32 source_input_read(u8 *data, u16 len)
{
if(output_buff == NULL){
return 0;
}
if(len > cbuf_get_data_len(&output_cbuf_h)){
return 0;
}
return cbuf_read(&output_cbuf_h, data, len);
}
u32 source_dev1_input_write(u8 *data, u16 len)
{
return source_input_write(data, len);
}



4. 发数实现方法 同发数实现方法
5. 本地自编自解流程实现 同本地自编自解流程实现
6. 数据流程的启动和停止方式
//启动,先启动上传,再启动解码
/**
* @brief 启动数据上传
*
* @param pipeline_name 启动的场景,固定为"ai_voice"
* @param source_uuid 上传的数据源,根据需要选择,上传mic选NODE_UUID_ADC,上传dac选NODE_UUID_SOURCE_DEV1
* @param code_type 编码格式,见audio_base.h
* @param ai_type 固定为0
* @return int
*/
int ai_voice_recoder_open(u8 *pipeline_name, u16 source_uuid, u32 code_type, u8 ai_type);
ai_voice_recoder_open("ai_voice", NODE_UUID_SOURCE_DEV1, 0x00100000, 0);//dac数据上传启动
/**
* @brief 自定义数据流播放器 启动
*
* @param pipeline_name 启动的场景,固定为"ai_voice"
* @param source_uuid 播放数据源,固定为NODE_UUID_SOURCE_DEV0
* @return int
*/
int dev_flow_player_open(u8 *pipeline_name, u16 source_uuid);
dev_flow_player_open("ai_voice", NODE_UUID_SOURCE_DEV0);//app语音数据下发解码启动
//停止,先停止解码,再停止上传
void dev_flow_player_close(u8 *pipeline_name);
dev_flow_player_close("ai_voice");//app语音数据下发解码关闭
void ai_voice_recoder_close(u8 *pipeline_name);
ai_voice_recoder_close("ai_voice");//dac数据上传关闭
(作废)opus立体声编码demo
注意:以下demo需要关闭随机核,改回固定核。修改如下
JL701N_翻译耳机.zip--701N V1.0.0 patch_06_01701N V1.0.0 patch_04_立体声opus编码.zip
更新记录:
2024年8月6日:
1、升级到SDK版本701N V1.0.0 patch_06_01
2024年6月14日:
1、升级到SDK版本701N V1.0.0 patch_05.32
2、支持单声道和立体声编码切换,支持单声道解码
2024年5月27日:
1、提高BLE发数速率
2024年5月22日:
1、修复SPP发数据时opus帧被拆开导致帧不完整问题
2024年5月13日:
- 添加缺少的文件
- 修复时钟不释放问题
701N V1.0.0 patch_06_01功能说明:
见701N V1.0.0 patch_06_01实现的功能:
701N V1.0.0 patch_04功能说明:
- 支持opus立体声编码(dac + mic)
- 支持opus单声道编码(dac或mic)
- 接口使用说明
void xxx_to_opus_close()
{
ai_voice_recoder_close(0, "ai_voice");
ai_voice_recoder_close(1, "ai_voice");
}
//opus单声道编码mic
void mic_to_opus()
{
xxx_to_opus_close();
void set_global_ch_mode(u8 mode);
set_global_ch_mode(AUDIO_CH_L);
if(bt_get_call_status() == 5){//非通话状态下才需要开adc
ai_voice_recoder_open(0, "ai_voice", NODE_UUID_ADC, 0x00100000, 0);
}
ai_voice_recoder_open(1, "ai_voice", NODE_UUID_SOURCE_DEV1, 0x00100000, 0);
}
//opus单声道编码dac
void dac_to_opus()
{
xxx_to_opus_close();
void set_global_ch_mode(u8 mode);
set_global_ch_mode(AUDIO_CH_R);
if(bt_get_call_status() == 5){//非通话状态下才需要开adc
ai_voice_recoder_open(0, "ai_voice", NODE_UUID_ADC, 0x00100000, 0);
}
ai_voice_recoder_open(1, "ai_voice", NODE_UUID_SOURCE_DEV1, 0x00100000, 0);
}
//opus立体声编码mic和dac
void mic_dac_to_opus()
{
xxx_to_opus_close();
void set_global_ch_mode(u8 mode);
set_global_ch_mode(AUDIO_CH_LR);
if(bt_get_call_status() == 5){//非通话状态下才需要开adc
ai_voice_recoder_open(0, "ai_voice", NODE_UUID_ADC, 0x00100000, 0);
}
ai_voice_recoder_open(1, "ai_voice", NODE_UUID_SOURCE_DEV1, 0x00100000, 0);
}
修改编码输出格式
(作废)~~DAC数据通过SPP或者BLE转发给APP(仅701N支持)~~
自定义的音频播放流程
以翻译耳机方案,播放APP下发的OPUS编码数据为例(代码见demo)
- 创建一个数据流

- 添加数据缓存流程,见APP语音以及解码播放实现方法
- 修改数据流启动代码 uuid对应数据流所在场景的id号

- 修改源数据格式
fmt->sample_rate = 16000; //采样率
fmt->coding_type = AUDIO_CODING_OPUS; //数据类型,PCM用AUDIO_CODING_PCM,还要打开PCM解码器
fmt->channel_mode = hdl->ch_num == 2 ? AUDIO_CH_LR : AUDIO_CH_MIX; //通道模式,根据源数据的声道选择
解码PCM需要打开PCM解码器
5. 根据解码格式调整每帧数据长度 opus:demo单声道用40字节
pcm:根据采样率和声道定,单声道8K用160,16K用320,44100用512等,立体声则对应乘以2
6. 启动解码和关闭解码
//打开解码,ch_num暂时只支持填1,暂时只支持解码单声道opus编码器的编码数据
//source_uuid固定填NODE_UUID_SOURCE_DEV0
int dev_flow_player_open(u8 ch_num, u16 source_uuid);
//关闭解码
void dev_flow_player_close(void);
opus编码写入SD卡demo
JL708N_翻译耳机_V3.0.0_feat_01_01.zip
注意:以下demo需要关闭随机核,改回固定核。修改如下
JL701N_翻译耳机_写SD卡_patch_06_01.zip -- gitlab
翻译耳机加写SD卡的流程(具体修改对比JL701N_翻译耳机.zip)
1、默认在线
2、支持EMMC
3、SDPG供电
4、支持PC模式查看文件
接口说明
- 修改SD卡IO

- 读写卡的接口
void *sd_file_open(u8 *file_name);//打开或者创建并打开文件,file_name:文件名,建议用txt格式
void sd_file_close(void *file);//关闭文件
int sd_file_write(void *file, u8 *data, u16 len);//写入
int sd_file_read(void *file, u8 *data, u16 len);//读取
int sd_file_seek(void *file, u32 offset);//指定读取的偏移地址,offset:相对文件起始地址的偏移
demo
3. 其他文件接口 见:\SDK\interface\utils\fs\fs.h
opus数据解码测试APP
【金山文档 | WPS云文档】 OPUS解码工具.rar
OPUS解码工具.rar
将编码文件(保证后缀是.opus),放到手机如下文件夹


从手机中提取PCM数据
opus数据编码解码APP demo
Android:杰理音频解码库开发资料_V2.0.0_Android.zip
IOS:opus解码 v1.1.1_Beta2_2024_09_29.zip
发数速率提升方法
SPP:以512字节为单位发数效率最高。实测速率 120-227KB/s
BLE:关于提升BLE传输速率说明.pdf,改MTU:https://gitee.com/Jieli-Tech/fw-AC63_BT_SDK/issues/I59IL3