// Copyright (c), Firelight Technologies Pty, Ltd. 2012-2025. #pragma once #include "fmod_studio.hpp" #include "fmod.hpp" #include "Runtime/Launch/Resources/Version.h" #include "Engine/Engine.h" #include "FMODStudioModule.h" #define verifyfmod(fn) \ { \ FMOD_RESULT _result = (fn); \ if (_result != FMOD_OK) \ { \ FMODUtils::LogError(_result, #fn); \ } \ } namespace FMODUtils { // Unreal defines 1 unit == 1cm, so convert to metres for Studio automatically #define FMOD_VECTOR_SCALE_DEFAULT 0.01f // Just call into module inline void LogError(FMOD_RESULT result, const char *function) { IFMODStudioModule::Get().LogError(result, function); } inline void Assign(FMOD_VECTOR &Dest, const FVector &Src) { Dest.x = Src.X; Dest.y = Src.Y; Dest.z = Src.Z; } inline FMOD_VECTOR ConvertWorldVector(const FVector &Src) { static FMatrix UE4toFMOD(FVector(0.0f, 0.0f, FMOD_VECTOR_SCALE_DEFAULT), FVector(FMOD_VECTOR_SCALE_DEFAULT, 0.0f, 0.0f), FVector(0.0f, FMOD_VECTOR_SCALE_DEFAULT, 0.0f), FVector::ZeroVector); FMOD_VECTOR Dest; Assign(Dest, UE4toFMOD.TransformPosition(Src)); return Dest; } inline FMOD_VECTOR ConvertUnitVector(const FVector &Src) { static FMatrix UE4toFMOD(FVector(0.0f, 0.0f, 1.0f), FVector(1.0f, 0.0f, 0.0f), FVector(0.0f, 1.0f, 0.0f), FVector::ZeroVector); FMOD_VECTOR Dest; Assign(Dest, UE4toFMOD.TransformVector(Src)); return Dest; } inline void Assign(FMOD_3D_ATTRIBUTES &Dest, const FTransform &Src) { Dest.position = ConvertWorldVector(Src.GetTranslation()); Dest.forward = ConvertUnitVector(Src.GetUnitAxis(EAxis::X)); Dest.up = ConvertUnitVector(Src.GetUnitAxis(EAxis::Z)); } inline float DistanceToUEScale(float FMODDistance) { return FMODDistance / FMOD_VECTOR_SCALE_DEFAULT; } inline bool IsWorldAudible(UWorld *World, bool AllowInEditor) { if (GEngine && IFMODStudioModule::Get().UseSound()) { if (!IsValid(World)) { return true; } if (World->bAllowAudioPlayback && World->GetNetMode() != NM_DedicatedServer) { EWorldType::Type previewEnum; previewEnum = EWorldType::EditorPreview; if (World->IsGameWorld() || World->WorldType == previewEnum || (AllowInEditor && World->WorldType == EWorldType::Editor)) { return true; } } } return false; } inline FMOD::Studio::ID ConvertGuid(const FGuid &UnrealGuid) { // Unreal doesn't follow the usual windows GUID format, instead it parses // them as 4 integers FMOD::Studio::ID StudioGuid; FMemory::Memcpy(&StudioGuid, &UnrealGuid, sizeof(StudioGuid)); Swap(StudioGuid.Data2, StudioGuid.Data3); Swap(StudioGuid.Data4[0], StudioGuid.Data4[3]); Swap(StudioGuid.Data4[1], StudioGuid.Data4[2]); Swap(StudioGuid.Data4[4], StudioGuid.Data4[7]); Swap(StudioGuid.Data4[5], StudioGuid.Data4[6]); return StudioGuid; } inline FGuid ConvertGuid(const FMOD::Studio::ID &StudioGuid) { // Unreal doesn't follow the usual windows GUID format, instead it parses // them as 4 integers FMOD::Studio::ID CopiedGuid; FMemory::Memcpy(&CopiedGuid, &StudioGuid, sizeof(StudioGuid)); Swap(CopiedGuid.Data2, CopiedGuid.Data3); Swap(CopiedGuid.Data4[0], CopiedGuid.Data4[3]); Swap(CopiedGuid.Data4[1], CopiedGuid.Data4[2]); Swap(CopiedGuid.Data4[4], CopiedGuid.Data4[7]); Swap(CopiedGuid.Data4[5], CopiedGuid.Data4[6]); FGuid UnrealGuid; FMemory::Memcpy(&UnrealGuid, &CopiedGuid, sizeof(CopiedGuid)); return UnrealGuid; } template inline FGuid GetID(StudioType *Instance) { FMOD::Studio::ID StudioID = { 0 }; verifyfmod(Instance->getID(&StudioID)); return FMODUtils::ConvertGuid(StudioID); } template inline FString GetPath(StudioType *Instance) { int ActualSize = 128; // Start with expected enough space TArray RawBuffer; FMOD_RESULT Result; do { RawBuffer.SetNum(ActualSize); Result = Instance->getPath(RawBuffer.GetData(), ActualSize, &ActualSize); } while (Result == FMOD_ERR_TRUNCATED); if (Result == FMOD_OK) { return FString(UTF8_TO_TCHAR(RawBuffer.GetData())); } else { return FString(); } } inline FString LookupNameFromGuid(FMOD::Studio::System *StudioSystem, const FMOD::Studio::ID &Guid) { int ActualSize = 128; // Start with expected enough space TArray RawBuffer; FMOD_RESULT Result; do { RawBuffer.SetNum(ActualSize); Result = StudioSystem->lookupPath(&Guid, RawBuffer.GetData(), ActualSize, &ActualSize); } while (Result == FMOD_ERR_TRUNCATED); if (Result == FMOD_OK) { return FString(UTF8_TO_TCHAR(RawBuffer.GetData())); } else { return FString(); } } inline FString LookupNameFromGuid(FMOD::Studio::System *StudioSystem, const FGuid &Guid) { return LookupNameFromGuid(StudioSystem, ConvertGuid(Guid)); } inline FString ParameterTypeToString(FMOD_STUDIO_PARAMETER_TYPE Type) { switch (Type) { case FMOD_STUDIO_PARAMETER_GAME_CONTROLLED: return FString("Game Controlled"); case FMOD_STUDIO_PARAMETER_AUTOMATIC_DISTANCE: return FString("Distance (Auto)"); case FMOD_STUDIO_PARAMETER_AUTOMATIC_EVENT_CONE_ANGLE: return FString("Event Cone Angle (Auto)"); case FMOD_STUDIO_PARAMETER_AUTOMATIC_EVENT_ORIENTATION: return FString("Event Orientation (Auto)"); case FMOD_STUDIO_PARAMETER_AUTOMATIC_DIRECTION: return FString("Direction (Auto)"); case FMOD_STUDIO_PARAMETER_AUTOMATIC_ELEVATION: return FString("Elevation (Auto)"); case FMOD_STUDIO_PARAMETER_AUTOMATIC_LISTENER_ORIENTATION: return FString("Listener Orientation (Auto)"); } return FString(); } }