UE C++ Windows平台调用讯飞语音合成接口

news2024/11/24 4:17:51

UE C++ Windows平台调用讯飞语音合成接口

  • 环境设置
  • 调用讯飞语音接口
  • 回放语音数据
  • 输出EXE

环境设置

  • 下载讯飞语音合成的Windows平台的C++版本SDK,包含lib库文件和dll动态链接库
  • 在UE工程下新建一个ThirdParty/msc目录,将lib库文件和dll动态链接库放入其中
  • [PROJECT].Build.cs文件增加库文件路径
  • 新建一个UE C++类ASpeech,继承自AActor,CPP文件引入讯飞库
    目录结构
    在这里插入图片描述
    [PROJECT].Build.cs文件新增库文件路径
    // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
    string MSCPath = Path.Combine(ThirdPartyPath, "msc/");
    PublicIncludePaths.AddRange(new string[] { Path.Combine(MSCPath, "Includes") });
    PublicSystemLibraryPaths.Add(Path.Combine(MSCPath, "Libraries"));

Speech.cpp文件引入库文件

#include "Speech.h"
#include "qtts.h"
#include "msp_cmn.h"
#include "msp_errors.h"
#include "Kismet/GameplayStatics.h"

using namespace Audio;
#ifdef _WIN64
#pragma comment(lib,"msc_x64.lib")//x64
#else
#pragma comment(lib,"msc.lib")//x86
#endif

调用讯飞语音接口

讯飞语音合成接口主要由以下几个:
MSPLogin
QTTSSessionBegin
QTTSTextPut
QTTSAudioGet
QTTSSessionEnd
MSPLogout
其中MSPLogin和MSPLogout在程序开始和结束的时候调用一次即可。
每次合成语音时,调用QTTSTextPut,然后循环调用QTTSAudioGet持续获取合成音频数据,直到数据全部接收完成,调用QTTSSessionEnd结束本次语音合成任务
代码如下

  • 初始化
bool ASpeech::Init(const FString& params) {
	// Init TTS
	int ret = MSP_SUCCESS;
	ret = MSPLogin(NULL, NULL, TCHAR_TO_UTF8(*params)); //第一个参数是用户名,第二个参数是密码,第三个参数是登录参数,用户名和密码可在http://www.xfyun.cn注册获取
	if (MSP_SUCCESS != ret)
	{
		FCString::Sprintf(_debug_string_buff, TEXT("MSPLogin failed, error code: %d."), ret);
		ScreenMsg(_debug_string_buff);
		bInited = false;
	}
	else {
		bInited = true;
	}
	return bInited;
}
  • 文字转语音
bool ASpeech::Text2Speech(const FString& text, const FString& params)
{
	if (bGenerating) {
		ScreenMsg(TEXT("Failed: Audio generation in progress!"));
		return false;
	}
	if (text.IsEmpty() || params.IsEmpty()) {
		ScreenMsg(TEXT("Failed: Parameter cannot be empty!"));
		return false;
	}

	// Init params
	int ret = -1;
	sessionID = NULL;
	bGenerating = false;
	GenerateIndex = 0;
	DataLength = 0;
	audioData.SetNumUninitialized(0);
	bPlaying = false;
	PlayTime = UGameplayStatics::GetTimeSeconds(GWorld);
	DataTime = 0;

	/* 开始合成 */
	sessionID = QTTSSessionBegin(TCHAR_TO_UTF8(*params), &ret);
	if (MSP_SUCCESS != ret)
	{
		FCString::Sprintf(_debug_string_buff, TEXT("QTTSSessionBegin failed, error code: %d."), ret);
		ScreenMsg(_debug_string_buff);
		return false;
	}
	const char* p_text = TCHAR_TO_UTF8(*text);
	ret = QTTSTextPut(sessionID, p_text, (unsigned int)strlen(p_text), NULL);
	if (MSP_SUCCESS != ret)
	{
		FCString::Sprintf(_debug_string_buff, TEXT("QTTSTextPut failed, error code: %d."), ret);
		ScreenMsg(_debug_string_buff);
		QTTSSessionEnd(sessionID, "TextPutError");
		return false;
	}

	bGenerating = true;
	return true;
}
  • 在Tick循环中获取语音数据
