UE4借助Cross-Platform Voice Chat Pro插件实现iOS上录音
Cross-Platform Voice Chat Pro插件
Universal cross-platform Pro Voice Chat是一个在Unreal Engine上多平台多人对战混合平台战队语音通讯的工具插件,支持 Windows / Android / iOS平台(不支持Mac电脑)。
录音功能
要实现录音功能,就要能访问Microphone采集的音频采样数据,Cross-Platform Voice Chat Pro插件的MicrophoneSpeakComponent组件有两个蓝图回调函数,可以帮我们获得实时采集音频数据。
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class UNIVERSALVOICECHATPRO_API UMicrophoneSpeakComponent : public UActorComponent
{
GENERATED_BODY()
// 可以帮我们获得Samples Data(音频采集数据)
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDataMicrophoneReceived, const TArray<uint8>&, data);
// 可以帮我们获得实时采集音频的Volume值(实现示波器)
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDataMicrophoneCurrentVolume, float, currentVolume);
public:
UMicrophoneSpeakComponent();
//...
}
借助插件实现录音
Cross-Platform Voice Chat Pro插件是作为团队语音功能实现的,需要用到多人游戏框架,PC上测试需要打开多人子系统和Voice功能。
编辑Config/DefaultEngine.ini,增加以下文本
[OnlineSubsystem]
DefaultPlatformService=Null
bHasVoiceEnabled=true
[Voice]
bEnabled=true
按照文档,在服务器中创建PlayerVoiceChatActor,然后通过多人游戏框架的Replication功能复制到客户端。
如果只使用iOS的录制功能,就不需要考虑整个插件框架了,单独使用MicrophoneSpeak组件即可。
给自己的游戏角色增加MicrophoneSpeak组件
在BeginPlay增加绑定回调事件
获取实时Volume值
获取Samples Data
启动录制
移动端权限
移动端启动录音前要调用VoiceChatAskMicrophonePermission请求语音权限,这是一段混合代码
void UUniversalVoiceChat::VoiceChatAskMicrophonePermission() {
#if PLATFORM_IOS
[[AVAudioSession sharedInstance] requestRecordPermission:^ (BOOL granted) {
if (granted)
{
}
else
{
}
}];
#endif
#if PLATFORM_ANDROID
UAudioCaptureAndroid::AndroidAskPermission();
#endif
#if PLATFORM_WINDOWS
#endif
}
注意,在调用这段代码以前要先配置plist,增加麦克风录音权限,否则调用VoiceChatAskMicrophonePermission时App会直接崩溃而没有任何提示。
Unreal Engine编译iOS可以在Project Setting中增加plist配置
Peoject Setting->iOS->Extra PList Data->Additional Plist Data
增加录音权限
<key>NSMicrophoneUsageDescription</key><string>Microphone is used for AR functionality</string>
在Windows平台上测试
在Windows平台上测试时,以上方法不能获取到数据,原因是插件在Windows平台上判断了是服务端还是客户端,执行响应代码时默认了MicrophoneSpeak组件在PlayerVoiceChatActor中使用,需要我们作一些修改。
// only capture if this component is from local voice chat actor
if (GetNetMode() != NM_DedicatedServer && UUniversalVoiceChat::GetMyPlayerVoiceActor() != NULL && UUniversalVoiceChat::GetMyPlayerVoiceActor() == (AActor*)GetOwner()){
#if PLATFORM_WINDOWS
if (PCVoiceCapture.IsValid() && PCVoiceCapture->IsCapturing()) {
uint32 VoiceCaptureBytesAvailable = 0;
EVoiceCaptureState::Type CaptureState = PCVoiceCapture->GetCaptureState(VoiceCaptureBytesAvailable);
PCVoiceCaptureBuffer.Reset();
if (CaptureState == EVoiceCaptureState::Ok && VoiceCaptureBytesAvailable > 0)
{
float VoiceCaptureTotalSquared = 0;
PCVoiceCaptureBuffer.SetNumUninitialized(VoiceCaptureBytesAvailable);
// get current voice data
uint64 OutSampleCounter = 0;
uint32 OutAvailableVoiceData = 0;
PCVoiceCapture->GetVoiceData(PCVoiceCaptureBuffer.GetData(), VoiceCaptureBytesAvailable, OutAvailableVoiceData, OutSampleCounter);
UE_LOG(LogTemp, Warning, TEXT("OutAvailableVoiceData %d OutSampleCounter %d"), OutAvailableVoiceData, OutSampleCounter);
OnDataMicrophoneReceived.Broadcast(PCVoiceCaptureBuffer);
PCVoiceCaptureBufferAppended.Append(PCVoiceCaptureBuffer);
int32 amountWillBeRemind = PCVoiceCaptureBufferAppended.Num() % encoderBytesPerFrame;
int32 frameSentToOpus = PCVoiceCaptureBufferAppended.Num() / encoderBytesPerFrame;
int32 startOffsetRemind = (PCVoiceCaptureBufferAppended.Num() / encoderBytesPerFrame) * encoderBytesPerFrame - 1;
//UE_LOG(LogTemp, Warning, TEXT("PCVoiceCaptureBuffer %d PCVoiceCaptureBufferAppended %d frameSentToOpus %d encoderBytesPerFrame %d"),
// PCVoiceCaptureBuffer.Num(), PCVoiceCaptureBufferAppended.Num(), frameSentToOpus, encoderBytesPerFrame);
// transmit local voice data to server
if (frameSentToOpus > 0) {
int32 uint8sizeBufferPCMMic = frameSentToOpus * encoderBytesPerFrame;//PCVoiceCaptureBuffer.Num();
uint8 *pointerBufferPCMMic = (uint8*)malloc(sizeof(uint8) * uint8sizeBufferPCMMic);
//UE_LOG(LogTemp, Warning, TEXT("prepare transmitDataToOtherAndLocal %d %d"), uint8sizeBufferPCMMic, PCVoiceCaptureBuffer.Num());
memcpy(pointerBufferPCMMic, PCVoiceCaptureBufferAppended.GetData(), uint8sizeBufferPCMMic);
transmitDataToOtherAndLocal(pointerBufferPCMMic, uint8sizeBufferPCMMic);
// remove transmited data
PCVoiceCaptureBufferAppended.RemoveAt(0, uint8sizeBufferPCMMic, false);
}
}
}
#endif
}
我们可以把这个判断去掉。
if (true || (GetNetMode() != NM_DedicatedServer && UUniversalVoiceChat::GetMyPlayerVoiceActor() != NULL && UUniversalVoiceChat::GetMyPlayerVoiceActor() == (AActor*)GetOwner()))
这样就可以在编辑器中调试获取音频了