版本
menu_open
Wwise Unreal Integration Documentation
Audio Input

Wwise Audio Input 插件

Unreal Integration 允许通过 Wwise Audio Input 插件向 Wwise 提供音频输入。请参阅 Audio Input Source Plug-in from the Wwise SDK documentation. 为了向 Wwise 提供音频输入,必须从 AkAudioInputComponent 继承类。

AkAudioInputComponent

AkAudioInputComponentAkComponent 派生。它是一个专门用于向 Wwise 提供音频输入的 AkComponent 。为此,需要实现两个关键函数:

/* 音频回调。此回调将由 Wwise 声音引擎连续调用,并用于为声音引擎提供音频采样。
*/
virtual bool FillSamplesBuffer(uint32 NumChannels, uint32 NumSamples, float** BufferToFill);
/* 此回调用于为 Wwise 声音引擎提供所需音频格式。*/
virtual void GetChannelConfig(AkAudioFormat& AudioFormat);

另外还有一个 Blueprint 函数:Post Associated Audio Input Event。它会以此组件作为游戏对象来源,将组件的 AkAudioEvent 连同关联的 AudioSamples 回调和 AudioFormat 回调一并发给 Wwise。

自定义音频输入行为

为了实现自定义音频输入行为,可编写由 AkAudioInputComponent 派生的自定义类。下面的 UAkVoiceInputComponent.h 和 UAkVoiceInputComponent.cpp 示例展示了类如何获取话筒输入并将其发送给 Wwise 声音引擎。

为了在 C++ Unreal 工程中使用这些文件,需要进行一些初始设置。首先,必须链接 AkAudio 和 Unreal Voice 模块。比如,在工程的 Build.cs 文件中将其添加到 PublicDependencyModuleNames:

public class MyModule : ModuleRules
{
public MyModule(ReadOnlyTargetRules Target) : base(Target)
{
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "AkAudio", "Voice" });
// 其他设置
}
}

首先,必须链接 AkAudio 和 Unreal Voice 模块。比如,在工程的 Build.cs 文件中将其添加到 PublicDependencyModuleNames:

[Voice]
bEnabled=true

在完成这些初始设置后可添加下例中的类,用于通过获取话筒输入并将其发送给 Wwise 来实现自定义音频输入行为。

注意,此处的代码仅用于为读者提供简单的示例。请勿将其用在发售的游戏中!

AkVoiceInputComponent.h:

#pragma once
#include "CoreMinimal.h"
#include "AkAudioInputComponent.h"
#include "Voice.h"
#include "AkVoiceInputComponent.generated.h"
/*
*/
UCLASS(ClassGroup = Audiokinetic, BlueprintType, hidecategories = (Transform, Rendering, Mobility, LOD, Component, Activation), meta = (BlueprintSpawnableComponent))
class WWISEDEMOGAME_API UAkVoiceInputComponent : public UAkAudioInputComponent
{
GENERATED_BODY()
UAkVoiceInputComponent(const class FObjectInitializer& ObjectInitializer);
virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override;
protected:
/* 此函数会在 Wwise 声音引擎中注销此组件所属的 GameObject 后调用。*/
virtual void PostUnregisterGameObject() override;
/* 音频回调。此回调将由 Wwise 声音引擎连续调用,并用于为声音引擎提供音频采样。*/
virtual bool FillSamplesBuffer(uint32 NumChannels, uint32 NumSamples, float** BufferToFill) override;
/* 此回调用于为 Wwise 声音引擎提供所需音频格式。*/
virtual void GetChannelConfig(AkAudioFormat& AudioFormat) override;
/* Unreal IVoiceCapture,用于获取话筒输入。*/
TSharedPtr<IVoiceCapture> VoiceCapture;
/* 此数组会在每次 Voice Capture 传入新的缓冲数据时重置和重写。*/
TArray<uint8> IncomingRawVoiceData;
/* 此数组会存管 Voice Capture 之前收集的所有音频数据。
在处理可用话筒数据时写入,在向 Wwise 引擎发送数据时读取并缩减。
建议不要缩减音频回调中的缓冲数据。请勿将此代码行用在发售的游戏中!*/
TArray<uint8> CollectedRawVoiceData;
/* 此标记用于阻止将话筒数据写入正被读取的已收集数据缓冲区。*/
FThreadSafeBool bIsReadingVoiceData = false;
};

AkVoiceInputComponent.cpp:

