322 lines
10 KiB
C++
322 lines
10 KiB
C++
/*******************************************************************************
|
|
The content of this file includes portions of the proprietary AUDIOKINETIC Wwise
|
|
Technology released in source code form as part of the game integration package.
|
|
The content of this file may not be used without valid licenses to the
|
|
AUDIOKINETIC Wwise Technology.
|
|
Note that the use of the game engine is subject to the Unreal(R) Engine End User
|
|
License Agreement at https://www.unrealengine.com/en-US/eula/unreal
|
|
|
|
License Usage
|
|
|
|
Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use
|
|
this file in accordance with the end user license agreement provided with the
|
|
software or, alternatively, in accordance with the terms contained
|
|
in a written agreement between you and Audiokinetic Inc.
|
|
Copyright (c) 2023 Audiokinetic Inc.
|
|
*******************************************************************************/
|
|
|
|
#include "AkAudioModule.h"
|
|
#include "AkAudioDevice.h"
|
|
#include "AkAudioStyle.h"
|
|
#include "AkSettings.h"
|
|
#include "AkSettingsPerUser.h"
|
|
#include "AkUnrealHelper.h"
|
|
|
|
#include "Wwise/WwiseResourceLoader.h"
|
|
#include "Wwise/WwiseSoundEngineModule.h"
|
|
#include "WwiseInitBankLoader/WwiseInitBankLoader.h"
|
|
|
|
#include "Misc/ScopedSlowTask.h"
|
|
|
|
#include "UObject/UObjectIterator.h"
|
|
|
|
|
|
#include "Wwise/API/WwiseSoundEngineAPI.h"
|
|
|
|
#if WITH_EDITORONLY_DATA
|
|
#include "Wwise/WwiseFileHandlerModule.h"
|
|
#include "Wwise/WwiseProjectDatabase.h"
|
|
#include "Wwise/WwiseDataStructure.h"
|
|
#include "Wwise/WwiseResourceCooker.h"
|
|
#endif
|
|
|
|
#if WITH_EDITOR
|
|
#include "AssetRegistry/AssetRegistryModule.h"
|
|
#include "HAL/FileManager.h"
|
|
#endif
|
|
#include <Runtime/Core/Public/Async/Async.h>
|
|
|
|
IMPLEMENT_MODULE(FAkAudioModule, AkAudio)
|
|
#define LOCTEXT_NAMESPACE "AkAudio"
|
|
|
|
FAkAudioModule* FAkAudioModule::AkAudioModuleInstance = nullptr;
|
|
FSimpleMulticastDelegate FAkAudioModule::OnModuleInitialized;
|
|
|
|
// AkUnrealHelper overrides
|
|
|
|
namespace AkUnrealHelper
|
|
{
|
|
static FString GetWwisePluginDirectoryImpl()
|
|
{
|
|
return FAkPlatform::GetWwisePluginDirectory();
|
|
}
|
|
|
|
static FString GetWwiseProjectPathImpl()
|
|
{
|
|
FString projectPath;
|
|
|
|
if (auto* settings = GetDefault<UAkSettings>())
|
|
{
|
|
projectPath = settings->WwiseProjectPath.FilePath;
|
|
|
|
if (FPaths::IsRelative(projectPath))
|
|
{
|
|
projectPath = FPaths::ConvertRelativePathToFull(GetProjectDirectory(), projectPath);
|
|
}
|
|
|
|
#if PLATFORM_WINDOWS
|
|
projectPath.ReplaceInline(TEXT("/"), TEXT("\\"));
|
|
#endif
|
|
}
|
|
|
|
return projectPath;
|
|
}
|
|
|
|
static FString GetSoundBankDirectoryImpl()
|
|
{
|
|
const UAkSettingsPerUser* UserSettings = GetDefault<UAkSettingsPerUser>();
|
|
FString SoundBankDirectory;
|
|
if (UserSettings && !UserSettings->GeneratedSoundBanksFolderUserOverride.Path.IsEmpty())
|
|
{
|
|
SoundBankDirectory = FPaths::Combine(GetContentDirectory(), UserSettings->GeneratedSoundBanksFolderUserOverride.Path);
|
|
}
|
|
else if (const UAkSettings* AkSettings = GetDefault<UAkSettings>())
|
|
{
|
|
SoundBankDirectory = FPaths::Combine(GetContentDirectory(), AkSettings->GeneratedSoundBanksFolder.Path);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAkAudio, Warning, TEXT("AkUnrealHelper::GetSoundBankDirectory : Please set the Generated Soundbanks Folder in Wwise settings. Otherwise, sound will not function."));
|
|
return {};
|
|
}
|
|
FPaths::CollapseRelativeDirectories(SoundBankDirectory);
|
|
if(!SoundBankDirectory.EndsWith(TEXT("/")))
|
|
{
|
|
SoundBankDirectory.AppendChar('/');
|
|
}
|
|
|
|
return SoundBankDirectory;
|
|
}
|
|
|
|
static FString GetStagePathImpl()
|
|
{
|
|
const UAkSettings* Settings = GetDefault<UAkSettings>();
|
|
#if WITH_EDITORONLY_DATA
|
|
if (Settings && !Settings->WwiseStagingDirectory.Path.IsEmpty())
|
|
{
|
|
return Settings->WwiseStagingDirectory.Path;
|
|
}
|
|
return TEXT("WwiseAudio");
|
|
#endif
|
|
if (Settings && !Settings->WwiseStagingDirectory.Path.IsEmpty())
|
|
{
|
|
return FPaths::ProjectContentDir() / Settings->WwiseStagingDirectory.Path;
|
|
}
|
|
return FPaths::ProjectContentDir() / TEXT("WwiseAudio");
|
|
}
|
|
}
|
|
|
|
void FAkAudioModule::StartupModule()
|
|
{
|
|
IWwiseSoundEngineModule::ForceLoadModule();
|
|
AkUnrealHelper::SetHelperFunctions(
|
|
AkUnrealHelper::GetWwisePluginDirectoryImpl,
|
|
AkUnrealHelper::GetWwiseProjectPathImpl,
|
|
AkUnrealHelper::GetSoundBankDirectoryImpl,
|
|
AkUnrealHelper::GetStagePathImpl);
|
|
|
|
#if WITH_EDITOR
|
|
// It is not wanted to initialize the SoundEngine while running the GenerateSoundBanks commandlet.
|
|
if (IsRunningCommandlet())
|
|
{
|
|
// We COULD use GetRunningCommandletClass(), but unfortunately it is set to nullptr in OnPostEngineInit.
|
|
// We need to parse the command line.
|
|
FString CmdLine(FCommandLine::Get());
|
|
if (CmdLine.Contains(TEXT("-run=GenerateSoundBanks")))
|
|
{
|
|
UE_LOG(LogAkAudio, Log, TEXT("FAkAudioModule::StartupModule: Detected GenerateSoundBanks commandlet is running. AkAudioModule will not be initialized."));
|
|
return;
|
|
}
|
|
|
|
#if WITH_EDITORONLY_DATA
|
|
if(IWwiseProjectDatabaseModule::IsInACookingCommandlet())
|
|
{
|
|
// Initialize the Rersource Cooker
|
|
IWwiseResourceCookerModule::GetModule();
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
if (AkAudioModuleInstance == this)
|
|
{
|
|
UE_LOG(LogAkAudio, Log, TEXT("FAkAudioModule::StartupModule: AkAudioModuleInstance already exists."));
|
|
return;
|
|
}
|
|
UE_CLOG(AkAudioModuleInstance, LogAkAudio, Warning, TEXT("FAkAudioModule::StartupModule: Updating AkAudioModuleInstance from (%p) to (%p)! Previous Module instance was improperly shut down!"), AkAudioModuleInstance, this);
|
|
|
|
AkAudioModuleInstance = this;
|
|
|
|
FScopedSlowTask SlowTask(0, LOCTEXT("InitWwisePlugin", "Initializing Wwise Plug-in AkAudioModule..."));
|
|
|
|
UpdateWwiseResourceLoaderSettings();
|
|
|
|
#if WITH_EDITORONLY_DATA
|
|
ParseGeneratedSoundBankData();
|
|
FWwiseInitBankLoader::Get()->UpdateInitBankInSettings();
|
|
|
|
// Loading the File Handler Module, in case it loads a different module with UStructs, so it gets packaged (Ex.: Simple External Source Manager)
|
|
IWwiseFileHandlerModule::GetModule();
|
|
#endif
|
|
|
|
AkAudioDevice = new FAkAudioDevice;
|
|
if (!AkAudioDevice)
|
|
{
|
|
UE_LOG(LogAkAudio, Log, TEXT("FAkAudioModule::StartupModule: Couldn't create FAkAudioDevice. AkAudioModule will not be fully initialized."));
|
|
bModuleInitialized = true;
|
|
return;
|
|
}
|
|
|
|
if (!AkAudioDevice->Init())
|
|
{
|
|
UE_LOG(LogAkAudio, Log, TEXT("FAkAudioModule::StartupModule: Couldn't initialize FAkAudioDevice. AkAudioModule will not be fully initialized."));
|
|
bModuleInitialized = true;
|
|
delete AkAudioDevice;
|
|
AkAudioDevice = nullptr;
|
|
return;
|
|
}
|
|
|
|
//Load init bank in Runtime (will be loaded by AudiokineticTools module calling ReloadWwiseAssets when in editor)
|
|
UE_LOG(LogAkAudio, VeryVerbose, TEXT("FAkAudioModule::StartupModule: Loading Init Bank."));
|
|
FWwiseInitBankLoader::Get()->LoadInitBank();
|
|
|
|
OnTick = FTickerDelegate::CreateRaw(AkAudioDevice, &FAkAudioDevice::Update);
|
|
TickDelegateHandle = FCoreTickerType::GetCoreTicker().AddTicker(OnTick);
|
|
|
|
AkAudioDevice->LoadDelayedObjects();
|
|
|
|
UE_LOG(LogAkAudio, VeryVerbose, TEXT("FAkAudioModule::StartupModule: Module Initialized."));
|
|
OnModuleInitialized.Broadcast();
|
|
bModuleInitialized = true;
|
|
}
|
|
|
|
void FAkAudioModule::ShutdownModule()
|
|
{
|
|
UE_CLOG(AkAudioModuleInstance && AkAudioModuleInstance != this, LogAkAudio, Warning, TEXT("FAkAudioModule::ShutdownModule: Shutting down a different instance (%p) that was initially instantiated (%p)!"), this, AkAudioModuleInstance);
|
|
|
|
FCoreTickerType::GetCoreTicker().RemoveTicker(TickDelegateHandle);
|
|
|
|
if (AkAudioDevice)
|
|
{
|
|
AkAudioDevice->Teardown();
|
|
delete AkAudioDevice;
|
|
AkAudioDevice = nullptr;
|
|
}
|
|
|
|
if (IWwiseSoundEngineModule::IsAvailable())
|
|
{
|
|
AkUnrealHelper::SetHelperFunctions(nullptr, nullptr, nullptr, nullptr);
|
|
}
|
|
|
|
AkAudioModuleInstance = nullptr;
|
|
}
|
|
|
|
FAkAudioDevice* FAkAudioModule::GetAkAudioDevice() const
|
|
{
|
|
return AkAudioDevice;
|
|
}
|
|
|
|
void FAkAudioModule::ReloadWwiseAssetData() const
|
|
{
|
|
SCOPED_AKAUDIO_EVENT(TEXT("ReloadWwiseAssetData"));
|
|
if (FAkAudioDevice::IsInitialized())
|
|
{
|
|
UE_LOG(LogAkAudio, Log, TEXT("FAkAudioModule::ReloadWwiseAssetData : Reloading Wwise asset data."));
|
|
if (AkAudioDevice)
|
|
{
|
|
AkAudioDevice->ClearSoundBanksAndMedia();
|
|
}
|
|
|
|
auto* InitBankLoader = FWwiseInitBankLoader::Get();
|
|
if (LIKELY(InitBankLoader))
|
|
{
|
|
InitBankLoader->LoadInitBank();
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAkAudio, Error, TEXT("LoadInitBank: WwiseInitBankLoader is not initialized."));
|
|
}
|
|
|
|
for (TObjectIterator<UAkAudioType> AudioAssetIt; AudioAssetIt; ++AudioAssetIt)
|
|
{
|
|
AudioAssetIt->LoadData();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogAkAudio, Verbose, TEXT("FAkAudioModule::ReloadWwiseAssetData : Skipping asset data reload because the SoundEngine is not initialized."));
|
|
}
|
|
}
|
|
|
|
void FAkAudioModule::UpdateWwiseResourceLoaderSettings()
|
|
{
|
|
SCOPED_AKAUDIO_EVENT(TEXT("UpdateWwiseResourceLoaderSettings"));
|
|
UE_LOG(LogAkAudio, Log, TEXT("FAkAudioModule::UpdateWwiseResourceLoaderSettings : Updating Resource Loader settings."));
|
|
|
|
auto* ResourceLoader = FWwiseResourceLoader::Get();
|
|
if (!ResourceLoader)
|
|
{
|
|
UE_LOG(LogAkAudio, Error, TEXT("FAkAudioModule::UpdateWwiseResourceLoaderSettings : No Resource Loader!"));
|
|
return;
|
|
}
|
|
auto* ResourceLoaderImpl = ResourceLoader->ResourceLoaderImpl.Get();
|
|
if (!ResourceLoaderImpl)
|
|
{
|
|
UE_LOG(LogAkAudio, Error, TEXT("FAkAudioModule::UpdateWwiseResourceLoaderSettings : No Resource Loader Impl!"));
|
|
return;
|
|
}
|
|
|
|
ResourceLoaderImpl->StagePath = AkUnrealHelper::GetStagePathImpl();
|
|
|
|
#if WITH_EDITORONLY_DATA
|
|
ResourceLoaderImpl->GeneratedSoundBanksPath = FDirectoryPath{AkUnrealHelper::GetSoundBankDirectory()};
|
|
#endif
|
|
}
|
|
|
|
#if WITH_EDITORONLY_DATA
|
|
void FAkAudioModule::ParseGeneratedSoundBankData()
|
|
{
|
|
SCOPED_AKAUDIO_EVENT(TEXT("ParseGeneratedSoundBankData"));
|
|
if (auto* AkSettings = GetDefault<UAkSettings>())
|
|
{
|
|
if (!AkSettings->AreSoundBanksGenerated())
|
|
{
|
|
UE_LOG(LogAkAudio, Warning, TEXT("FAkAudioModule::ParseGeneratedSoundBankData: SoundBanks are not yet generated, nothing to parse.\nCurrent Generated SoundBanks path is: %s"), *AkUnrealHelper::GetSoundBankDirectory());
|
|
return;
|
|
}
|
|
}
|
|
UE_LOG(LogAkAudio, Log, TEXT("FAkAudioModule::ParseGeneratedSoundBankData : Parsing Wwise project data."));
|
|
auto* ProjectDatabase = FWwiseProjectDatabase::Get();
|
|
if (UNLIKELY(!ProjectDatabase))
|
|
{
|
|
UE_LOG(LogAkAudio, Warning, TEXT("FAkAudioModule::ParseGeneratedSoundBankData : Could not get FWwiseProjectDatabase instance. Generated sound data will not be parsed."));
|
|
}
|
|
else
|
|
{
|
|
ProjectDatabase->UpdateDataStructure();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#undef LOCTEXT_NAMESPACE
|