用Hi3861联网科大讯飞实现TTS功能 原创 精华

再见南丫岛
发布于 2022-3-3 16:20
浏览
4收藏

因为业务需要,需要实现TTS功能。现讲开发过程和实现方式整理成文档,供有需要的人参考和讨论。
1、科大讯飞讯飞开放平台可以联网实现TTS功能,注册之后每天可以免费500次访问。
用Hi3861联网科大讯飞实现TTS功能-鸿蒙开发者社区
2、访问科大讯飞平台
目前访问需要Websocket API,帮助文档链接,具体的使用流程可以参看文档说明。
3、在Openharmony下移植websocket
访问websocket使用的是nopoll开源方案。将nopoll工程复制到third_party\nopoll下,在该文件下,添加BUILD.gn文件。

import("//build/lite/config/component/lite_component.gni")
import("//build/lite/ndk/ndk.gni")

config("nopoll_config") {
    include_dirs = [
        "nopoll",
        "//device/hisilicon/hispark_pegasus/sdk_liteos/third_party/lwip_sack/include",
        "//kernel/liteos_m/components/cmsis/2.0",
        "//third_party/lwip/src/include",
        "//third_party/tinycrypt/include",
    ]
}
cflags = [ "-Wno-unused-variable" ]
cflags += [ "-Wno-unused-but-set-variable" ]
cflags += [ "-Wno-unused-parameter" ]
cflags += [ "-Wno-sign-compare" ]
cflags += [ "-Wno-unused-function" ]
cflags += [ "-Wno-return-type" ]
nopoll_sources = [
    "nopoll/nopoll.c",
    "nopoll/nopoll_decl.c",
    "nopoll/nopoll_win32.c",
    "nopoll/nopoll_ctx.c",
    "nopoll/nopoll_conn.c",
    "nopoll/nopoll_log.c",
    "nopoll/nopoll_listener.c",
    "nopoll/nopoll_loop.c",
    "nopoll/nopoll_io.c",
    "nopoll/nopoll_msg.c",
    "nopoll/nopoll_conn_opts.c",
"nopoll/nopoll_rtthread.c",
]

lite_library("nopoll_static") {
    target_type = "static_library"
    sources = nopoll_sources
    public_configs = [ ":nopoll_config" ]
}

lite_library("nopoll_shared") {
    target_type = "shared_library"
    sources = nopoll_sources
    public_configs = [ ":nopoll_config" ]
}

ndk_lib("nopoll_ndk") {
    if (board_name != "hi3861v100") {
        lib_extension = ".so"
        deps = [
            ":nopoll_shared"
        ]
    } else {
        deps = [
            ":nopoll_static"
        ]
    }
    head_files = [
        "//third_party/nopoll"
    ]
}

然后在工程的gn文件下,
用Hi3861联网科大讯飞实现TTS功能-鸿蒙开发者社区
4、实现websocket功能(关键代码)
nopoll还是很吃内存的,需要动态开辟很大的空间。因为考虑到空间,所以,转换的tts格式是mp3格式。
(1)websocket规则的日期获取

char *week[] = {"Mon, ", "Tues, ", "Wed, ", "Thur, ","Fri, ", "Sat, ","Sun, "};
char *month[] = {"", " Jan ", " Feb ", " Mar ", " Apr "," May ", " June "," July ", " Aug ", " Sept ", " Oct "," Nov ", " Dec "};

static void get_date(char *date)
{
	int tv_sec = hi_get_real_time();
	DEBUG_printf("hi_get_real_time=%d\r\n",tv_sec);
	//timeutils_struct_time_t tm;
    //timeutils_seconds_since_2000_to_struct_time(tv_sec, &tm);
	time_t t = tv_sec;
	struct tm *tm = localtime(&t);

    // date: Tue, 15 Oct 2019 07:00:50 GMT 
    sprintf(date, "%s%02d%s%d%s%02d%s%02d%s%02d%s", week[tm->tm_wday], tm->tm_mday, month[tm->tm_mon], tm->tm_year+1900, " ",tm->tm_hour,":", tm->tm_min, ":", tm->tm_sec, " GMT");
}

因为需要校验时间,所以,设备需要联网,然后从网络拉取时间,进行时间更新。
(2)上传的json打包