#include "AkVoiceInputComponent.h"
UAkVoiceInputComponent::UAkVoiceInputComponent(const class FObjectInitializer& ObjectInitializer) :
UAkAudioInputComponent(ObjectInitializer)
{
CollectedRawVoiceData.Reset();
VoiceCapture = FVoiceModule::Get().CreateVoiceCapture();
}
void UAkVoiceInputComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (!VoiceCapture.IsValid())
{
return;
}
uint32 NumAvailableVoiceCaptureBytes = 0;
EVoiceCaptureState::Type CaptureState = VoiceCapture->GetCaptureState(NumAvailableVoiceCaptureBytes);
/* IVoiceCapture 会在每次 Tick 时更新其 EVoiceCaptureState。
我们可以在两次 Tick 之间通过该状态来辨别是否真的有要收集的新数据。*/
if (CaptureState == EVoiceCaptureState::Ok && NumAvailableVoiceCaptureBytes > 0)
{
uint32 NumVoiceCaptureBytesReturned = 0;
IncomingRawVoiceData.Reset((int32)NumAvailableVoiceCaptureBytes);
IncomingRawVoiceData.AddDefaulted(NumAvailableVoiceCaptureBytes);
uint64 SampleCounter = 0;
VoiceCapture->GetVoiceData(IncomingRawVoiceData.GetData(), NumAvailableVoiceCaptureBytes, NumVoiceCaptureBytesReturned, SampleCounter);
if (NumVoiceCaptureBytesReturned > 0)
{
/* 在从收集的数据缓冲区读取数据时加以调节 */
while (bIsReadingVoiceData) {}
CollectedRawVoiceData.Append(IncomingRawVoiceData);
}
}
}
bool UAkVoiceInputComponent::FillSamplesBuffer(uint32 NumChannels, uint32 NumSamples, float** BufferToFill)
{
if (!VoiceCapture.IsValid())
{
return false;
}
const uint8 NumBytesPerSample = 2;
const uint32 NumRequiredBytesPerChannel = NumSamples * NumBytesPerSample;
const uint32 NumRequiredBytes = NumRequiredBytesPerChannel * NumChannels;
int16 VoiceSample = 0;
uint32 RawChannelIndex = 0;
uint32 RawSampleIndex = 0;
bIsReadingVoiceData = true;
const int32 NumSamplesAvailable = CollectedRawVoiceData.Num() / NumBytesPerSample;
const uint32 BufferSlack = (uint32)FMath::Max(0, (int32)(NumSamples * NumChannels) - NumSamplesAvailable);
for (uint32 c = 0; c < NumChannels; ++c)
{
RawChannelIndex = c * NumRequiredBytesPerChannel;
for (uint32 s = 0; s < NumSamples; ++s)
{
if (s >= (NumSamples - BufferSlack) / NumChannels)
{
/* 若 Wwise 引擎认为从 Voice Capture 收到的数据不足,则使用零来填补缺失采样。*/
BufferToFill[c][s] = 0.0f;
}
else
{
/* 将传入的话筒音频数据转换为带符号浮点数。*/
uint32 RawSampleDataMSBIndex = s * 2 + 1;
uint32 RawSampleDataLSBIndex = s * 2;
VoiceSample = (CollectedRawVoiceData[RawSampleDataMSBIndex] << 8) | CollectedRawVoiceData[RawSampleDataLSBIndex];
BufferToFill[c][s] = VoiceSample / (float)INT16_MAX;
}
}
}
const int32 NumBytesRead = (NumSamples - BufferSlack) * NumBytesPerSample;
/* 注意:建议不要缩减音频回调中的缓冲数据。请勿将此代码行用在发售的游戏中!*/
CollectedRawVoiceData.RemoveAt(0, NumBytesRead);
bIsReadingVoiceData = false;
return true;
}
void UAkVoiceInputComponent::GetChannelConfig(AkAudioFormat& AudioFormat)
{
const int sampleRate = 16000;
AudioFormat.uSampleRate = sampleRate;
AudioFormat.channelConfig.SetStandard(AK_SPEAKER_SETUP_MONO);
if (VoiceCapture.IsValid())
{
/* 发送空白设备名称以使用默认设备。*/
if (!VoiceCapture->Init(FString(""), AudioFormat.uSampleRate, AudioFormat.channelConfig.uNumChannels))
{
UE_LOG(LogTemp, Error, TEXT("Failed to initialize device for voice input!"));
return;
}
VoiceCapture->Start();
}
}
void UAkVoiceInputComponent::PostUnregisterGameObject()
{
Super::PostUnregisterGameObject();
if (VoiceCapture.IsValid())
{
VoiceCapture->Stop();
VoiceCapture->Shutdown();
}
}

在将此类添加到 Unreal 工程后,可创建带有 AkVoiceInputComponent 的自定义 Blueprint 类,并调用 Blueprint 函数 Post Associated Audio Input Event(包含在 AkAudioInputComponent 基类中),以便将话筒数据发送给 Wwise。下图展示了自定义 Blueprint 类的部分 Blueprint(基于 Actor ,并包含名为 AkVoiceInputAkVoiceInputComponent )。


此页面对您是否有帮助?

需要技术支持?

仍有疑问?或者问题?需要更多信息?欢迎联系我们,我们可以提供帮助!

查看我们的“技术支持”页面

介绍一下自己的项目。我们会竭力为您提供帮助。

来注册自己的项目,我们帮您快速入门,不带任何附加条件!

开始 Wwise 之旅