Hello, I am currently trying to write a plugin retrieving some debug information from the FMODPlugin. I have stumbled over something I cannot seem to find a solution for.
Given an Actor with an UFMODAudioComponent that uses an UFMODEvent which is a 3DSound I could not find a way to retrieve its current volume after attenuation has been applied.
The distances are easy to get, even when overriden and apparently the type of function can be retrieved with the FMOD_MODE. However I also do not know how to retrieve this, as I do not seem to understand the link between an EventInstance and a Sound.
Is there a way to get this information or am I trying somethin impossible?
To get the instantaneous peak and rms levels of a playing event you can use DSP::getMeteringInfo
on the ChannelGroup
that the event is playing on. It’s a bit of drilling down from the UFMODEvent to get to it, but the process is as follows:
- Get the underlying event instance from the UFMODEvent
- Access the event instance’s channel group using
EventInstance::getChannelGroup
- Get the head, tail or fader DSP object using
Channel::getDSP
, depending on which point you want to access the metering info - Enable metering on your chosen DSP object with
DSP::setMeteringEnabled
- Retrieve the metering info with
DSP::getMeteringInfo
- Read the resulting peak, rms etc level info from the returned
FMOD_DSP_METERING_INFO
object
I have a basic example class here of what that would look like in the context of UE4.
#include "DisplayVolume.h"
#include <fmod_studio.hpp>
#include <FMODStudioModule.h>
#include <FMODAudioComponent.h>
#include <FMODEvent.h>
#include <FMODBlueprintStatics.h>
FMOD::ChannelGroup* Channel;
FMOD::DSP* HeadDSP;
FMOD::Studio::System* StudioSystem;
FMOD::Studio::EventInstance* Instance;
void ERRCHECK(FMOD_RESULT result, int line)
{
if (result != FMOD_OK)
{
UE_LOG(LogTemp, Warning, TEXT("Result = %d, %d"), (int)result, line);
}
}
// Sets default values for this component's properties
UDisplayVolume::UDisplayVolume()
{
PrimaryComponentTick.bCanEverTick = true;
}
UDisplayVolume::~UDisplayVolume()
{
Channel->release();
}
// Called when the game starts
void UDisplayVolume::BeginPlay()
{
Super::BeginPlay();
if (IFMODStudioModule::IsAvailable())
{
StudioSystem = IFMODStudioModule::Get().GetStudioSystem(EFMODSystemContext::Runtime);
if (StudioSystem)
{
// Create event instance
FFMODEventInstance wrapper = UFMODBlueprintStatics::PlayEvent2D(
GetOwner(),
Event, // UFMODEvent*, Declared in DisplayVolume.h
true);
Instance = wrapper.Instance;
}
}
}
// Called every frame
void UDisplayVolume::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
if (IFMODStudioModule::IsAvailable())
{
if (StudioSystem && Instance)
{
ERRCHECK(Instance->getChannelGroup(&Channel), __LINE__);
if (Channel)
{
ERRCHECK(Channel->getDSP(FMOD_CHANNELCONTROL_DSP_HEAD, &HeadDSP), __LINE__);
if (HeadDSP)
{
ERRCHECK(HeadDSP->setMeteringEnabled(false, true), __LINE__);
FMOD_DSP_METERING_INFO Info = {};
ERRCHECK(HeadDSP->getMeteringInfo(nullptr, &Info), __LINE__);
GEngine->ClearOnScreenDebugMessages();
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(
TEXT("Metering: %d channels, %d len. rms: %.3f, %.3f, %.3f, %.3f, %.3f, %.3f"),
Info.numchannels, Info.numsamples,
Info.rmslevel[0], Info.rmslevel[1],
Info.rmslevel[2], Info.rmslevel[3],
Info.rmslevel[4], Info.rmslevel[5])
);
}
}
}
}
}
And the resulting image of the volume debug info.
Hopefully that all makes sense, let me know if you have any issues!
Thanks for the quick answer. If I got this correctly, the Spatializer is also just a DSP so getting the last DSP in the chain should give us the correct value?
At the moment we are using the getAudability function on the ChannelControl to determine an Events estimated volume. Like so:
inline float GetAudibility(const UFMODAudioComponent* audioComponent)
{
FMOD::ChannelGroup* channelGroup = nullptr;
audioComponent->StudioInstance->getChannelGroup(&channelGroup);
if(!channelGroup)
return 0.0f;
float audibility;
channelGroup->getAudibility(&audibility);
return audibility;
}
I assume it does not really represent the volume, but seems to give good estimate and seems to contain a lot of the information we want in a normalized floating point value according to the documentation.
Could this be used as a stand in as it seems to change with the volume of the Event being played?
Correct, if it is a 3D sound with a Spatializer at the last node, then yes, passing FMOD_CHANNELCONTROL_DSP_TAIL
into getDSP
will return the Spatializer and you can grab the metering info from that.
If you need exact rms or peak values then you should use the getMeteringInfo
method outlined above, but if you just want a non-standardised metric of how loud or quiet something is then getAudibilty
is suitable.