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

261 lines
7.3 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AudioResampler.h"
// Convenience macro for the case in which LibSampleRate needs to be built for limited platforms.
#ifndef WITH_LIBSAMPLERATE
#define WITH_LIBSAMPLERATE (WITH_EDITOR && !PLATFORM_LINUX)
#endif
DEFINE_LOG_CATEGORY(LogAudioResampler);
#if WITH_LIBSAMPLERATE
#include "samplerate.h"
#endif // WITH_LIBSAMPLERATE
namespace Audio
{
// Helper function to ensure that buffers are appropriately set up.
bool CheckBufferValidity(const FResamplingParameters& InParameters, FResamplerResults& OutData)
{
if (OutData.OutBuffer == nullptr)
{
UE_LOG(LogAudioResampler, Error, TEXT("Please specify an output buffer when using Resample()."));
return false;
}
if (InParameters.SourceSampleRate <= 0.0f || InParameters.DestinationSampleRate <= 0.0f)
{
UE_LOG(LogAudioResampler, Error, TEXT("Please use non-zero, positive sample rates when calling Resample()."));
return false;
}
if (OutData.OutBuffer->Num() < GetOutputBufferSize(InParameters))
{
UE_LOG(LogAudioResampler, Error, TEXT("Insufficient space in output buffer: Please allocate space for %d samples."), GetOutputBufferSize(InParameters));
return false;
}
return true;
}
int32 GetOutputBufferSize(const FResamplingParameters& InParameters)
{
const int32 NumChannels = FMath::Max(1, InParameters.NumChannels);
const int32 NumInputFrames = InParameters.InputBuffer.Num() / NumChannels;
const float Ratio = InParameters.DestinationSampleRate / InParameters.SourceSampleRate;
const int32 NumOutputFrames = FMath::CeilToInt(Ratio * NumInputFrames);
return NumChannels * NumOutputFrames;
}
bool Resample(const FResamplingParameters& InParameters, FResamplerResults& OutData)
{
#if WITH_LIBSAMPLERATE
// Check validity of buffers.
if (!CheckBufferValidity(InParameters, OutData))
{
return false;
}
// Create new converter
int32 Error = 0;
#ifdef LIBSAMPLERATE_WITHOUT_SINC
SRC_STATE* Converter = src_new(SRC_LINEAR, InParameters.NumChannels, &Error);
#else
SRC_STATE* Converter = src_new(SRC_SINC_BEST_QUALITY, InParameters.NumChannels, &Error);
#endif
if (Converter == nullptr || Error != 0)
{
UE_LOG(LogTemp, Error, TEXT("Error creating sample converter: %hs"), src_strerror(Error));
return false;
}
SRC_DATA SrcData;
SrcData.data_in = InParameters.InputBuffer.GetData();
SrcData.data_out = OutData.OutBuffer->GetData();
SrcData.input_frames = InParameters.InputBuffer.Num() / InParameters.NumChannels;
SrcData.output_frames = OutData.OutBuffer->Num() / InParameters.NumChannels;
SrcData.src_ratio = InParameters.DestinationSampleRate / InParameters.SourceSampleRate;
SrcData.end_of_input = 1;
Error = src_process(Converter, &SrcData);
if (Error != 0)
{
UE_LOG(LogTemp, Error, TEXT("Error on Resampling process: %hs"), src_strerror(Error));
return false;
}
OutData.InputFramesUsed = SrcData.input_frames_used;
OutData.OutputFramesGenerated = SrcData.output_frames_gen;
// Clean up:
src_delete(Converter);
#endif //WITH_LIBSAMPLERATE
return true;
}
class FResamplerImpl
{
public:
FResamplerImpl();
~FResamplerImpl();
void Init(EResamplingMethod ResamplingMethod, float StartingSampleRateRatio, int32 InNumChannels);
void SetSampleRateRatio(float InRatio);
int32 ProcessAudio(float* InAudioBuffer, int32 InSamples, bool bEndOfInput, float* OutAudioBuffer, int32 MaxOutputFrames, int32& OutNumFrames);
#if WITH_LIBSAMPLERATE
float CurrentSampleRateRatio;
SRC_STATE* LibSRCState;
SRC_DATA Data;
#endif
};
#if WITH_LIBSAMPLERATE
FResamplerImpl::FResamplerImpl()
: CurrentSampleRateRatio(-1.0f)
, LibSRCState(nullptr)
{
}
FResamplerImpl::~FResamplerImpl()
{
if (LibSRCState)
{
LibSRCState = src_delete(LibSRCState);
}
check(!LibSRCState);
}
void FResamplerImpl::Init(EResamplingMethod ResamplingMethod, float StartingSampleRateRatio, int32 InNumChannels)
{
int32 ErrorResult = 0;
// Reset the SRC state if we already have one with equal channels
if (LibSRCState)
{
if (InNumChannels == src_get_channels(LibSRCState))
{
ErrorResult = src_reset(LibSRCState);
if (ErrorResult != 0)
{
const char* ErrorString = src_strerror(ErrorResult);
UE_LOG(LogAudioResampler, Error, TEXT("Failed to reset sample converter state: %hs."), ErrorString);
return;
}
}
else
{
// If the channel counts do not match, then remove one.
LibSRCState = src_delete(LibSRCState);
check(nullptr == LibSRCState);
}
}
// Create a new one if one does not exist.
if (nullptr == LibSRCState)
{
LibSRCState = src_new((int32)ResamplingMethod, InNumChannels, &ErrorResult);
if (!LibSRCState)
{
const char* ErrorString = src_strerror(ErrorResult);
UE_LOG(LogAudioResampler, Error, TEXT("Failed to create a sample rate convertor state object: %hs."), ErrorString);
}
}
if (LibSRCState)
{
ErrorResult = src_set_ratio(LibSRCState, StartingSampleRateRatio);
if (ErrorResult != 0)
{
const char* ErrorString = src_strerror(ErrorResult);
UE_LOG(LogAudioResampler, Error, TEXT("Failed to set sample rate ratio: %hs."), ErrorString);
}
}
CurrentSampleRateRatio = StartingSampleRateRatio;
}
void FResamplerImpl::SetSampleRateRatio(float InRatio)
{
CurrentSampleRateRatio = FMath::Max(InRatio, 0.00001f);
}
int32 FResamplerImpl::ProcessAudio(float* InAudioBuffer, int32 InSamples, bool bEndOfInput, float* OutAudioBuffer, int32 MaxOutputFrames, int32& OutNumFrames)
{
if (LibSRCState)
{
Data.data_in = InAudioBuffer;
Data.input_frames = InSamples;
Data.data_out = OutAudioBuffer;
Data.output_frames = MaxOutputFrames;
Data.src_ratio = (double)CurrentSampleRateRatio;
Data.end_of_input = bEndOfInput ? 1 : 0;
int32 ErrorResult = src_process(LibSRCState, &Data);
if (ErrorResult != 0)
{
const char* ErrorString = src_strerror(ErrorResult);
UE_LOG(LogAudioResampler, Error, TEXT("Failed to process audio: %hs."), ErrorString);
return ErrorResult;
}
OutNumFrames = Data.output_frames_gen;
}
return 0;
}
#else
// Null implementation
FResamplerImpl::FResamplerImpl() {}
FResamplerImpl::~FResamplerImpl() {}
void FResamplerImpl::Init(EResamplingMethod ResamplingMethod, float StartingSampleRateRatio, int32 InNumChannels) {}
void FResamplerImpl::SetSampleRateRatio(float InRatio) {}
int32 FResamplerImpl::ProcessAudio(float* InAudioBuffer, int32 InSamples, bool bEndOfInput, float* OutAudioBuffer, int32 MaxOutputFrames, int32& OutNumFrames) { return 0; }
#endif
FResampler::FResampler()
{
Impl = CreateImpl();
}
FResampler::~FResampler()
{
}
void FResampler::Init(EResamplingMethod ResamplingMethod, float StartingSampleRateRatio, int32 InNumChannels)
{
if (Impl.IsValid())
{
Impl->Init(ResamplingMethod, StartingSampleRateRatio, InNumChannels);
}
}
void FResampler::SetSampleRateRatio(float InRatio)
{
if (Impl.IsValid())
{
Impl->SetSampleRateRatio(InRatio);
}
}
int32 FResampler::ProcessAudio(float* InAudioBuffer, int32 InSamples, bool bEndOfInput, float* OutAudioBuffer, int32 MaxOutputFrames, int32& OutNumFrames)
{
if (Impl.IsValid())
{
return Impl->ProcessAudio(InAudioBuffer, InSamples, bEndOfInput, OutAudioBuffer, MaxOutputFrames, OutNumFrames);
}
return 0;
}
TUniquePtr<FResamplerImpl> FResampler::CreateImpl()
{
return TUniquePtr<FResamplerImpl>(new FResamplerImpl());
}
}