I solved the problem by recreating the header myself using SampleRate and the Channels amount. It seems to work for basic PCM-16bit depth wave files, which is enough for me. Here is the code to do so (functions are static because it’s in a BP static library):
→ Please note that the function GetImportedSoundWaveData()
is only available in editor mode, meaning that the code below will not work on packaged projects.
.h :
static TArray<uint8> SoundWaveToBytes(const USoundWave* Audio);
static TArray<uint8> FStringToBytes(const FString& String);
static TArray<uint8> uint32ToBytes(const uint32 Value, bool UseLittleEndian = true);
static TArray<uint8> uint16ToBytes(const uint16 Value, bool UseLittleEndian = true);
.cpp :
TArray<uint8> SoundWaveToBytes(const USoundWave* Audio)
{
TArray<uint8> BytesArr;
if (IsValid(Audio))
{
TArray<uint8> AudioBytes;
TArray<uint8> AudioHeader;
uint32 SampleRate;
uint16 BitDepth = 16; // How to retrieve information directly from the SoundWave ??
uint16 Channels;
if (Audio->GetImportedSoundWaveData(AudioBytes, SampleRate, Channels))
{
// Please see: https://docs.fileformat.com/audio/wav/ Bytes
AudioHeader.Append(FStringToBytes("RIFF")); // 1-4
AudioHeader.Append(uint32ToBytes(AudioBytes.Num() + 36)); // 5-8
AudioHeader.Append(FStringToBytes("WAVEfmt ")); // 9-16
AudioHeader.Append(uint32ToBytes(16)); // 17-20
AudioHeader.Append(uint16ToBytes(1)); // 21-22
AudioHeader.Append(uint16ToBytes(Channels)); // 23-24
AudioHeader.Append(uint32ToBytes(SampleRate)); // 25-28
AudioHeader.Append(uint32ToBytes(SampleRate * BitDepth * Channels / 8)); // 29-32
AudioHeader.Append(uint16ToBytes(BitDepth * Channels / 8)); // 33-34
AudioHeader.Append(uint16ToBytes(BitDepth)); // 35-36
AudioHeader.Append(FStringToBytes("data")); // 37-40
AudioHeader.Append(uint32ToBytes(AudioBytes.Num())); // 41-44
BytesArr.Append(AudioHeader);
BytesArr.Append(AudioBytes);
}
}
return BytesArr;
}
TArray<uint8> FStringToBytes(const FString& String)
{
TArray<uint8> OutBytes;
// Handle empty strings
if (String.Len() > 0)
{
FTCHARToUTF8 Converted(*String); // Convert to UTF8
OutBytes.Append(reinterpret_cast<const uint8*>(Converted.Get()), Converted.Length());
}
return OutBytes;
}
TArray<uint8> uint32ToBytes(const uint32 Value, bool UseLittleEndian)
{
TArray<uint8> OutBytes;
if (UseLittleEndian)
{
OutBytes.Add(Value >> 0 & 0xFF);
OutBytes.Add(Value >> 8 & 0xFF);
OutBytes.Add(Value >> 16 & 0xFF);
OutBytes.Add(Value >> 24 & 0xFF);
}
else
{
OutBytes.Add(Value >> 24 & 0xFF);
OutBytes.Add(Value >> 16 & 0xFF);
OutBytes.Add(Value >> 8 & 0xFF);
OutBytes.Add(Value >> 0 & 0xFF);
}
return OutBytes;
}
TArray<uint8> uint16ToBytes(const uint16 Value, bool UseLittleEndian)
{
TArray<uint8> OutBytes;
if (UseLittleEndian)
{
OutBytes.Add(Value >> 0 & 0xFF);
OutBytes.Add(Value >> 8 & 0xFF);
}
else
{
OutBytes.Add(Value >> 8 & 0xFF);
OutBytes.Add(Value >> 0 & 0xFF);
}
return OutBytes;
}
Usage :
USoundWave* Audio = ...;
TArray<uint8> AudioBytes = SoundWaveToBytes(Audio);
When I send the audio bytes in the HTTP POST multipart/form-data request (see this) the file is now readable by the server!