void ASpeech::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	/* 获取合成音频 */
	if (bGenerating && NULL != sessionID) {
		int ret = -1;
		unsigned int len = 0;
		int synth_status = MSP_TTS_FLAG_STILL_HAVE_DATA;
		const void* data = QTTSAudioGet(sessionID, &len, &synth_status, &ret);
		if (MSP_SUCCESS == ret) {
			if (len > 0) {
				uint8 *p_data = (uint8 *)data;
				for (unsigned int i = 0; i < len; ++i) {
					audioData.Add(p_data[i]);
				}
				DataLength += len;
				// Play audio
				if (0 == GenerateIndex) { // 第一次接收到语音数据,同步开始播放
					if (NULL != AudioComponent) {
						AudioComponent->Play();
						bPlaying = true;
						PlayTime = UGameplayStatics::GetTimeSeconds(GWorld);
					}
				}
				++GenerateIndex;
				payloadReceivedVoiceData(p_data, len); // 装在数据到播放Wave数据区
				OnAudioGet(); // 蓝图调用通知
			}
			if (MSP_TTS_FLAG_DATA_END == synth_status) {
				/* 合成完毕 */
				ret = QTTSSessionEnd(sessionID, "Normal");
				if (MSP_SUCCESS != ret)
				{
					FCString::Sprintf(_debug_string_buff, TEXT("QTTSSessionEnd failed, error code: %d."), ret);
					ScreenMsg(_debug_string_buff);
				}
				bGenerating = false;
				sessionID = NULL;
			}
		}
		else {
		  /* 合成失败 */
			FCString::Sprintf(_debug_string_buff, TEXT("QTTSAudioGet failed, error code: %d."), ret);
			ScreenMsg(_debug_string_buff);
			QTTSSessionEnd(sessionID, "AudioGetError");
			bGenerating = false;
			sessionID = NULL;
		}
	}
}
  • EndPlay结束时反初始化
void ASpeech::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
	Uninit();

	Super::EndPlay(EndPlayReason);
}
void ASpeech::Uninit() {
	MSPLogout();
	bInited = false;
}

回放语音数据

合成的语音数据为PCM格式数据,需要通过SoundWaveProcedural和AudioComponent进行加载和回放

  • SoundWaveProcedural为音频源,可以动态装载音频数据
  • AudioComponent为播放组件
    为Speech Actor增加SoundWaveProcedural和AudioComponent组件
    1、在构造函数中创建AudioComponent组件
ASpeech::ASpeech()
	:
	SoundWaveProcedural(NULL),
	NumChannels(1),
	NumSamples(samples_per_sec),
	SampleRate(samples_per_sec)
{
	AudioComponent = CreateDefaultSubobject<UAudioComponent>(TEXT("Audio"));
	AudioComponent->SetupAttachment(GetRootComponent());

 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
}

2、在BeginPlay函数中初始化SoundWaveProcedural对象,并设置为AudioComponent播放源

void ASpeech::BeginPlay()
{
	Super::BeginPlay();

	// Init audio component
	AudioComponent->bAutoActivate = true;
	AudioComponent->bAlwaysPlay = true;
	AudioComponent->PitchMultiplier = 1.0f;
	AudioComponent->VolumeMultiplier = 1.0f;
	AudioComponent->bIsUISound = false;
	AudioComponent->AttenuationSettings = nullptr;
	AudioComponent->bOverrideAttenuation = false;
	AudioComponent->bAllowSpatialization = false;

	// Init sound wave procedural
	SoundWaveProcedural = NewObject<USoundWaveProcedural>();
	SoundWaveProcedural->SetSampleRate(SampleRate);
	SoundWaveProcedural->NumChannels = NumChannels;
	SoundWaveProcedural->Duration = INDEFINITELY_LOOPING_DURATION;
	SoundWaveProcedural->SoundGroup = SOUNDGROUP_Default;
	SoundWaveProcedural->bLooping = false;
	SoundWaveProcedural->bProcedural = true;
	SoundWaveProcedural->Pitch = 1.0f;
	SoundWaveProcedural->Volume = 1.0f;
	SoundWaveProcedural->AttenuationSettings = nullptr;
	SoundWaveProcedural->bDebug = true;
	SoundWaveProcedural->VirtualizationMode = EVirtualizationMode::PlayWhenSilent;

	// Set audio component source
	AudioComponent->SetSound(SoundWaveProcedural);
	Init(TEXT("appid = 55xxxx45, work_dir = ."));
}

3、在Ticks循环函数通过QTTSAudioGet获取到数据时,装载数据到SoundWaveProcedural对象中

void ASpeech::payloadReceivedVoiceData(const uint8 *Data, int32 DataSize)
{
	if (NULL == AudioComponent || NULL == SoundWaveProcedural) {
		ScreenMsg(TEXT("Error: The sound playback component is empty!"));
		return;
	}
	SoundWaveProcedural->QueueAudio(Data, DataSize);
}

