#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(0, 0); } bool FSampler::Load(const FString& InPath) { Path = InPath; #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 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 OutBuffer; OutBuffer.SetNumUninitialized(GetSampleCount() * Ratio); TArray 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(); 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(0, 0); } TArray FSampler::GetInterlaceBuffer() const { TArray 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& InterlaceBuffer, uint32 FrameNum) { SampleBuffer.SetNum(ChannelCount); for (int i = 0; i < ChannelCount; ++i) { TArray64& ChannelBuffer = SampleBuffer[i]; ChannelBuffer.SetNum(FrameNum); for (uint64 j = 0; j < FrameNum; ++j) { ChannelBuffer[j] = InterlaceBuffer[j * ChannelCount + i]; } } FrameCount = FrameNum; }