Dify部署科大讯飞TTS linux SDK
·
目的:实现实时语音生成(尝试过index-tts-vllm,GPT-SoVITS,cosyvoice等模型,发现生成速度和混合文本的合成音频都不满足预期效果,注:qwen3 0.6b tts的混合文本合成效果不错)
服务细节流程参考:
dify TTS部署 GPT-SoVITS
这里主要说一下调用SDK步骤:
总的来说即修改demo的cpp代码,然后使用python搭建一个fastapi服务调用
1.在科大讯飞官网注册一个账号(可以免费使用10台机器90天)
2.下载SDK文件:离线语音合成(新版)
SDK的readme写的很清楚,按着操作就能跑通demo
3.因为demo内的输入和输出是固定的,显然不符合部署为服务的需求,这里进行了改动。
新增头文件aikit_interface.h:
// aikit_interface.h
#ifndef AIKIT_INTERFACE_H
#define AIKIT_INTERFACE_H
#ifdef __cplusplus
extern "C" {
#endif
// 初始化函数
int AEE_Init();
// 文本转语音函数
// text: 输入文本
// output_file: 输出音频文件路径
// language: 1-中文, 2-英文
// role: 发音人角色
int TestXTTS(const char* text, const char* output_file, int language, const char* role);
// 清理资源
void AEE_Cleanup();
#ifdef __cplusplus
}
#endif
#endif
// aikit_interface.cpp
#include <fstream>
#include <atomic>
#include <unistd.h>
#include <cstring>
#include "aikit_biz_api.h"
#include "aikit_constant.h"
#include "aikit_biz_config.h"
#include "aikit_interface.h"
using namespace std;
using namespace AIKIT;
static std::atomic_bool ttsFinished(false);
FILE *fin = nullptr;
static const char *ABILITY = "e2e44feff";
// 回调函数
void OnOutput(AIKIT_HANDLE* handle, const AIKIT_OutputData* output){
printf("OnOutput key:%s\n",(char*)output->node->key);
printf("OnOutput status:%d\n",output->node->status);
if((output->node->value) && (fin != nullptr)) {
fwrite(output->node->value, sizeof(char), output->node->len, fin);
}
}
void OnEvent(AIKIT_HANDLE* handle, AIKIT_EVENT eventType, const AIKIT_OutputEvent* eventValue){
printf("OnEvent:%d\n",eventType);
if(eventType == AIKIT_Event_End){
ttsFinished = true;
}
}
void OnError(AIKIT_HANDLE* handle, int32_t err, const char* desc){
printf("OnError:%d\n",err);
}
// 初始化函数
int AEE_Init() {
AIKIT_Configurator::builder()
.app()
.appID("7e80a282")
.apiSecret("MDcwOTVlMzVlYzYzYzI2OTViYmFmOWFl")
.apiKey("7adafe35f33937d6551db4021eeee3bc")
.workDir("./")
.auth()
.authType(0)
.log()
.logLevel(LOG_LVL_INFO)
.logPath("./");
int ret = AIKIT_Init();
if(ret != 0){
printf("AIKIT_Init failed:%d\n",ret);
return ret;
}
AIKIT_Callbacks cbs = {OnOutput, OnEvent, OnError};
AIKIT_RegisterAbilityCallback(ABILITY, cbs);
return 0;
}
// 文本转语音函数
int TestXTTS(const char* text, const char* output_file, int language, const char* role) {
AIKIT_ParamBuilder* paramBuilder = nullptr;
AIKIT_DataBuilder* dataBuilder = nullptr;
AIKIT_HANDLE* handle = nullptr;
AiText* aiText_raw = nullptr;
ttsFinished = false;
paramBuilder = AIKIT_ParamBuilder::create();
paramBuilder->clear();
paramBuilder->param("vcn", role, strlen(role)); // 发音人角色 xiaoyan:xiaoyan(Female, Chinese), xiaofeng:xiaofeng(Male, Chinese), catherine:catherine(Female, US English)), chongchong:chongchong(Female, Chinese), john:john(Male, US English), christiance:德语, mariane:法语, anna:意大利语, xiaolin:日语, zhongcun:日语, kim:韩语, keshu:俄语, felisa:西班牙语, xiaofang:中文童声, xiaomei:粤语, xiaoyuan:中文普通话, abha:印地语, suparut:泰语, qianqian:中文qianqian, xiaoguan:中文xiaoguan
paramBuilder->param("vcnModel", role, strlen(role));
paramBuilder->param("language", language); //1:中文, 2:英文, 3:法语, 5:日语, 6:俄语, 9:德语, 15:意大利语, 16:韩语, 23:西班牙语, 12:粤语, 8:印地语, 27:泰语
paramBuilder->param("pitch", 60); // 语调 0~100
paramBuilder->param("speed", 65); // 语速 0~100
paramBuilder->param("reg", 0); // 英文发音 0:引擎自动判断, 1:按字母发音, 2:按单词发音
paramBuilder->param("rdn", 0); // 使用参数 0:引擎自动判断, 1:按数字发音, 2:按字符串发音
paramBuilder->param("textEncoding", "UTF-8", strlen("UTF-8"));
int ret = AIKIT_Start(ABILITY, AIKIT_Builder::build(paramBuilder), nullptr, &handle);
printf("AIKIT_Start:%d\n", ret);
if(ret != 0){
goto exit;
}
dataBuilder = AIKIT_DataBuilder::create();
dataBuilder->clear();
aiText_raw = AiText::get("text")->data(text, strlen(text))->once()->valid();
dataBuilder->payload(aiText_raw);
fin = fopen(output_file, "wb");
if (fin == nullptr) {
printf("fopen %s fail.\n", output_file);
ret = -1;
goto exit;
}
ret = AIKIT_Write(handle, AIKIT_Builder::build(dataBuilder));
printf("AIKIT_Write:%d\n", ret);
if(ret != 0){
fclose(fin);
goto exit;
}
// 等待转换完成
while(ttsFinished != true){
usleep(1000);
}
ret = AIKIT_End(handle);
exit:
if (fin != nullptr) {
fclose(fin);
fin = nullptr;
}
if(paramBuilder != nullptr){
delete paramBuilder;
paramBuilder = nullptr;
}
if(dataBuilder != nullptr){
delete dataBuilder;
dataBuilder = nullptr;
}
return ret;
}
void AEE_Cleanup() {
AIKIT_UnInit();
}
4.编译上面的cpp为动态库:./build_interface.sh
#!/bin/bash
# build_interface.sh - 编译为动态库
g++ -std=c++11 -fPIC -shared -o libaikit.so aikit_interface.cpp -Iinclude -Llibs -laikit -Wl,-rpath=libs
echo "编译完成,生成 libaikit.so"
5.用python调用前,根据SDK的readme描述,需要先添加环境变量:
export LD_LIBRARY_PATH=./libs:$LD_LIBRARY_PATH
6.测试调用,使用ctypes库
# tts_simple.py - 简单的文本转语音测试
import ctypes
import os
from ctypes import c_char_p, c_int
class SimpleTTS:
def __init__(self):
# 获取当前脚本所在目录
current_dir = os.path.dirname(os.path.abspath(__file__))
lib_path = os.path.join(current_dir, 'libaikit.so')
print(f"尝试加载库: {lib_path}")
# 检查库文件是否存在
if not os.path.exists(lib_path):
print(f"错误: 库文件不存在: {lib_path}")
print("当前目录文件列表:")
for file in os.listdir(current_dir):
print(f" {file}")
raise FileNotFoundError(f"找不到库文件: {lib_path}")
try:
self.lib = ctypes.CDLL(lib_path)
print("动态库加载成功")
except Exception as e:
print(f"加载动态库失败: {e}")
raise
self._setup_functions()
# 初始化SDK
print("正在初始化TTS SDK...")
result = self.lib.AEE_Init()
if result != 0:
raise RuntimeError(f"TTS SDK初始化失败,错误码: {result}")
print("TTS SDK 初始化成功")
def _setup_functions(self):
self.lib.AEE_Init.argtypes = []
self.lib.AEE_Init.restype = c_int
self.lib.TestXTTS.argtypes = [c_char_p, c_char_p, c_int, c_char_p]
self.lib.TestXTTS.restype = c_int
self.lib.AEE_Cleanup.argtypes = []
self.lib.AEE_Cleanup.restype = None
def tts(self, text, output_file, language=1, voice="xiaofang"):
"""
文本转语音
Args:
text: 要转换的文本
output_file: 输出文件路径
language: 1-中文, 2-英文
voice: 发音人 (xiaofang, catherine等)
"""
print(f"开始文本转语音...")
print(f"文本: {text}")
print(f"输出文件: {output_file}")
print(f"语言: {language}")
print(f"发音人: {voice}")
result = self.lib.TestXTTS(
text.encode('utf-8'),
output_file.encode('utf-8'),
language,
voice.encode('utf-8')
)
success = (result == 0)
if success:
print(f"文本转语音成功,音频保存至: {output_file}")
else:
print(f"文本转语音失败,错误码: {result}")
return success
def __del__(self):
if hasattr(self, 'lib'):
print("清理资源...")
self.lib.AEE_Cleanup()
# 使用示例
if __name__ == "__main__":
try:
tts = SimpleTTS()
# 中文转换
print("\n" + "="*50)
success = tts.tts("你好,这是一个测试", "./test_chinese.pcm", 1, "xiaoyan")
print(f"中文转换: {'成功' if success else '失败'}")
except Exception as e:
print(f"发生错误: {e}")
7.如果步骤6正常,即可将该服务伪装成openai接口格式,用于连接dify,步骤参考文章头部的链接,注:该SDK返回的pcm格式音频文件,而dify并不支持pcm格式音频输入,所以这里需要根据需要,修改cpp的输出,亦或是在python服务中将pcm转为mp3格式
更多推荐


所有评论(0)