Fix audio module: implement WAV parsing and audio playback
- Implement ParseWAVData and ParseWAVHeader in AudioLoader to properly parse WAV file headers (sample rate, channels, bits per sample, duration) - Modify Load() to call ParseWAVData for WAV files during loading - Add DecodeAudioData() to AudioSourceComponent to decode PCM bytes to float - Update SetClip() to trigger audio decoding - Fix ProcessAudio() to read from decoded data instead of empty output buffer - Add WAV parsing unit tests (ParseWAV_Mono44100_16bit, ParseWAV_Stereo48000_16bit) Fixes issues: - AudioLoader::ParseWAVData was a stub returning true without parsing - AudioLoader::Load didn't extract audio metadata from WAV headers - AudioSourceComponent::ProcessAudio read from empty m_outputBuffer All 167 tests pass.
This commit is contained in:
@@ -1,10 +1,68 @@
|
||||
#include "Resources/AudioLoader.h"
|
||||
#include "Resources/ResourceManager.h"
|
||||
#include "Resources/ResourceTypes.h"
|
||||
#include <cstring>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
namespace {
|
||||
|
||||
struct WAVHeader {
|
||||
uint32_t sampleRate = 44100;
|
||||
uint32_t channels = 2;
|
||||
uint32_t bitsPerSample = 16;
|
||||
uint32_t dataSize = 0;
|
||||
uint32_t dataOffset = 44;
|
||||
};
|
||||
|
||||
bool ParseWAVHeader(const uint8_t* data, size_t size, WAVHeader& header) {
|
||||
if (size < 44) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data[0] != 'R' || data[1] != 'I' || data[2] != 'F' || data[3] != 'F') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data[8] != 'W' || data[9] != 'A' || data[10] != 'V' || data[11] != 'E') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data[12] != 'f' || data[13] != 'm' || data[14] != 't' || data[15] != ' ') {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t subchunk1Size = *reinterpret_cast<const uint32_t*>(&data[16]);
|
||||
if (subchunk1Size < 16) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t audioFormat = *reinterpret_cast<const uint16_t*>(&data[20]);
|
||||
if (audioFormat != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
header.channels = *reinterpret_cast<const uint16_t*>(&data[22]);
|
||||
header.sampleRate = *reinterpret_cast<const uint32_t*>(&data[24]);
|
||||
header.bitsPerSample = *reinterpret_cast<const uint16_t*>(&data[34]);
|
||||
|
||||
if (data[36] != 'd' || data[37] != 'a' || data[38] != 't' || data[39] != 'a') {
|
||||
return false;
|
||||
}
|
||||
|
||||
header.dataSize = *reinterpret_cast<const uint32_t*>(&data[40]);
|
||||
header.dataOffset = 44;
|
||||
|
||||
if (header.dataOffset + header.dataSize > size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AudioLoader::AudioLoader() = default;
|
||||
|
||||
AudioLoader::~AudioLoader() = default;
|
||||
@@ -38,6 +96,12 @@ LoadResult AudioLoader::Load(const Containers::String& path, const ImportSetting
|
||||
audioClip->m_guid = ResourceGUID::Generate(path);
|
||||
|
||||
AudioFormat format = DetectAudioFormat(path, data);
|
||||
if (format == AudioFormat::WAV) {
|
||||
if (!ParseWAVData(data, audioClip)) {
|
||||
delete audioClip;
|
||||
return LoadResult("Failed to parse WAV data");
|
||||
}
|
||||
}
|
||||
audioClip->SetAudioFormat(format);
|
||||
audioClip->SetAudioData(data);
|
||||
|
||||
@@ -53,6 +117,20 @@ ImportSettings* AudioLoader::GetDefaultSettings() const {
|
||||
}
|
||||
|
||||
bool AudioLoader::ParseWAVData(const Containers::Array<Core::uint8>& data, AudioClip* audioClip) {
|
||||
WAVHeader header;
|
||||
if (!ParseWAVHeader(data.Data(), data.Size(), header)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
audioClip->SetSampleRate(header.sampleRate);
|
||||
audioClip->SetChannels(header.channels);
|
||||
audioClip->SetBitsPerSample(header.bitsPerSample);
|
||||
|
||||
uint32_t bytesPerSample = header.bitsPerSample / 8;
|
||||
uint32_t totalSamples = header.dataSize / (bytesPerSample * header.channels);
|
||||
float duration = static_cast<float>(totalSamples) / (header.sampleRate * header.channels);
|
||||
audioClip->SetDuration(duration);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user