// Copyright Epic Games, Inc. All Rights Reserved. #include "WaveTableFileUtilities.h" #include "Factories/SoundFactory.h" #include "Sound/SoundWave.h" #include "UObject/Package.h" #include "Utils.h" #include "WaveTableEditorModule.h" #include "WaveTableSampler.h" namespace WaveTable::Editor { namespace FileUtilities { void LoadPCMChannel(const FString& InFilePath, int32 InChannelIndex, FWaveTableData& OutData, int32& OutSampleRate) { OutData.Empty(); if (!InFilePath.IsEmpty()) { if (USoundFactory* SoundWaveFactory = NewObject()) { // Setup sane defaults for importing localized sound waves SoundWaveFactory->bAutoCreateCue = false; SoundWaveFactory->SuppressImportDialogs(); UPackage* TempPackage = CreatePackage(TEXT("/Temp/WaveTable")); if (USoundWave* SoundWave = ImportObject(TempPackage, "ImportedWaveTable", RF_Public | RF_Standalone, *InFilePath, nullptr, SoundWaveFactory)) { TArray RawPCMData; uint16 NumChannels = 0; uint32 FileSampleRate = 0; SoundWave->GetImportedSoundWaveData(RawPCMData, FileSampleRate, NumChannels); OutSampleRate = static_cast(FileSampleRate); // Deinterleave & perform BDC if necessary if (NumChannels > 0) { const int32 NumFrames = RawPCMData.Num() / NumChannels / sizeof(int16); const int16* RawDataPtr = (const int16*)(RawPCMData.GetData()); const int32 ChannelOffset = (InChannelIndex % NumChannels); OutData.Zero(NumFrames); constexpr float ConversionValue = 1.0f / static_cast(TNumericLimits::Max()); switch (OutData.GetBitDepth()) { case EWaveTableBitDepth::IEEE_Float: { TArrayView DataView; OutData.GetDataView(DataView); for (int32 Index = 0; Index < DataView.Num(); ++Index) { DataView[Index] = (RawDataPtr[(Index * NumChannels) + ChannelOffset]) * ConversionValue; } if (!DataView.IsEmpty()) { OutData.SetFinalValue(DataView.Last()); } } break; case EWaveTableBitDepth::PCM_16: { TArrayView DataView; OutData.GetDataView(DataView); for (int32 Index = 0; Index < DataView.Num(); ++Index) { DataView[Index] = (RawDataPtr[(Index * NumChannels) + ChannelOffset]); } if (!DataView.IsEmpty()) { OutData.SetFinalValue(DataView.Last() * ConversionValue); } } break; default: { static_assert(static_cast(EWaveTableBitDepth::COUNT) == 2, "Possible missing switch case coverage for 'EWaveTableBitDepth'"); checkNoEntry(); } break; } } else { UE_LOG(LogWaveTableEditor, Error, TEXT("Failed to import/reimport file '%s': Invalid num channels '%u' imported"), *InFilePath, NumChannels); } } else { UE_LOG(LogWaveTableEditor, Error, TEXT("Failed to import/reimport file '%s'"), *InFilePath); } } } } void LoadPCMChannel(const FString& InFilePath, int32 InChannelIndex, TArray& OutPCMData) { OutPCMData.Empty(); if (!InFilePath.IsEmpty()) { if (USoundFactory* SoundWaveFactory = NewObject()) { // Setup sane defaults for importing localized sound waves SoundWaveFactory->bAutoCreateCue = false; SoundWaveFactory->SuppressImportDialogs(); UPackage* TempPackage = CreatePackage(TEXT("/Temp/WaveTable")); if (USoundWave* SoundWave = ImportObject(TempPackage, "ImportedWaveTable", RF_Public | RF_Standalone, *InFilePath, nullptr, SoundWaveFactory)) { TArray RawPCMData; uint32 SampleRate = 0; uint16 NumChannels = 0; SoundWave->GetImportedSoundWaveData(RawPCMData, SampleRate, NumChannels); if (NumChannels > 0) { const int32 NumFrames = RawPCMData.Num() / NumChannels / sizeof(int16); OutPCMData.Empty(); OutPCMData.AddZeroed(NumFrames); const int16* RawDataPtr = (const int16*)(RawPCMData.GetData()); const int32 ChannelOffset = (InChannelIndex % NumChannels); constexpr float Max16BitAsFloat = static_cast(TNumericLimits::Max()); for (int32 i = 0; i < OutPCMData.Num(); ++i) { OutPCMData[i] = (RawDataPtr[(i * NumChannels) + ChannelOffset]) / Max16BitAsFloat; } } } } } } } // namespace FileUtilities } // namespace WaveTable::Editor