前言
- Q: 本文初衷?
- 引擎的初始化是所有操作的第一步,Wwise的SoundEngine初始化不复杂,本文不讨论一些自定义的设置,只是
记录初始化的步骤
,让读者可以五分钟完成初始化工作。关于工程头文件和静态库的配置可以参考这里。在StreamManager步骤涉及到文件流接口的实现,本文采用Wwise提供的默认实现之一AkFilePackageLowLevelIOBlocking
。文件流部分比较复杂,我会重开文章讨论。
初始化顺序
MemoryHook
- 在AkTypes.h文件中有以下extern声明,具体定义自己根据系统和需求实现
1
2
3
4
5
namespace AK
{
AK_EXTERNFUNC( void *, AllocHook )( size_t in_size); ///< Number of bytes to allocate
AK_EXTERNFUNC( void, FreeHook )(void * in_pMemAddress); ///< Pointer to the start of memory allocated withAllocHook
}
- 可以参考一下Unreal Engine4的集成,通过UnrealMemory中的接口在AkAudioDevice.cpp中有实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
namespace AK
{
void * AllocHook( size_t in_size )
{
//FMemory声明在Runtime/Core/Public/HAL/UnrealMemory.h
return FMemory::Malloc( in_size );
}
void FreeHook( void * in_ptr )
{
FMemory::Free( in_ptr );
}
#ifdef _WIN32 // only on PC and XBox360, 这个两个回调函数非必须,可能会在Stream Manager的I/O Pool中用到
void * VirtualAllocHook(
void * in_pMemAddress,
size_t in_size,
unsigned long in_dwAllocationType,
unsigned long in_dwProtect
)
{
return VirtualAlloc( in_pMemAddress, in_size, in_dwAllocationType, in_dwProtect );
}
void VirtualFreeHook(
void * in_pMemAddress,
size_t in_size,
unsigned long in_dwFreeType
)
{
VirtualFree( in_pMemAddress, in_size, in_dwFreeType );
}
#endif // only on PC and XBox360
#if PLATFORM_SWITCH //Switch平台
void * AlignedAllocHook(size_t in_size, size_t in_alignment)
{
return aligned_alloc(in_alignment, in_size);
}
void AlignedFreeHook(void * in_ptr)
{
free(in_ptr);
}
#endif
#if PLATFORM_XBOXONE //XBoxOne
void * APUAllocHook(
size_t in_size, ///< Number of bytes to allocate.
unsigned int in_alignment ///< Alignment in bytes (must be power of two, greater than or equal to four).
)
{
void * pReturn = nullptr;
ApuAlloc( &pReturn, NULL, (UINT32) in_size, in_alignment );
return pReturn;
}
void APUFreeHook(
void * in_pMemAddress ///< Virtual address as returned by APUAllocHook.
)
{
ApuFree( in_pMemAddress );
}
#endif
}
Memory Manager
- 然后最先初始化
内存管理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <AK/SoundEngine/Common/AkMemoryMgr.h> //Memory Manager
#include <AK/SoundEngine/Common/AkModule.h> //Default memory and stream managers
(...)
bool InitSoundEngine()
{
AkMemSettings memSettings;
memSettings.uMaxNumPools = 20;
if ( AK::MemoryMgr::Init( &memSettings ) != AK_Success )
{
assert( ! "Could not create the memory manager." );
return false;
}
(...)
}
- 参考Unreal4中设置, 把所有初始化设置放在EnsureInitialized()
1
2
3
4
5
6
7
8
9
10
bool FAkAudioDevice::EnsureInitialized()
{
(...)
AkMemSettings memSettings;
memSettings.uMaxNumPools = 256;
if ( AK::MemoryMgr::Init( &memSettings ) != AK_Success )
{
return false;
}
}
- Wwise声音引擎的所有内存访问功能都是通过Ak::MemoryMgr这个接口类。默认的实现封装在AkMemoryMgr.lib中。Ak::MemoryMgr::Init()的申明放在AkModule.h。整个AkMemoryMgr接口可以Override,这里不做讨论。
Streaming Manager
-
Wwise官方推荐的集成方式是使用默认的
Stream Manager
实现,然后实现AkStreamMgrModule.h
中的接口。这些接口是Low-Level IO的组成部分。 - 总结Stream Manager初始化流程为:
- 创建StreamMgr对象
- 创建并修改AkDevice设置
- 创建I/O Hook
- 用Hook对象和AkDevice设置来初始化Stream Device。至少有一个初始化过的Stream Device对象,Stream Manager才能正常工作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
#include <AK/SoundEngine/Common/IAkStreamMgr.h> // Streaming Manager #include <AK/Tools/Common/AkPlatformFuncs.h> // Thread defines #include <AkFilePackageLowLevelIOBlocking.h> // Sample low-level I/O implementation CAkFilePackageLowLevelIOBlocking g_lowLevelIO; //这里的IO Hook实现用的是SDK示例工程中的实现,Wwise SDK\版本\SDK\samples\SoundEngine\Win32 (...) bool InitSoundEngine() { (...) AkStreamMgrSettings stmSettings; //创建默认Stream Manager设置对象 AK::StreamMgr::GetDefaultSettings( stmSettings ); //这里可以做设置修改 //用设置初始化Stream Manager对象 if ( !AK::StreamMgr::Create( stmSettings ) ) { assert( ! "Could not create the Streaming Manager" ); return false; } AkDeviceSettings deviceSettings; //创建默认Device设置对象 AK::StreamMgr::GetDefaultDeviceSettings( deviceSettings ); //这里可以做设置修改 //用Hook对象和设置初始化Stream Device if ( g_lowLevelIO.Init( deviceSettings ) != AK_Success ) { assert( ! "Could not create the streaming device and Low-Level I/O system" ); return false; } (...) }
-
参考一下Unreal4的集成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
bool FAkAudioDevice::EnsureInitialized() { (...) AkStreamMgrSettings stmSettings; //创建默认Stream Manager设置对象 AK::StreamMgr::GetDefaultSettings( stmSettings ); //用设置初始化Stream Manager对象 AK::IAkStreamMgr * pStreamMgr = AK::StreamMgr::Create( stmSettings ); if ( ! pStreamMgr ) { return false; } AkDeviceSettings deviceSettings; //创建默认Device设置对象 AK::StreamMgr::GetDefaultDeviceSettings( deviceSettings ); //device设置自定义 deviceSettings.uGranularity = AK_UNREAL_IO_GRANULARITY; deviceSettings.uSchedulerTypeFlags = AK_SCHEDULER_DEFERRED_LINED_UP; deviceSettings.uMaxConcurrentIO = AK_UNREAL_MAX_CONCURRENT_IO; #if PLATFORM_MAC deviceSettings.threadProperties.uStackSize = 4 * 1024 * 1024; // From FRunnableThreadMac #elif PLATFORM_APPLE deviceSettings.threadProperties.uStackSize = 256 * 1024; // From FRunnableThreadApple #elif PLATFORM_SWITCH deviceSettings.threadProperties.uStackSize = 1 * 1024 * 1024; #endif //这里用的I/O Hook实现在AkUnrealIOHookDeferred.cpp LowLevelIOHook = new CAkFilePackageLowLevelIO<CAkUnrealIOHookDeferred, CAkDiskPackage,AkFileCustomParamPolicy>(); //用Hook对象和设置初始化Stream Device if (!LowLevelIOHook->Init( deviceSettings )) { delete LowLevelIOHook; LowLevelIOHook = nullptr; return false; } }
Sound Engine
-
前两项初始化成功后就可以初始化
Sound Engine
了1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#include <AK/SoundEngine/Common/AkSoundEngine.h> // Sound engine bool InitSoundEngine() { (...) AkInitSettings initSettings; AkPlatformInitSettings platformInitSettings; //创建引擎默认初始化设置 AK::SoundEngine::GetDefaultInitSettings( initSettings ); //创建引擎默认平台初始化设置 AK::SoundEngine::GetDefaultPlatformInitSettings( platformInitSettings ); //这里可以自定义设置 //用引擎和平台设置来初始化引擎 if ( AK::SoundEngine::Init( &initSettings, &platformInitSettings ) != AK_Success ) { assert( ! "Could not initialize the Sound Engine." ); return false; } (...) }
-
参考Unreal4集成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
bool FAkAudioDevice::EnsureInitialized() { (...) AkInitSettings initSettings; AkPlatformInitSettings platformInitSettings; //创建引擎默认初始化设置 AK::SoundEngine::GetDefaultInitSettings( initSettings ); //创建引擎默认平台初始化设置 AK::SoundEngine::GetDefaultPlatformInitSettings( platformInitSettings ); //自定义设置 initSettings.eFloorPlane = AkFloorPlane_XY; #if !(PLATFORM_ANDROID || PLATFORM_IOS) // Keep default size on mobile platforms. platformInitSettings.uLEngineDefaultPoolSize = 128 * 1024 * 1024; #endif #if PLATFORM_ANDROID && !PLATFORM_LUMIN extern JavaVM* GJavaVM; platformInitSettings.pJavaVM = GJavaVM; platformInitSettings.jNativeActivity = FAndroidApplication::GetGameActivityThis(); #endif #if defined AK_WIN // OCULUS_START vhamm audio redirect with build of wwise >= 2015.1.5 if (IHeadMountedDisplayModule::IsAvailable()) { FString AudioOutputDevice; IHeadMountedDisplayModule& Hmd = IHeadMountedDisplayModule::Get(); AudioOutputDevice = Hmd.GetAudioOutputDevice(); if(!AudioOutputDevice.IsEmpty()) initSettings.settingsMainOutput.idDevice = AK::GetDeviceIDFromName((wchar_t*)*AudioOutputDevice); } // OCULUS_END #endif const UAkSettings* AkSettings = GetDefault<UAkSettings>(); if (AkSettings && AkSettings->bEnableMultiCoreRendering) { initSettings.taskSchedulerDesc.fcnParallelFor = AkUE4_ParallelForFunc; check(FTaskGraphInterface::Get().IsRunning()); check(FPlatformProcess::SupportsMultithreading()); check(ENamedThreads::bHasHighPriorityThreads); initSettings.taskSchedulerDesc.uNumSchedulerWorkerThreads = FTaskGraphInterface::Get(.GetNumWorkerThreads(); } ////用引擎和平台设置来初始化引擎 if ( AK::SoundEngine::Init( &initSettings, &platformInitSettings ) != AK_Success ) { return false; } }
Music Engine
-
如果游戏用到Wwise的互动音乐部分,则需要初始化
Music Engine
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
#include <AK/MusicEngine/Common/AkMusicEngine.h> // Music Engine bool InitSoundEngine() { (...) AkMusicSettings musicInit; //创建默认Music Engine设置 AK::MusicEngine::GetDefaultInitSettings( musicInit ); //这里可以自定义设置 //用Music Engine设置初始化Music Engine if ( AK::MusicEngine::Init( &musicInit ) != AK_Success ) { assert( ! "Could not initialize the Music Engine." ); return false; } (...) }
-
参考Unreal4集成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
bool FAkAudioDevice::EnsureInitialized() { (...) AkMusicSettings musicInit; //创建默认Music Engine设置 AK::MusicEngine::GetDefaultInitSettings( musicInit ); //这里可以自定义设置 //用Music Engine设置初始化Music Engine if ( AK::MusicEngine::Init( &musicInit ) != AK_Success ) { return false; } //这里还初始化了空间组件 AkSpatialAudioInitSettings spatialAudioInit; if ( AK::SpatialAudio::Init(spatialAudioInit) != AK_Success) { return false; } }
Communications
-
如果你想用Wwise Authoring Application来连接到游戏进行profiling和mixing,你就需要继续初始化
communications
模块。这是个很好用的Debug模块,建议初始化,但是记得在Release版本中关闭。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
#ifndef AK_OPTIMIZED #include <AK/Comm/AkCommunication.h> #endif // AK_OPTIMIZED bool InitSoundEngine() { (...) //Release版本中关闭 #ifndef AK_OPTIMIZED AkCommSettings commSettings; //创建commm默认设置 AK::Comm::GetDefaultInitSettings( commSettings ); //这里可以自定义设置 //用设置初始化comm模块 if ( AK::Comm::Init( commSettings ) != AK_Success ) { assert( ! "Could not initialize communication." ); return false; } #endif // AK_OPTIMIZED (...) }
-
Unreal4集成中按平台和版本做了一些自定义设置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
//Release版本中关闭 #ifndef AK_OPTIMIZED #if !PLATFORM_LINUX #if UE_4_18_OR_LATER const bool HasProjectName = FApp::HasProjectName(); #else const bool HasProjectName = FApp::HasGameName(); #endif // UE_4_18_OR_LATER if(HasProjectName) { #if UE_4_18_OR_LATER FString GameName = FApp::GetProjectName(); #else FString GameName = FApp::GetGameName(); #endif // UE_4_18_OR_LATER #if WITH_EDITORONLY_DATA if(!IsRunningGame()) GameName += TEXT(" (Editor)"); #endif AkCommSettings commSettings; //用设置初始化comm模块 AK::Comm::GetDefaultInitSettings( commSettings ); #if PLATFORM_SWITCH //自定义设置 commSettings.bInitSystemLib = false; #endif FCStringAnsi::Strcpy(commSettings.szAppNetworkName, AK_COMM_SETTINGS_MAX_STRING_SIZE, TCHAR_TO_ANS(*GameName)); //用设置初始化comm模块 if ( AK::Comm::Init( commSettings ) != AK_Success ) { UE_LOG(LogInit, Warning, TEXT("Could not initialize communication. GameName is %s"), *GameName); //return false; } } #endif #endif
-
Wwise提供了一个固定通信端口,两个动态通信端口。固定端口是AkCommSetting::Ports::uDiscoveryBroadcast。动态端口是AkCommSettings::Ports::uCommand,AkCommSettings::Ports::uNotification
-
Wwise提供了
Integration Demo
代码的Init部分也比较有参考意义。