From 1e2f102f58cf48bb083e242f0e0449e4cc250456 Mon Sep 17 00:00:00 2001 From: Buzz Burrowes Date: Thu, 18 Mar 2021 18:18:55 -0400 Subject: [PATCH 01/12] Wrap a few more getters for blueprints Wrap a few more fmod api 'getters' for blueprint use. --- .../FMODStudio/Classes/FMODAudioComponent.h | 12 ++++++ .../FMODStudio/Private/FMODAudioComponent.cpp | 39 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/FMODStudio/Source/FMODStudio/Classes/FMODAudioComponent.h b/FMODStudio/Source/FMODStudio/Classes/FMODAudioComponent.h index 51623bc0..ad0db42b 100755 --- a/FMODStudio/Source/FMODStudio/Classes/FMODAudioComponent.h +++ b/FMODStudio/Source/FMODStudio/Classes/FMODAudioComponent.h @@ -206,14 +206,26 @@ class FMODSTUDIO_API UFMODAudioComponent : public USceneComponent UFUNCTION(BlueprintCallable, Category = "Audio|FMOD|Components") void SetVolume(float volume); + /** Gets the volume level. */ + UFUNCTION(BlueprintCallable, Category = "Audio|FMOD|Components") + float GetVolume(); + /** Sets the pitch multiplier. */ UFUNCTION(BlueprintCallable, Category = "Audio|FMOD|Components") void SetPitch(float pitch); + /** Gets the pitch multiplier. */ + UFUNCTION(BlueprintCallable, Category = "Audio|FMOD|Components") + float GetPitch(); + /** Pause/Unpause an audio component. */ UFUNCTION(BlueprintCallable, Category = "Audio|FMOD|Components") void SetPaused(bool paused); + /** Get Pause/Unpause state of an audio component. */ + UFUNCTION(BlueprintCallable, Category = "Audio|FMOD|Components") + bool GetPaused(); + /** Set a parameter of the Event. */ UFUNCTION(BlueprintCallable, Category = "Audio|FMOD|Components") void SetParameter(FName Name, float Value); diff --git a/FMODStudio/Source/FMODStudio/Private/FMODAudioComponent.cpp b/FMODStudio/Source/FMODStudio/Private/FMODAudioComponent.cpp index 5e370643..d402ee5a 100755 --- a/FMODStudio/Source/FMODStudio/Private/FMODAudioComponent.cpp +++ b/FMODStudio/Source/FMODStudio/Private/FMODAudioComponent.cpp @@ -772,6 +772,19 @@ void UFMODAudioComponent::SetVolume(float Volume) } } +float UFMODAudioComponent::GetVolume() +{ + if (!StudioInstance) return 0.0f; + + float volume = 0.0f; + FMOD_RESULT Result = StudioInstance->getVolume(&volume); + if (Result != FMOD_OK) + { + UE_LOG(LogFMOD, Warning, TEXT("Failed to get volume")); + } + return volume; +} + void UFMODAudioComponent::SetPitch(float Pitch) { if (StudioInstance) @@ -784,6 +797,19 @@ void UFMODAudioComponent::SetPitch(float Pitch) } } +float UFMODAudioComponent::GetPitch() +{ + if (!StudioInstance) return 1.0f; + + float pitch = 1.0f; + FMOD_RESULT Result = StudioInstance->getPitch(&pitch); + if (Result != FMOD_OK) + { + UE_LOG(LogFMOD, Warning, TEXT("Failed to get pitch")); + } + return pitch; +} + void UFMODAudioComponent::SetPaused(bool Paused) { if (StudioInstance) @@ -796,6 +822,19 @@ void UFMODAudioComponent::SetPaused(bool Paused) } } +bool UFMODAudioComponent::GetPaused() +{ + if (!StudioInstance) return false; + + bool paused = false; + FMOD_RESULT Result = StudioInstance->getPaused(&paused); + if (Result != FMOD_OK) + { + UE_LOG(LogFMOD, Warning, TEXT("Failed to get paused")); + } + return paused; +} + void UFMODAudioComponent::SetParameter(FName Name, float Value) { if (StudioInstance) From d5891be0e62a9b470120f25b8a48f93d5550f6f4 Mon Sep 17 00:00:00 2001 From: Buzz Burrowes Date: Fri, 19 Mar 2021 09:59:25 -0400 Subject: [PATCH 02/12] Blueprint access to vca's current volume --- .../FMODStudio/Classes/FMODBlueprintStatics.h | 6 +++++ .../Private/FMODBlueprintStatics.cpp | 23 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/FMODStudio/Source/FMODStudio/Classes/FMODBlueprintStatics.h b/FMODStudio/Source/FMODStudio/Classes/FMODBlueprintStatics.h index 89242911..085f9220 100755 --- a/FMODStudio/Source/FMODStudio/Classes/FMODBlueprintStatics.h +++ b/FMODStudio/Source/FMODStudio/Classes/FMODBlueprintStatics.h @@ -178,6 +178,12 @@ class FMODSTUDIO_API UFMODBlueprintStatics : public UBlueprintFunctionLibrary UFUNCTION(BlueprintCallable, Category = "Audio|FMOD|VCA", meta = (UnsafeDuringActorConstruction = "true")) static void VCASetVolume(class UFMODVCA *Vca, float Volume); + /** Get volume on a VCA + * @param Vca - VCA to use + */ + UFUNCTION(BlueprintCallable, Category = "Audio|FMOD|VCA", meta = (UnsafeDuringActorConstruction = "true")) + static float VCAGetVolume(class UFMODVCA *Vca); + /** Set a global parameter from the System. * @param Name - Name of parameter * @param Value - Value of parameter diff --git a/FMODStudio/Source/FMODStudio/Private/FMODBlueprintStatics.cpp b/FMODStudio/Source/FMODStudio/Private/FMODBlueprintStatics.cpp index 781d92d6..87a21eff 100755 --- a/FMODStudio/Source/FMODStudio/Private/FMODBlueprintStatics.cpp +++ b/FMODStudio/Source/FMODStudio/Private/FMODBlueprintStatics.cpp @@ -344,6 +344,29 @@ void UFMODBlueprintStatics::VCASetVolume(class UFMODVCA *Vca, float Volume) } } +float UFMODBlueprintStatics::VCAGetVolume(class UFMODVCA *Vca) +{ + float volume = -1.0f; + + FMOD::Studio::System *StudioSystem = IFMODStudioModule::Get().GetStudioSystem(EFMODSystemContext::Runtime); + if (StudioSystem != nullptr && IsValid(Vca)) + { + FMOD::Studio::ID guid = FMODUtils::ConvertGuid(Vca->AssetGuid); + FMOD::Studio::VCA *vca = nullptr; + FMOD_RESULT result = StudioSystem->getVCAByID(&guid, &vca); + if (result == FMOD_OK && vca != nullptr) + { + vca->getVolume(&volume); + } + } + if (volume < 0.0f) + { + UE_LOG(LogFMOD, Warning, TEXT("Failed to get vca volume")); + volume = 0.0f; + } + return volume; +} + void UFMODBlueprintStatics::SetGlobalParameterByName(FName Name, float Value) { FMOD::Studio::System *StudioSystem = IFMODStudioModule::Get().GetStudioSystem(EFMODSystemContext::Runtime); From 0289a3547cc8eb9749e68f2bdcb4b16a0397932f Mon Sep 17 00:00:00 2001 From: Buzz Burrowes Date: Fri, 19 Mar 2021 10:06:29 -0400 Subject: [PATCH 03/12] Locale related - Return result of SetLocale function back to Blueprint - SetLocale before loading any banks --- FMODStudio/Source/FMODStudio/Classes/FMODBlueprintStatics.h | 2 +- FMODStudio/Source/FMODStudio/Private/FMODBlueprintStatics.cpp | 4 ++-- FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp | 4 ++++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/FMODStudio/Source/FMODStudio/Classes/FMODBlueprintStatics.h b/FMODStudio/Source/FMODStudio/Classes/FMODBlueprintStatics.h index 085f9220..6bc5852c 100755 --- a/FMODStudio/Source/FMODStudio/Classes/FMODBlueprintStatics.h +++ b/FMODStudio/Source/FMODStudio/Classes/FMODBlueprintStatics.h @@ -325,5 +325,5 @@ class FMODSTUDIO_API UFMODBlueprintStatics : public UBlueprintFunctionLibrary /** Set the active locale for subsequent bank loads. */ UFUNCTION(BlueprintCallable, Category = "Audio|FMOD") - static void SetLocale(const FString& Locale); + static bool SetLocale(const FString& Locale); }; diff --git a/FMODStudio/Source/FMODStudio/Private/FMODBlueprintStatics.cpp b/FMODStudio/Source/FMODStudio/Private/FMODBlueprintStatics.cpp index 87a21eff..62c46bcf 100755 --- a/FMODStudio/Source/FMODStudio/Private/FMODBlueprintStatics.cpp +++ b/FMODStudio/Source/FMODStudio/Private/FMODBlueprintStatics.cpp @@ -685,7 +685,7 @@ void UFMODBlueprintStatics::MixerResume() } } -void UFMODBlueprintStatics::SetLocale(const FString& Locale) +bool UFMODBlueprintStatics::SetLocale(const FString& Locale) { - IFMODStudioModule::Get().SetLocale(Locale); + return IFMODStudioModule::Get().SetLocale(Locale); } diff --git a/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp b/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp index 3e67a0eb..afb10c52 100755 --- a/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp +++ b/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp @@ -26,6 +26,7 @@ #include "Runtime/Media/Public/IMediaClockSink.h" #include "Runtime/Media/Public/IMediaModule.h" #include "TimerManager.h" +#include "Internationalization/Culture.h" #include "fmod_studio.hpp" #include "fmod_errors.h" @@ -1266,6 +1267,9 @@ void FFMODStudioModule::LoadBanks(EFMODSystemContext::Type Type) { const UFMODSettings &Settings = *GetDefault(); + FCultureRef culture = FInternationalization::Get().GetCurrentLanguage(); + SetLocale(culture.Get().GetTwoLetterISOLanguageName()); + FailedBankLoads[Type].Reset(); if (Type == EFMODSystemContext::Auditioning || Type == EFMODSystemContext::Editor) { From 1d3f1bb6c53c35ea67570a2eeb442c9ef646e398 Mon Sep 17 00:00:00 2001 From: Buzz Burrowes Date: Fri, 19 Mar 2021 10:11:47 -0400 Subject: [PATCH 04/12] Changes to allow clips to start in the middle --- .../FMODEventControlSectionTemplate.cpp | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/FMODStudio/Source/FMODStudio/Private/Sequencer/FMODEventControlSectionTemplate.cpp b/FMODStudio/Source/FMODStudio/Private/Sequencer/FMODEventControlSectionTemplate.cpp index 2eff969e..b4ae033f 100644 --- a/FMODStudio/Source/FMODStudio/Private/Sequencer/FMODEventControlSectionTemplate.cpp +++ b/FMODStudio/Source/FMODStudio/Private/Sequencer/FMODEventControlSectionTemplate.cpp @@ -56,11 +56,15 @@ struct FFMODEventKeyState : IPersistentEvaluationData FKeyHandle InvalidKeyHandle; }; +static FSharedPersistentDataKey FmodEventStateSharedKey(FMovieSceneSharedDataId::Allocate(), FMovieSceneEvaluationOperand()); +struct EventControlState : IPersistentEvaluationData { bool playing = false; }; + struct FFMODEventControlExecutionToken : IMovieSceneExecutionToken { - FFMODEventControlExecutionToken(EFMODEventControlKey InEventControlKey, FFrameTime InKeyTime) + FFMODEventControlExecutionToken(EFMODEventControlKey InEventControlKey, FFrameTime InKeyTime, bool InStartedOnTime) : EventControlKey(InEventControlKey) , KeyTime(InKeyTime) + , StartedOnTime(InStartedOnTime) { } @@ -70,6 +74,7 @@ struct FFMODEventControlExecutionToken : IMovieSceneExecutionToken { for (TWeakObjectPtr<> &WeakObject : Player.FindBoundObjects(Operand)) { + EventControlState& eventControlSate = PersistentData.GetOrAdd(FmodEventStateSharedKey); UFMODAudioComponent *AudioComponent = Cast(WeakObject.Get()); if (!AudioComponent) @@ -92,10 +97,19 @@ struct FFMODEventControlExecutionToken : IMovieSceneExecutionToken EFMODSystemContext::Type SystemContext = (GWorld && GWorld->WorldType == EWorldType::Editor) ? EFMODSystemContext::Editor : EFMODSystemContext::Runtime; AudioComponent->PlayInternal(SystemContext); + AudioComponent->SetPitch(Player.GetPlaybackSpeed()); + if (!StartedOnTime) + { + FFrameTime currentPos = Context.GetOffsetTime(-KeyTime); + int32 timelineMs = (int32)(Context.GetFrameRate().AsSeconds(currentPos) * 1000.0); + AudioComponent->SetTimelinePosition(timelineMs); + } + eventControlSate.playing = true; } else if (EventControlKey == EFMODEventControlKey::Stop) { AudioComponent->Stop(); + eventControlSate.playing = false; } } } @@ -103,6 +117,7 @@ struct FFMODEventControlExecutionToken : IMovieSceneExecutionToken EFMODEventControlKey EventControlKey; FFrameTime KeyTime; + bool StartedOnTime; }; FFMODEventControlSectionTemplate::FFMODEventControlSectionTemplate(const UFMODEventControlSection &Section) @@ -118,10 +133,11 @@ void FFMODEventControlSectionTemplate::Evaluate(const FMovieSceneEvaluationOpera if (!bPlaying) { - ExecutionTokens.Add(FFMODEventControlExecutionToken(EFMODEventControlKey::Stop, FFrameTime(0))); + ExecutionTokens.Add(FFMODEventControlExecutionToken(EFMODEventControlKey::Stop, FFrameTime(0), true)); } else { + const EventControlState* eventControlSate = PersistentData.Find(FmodEventStateSharedKey); TRange PlaybackRange = Context.GetFrameNumberRange(); TMovieSceneChannelData ChannelData = ControlKeys.GetData(); @@ -130,9 +146,10 @@ void FFMODEventControlSectionTemplate::Evaluate(const FMovieSceneEvaluationOpera TArrayView Values = ChannelData.GetValues(); const int32 LastKeyIndex = Algo::UpperBound(Times, PlaybackRange.GetUpperBoundValue()) - 1; - if (LastKeyIndex >= 0 && PlaybackRange.Contains(Times[LastKeyIndex])) + bool startedOnTime = LastKeyIndex >= 0 && PlaybackRange.Contains(Times[LastKeyIndex]); + if (startedOnTime || ((EFMODEventControlKey)Values[LastKeyIndex] == EFMODEventControlKey::Play && eventControlSate && !eventControlSate->playing)) { - FFMODEventControlExecutionToken NewToken((EFMODEventControlKey)Values[LastKeyIndex], Times[LastKeyIndex]); + FFMODEventControlExecutionToken NewToken((EFMODEventControlKey)Values[LastKeyIndex], Times[LastKeyIndex], startedOnTime); ExecutionTokens.Add(MoveTemp(NewToken)); } } From 53b9292ce66d0f4fbedfb57c4df738a507a6693a Mon Sep 17 00:00:00 2001 From: Buzz Burrowes Date: Fri, 19 Mar 2021 10:28:29 -0400 Subject: [PATCH 05/12] Update FMODBankUpdateNotifier.cpp --- .../Source/FMODStudioEditor/Private/FMODBankUpdateNotifier.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FMODStudio/Source/FMODStudioEditor/Private/FMODBankUpdateNotifier.cpp b/FMODStudio/Source/FMODStudioEditor/Private/FMODBankUpdateNotifier.cpp index ce5afab0..7623fcd8 100644 --- a/FMODStudio/Source/FMODStudioEditor/Private/FMODBankUpdateNotifier.cpp +++ b/FMODStudio/Source/FMODStudioEditor/Private/FMODBankUpdateNotifier.cpp @@ -63,7 +63,7 @@ void FFMODBankUpdateNotifier::Refresh() { if (!FilePath.IsEmpty()) { - const FDateTime NewFileTime = IFileManager::Get().GetTimeStamp(*FilePath); + const FDateTime NewFileTime = MostRecentFileTime(); if (NewFileTime != FileTime) { const UFMODSettings &Settings = *GetDefault(); From ed36a2aaf51a1e00e163f817f9c4d3cbab474273 Mon Sep 17 00:00:00 2001 From: Buzz Burrowes Date: Fri, 19 Mar 2021 10:39:24 -0400 Subject: [PATCH 06/12] Add ability to make some per-platform settings --- .../Source/FMODStudio/Classes/FMODSettings.h | 76 ++++++++++++------- .../FMODStudio/Private/FMODSettings.cpp | 4 - .../FMODStudio/Private/FMODStudioModule.cpp | 21 +++-- 3 files changed, 62 insertions(+), 39 deletions(-) diff --git a/FMODStudio/Source/FMODStudio/Classes/FMODSettings.h b/FMODStudio/Source/FMODStudio/Classes/FMODSettings.h index 1c7732cd..120d9444 100755 --- a/FMODStudio/Source/FMODStudio/Classes/FMODSettings.h +++ b/FMODStudio/Source/FMODStudio/Classes/FMODSettings.h @@ -94,6 +94,42 @@ struct FFMODProjectLocale bool bDefault; }; +USTRUCT() +struct FFMODPerPlatformConfig +{ + GENERATED_USTRUCT_BODY() + /** + * Sample rate to use, or 0 to match system rate. + */ + + UPROPERTY(config, EditAnywhere, Category = InitSettings) + int32 SampleRate = 48000; + + /** Project Output Format, should match the mode set up for the Studio project. */ + UPROPERTY(config, EditAnywhere, Category = Basic) + TEnumAsByte OutputFormat = EFMODSpeakerMode::Surround_5_1; + + /** + * Whether to match hardware sample rate where reasonable (44.1kHz to 48kHz). + */ + UPROPERTY(config, EditAnywhere, Category = InitSettings) + bool bMatchHardwareSampleRate = true; + + /** + * DSP mixer buffer length (eg. 512, 1024) or 0 for system default. + * When changing the Buffer Length, Buffer Count also needs to be set. + */ + UPROPERTY(config, EditAnywhere, Category = InitSettings) + int32 DSPBufferLength = 0; + + /** + * DSP mixer buffer count (eg. 2, 4) or 0 for system default. + * When changing the Buffer Count, Buffer Length also needs to be set. + */ + UPROPERTY(config, EditAnywhere, Category = InitSettings) + int32 DSPBufferCount = 0; +}; + UCLASS(config = Engine, defaultconfig) class FMODSTUDIO_API UFMODSettings : public UObject { @@ -130,10 +166,6 @@ class FMODSTUDIO_API UFMODSettings : public UObject UPROPERTY(config, EditAnywhere, Category = Basic, meta = (RelativeToGameContentDir)) FDirectoryPath BankOutputDirectory; - /** Project Output Format, should match the mode set up for the Studio project. */ - UPROPERTY(config, EditAnywhere, Category = Basic) - TEnumAsByte OutputFormat; - /** * Locales for localized banks. These should match the project locales configured in the FMOD Studio project. */ @@ -152,18 +184,6 @@ class FMODSTUDIO_API UFMODSettings : public UObject UPROPERTY(config, EditAnywhere, Category = InitSettings) float Vol0VirtualLevel; - /** - * Sample rate to use, or 0 to match system rate. - */ - UPROPERTY(config, EditAnywhere, Category = InitSettings) - int32 SampleRate; - - /** - * Whether to match hardware sample rate where reasonable (44.1kHz to 48kHz). - */ - UPROPERTY(config, EditAnywhere, Category = InitSettings) - bool bMatchHardwareSampleRate; - /** * Number of actual software voices that can be used at once. */ @@ -176,19 +196,17 @@ class FMODSTUDIO_API UFMODSettings : public UObject UPROPERTY(config, EditAnywhere, Category = InitSettings) int32 TotalChannelCount; - /** - * DSP mixer buffer length (eg. 512, 1024) or 0 for system default. - * When changing the Buffer Length, Buffer Count also needs to be set. - */ - UPROPERTY(config, EditAnywhere, Category = InitSettings) - int32 DSPBufferLength; - - /** - * DSP mixer buffer count (eg. 2, 4) or 0 for system default. - * When changing the Buffer Count, Buffer Length also needs to be set. - */ - UPROPERTY(config, EditAnywhere, Category = InitSettings) - int32 DSPBufferCount; + UPROPERTY(config, EditAnywhere, Category = PerPlatformSettings) + FFMODPerPlatformConfig DefaultPlatformSettings; + + UPROPERTY(config, EditAnywhere, Category = PerPlatformSettings) + FFMODPerPlatformConfig XB1PlatformSettings; + + UPROPERTY(config, EditAnywhere, Category = PerPlatformSettings) + FFMODPerPlatformConfig PS4PlatformSettings; + + UPROPERTY(config, EditAnywhere, Category = PerPlatformSettings) + FFMODPerPlatformConfig SwitchPlatformSettings; /** * File buffer size in bytes (2048 by default). diff --git a/FMODStudio/Source/FMODStudio/Private/FMODSettings.cpp b/FMODStudio/Source/FMODStudio/Private/FMODSettings.cpp index 4f92d927..c65197a6 100755 --- a/FMODStudio/Source/FMODStudio/Private/FMODSettings.cpp +++ b/FMODStudio/Source/FMODStudio/Private/FMODSettings.cpp @@ -19,7 +19,6 @@ UFMODSettings::UFMODSettings(const FObjectInitializer &ObjectInitializer) { MasterBankName = TEXT("Master"); BankOutputDirectory.Path = TEXT("FMOD"); - OutputFormat = EFMODSpeakerMode::Surround_5_1; ContentBrowserPrefix = TEXT("/Game/FMOD/"); bLoadAllBanks = true; bLoadAllSampleData = false; @@ -28,14 +27,11 @@ UFMODSettings::UFMODSettings(const FObjectInitializer &ObjectInitializer) Vol0VirtualLevel = 0.0001f; RealChannelCount = 64; TotalChannelCount = 512; - DSPBufferLength = 0; - DSPBufferCount = 0; FileBufferSize = 2048; StudioUpdatePeriod = 0; LiveUpdatePort = 9264; EditorLiveUpdatePort = 9265; ReloadBanksDelay = 5; - bMatchHardwareSampleRate = true; bLockAllBuses = false; } diff --git a/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp b/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp index afb10c52..7a0761ca 100755 --- a/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp +++ b/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp @@ -541,7 +541,6 @@ void FFMODStudioModule::CreateStudioSystem(EFMODSystemContext::Type Type) const UFMODSettings &Settings = *GetDefault(); bLoadAllSampleData = Settings.bLoadAllSampleData; - FMOD_SPEAKERMODE OutputMode = ConvertSpeakerMode(Settings.OutputFormat); FMOD_STUDIO_INITFLAGS StudioInitFlags = FMOD_STUDIO_INIT_NORMAL; FMOD_INITFLAGS InitFlags = FMOD_INIT_NORMAL; @@ -592,8 +591,18 @@ void FFMODStudioModule::CreateStudioSystem(EFMODSystemContext::Type Type) InitData = (void *)WavWriterDestUTF8.Get(); } - int SampleRate = Settings.SampleRate; - if (Settings.bMatchHardwareSampleRate) +#if PLATFORM_PS4 + const FFMODPerPlatformConfig& platformConfig = Settings.PS4PlatformSettings; +#elif PLATFORM_XBOXONE + const FFMODPerPlatformConfig& platformConfig = Settings.XB1PlatformSettings; +#elif PLATFORM_SWITCH + const FFMODPerPlatformConfig& platformConfig = Settings.SwitchPlatformSettings; +#else + const FFMODPerPlatformConfig& platformConfig = Settings.DefaultPlatformSettings; +#endif + + int SampleRate = platformConfig.SampleRate; + if (platformConfig.bMatchHardwareSampleRate) { int DefaultSampleRate = 0; verifyfmod(lowLevelSystem->getSoftwareFormat(&DefaultSampleRate, 0, 0)); @@ -608,13 +617,13 @@ void FFMODStudioModule::CreateStudioSystem(EFMODSystemContext::Type Type) } } - verifyfmod(lowLevelSystem->setSoftwareFormat(SampleRate, OutputMode, 0)); + verifyfmod(lowLevelSystem->setSoftwareFormat(SampleRate, ConvertSpeakerMode(platformConfig.OutputFormat), 0)); verifyfmod(lowLevelSystem->setSoftwareChannels(Settings.RealChannelCount)); AttachFMODFileSystem(lowLevelSystem, Settings.FileBufferSize); - if (Settings.DSPBufferLength > 0 && Settings.DSPBufferCount > 0) + if (platformConfig.DSPBufferLength > 0 && platformConfig.DSPBufferCount > 0) { - verifyfmod(lowLevelSystem->setDSPBufferSize(Settings.DSPBufferLength, Settings.DSPBufferCount)); + verifyfmod(lowLevelSystem->setDSPBufferSize(platformConfig.DSPBufferLength, platformConfig.DSPBufferCount)); } FMOD_ADVANCEDSETTINGS advSettings = { 0 }; From 5912384ab7f1175d71ca9f870c9330dec03f40e2 Mon Sep 17 00:00:00 2001 From: Buzz Burrowes Date: Fri, 19 Mar 2021 10:42:21 -0400 Subject: [PATCH 07/12] Fix bug with locking all buses upon bank load --- FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp b/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp index 7a0761ca..c7c3d491 100755 --- a/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp +++ b/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp @@ -1364,6 +1364,10 @@ void FFMODStudioModule::LoadBanks(EFMODSystemContext::Type Type) // Optionally lock all buses to make sure they are created if (MasterBank && Settings.bLockAllBuses) { + // The attempt to lock all buses will fail if the bank load + // hasn't been processed, so wait... + StudioSystem[Type]->flushCommands(); + UE_LOG(LogFMOD, Verbose, TEXT("Locking all buses")); int BusCount = 0; verifyfmod(MasterBank->getBusCount(&BusCount)); From 8274fa8d3d102a40dcd1e3b7b6ba6a56444fac54 Mon Sep 17 00:00:00 2001 From: Buzz Burrowes Date: Fri, 19 Mar 2021 10:44:21 -0400 Subject: [PATCH 08/12] Added a GetAssetPath utility function --- FMODStudio/Source/FMODStudio/Public/FMODUtils.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/FMODStudio/Source/FMODStudio/Public/FMODUtils.h b/FMODStudio/Source/FMODStudio/Public/FMODUtils.h index f5035107..42aa4b10 100755 --- a/FMODStudio/Source/FMODStudio/Public/FMODUtils.h +++ b/FMODStudio/Source/FMODStudio/Public/FMODUtils.h @@ -9,6 +9,7 @@ #include "Engine/Engine.h" #include "FMODStudioModule.h" +#include "FMODAsset.h" #define verifyfmod(fn) \ { \ @@ -177,6 +178,12 @@ inline FString LookupNameFromGuid(FMOD::Studio::System *StudioSystem, const FGui return LookupNameFromGuid(StudioSystem, ConvertGuid(Guid)); } +inline FString GetAssetPath(UFMODAsset* asset) +{ + FMOD::Studio::System *system = IFMODStudioModule::Get().GetStudioSystem(EFMODSystemContext::Runtime); + return LookupNameFromGuid(system, asset->AssetGuid); +} + inline FString ParameterTypeToString(FMOD_STUDIO_PARAMETER_TYPE Type) { switch (Type) From e5beee12a3de207599cae21bcb845049e7f22a4e Mon Sep 17 00:00:00 2001 From: Buzz Burrowes Date: Fri, 19 Mar 2021 10:55:36 -0400 Subject: [PATCH 09/12] Add support for delegates that are notified on various state changes --- .../FMODStudio/Private/FMODStudioModule.cpp | 120 ++++++++++++++++++ .../FMODStudio/Public/FMODStudioModule.h | 33 +++++ 2 files changed, 153 insertions(+) diff --git a/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp b/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp index c7c3d491..fcb010a7 100755 --- a/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp +++ b/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp @@ -246,6 +246,19 @@ class FFMODStudioModule : public IFMODStudioModule virtual bool SetLocale(const FString& Locale) override; + virtual FDelegateHandle AddStudioInstanceWatcher(FStudioInstanceWatcherDelegate callback) override; + virtual void RemoveStudioInstanceWatcher(FDelegateHandle callback) override; + virtual FDelegateHandle AddStudioBankLoadWatcher(FStudioBankLoadWatcherDelegate callback) override; + virtual void RemoveStudioBankLoadWatcher(FDelegateHandle callback) override; + virtual FDelegateHandle AddMixerPreSuspendWatcher(FMixerPreSuspendWatcherDelegate callback) override; + virtual void RemoveMixerPreSuspendWatcher(FDelegateHandle callback) override; + virtual FDelegateHandle AddMixerPostSuspendWatcher(FMixerPostSuspendWatcherDelegate callback) override; + virtual void RemoveMixerPostSuspendWatcher(FDelegateHandle callback) override; + virtual FDelegateHandle AddMixerPreResumeWatcher(FMixerPreResumeWatcherDelegate callback) override; + virtual void RemoveMixerPreResumeWatcher(FDelegateHandle callback) override; + virtual FDelegateHandle AddMixerPostResumeWatcher(FMixerPostResumeWatcherDelegate callback) override; + virtual void RemoveMixerPostResumeWatcherDelegate(FDelegateHandle callback) override; + void ResetInterpolation(); #if PLATFORM_IOS @@ -315,6 +328,16 @@ class FFMODStudioModule : public IFMODStudioModule void *MemPool; bool bLoadAllSampleData; + + /** Delegates that want to be informed when studio instances are created or destroyed */ + FStudioInstanceWatchers instanceWatchers; + /** Delegates that want to be informed when banks are loaded and unloaded */ + FStudioBankLoadWatchers bankLoadWatchers; + /** Delegates that want to be informed when suspending or resuming */ + FMixerPreSuspendWatchers preSuspendWatchers; + FMixerPostSuspendWatchers postSuspendWatchers; + FMixerPreResumeWatchers preResumeWatchers; + FMixerPostResumeWatchers postResumeWatchers; }; IMPLEMENT_MODULE(FFMODStudioModule, FMODStudio) @@ -694,12 +717,16 @@ void FFMODStudioModule::CreateStudioSystem(EFMODSystemContext::Type Type) MediaModule->GetClock().AddSink(ClockSinks[Type].ToSharedRef()); } + + instanceWatchers.Broadcast(Type, StudioSystem[Type]); } void FFMODStudioModule::DestroyStudioSystem(EFMODSystemContext::Type Type) { UE_LOG(LogFMOD, Verbose, TEXT("DestroyStudioSystem for context %s"), FMODSystemContextNames[Type]); + instanceWatchers.Broadcast(Type, nullptr); + if (ClockSinks[Type].IsValid()) { // Calling through the shared ptr enforces thread safety with the media clock @@ -1179,8 +1206,13 @@ void FFMODStudioModule::SetSystemPaused(bool paused) // Resume mixer before making calls for Android in particular if (!paused) { + preResumeWatchers.Broadcast(); LowLevelSystem->mixerResume(); } + else + { + preSuspendWatchers.Broadcast(); + } FMOD::ChannelGroup *MasterChannelGroup = nullptr; verifyfmod(LowLevelSystem->getMasterChannelGroup(&MasterChannelGroup)); @@ -1189,6 +1221,11 @@ void FFMODStudioModule::SetSystemPaused(bool paused) if (paused) { LowLevelSystem->mixerSuspend(); + postSuspendWatchers.Broadcast(); + } + else + { + postResumeWatchers.Broadcast(); } } @@ -1272,6 +1309,87 @@ bool FFMODStudioModule::SetLocale(const FString& LocaleName) return false; } +FDelegateHandle FFMODStudioModule::AddStudioInstanceWatcher(FStudioInstanceWatcherDelegate callback) +{ + // let new watcher know about the current state... + for (int i = 0; i < EFMODSystemContext::Max; ++i) + callback.ExecuteIfBound((EFMODSystemContext::Type)i, StudioSystem[i]); + return instanceWatchers.Add(callback); +} + +FDelegateHandle FFMODStudioModule::AddStudioBankLoadWatcher(FStudioBankLoadWatcherDelegate callback) +{ + // Tell the caller about banks that have already been loaded... + for (int t = EFMODSystemContext::Auditioning; t < EFMODSystemContext::Max; ++t) + { + FMOD::Studio::System* system = GetStudioSystem((EFMODSystemContext::Type)t); + if (!system) continue; + + int numBanks; + verifyfmod(system->getBankCount(&numBanks)); + if (numBanks == 0) continue; + + FMOD::Studio::Bank** banks = new FMOD::Studio::Bank*[numBanks]; + verifyfmod(system->getBankList(banks, numBanks, &numBanks)); + for (int i = 0; i < numBanks; ++i) + { + callback.ExecuteIfBound((EFMODSystemContext::Type)t, system, banks[i]); + } + } + return bankLoadWatchers.Add(callback); +} + +FDelegateHandle FFMODStudioModule::AddMixerPreSuspendWatcher(FMixerPreSuspendWatcherDelegate callback) +{ + return preSuspendWatchers.Add(callback); +} + +FDelegateHandle FFMODStudioModule::AddMixerPostSuspendWatcher(FMixerPostSuspendWatcherDelegate callback) +{ + return postSuspendWatchers.Add(callback); +} + +FDelegateHandle FFMODStudioModule::AddMixerPreResumeWatcher(FMixerPreResumeWatcherDelegate callback) +{ + return preResumeWatchers.Add(callback); +} + +FDelegateHandle FFMODStudioModule::AddMixerPostResumeWatcher(FMixerPostResumeWatcherDelegate callback) +{ + return postResumeWatchers.Add(callback); +} + +void FFMODStudioModule::RemoveStudioInstanceWatcher(FDelegateHandle callback) +{ + instanceWatchers.Remove(callback); +} + + +void FFMODStudioModule::RemoveStudioBankLoadWatcher(FDelegateHandle callback) +{ + bankLoadWatchers.Remove(callback); +} + +void FFMODStudioModule::RemoveMixerPreSuspendWatcher(FDelegateHandle callback) +{ + preSuspendWatchers.Remove(callback); +} + +void FFMODStudioModule::RemoveMixerPostSuspendWatcher(FDelegateHandle callback) +{ + postSuspendWatchers.Remove(callback); +} + +void FFMODStudioModule::RemoveMixerPreResumeWatcher(FDelegateHandle callback) +{ + preResumeWatchers.Remove(callback); +} + +void FFMODStudioModule::RemoveMixerPostResumeWatcherDelegate(FDelegateHandle callback) +{ + postResumeWatchers.Remove(callback); +} + void FFMODStudioModule::LoadBanks(EFMODSystemContext::Type Type) { const UFMODSettings &Settings = *GetDefault(); @@ -1403,6 +1521,8 @@ void FFMODStudioModule::LoadBanks(EFMODSystemContext::Type Type) { verifyfmod(Entry.Bank->loadSampleData()); } + + bankLoadWatchers.Broadcast(Type, StudioSystem[Type], Entry.Bank); } if (Entry.Bank == nullptr || Entry.Result != FMOD_OK) { diff --git a/FMODStudio/Source/FMODStudio/Public/FMODStudioModule.h b/FMODStudio/Source/FMODStudio/Public/FMODStudioModule.h index 2ff9a5d9..aebb2bf8 100755 --- a/FMODStudio/Source/FMODStudio/Public/FMODStudioModule.h +++ b/FMODStudio/Source/FMODStudio/Public/FMODStudioModule.h @@ -11,6 +11,7 @@ namespace Studio class System; class EventDescription; class EventInstance; +class Bank; } } @@ -41,6 +42,20 @@ enum Type }; } +DECLARE_MULTICAST_DELEGATE_TwoParams(FStudioInstanceWatchers, EFMODSystemContext::Type, FMOD::Studio::System*) +typedef FStudioInstanceWatchers::FDelegate FStudioInstanceWatcherDelegate; +DECLARE_MULTICAST_DELEGATE_ThreeParams(FStudioBankLoadWatchers, EFMODSystemContext::Type, FMOD::Studio::System*, FMOD::Studio::Bank*) +typedef FStudioBankLoadWatchers::FDelegate FStudioBankLoadWatcherDelegate; + +DECLARE_MULTICAST_DELEGATE(FMixerPreSuspendWatchers) +typedef FMixerPreSuspendWatchers::FDelegate FMixerPreSuspendWatcherDelegate; +DECLARE_MULTICAST_DELEGATE(FMixerPostSuspendWatchers) +typedef FMixerPostSuspendWatchers::FDelegate FMixerPostSuspendWatcherDelegate; +DECLARE_MULTICAST_DELEGATE(FMixerPreResumeWatchers) +typedef FMixerPreResumeWatchers::FDelegate FMixerPreResumeWatcherDelegate; +DECLARE_MULTICAST_DELEGATE(FMixerPostResumeWatchers) +typedef FMixerPostResumeWatchers::FDelegate FMixerPostResumeWatcherDelegate; + /** * The public interface to this module */ @@ -167,4 +182,22 @@ class IFMODStudioModule : public IModuleInterface /** Called by the editor module when banks have been modified on disk */ virtual void ReloadBanks() = 0; #endif + + /** Support for delegates to be called when studio instances are created and destroyed */ + virtual FDelegateHandle AddStudioInstanceWatcher(FStudioInstanceWatcherDelegate callback) = 0; + virtual void RemoveStudioInstanceWatcher(FDelegateHandle callback) = 0; + + /** Support for delegates to be called when banks are loaded and unloaded */ + virtual FDelegateHandle AddStudioBankLoadWatcher(FStudioBankLoadWatcherDelegate callback) = 0; + virtual void RemoveStudioBankLoadWatcher(FDelegateHandle callback) = 0; + + /** Support for delegates to be called when platform suspend/resume callbacks will be handled */ + virtual FDelegateHandle AddMixerPreSuspendWatcher(FMixerPreSuspendWatcherDelegate callback) = 0; + virtual void RemoveMixerPreSuspendWatcher(FDelegateHandle callback) = 0; + virtual FDelegateHandle AddMixerPostSuspendWatcher(FMixerPostSuspendWatcherDelegate callback) = 0; + virtual void RemoveMixerPostSuspendWatcher(FDelegateHandle callback) = 0; + virtual FDelegateHandle AddMixerPreResumeWatcher(FMixerPreResumeWatcherDelegate callback) = 0; + virtual void RemoveMixerPreResumeWatcher(FDelegateHandle callback) = 0; + virtual FDelegateHandle AddMixerPostResumeWatcher(FMixerPostResumeWatcherDelegate callback) = 0; + virtual void RemoveMixerPostResumeWatcherDelegate(FDelegateHandle callback) = 0; }; From b35fb45264b0688db8b4ab181b70c087f0c066a4 Mon Sep 17 00:00:00 2001 From: Buzz Burrowes Date: Fri, 19 Mar 2021 10:59:55 -0400 Subject: [PATCH 10/12] Crash Bug Fix fix crash where fmod module tries to access the media module at shutdown time --- .../Source/FMODStudio/Private/FMODStudioModule.cpp | 11 +++++++---- .../Source/FMODStudio/Public/FMODStudioModule.h | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp b/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp index fcb010a7..a38981ec 100755 --- a/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp +++ b/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp @@ -732,11 +732,13 @@ void FFMODStudioModule::DestroyStudioSystem(EFMODSystemContext::Type Type) // Calling through the shared ptr enforces thread safety with the media clock ClockSinks[Type]->OnDestroyStudioSystem(); - IMediaModule *MediaModule = FModuleManager::LoadModulePtr("Media"); - - if (MediaModule != nullptr) + if (!bIsShuttingDown) { - MediaModule->GetClock().RemoveSink(ClockSinks[Type].ToSharedRef()); + IMediaModule *MediaModule = FModuleManager::LoadModulePtr("Media"); + if (MediaModule != nullptr) + { + MediaModule->GetClock().RemoveSink(ClockSinks[Type].ToSharedRef()); + } } ClockSinks[Type].Reset(); @@ -1236,6 +1238,7 @@ void FFMODStudioModule::SetSystemPaused(bool paused) void FFMODStudioModule::ShutdownModule() { UE_LOG(LogFMOD, Verbose, TEXT("FFMODStudioModule shutdown")); + bIsShuttingDown = true; DestroyStudioSystem(EFMODSystemContext::Auditioning); DestroyStudioSystem(EFMODSystemContext::Runtime); diff --git a/FMODStudio/Source/FMODStudio/Public/FMODStudioModule.h b/FMODStudio/Source/FMODStudio/Public/FMODStudioModule.h index aebb2bf8..e391e07d 100755 --- a/FMODStudio/Source/FMODStudio/Public/FMODStudioModule.h +++ b/FMODStudio/Source/FMODStudio/Public/FMODStudioModule.h @@ -200,4 +200,6 @@ class IFMODStudioModule : public IModuleInterface virtual void RemoveMixerPreResumeWatcher(FDelegateHandle callback) = 0; virtual FDelegateHandle AddMixerPostResumeWatcher(FMixerPostResumeWatcherDelegate callback) = 0; virtual void RemoveMixerPostResumeWatcherDelegate(FDelegateHandle callback) = 0; + + bool bIsShuttingDown = false; }; From 4600552bd789ee3e013ff7e7c17563d7ade89cf4 Mon Sep 17 00:00:00 2001 From: Buzz Burrowes Date: Fri, 19 Mar 2021 11:04:15 -0400 Subject: [PATCH 11/12] Function required to solve race condition bug --- FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp | 8 ++++++++ FMODStudio/Source/FMODStudio/Public/FMODStudioModule.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp b/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp index a38981ec..f1b64f80 100755 --- a/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp +++ b/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp @@ -195,6 +195,7 @@ class FFMODStudioModule : public IFMODStudioModule void CreateStudioSystem(EFMODSystemContext::Type Type); void DestroyStudioSystem(EFMODSystemContext::Type Type); + virtual void DestroyStudioSystems() override; bool Tick(float DeltaTime); @@ -802,6 +803,13 @@ void FFMODStudioModule::DestroyStudioSystem(EFMODSystemContext::Type Type) } } +void FFMODStudioModule::DestroyStudioSystems() +{ + DestroyStudioSystem(EFMODSystemContext::Auditioning); + DestroyStudioSystem(EFMODSystemContext::Runtime); + DestroyStudioSystem(EFMODSystemContext::Editor); +} + bool FFMODStudioModule::Tick(float DeltaTime) { if (ClockSinks[EFMODSystemContext::Auditioning].IsValid()) diff --git a/FMODStudio/Source/FMODStudio/Public/FMODStudioModule.h b/FMODStudio/Source/FMODStudio/Public/FMODStudioModule.h index e391e07d..f4879bc8 100755 --- a/FMODStudio/Source/FMODStudio/Public/FMODStudioModule.h +++ b/FMODStudio/Source/FMODStudio/Public/FMODStudioModule.h @@ -202,4 +202,6 @@ class IFMODStudioModule : public IModuleInterface virtual void RemoveMixerPostResumeWatcherDelegate(FDelegateHandle callback) = 0; bool bIsShuttingDown = false; + + virtual void DestroyStudioSystems() = 0; }; From de9de6f8dc51b53294baedbccbb22df536503b94 Mon Sep 17 00:00:00 2001 From: Buzz Burrowes Date: Fri, 19 Mar 2021 11:07:46 -0400 Subject: [PATCH 12/12] Add a few more project settings for some configurable fmod settings --- .../Source/FMODStudio/Classes/FMODSettings.h | 18 ++++++++++++++++++ .../Source/FMODStudio/Private/FMODSettings.cpp | 3 +++ .../FMODStudio/Private/FMODStudioModule.cpp | 2 ++ 3 files changed, 23 insertions(+) diff --git a/FMODStudio/Source/FMODStudio/Classes/FMODSettings.h b/FMODStudio/Source/FMODStudio/Classes/FMODSettings.h index 120d9444..c143f138 100755 --- a/FMODStudio/Source/FMODStudio/Classes/FMODSettings.h +++ b/FMODStudio/Source/FMODStudio/Classes/FMODSettings.h @@ -184,6 +184,24 @@ class FMODSTUDIO_API UFMODSettings : public UObject UPROPERTY(config, EditAnywhere, Category = InitSettings) float Vol0VirtualLevel; + /** + * Scaling factor for doppler shift. Default = 1.0. + */ + UPROPERTY(config, EditAnywhere, Category = InitSettings) + float DopplerScale; + + /** + * Relative distance factor to FMOD's units. Default = 1.0. (1.0 = 1 metre). + */ + UPROPERTY(config, EditAnywhere, Category = InitSettings) + float DistanceFactor; + + /** + * Scaling factor for 3D sound rolloff or attenuation for FMOD_3D_INVERSEROLLOFF based sounds only (which is the default type). Default = 1.0. + */ + UPROPERTY(config, EditAnywhere, Category = InitSettings) + float RolloffScale; + /** * Number of actual software voices that can be used at once. */ diff --git a/FMODStudio/Source/FMODStudio/Private/FMODSettings.cpp b/FMODStudio/Source/FMODStudio/Private/FMODSettings.cpp index c65197a6..1d466b71 100755 --- a/FMODStudio/Source/FMODStudio/Private/FMODSettings.cpp +++ b/FMODStudio/Source/FMODStudio/Private/FMODSettings.cpp @@ -25,6 +25,9 @@ UFMODSettings::UFMODSettings(const FObjectInitializer &ObjectInitializer) bEnableLiveUpdate = true; bVol0Virtual = true; Vol0VirtualLevel = 0.0001f; + DopplerScale = 1.0f; + DistanceFactor = 1.0f; + RolloffScale = 1.0f; RealChannelCount = 64; TotalChannelCount = 512; FileBufferSize = 2048; diff --git a/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp b/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp index f1b64f80..edafe737 100755 --- a/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp +++ b/FMODStudio/Source/FMODStudio/Private/FMODStudioModule.cpp @@ -719,6 +719,8 @@ void FFMODStudioModule::CreateStudioSystem(EFMODSystemContext::Type Type) MediaModule->GetClock().AddSink(ClockSinks[Type].ToSharedRef()); } + verifyfmod(lowLevelSystem->set3DSettings(Settings.DopplerScale, Settings.DistanceFactor, Settings.RolloffScale)); + instanceWatchers.Broadcast(Type, StudioSystem[Type]); }