Files
XCEngine/tests/Resources/test_audio_loader.cpp
ssdfasd 2432a646ce 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.
2026-03-22 02:03:51 +08:00

119 lines
3.6 KiB
C++

#include <gtest/gtest.h>
#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);
}
TEST(AudioLoader, GetSupportedExtensions) {
AudioLoader loader;
auto extensions = loader.GetSupportedExtensions();
EXPECT_GE(extensions.Size(), 1u);
}
TEST(AudioLoader, CanLoad) {
AudioLoader loader;
EXPECT_TRUE(loader.CanLoad("test.wav"));
EXPECT_TRUE(loader.CanLoad("test.mp3"));
EXPECT_TRUE(loader.CanLoad("test.ogg"));
EXPECT_FALSE(loader.CanLoad("test.txt"));
EXPECT_FALSE(loader.CanLoad("test.png"));
}
TEST(AudioLoader, LoadInvalidPath) {
AudioLoader loader;
LoadResult result = loader.Load("invalid/path/audio.wav");
EXPECT_FALSE(result);
}
} // namespace