Files
UnrealEngine/Engine/Source/Runtime/AudioExtensions/Private/IAudioModulation.cpp
2025-05-18 13:04:45 +08:00

422 lines
11 KiB
C++

// 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<FModulatorHandleId> NextHandleId = INDEX_NONE;
static const FModulationParameter DefaultParameter = { };
}
namespace ModulationInterfacePrivate
{
class FModulationParameterRegistry
{
TMap<FName, TUniquePtr<FModulationParameter>> 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<FModulationParameter>* ParamPtr = Values.Find(InName))
{
if (FModulationParameter* Param = ParamPtr->Get())
{
*Param = MoveTemp(InParameter);
return;
}
}
Values.Add(InName, MakeUnique<FModulationParameter>(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<FModulationParameter>* 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<IAudioModulationManager> 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<IAudioModulationManager> ModPtr = Modulation.Pin())
{
ModPtr->UnregisterModulator(*this);
}
}
FModulatorHandle& FModulatorHandle::operator=(const FModulatorHandle& InOther)
{
Parameter = InOther.Parameter;
if (TSharedPtr<IAudioModulationManager> 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<IAudioModulationManager> 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<IAudioModulationManager> ModPtr = Modulation.Pin())
{
return ModPtr->GetModulatorValue(*this, OutValue);
}
return false;
}
bool FModulatorHandle::GetValueThreadSafe(float& OutValue) const
{
check(IsValid());
OutValue = 1.0f;
if (TSharedPtr<IAudioModulationManager> 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<Audio::IProxyData> 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<Audio::IProxyData>();
}
TUniquePtr<Audio::IModulatorSettings> USoundModulatorBase::CreateProxySettings() const
{
checkNoEntry();
return TUniquePtr<Audio::IModulatorSettings>();
}