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:
@@ -2,12 +2,93 @@
|
||||
#include <XCEngine/Resources/AudioLoader.h>
|
||||
#include <XCEngine/Resources/ResourceTypes.h>
|
||||
#include <XCEngine/Containers/Array.h>
|
||||
#include <XCEngine/Resources/ResourceManager.h>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
|
||||
using namespace XCEngine::Resources;
|
||||
using namespace XCEngine::Containers;
|
||||
|
||||
namespace {
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct WAVFileHeader {
|
||||
char riff[4];
|
||||
uint32_t fileSize;
|
||||
char wave[4];
|
||||
char fmt[4];
|
||||
uint32_t fmtSize;
|
||||
uint16_t audioFormat;
|
||||
uint16_t numChannels;
|
||||
uint32_t sampleRate;
|
||||
uint32_t byteRate;
|
||||
uint16_t blockAlign;
|
||||
uint16_t bitsPerSample;
|
||||
char data[4];
|
||||
uint32_t dataSize;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
void CreateTestWAVFile(const char* filepath, uint16_t channels, uint32_t sampleRate, uint16_t bitsPerSample, uint32_t numSamples) {
|
||||
WAVFileHeader header = {};
|
||||
header.riff[0] = 'R'; header.riff[1] = 'I'; header.riff[2] = 'F'; header.riff[3] = 'F';
|
||||
header.wave[0] = 'W'; header.wave[1] = 'A'; header.wave[2] = 'V'; header.wave[3] = 'E';
|
||||
header.fmt[0] = 'f'; header.fmt[1] = 'm'; header.fmt[2] = 't'; header.fmt[3] = ' ';
|
||||
header.fmtSize = 16;
|
||||
header.audioFormat = 1;
|
||||
header.numChannels = channels;
|
||||
header.sampleRate = sampleRate;
|
||||
header.bitsPerSample = bitsPerSample;
|
||||
header.blockAlign = channels * bitsPerSample / 8;
|
||||
header.byteRate = sampleRate * header.blockAlign;
|
||||
header.data[0] = 'd'; header.data[1] = 'a'; header.data[2] = 't'; header.data[3] = 'a';
|
||||
header.dataSize = numSamples * header.blockAlign;
|
||||
header.fileSize = 36 + header.dataSize;
|
||||
|
||||
std::vector<uint8_t> wavData(sizeof(WAVFileHeader) + header.dataSize, 0);
|
||||
std::memcpy(wavData.data(), &header, sizeof(WAVFileHeader));
|
||||
|
||||
std::ofstream file(filepath, std::ios::binary);
|
||||
file.write(reinterpret_cast<const char*>(wavData.data()), wavData.size());
|
||||
}
|
||||
|
||||
TEST(AudioLoader, ParseWAV_Mono44100_16bit) {
|
||||
const char* testPath = "test_mono_44100.wav";
|
||||
CreateTestWAVFile(testPath, 1, 44100, 16, 44100);
|
||||
|
||||
AudioLoader loader;
|
||||
LoadResult result = loader.Load(testPath);
|
||||
|
||||
EXPECT_TRUE(result);
|
||||
if (result) {
|
||||
AudioClip* clip = static_cast<AudioClip*>(result.resource);
|
||||
EXPECT_EQ(clip->GetSampleRate(), 44100u);
|
||||
EXPECT_EQ(clip->GetChannels(), 1u);
|
||||
EXPECT_EQ(clip->GetBitsPerSample(), 16u);
|
||||
EXPECT_GT(clip->GetDuration(), 0.0f);
|
||||
}
|
||||
|
||||
std::remove(testPath);
|
||||
}
|
||||
|
||||
TEST(AudioLoader, ParseWAV_Stereo48000_16bit) {
|
||||
const char* testPath = "test_stereo_48000.wav";
|
||||
CreateTestWAVFile(testPath, 2, 48000, 16, 4800);
|
||||
|
||||
AudioLoader loader;
|
||||
LoadResult result = loader.Load(testPath);
|
||||
|
||||
EXPECT_TRUE(result);
|
||||
if (result) {
|
||||
AudioClip* clip = static_cast<AudioClip*>(result.resource);
|
||||
EXPECT_EQ(clip->GetSampleRate(), 48000u);
|
||||
EXPECT_EQ(clip->GetChannels(), 2u);
|
||||
EXPECT_EQ(clip->GetBitsPerSample(), 16u);
|
||||
}
|
||||
|
||||
std::remove(testPath);
|
||||
}
|
||||
|
||||
TEST(AudioLoader, GetResourceType) {
|
||||
AudioLoader loader;
|
||||
EXPECT_EQ(loader.GetResourceType(), ResourceType::AudioClip);
|
||||
|
||||
Reference in New Issue
Block a user