Wwise를 사용하여 UE 게임에 두 개의 오디오 장치 구현하기

게임 오디오 / 사운드 디자인 / VR 체험

1

먼저 제 소개를 해드릴게요. 저는 에드 카신스키(Ed Kashinsky)이며 러시아 상트페테르부르크 출신 사운드 디자이너 겸 음악가입니다. 현재 저는 아주 흥미롭고 독특한 사운드를 가진 프로젝트를 작업하고 있죠. 바로 프리스트 vs. 폴터가이스트 VR (Priest vs. Poltergeist VR)라는 로컬 멀티플레이어 VR 게임입니다. 이 프로젝트에 사운드를 작업하는 것이 굉장히 까다로웠습니다. 사운드가 아주 흥미롭고 독특하다고 느껴지는 이유가 바로 이 때문이지 않을까 하네요.

프리스트 vs. 폴터가이스트는 폴터가이스트 (시끄러운 소리를 내는 유령)의 역할을 하는 VR 플레이어와 성직자의 역할을 하며 마우스와 키보드로 캐릭터를 제어하는 PC 플레이어 간의 결투 게임입니다. 여기서 까다로운 점은 두 플레이어 모두 한 PC에서 플레이한다는 점입니다. 두 플레이어가 다른 출력 장치 (VR 헤드폰과 PC 헤드폰/스피커)를 통해 소리를 들어야 할 뿐만 아니라, 각 사운드가 별도의 위치 지정, 감쇠, 차단, 타이밍 등을 가져야 하죠.

레벨의 어딘가에서 의자가 땅바닥으로 떨어진다고 상상해보세요. 한 플레이어가 불과 몇 미터 떨어진 곳에 있었다면 소리가 아주 명확하게 들리겠죠. 하지만 다른 플레이어가 같은 거리만큼 떨어져 있지만 벽을 사이에 두고 있을 경우 충돌음이 더욱 건조하고 낮게 들릴 겁니다. 그렇기 때문에 두 플레이어 모두 각자의 출력 장치에서 충돌음을 들어야 하지만 각자 현재 위치와 설정에 따라 소리가 변경되어야 합니다.

Unreal의 네이티브 오디오 라이브러리를 포함한 몇 가지 해결책을 시험해본 후 저희는 Wwise를 사용하기로 했습니다. Wwise는 별도의 응용 프로그램 안에서 원하는 대로 사운드를 작업할 수 있게 해주는 게임을 위한 독립형 오디오 엔진입니다. 심지어 저희 게임의 궁극적인 목표인 사운드를 서로 다른 오디오 장치로 전송하는 기능도 가지고 있죠.

이 엔진의 기능을 Unreal에 통합하기 위해서 Wwise는 플러그인을 제공합니다. 이론적으로 이 플러그인은 Wwise 엔진의 모든 기능을 게임 엔진으로 전달하여 게임 엔진 안에서 직접 사용할 수 있게 해주어야 합니다. 여기서 바로 문제점이 생겼습니다. Wwise의 Unreal 플러그인이 저희가 보조 오디오 장치 출력으로 사운드를 재생하는 데에 필요한 기능을 포함하지 않더라고요.

그래서 직접 C++를 사용하여 이 기능을 추가해야 했죠.

목표

플러그인의 주요 컴포넌트인 AkComponent는 레벨 안에 존재하며 리스너에미터 두 가지 모두로서 작동합니다. 에미터는 레벨 안에 있는 어느 위치에서나 사운드를 재생합니다. 리스너는 한 쌍의 귀와 같이 모든 사운드를 듣고 캐릭터의 위치에 따라 사운드를 오디오 장치로 출력합니다 (보통 카메라 컴포넌트에 연결되어 있죠).

저희 게임에는 두 캐릭터가 있습니다. 각 캐릭터는 동시에 두 오디오 장치로 출력하는 리스너의 역할을 하는 AkComponent를 가지고 있습니다. 일반적으로 다음과 같죠:

2

  • 에미터는 Post Ak Event 블루프린트 함수를 실행하며 이로 인해 Wwise에서 이벤트가 실행됩니다.
  • 이 이벤트는 두 오디오 버스로 전송되는 사운드를 재생하죠.
  • 오디오 버스는 각자의 Wwise 오디오 장치로 사운드를 전송합니다. 저희 게임의 경우 System 혹은 System_VR로 사운드를 전송하죠.
  • 각 리스너는 각자의 출력으로 연결되어 있으며 (Output Device 1, Output Device 2) Wwise로부터 사운드를 듣습니다. 사운드가 오디오 버스에 도달하면 각자의 출력에서 사운드를 재생합니다.

