一、环境要求
·UnrealEngine 4.22 及以上版本。
·Microsoft Visual Studio。
·能运行 UnrealEngine 工程的配置环境。
二、前提条件
请提前申请开通 GME 实时语音、语音消息服务,获取到 AppId 以及 Key。申请 GME 服务,详情请参见 服务开通,appId 对应控制台的 AppID,authKey 对应控制台的权限密钥。
三、步骤1:下载工程
通过 下载指引 下载 Unreal Sample Project。
四、步骤2:配置工程
下载后打开工程目录,路径 Source\\UEDemo1 中,找到 UserConfig.cpp,将图中红色框处 appID 及 appKey 修改为所申请的 GME 控制台 服务管理-应用设置 中的 AppID 以及权限密钥。
五、步骤3:编译运行
1)运行程序
单击编辑器运行按钮 ,进入运行程序。
2)初始化
·UserID:相当于 openID,openID 是应用内用户的唯一标识符,每个端的 openid 必须唯一。
·Voice Chat:实时语音功能界面。
·Voice Message:语音消息功能界面。
单击 Login,进行初始化,再单击 Voice Chat 进入实时语音房间配置界面。
3)实时语音进房
·RoomId:房间号码,同房间的人可以互相语音交流。
·RoomType:请使用 Fluency 进入房间。
·JoinRoom:进入语音房间。
·Back:后退到上一个界面。
配置好实时语音房间号后,单击 JoinRoom 进入实时语音房间。
4)实时语音功能
界面上会显示进房的 RoomID 以及本端的 openID。
·Mic:勾选打开麦克风。
·Speaker:勾选打开扬声器。
·3D Voice Effect:勾选打开3D音效。
·Voice Change:选择变声效果。
本端勾选 Mic 及 Speaker 后,另一个终端的机器也重复以上步骤,进入相同的房间,也勾选 Mic 以及 Speaker,便可以实现双方沟通。
如果两个终端都勾选 3D Voice Effect,可通过键盘 【A】【S】【D】【W】改变方位,体验3D语音方位感。
5)语音消息功能
·Language:选择转文本的目标语音,例如说的是中文就选普通话。
·Audio:录音后点击可以收听。
·Audio-to-Text:语音转出来的文本内容。
·Push To Talk:按住进行录音。
·Back:后退到上一个界面。
按住 Push to Talk 按钮,对着麦克风进行说话,放开后语音会转成文本显示在界面中。
六、Sample Project 代码介绍
使用 GME 实时语音主要的流程是 Init > EnterRoom > EnableMic > EnableSpeaker。Sample Project主要的代码在 BaseViewController.cpp 以及 ExperientialDemoViewController.cpp 中。
七、初始化相关
初始化相关的代码在 BaseViewController.cpp 文件中的 InitGME 函数中。这里面包含了初始化、语音消息的鉴权初始化以及设置回调 TMGDelegate。
int UBaseViewController::InitGME(std::string sdkAppId, std::string sdkAppKey, std::string userId) { int nAppid = atoi(sdkAppId.c_str()); int ret = ITMGContextGetInstance()->Init(sdkAppId.c_str(), userId.c_str()); ITMGContextGetInstance()->SetTMGDelegate(this); int RetCode = (int) ITMGContextGetInstance()->CheckMicPermission(); FString msg = FString::Printf(TEXT("check Permission retcode =%d"), RetCode); GEngine->AddOnScreenDebugMessage(INDEX_NONE, 10.0f, FColor::Yellow, *msg); char strSig[128] = {0}; unsigned int nLength = 128; nLength = QAVSDK_AuthBuffer_GenAuthBuffer(nAppid, "0", userId.c_str(), sdkAppKey.c_str(), (unsigned char *)strSig, nLength); ITMGContextGetInstance()->GetPTT()->ApplyPTTAuthbuffer(strSig, nLength); m_appId = sdkAppId; m_appKey = sdkAppKey; m_userId = userId; m_isEnableTips = false; m_tipsMark = 0; return ret;}
使用 GME 需要周期性的调用 Poll 函数。在 UEDemoLevelScriptActor.cpp 脚本中的 Tick 中调用。
void AUEDemoLevelScriptActor::Tick(float DeltaSeconds) { Super::Tick(DeltaSeconds); m_pTestDemoViewController->UpdateTips(); m_pCurrentViewController->UpdatePosition(); ITMGContextGetInstance()->Poll();}
八、进房相关
进房相关的代码在 BaseViewController.cpp 文件中的 EnterRoom 函数中。
void UBaseViewController::EnterRoom(std::string roomID, ITMG_ROOM_TYPE roomType) { int nAppid = atoi(m_appId.c_str()); UserConfig::SetRoomID(roomID); char strSig[128] = {0}; unsigned int nLength = 128; nLength = QAVSDK_AuthBuffer_GenAuthBuffer(nAppid, roomID.c_str(), m_userId.c_str(), m_appKey.c_str(), (unsigned char *)strSig, nLength); GEngine->AddOnScreenDebugMessage(INDEX_NONE, 10.0f, FColor::Yellow, TEXT("onEnterRoom")); ITMGContextGetInstance()->EnterRoom(roomID.c_str(), roomType, strSig, nLength);}
进房回调在同一脚本中的 OnEvent 函数中。
if (eventType == ITMG_MAIN_EVENT_TYPE_ENTER_ROOM) { int32 result = JsonObject->GetIntegerField(TEXT("result")); FString error_info = JsonObject->GetStringField(TEXT("error_info")); if (result == 0) { GEngine->AddOnScreenDebugMessage(INDEX_NONE, 20.0f, FColor::Yellow, TEXT("Enter room success.")); } else { FString msg = FString::Printf(TEXT("Enter room failed. result=%d, info = %ls"), result, *error_info); GEngine->AddOnScreenDebugMessage(INDEX_NONE, 20.0f, FColor::Yellow, *msg); } onEnterRoomCompleted(result, error_info);
九、打开设备
进房成功后打开设备,相关代码在 ExperientialDemoViewController.cpp 中。
void UExperientialDemoViewController::onCheckMic(bool isChecked) { //GEngine->AddOnScreenDebugMessage(INDEX_NONE, 10.0f, FColor::Yellow, L"onCheckMic"); ITMGContext *pContext = ITMGContextGetInstance(); if (pContext) { ITMGAudioCtrl *pTmgCtrl = pContext->GetAudioCtrl(); if (pTmgCtrl) { pTmgCtrl->EnableMic(isChecked); } }}void UExperientialDemoViewController::onCheckSpeaker(bool isChecked) { //GEngine->AddOnScreenDebugMessage(INDEX_NONE, 10.0f, FColor::Yellow, L"onCheckSpeaker"); ITMGContext *pContext = ITMGContextGetInstance(); if (pContext) { ITMGAudioCtrl *pTmgCtrl = pContext->GetAudioCtrl(); if (pTmgCtrl) { pTmgCtrl->EnableSpeaker(isChecked); } }}
十、3D 音效相关
3D 音效的接入可以参考 3D 音效文档。在工程中,首先初始化 3D 音效功能,相关代码在 ExperientialDemoViewController.cpp 中。
void UExperientialDemoViewController::onCheckSpatializer(bool isChecked) { char buffer[256]={0};// snprintf(buffer, sizeof(buffer), "%s3d_model", getFilePath().c_str()); snprintf(buffer, sizeof(buffer), "%sgme_2.8_3d_model.dat", getFilePath().c_str()); int ret1 = ITMGContextGetInstance()->GetAudioCtrl()->InitSpatializer(buffer); int ret2 = ITMGContextGetInstance()->GetAudioCtrl()->EnableSpatializer(isChecked, false); FString msg = FString::Printf(TEXT("InitSpatializer=%d, EnableSpatializer ret=%d"), ret1, ret2); GEngine->AddOnScreenDebugMessage(INDEX_NONE, 10.0f, FColor::Yellow, msg);}
在 UEDemoLevelScriptActor.cpp 脚本 Tick 中调用 UpdatePosition 函数。
void AUEDemoLevelScriptActor::Tick(float DeltaSeconds) { Super::Tick(DeltaSeconds); m_pTestDemoViewController->UpdateTips(); m_pCurrentViewController->UpdatePosition(); ITMGContextGetInstance()->Poll();}void UBaseViewController::UpdatePosition() { if (!m_isCreated) return; ITMGRoom *pTmgRoom = ITMGContextGetInstance()->GetRoom(); if (!pTmgRoom) { return; } int nRange = GetRange(); pTmgRoom->UpdateAudioRecvRange(nRange); FVector cameraLocation = UGameplayStatics::GetPlayerCameraManager(m_pActor->GetWorld(), 0)->GetCameraLocation(); FRotator cameraRotation = UGameplayStatics::GetPlayerCameraManager(m_pActor->GetWorld(), 0)->GetCameraRotation(); FString msg = FString::Printf(TEXT("location(x=%.2f,y=%.2f,z=%.2f), rotation(pitch=%.2f,yaw=%.2f,roll=%.2f)"), cameraLocation.X, cameraLocation.Y, cameraLocation.Z, cameraRotation.Pitch, cameraRotation.Yaw, cameraRotation.Roll); int position[] = { (int)cameraLocation.X,(int)cameraLocation.Y, (int)cameraLocation.Z }; FMatrix matrix = ((FRotationMatrix)cameraRotation); float forward[] = { matrix.GetColumn(0).X,matrix.GetColumn(1).X,matrix.GetColumn(2).X }; float right[] = { matrix.GetColumn(0).Y,matrix.GetColumn(1).Y,matrix.GetColumn(2).Y }; float up[] = { matrix.GetColumn(0).Z,matrix.GetColumn(1).Z,matrix.GetColumn(2).Z }; pTmgRoom->UpdateSelfPosition(position, forward, right, up); SetPositionInfo(msg);}
在 ExperientialDemoViewController.cpp 中打开3D 效果。
void UExperientialDemoViewController::onCheckSpatializer(bool isChecked) { char buffer[256]={0};// snprintf(buffer, sizeof(buffer), "%s3d_model", getFilePath().c_str()); snprintf(buffer, sizeof(buffer), "%sgme_2.8_3d_model.dat", getFilePath().c_str()); int ret1 = ITMGContextGetInstance()->GetAudioCtrl()->InitSpatializer(buffer); int ret2 = ITMGContextGetInstance()->GetAudioCtrl()->EnableSpatializer(isChecked, false); FString msg = FString::Printf(TEXT("InitSpatializer=%d, EnableSpatializer ret=%d"), ret1, ret2); GEngine->AddOnScreenDebugMessage(INDEX_NONE, 10.0f, FColor::Yellow, msg);}
十一、注意事项
如果使用的是 Unreal Engine5.3 及以上版本,需要在 Demo 的 .uproject 工程配置文件的 Plugins下新增 XRBase 组件