4、第一次装载数据时就可以同步开始播放音频了

				// Play audio
				if (0 == GenerateIndex) { // 第一次接收到语音数据,同步开始播放
					if (NULL != AudioComponent) {
						AudioComponent->Play();
						bPlaying = true;
						PlayTime = UGameplayStatics::GetTimeSeconds(GWorld);
					}
				}
				++GenerateIndex;
				payloadReceivedVoiceData(p_data, len); // 装在数据到播放Wave数据区

输出EXE

输出EXE时,注意将ThirdParty里面的DLL拷贝到输出目录下,UE的输出程序不会自动拷贝ThirdParty下的DLL文件
拷贝文件路径如下
在这里插入图片描述
程序截图
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/549678.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mybatis是如何集成到spring的之托管mapper接口

前言 mybatis集成到spring可以参考spring mvc集成mybatis进行数据库访问 &#xff0c;其中mybatis集成到spring最重要的两个配置分别是SqlSessionFactoryBean和MapperScannerConfigurer&#xff0c;如下所示&#xff1a; <!--mybatis sqlSeesionFactory配置--><bean…

实验五 串行通讯建模以及教程

目录 教程&#xff1a; 第一步下载matlib 第二步找到Simulink 相关文件 链接&#xff1a;https://pan.baidu.com/s/1Im-TUVfV4d8dok2ebXbmjw?pwd2222 提取码&#xff1a;2222 【实验目的】 1、了解MATLAB软件环境和Simulink建模过程&#xff0c;掌握Simulink图形化编程方…

给 compose draw 绘制的非规则图形添加点击监听

前言 导言 在之前的两篇文章中&#xff0c;我们从实例出发&#xff0c;以实践的方式简单介绍了 compose 自定义绘制&#xff08;如何自己绘制想要的控件&#xff09;、为自定义绘制增加动画&#xff08;让控件动起来&#xff09;。 在这篇文章中&#xff0c;我们依然从实例出…

Linux 权限-+完整思维导图+实图例子+深入细节+通俗易懂建议收藏

绪论 当时间的主人&#xff0c;命运的主宰&#xff0c;灵魂的舵手。上一回已将基础权限全部学习完了&#xff0c;本章开始我们将进入到权限的学习。 话不多说安全带系好&#xff0c;发车啦&#xff08;建议电脑观看&#xff09;。 附&#xff1a;红色&#xff0c;部分为重点部分…

基于Gitee的webhook编写hugo的自动构建实现博客自动更新

前言 差不多半年前趁着某云优惠&#xff0c;我买了5年的轻量级应用服务器。 拿着这个服务器原本打算做我的某个APP的服务端的&#xff0c;后来又觉得迁移数据好麻烦&#xff0c;所以随便搞了个博客上去。 选来选去&#xff0c;使用了 hugo 作为构建引擎。 正好&#xff0c;…

跟我一起使用 compose 做一个跨平台的黑白棋游戏(1)整体实现思路

前言 为什么写这系列文章 虽然 compose 正式版已经出来很久了&#xff0c;也有很多大佬写了很多教程文章和实例 demo &#xff0c;但是对于 compose 其实我也还是一知半解的。 特别是对于 compose 的状态管理&#xff0c;由于 compose 声明式的特性&#xff0c;如果不对状态…

chatgpt赋能Python-pythonfor怎么用

PythonFor SEO&#xff1a;如何利用Python提高SEO效果 SEO&#xff08;搜索引擎优化&#xff09;是现代数字营销中至关重要的一环。随着搜索引擎算法不断发展&#xff0c;优化网站以提高排名已经成为了一门复杂的艺术。幸运的是&#xff0c;Python提供了一些强大的工具来简化这…

chatgpt赋能Python-pythonelem

PythonELEM - 简易的Python学习工具 作为一名有10年Python编程经验的工程师&#xff0c;我可以深刻地体会到新手们学习Python的难处。PythonELEM是一个以Python为主题的学习工具&#xff0c;它可以帮助初学者更容易地掌握Python编程。 PythonELEM的功能 PythonELEM是一个简易…

餐饮油烟排放监测管理系统的设计与应用

安科瑞虞佳豪 连日来&#xff0c;河东区生态环境保护综合行政执法支队组织开展餐饮行业油烟净化专项检查工作&#xff0c;有效应对即将到来的夏季餐饮油烟对环境的污染&#xff0c;着力解决群众身边的环境问题。 执法人员对辖区餐饮商户集中区域开展常态化巡查&#xff0c;重…

