UE C++ Windows プラットフォームは iFLYTEK 音声合成インターフェイスを呼び出します

UE C++ Windows プラットフォームは iFLYTEK 音声合成インターフェイスを呼び出します

環境設定

  • Windows プラットフォーム用 Xunfei Speech Synthesis の C++ バージョン SDK (lib ライブラリ ファイルと dll ダイナミック リンク ライブラリを含む) をダウンロードします。
  • UE プロジェクトの下に新しい ThirdParty/msc ディレクトリを作成し、そこに lib ライブラリ ファイルと dll ダイナミック リンク ライブラリを配置します。
  • ライブラリ ファイルのパスを [PROJECT].Build.cs ファイルに追加します
  • AActor から継承した新しい UE C++ クラス ASpeech を作成し、CPP ファイルをインポート Xunfei ライブラリ
    ディレクトリ構造
    ここに画像の説明を挿入
    [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

Xunfei 音声インターフェイスに電話をかける

Xunfei 音声合成インターフェイスは主に以下で構成されます。
MSPLogin
QTTSSessionBegin
QTTSTextPut
QTTSAudioGet
QTTSSessionEnd
MSPLogout
このうち、MSPLogin と MSPLogout はプログラムの最初と最後に 1 回呼び出すことができます。
音声を合成するたびに、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 は再生コンポーネントです。
    SoundWaveProcedural コンポーネントと AudioComponent コンポーネントを Speech Actor に追加します
    。 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 ファイルを自動的にコピーしません。コピー ファイルのパスは次のとおりです。プログラムの
スクリーン
ここに画像の説明を挿入
ショット
ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/qq_31042143/article/details/130788107