이렇게 하기 위해서 저희는 AkComponent 설정에서 두 개의 텍스트 입력란을 얻어서 사운드를 라우팅할 오디오 장치의 이름을 입력할 수 있게 해야 했습니다.

3

1 단계. Wwise 구성하기

먼저 Wwise 오디오 장치오디오 버스를 만들어봅시다. 더 자세한 정보는 원본 설명서 (버스 라우팅 섹션)를 참고해 주세요.

4

  • Wwise에서 VR 플레이어에 사용할 장치를 추가합니다. 타입을 'System'으로 설정하고 이름을 'System_VR'로 지정합니다. 바로 이 이름이 나중에 AkComponent 설정의 'Wwise Device Name' 입력란에 입력해야 할 Wwise 오디오 장치의 이름입니다.
  • VR에 사용할 Audio Bus를 만들고 System_VR을 Audio Device로 설정합니다.

5

  • Wwise에서는 사운드가 단 하나의 출력 버스로만 전송될 수 있습니다. 하지만 저희는 사운드가 두 캐릭터에게 재생되어야 하기 때문에 Auxilary Bus를 만들어서 사용해봅시다.

6

한 사운드를 두 장치에서 재생하기 위해서 VR 오디오 버스에 사용할 네스팅된 Auxiliary Bus를 만들어야 합니다. 바로 다음과 같이 말이죠:

7

이제 사운드를 각 캐릭터로 혹은 두 캐릭터로 전송할 수 있습니다.

***

이제 주요 작업이 끝났으니 모든 사운드에 버스를 설정하기만 하면 됩니다. 예를 들면 다음과 같습니다:

  • 성직자 사운드 (PC): Output Bus를 Master Audio Bus로 설정하고 Auxiliary Bus는 비워둡니다
  • 폴터가이스트 사운드 (VR): Output Bus를 Master Audio VR Bus로 설정하고 Auxiliary Bus는 비워둡니다
  • 두 캐릭터에 사용할 사운드: Output Bus를 Master Audio Bus로 설정하고 Auxiliary Buses 목록에서 Master_VR_Aux_Bus를 선택합니다

8

이제 Wwise 구성이 끝났습니다.

2 단계. AkComponent 구성하기

이제 컴파일러를 다뤄야 합니다. 그렇지 않으면 플러그인의 변경 사항이 작동하지 않습니다. Blueprint 프로젝트를 C++ 프로젝트로 변환하고 컴파일하는 법을 모르신다면 다음 설명서를 참고해 주세요:

https://docs.unrealengine.com/en-US/Programming/Development/CompilingProjects/index.html

https://docs.unrealengine.com/en-US/Programming/Introduction/index.html

작업해야 할 파일은 아래에서 찾을 수 있습니다

{Project Folder}/Plugins/Wwise/Source/AkAudio

AkAudioDevice.h

/Public/AkAudioDevice.h를 열고 ‘public:’ 뒤에 다음 내용을 추가합니다

//wwise에서의 장치로 오디오 장치 연결
AkOutputDeviceID AddCustomOutput(FString AudioDevice, FString WwiseDevice, UAkComponent* in_pComponent);
//연결 제거
AKRESULT RemoveCustomOutput(AkOutputDeviceID deviceId);
// 하위 문자열로 오디오 장치 검색
TTuple <AkUInt32, FString> SearchAudioDeviceIdByName(FString deviceName);

9

AKComponent.h 

이제 /Classes/AkComponent.h로 가서 ‘public:’ 뒤에 다음 내용을 추가합니다

