Files
UnrealEngineUWP/Engine/Source/Runtime/AudioMixer/Private/SoundFileIO/SoundFileIO.cpp
Ethan Geller b92cd92207 Fix Robomerge from CL 5836157:
#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]
2019-04-12 14:26:40 -04:00

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;
}
}