버전

menu_open
Wwise Unreal Integration Documentation
오디오 입력

Wwise Audio Input 플러그인

Unreal 통합은 Wwise Audio Input 플러그인을 통해 Wwise에 오디오 입력을 제공하는 방법을 제공합니다. Wwise SDK 설명서에서 오디오 입력 음원 플러그인을 참고하세요. Wwise에 오디오 입력을 제공하려면 클래스가 AkAudioInputComponent에서 상속해야 합니다.

AkAudioInputComponent

AkAudioInputComponentAkComponent 로부터 파생됩니다. 이 컴포넌트는 Wwise에 오디오 입력을 제공하는 데에 사용되는 특수한 AkComponent 입니다. 다음은 구현되어야 하는 두 가지 주요 함수입니다. Wwise

/* 오디오 호출. 이 함수는 Wwise 사운드 엔진에 의해 계속해서 호출되며
* 사운드 엔진에 오디오 샘플을 제공하는 데에 사용됩니다. */
virtual bool FillSamplesBuffer(uint32 NumChannels, uint32 NumSamples, float** BufferToFill);
/* 이 콜백은 Wwise 사운드 엔진에 필요한 오디오 형식을 제공하는 데에 사용됩니다. */
virtual void GetChannelConfig(AkAudioFormat& AudioFormat);

이 컴포넌트에는 Post Associated Audio Input Event라는 Blueprint 함수도 있습니다. 이 함수는 컴포넌트를 게임 오브젝트의 음원으로 사용하여 해당 컴포넌트의 AkAudioEvent를 연관된 AudioSamples 콜백과 AudioFormat 콜백과 함께 Wwise에 게시합니다.

커스텀 오디오 입력 작동 방식

AkAudioInputComponent에서 파생되는 커스텀 클래스를 제작하여 커스텀 오디오 입력 작동 방식을 구현할 수 있습니다. 아래의 UAkVoiceInputComponent.h와 UAkVoiceInputComponent.cpp 예시에서는 마이크 입력을 가져와서 Wwise 사운드 엔진에 보내주는 함수를 보여줍니다.

이러한 파일을 C++ Unreal 프로젝트에서 사용하기 위해서는 몇 가지 초기 설정 작업이 필요합니다. 먼저 아래 예시와 같이 AkAudio와 Unreal Voice 모듈을 프로젝트의 Build.cs 안에 있는 PublicDependencyModuleNames에 모두 추가하여 연결해야 합니다. Wwise

public class MyModule : ModuleRules
{
public MyModule(ReadOnlyTargetRules Target) : base(Target)
{
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "AkAudio", "Voice" });
// 그 외 설정
}
}

그리고 DefaultEngine.ini 파일에 다음 줄을 추가하여 Voice 모듈에 있는 bEnabled 플래그를 반드시 true로 설정해야 합니다.

Wwise

[Voice]
bEnabled=true

초기 설정 작업이 끝난 후에 마이크 입력을 Wwise에 전송하는 맞춤 오디오 입력 작동 방식을 보여주는 다음 클래스를 추가할 수 있습니다.

이 코드는 빠르고 간단한 예시를 위해 여기에서만 사용됩니다. 출시 게임에서 사용하도록 제작되지 않았습니다!

AkVoiceInputComponent.h:

Wwise

#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:
/* 이 부분은 이 컴포넌트를 소유하는 GameObject가Wwise 사운드 엔진에서 등록 해제된 후에 호출됩니다. */
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;
/* 이 배열은 보이스 캡처로부터 새로운 버퍼가 들어올 때마다 초기화되며 다시 채워집니다. */
TArray<uint8> IncomingRawVoiceData;
/* 이 배열은 보이스 캡처로부터 수집된 이던의 모든 오디오 데이터를 담습니다.
제공되는 마이크 데이터를 처리할 때 제작되며
Wwise 엔진에 데이터를 전달할 때 읽혀집니다 (그리고 축소됩니다).
오디오 콜백에서 버퍼를 축소하는 것은 권장하지 않으며 출시 게임에서는 사용하지 말아야 합니다!*/
TArray<uint8> CollectedRawVoiceData;
/* 이 플래그는 마이크 데이터를 읽는 동안 수집된 데이터 버퍼에 마이크 데이터를 제작하지 않도록 방지해줍니다. */
FThreadSafeBool bIsReadingVoiceData = false;
};

AkVoiceInputComponent.cpp:

Wwise

#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는 각 틱마다 EVoiceCaptureState를 업데이트합니다.
그렇기 때문에 틱과 틱 사이에 이 명시문이 수집할 새로운 데이터가 실제로 있는지 알려줄 것임을 알 수 있습니다. */
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 엔진에서 필요로 하는 데이터보다 적을 경우
누락된 샘플을 0으로 패딩합니다. */
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;
/* NOTE: 오디오 콜백에서 버퍼를 축소하는 것은 권장하지 않습니다. 출시 게임에서는 사용하지 마세요! */
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 클래스를 만들 수 있으며 Post Associated Audio Input Event Blueprint 함수 (기반 클래스 AkAudioInputComponent로부터)를 호출하여 마이크 데이터를 Wwise에 전송하기 시작할 수 있습니다. 아래 이미지는 커스텀 Blueprint 클래스를 위한 Blueprint 클래스의 일부입니다. 이 클래스는 Actor를 기반으로 하며 AkVoiceInput 이라는 AkVoiceInputComponent 를 가지고 있습니다.


이 페이지가 도움이 되었나요?

지원이 필요하신가요?

질문이 있으신가요? 문제를 겪고 계신가요? 더 많은 정보가 필요하신가요? 저희에게 문의해주시면 도와드리겠습니다!

지원 페이지를 방문해 주세요

작업하는 프로젝트에 대해 알려주세요. 언제든지 도와드릴 준비가 되어 있습니다.

프로젝트를 등록하세요. 아무런 조건이나 의무 사항 없이 빠른 시작을 도와드리겠습니다.

Wwise를 시작해 보세요