TTuple <AkUInt32, FString> FAkAudioDevice::SearchAudioDeviceIdByName(FString deviceName)
{
TTuple <AkUInt32, FString> result;
AkUInt32 deviceId = AK_INVALID_DEVICE_ID;

if (deviceName.Len() == 0) {
// getting default device
AK::GetWindowsDevice(-1, deviceId, NULL, AkDeviceState_Active);
auto deviceNameWstr = AK::GetWindowsDeviceName(-1, deviceId, AkDeviceState_Active);
result.Key = deviceId;
result.Value = FString(deviceNameWstr);
} else {
AkUInt32 immDeviceCount = AK::GetWindowsDeviceCount(AkDeviceState_Active);
for (AkUInt32 i = 0; i < immDeviceCount; ++i) {
AK::GetWindowsDevice(i, deviceId, NULL, AkDeviceState_Active);
auto deviceNameWstr = AK::GetWindowsDeviceName(i, deviceId, AkDeviceState_Active);
if (FString(deviceNameWstr).Contains(deviceName)) {
result.Key = deviceId;
result.Value = FString(deviceNameWstr);
break;
}
}
}
return result;
}
AkOutputDeviceID FAkAudioDevice::AddCustomOutput(FString AudioDevice, FString WwiseDevice, UAkComponent* in_pComponent)
{
TTuple <AkUInt32, FString> Device;
AkOutputDeviceID deviceId = AK_INVALID_DEVICE_ID;
FString WwiseDeviceName = "System";
AKRESULT res = AK_Fail;
if (AudioDevice.Len() == 0 && WwiseDevice.Len() == 0) {
return deviceId;
}
if (WwiseDevice.Len() > 0) {
WwiseDeviceName = WwiseDevice;
}
Device = SearchAudioDeviceIdByName(*AudioDevice);
if (Device.Key) {
AkOutputSettings outputSettings(*WwiseDeviceName, Device.Key);
auto gameObjID = in_pComponent->GetAkGameObjectID();
res = AK::SoundEngine::AddOutput(outputSettings, &deviceId, &gameObjID, 1);
}
FString componentName = in_pComponent->GetName();
if (res != AK_Success) {
UE_LOG(LogAkAudio, Error, TEXT("Error attaching of AkComponent \"%s\" to \"%s\" <-> \"%s\". Error \"%d"), *componentName, *AudioDevice, *WwiseDeviceName, res);
} else {
UE_LOG(LogAkAudio, Warning, TEXT("AkComponent \"%s\" attached to \"%s\" <-> \"%s\" "), *componentName, *Device.Value, *WwiseDeviceName);
}
return deviceId;
}
AKRESULT FAkAudioDevice::RemoveCustomOutput(AkOutputDeviceID deviceId)
{
return AK::SoundEngine::RemoveOutput(deviceId);
}

AKComponent.h 

이제 /Classes/AkComponent.h로 가서 ‘public:’ 뒤에 다음 내용을 추가합니다

