AkAudioModule.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. /*******************************************************************************
  2. The content of this file includes portions of the proprietary AUDIOKINETIC Wwise
  3. Technology released in source code form as part of the game integration package.
  4. The content of this file may not be used without valid licenses to the
  5. AUDIOKINETIC Wwise Technology.
  6. Note that the use of the game engine is subject to the Unreal(R) Engine End User
  7. License Agreement at https://www.unrealengine.com/en-US/eula/unreal
  8. License Usage
  9. Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use
  10. this file in accordance with the end user license agreement provided with the
  11. software or, alternatively, in accordance with the terms contained
  12. in a written agreement between you and Audiokinetic Inc.
  13. Copyright (c) 2023 Audiokinetic Inc.
  14. *******************************************************************************/
  15. #include "AkAudioModule.h"
  16. #include "AkAudioDevice.h"
  17. #include "AkAudioStyle.h"
  18. #include "AkSettings.h"
  19. #include "AkSettingsPerUser.h"
  20. #include "AkUnrealHelper.h"
  21. #include "Wwise/WwiseResourceLoader.h"
  22. #include "Wwise/WwiseSoundEngineModule.h"
  23. #include "WwiseInitBankLoader/WwiseInitBankLoader.h"
  24. #include "Misc/ScopedSlowTask.h"
  25. #include "UObject/UObjectIterator.h"
  26. #include "Wwise/API/WwiseSoundEngineAPI.h"
  27. #if WITH_EDITORONLY_DATA
  28. #include "Wwise/WwiseFileHandlerModule.h"
  29. #include "Wwise/WwiseProjectDatabase.h"
  30. #include "Wwise/WwiseDataStructure.h"
  31. #include "Wwise/WwiseResourceCooker.h"
  32. #endif
  33. #if WITH_EDITOR
  34. #include "AssetRegistry/AssetRegistryModule.h"
  35. #include "HAL/FileManager.h"
  36. #endif
  37. #include <Runtime/Core/Public/Async/Async.h>
  38. IMPLEMENT_MODULE(FAkAudioModule, AkAudio)
  39. #define LOCTEXT_NAMESPACE "AkAudio"
  40. FAkAudioModule* FAkAudioModule::AkAudioModuleInstance = nullptr;
  41. FSimpleMulticastDelegate FAkAudioModule::OnModuleInitialized;
  42. // AkUnrealHelper overrides
  43. namespace AkUnrealHelper
  44. {
  45. static FString GetWwisePluginDirectoryImpl()
  46. {
  47. return FAkPlatform::GetWwisePluginDirectory();
  48. }
  49. static FString GetWwiseProjectPathImpl()
  50. {
  51. FString projectPath;
  52. if (auto* settings = GetDefault<UAkSettings>())
  53. {
  54. projectPath = settings->WwiseProjectPath.FilePath;
  55. if (FPaths::IsRelative(projectPath))
  56. {
  57. projectPath = FPaths::ConvertRelativePathToFull(GetProjectDirectory(), projectPath);
  58. }
  59. #if PLATFORM_WINDOWS
  60. projectPath.ReplaceInline(TEXT("/"), TEXT("\\"));
  61. #endif
  62. }
  63. return projectPath;
  64. }
  65. static FString GetSoundBankDirectoryImpl()
  66. {
  67. const UAkSettingsPerUser* UserSettings = GetDefault<UAkSettingsPerUser>();
  68. FString SoundBankDirectory;
  69. if (UserSettings && !UserSettings->GeneratedSoundBanksFolderUserOverride.Path.IsEmpty())
  70. {
  71. SoundBankDirectory = FPaths::Combine(GetContentDirectory(), UserSettings->GeneratedSoundBanksFolderUserOverride.Path);
  72. }
  73. else if (const UAkSettings* AkSettings = GetDefault<UAkSettings>())
  74. {
  75. SoundBankDirectory = FPaths::Combine(GetContentDirectory(), AkSettings->GeneratedSoundBanksFolder.Path);
  76. }
  77. else
  78. {
  79. UE_LOG(LogAkAudio, Warning, TEXT("AkUnrealHelper::GetSoundBankDirectory : Please set the Generated Soundbanks Folder in Wwise settings. Otherwise, sound will not function."));
  80. return {};
  81. }
  82. FPaths::CollapseRelativeDirectories(SoundBankDirectory);
  83. if(!SoundBankDirectory.EndsWith(TEXT("/")))
  84. {
  85. SoundBankDirectory.AppendChar('/');
  86. }
  87. return SoundBankDirectory;
  88. }
  89. static FString GetStagePathImpl()
  90. {
  91. const UAkSettings* Settings = GetDefault<UAkSettings>();
  92. #if WITH_EDITORONLY_DATA
  93. if (Settings && !Settings->WwiseStagingDirectory.Path.IsEmpty())
  94. {
  95. return Settings->WwiseStagingDirectory.Path;
  96. }
  97. return TEXT("WwiseAudio");
  98. #endif
  99. if (Settings && !Settings->WwiseStagingDirectory.Path.IsEmpty())
  100. {
  101. return FPaths::ProjectContentDir() / Settings->WwiseStagingDirectory.Path;
  102. }
  103. return FPaths::ProjectContentDir() / TEXT("WwiseAudio");
  104. }
  105. }
  106. void FAkAudioModule::StartupModule()
  107. {
  108. IWwiseSoundEngineModule::ForceLoadModule();
  109. AkUnrealHelper::SetHelperFunctions(
  110. AkUnrealHelper::GetWwisePluginDirectoryImpl,
  111. AkUnrealHelper::GetWwiseProjectPathImpl,
  112. AkUnrealHelper::GetSoundBankDirectoryImpl,
  113. AkUnrealHelper::GetStagePathImpl);
  114. #if WITH_EDITOR
  115. // It is not wanted to initialize the SoundEngine while running the GenerateSoundBanks commandlet.
  116. if (IsRunningCommandlet())
  117. {
  118. // We COULD use GetRunningCommandletClass(), but unfortunately it is set to nullptr in OnPostEngineInit.
  119. // We need to parse the command line.
  120. FString CmdLine(FCommandLine::Get());
  121. if (CmdLine.Contains(TEXT("-run=GenerateSoundBanks")))
  122. {
  123. UE_LOG(LogAkAudio, Log, TEXT("FAkAudioModule::StartupModule: Detected GenerateSoundBanks commandlet is running. AkAudioModule will not be initialized."));
  124. return;
  125. }
  126. #if WITH_EDITORONLY_DATA
  127. if(IWwiseProjectDatabaseModule::IsInACookingCommandlet())
  128. {
  129. // Initialize the Rersource Cooker
  130. IWwiseResourceCookerModule::GetModule();
  131. }
  132. #endif
  133. }
  134. #endif
  135. if (AkAudioModuleInstance == this)
  136. {
  137. UE_LOG(LogAkAudio, Log, TEXT("FAkAudioModule::StartupModule: AkAudioModuleInstance already exists."));
  138. return;
  139. }
  140. UE_CLOG(AkAudioModuleInstance, LogAkAudio, Warning, TEXT("FAkAudioModule::StartupModule: Updating AkAudioModuleInstance from (%p) to (%p)! Previous Module instance was improperly shut down!"), AkAudioModuleInstance, this);
  141. AkAudioModuleInstance = this;
  142. FScopedSlowTask SlowTask(0, LOCTEXT("InitWwisePlugin", "Initializing Wwise Plug-in AkAudioModule..."));
  143. UpdateWwiseResourceLoaderSettings();
  144. #if WITH_EDITORONLY_DATA
  145. ParseGeneratedSoundBankData();
  146. FWwiseInitBankLoader::Get()->UpdateInitBankInSettings();
  147. // Loading the File Handler Module, in case it loads a different module with UStructs, so it gets packaged (Ex.: Simple External Source Manager)
  148. IWwiseFileHandlerModule::GetModule();
  149. #endif
  150. AkAudioDevice = new FAkAudioDevice;
  151. if (!AkAudioDevice)
  152. {
  153. UE_LOG(LogAkAudio, Log, TEXT("FAkAudioModule::StartupModule: Couldn't create FAkAudioDevice. AkAudioModule will not be fully initialized."));
  154. bModuleInitialized = true;
  155. return;
  156. }
  157. if (!AkAudioDevice->Init())
  158. {
  159. UE_LOG(LogAkAudio, Log, TEXT("FAkAudioModule::StartupModule: Couldn't initialize FAkAudioDevice. AkAudioModule will not be fully initialized."));
  160. bModuleInitialized = true;
  161. delete AkAudioDevice;
  162. AkAudioDevice = nullptr;
  163. return;
  164. }
  165. //Load init bank in Runtime (will be loaded by AudiokineticTools module calling ReloadWwiseAssets when in editor)
  166. UE_LOG(LogAkAudio, VeryVerbose, TEXT("FAkAudioModule::StartupModule: Loading Init Bank."));
  167. FWwiseInitBankLoader::Get()->LoadInitBank();
  168. OnTick = FTickerDelegate::CreateRaw(AkAudioDevice, &FAkAudioDevice::Update);
  169. TickDelegateHandle = FCoreTickerType::GetCoreTicker().AddTicker(OnTick);
  170. AkAudioDevice->LoadDelayedObjects();
  171. UE_LOG(LogAkAudio, VeryVerbose, TEXT("FAkAudioModule::StartupModule: Module Initialized."));
  172. OnModuleInitialized.Broadcast();
  173. bModuleInitialized = true;
  174. }
  175. void FAkAudioModule::ShutdownModule()
  176. {
  177. UE_CLOG(AkAudioModuleInstance && AkAudioModuleInstance != this, LogAkAudio, Warning, TEXT("FAkAudioModule::ShutdownModule: Shutting down a different instance (%p) that was initially instantiated (%p)!"), this, AkAudioModuleInstance);
  178. FCoreTickerType::GetCoreTicker().RemoveTicker(TickDelegateHandle);
  179. if (AkAudioDevice)
  180. {
  181. AkAudioDevice->Teardown();
  182. delete AkAudioDevice;
  183. AkAudioDevice = nullptr;
  184. }
  185. if (IWwiseSoundEngineModule::IsAvailable())
  186. {
  187. AkUnrealHelper::SetHelperFunctions(nullptr, nullptr, nullptr, nullptr);
  188. }
  189. AkAudioModuleInstance = nullptr;
  190. }
  191. FAkAudioDevice* FAkAudioModule::GetAkAudioDevice() const
  192. {
  193. return AkAudioDevice;
  194. }
  195. void FAkAudioModule::ReloadWwiseAssetData() const
  196. {
  197. SCOPED_AKAUDIO_EVENT(TEXT("ReloadWwiseAssetData"));
  198. if (FAkAudioDevice::IsInitialized())
  199. {
  200. UE_LOG(LogAkAudio, Log, TEXT("FAkAudioModule::ReloadWwiseAssetData : Reloading Wwise asset data."));
  201. if (AkAudioDevice)
  202. {
  203. AkAudioDevice->ClearSoundBanksAndMedia();
  204. }
  205. auto* InitBankLoader = FWwiseInitBankLoader::Get();
  206. if (LIKELY(InitBankLoader))
  207. {
  208. InitBankLoader->LoadInitBank();
  209. }
  210. else
  211. {
  212. UE_LOG(LogAkAudio, Error, TEXT("LoadInitBank: WwiseInitBankLoader is not initialized."));
  213. }
  214. for (TObjectIterator<UAkAudioType> AudioAssetIt; AudioAssetIt; ++AudioAssetIt)
  215. {
  216. AudioAssetIt->LoadData();
  217. }
  218. }
  219. else
  220. {
  221. UE_LOG(LogAkAudio, Verbose, TEXT("FAkAudioModule::ReloadWwiseAssetData : Skipping asset data reload because the SoundEngine is not initialized."));
  222. }
  223. }
  224. void FAkAudioModule::UpdateWwiseResourceLoaderSettings()
  225. {
  226. SCOPED_AKAUDIO_EVENT(TEXT("UpdateWwiseResourceLoaderSettings"));
  227. UE_LOG(LogAkAudio, Log, TEXT("FAkAudioModule::UpdateWwiseResourceLoaderSettings : Updating Resource Loader settings."));
  228. auto* ResourceLoader = FWwiseResourceLoader::Get();
  229. if (!ResourceLoader)
  230. {
  231. UE_LOG(LogAkAudio, Error, TEXT("FAkAudioModule::UpdateWwiseResourceLoaderSettings : No Resource Loader!"));
  232. return;
  233. }
  234. auto* ResourceLoaderImpl = ResourceLoader->ResourceLoaderImpl.Get();
  235. if (!ResourceLoaderImpl)
  236. {
  237. UE_LOG(LogAkAudio, Error, TEXT("FAkAudioModule::UpdateWwiseResourceLoaderSettings : No Resource Loader Impl!"));
  238. return;
  239. }
  240. ResourceLoaderImpl->StagePath = AkUnrealHelper::GetStagePathImpl();
  241. #if WITH_EDITORONLY_DATA
  242. ResourceLoaderImpl->GeneratedSoundBanksPath = FDirectoryPath{AkUnrealHelper::GetSoundBankDirectory()};
  243. #endif
  244. }
  245. #if WITH_EDITORONLY_DATA
  246. void FAkAudioModule::ParseGeneratedSoundBankData()
  247. {
  248. SCOPED_AKAUDIO_EVENT(TEXT("ParseGeneratedSoundBankData"));
  249. if (auto* AkSettings = GetDefault<UAkSettings>())
  250. {
  251. if (!AkSettings->AreSoundBanksGenerated())
  252. {
  253. UE_LOG(LogAkAudio, Warning, TEXT("FAkAudioModule::ParseGeneratedSoundBankData: SoundBanks are not yet generated, nothing to parse.\nCurrent Generated SoundBanks path is: %s"), *AkUnrealHelper::GetSoundBankDirectory());
  254. return;
  255. }
  256. }
  257. UE_LOG(LogAkAudio, Log, TEXT("FAkAudioModule::ParseGeneratedSoundBankData : Parsing Wwise project data."));
  258. auto* ProjectDatabase = FWwiseProjectDatabase::Get();
  259. if (UNLIKELY(!ProjectDatabase))
  260. {
  261. UE_LOG(LogAkAudio, Warning, TEXT("FAkAudioModule::ParseGeneratedSoundBankData : Could not get FWwiseProjectDatabase instance. Generated sound data will not be parsed."));
  262. }
  263. else
  264. {
  265. ProjectDatabase->UpdateDataStructure();
  266. }
  267. }
  268. #endif
  269. #undef LOCTEXT_NAMESPACE