2024-01-25 11:21:15 +08:00

143 lines
3.5 KiB
C++

#include "Sampler.h"
#include "Paths.h"
#include "Singleton/PortAudioAPI.h"
#include "Singleton/MidiSequencer.h"
#include "sndfile.hh"
#include "samplerate.h"
FSampler::FSampler()
{
ChannelCount = 0;
FrameCount = 0;
SampleRate = 0;
IncomingOffset = 0;
IncomingRange = TRange<AudioFrame>(0, 0);
}
bool FSampler::Load(const FString& Path)
{
#if PLATFORM_WINDOWS
SndfileHandle FileHandle(TCHAR_TO_WCHAR(*Path));
#else
SndfileHandle FileHandle(TCHAR_TO_UTF8(*FilePath));
#endif
if (FileHandle.frames() == 0)
return false;
Name = FPaths::GetBaseFilename(Path);
Vendor = FName("Nanako");
FrameCount = FileHandle.frames();
ChannelCount = FileHandle.channels();
SampleRate = FileHandle.samplerate();
TArray64<float> TempBuffer;
TempBuffer.SetNumUninitialized(FrameCount * ChannelCount);
FileHandle.readf(TempBuffer.GetData(), FrameCount * ChannelCount);
SetFromInterlaceBuffer(TempBuffer, FrameCount);
UpdateSampleRate(FPortAudioAPI::Get().SampleRate);
UpdateBlockSize(FPortAudioAPI::Get().BlockSize);
ChannelInterface = new FChannelInterface(0, ChannelCount);
FMidiSequencer::Get().NewSamplePattern(this);
return true;
}
void FSampler::UpdateSampleRate(float InSampleRate)
{
if (InSampleRate == SampleRate)
return;
int32 Error = 0;
SRC_STATE* Converter = src_new(SRC_LINEAR, ChannelCount, &Error);
if (!Converter || Error != 0)
{
UE_LOG(LogTemp, Error, TEXT("Error creating sample converter: %hs"), src_strerror(Error));
return;
}
const double Ratio = InSampleRate / SampleRate;
TArray64<float> OutBuffer;
OutBuffer.SetNumUninitialized(GetSampleCount() * Ratio);
TArray<float> InterlaceBuffer = GetInterlaceBuffer();
SRC_DATA SrcData;
SrcData.data_in = InterlaceBuffer.GetData();
SrcData.data_out = OutBuffer.GetData();
SrcData.input_frames = FrameCount;
SrcData.output_frames = FrameCount * Ratio;
SrcData.src_ratio = Ratio;
Error = src_process(Converter, &SrcData);
if (Error != 0)
{
UE_LOG(LogTemp, Error, TEXT("Error on Resampling process: %hs"), src_strerror(Error));
return;
}
// Clean up:
src_delete(Converter);
SetFromInterlaceBuffer(OutBuffer, SrcData.output_frames_gen);
SampleRate = InSampleRate;
FrameCount = SrcData.output_frames_gen;
}
void FSampler::Process(int32 FrameNum)
{
const int32 Size = IncomingRange.Size<int32>();
if (Size == 0)
return;
// FrameNum = FMath::Min(Size, FrameNum);
float** OutputHeader = ChannelInterface->GetOutputHeader(); // 非交错缓冲区, SampleBuffer为交错缓冲区
const uint64 PatternPos = IncomingRange.GetLowerBoundValue();
const uint64 PatternEndPos = IncomingRange.GetUpperBoundValue();
for (int i = 0; i < ChannelCount; ++i)
{
FMemory::Memcpy(OutputHeader[i] + IncomingOffset, SampleBuffer[i].GetData() + PatternPos, Size * sizeof(float));
}
IncomingMidi.clear();
IncomingOffset = 0;
IncomingRange = TRange<AudioFrame>(0, 0);
}
TArray<float> FSampler::GetInterlaceBuffer() const
{
TArray<float> Out;
Out.SetNumUninitialized(FrameCount * ChannelCount);
for (int i = 0; i < ChannelCount; ++i)
{
for (uint32 j = 0; j < FrameCount; ++j)
{
Out[j * ChannelCount + i] = SampleBuffer[i][j];
}
}
return Out;
}
void FSampler::SetFromInterlaceBuffer(const TArray64<float>& InterlaceBuffer, uint32 FrameNum)
{
SampleBuffer.SetNum(ChannelCount);
for (int i = 0; i < ChannelCount; ++i)
{
TArray64<float>& ChannelBuffer = SampleBuffer[i];
ChannelBuffer.SetNum(FrameNum);
for (uint64 j = 0; j < FrameNum; ++j)
{
ChannelBuffer[j] = InterlaceBuffer[j * ChannelCount + i];
}
}
FrameCount = FrameNum;
}