初版
This commit is contained in:
@@ -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
|
||||
@@ -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, ¶m);
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user