// Copyright Epic Games, Inc. All Rights Reserved. #include "IAudioModulation.h" #include "Containers/Map.h" #include "UObject/Object.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(IAudioModulation) namespace Audio { namespace ModulationPrivate { static std::atomic NextHandleId = INDEX_NONE; static const FModulationParameter DefaultParameter = { }; } namespace ModulationInterfacePrivate { class FModulationParameterRegistry { TMap> Values; mutable FCriticalSection ThreadSafeValueAccessor; public: bool IsRegistered(FName InName) const { FScopeLock Lock(&ThreadSafeValueAccessor); return Values.Contains(InName); } void Register(FName InName, FModulationParameter&& InParameter) { FScopeLock Lock(&ThreadSafeValueAccessor); if (const TUniquePtr* ParamPtr = Values.Find(InName)) { if (FModulationParameter* Param = ParamPtr->Get()) { *Param = MoveTemp(InParameter); return; } } Values.Add(InName, MakeUnique(FModulationParameter(InParameter))); } bool Unregister(FName InName) { FScopeLock Lock(&ThreadSafeValueAccessor); return Values.Remove(InName) > 0; } void UnregisterAll() { FScopeLock Lock(&ThreadSafeValueAccessor); Values.Reset(); } const FModulationParameter* Get(FName InName) const { FScopeLock Lock(&ThreadSafeValueAccessor); if (const TUniquePtr* ParamPtr = Values.Find(InName)) { if (const FModulationParameter* Param = ParamPtr->Get()) { return Param; } } return nullptr; } } ParameterRegistry; } FModulatorHandleId CreateModulatorHandleId() { return ++ModulationPrivate::NextHandleId; } FModulationParameter::FModulationParameter() : MixFunction(GetDefaultMixFunction()) , UnitFunction(GetDefaultUnitConversionFunction()) , NormalizedFunction(GetDefaultNormalizedConversionFunction()) { } FModulationParameter::FModulationParameter(FModulationParameter&& InParam) : ParameterName(MoveTemp(InParam.ParameterName)) , DefaultValue(InParam.DefaultValue) , MinValue(InParam.MinValue) , MaxValue(InParam.MaxValue) , bRequiresConversion(InParam.bRequiresConversion) #if WITH_EDITORONLY_DATA , UnitDisplayName(MoveTemp(InParam.UnitDisplayName)) , ClassName(MoveTemp(InParam.ClassName)) #endif // WITH_EDITORONLY_DATA , MixFunction(MoveTemp(InParam.MixFunction)) , UnitFunction(MoveTemp(InParam.UnitFunction)) , NormalizedFunction(MoveTemp(InParam.NormalizedFunction)) { } FModulationParameter::FModulationParameter(const FModulationParameter& InParam) : ParameterName(InParam.ParameterName) , DefaultValue(InParam.DefaultValue) , MinValue(InParam.MinValue) , MaxValue(InParam.MaxValue) , bRequiresConversion(InParam.bRequiresConversion) #if WITH_EDITORONLY_DATA , UnitDisplayName(InParam.UnitDisplayName) , ClassName(InParam.ClassName) #endif // WITH_EDITORONLY_DATA , MixFunction(InParam.MixFunction) , UnitFunction(InParam.UnitFunction) , NormalizedFunction(InParam.NormalizedFunction) { } FModulationParameter& FModulationParameter::operator=(const FModulationParameter& InParam) { ParameterName = InParam.ParameterName; DefaultValue = InParam.DefaultValue; MinValue = InParam.MinValue; MaxValue = InParam.MaxValue; bRequiresConversion = InParam.bRequiresConversion; #if WITH_EDITORONLY_DATA UnitDisplayName = InParam.UnitDisplayName; ClassName = InParam.ClassName; #endif // WITH_EDITORONLY_DATA MixFunction = InParam.MixFunction; UnitFunction = InParam.UnitFunction; NormalizedFunction = InParam.NormalizedFunction; return *this; } FModulationParameter& FModulationParameter::operator=(FModulationParameter&& InParam) { ParameterName = MoveTemp(InParam.ParameterName); DefaultValue = InParam.DefaultValue; MinValue = InParam.MinValue; MaxValue = InParam.MaxValue; bRequiresConversion = InParam.bRequiresConversion; #if WITH_EDITORONLY_DATA UnitDisplayName = MoveTemp(InParam.UnitDisplayName); ClassName = MoveTemp(InParam.ClassName); #endif // WITH_EDITORONLY_DATA MixFunction = MoveTemp(InParam.MixFunction); UnitFunction = MoveTemp(InParam.UnitFunction); NormalizedFunction = MoveTemp(InParam.NormalizedFunction); return *this; } const FModulationMixFunction& FModulationParameter::GetDefaultMixFunction() { static const FModulationMixFunction DefaultMixFunction = [](float& InOutValueA, float InValueB) { InOutValueA *= InValueB; }; return DefaultMixFunction; } const FModulationUnitConversionFunction& FModulationParameter::GetDefaultUnitConversionFunction() { static const FModulationUnitConversionFunction ConversionFunction = [](float& InOutValue) { }; return ConversionFunction; }; const FModulationNormalizedConversionFunction& FModulationParameter::GetDefaultNormalizedConversionFunction() { static const FModulationNormalizedConversionFunction ConversionFunction = [](float& InOutValue) { }; return ConversionFunction; }; void RegisterModulationParameter(FName InName, FModulationParameter&& InParameter) { using namespace ModulationInterfacePrivate; ParameterRegistry.Register(InName, MoveTemp(InParameter)); } bool UnregisterModulationParameter(FName InName) { using namespace ModulationInterfacePrivate; return ParameterRegistry.Unregister(InName); } void UnregisterAllModulationParameters() { using namespace ModulationInterfacePrivate; ParameterRegistry.UnregisterAll(); } bool IsModulationParameterRegistered(FName InName) { using namespace ModulationInterfacePrivate; return ParameterRegistry.IsRegistered(InName); } const FModulationParameter* GetModulationParameterPtr(FName InName) { using namespace ModulationInterfacePrivate; return ParameterRegistry.Get(InName); } const FModulationParameter& GetDefaultModulationParameter() { return ModulationPrivate::DefaultParameter; } const FModulationParameter& GetModulationParameter(FName InName) { using namespace ModulationInterfacePrivate; if (const FModulationParameter* Param = ParameterRegistry.Get(InName)) { return *Param; } return ModulationPrivate::DefaultParameter; } FModulatorHandle::FModulatorHandle(Audio::FModulationParameter&& InParameter) : Parameter(InParameter) , HandleId(CreateModulatorHandleId()) { } FModulatorHandle::FModulatorHandle(IAudioModulationManager& InModulation, const Audio::IModulatorSettings& InModulatorSettings, Audio::FModulationParameter&& InParameter) : Parameter(MoveTemp(InParameter)) , HandleId(CreateModulatorHandleId()) , Modulation(InModulation.AsShared()) { ModulatorTypeId = InModulatorSettings.Register(HandleId, InModulation); if (ModulatorTypeId != INDEX_NONE) { ModulatorId = InModulatorSettings.GetModulatorId(); } } FModulatorHandle::FModulatorHandle(const FModulatorHandle& InOther) { HandleId = CreateModulatorHandleId(); if (TSharedPtr ModPtr = InOther.Modulation.Pin()) { ModPtr->RegisterModulator(HandleId, InOther.ModulatorId); Parameter = InOther.Parameter; ModulatorId = InOther.ModulatorId; ModulatorTypeId = InOther.ModulatorTypeId; Modulation = InOther.Modulation; } } FModulatorHandle::FModulatorHandle(FModulatorHandle&& InOther) : Parameter(MoveTemp(InOther.Parameter)) , HandleId(InOther.HandleId) , ModulatorTypeId(InOther.ModulatorTypeId) , ModulatorId(InOther.ModulatorId) , Modulation(InOther.Modulation) { // Move does not register as presumed already activated or // copying default handle, which is invalid. Removes data // from handle being moved to avoid double deactivation on // destruction. InOther.Parameter = FModulationParameter(); InOther.HandleId = INDEX_NONE; InOther.ModulatorTypeId = INDEX_NONE; InOther.ModulatorId = INDEX_NONE; InOther.Modulation.Reset(); } FModulatorHandle::~FModulatorHandle() { if (TSharedPtr ModPtr = Modulation.Pin()) { ModPtr->UnregisterModulator(*this); } } FModulatorHandle& FModulatorHandle::operator=(const FModulatorHandle& InOther) { Parameter = InOther.Parameter; if (TSharedPtr ModPtr = InOther.Modulation.Pin()) { HandleId = CreateModulatorHandleId(); ModulatorId = InOther.ModulatorId; ModulatorTypeId = InOther.ModulatorTypeId; Modulation = InOther.Modulation; if (ModulatorId != INDEX_NONE) { ModPtr->RegisterModulator(HandleId, ModulatorId); } } else { HandleId = INDEX_NONE; ModulatorId = INDEX_NONE; ModulatorTypeId = INDEX_NONE; Modulation.Reset(); } return *this; } FModulatorHandle& FModulatorHandle::operator=(FModulatorHandle&& InOther) { if (HandleId != INDEX_NONE) { if (TSharedPtr ModPtr = Modulation.Pin()) { ModPtr->UnregisterModulator(*this); } } // Move does not activate as presumed already activated or // copying default handle, which is invalid. Removes data // from handle being moved to avoid double deactivation on // destruction. Parameter = MoveTemp(InOther.Parameter); HandleId = InOther.HandleId; ModulatorId = InOther.ModulatorId; ModulatorTypeId = InOther.ModulatorTypeId; Modulation = InOther.Modulation; InOther.Parameter = FModulationParameter(); InOther.HandleId = INDEX_NONE; InOther.ModulatorId = INDEX_NONE; InOther.ModulatorTypeId = INDEX_NONE; InOther.Modulation.Reset(); return *this; } FModulatorId FModulatorHandle::GetModulatorId() const { return ModulatorId; } const FModulationParameter& FModulatorHandle::GetParameter() const { return Parameter; } FModulatorTypeId FModulatorHandle::GetTypeId() const { return ModulatorTypeId; } uint32 FModulatorHandle::GetHandleId() const { return HandleId; } bool FModulatorHandle::GetValue(float& OutValue) const { check(IsValid()); OutValue = 1.0f; if (TSharedPtr ModPtr = Modulation.Pin()) { return ModPtr->GetModulatorValue(*this, OutValue); } return false; } bool FModulatorHandle::GetValueThreadSafe(float& OutValue) const { check(IsValid()); OutValue = 1.0f; if (TSharedPtr ModPtr = Modulation.Pin()) { return ModPtr->GetModulatorValueThreadSafe(*this, OutValue); } return false; } bool FModulatorHandle::IsValid() const { return ModulatorId != INDEX_NONE; } } // namespace Audio const Audio::FModulationParameter& USoundModulatorBase::GetOutputParameter() const { return Audio::ModulationPrivate::DefaultParameter; } TSharedPtr USoundModulatorBase::CreateProxyData(const Audio::FProxyDataInitParams& InitParams) { // This should never be hit as all instances of modulators should implement their own version of the proxy data interface. checkNoEntry(); return TSharedPtr(); } TUniquePtr USoundModulatorBase::CreateProxySettings() const { checkNoEntry(); return TUniquePtr(); }