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:
2026-03-22 02:03:51 +08:00
parent 161a0896d5
commit 2432a646ce
4 changed files with 250 additions and 5 deletions

View File

@@ -52,7 +52,7 @@ void AudioSourceComponent::SetClip(Resources::AudioClip* clip) {
m_clip = clip;
m_isDecoded = false;
if (clip && clip->IsValid()) {
m_decodedData.resize(clip->GetAudioData().Size() / 2);
DecodeAudioData();
}
}
@@ -168,10 +168,60 @@ void AudioSourceComponent::OnDestroy() {
Stop();
}
void AudioSourceComponent::DecodeAudioData() {
if (!m_clip || !m_clip->IsValid()) {
return;
}
if (m_isDecoded) {
return;
}
const auto& audioData = m_clip->GetAudioData();
if (audioData.Empty()) {
return;
}
Audio::uint32 channels = m_clip->GetChannels();
Audio::uint32 bitsPerSample = m_clip->GetBitsPerSample();
uint32_t bytesPerSample = bitsPerSample / 8;
uint32_t totalSamples = static_cast<uint32_t>(audioData.Size()) / bytesPerSample;
m_decodedData.resize(totalSamples);
const uint8_t* rawData = audioData.Data();
if (bitsPerSample == 16) {
const int16_t* samples16 = reinterpret_cast<const int16_t*>(rawData);
for (uint32_t i = 0; i < totalSamples; ++i) {
m_decodedData[i] = samples16[i] / 32768.0f;
}
} else if (bitsPerSample == 8) {
for (uint32_t i = 0; i < totalSamples; ++i) {
m_decodedData[i] = (rawData[i] - 128) / 128.0f;
}
} else if (bitsPerSample == 24) {
for (uint32_t i = 0; i < totalSamples; ++i) {
int32_t sample = (rawData[i * 3] | (rawData[i * 3 + 1] << 8) | (rawData[i * 3 + 2] << 16));
if (sample & 0x800000) {
sample |= 0xFF000000;
}
m_decodedData[i] = sample / 8388608.0f;
}
} else if (bitsPerSample == 32) {
const int32_t* samples32 = reinterpret_cast<const int32_t*>(rawData);
for (uint32_t i = 0; i < totalSamples; ++i) {
m_decodedData[i] = samples32[i] / 2147483648.0f;
}
}
m_isDecoded = true;
}
void AudioSourceComponent::ProcessAudio(float* buffer, Audio::uint32 sampleCount, Audio::uint32 channels,
const Math::Vector3& listenerPosition,
const Math::Quaternion& listenerRotation) {
if (m_playState != Audio::PlayState::Playing || !m_clip) {
if (m_playState != Audio::PlayState::Playing || !m_clip || !m_isDecoded) {
return;
}
@@ -179,18 +229,53 @@ void AudioSourceComponent::ProcessAudio(float* buffer, Audio::uint32 sampleCount
return;
}
if (m_decodedData.empty()) {
return;
}
float volume = m_volume;
if (m_spatialize) {
Apply3DAttenuation(listenerPosition);
volume *= m_volume;
}
for (Audio::uint32 i = 0; i < sampleCount && i < BufferSize * 2; ++i) {
buffer[i] += m_outputBuffer[i] * volume;
Audio::uint32 clipChannels = m_clip->GetChannels();
Audio::uint64 totalSamples = static_cast<Audio::uint64>(m_decodedData.size());
Audio::uint64 samplesPerFrame = sampleCount * channels;
for (Audio::uint32 i = 0; i < sampleCount; ++i) {
for (Audio::uint32 ch = 0; ch < channels; ++ch) {
Audio::uint64 outputIndex = m_samplePosition + i * channels + ch;
if (outputIndex >= totalSamples) {
if (m_isLooping && totalSamples > 0) {
outputIndex = outputIndex % totalSamples;
} else {
buffer[i * channels + ch] += 0.0f;
continue;
}
}
Audio::uint64 decodedChannel = (ch < clipChannels) ? ch : (clipChannels - 1);
Audio::uint64 decodedIndex = (outputIndex / channels) * clipChannels + decodedChannel;
float sample = m_decodedData[decodedIndex];
buffer[i * channels + ch] += sample * volume;
}
}
m_samplePosition += samplesPerFrame;
if (m_samplePosition >= totalSamples) {
if (m_isLooping) {
m_samplePosition = m_samplePosition % totalSamples;
} else {
Stop();
}
}
if (m_isEnergyDetecting) {
UpdateEnergy(buffer, sampleCount);
UpdateEnergy(buffer, sampleCount * channels);
}
}