void ws_xfyun_tts_request_json(char *buff)
{
	char *string = NULL;
	cJSON *root = cJSON_CreateObject();
	//common
	cJSON *cj_common = cJSON_CreateObject();
	cJSON_AddItemToObject(root, "common", cj_common);
    cJSON_AddItemToObject(cj_common, "app_id", cJSON_CreateString("0ea5cd21"));

	//business
	cJSON *cj_business = cJSON_CreateObject();
	cJSON_AddItemToObject(root, "business", cj_business);
    cJSON_AddItemToObject(cj_business, "aue", cJSON_CreateString("lame"));
	cJSON_AddItemToObject(cj_business, "sfl", cJSON_CreateNumber(1));
    cJSON_AddItemToObject(cj_business, "vcn",cJSON_CreateString("xiaoyan"));
	cJSON_AddItemToObject(cj_business, "tte",cJSON_CreateString("UTF8"));
	cJSON_AddItemToObject(cj_business, "pitch",cJSON_CreateNumber(50));
	cJSON_AddItemToObject(cj_business, "speed",cJSON_CreateNumber(50));

	//data
	cJSON *cj_data = cJSON_CreateObject();
	cJSON_AddItemToObject(root, "data", cj_data);
	cJSON_AddItemToObject(cj_data, "status", cJSON_CreateNumber(2));
	
	char base64_text[64];
	int base64_len = sizeof(base64_text);
	tiny_base64_encode(base64_text,&base64_len,tts_text,strlen(tts_text));
	cJSON_AddItemToObject(cj_data, "text", cJSON_CreateString(base64_text));//北京 5YyX5Lqs

	string = cJSON_PrintUnformatted(root);
	strcpy(buff, string);
	cJSON_Delete(root);
    free(string);
}

(3)MP3解码
使用了helix库。

void mp3_decode_array(char *data,int len)
{  
    HMP3Decoder Decoder;
    MP3FrameInfo mp3FrameInfo;
    int bytesleft = len;
    int decode_step = 0;;

    unsigned short int output[1024*2];
    Decoder = MP3InitDecoder();

    int offset = MP3FindSyncWord(data,bytesleft); //搜索缓存中第一个有效数据帧
    DEBUG_printf("offset = %d\r\n",offset);
    if (offset < 0)
    {
        DEBUG_printf("MP3FindSyncWord not find.\r\n");
        bytesleft = 0; // All data not avalible, clear the buffer.
        return; 
    }
    else if (offset > 0)
    {
        //去除头部无效数据
        bytesleft -= offset; 
        decode_step += offset; 
    }
    //以下解码n帧,readPtr会递增,bytesleft递减
    unsigned char *readPtr;
    readPtr = data+decode_step;  
    while (bytesleft > 0)
    {    
        int ret = MP3Decode(Decoder, &readPtr, &bytesleft, (short *)output, 0);
        if (ret == ERR_MP3_NONE) //正常解码
        {
            DEBUG_printf("decode ok:bytesleft=%d\r\n",bytesleft);
            MP3GetLastFrameInfo(Decoder, &(mp3FrameInfo));
            hi_i2s_write((unsigned char *)output, mp3FrameInfo.outputSamps * 2, 1000);
        }
        else//解码异常
        {
            DEBUG_printf("decode err: %d %d\r\n", ret,bytesleft);  
        }
    }
    DEBUG_printf("decode end.\r\n");  
}

5、其他的部分陆续补充。等代码再整理一下,会开放源代码。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2022-3-3 16:55:52修改
3
收藏 4
回复
举报
5条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

感谢分享,期待大佬源码

回复
2022-3-3 18:06:23
物联风景
物联风景

这个厉害了

回复
2022-3-4 08:36:33
harmony门
harmony门

大佬,请问源码整理出来了嘛,还有可以问一下可以采用科大讯飞LinuxSDK做离线的吗?


回复
2023-5-1 14:40:19
wx5be994ca4663e
wx5be994ca4663e

楼主,源码有整理出来吗?发出来学习学习


回复
2024-1-30 20:39:50
忙忙忙困困困
忙忙忙困困困

好厉害

1
回复
2024-2-3 00:11:14
回复
    相关推荐