You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#jira FORT-164897 Fix for issue with imported, non-16 bit WAV files having their PCM data padded into 2048 sample blocks, then truncated by one byte. This caused issues on cook with certain platforms, but doesn't seem to affect anything else (maybe loops?). #rb aaron.mcleran, rob.gay #lockdown cristina.riveron #ROBOMERGE-OWNER: ethan.geller #ROBOMERGE-AUTHOR: ethan.geller #ROBOMERGE-SOURCE: CL 5845931 in //UE4/Release-4.22/... #ROBOMERGE-BOT: RELEASE (Release-4.22 -> Main) [CL 5862476 by Ethan Geller in Main branch]
149 lines
4.7 KiB
C++
149 lines
4.7 KiB
C++
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "SoundFileIO/SoundFileIO.h"
|
|
|
|
#include "CoreMinimal.h"
|
|
|
|
#include "Async/AsyncWork.h"
|
|
#include "AudioMixerDevice.h"
|
|
#include "Engine/Engine.h"
|
|
#include "EngineGlobals.h"
|
|
#include "Misc/FileHelper.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "SoundFileIOManager.h"
|
|
#include "SoundFile.h"
|
|
#include "SoundFileIOEnums.h"
|
|
#include "Stats/Stats.h"
|
|
|
|
|
|
namespace Audio
|
|
{
|
|
bool AUDIOMIXER_API InitSoundFileIOManager()
|
|
{
|
|
return Audio::SoundFileIOManagerInit();
|
|
}
|
|
|
|
bool AUDIOMIXER_API ShutdownSoundFileIOManager()
|
|
{
|
|
return Audio::SoundFileIOManagerShutdown();
|
|
}
|
|
|
|
bool AUDIOMIXER_API ConvertAudioToWav(const TArray<uint8>& InAudioData, TArray<uint8>& OutWaveData)
|
|
{
|
|
const FSoundFileConvertFormat ConvertFormat = FSoundFileConvertFormat::CreateDefault();
|
|
|
|
FSoundFileIOManager SoundIOManager;
|
|
TSharedPtr<ISoundFileReader> InputSoundDataReader = SoundIOManager.CreateSoundDataReader();
|
|
|
|
ESoundFileError::Type Error = InputSoundDataReader->Init(&InAudioData);
|
|
if (Error != ESoundFileError::Type::NONE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
TArray<ESoundFileChannelMap::Type> ChannelMap;
|
|
|
|
FSoundFileDescription InputDescription;
|
|
InputSoundDataReader->GetDescription(InputDescription, ChannelMap);
|
|
|
|
FSoundFileDescription NewSoundFileDescription;
|
|
NewSoundFileDescription.NumChannels = InputDescription.NumChannels;
|
|
NewSoundFileDescription.NumFrames = InputDescription.NumFrames;
|
|
NewSoundFileDescription.FormatFlags = ConvertFormat.Format;
|
|
NewSoundFileDescription.SampleRate = InputDescription.SampleRate;
|
|
NewSoundFileDescription.NumSections = InputDescription.NumSections;
|
|
NewSoundFileDescription.bIsSeekable = InputDescription.bIsSeekable;
|
|
|
|
TSharedPtr<ISoundFileWriter> SoundFileWriter = SoundIOManager.CreateSoundFileWriter();
|
|
Error = SoundFileWriter->Init(NewSoundFileDescription, ChannelMap, ConvertFormat.EncodingQuality);
|
|
if (Error != ESoundFileError::Type::NONE)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Create a buffer to do the processing
|
|
SoundFileCount ProcessBufferSamples = 1024 * NewSoundFileDescription.NumChannels;
|
|
TArray<float> ProcessBuffer;
|
|
ProcessBuffer.Init(0.0f, ProcessBufferSamples);
|
|
|
|
// Find the max value if we've been told to do peak normalization on import
|
|
float MaxValue = 0.0f;
|
|
SoundFileCount InputSamplesRead = 0;
|
|
bool bPerformPeakNormalization = ConvertFormat.bPerformPeakNormalization;
|
|
if (bPerformPeakNormalization)
|
|
{
|
|
Error = InputSoundDataReader->ReadSamples(ProcessBuffer.GetData(), ProcessBufferSamples, InputSamplesRead);
|
|
check(Error == ESoundFileError::Type::NONE);
|
|
|
|
while (InputSamplesRead)
|
|
{
|
|
for (SoundFileCount Sample = 0; Sample < InputSamplesRead; ++Sample)
|
|
{
|
|
if (ProcessBuffer[Sample] > FMath::Abs(MaxValue))
|
|
{
|
|
MaxValue = ProcessBuffer[Sample];
|
|
}
|
|
}
|
|
|
|
Error = InputSoundDataReader->ReadSamples(ProcessBuffer.GetData(), ProcessBufferSamples, InputSamplesRead);
|
|
check(Error == ESoundFileError::Type::NONE);
|
|
}
|
|
|
|
// If this happens, it means we have a totally silent file
|
|
if (MaxValue == 0.0)
|
|
{
|
|
bPerformPeakNormalization = false;
|
|
}
|
|
|
|
// Seek the file back to the beginning
|
|
SoundFileCount OutOffset;
|
|
InputSoundDataReader->SeekFrames(0, ESoundFileSeekMode::FROM_START, OutOffset);
|
|
}
|
|
|
|
bool SamplesProcessed = true;
|
|
|
|
// Read the first block of samples
|
|
Error = InputSoundDataReader->ReadSamples(ProcessBuffer.GetData(), ProcessBufferSamples, InputSamplesRead);
|
|
check(Error == ESoundFileError::Type::NONE);
|
|
|
|
SoundFileCount SamplesWritten = 0;
|
|
|
|
while (InputSamplesRead == ProcessBuffer.Num())
|
|
{
|
|
Error = SoundFileWriter->WriteSamples((const float*)ProcessBuffer.GetData(), InputSamplesRead, SamplesWritten);
|
|
check(Error == ESoundFileError::Type::NONE);
|
|
check(SamplesWritten == InputSamplesRead);
|
|
|
|
// read more samples
|
|
Error = InputSoundDataReader->ReadSamples(ProcessBuffer.GetData(), ProcessBufferSamples, InputSamplesRead);
|
|
check(Error == ESoundFileError::Type::NONE);
|
|
|
|
// ... normalize the samples if we're told to
|
|
if (bPerformPeakNormalization)
|
|
{
|
|
for (int32 Sample = 0; Sample < InputSamplesRead; ++Sample)
|
|
{
|
|
ProcessBuffer[Sample] /= MaxValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Write final block of samples
|
|
Error = SoundFileWriter->WriteSamples((const float*)ProcessBuffer.GetData(), InputSamplesRead, SamplesWritten);
|
|
check(Error == ESoundFileError::Type::NONE);
|
|
|
|
// Release the sound file handles as soon as we finished converting the file
|
|
InputSoundDataReader->Release();
|
|
SoundFileWriter->Release();
|
|
|
|
// Get the raw binary data.....
|
|
TArray<uint8>* Data = nullptr;
|
|
SoundFileWriter->GetData(&Data);
|
|
|
|
OutWaveData.Init(0, Data->Num());
|
|
FMemory::Memcpy(OutWaveData.GetData(), (const void*)&(*Data)[0], OutWaveData.Num());
|
|
|
|
return true;
|
|
}
|
|
}
|