AkAudioModule.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  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 "WwiseUnrealDefines.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 "Async/Async.h"
  38. IMPLEMENT_MODULE(FAkAudioModule, AkAudio)
  39. #define LOCTEXT_NAMESPACE "AkAudio"
  40. FAkAudioModule* FAkAudioModule::AkAudioModuleInstance = nullptr;
  41. FSimpleMulticastDelegate FAkAudioModule::OnModuleInitialized;
  42. FSimpleMulticastDelegate FAkAudioModule::OnWwiseAssetDataReloaded;
  43. // WwiseUnrealHelper overrides
  44. namespace WwiseUnrealHelper
  45. {
  46. static FString GetWwisePluginDirectoryImpl()
  47. {
  48. return FAkPlatform::GetWwisePluginDirectory();
  49. }
  50. static FString GetWwiseProjectPathImpl()
  51. {
  52. FString projectPath;
  53. if (auto* settings = GetDefault<UAkSettings>())
  54. {
  55. projectPath = settings->WwiseProjectPath.FilePath;
  56. if (FPaths::IsRelative(projectPath))
  57. {
  58. projectPath = FPaths::ConvertRelativePathToFull(GetProjectDirectory(), projectPath);
  59. }
  60. #if PLATFORM_WINDOWS
  61. projectPath.ReplaceInline(TEXT("/"), TEXT("\\"));
  62. #endif
  63. }
  64. return projectPath;
  65. }
  66. static FString GetSoundBankDirectoryImpl()
  67. {
  68. const UAkSettingsPerUser* UserSettings = GetDefault<UAkSettingsPerUser>();
  69. FString SoundBankDirectory;
  70. if (UserSettings && !UserSettings->RootOutputPathOverride.Path.IsEmpty())
  71. {
  72. SoundBankDirectory = UserSettings->RootOutputPathOverride.Path;
  73. if(FPaths::IsRelative(UserSettings->RootOutputPathOverride.Path))
  74. {
  75. SoundBankDirectory = FPaths::Combine(GetContentDirectory(), UserSettings->RootOutputPathOverride.Path);
  76. }
  77. }
  78. else if (const UAkSettings* AkSettings = GetDefault<UAkSettings>())
  79. {
  80. if(AkSettings->RootOutputPath.Path.IsEmpty())
  81. {
  82. return {};
  83. }
  84. SoundBankDirectory = AkSettings->RootOutputPath.Path;
  85. if(FPaths::IsRelative(AkSettings->RootOutputPath.Path))
  86. {
  87. SoundBankDirectory = FPaths::Combine(GetContentDirectory(), AkSettings->RootOutputPath.Path);
  88. }
  89. }
  90. else
  91. {
  92. UE_LOG(LogAkAudio, Warning, TEXT("WwiseUnrealHelper::GetSoundBankDirectory : Please set the Generated Soundbanks Folder in Wwise settings. Otherwise, sound will not function."));
  93. return {};
  94. }
  95. FPaths::CollapseRelativeDirectories(SoundBankDirectory);
  96. if(!SoundBankDirectory.EndsWith(TEXT("/")))
  97. {
  98. SoundBankDirectory.AppendChar('/');
  99. }
  100. return SoundBankDirectory;
  101. }
  102. static FString GetStagePathImpl()
  103. {
  104. const UAkSettings* Settings = GetDefault<UAkSettings>();
  105. #if WITH_EDITORONLY_DATA
  106. if (Settings && !Settings->WwiseStagingDirectory.Path.IsEmpty())
  107. {
  108. return Settings->WwiseStagingDirectory.Path;
  109. }
  110. return TEXT("WwiseAudio");
  111. #endif
  112. if (Settings && !Settings->WwiseStagingDirectory.Path.IsEmpty())
  113. {
  114. return FPaths::ProjectContentDir() / Settings->WwiseStagingDirectory.Path;
  115. }
  116. return FPaths::ProjectContentDir() / TEXT("WwiseAudio");
  117. }
  118. }
  119. void FAkAudioModule::StartupModule()
  120. {
  121. IWwiseSoundEngineModule::ForceLoadModule();
  122. WwiseUnrealHelper::SetHelperFunctions(
  123. WwiseUnrealHelper::GetWwisePluginDirectoryImpl,
  124. WwiseUnrealHelper::GetWwiseProjectPathImpl,
  125. WwiseUnrealHelper::GetSoundBankDirectoryImpl,
  126. WwiseUnrealHelper::GetStagePathImpl);
  127. #if WITH_EDITOR
  128. // It is not wanted to initialize the SoundEngine while running the GenerateSoundBanks commandlet.
  129. if (IsRunningCommandlet())
  130. {
  131. // We COULD use GetRunningCommandletClass(), but unfortunately it is set to nullptr in OnPostEngineInit.
  132. // We need to parse the command line.
  133. FString CmdLine(FCommandLine::Get());
  134. if (CmdLine.Contains(TEXT("run=GenerateSoundBanks")))
  135. {
  136. UE_LOG(LogAkAudio, Log, TEXT("FAkAudioModule::StartupModule: Detected GenerateSoundBanks commandlet is running. AkAudioModule will not be initialized."));
  137. return;
  138. }
  139. #if WITH_EDITORONLY_DATA
  140. if(!IWwiseProjectDatabaseModule::ShouldInitializeProjectDatabase())
  141. {
  142. // Initialize the Rersource Cooker
  143. IWwiseResourceCookerModule::GetModule();
  144. }
  145. #endif
  146. }
  147. #endif
  148. if (AkAudioModuleInstance == this)
  149. {
  150. UE_LOG(LogAkAudio, Log, TEXT("FAkAudioModule::StartupModule: AkAudioModuleInstance already exists."));
  151. return;
  152. }
  153. UE_CLOG(AkAudioModuleInstance, LogAkAudio, Warning, TEXT("FAkAudioModule::StartupModule: Updating AkAudioModuleInstance from (%p) to (%p)! Previous Module instance was improperly shut down!"), AkAudioModuleInstance, this);
  154. AkAudioModuleInstance = this;
  155. FScopedSlowTask SlowTask(0, LOCTEXT("InitWwisePlugin", "Initializing Wwise Plug-in AkAudioModule..."));
  156. UpdateWwiseResourceLoaderSettings();
  157. #if WITH_EDITORONLY_DATA
  158. if (auto* AkSettings = GetDefault<UAkSettings>())
  159. {
  160. if (AkSettings->AreSoundBanksGenerated())
  161. {
  162. ParseGeneratedSoundBankData();
  163. FWwiseInitBankLoader::Get()->UpdateInitBankInSettings();
  164. }
  165. }
  166. // Loading the File Handler Module, in case it loads a different module with UStructs, so it gets packaged (Ex.: Simple External Source Manager)
  167. IWwiseFileHandlerModule::GetModule();
  168. #endif
  169. AkAudioDevice = new FAkAudioDevice;
  170. if (!AkAudioDevice)
  171. {
  172. UE_LOG(LogAkAudio, Log, TEXT("FAkAudioModule::StartupModule: Couldn't create FAkAudioDevice. AkAudioModule will not be fully initialized."));
  173. bModuleInitialized = true;
  174. return;
  175. }
  176. if (!AkAudioDevice->Init())
  177. {
  178. UE_LOG(LogAkAudio, Log, TEXT("FAkAudioModule::StartupModule: Couldn't initialize FAkAudioDevice. AkAudioModule will not be fully initialized."));
  179. bModuleInitialized = true;
  180. delete AkAudioDevice;
  181. AkAudioDevice = nullptr;
  182. return;
  183. }
  184. //Load init bank in Runtime
  185. UE_LOG(LogAkAudio, VeryVerbose, TEXT("FAkAudioModule::StartupModule: Loading Init Bank."));
  186. FWwiseInitBankLoader::Get()->LoadInitBank();
  187. OnTick = FTickerDelegate::CreateRaw(AkAudioDevice, &FAkAudioDevice::Update);
  188. TickDelegateHandle = FCoreTickerType::GetCoreTicker().AddTicker(OnTick);
  189. AkAudioDevice->LoadDelayedObjects();
  190. UE_LOG(LogAkAudio, VeryVerbose, TEXT("FAkAudioModule::StartupModule: Module Initialized."));
  191. OnModuleInitialized.Broadcast();
  192. bModuleInitialized = true;
  193. }
  194. void FAkAudioModule::ShutdownModule()
  195. {
  196. UE_CLOG(AkAudioModuleInstance && AkAudioModuleInstance != this, LogAkAudio, Warning, TEXT("FAkAudioModule::ShutdownModule: Shutting down a different instance (%p) that was initially instantiated (%p)!"), this, AkAudioModuleInstance);
  197. FCoreTickerType::GetCoreTicker().RemoveTicker(TickDelegateHandle);
  198. if (AkAudioDevice)
  199. {
  200. AkAudioDevice->Teardown();
  201. delete AkAudioDevice;
  202. AkAudioDevice = nullptr;
  203. }
  204. if (IWwiseSoundEngineModule::IsAvailable())
  205. {
  206. WwiseUnrealHelper::SetHelperFunctions(nullptr, nullptr, nullptr, nullptr);
  207. }
  208. AkAudioModuleInstance = nullptr;
  209. }
  210. FAkAudioDevice* FAkAudioModule::GetAkAudioDevice() const
  211. {
  212. return AkAudioDevice;
  213. }
  214. void FAkAudioModule::ReloadWwiseAssetData() const
  215. {
  216. SCOPED_AKAUDIO_EVENT(TEXT("ReloadWwiseAssetData"));
  217. if (FAkAudioDevice::IsInitialized())
  218. {
  219. UE_LOG(LogAkAudio, Log, TEXT("FAkAudioModule::ReloadWwiseAssetData : Reloading Wwise asset data."));
  220. if (AkAudioDevice)
  221. {
  222. AkAudioDevice->ClearSoundBanksAndMedia();
  223. }
  224. auto* InitBankLoader = FWwiseInitBankLoader::Get();
  225. if (LIKELY(InitBankLoader))
  226. {
  227. InitBankLoader->LoadInitBank();
  228. }
  229. else
  230. {
  231. UE_LOG(LogAkAudio, Error, TEXT("LoadInitBank: WwiseInitBankLoader is not initialized."));
  232. }
  233. for (TObjectIterator<UAkAudioType> AudioAssetIt; AudioAssetIt; ++AudioAssetIt)
  234. {
  235. AudioAssetIt->LoadData();
  236. }
  237. OnWwiseAssetDataReloaded.Broadcast();
  238. }
  239. else
  240. {
  241. UE_LOG(LogAkAudio, Verbose, TEXT("FAkAudioModule::ReloadWwiseAssetData : Skipping asset data reload because the SoundEngine is not initialized."));
  242. }
  243. }
  244. void FAkAudioModule::UpdateWwiseResourceLoaderSettings()
  245. {
  246. SCOPED_AKAUDIO_EVENT(TEXT("UpdateWwiseResourceLoaderSettings"));
  247. UE_LOG(LogAkAudio, Log, TEXT("FAkAudioModule::UpdateWwiseResourceLoaderSettings : Updating Resource Loader settings."));
  248. auto* ResourceLoader = FWwiseResourceLoader::Get();
  249. if (!ResourceLoader)
  250. {
  251. UE_LOG(LogAkAudio, Error, TEXT("FAkAudioModule::UpdateWwiseResourceLoaderSettings : No Resource Loader!"));
  252. return;
  253. }
  254. auto* ResourceLoaderImpl = ResourceLoader->ResourceLoaderImpl.Get();
  255. if (!ResourceLoaderImpl)
  256. {
  257. UE_LOG(LogAkAudio, Error, TEXT("FAkAudioModule::UpdateWwiseResourceLoaderSettings : No Resource Loader Impl!"));
  258. return;
  259. }
  260. ResourceLoaderImpl->StagePath = WwiseUnrealHelper::GetStagePathImpl();
  261. #if WITH_EDITORONLY_DATA
  262. ResourceLoaderImpl->GeneratedSoundBanksPath = FDirectoryPath{WwiseUnrealHelper::GetSoundBankDirectory()};
  263. #endif
  264. }
  265. #if WITH_EDITORONLY_DATA
  266. void FAkAudioModule::ParseGeneratedSoundBankData()
  267. {
  268. SCOPED_AKAUDIO_EVENT(TEXT("ParseGeneratedSoundBankData"));
  269. if (auto* AkSettings = GetDefault<UAkSettings>())
  270. {
  271. if (!AkSettings->AreSoundBanksGenerated())
  272. {
  273. UE_LOG(LogAkAudio, Warning, TEXT("FAkAudioModule::ParseGeneratedSoundBankData: SoundBanks are not yet generated, nothing to parse.\nCurrent Generated SoundBanks path is: %s"), *WwiseUnrealHelper::GetSoundBankDirectory());
  274. return;
  275. }
  276. }
  277. UE_LOG(LogAkAudio, Log, TEXT("FAkAudioModule::ParseGeneratedSoundBankData : Parsing Wwise project data."));
  278. auto* ProjectDatabase = FWwiseProjectDatabase::Get();
  279. if (UNLIKELY(!ProjectDatabase))
  280. {
  281. UE_LOG(LogAkAudio, Warning, TEXT("FAkAudioModule::ParseGeneratedSoundBankData : Could not get FWwiseProjectDatabase instance. Generated sound data will not be parsed."));
  282. }
  283. else
  284. {
  285. ProjectDatabase->UpdateDataStructure();
  286. }
  287. }
  288. #endif
  289. #undef LOCTEXT_NAMESPACE