This commit is contained in:
huxi
2025-12-03 11:12:34 +08:00
parent c23ae4f24c
commit bc195654bf
8163 changed files with 3799544 additions and 92 deletions
@@ -0,0 +1,297 @@
#include "sparkdesk_main.h"
#include "authentication.h"
#include "stdlib.h"
#include "app_config.h"
#include "app_task.h"
#include "system/timer.h"
#include "app_main.h"
#include "init.h"
#include "key_event_deal.h"
#include "device/device.h"
#include "app_power_manage.h"
#include "btstack/avctp_user.h"
#include "asm/charge.h"
#include "cJSON.h"
#include "media/includes.h"
#include "audio_config.h"
#include "ai_interaction/ai_audio.h"
#include "websocket_define.h"
#include "ifly_socket.h"
#include "ui/ui_api.h"
#include "ifly_common.h"
#if TCFG_IFLYTEK_ENABLE
#define LOG_TAG_CONST NET_IFLY
#define LOG_TAG "[IFLY_AI]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
#define LOG_CLI_ENABLE
#include "debug.h"
#ifdef TCFG_IFLYTEK_APP_ID
#define APP_ID TCFG_IFLYTEK_APP_ID
#else
#define APP_ID "123"
#endif
#define MAX_SPARKDESK_TOKENS ((MAX_SPARKDESK_LEN - 100) / 6) //1个tokens相当于1.5个汉字,1个汉字最大4个byte,即1个tokens约6个byte
#define RECV_LAST_FRAME 2
typedef enum {
IFLY_AI_STATUS_NULL = 0,
IFLY_AI_STATUS_START, // 启动
IFLY_AI_STATUS_SEND, // 数据已经发给socket
IFLY_AI_STATUS_RECV, // 有接受到数据
IFLY_AI_STATUS_RECV_END,// 接受完成
IFLY_AI_STATUS_RECV_ERROR,// 接受错误
IFLY_AI_STATUS_EXIT, // 已经退出
} ifly_ai_status;
struct sparkdesk_info_t {
u8 force_stop;
u8 recv_finish;
ifly_ai_status status;
ifly_ai_param *param;
};
static struct sparkdesk_info_t sparkdesk_info;
static struct ifly_websocket_struct sparkdesk_socket;
//json解析函数_ai
static void ifly_sparkdesk_recv_cb(u8 *j_str, u32 len, u8 type)
{
if (sparkdesk_info.force_stop) {
return;
}
if (sparkdesk_info.status >= IFLY_AI_STATUS_RECV_END) {
return;
}
cJSON *cjson_root = cJSON_Parse((char *)j_str);
if (cjson_root == NULL) {
log_error("cjson error...\r\n");
if (sparkdesk_info.status <= IFLY_AI_STATUS_RECV_END) {
sparkdesk_info.status = IFLY_AI_STATUS_RECV_ERROR;
}
return;
}
sparkdesk_info.status = IFLY_AI_STATUS_RECV;
cJSON *cjson_header = cJSON_GetObjectItem(cjson_root, "header");
cJSON *cjson_status = cJSON_GetObjectItem(cjson_header, "status");
cJSON *cjson_payload = cJSON_GetObjectItem(cjson_root, "payload");
cJSON *cjson_choices = cJSON_GetObjectItem(cjson_payload, "choices");
cJSON *cjson_text = cJSON_GetObjectItem(cjson_choices, "text");
int arr_size = cJSON_GetArraySize(cjson_text);
cJSON *arr_item = cjson_text->child;
if (!sparkdesk_info.param->ai_res) {
return;
}
u32 ai_res_len = strlen(sparkdesk_info.param->ai_res);
for (int i = 0; i < arr_size; i++) {
cJSON *cjson_content = cJSON_GetObjectItem(arr_item, "content");
char *cjson_str = cJSON_Print(cjson_content);
u32 json_len = strlen(cjson_str);
if ((ai_res_len + json_len + 1) > sparkdesk_info.param->ai_res_len) {
log_error("len error\n");
} else {
strcpy(&sparkdesk_info.param->ai_res[ai_res_len], cjson_str);
ai_res_len += json_len;
}
arr_item = arr_item->next;
cJSON_free(cjson_str);
}
int res_len = strlen(sparkdesk_info.param->ai_res);
str_remove_quote(sparkdesk_info.param->ai_res, res_len);
log_info("final res:%s\n", sparkdesk_info.param->ai_res);
if (cjson_status->valueint == RECV_LAST_FRAME) {
sparkdesk_info.status = IFLY_AI_STATUS_RECV_END;
sparkdesk_info.recv_finish = 1;
sparkdesk_info.param->event_cb(IFLY_AI_EVT_RECV_OK, sparkdesk_info.param);
}
cJSON_Delete(cjson_root);
}
//ai数据模块
static char *ifly_sparkdesk_format_audio_data(void)
{
char *data_str = NULL;
cJSON *cjson_test = NULL;
cJSON *cjson_header = NULL;
cJSON *cjson_parameter = NULL;
cJSON *cjson_payload = NULL;
cJSON *cjson_chat = NULL;
cJSON *cjson_message = NULL;
cJSON *cjson_text = NULL;
cJSON *cjson_texts = NULL;
cjson_test = cJSON_CreateObject();
cjson_header = cJSON_CreateObject();
cjson_parameter = cJSON_CreateObject();
cjson_payload = cJSON_CreateObject();
cjson_chat = cJSON_CreateObject();
cjson_message = cJSON_CreateObject();
cjson_text = cJSON_CreateObject();
cJSON_AddStringToObject(cjson_header, "app_id", APP_ID);
cJSON_AddItemToObject(cjson_test, "header", cjson_header);
cJSON_AddStringToObject(cjson_chat, "domain", "generalv3");
cJSON_AddNumberToObject(cjson_chat, "max_tokens", MAX_SPARKDESK_TOKENS);
cJSON_AddItemToObject(cjson_parameter, "chat", cjson_chat);
cJSON_AddItemToObject(cjson_test, "parameter", cjson_parameter);
cjson_texts = cJSON_AddArrayToObject(cjson_message, "text");
cJSON_AddStringToObject(cjson_text, "role", "user");
cJSON_AddStringToObject(cjson_text, "content", sparkdesk_info.param->content);
cJSON_AddItemToArray(cjson_texts, cjson_text);
cJSON_AddItemToObject(cjson_payload, "message", cjson_message);
cJSON_AddItemToObject(cjson_test, "payload", cjson_payload);
data_str = cJSON_Print(cjson_test);
cJSON_Delete(cjson_test);
return data_str;
}
static bool ifly_sparkdesk_get_send(u8 **buf, u32 *len)
{
if (sparkdesk_info.force_stop) {
log_info("ai task kill!\n");
return false;
}
if (sparkdesk_info.status >= IFLY_AI_STATUS_RECV_END) {
log_info("ai task kill!\n");
return false;
}
if (sparkdesk_info.status < IFLY_AI_STATUS_SEND) {
sparkdesk_info.status = IFLY_AI_STATUS_SEND;
char *input_src_json = ifly_sparkdesk_format_audio_data();
if (input_src_json == NULL) {
log_error("get json err \n");
return false;
}
*buf = (u8 *)input_src_json;
*len = strlen(input_src_json);
return true;
}
os_time_dly(2);
return true;
}
static int ifly_sparkdesk_event_cb(ifly_socket_event_enum evt, void *param)
{
switch (evt) {
case IFLY_SOCKET_EVT_SEND_OK:
cJSON_free(param);
os_time_dly(5);
break;
case IFLY_SOCKET_EVT_SEND_ERROR:
cJSON_free(param);
break;
case IFLY_SOCKET_EVT_INIT_OK:
break;
case IFLY_SOCKET_EVT_INIT_ERROR:
case IFLY_SOCKET_EVT_HANSHACK_ERROR:
case IFLY_SOCKET_EVT_ACCIDENT_END:
case IFLY_SOCKET_EVT_END:
case IFLY_SOCKET_EVT_FORCE_END:
if ((evt != IFLY_SOCKET_EVT_END) && (evt != IFLY_SOCKET_EVT_FORCE_END)) {
if (!sparkdesk_info.recv_finish) {
sparkdesk_info.param->event_cb(IFLY_AI_EVT_NETWORK_FAIL, sparkdesk_info.param);
}
}
break;
case IFLY_SOCKET_EVT_EXIT:
sparkdesk_info.status = IFLY_AI_STATUS_EXIT;
sparkdesk_info.param->event_cb(IFLY_AI_EVT_EXIT, sparkdesk_info.param);
if (sparkdesk_socket.auth) {
printf("@@@@@@ free sparkdesk auth!!!\n");
net_iflytek_free(sparkdesk_socket.auth);
sparkdesk_socket.auth = NULL;
}
break;
default:
break;
}
return 0;
}
bool ifly_sparkdesk_start(ifly_ai_param *param)
{
memset(&sparkdesk_info, 0, sizeof(struct sparkdesk_info_t));
memset(&sparkdesk_socket, 0, sizeof(struct ifly_websocket_struct));
#if TCFG_IFLYTEK_USE_PSRAM
cJSON_Hooks hooks;
hooks.malloc_fn = net_iflytek_malloc;
hooks.free_fn = net_iflytek_free;
cJSON_InitHooks(&hooks);
#endif
sparkdesk_info.param = param;
sparkdesk_socket.auth = (u8 *)ifly_authentication("wss://spark-api.xf-yun.com/v3.1/chat",
"spark-api.xf-yun.com",
"GET /v3.1/chat HTTP/1.1", 20);
if (!sparkdesk_socket.auth) {
sparkdesk_info.param->event_cb(IFLY_AI_EVT_NETWORK_FAIL, sparkdesk_info.param);
return false;
}
sparkdesk_socket.task_name = "ifly_ai";
sparkdesk_socket.socket_mode = WEBSOCKET_MODE;
sparkdesk_socket.recv_cb = ifly_sparkdesk_recv_cb;
sparkdesk_socket.get_send = ifly_sparkdesk_get_send;
sparkdesk_socket.event_cb = ifly_sparkdesk_event_cb;
sparkdesk_info.status = IFLY_AI_STATUS_START;
//创建链接
bool ret = ifly_websocket_client_create(&sparkdesk_socket);
if (ret == false) {
sparkdesk_info.status = IFLY_AI_STATUS_NULL;
net_iflytek_free(sparkdesk_socket.auth);
sparkdesk_socket.auth = NULL;
}
return ret;
}
void ifly_sparkdesk_stop(u8 force_stop, u32 to_ms)
{
log_info("ai stop!\n");
sparkdesk_info.force_stop = force_stop;
while (sparkdesk_socket.auth) { // 结束时auth会自动释放
os_time_dly(1);
if (to_ms <= 10) {
break;
}
to_ms -= 10;
}
if (to_ms < 1000) {
to_ms = 1000;
}
sparkdesk_info.force_stop = 1;
ifly_websocket_client_release(&sparkdesk_socket, to_ms);
}
bool ifly_sparkdesk_is_work()
{
if ((sparkdesk_info.status != IFLY_AI_STATUS_NULL) && (sparkdesk_info.status != IFLY_AI_STATUS_EXIT)) {
return true;
}
return false;
}
#endif
@@ -0,0 +1,31 @@
#ifndef __SPARKDESK_MAIN_H
#define __SPARKDESK_MAIN_H
#include "generic/includes.h"
#define MAX_SPARKDESK_LEN 2000
typedef enum {
IFLY_AI_EVT_RECV_OK = 1, // AI对话完成。*param: ifly_ai_param*
IFLY_AI_EVT_NETWORK_FAIL, // 结束。*param: ifly_ai_param*
IFLY_AI_EVT_EXIT, // 结束。*param: ifly_ai_param*
} ifly_ai_event_enum ;
typedef struct ifly_ai_struct {
// 参数信息,所有参数都需要赋值
char *content; // 输入对话数据
char *ai_res; // 输出数据
u32 ai_res_len; // 输出数据buf长度,最大MAX_SPARKDESK_LEN
int (*event_cb)(ifly_ai_event_enum evt, void *param); // 事件回调
} ifly_ai_param;
// AI对话启动。*param参数句柄需要在stop之后才能释放
bool ifly_sparkdesk_start(ifly_ai_param *param);
// AI对话结束。
void ifly_sparkdesk_stop(u8 force_stop, u32 to_ms);
//判断是否正在运行
bool ifly_sparkdesk_is_work(void);
#endif
@@ -0,0 +1,212 @@
#include "app_config.h"
#include "ui/ui_api.h"
#include "jlui/ui.h"
#include "ui_style.h"
#include "app_task.h"
#include "system/timer.h"
#include "app_main.h"
#include "init.h"
#include "key_event_deal.h"
#include "device/device.h"
#include "app_power_manage.h"
#include "btstack/avctp_user.h"
#include "asm/charge.h"
#include "cat1/cat1_common.h"
#include "cJSON.h"
#include "media/includes.h"
#include "audio_config.h"
#include "res/resfile.h"
#include "res_config.h"
#include "circular_buf.h"
#include "ntp/ntp.h"
#include "mbedtls/base64.h"
#include "mbedtls/md.h"
#include "authentication.h"
#include "rtc.h"
#include "ifly_common.h"
#if TCFG_IFLYTEK_ENABLE
#define LOG_TAG_CONST NET_IFLY
#define LOG_TAG "[IFLY_AUTH]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
#define LOG_CLI_ENABLE
#include "debug.h"
#ifdef TCFG_IFLYTEK_APP_SECRET
#define API_SECRET TCFG_IFLYTEK_APP_SECRET
#else
#define API_SECRET "123"
#endif
#ifdef TCFG_IFLYTEK_APP_KEY
#define API_KEY TCFG_IFLYTEK_APP_KEY
#else
#define API_KEY "123"
#endif
// 鉴权信息结构体
struct auth_info_t {
char sig_ori[100];
char sig_hmac[100];
char sig_hmac_base64[50];
char author_ori[200];
char author[300];
char date_str[50];
struct sys_time curtime;
struct tm pt;
};
extern void ntp_client_get_time(const char *host);
extern size_t strftime_2(char *ptr, size_t maxsize, const char *format, const struct tm *timeptr);
/* void *_calloc_r(struct _reent *r, size_t a, size_t b) */
/* { */
/* return calloc(a, b); */
/* } */
static char dec2hex(short int c)
{
if (0 <= c && c <= 9) {
return c + '0';
} else if (10 <= c && c <= 15) {
return c + 'A' - 10;
} else {
return -1;
}
}
static void urlencode(char url[])
{
int i = 0;
int len = strlen(url);
int res_len = 0;
char res[100];
for (i = 0; i < len; ++i) {
char c = url[i];
if (('0' <= c && c <= '9') ||
('a' <= c && c <= 'z') ||
('A' <= c && c <= 'Z') ||
c == '/' || c == '.') {
res[res_len++] = c;
} else {
int j = (short int)c;
if (j < 0) {
j += 256;
}
int i1, i0;
i1 = j / 16;
i0 = j - i1 * 16;
res[res_len++] = '%';
res[res_len++] = dec2hex(i1);
res[res_len++] = dec2hex(i0);
}
}
res[res_len] = '\0';
strcpy(url, res);
}
static int ifly_get_sys_time(struct sys_time *time)//获取时间
{
rtc_read_time(time);
int week = caculate_weekday_by_time(time);
return week;
/* void *fd = dev_open("rtc", NULL); */
/* if (!fd) { */
/* return ; */
/* } */
/* dev_ioctl(fd, IOCTL_GET_SYS_TIME, (u32)time); */
/* dev_close(fd); */
}
char *ifly_authentication(char *host_name, char *host, char *path, int retry)
{
//初始化
struct auth_info_t *auth = net_iflytek_malloc(sizeof(struct auth_info_t));
char *url_xf = net_iflytek_malloc(400);
int retry_cnt = 0;
__retry:
//获取网络时间
/* ntp_client_get_time("s2c.time.edu.cn"); */
ntp_client_get_time("ntp.ntsc.ac.cn");
/* int ntp = ntp_client_get_time("s2c.time.edu.cn"); */
/* if (ntp == -1) { */
/* log_error("get ntp error!!\n"); */
/* return NULL; */
/* } */
int week = ifly_get_sys_time(&(auth->curtime));
auth->pt.tm_year = auth->curtime.year - 1900;
auth->pt.tm_mon = auth->curtime.month - 1;
auth->pt.tm_mday = auth->curtime.day;
auth->pt.tm_hour = auth->curtime.hour;
auth->pt.tm_min = auth->curtime.min;
auth->pt.tm_sec = auth->curtime.sec;
if (week == 7) {
auth->pt.tm_wday = 0;
} else {
auth->pt.tm_wday = week;
}
strftime_2(auth->date_str, 40, "", &auth->pt);
log_info("%s\n", auth->date_str);
//拼接sig原始字符
sprintf(auth->sig_ori, "%s%s\ndate: %s\n%s", "host: ", host, auth->date_str, path);
//通过hmac_sha256加密
mbedtls_md_context_t ctx;
const mbedtls_md_info_t *info;
mbedtls_md_init(&ctx);
info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
mbedtls_md_setup(&ctx, info, 1);
mbedtls_md_hmac_starts(&ctx, (unsigned char *)API_SECRET, strlen(API_SECRET));
mbedtls_md_hmac_update(&ctx, (unsigned char *)auth->sig_ori, strlen(auth->sig_ori));
mbedtls_md_hmac_finish(&ctx, (unsigned char *)auth->sig_hmac);
mbedtls_md_free(&ctx);
//进行base64编码
int res;
mbedtls_base64_encode((unsigned char *)auth->sig_hmac_base64, 50, (size_t *)&res, (unsigned char *)auth->sig_hmac, strlen(auth->sig_hmac));
if (strlen(auth->sig_hmac_base64) != 44) { // 根据科大讯飞文档,hamc base64编码后,正常为44bytes
log_info("base64 encode error!!\n");
if (retry_cnt < retry) {
memset(auth, 0, sizeof(struct auth_info_t));
memset((char *)url_xf, 0, sizeof(url_xf));
retry_cnt++;
goto __retry;
}
}
//拼接authorization_ori字符串
strcat(auth->author_ori, "api_key=\"");
strcat(auth->author_ori, API_KEY);
strcat(auth->author_ori, "\",");
strcat(auth->author_ori, "algorithm=\"hmac-sha256\",");
strcat(auth->author_ori, "headers=\"host date request-line\",");
strcat(auth->author_ori, "signature=\"");
strcat(auth->author_ori, auth->sig_hmac_base64);
strcat(auth->author_ori, "\"");
//对autho_ori进行base64编码
int author_res;
mbedtls_base64_encode((unsigned char *)auth->author, 300, (size_t *)&author_res, (unsigned char *)auth->author_ori, strlen(auth->author_ori));
//拼接url
strcat(url_xf, host_name);
strcat(url_xf, "?");
urlencode(auth->date_str);
strcat(url_xf, "authorization=");
strcat(url_xf, auth->author);
strcat(url_xf, "&date=");
strcat(url_xf, auth->date_str);
strcat(url_xf, "&host=");
strcat(url_xf, host);
log_info("url_xf:%s\n", url_xf);
net_iflytek_free(auth);
return url_xf;
}
#endif
@@ -0,0 +1,9 @@
#ifndef __AUTHENTICATION_H
#define __AUTHENTICATION_H
#include "generic/includes.h"
// 鉴权接口。内存需要释放
char *ifly_authentication(char *host_name, char *host, char *path, int retry);
#endif
+123
View File
@@ -0,0 +1,123 @@
#include "ifly_socket.h"
#include "authentication.h"
#include "sparkdesk_main.h"
#include "tts_main.h"
#include "vad_main.h"
#include "system/timer.h"
#include "app_config.h"
#if TCFG_IFLYTEK_ENABLE
#define IFLY_DEMO_TEST 0
#if IFLY_DEMO_TEST
struct ifly_net_struct {
// vad
ifly_vad_param vad_param;
// ai
ifly_ai_param ai_param;
// tts
ifly_tts_param tts_param;
// other
char local_text[MAX_VAD_LEN]; // 对话文本。近端
char ai_text[MAX_SPARKDESK_LEN]; // 对话文本。远端
int vad_timer;
};
static struct ifly_net_struct *p_ifly_net = NULL;
static int ifly_tts_event_cb(ifly_tts_event_enum evt, void *param) //tts任务状态callback
{
switch (evt) {
case IFLY_TTS_EVT_PLAY_START:
break;
case IFLY_TTS_EVT_PLAY_STOP:
break;
case IFLY_TTS_EVT_PLAY_FAIL_STOP:
break;
case IFLY_TTS_EVT_EXIT:
if (p_ifly_net) {
free(p_ifly_net);
printf("free!!\n");
p_ifly_net = NULL;
}
break;
default:
break;
}
return 0;
}
static int ifly_ai_event_cb(ifly_ai_event_enum evt, void *param) //ai任务状态callback
{
switch (evt) {
case IFLY_AI_EVT_RECV_OK:
ifly_sparkdesk_stop(10);
p_ifly_net->tts_param.event_cb = ifly_tts_event_cb;
p_ifly_net->tts_param.text_res = p_ifly_net->ai_text;
ifly_tts_start(&p_ifly_net->tts_param); //创建tts任务
break;
case IFLY_AI_EVT_EXIT:
break;
default:
break;
}
return 0;
}
static int ifly_vad_event_cb(ifly_vad_event_enum evt, void *param) //vad任务状态callback
{
switch (evt) {
case IFLY_VAD_EVT_AUDIO_START:
break;
case IFLY_VAD_EVT_RECV_OK:
break;
case IFLY_VAD_EVT_EXIT: //vad结束,开启ai
p_ifly_net->ai_param.ai_res = p_ifly_net->ai_text;
p_ifly_net->ai_param.ai_res_len = MAX_SPARKDESK_LEN;
p_ifly_net->ai_param.event_cb = ifly_ai_event_cb;
p_ifly_net->ai_param.content = p_ifly_net->local_text;
p_ifly_net->ai_param.ai_res[0] = 0;
ifly_sparkdesk_start(&p_ifly_net->ai_param); //创建ai任务
break;
default:
break;
}
return 0;
}
static void stop_rec(void *priv) //8s结束录音,并关闭vad任务
{
ifly_vad_stop(10);
if (p_ifly_net->vad_timer) {
sys_timer_del(p_ifly_net->vad_timer);
p_ifly_net->vad_timer = 0;
}
}
void ifly_net_demo(void)
{
if (p_ifly_net == NULL) {
p_ifly_net = zalloc(sizeof(struct ifly_net_struct));
ASSERT(p_ifly_net);
}
p_ifly_net->vad_param.vad_res = p_ifly_net->local_text;
p_ifly_net->vad_param.vad_res_len = MAX_VAD_LEN;
p_ifly_net->vad_param.event_cb = ifly_vad_event_cb;
p_ifly_net->vad_param.vad_res[0] = 0;
if (!p_ifly_net->vad_timer) {
p_ifly_net->vad_timer = sys_timer_add(NULL, stop_rec, 8000);
}
ifly_vad_start(&p_ifly_net->vad_param); //创建vad任务
}
#endif
#endif
@@ -0,0 +1,37 @@
#include "ifly_common.h"
#include "app_config.h"
static void str_remove_quote(char *str, int len)
{
int i, j;
for (i = 0, j = 0; i < len; i++) {
if (str[i] != '\"') {
str[j++] = str[i];
}
}
str[j] = '\0';
}
void *net_iflytek_malloc(size_t size)
{
#if TCFG_IFLYTEK_USE_PSRAM && TCFG_PSRAM_DEV_ENABLE
void *p = malloc_psram(size);
if (p) {
memset(p, 0, size);
}
return p;
#else
return zalloc(size);
#endif
}
void net_iflytek_free(void *pv)
{
#if TCFG_IFLYTEK_USE_PSRAM && TCFG_PSRAM_DEV_ENABLE
return free_psram(pv);
#else
return free(pv);
#endif
}
@@ -0,0 +1,11 @@
#ifndef __IFLY_COMMON__H
#define __IFLY_COMMON__H
#include "generic/includes.h"
// 引号去除函数
void str_remove_quote(char *str, int len);
void *net_iflytek_malloc(size_t size);
void net_iflytek_free(void *pv);
#endif
@@ -0,0 +1,276 @@
#include "websocket_api.h"
#include "os/os_compat.h"
#ifdef WEBSOCKET_MEMHEAP_DEBUG
#include "mem_leak_test.h"
#endif
#include "ifly_socket.h"
#include "ui/ui_api.h"
#include "cJSON.h"
#include "app_config.h"
#if TCFG_IFLYTEK_ENABLE
#define LOG_TAG_CONST NET_IFLY
#define LOG_TAG "[IFLY_SOCKET]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
#define LOG_CLI_ENABLE
#include "debug.h"
#define IFLY_SOCKET_KILL_TASK_NAME "ifly_socket_kill"
struct ifly_socket_info {
struct websocket_struct socket_info;
u8 force_stop; // 强制结束
};
extern int task_kill(const char *name);
extern void websockets_sleep(unsigned int ms);
static void task_kill_callback(char *buf)
{
log_info("[msg]>>>>>>>>>>>*buf=%s", buf);
task_kill(buf);
}
static void ifly_socket_kill_task_main(void *priv)
{
int msg[16];
while (1) {
os_taskq_pend(NULL, msg, ARRAY_SIZE(msg));
}
}
int ifly_socket_kill_task_create(void)
{
int err = os_task_create(ifly_socket_kill_task_main,
NULL,
5,
256,
256,
IFLY_SOCKET_KILL_TASK_NAME);
return err;
}
void ifly_socket_kill_task_release(void)
{
if (!strcmp(os_current_task(), IFLY_SOCKET_KILL_TASK_NAME)) {
task_kill(IFLY_SOCKET_KILL_TASK_NAME);
return ;
}
int msg[4];
msg[0] = (int)task_kill_callback;
msg[1] = 1;
msg[2] = (int)IFLY_SOCKET_KILL_TASK_NAME;
os_taskq_post_type("app_core", Q_CALLBACK, 3, msg);
}
static void websockets_client_reg(struct websocket_struct *websockets_info, ifly_socket_param *ifly_socket)
{
memset(websockets_info, 0, sizeof(struct websocket_struct));
websockets_info->_init = websockets_client_socket_init;
websockets_info->_exit = websockets_client_socket_exit;
websockets_info->_handshack = webcockets_client_socket_handshack;
websockets_info->_send = websockets_client_socket_send;
websockets_info->_recv_thread = websockets_client_socket_recv_thread;
websockets_info->_heart_thread = websockets_client_socket_heart_thread;
websockets_info->_recv_cb = ifly_socket->recv_cb;
websockets_info->_recv = NULL;
websockets_info->websocket_mode = ifly_socket->socket_mode;
websockets_info->kill_task = IFLY_SOCKET_KILL_TASK_NAME;
}
static int websockets_client_init(struct websocket_struct *websockets_info, u8 *url, const char *origin_str, const char *user_agent_str)
{
websockets_info->ip_or_url = url;
websockets_info->origin_str = origin_str;
websockets_info->user_agent_str = user_agent_str;
websockets_info->recv_time_out = 1000;
//应用层和库的版本检测,结构体不一样则返回出错
int err = websockets_struct_check(sizeof(struct websocket_struct));
if (err == FALSE) {
return err;
}
return websockets_info->_init(websockets_info);
}
static int websockets_client_handshack(struct websocket_struct *websockets_info)
{
return websockets_info->_handshack(websockets_info);
}
static int websockets_client_send(struct websocket_struct *websockets_info, u8 *buf, int len, char type)
{
//SSL加密时一次发送数据不能超过16K,用户需要自己分包
return websockets_info->_send(websockets_info, buf, len, type);
}
static void websockets_client_exit(struct websocket_struct *websockets_info)
{
websockets_info->_exit(websockets_info);
}
static void ifly_socket_task_main(void *priv)
{
int err;
ifly_socket_event_enum ifly_evt = IFLY_SOCKET_EVT_END;
ifly_socket_param *ifly_socket = (ifly_socket_param *)priv;
struct ifly_socket_info *info = (struct ifly_socket_info *)ifly_socket->socket_hdl;
const char *origin_str = "http://coolaf.com";
log_info(" . ----------------- Client Websocket ------------------\r\n");
/* 0 . malloc buffer */
struct websocket_struct *websockets_info = &info->socket_info;
/* 1 . register */
websockets_client_reg(websockets_info, ifly_socket);
/* 2 . init */
err = websockets_client_init(websockets_info, ifly_socket->auth, origin_str, NULL);
if (FALSE == err) {
log_error(" . ! Cilent websocket init error !!!\r\n");
ifly_evt = IFLY_SOCKET_EVT_INIT_ERROR;
goto exit_ws;
}
/* 3 . hanshack */
err = websockets_client_handshack(websockets_info);
if (FALSE == err) {
log_error(" . ! Handshake error !!!\r\n");
ifly_evt = IFLY_SOCKET_EVT_HANSHACK_ERROR;
goto exit_ws;
}
log_info(" . Handshake success \r\n");
/* 4 . CreateThread */
err = os_task_create(websockets_info->_heart_thread,
websockets_info,
3,
1024,
0,
"websocket_client_heart");
if (err == 0) {
websockets_info->ping_thread_id = 1;
} else {
websockets_info->ping_thread_id = 0;
}
err = os_task_create(websockets_info->_recv_thread,
websockets_info,
2,
1024,
0,
"websocket_client_recv");
if (err == 0) {
websockets_info->recv_thread_id = 1;
} else {
websockets_info->recv_thread_id = 0;
}
ifly_socket->event_cb(IFLY_SOCKET_EVT_INIT_OK, ifly_socket);
/* 5 . recv or send data */
while (1) {
u8 *send_buf = NULL;
u32 send_len = 0;
if (websockets_info->websocket_valid == INVALID_ESTABLISHED) {
//websocket底层断开连接
ifly_evt = IFLY_SOCKET_EVT_ACCIDENT_END;
goto exit_ws;
}
if (info->force_stop) {
ifly_evt = IFLY_SOCKET_EVT_FORCE_END;
goto exit_ws;
}
err = ifly_socket->get_send(&send_buf, &send_len);
if (err == false) {
// 获取不到数据,正常退出
goto exit_ws;
}
if ((send_buf == NULL) || (send_len == 0)) {
continue;
}
err = websockets_client_send(websockets_info, send_buf, send_len, WCT_TXTDATA);
if (FALSE == err) {
log_error(" . ! send err !!!\r\n");
ifly_socket->event_cb(IFLY_SOCKET_EVT_SEND_ERROR, send_buf);
goto exit_ws;
}
ifly_socket->event_cb(IFLY_SOCKET_EVT_SEND_OK, send_buf);
}
exit_ws:
/* 6 . exit */
ifly_socket->event_cb(ifly_evt, ifly_socket);
if (websockets_info->ping_thread_id) {
websockets_info->ping_kill_flag = 1;
}
if (websockets_info->recv_thread_id) {
websockets_info->recv_kill_flag = 1;
}
while (websockets_info->recv_kill_flag || websockets_info->ping_kill_flag) {
os_time_dly(1);
}
websockets_client_exit(websockets_info);
ifly_socket->event_cb(IFLY_SOCKET_EVT_EXIT, ifly_socket);
free(ifly_socket->socket_hdl);
ifly_socket->socket_hdl = NULL;
int msg[5];
msg[0] = (int)task_kill_callback;
msg[1] = 1;
msg[2] = (int)os_current_task();
err = os_taskq_post_type("app_core", Q_CALLBACK, 3, msg);
os_time_dly(-1);
}
bool ifly_websocket_client_create(ifly_socket_param *ifly_socket)
{
ifly_socket->socket_hdl = zalloc(sizeof(struct ifly_socket_info));
if (ifly_socket->socket_hdl == NULL) {
return false;
}
os_task_create(ifly_socket_task_main,
ifly_socket,
3,
2048,
0,
ifly_socket->task_name);
return true;
}
void ifly_websocket_client_release(ifly_socket_param *ifly_socket, u32 to_ms)
{
if (ifly_socket && ifly_socket->socket_hdl) {
struct ifly_socket_info *info = (struct ifly_socket_info *)ifly_socket->socket_hdl;
info->force_stop = 1;
if (!strcmp(os_current_task(), ifly_socket->task_name)) {
// 任务内部调用
return ;
}
// 其他任务调用
while (ifly_socket->socket_hdl) {
os_time_dly(1);
if (to_ms <= 10) {
break;
}
to_ms -= 10;
}
}
}
#endif
@@ -0,0 +1,45 @@
#ifndef __IFLY_SOCKET_H__
#define __IFLY_SOCKET_H__
#include "generic/includes.h"
typedef enum {
IFLY_SOCKET_EVT_SEND_OK = 1, // socket发数成功。*param: 发数buf
IFLY_SOCKET_EVT_SEND_ERROR, // socket发数失败。*param: 发数buf
IFLY_SOCKET_EVT_INIT_ERROR, // socket初始化失败。*param: ifly_socket_param*
IFLY_SOCKET_EVT_HANSHACK_ERROR, // socket握手失败。*param: ifly_socket_param*
IFLY_SOCKET_EVT_INIT_OK, // socket初始化成功。*param: ifly_socket_param*
IFLY_SOCKET_EVT_ACCIDENT_END, //意外结束。。*param: ifly_socket_param*
IFLY_SOCKET_EVT_END, // 结束。socket释放资源之前。*param: ifly_socket_param*
IFLY_SOCKET_EVT_FORCE_END, // 强制结束。socket释放资源之前。*param: ifly_socket_param*
IFLY_SOCKET_EVT_EXIT, // 结束。socket释放资源之后。*param: ifly_socket_param*
} ifly_socket_event_enum ;
typedef struct ifly_websocket_struct {
// 参数信息,所有参数都需要赋值
char *task_name; // 任务名
u8 *auth; // 鉴权buf
u8 socket_mode; // 是否加密。WEBSOCKET_MODE, WEBSOCKETS_MODE
void (*recv_cb)(u8 *buf, u32 len, u8 type); // 收数回调
bool (*get_send)(u8 **buf, u32 *len); // 获取发数buf
int (*event_cb)(ifly_socket_event_enum evt, void *param); // 事件回调
// 模块内部记录信息,不需要赋值
void *socket_hdl; // socket句柄
} ifly_socket_param;
// socket创建。*ifly_socket参数句柄需要在release之后才能释放
bool ifly_websocket_client_create(ifly_socket_param *ifly_socket);
// socket释放。
void ifly_websocket_client_release(ifly_socket_param *ifly_socket, u32 to_ms);
// 用于处理socket收发任务的删除,需要在所有socket创建之前创建,所有socket释放之后释放
int ifly_socket_kill_task_create(void);
void ifly_socket_kill_task_release(void);
#endif
@@ -0,0 +1,88 @@
#include "circular_buf.h"
#include "app_config.h"
/* #include "audio_dec.h" */
/* #include "media/pcm_decoder.h" */
#include "tts_main.h"
#include "jlstream.h"
#include "ai_rx_player.h"
#if TCFG_IFLYTEK_ENABLE
#define LOG_TAG_CONST NET_IFLY
#define LOG_TAG "[IFLY_SOCKET]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
#define LOG_CLI_ENABLE
#include "debug.h"
extern struct ai_rx_cb ai_rx_cb_t;
enum stream_node_state iflytek_tts_ai_rx_get_frame(struct ai_rx_file_handle *hdl, struct stream_frame **pframe)
{
#define FIX_LEN 1024
extern int iflytek_tts_cbuf_data_len();
int available = iflytek_tts_cbuf_data_len();
int read_size = MIN(available, FIX_LEN);
if (read_size <= 0) {
*pframe = NULL;
return NODE_STA_RUN;
}
struct stream_frame *frame = jlstream_get_frame(hdl->node->oport, read_size);
if (!frame) {
*pframe = NULL;
return NODE_STA_RUN;
}
extern int iflytek_tts_cbuf_read_data(u8 * data, int size);
int rlen = iflytek_tts_cbuf_read_data(frame->data, read_size);
frame->len = MAX(rlen, 0);
if (rlen != read_size) {
printf("cbuf_read mismatch: req=%d, act=%d\n", read_size, rlen);
}
*pframe = frame;
return NODE_STA_RUN;
}
static void iflytek_tts_audio_begin(void)
{
struct ai_rx_player_param param = {0};
param.coding_type = AUDIO_CODING_STREAM_MP3;
param.sample_rate = 16000;
param.channel_mode = AUDIO_CH_MIX;
param.type = AI_SERVICE_VOICE;
ai_rx_player_open(NULL, 0, &param);
extern void audio_app_volume_set(u8 state, s16 volume, u8 fade);
audio_app_volume_set(1, 100, 1);
}
static void iflytek_tts_audio_start(void *arg)
{
iflytek_tts_audio_begin();
}
void iflytek_tts_audio_play()
{
if (ai_rx_cb_t.get_frame_event_cb == NULL) {
ai_rx_cb_t.get_frame_event_cb = iflytek_tts_ai_rx_get_frame;
}
iflytek_tts_audio_start(NULL);
}
void iflytek_tts_audio_stop(void *arg)
{
ai_rx_player_close(0);
if (ai_rx_cb_t.get_frame_event_cb) {
ai_rx_cb_t.get_frame_event_cb = NULL;
}
}
bool ifly_net_tts_dec_check_run(void)
{
extern bool ai_rx_player_runing(u8 source);
if (ai_rx_player_runing(0)) {
return true;
}
return false;
}
#endif
@@ -0,0 +1,8 @@
#ifndef __IFLY_DEC_FILE_H
#define __IFLY_DEC_FILE_H
bool ifly_net_tts_dec_check_run(void);
void iflytek_tts_audio_stop(void *arg);
void iflytek_tts_audio_play();
#endif
@@ -0,0 +1,527 @@
#include "tts_main.h"
#include "cJSON.h"
#include "circular_buf.h"
#include "stdlib.h"
#include "app_config.h"
#include "authentication.h"
#include "ifly_dec_file.h"
#include "sparkdesk_main.h"
#include "websocket_define.h"
#include "ifly_socket.h"
#include "authentication.h"
#include "stdlib.h"
#include "app_config.h"
#include "app_task.h"
#include "system/timer.h"
#include "app_main.h"
#include "init.h"
#include "key_event_deal.h"
#include "device/device.h"
#include "app_power_manage.h"
#include "btstack/avctp_user.h"
#include "asm/charge.h"
#include "cJSON.h"
#include "media/includes.h"
#include "audio_config.h"
#include "circular_buf.h"
#include "ui/ui_api.h"
#include "ifly_common.h"
#if TCFG_IFLYTEK_ENABLE
#define LOG_TAG_CONST NET_IFLY
#define LOG_TAG "[IFLY_TTS]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
#define LOG_CLI_ENABLE
#include "debug.h"
#define TTS_AUDIO_SAVE_TEST 0
#if TTS_AUDIO_SAVE_TEST
static FILE *save_file = NULL;
#endif
#ifdef TCFG_IFLYTEK_APP_ID
#define APP_ID TCFG_IFLYTEK_APP_ID
#else
#define APP_ID "123"
#endif
#define RECV_LAST_FRAME 2
#define IFLY_TTS_PKG_MAX (1024 * 10) //经测试,最长一包下发数据的长度为8500bytes左右
#define IFLY_TTS_CBUF_LEN (IFLY_TTS_PKG_MAX + 4096)
#define TTS_TIMEOUT_TIME 8 //防止tts在播放过程中收不到消息而卡住,8s没接收消息,就关闭任务
typedef enum {
IFLY_TTS_STATUS_NULL = 0,
IFLY_TTS_STATUS_START, // 启动
IFLY_TTS_STATUS_SEND, // 数据已经发给socket
IFLY_TTS_STATUS_RECV, // 有接受到数据
IFLY_TTS_STATUS_RECV_SEC, // 接收到第二包数据
IFLY_TTS_STATUS_RECV_END,// 接受完成
IFLY_TTS_STATUS_PLAY_END,// 播放完
IFLY_TTS_STATUS_RECV_ERROR,// 接受错误
IFLY_TTS_STATUS_EXIT, // 已经退出
} ifly_tts_status;
struct tts_info_t {
u8 force_stop; // 强制结束
u8 recv_finish; // 接受完毕
u8 tts_to_cnt; // 超时计数
u16 tts_timer; // 超时用的timer ID
cbuffer_t dec_cbuf; // 用于tts音频播放的cbuf
char *dec_out_buf;
ifly_tts_status status;
ifly_tts_param *param;
};
static struct tts_info_t tts_info;
static struct ifly_websocket_struct tts_socket;
static void *ifly_tts_usr_task = "app_core";
extern int mbedtls_base64_encode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen);
extern int mbedtls_base64_decode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen);
#if 0
static int test_read(void *priv, void *buf, u32 len, u8 tmp_flag, int tmp_offset)
{
int rlen = 0;
if (tts_info.status > IFLY_TTS_STATUS_RECV_END) {
return 0;
}
if (tmp_flag) {
// 格式检查时读数,不保留读取记录
void *rbuf = cbuf_read_alloc(&tts_info.dec_cbuf, &rlen);
if (rlen) {
if (rlen > len) {
rlen = len;
}
memcpy(buf, rbuf, rlen);
}
} else {
// 正常读数
rlen = cbuf_read(&tts_info.dec_cbuf, buf, len);
if (rlen != len) {
if (tts_info.status >= IFLY_TTS_STATUS_RECV_END) {
rlen = cbuf_get_data_len(&tts_info.dec_cbuf);
rlen = cbuf_read(&tts_info.dec_cbuf, buf, rlen);
}
}
}
if (tts_info.status < IFLY_TTS_STATUS_RECV_END) {
if (rlen == 0) {
rlen = -1; // 没结束时返回-1挂起解码,不结束解码
}
}
return rlen;
}
#endif
int iflytek_tts_cbuf_data_len()
{
int rlen = cbuf_get_data_len(&tts_info.dec_cbuf);
return rlen;
}
int iflytek_tts_cbuf_read_data(u8 *data, int size)
{
int rlen = cbuf_read(&tts_info.dec_cbuf, data, size);
return rlen;
}
static void ifly_net_tts_stop(u32 dat)
{
int to = dat;
while (tts_info.dec_out_buf && (tts_info.status < IFLY_TTS_STATUS_PLAY_END)) {
/* if (cbuf_get_data_len(&tts_info.dec_cbuf) == 0) { */
/* break; */
/* } */
os_time_dly(1);
if (to < 10) {
break;
}
to -= 10;
}
if (tts_info.status < IFLY_TTS_STATUS_PLAY_END) {
tts_info.status = IFLY_TTS_STATUS_PLAY_END;
}
extern void iflytek_tts_audio_stop(void *arg);
iflytek_tts_audio_stop(NULL);
}
static void ifly_net_tts_start(u32 dat)
{
log_info("ifly_net_tts_start");
// 开启ai_rx节点
extern void iflytek_tts_audio_play();
iflytek_tts_audio_play();
}
static void ifly_tts_dec_func(void(*func)(u32 dat), int to)
{
int argv[3];
argv[0] = (int)func;
argv[1] = 1;
argv[2] = to;
int ret = os_taskq_post_type(ifly_tts_usr_task, Q_CALLBACK, 3, argv);
if ((ret == OS_ERR_POST_NULL_PTR) || (ret == OS_TASK_NOT_EXIST)) {
os_taskq_post_type("app_core", Q_CALLBACK, 3, argv);
}
}
//tts数据模块
char *ifly_tts_format_text_data(void)
{
char *data_str = NULL;
int out_len = 0;
cJSON *cjson_test = NULL;
cJSON *cjson_common = NULL;
cJSON *cjson_business = NULL;
cJSON *cjson_data = NULL;
char *buf = net_iflytek_malloc(MAX_SPARKDESK_LEN);
mbedtls_base64_encode((unsigned char *)buf, MAX_SPARKDESK_LEN, (size_t *)&out_len, (unsigned char *)tts_info.param->text_res, strlen(tts_info.param->text_res) + 1);
//定义最长回答
cjson_test = cJSON_CreateObject();
cjson_common = cJSON_CreateObject();
cjson_business = cJSON_CreateObject();
cjson_data = cJSON_CreateObject();
cJSON_AddStringToObject(cjson_common, "app_id", APP_ID);
cJSON_AddItemToObject(cjson_test, "common", cjson_common);
cJSON_AddStringToObject(cjson_business, "aue", "lame");
cJSON_AddNumberToObject(cjson_business, "sfl", 1);
cJSON_AddStringToObject(cjson_business, "auf", "audio/L16;rate=16000");
cJSON_AddStringToObject(cjson_business, "vcn", "xiaoyan");
cJSON_AddStringToObject(cjson_business, "tte", "UTF8");
cJSON_AddItemToObject(cjson_test, "business", cjson_business);
cJSON_AddNumberToObject(cjson_data, "status", 2);
cJSON_AddStringToObject(cjson_data, "text", buf);
cJSON_AddItemToObject(cjson_test, "data", cjson_data);
data_str = cJSON_Print(cjson_test);
net_iflytek_free(buf);
cJSON_Delete(cjson_test);
log_info("tts content:%s\n", data_str);
return data_str;
}
static void tts_recv_timeout_timer(void *priv)
{
if (tts_info.tts_to_cnt < TTS_TIMEOUT_TIME) {
tts_info.tts_to_cnt++;
if (tts_info.tts_to_cnt == TTS_TIMEOUT_TIME) {
log_info("tts timeout!");
if (tts_info.status < IFLY_TTS_STATUS_RECV_ERROR) {
tts_info.status = IFLY_TTS_STATUS_RECV_ERROR;
}
}
}
}
//tts解析播放模块
static void ifly_tts_recv_cb(u8 *j_str, u32 len, u8 type)
{
tts_info.tts_to_cnt = 0; //收到数据重置定时器
if (tts_info.force_stop) {
return;
}
if (tts_info.status >= IFLY_TTS_STATUS_RECV_END) {
return;
}
cJSON *cjson_root = cJSON_Parse((char *)j_str);
char *audio = NULL;
char *res_tts = NULL;
if (cjson_root == NULL) {
log_error("cjson error...\r\n");
if (tts_info.status <= IFLY_TTS_STATUS_RECV_END) {
tts_info.status = IFLY_TTS_STATUS_RECV_ERROR;
}
return;
}
cJSON *cjson_data = cJSON_GetObjectItem(cjson_root, "data");
cJSON *cjson_status = cJSON_GetObjectItem(cjson_data, "status");
cJSON *cjson_audio = cJSON_GetObjectItem(cjson_data, "audio");
audio = cJSON_Print(cjson_audio);
if (!audio) {
log_error("audio is null!!\n");
tts_info.status = IFLY_TTS_STATUS_RECV_ERROR;
return;
}
str_remove_quote(audio, strlen(audio));
res_tts = net_iflytek_malloc(IFLY_TTS_PKG_MAX);
if (!res_tts) {
log_error("malloc fail!!\n");
tts_info.status = IFLY_TTS_STATUS_RECV_ERROR;
return;
}
int olen = 0;
int ret = mbedtls_base64_decode((unsigned char *)res_tts, IFLY_TTS_PKG_MAX, (size_t *)&olen, (unsigned char *)audio, strlen(audio));
if (ret || (olen > IFLY_TTS_PKG_MAX)) {
log_error("mbedtls_base64_decode fail!!\n");
tts_info.status = IFLY_TTS_STATUS_RECV_ERROR;
return;
}
#if TTS_AUDIO_SAVE_TEST
if (save_file) {
int wlen = fwrite(save_file, res_tts, olen);
if (wlen != olen) {
log_error("save file err: %d, %d\n", wlen, olen);
}
}
#endif
//若cbuf满了,需要先while住,等待播放
int to = 1500;
while (!cbuf_write(&tts_info.dec_cbuf, res_tts, olen)) {
if (tts_info.force_stop) {
break;
}
os_time_dly(1);
if (to < 10) {
log_info(">>>>>>>>>>>>>>>>>>>>>>>cbuf full");
break;
}
to -= 10;
}
if (tts_info.status == IFLY_TTS_STATUS_RECV) {
tts_info.status = IFLY_TTS_STATUS_RECV_SEC; // 如果有多帧语音数据,第二帧才开始播放,避免mp3_dec获取不到数据提前结束解码
log_info("tts start play!\n");
ifly_tts_dec_func(ifly_net_tts_start, 0);
tts_info.param->event_cb(IFLY_TTS_EVT_PLAY_START, tts_info.param);
}
if (tts_info.status < IFLY_TTS_STATUS_RECV) {
tts_info.status = IFLY_TTS_STATUS_RECV;
if (cjson_status->valueint == RECV_LAST_FRAME) { // 如果只有一帧语音数据,直接播放
log_info("tts start play!\n");
ifly_tts_dec_func(ifly_net_tts_start, 0);
tts_info.param->event_cb(IFLY_TTS_EVT_PLAY_START, tts_info.param);
}
}
if (cjson_status->valueint == RECV_LAST_FRAME) {
log_info("tts last frame!\n");
tts_info.status = IFLY_TTS_STATUS_RECV_END;
tts_info.recv_finish = 1;
ifly_tts_dec_func(ifly_net_tts_stop, 3000);
tts_info.param->event_cb(IFLY_TTS_EVT_PLAY_STOP, tts_info.param);
}
cJSON_free(audio);
cJSON_Delete(cjson_root);
net_iflytek_free(res_tts);
/* ifly_net_tts_dec_resume(NULL); */
}
static bool ifly_tts_get_send(u8 **buf, u32 *len)
{
if (tts_info.force_stop) {
log_info("tts task kill!\n");
return false;
}
if (tts_info.status >= IFLY_TTS_STATUS_PLAY_END) {
log_info("tts task kill!\n");
return false;
}
if (tts_info.status < IFLY_TTS_STATUS_SEND) {
tts_info.status = IFLY_TTS_STATUS_SEND;
char *input_src_json = ifly_tts_format_text_data();
if (input_src_json == NULL) {
log_error("get json err \n");
return false;
}
*buf = (u8 *)input_src_json;
*len = strlen(input_src_json);
return true;
}
os_time_dly(2);
return true;
}
static int ifly_tts_event_cb(ifly_socket_event_enum evt, void *param)
{
switch (evt) {
case IFLY_SOCKET_EVT_SEND_OK:
if (!tts_info.tts_timer) {
tts_info.tts_timer = sys_timer_add(NULL, tts_recv_timeout_timer, 1000);
}
cJSON_free(param);
break;
case IFLY_SOCKET_EVT_SEND_ERROR:
cJSON_free(param);
break;
case IFLY_SOCKET_EVT_INIT_OK:
break;
case IFLY_SOCKET_EVT_INIT_ERROR:
case IFLY_SOCKET_EVT_HANSHACK_ERROR:
case IFLY_SOCKET_EVT_ACCIDENT_END:
case IFLY_SOCKET_EVT_END:
case IFLY_SOCKET_EVT_FORCE_END:
if (ifly_net_tts_dec_check_run()) { // 正常结束会自动关闭,这里为异常或者强制关闭处理
int to = 10;
if (evt == IFLY_SOCKET_EVT_ACCIDENT_END) { //如果是意外退出,先播放完cbuf的内容
to = 300;
} else {
if (tts_info.status < IFLY_TTS_STATUS_RECV_ERROR) {
tts_info.status = IFLY_TTS_STATUS_RECV_ERROR;
}
}
ifly_tts_dec_func(ifly_net_tts_stop, to * 10);
while (ifly_net_tts_dec_check_run()) {
os_time_dly(1);
to -= 1;
if (to <= 0) {
break;
}
if (tts_info.force_stop) {
tts_info.status = IFLY_TTS_STATUS_RECV_ERROR;
break;
}
}
}
if ((evt != IFLY_SOCKET_EVT_END) && (evt != IFLY_SOCKET_EVT_FORCE_END)) {
if (!tts_info.recv_finish) {
tts_info.param->event_cb(IFLY_TTS_EVT_PLAY_FAIL_STOP, tts_info.param);
}
}
break;
case IFLY_SOCKET_EVT_EXIT:
tts_info.status = IFLY_TTS_STATUS_EXIT;
tts_info.param->event_cb(IFLY_TTS_EVT_EXIT, tts_info.param);
if (tts_socket.auth) {
net_iflytek_free(tts_socket.auth);
tts_socket.auth = NULL;
}
if (tts_info.tts_timer) {
sys_timer_del(tts_info.tts_timer);
tts_info.tts_timer = 0;
}
if (tts_info.dec_out_buf) {
net_iflytek_free(tts_info.dec_out_buf);
tts_info.dec_out_buf = NULL;;
}
break;
default:
break;
}
return 0;
}
bool ifly_tts_start(ifly_tts_param *param)
{
memset(&tts_info, 0, sizeof(tts_info));
memset(&tts_socket, 0, sizeof(struct ifly_websocket_struct));
#if TCFG_IFLYTEK_USE_PSRAM
cJSON_Hooks hooks;
hooks.malloc_fn = net_iflytek_malloc;
hooks.free_fn = net_iflytek_free;
cJSON_InitHooks(&hooks);
#endif
tts_info.param = param;
tts_info.dec_out_buf = net_iflytek_malloc(sizeof(char) * IFLY_TTS_CBUF_LEN);
cbuf_init(&tts_info.dec_cbuf, tts_info.dec_out_buf, IFLY_TTS_CBUF_LEN);
tts_socket.auth = (u8 *)ifly_authentication("wss://tts-api.xfyun.cn/v2/tts",
"tts-api.xfyun.cn",
"GET /v2/tts HTTP/1.1", 20);
if (!tts_socket.auth) {
net_iflytek_free(tts_info.dec_out_buf);
tts_info.dec_out_buf = NULL;;
tts_info.param->event_cb(IFLY_TTS_EVT_PLAY_FAIL_STOP, tts_info.param);
return false;
}
tts_socket.task_name = "ifly_tts";
tts_socket.socket_mode = WEBSOCKET_MODE;
tts_socket.recv_cb = ifly_tts_recv_cb;
tts_socket.get_send = ifly_tts_get_send;
tts_socket.event_cb = ifly_tts_event_cb;
tts_info.status = IFLY_TTS_STATUS_START;
//创建链接
bool ret = ifly_websocket_client_create(&tts_socket);
if (ret == false) {
tts_info.status = IFLY_TTS_STATUS_NULL;
net_iflytek_free(tts_socket.auth);
tts_socket.auth = NULL;
net_iflytek_free(tts_info.dec_out_buf);
tts_info.dec_out_buf = NULL;;
}
#if TTS_AUDIO_SAVE_TEST
if (save_file) {
fclose(save_file);
save_file = NULL;
}
save_file = fopen("storage/sd0/C/sf.mp3", "w+");
if (!save_file) {
log_error("fopen err \n\n");
}
#endif
return ret;
}
void ifly_tts_stop(u8 force_stop, u32 to_ms)
{
log_info("tts close!\n");
tts_info.force_stop = force_stop;
while (tts_socket.auth) { // 结束时auth会自动释放
os_time_dly(1);
if (to_ms <= 10) {
break;
}
to_ms -= 10;
}
if (to_ms < 1000) {
to_ms = 1000;
}
tts_info.force_stop = 1;
ifly_websocket_client_release(&tts_socket, to_ms);
#if TTS_AUDIO_SAVE_TEST
if (save_file) {
fclose(save_file);
save_file = NULL;
}
#endif
}
bool ifly_tts_is_work()
{
if ((tts_info.status != IFLY_TTS_STATUS_NULL) && (tts_info.status != IFLY_TTS_STATUS_EXIT)) {
return true;
}
return false;
}
#endif
@@ -0,0 +1,26 @@
#ifndef __TTS_MAIN_H
#define __TTS_MAIN_H
#include "generic/includes.h"
typedef enum {
IFLY_TTS_EVT_PLAY_START = 1, // 播放开始。*param: ifly_tts_param*
IFLY_TTS_EVT_PLAY_STOP, // 播放结束。*param: ifly_tts_param*
IFLY_TTS_EVT_PLAY_FAIL_STOP, // 播放非正常结束。例如超时、强制结束等。*param: ifly_tts_param*
IFLY_TTS_EVT_EXIT, // 结束。*param: ifly_tts_param*
} ifly_tts_event_enum ;
typedef struct ifly_tts_struct {
// 参数信息,所有参数都需要赋值
char *text_res; // 文本数据
int (*event_cb)(ifly_tts_event_enum evt, void *param); // 事件回调
} ifly_tts_param;
// TTS启动。*param参数句柄需要在stop之后才能释放
bool ifly_tts_start(ifly_tts_param *param);
// TTS结束。
void ifly_tts_stop(u8 force_stop, u32 to_ms);
// 判断tts是否正在运行
bool ifly_tts_is_work(void);
#endif
@@ -0,0 +1,476 @@
#include "string.h"
#include "vad_main.h"
#include "ifly_dec_file.h"
#include "sparkdesk_main.h"
#include "authentication.h"
#include "websocket_define.h"
#include "ifly_socket.h"
#include "app_config.h"
#include "app_task.h"
#include "system/timer.h"
#include "app_main.h"
#include "init.h"
#include "key_event_deal.h"
#include "device/device.h"
#include "app_power_manage.h"
#include "btstack/avctp_user.h"
#include "asm/charge.h"
#include "cJSON.h"
#include "audio_config.h"
#include "third_party_profile/interface/app_protocol_common.h"
#include "circular_buf.h"
#include "ui/ui_api.h"
#include "ifly_common.h"
#if TCFG_IFLYTEK_ENABLE
#define LOG_TAG_CONST NET_IFLY
#define LOG_TAG "[IFLY_VAD]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
#define LOG_CLI_ENABLE
#include "debug.h"
#ifdef TCFG_IFLYTEK_APP_ID
#define APP_ID TCFG_IFLYTEK_APP_ID
#else
#define APP_ID "123"
#endif
#if TCFG_ENC_SPEEX_ENABLE
#define AI_AUDIO_CODING_TYPE AUDIO_CODING_SPEEX // 编码格式
#else
#error "ONLY SUPPORT SPEEX"
#endif
#define AI_AUDIO_CODING_SR 16000 // 采样率。和audio_mic_enc_open()函数中的对应
#define PCM_OUT_BUF_LEN (AI_AUDIO_CODING_SR*2/1000 * 30)
#define SPEEX_SIZE 42
#define AUDIO_LEN 168
#define BASE63_AUDIO_LEN 256
#define STATUS_FIRST_FRAME 0
#define STATUS_CONTINUE_FRAME 1
#define STATUS_LAST_FRAME 2
#define STATUS_NED_FRAME 3
#define HEART_BEAT_REQ "client ping" // 服务器下发的心跳保持请求
typedef enum {
IFLY_VAD_STATUS_NULL = 0,
IFLY_VAD_STATUS_START, // 启动
IFLY_VAD_STATUS_PCM_START, // 音频启动发数
IFLY_VAD_STATUS_SENDING, // 音频数据发数中
IFLY_VAD_STATUS_SEND_END, // 音频数据发数完毕
IFLY_VAD_STATUS_RECV, // 有接受到数据
IFLY_VAD_STATUS_RECV_END, // 接受完成
IFLY_VAD_STATUS_RECV_ERROR, // 接受错误
IFLY_VAD_STATUS_EXIT, // 已经退出
} ifly_vad_status;
struct vad_info_t {
u8 force_stop; // 强制结束
u8 recv_finish; // 接收结束
u8 frame_status;
char *pcm_out_buf;
cbuffer_t pcm_cbuf;
ifly_vad_status status;
ifly_vad_param *param;
};
static struct vad_info_t vad_info;
static struct ifly_websocket_struct vad_socket;
#define AI_AUDIO_SAVE_TEST 0
#if AI_AUDIO_SAVE_TEST
static FILE *save_file = NULL;
#endif
extern int mbedtls_base64_encode(unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen);
//录音编码模块
static u16 vad_audio_send_data(u8 *voice_buf, u16 voice_len)
{
#if AI_AUDIO_SAVE_TEST
if (save_file) {
int wlen = fwrite(save_file, voice_buf, voice_len);
if (wlen != voice_len) {
log_error("save file err: %d, %d\n", wlen, voice_len);
}
}
#endif
//上传数据到服务器
int wlen = cbuf_write(&vad_info.pcm_cbuf, voice_buf, voice_len);
if (wlen != voice_len) {
log_error("pcm out err: %d, %d\n", wlen, voice_len);
}
return 0;
}
static int vad_audio_stop(int cancel)
{
if (!ai_mic_is_busy()) {
log_error("ai_mic_is_null \n\n");
return true;
}
ai_mic_rec_close();
#if AI_AUDIO_SAVE_TEST
if (save_file) {
fclose(save_file);
save_file = NULL;
}
#endif
return true;
}
static int vad_audio_start(void)
{
if (ai_mic_is_busy()) {
log_error("ai_mic_is_busy \n\n");
return false;
}
#if AI_AUDIO_SAVE_TEST
if (save_file) {
fclose(save_file);
save_file = NULL;
}
save_file = fopen("storage/sd0/C/sf.bin", "w+");
if (!save_file) {
log_error("fopen err \n\n");
}
#endif
mic_rec_pram_init(AI_AUDIO_CODING_TYPE, 0, vad_audio_send_data, 4, 1024);
ai_mic_rec_start();
return true;
}
static void ifly_vad_recv_cb(u8 *j_str, u32 len, u8 type)
{
log_info("recv:%s\n", j_str);
if (vad_info.force_stop) {
return;
}
if (vad_info.status >= IFLY_VAD_STATUS_RECV_END) {
return;
}
if (!strcmp((char *)j_str, HEART_BEAT_REQ)) { // 保持心跳请求,不解析
return;
}
cJSON *cjson_root = cJSON_Parse((char *)j_str); //json解析错误
if (cjson_root == NULL) {
log_error("cjson error...\r\n");
if (vad_info.status <= IFLY_VAD_STATUS_RECV_END) {
vad_info.status = IFLY_VAD_STATUS_RECV_ERROR;
}
vad_info.param->event_cb(IFLY_VAD_EVT_NETWORK_RECV_ERROR, vad_info.param);
return;
}
vad_info.status = IFLY_VAD_STATUS_RECV;
cJSON *cjson_data = cJSON_GetObjectItem(cjson_root, "data");
cJSON *cjson_status = cJSON_GetObjectItem(cjson_data, "status");
cJSON *cjson_result = cJSON_GetObjectItem(cjson_data, "result");
cJSON *cjson_ws = cJSON_GetObjectItem(cjson_result, "ws");
int arr_size = cJSON_GetArraySize(cjson_ws);
cJSON *arr_item = cjson_ws->child;
u32 vad_res_len = strlen(vad_info.param->vad_res);
for (int i = 0; i < arr_size; i++) {
cJSON *cjson_cw = cJSON_GetObjectItem(arr_item, "cw");
int arr_size_cw = cJSON_GetArraySize(cjson_cw);
cJSON *arr_item_cw = cjson_cw->child;
for (int j = 0; j < arr_size_cw; j++) {
cJSON *cjson_w = cJSON_GetObjectItem(arr_item_cw, "w");
char *cjson_str = cJSON_Print(cjson_w);
u32 json_len = strlen(cjson_str);
if ((vad_res_len + json_len + 1) > vad_info.param->vad_res_len) {
log_error("len error\n");
} else {
strcpy(&vad_info.param->vad_res[vad_res_len], cjson_str);
vad_res_len += json_len;
}
arr_item_cw = arr_item_cw->next;
cJSON_free(cjson_str);
}
arr_item = arr_item->next;
}
int res_len = strlen(vad_info.param->vad_res);
str_remove_quote(vad_info.param->vad_res, res_len);
log_info("final res:%s\n", vad_info.param->vad_res);
if (cjson_status->valueint == STATUS_LAST_FRAME) {
vad_info.status = IFLY_VAD_STATUS_RECV_END;
vad_info.recv_finish = 1;
vad_info.param->event_cb(IFLY_VAD_EVT_RECV_OK, vad_info.param);
}
cJSON_Delete(cjson_root);
}
//语音听写数据模块
char *ifly_vad_format_audio_data(void)
{
char *data_str = NULL;
cJSON *cjson_test = NULL;
cJSON *cjson_common = NULL;
cJSON *cjson_business = NULL;
cJSON *cjson_data = NULL;
int out_len = 0;
char *buf = net_iflytek_malloc(BASE63_AUDIO_LEN);
char *audio_data = net_iflytek_malloc(AUDIO_LEN);
ASSERT(buf);
ASSERT(audio_data);
int rlen = cbuf_read(&vad_info.pcm_cbuf, audio_data, AUDIO_LEN);
if (rlen != AUDIO_LEN) {
net_iflytek_free(buf);
net_iflytek_free(audio_data);
return NULL;
}
if (vad_info.frame_status == STATUS_FIRST_FRAME) {
mbedtls_base64_encode((unsigned char *)buf, BASE63_AUDIO_LEN, (size_t *)&out_len, (unsigned char *)audio_data, AUDIO_LEN);
//编码第一帧
cjson_test = cJSON_CreateObject();
cjson_common = cJSON_CreateObject();
cjson_business = cJSON_CreateObject();
cjson_data = cJSON_CreateObject();
cJSON_AddStringToObject(cjson_common, "app_id", APP_ID);
cJSON_AddItemToObject(cjson_test, "common", cjson_common);
cJSON_AddStringToObject(cjson_business, "language", "zh_cn");
cJSON_AddStringToObject(cjson_business, "domain", "iat");
cJSON_AddStringToObject(cjson_business, "accent", "mandarin");
cJSON_AddNumberToObject(cjson_business, "speex_size", SPEEX_SIZE);
cJSON_AddItemToObject(cjson_test, "business", cjson_business);
cJSON_AddNumberToObject(cjson_data, "status", 0);
cJSON_AddStringToObject(cjson_data, "format", "audio/L16;rate=16000");
cJSON_AddStringToObject(cjson_data, "encoding", "speex-wb");
cJSON_AddStringToObject(cjson_data, "audio", buf);
cJSON_AddItemToObject(cjson_test, "data", cjson_data);
data_str = cJSON_Print(cjson_test);
cJSON_Delete(cjson_test);
vad_info.frame_status = STATUS_CONTINUE_FRAME;
} else if (vad_info.frame_status == STATUS_CONTINUE_FRAME) {
mbedtls_base64_encode((unsigned char *)buf, BASE63_AUDIO_LEN, (size_t *)&out_len, (unsigned char *)audio_data, AUDIO_LEN);
//编码
cjson_test = cJSON_CreateObject();
cjson_data = cJSON_CreateObject();
cJSON_AddNumberToObject(cjson_data, "status", 1);
cJSON_AddStringToObject(cjson_data, "format", "audio/L16;rate=16000");
cJSON_AddStringToObject(cjson_data, "encoding", "speex-wb");
cJSON_AddStringToObject(cjson_data, "audio", buf);
cJSON_AddItemToObject(cjson_test, "data", cjson_data);
data_str = cJSON_Print(cjson_test);
cJSON_Delete(cjson_test);
} else {
//编码最后一帧
mbedtls_base64_encode((unsigned char *)buf, BASE63_AUDIO_LEN, (size_t *)&out_len, (unsigned char *)audio_data, AUDIO_LEN);
cjson_test = cJSON_CreateObject();
cjson_data = cJSON_CreateObject();
cJSON_AddNumberToObject(cjson_data, "status", 2);
cJSON_AddStringToObject(cjson_data, "format", "audio/L16;rate=16000");
cJSON_AddStringToObject(cjson_data, "encoding", "speex-wb");
cJSON_AddStringToObject(cjson_data, "audio", buf);
cJSON_AddItemToObject(cjson_test, "data", cjson_data);
data_str = cJSON_Print(cjson_test);
cJSON_Delete(cjson_test);
if (vad_info.status < IFLY_VAD_STATUS_SEND_END) {
vad_info.status = IFLY_VAD_STATUS_SEND_END;
vad_audio_stop(1);
cbuf_clear(&vad_info.pcm_cbuf);
}
}
net_iflytek_free(buf);
net_iflytek_free(audio_data);
return data_str;
}
static bool ifly_vad_get_send(u8 **buf, u32 *len)
{
if (vad_info.force_stop) {
log_info("vad task kill!\n");
return false;
}
if (vad_info.status >= IFLY_VAD_STATUS_RECV_END) {
log_info("recv end vad task kill!\n");
return false;
}
if (vad_info.status < IFLY_VAD_STATUS_PCM_START) {
vad_info.status = IFLY_VAD_STATUS_PCM_START;
vad_audio_start();
vad_info.param->event_cb(IFLY_VAD_EVT_AUDIO_START, vad_info.param);
}
if (cbuf_get_data_len(&vad_info.pcm_cbuf) >= AUDIO_LEN) {
char *input_src_json = ifly_vad_format_audio_data();
if (input_src_json == NULL) {
log_error("get json err \n");
return false;
}
*buf = (u8 *)input_src_json;
*len = strlen(input_src_json);
return true;
}
os_time_dly(2);
return true;
}
static int ifly_vad_event_cb(ifly_socket_event_enum evt, void *param)
{
switch (evt) {
case IFLY_SOCKET_EVT_SEND_OK:
cJSON_free(param);
break;
case IFLY_SOCKET_EVT_SEND_ERROR:
cJSON_free(param);
break;
case IFLY_SOCKET_EVT_INIT_OK:
break;
case IFLY_SOCKET_EVT_INIT_ERROR:
case IFLY_SOCKET_EVT_HANSHACK_ERROR:
case IFLY_SOCKET_EVT_ACCIDENT_END:
case IFLY_SOCKET_EVT_END:
case IFLY_SOCKET_EVT_FORCE_END:
vad_audio_stop(1);
if ((evt != IFLY_SOCKET_EVT_END) && (evt != IFLY_SOCKET_EVT_FORCE_END)) {
if (!vad_info.recv_finish) {
vad_info.param->event_cb(IFLY_VAD_EVT_NETWORK_FAIL, vad_info.param);
}
}
break;
case IFLY_SOCKET_EVT_EXIT:
vad_info.status = IFLY_VAD_STATUS_EXIT;
vad_info.param->event_cb(IFLY_VAD_EVT_EXIT, vad_info.param);
if (vad_socket.auth) {
net_iflytek_free(vad_socket.auth);
vad_socket.auth = NULL;
}
if (vad_info.pcm_out_buf) {
net_iflytek_free(vad_info.pcm_out_buf);
vad_info.pcm_out_buf = NULL;
}
break;
default:
break;
}
return 0;
}
bool ifly_vad_start(ifly_vad_param *param)
{
memset(&vad_info, 0, sizeof(struct vad_info_t));
memset(&vad_socket, 0, sizeof(struct ifly_websocket_struct));
#if TCFG_IFLYTEK_USE_PSRAM
cJSON_Hooks hooks;
hooks.malloc_fn = net_iflytek_malloc;
hooks.free_fn = net_iflytek_free;
cJSON_InitHooks(&hooks);
#endif
vad_info.pcm_out_buf = net_iflytek_malloc(PCM_OUT_BUF_LEN);
ASSERT(vad_info.pcm_out_buf);
cbuf_init(&vad_info.pcm_cbuf, vad_info.pcm_out_buf, PCM_OUT_BUF_LEN);
vad_info.param = param;
vad_socket.auth = (u8 *)ifly_authentication("wss://iat-api.xfyun.cn/v2/iat",
"iat-api.xfyun.cn",
"GET /v2/iat HTTP/1.1", 20);
if (!vad_socket.auth) {
net_iflytek_free(vad_info.pcm_out_buf);
vad_info.pcm_out_buf = NULL;
vad_info.param->event_cb(IFLY_VAD_EVT_NETWORK_FAIL, vad_info.param);
return false;
}
vad_socket.task_name = "ifly_vad";
vad_socket.socket_mode = WEBSOCKET_MODE;
vad_socket.recv_cb = ifly_vad_recv_cb;
vad_socket.get_send = ifly_vad_get_send;
vad_socket.event_cb = ifly_vad_event_cb;
vad_info.status = IFLY_VAD_STATUS_START;
//创建链接
bool ret = ifly_websocket_client_create(&vad_socket);
if (ret == false) {
vad_info.status = IFLY_VAD_STATUS_NULL;
net_iflytek_free(vad_socket.auth);
vad_socket.auth = NULL;
net_iflytek_free(vad_info.pcm_out_buf);
vad_info.pcm_out_buf = NULL;
}
return ret;
}
void ifly_vad_stop(u8 force_stop, u32 to_ms)
{
log_info("vad stop!\n");
ifly_vad_audio_stop();
vad_info.force_stop = force_stop;
while (vad_socket.auth) { // 结束时auth会自动释放
os_time_dly(1);
if (to_ms <= 10) {
break;
}
to_ms -= 10;
}
if (to_ms < 1000) {
to_ms = 1000;
}
vad_info.force_stop = 1;
ifly_websocket_client_release(&vad_socket, to_ms);
}
void ifly_vad_audio_stop(void)
{
vad_info.frame_status = STATUS_LAST_FRAME; // 停止语音发送
}
bool ifly_vad_is_work()
{
if ((vad_info.status != IFLY_VAD_STATUS_NULL) && (vad_info.status != IFLY_VAD_STATUS_EXIT)) {
return true;
}
return false;
}
#endif
@@ -0,0 +1,35 @@
#ifndef __VAD_MAIN_H
#define __VAD_MAIN_H
#include "generic/includes.h"
#define MAX_VAD_LEN 1000
typedef enum {
IFLY_VAD_EVT_AUDIO_START = 1, // 音频启动。*param: ifly_vad_param*
IFLY_VAD_EVT_RECV_OK, // VAD接受数据完毕。*param: ifly_vad_param*
IFLY_VAD_EVT_NETWORK_FAIL, // 网络错误。*param: ifly_ai_param*
IFLY_VAD_EVT_NETWORK_RECV_ERROR, // 网络接收错误。*param: ifly_ai_param*
IFLY_VAD_EVT_EXIT, // 结束。*param: ifly_ai_param*
} ifly_vad_event_enum ;
typedef struct ifly_vad_struct {
// 参数信息,所有参数都需要赋值
char *vad_res; // 输出数据
u32 vad_res_len; // 输出数据buf长度,最大MAX_VAD_LEN
int (*event_cb)(ifly_vad_event_enum evt, void *param); // 事件回调
} ifly_vad_param;
// VAD启动。*param参数句柄需要在stop之后才能释放
bool ifly_vad_start(ifly_vad_param *param);
// VAD结束。
void ifly_vad_stop(u8 force_stop, u32 to_ms);
// VAD停止发送语音。
void ifly_vad_audio_stop(void);
// 判断vad是否正在运行
bool ifly_vad_is_work(void);
#endif