Files
FlaxEngine/Source/Engine/Tools/AudioTool/WaveDecoder.cpp
2022-10-20 17:28:12 +02:00

169 lines
5.0 KiB
C++

// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
#include "WaveDecoder.h"
#include "Engine/Core/Log.h"
#include "AudioTool.h"
#define WAVE_FORMAT_PCM 0x0001
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
#define WAVE_FORMAT_ALAW 0x0006
#define WAVE_FORMAT_MULAW 0x0007
#define WAVE_FORMAT_EXTENDED 0xFFFE
#define MAIN_CHUNK_SIZE 12
bool WaveDecoder::ParseHeader(AudioDataInfo& info)
{
bool foundData = false;
while (!foundData)
{
// Get sub-chunk ID and size
uint8 subChunkId[4];
mStream->ReadBytes(subChunkId, sizeof(subChunkId));
uint32 subChunkSize = 0;
mStream->ReadUint32(&subChunkSize);
// FMT chunk
if (subChunkId[0] == 'f' && subChunkId[1] == 'm' && subChunkId[2] == 't' && subChunkId[3] == ' ')
{
uint16 format;
mStream->ReadUint16(&format);
if (format != WAVE_FORMAT_PCM && format != WAVE_FORMAT_IEEE_FLOAT && format != WAVE_FORMAT_EXTENDED)
{
LOG(Warning, "Wave file doesn't contain raw PCM data. Not supported.");
return false;
}
uint16 numChannels = 0;
mStream->ReadUint16(&numChannels);
uint32 sampleRate = 0;
mStream->ReadUint32(&sampleRate);
uint32 byteRate = 0;
mStream->ReadUint32(&byteRate);
uint16 blockAlign = 0;
mStream->ReadUint16(&blockAlign);
uint16 bitDepth = 0;
mStream->ReadUint16(&bitDepth);
if (bitDepth != 8 && bitDepth != 16 && bitDepth != 24 && bitDepth != 32)
{
LOG(Warning, "Unsupported number of bits per sample: {0}", bitDepth);
return false;
}
info.NumChannels = numChannels;
info.SampleRate = sampleRate;
info.BitDepth = bitDepth;
// Read extension data, and get the actual format
if (format == WAVE_FORMAT_EXTENDED)
{
uint16 extensionSize = 0;
mStream->ReadUint16(&extensionSize);
if (extensionSize != 22)
{
LOG(Warning, "Wave file doesn't contain raw PCM data. Not supported.");
return false;
}
uint16 validBitDepth = 0;
mStream->ReadUint16(&validBitDepth);
uint32 channelMask = 0;
mStream->ReadUint32(&channelMask);
uint8 subFormat[16];
mStream->ReadBytes(subFormat, sizeof(subFormat));
Platform::MemoryCopy(&format, subFormat, sizeof(format));
if (format != WAVE_FORMAT_PCM)
{
LOG(Warning, "Wave file doesn't contain raw PCM data. Not supported.");
return false;
}
}
mBytesPerSample = bitDepth / 8;
mFormat = format;
}
// DATA chunk
else if (subChunkId[0] == 'd' && subChunkId[1] == 'a' && subChunkId[2] == 't' && subChunkId[3] == 'a')
{
info.NumSamples = subChunkSize / mBytesPerSample;
mDataOffset = (uint32)mStream->GetPosition();
foundData = true;
}
// Unsupported chunk type
else
{
if (mStream->GetPosition() + subChunkSize >= mStream->GetLength())
return false;
mStream->SetPosition(mStream->GetPosition() + subChunkSize);
}
}
return true;
}
bool WaveDecoder::Open(ReadStream* stream, AudioDataInfo& info, uint32 offset)
{
ASSERT(stream);
mStream = stream;
mStream->SetPosition(offset + MAIN_CHUNK_SIZE);
if (!ParseHeader(info))
{
LOG(Warning, "Provided file is not a valid WAVE file.");
return false;
}
return true;
}
void WaveDecoder::Seek(uint32 offset)
{
mStream->SetPosition(mDataOffset + offset * mBytesPerSample);
}
void WaveDecoder::Read(byte* samples, uint32 numSamples)
{
const uint32 numRead = numSamples * mBytesPerSample;
mStream->ReadBytes(samples, numRead);
// 8-bit samples are stored as unsigned, but engine convention is to store all bit depths as signed
if (mBytesPerSample == 1)
{
for (uint32 i = 0; i < numRead; i++)
{
int8 val = samples[i] - 128;
samples[i] = *((uint8*)&val);
}
}
// IEEE float need to be converted into signed PCM data
else if (mFormat == WAVE_FORMAT_IEEE_FLOAT)
{
AudioTool::ConvertFromFloat((const float*)samples, (int32*)samples, numSamples);
}
}
bool WaveDecoder::IsValid(ReadStream* stream, uint32 offset)
{
ASSERT(stream);
stream->SetPosition(offset);
byte header[MAIN_CHUNK_SIZE];
stream->ReadBytes(header, sizeof(header));
return (header[0] == 'R') && (header[1] == 'I') && (header[2] == 'F') && (header[3] == 'F')
&& (header[8] == 'W') && (header[9] == 'A') && (header[10] == 'V') && (header[11] == 'E');
}