Metal入门学习:绘制渲染三角形

一、编程指南PDF下载链接(中英文档&#xff09; 1、Metal编程指南PDF链接 https://github.com/dennie-lee/ios_tech_record/raw/main/Metal学习PDF/Metal 编程指南.pdf 2、Metal着色语言(Metal Shader Language:简称MSL)编程指南PDF链接 https://github.com/dennie-lee/ios_te…

chatgpt赋能Python-pythoncumsum

Python中的cumsum-累积求和函数 在数据处理中&#xff0c;经常需要对一个序列的元素进行累加。Python中提供了累积求和函数cumsum()&#xff0c;用于对一个序列的元素进行累加求和操作。 什么是cumsum()函数 cumsum()函数是Python中numpy模块中的一个函数&#xff0c;用于对…

通过小米万兆路由器将小米SoundMove 无缝接入 ChatGPT

通过小米万兆路由器将小米SoundMove 无缝接入 ChatGPT 本教程内容参考 Github 地址(可选)部署查看小米 SoundMove 信息的环境(可选)查看小米 SoundMove 的信息以容器方式部署程序到小米万兆路由器实际效果有待改善点 本教程内容 1 是记录了将小米 SoundMove 接入 ChatGPT 的操…

面向《海贼王》领域数据的知识图谱项目

访问【WRITE-BUG数字空间】_[内附完整源码和文档] 本次任务试图为《海贼王》中出现的各个实体&#xff0c;包括人物、地点、组织等&#xff0c;构建一个知识图谱&#xff0c;帮助我们更好的理解这部作品。 项目内容包括数据采集、知识存储、知识抽取、知识计算、知识应用五大部…

【运动规划算法项目实战】如何使用Pure Pursuit算法进行路径跟踪(附ROS C++代码)

文章目录 前言一、简介二、Pure Pursuit算法优缺点三、 代码实现3.1 算法实现步骤3.2 pure_pursuit.h3.3 pure_pursuit.cpp3.4 cubic_spline_path.py3.5 节点连接关系3.6 RVIZ显示四、总结前言 在自动驾驶和机器人导航领域,路径跟踪是一项关键技术,它使车辆或机器人能够按照…

多线程排序法

多线程排序法 chatGPT给我改的多线程排序法 using System.Collections.Concurrent; using System.Threading; ConcurrentBag<int> sortedList new ConcurrentBag<int>(); void Sort() { int[] arr {2, 6, 12, 8}; List<Thread> threads new List<Threa…

chatgpt赋能Python-pythondone

PythonDone&#xff1a;将Python编程变得更加简单 介绍 Python是一种有着广泛应用的高级编程语言&#xff0c;由于其简洁易学、开发效率高、可移植性好等特点&#xff0c;成为业内最热门的技术之一。但是&#xff0c;对于一些初学者来说&#xff0c;Python的学习过程可能还是…

【论文分享|SIGMOD‘22】WeTune 自动发现和验证重写规则

作者&#xff1a;谢其骏 北京航空航天大学在读硕士&#xff0c; Databend 研发工程师实习生 https://github.com/jun0315 论文原文&#xff1a; Zhaoguo Wang, Zhou Zhou, Yicun Yang, Haoran Ding, Gansen Hu, Ding Ding, Chuzhe Tang, Haibo Chen, Jinyang Li. WeTune: Auto…

【AIGC】11、MDETR | LeCun 团队于 2021 年推出的端到端多模态理解模型

文章目录 一、背景二、方法2.1 DETR2.2 MDETR 三、效果3.1 预训练调整后的检测器3.2 下游任务 论文&#xff1a;MDETR - Modulated Detection for End-to-End Multi-Modal Understanding 代码&#xff1a;https://github.com/ashkamath/mdetr 出处&#xff1a;ICCV 2021 Oral…

chatgpt赋能Python-pythonctrl快捷键

PythonCtrl快捷键使用指南 作为一名有10年Python编程经验的工程师&#xff0c;我深知PythonCtrl快捷键的重要性。PythonCtrl作为一个Python的开源编辑器&#xff0c;在每一个版本中都加入了更多的功能和快捷键&#xff0c;使得Python编程更加高效和易用。在本篇文章中&#xf…

卡方分布分析与应用

卡方检验(chi-square&#xff0c;记为χ2检验)是统计学中常用来计数数据分析的方法&#xff0c;对于总体的分布不作任何假设&#xff0c;因此它属于非参数检验法中的一种。本博文从理论到实际应用去阐述卡方检验&#xff0c;最后用python语言去实现卡方分布的代码。 1. 卡方分…