/**
* Name of Audio Device in Wwise. If empty, "System" is using
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "AkComponent")
FString WwiseDeviceName;
/**
* Name of Audio Device in OS. If empty, default device is using
*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "AkComponent")
FString AudioDeviceName;
AkOutputDeviceID OutputID;

10

AkComponent.cpp

마지막으로 /Private/AkComponent.cpp로 가서 PostRegisterGameObjectPostUnregisterGameObject라는 두 개의 빈 함수를 찾아서 다음 내용으로 교체합니다:

void UAkComponent::PostRegisterGameObject()
{
FAkAudioDevice* AkAudioDevice = FAkAudioDevice::Get();
if (AudioDeviceName.Len() > 0 || WwiseDeviceName.Len() > 0) {
OutputID = AkAudioDevice->AddCustomOutput(AudioDeviceName, WwiseDeviceName, this);
}
}
void UAkComponent::PostUnregisterGameObject()
{
FAkAudioDevice* AkAudioDevice = FAkAudioDevice::Get();
if (AkAudioDevice && OutputID != AK_INVALID_DEVICE_ID) {
AkAudioDevice->RemoveCustomOutput(OutputID);
}
}

11

여기까지 잘 따라오셨나요? 축하드립니다! 이제 Visual Studio에서 프로젝트를 컴파일하고 Unreal 에디터를 실행하기만 하면 됩니다.

모든 작업을 올바르게 실행했다면 Unreal Engine 에디터의 AkComponent 설정에 두 개의 텍스트 입력란이 나타납니다. 바로 이 입력란이 오디오 출력 장치를 Wwise 오디오 장치와 연결해 줍니다.

  • Wwise Device Name - 저희의 경우 'System' 혹은 'System_VR'
  • Audio Device Name - 예를 들어 'Sony Headphones'과 같은 하드웨어 장치 이름. 'Headphones' 만으로도 충분히 실제 장치를 찾을 수 있을 겁니다.

장치를 찾았는지의 여부는 Unreal Editor의 Output Log에서 확인할 수 있습니다:

12

3 단계. 마무리 작업

마지막 단계는 캐릭터 리스너를 설정하는 것입니다. 각 폰/캐릭터에 사용할 AkComponent를 만듭니다. 그런 다음 AkComponent의 설정에서 각 컴포넌트에 맞게 오디오 장치를 입력합니다.

13

이때 '모든' 에미터에 리스너를 설정하는 것이 아주 중요합니다. 그렇지 않으면 이때까지 작업한 내용이 작동하지 않게 됩니다. 리스너를 설정하기 위해서는 'Set Listeners'라는 블루프린트 함수를 사용할 수 있습니다.

Begin Play에서 다음 예시와 같이 함수를 사용하세요.

14

이제 작업이 끝났습니다. 에디터에서 게임을 실행하고 두 오디오 장치를 확인해보세요. Output Log에 다음 내용이 표시되며 두 출력 장치에서 사운드를 들을 수 있을 겁니다.

15

궁금한 것이 있으시다면 제 Soundcloud를 둘러보시거나 저에게 연락해 주세요.

 

에드 카신스키(ED KASHINSKY)

작곡가 겸 사운드 디자이너

에드 카신스키(ED KASHINSKY)

작곡가 겸 사운드 디자이너

에드는 상트페테르부르크 출신 사운드 디자이너 겸 음악가이며 사운드 제작과 소프트웨어 개발에 대한 지식도 겸비하고 있습니다. 상호작용 음악과 사운드 엔진에 관심이 많습니다.

댓글

댓글 달기

이메일 주소는 공개되지 않습니다.

다른 글

제 1부 - 니어 : 오토마타(NieR:Automata)의 공간 음향과 Wwise로 구현한 다양한 게임 플레이 유형

니어 : 오토마타는 외계 침략자들이 인류를 달에 추방한 후 황무지가 된 지구에서 펼쳐지는 액션 롤플레잉 게임 (RPG)입니다. 이 게임은 인류가 지구를 되찾기 위해 개발한...

11.9.2019 - 작성자: PlatinumGames Inc. (플래티넘 게임즈)

Mystralia의 마법적이고 역동적인 음악 사운드스케이프 만들기

Mages of Mystralia는 주인공 지아(Zia)가 마법의 기술을 배우는 매력적이고 다채로운 액션 어드벤처 게임입니다. Borealys Games의 작곡자이자 사운드...

23.6.2020 - 작성자: 안토이네 바숀(ANTOINE VACHON)

상호작용 사운드를 통한 기능적 오디오 인터페이스: 자동차 안전, 안내 및 엔터테인먼트 응용을 위한 청각적 디스플레이 사용하기

본 백서에서는, 자동차 HMI(Human-Machine Interface)를 향상시키고 전반적인 사용자 환경을 개선하기 위해 소리가 혁신적인 방법으로 사용되는 고급 사용 사례를...

15.7.2020 - 작성자: 프랑수아 티볼트 (François Thibault)

사운드 디자이너가 PureData + Heavy를 사용하여 DSP 플러그인을 개발하는 법 - 제 2부

제 1부에서는 Patch 파일을 사용하여 '블루프린트'를 제작하는 법을 설명해 드렸습니다. 이제 Heavy 컴파일러를 사용하여 '자동 워크숍'을 제작한 후, 이 자동 워크숍을...

24.11.2020 - 작성자: 천종 호(Chenzhong Hou)

아우터 월드(Outer Worlds)의 사운드: 제 1부

저희 Obsidian 오디오 팀은 Wwise와 Unreal을 사용하여 아우터 월드(Outer Worlds)의 사운드, 음악, VO를 제작한 방식을 두 편의 글로 심층적으로...

8.12.2020 - 작성자: 옵시디언 엔터테인먼트 (Obsidian Entertainment)

Wwise 미디 기본 지식: 뉴 슈퍼 럭키스 테일(New Super Lucky Tale)의 폭스베리 타이머 음악적 미디 마법!

안녕하세요 멋진 Wwise 사용자분들 :) 게임 오디오 업계에서 살아남기 위한 필수적인 스킬은 바로 문제를 해결하는 능력입니다. 사용하는 도구의 크고 작은 모든 면을 아는 것은...

8.2.2021 - 작성자: 애론 브라운(AARON BROWN)

다른 글

제 1부 - 니어 : 오토마타(NieR:Automata)의 공간 음향과 Wwise로 구현한 다양한 게임 플레이 유형

니어 : 오토마타는 외계 침략자들이 인류를 달에 추방한 후 황무지가 된 지구에서 펼쳐지는 액션 롤플레잉 게임 (RPG)입니다. 이 게임은 인류가 지구를 되찾기 위해 개발한...

Mystralia의 마법적이고 역동적인 음악 사운드스케이프 만들기

Mages of Mystralia는 주인공 지아(Zia)가 마법의 기술을 배우는 매력적이고 다채로운 액션 어드벤처 게임입니다. Borealys Games의 작곡자이자 사운드...

상호작용 사운드를 통한 기능적 오디오 인터페이스: 자동차 안전, 안내 및 엔터테인먼트 응용을 위한 청각적 디스플레이 사용하기

본 백서에서는, 자동차 HMI(Human-Machine Interface)를 향상시키고 전반적인 사용자 환경을 개선하기 위해 소리가 혁신적인 방법으로 사용되는 고급 사용 사례를...