mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2026-10-23 20:18:10 +00:00
fix: correct WAV RIFF length and padding (#1032)
* fix: correct WAV RIFF length and padding Include the PCM payload in the RIFF chunk size and pad odd-length data chunks. Add regression coverage for empty, odd, even and larger payloads. * chore: add comment explaning the wav padding byte --------- Co-authored-by: Jan Laupetin <[email protected]>
This commit is contained in:
1 parent
30d2d498bd
commit
e95805bce4
2 files changed
+47
-2
No files matched your search
@@ -7,8 +7,8 @@ WavWriter::WavWriter(std::ostream& stream)
|
||||
|
||||
void WavWriter::WritePcmHeader(const WavMetaData& metaData, const size_t dataLen) const
|
||||
{
|
||||
constexpr auto riffMasterChunkSize = static_cast<uint32_t>(sizeof(WAV_CHUNK_ID_RIFF) + sizeof(uint32_t) + sizeof(WAV_WAVE_ID) + sizeof(WavChunkHeader)
|
||||
+ sizeof(WavFormatChunkPcm) + sizeof(WavChunkHeader) + sizeof(uint32_t));
|
||||
const auto riffMasterChunkSize =
|
||||
static_cast<uint32_t>(sizeof(WAV_WAVE_ID) + sizeof(WavChunkHeader) + sizeof(WavFormatChunkPcm) + sizeof(WavChunkHeader) + dataLen + (dataLen & 1u));
|
||||
|
||||
m_stream.write(reinterpret_cast<const char*>(&WAV_CHUNK_ID_RIFF), sizeof(WAV_CHUNK_ID_RIFF));
|
||||
m_stream.write(reinterpret_cast<const char*>(&riffMasterChunkSize), sizeof(riffMasterChunkSize));
|
||||
@@ -34,4 +34,9 @@ void WavWriter::WritePcmHeader(const WavMetaData& metaData, const size_t dataLen
|
||||
void WavWriter::WritePcmData(const void* data, const size_t dataLen) const
|
||||
{
|
||||
m_stream.write(static_cast<const char*>(data), static_cast<std::streamsize>(dataLen));
|
||||
|
||||
// > The data is always padded to the nearest WORD boundary.
|
||||
// https://learn.microsoft.com/en-us/windows/win32/xaudio2/resource-interchange-file-format--riff-#chunks
|
||||
if (dataLen & 1u)
|
||||
m_stream.put('\0');
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "Sound/WavWriter.h"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/generators/catch_generators.hpp>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
TEST_CASE("WavWriter: RIFF contains the complete PCM payload and alignment padding", "[sound][wav]")
|
||||
{
|
||||
const auto size = GENERATE(0uz, 1uz, 2uz, 31uz, 2048uz);
|
||||
const std::string samples(size, static_cast<char>(0x81));
|
||||
std::ostringstream output(std::ios::binary);
|
||||
const WavWriter writer(output);
|
||||
writer.WritePcmHeader({1, 22050, 8}, samples.size());
|
||||
writer.WritePcmData(samples.data(), samples.size());
|
||||
const auto bytes = output.str();
|
||||
REQUIRE(bytes.size() >= sizeof(WavHeader));
|
||||
WavHeader header{};
|
||||
std::memcpy(&header, bytes.data(), sizeof(header));
|
||||
CHECK(header.chunkIdRiff == WAV_CHUNK_ID_RIFF);
|
||||
CHECK(header.chunkIdSize == bytes.size() - 8u);
|
||||
CHECK(header.format == WAV_WAVE_ID);
|
||||
CHECK(header.chunkHeader.chunkID == WAV_CHUNK_ID_FMT);
|
||||
CHECK(header.chunkHeader.chunkSize == sizeof(WavFormatChunkPcm));
|
||||
CHECK(header.formatChunk.wFormatTag == WavFormat::PCM);
|
||||
CHECK(header.formatChunk.nChannels == 1u);
|
||||
CHECK(header.formatChunk.nSamplesPerSec == 22050u);
|
||||
CHECK(header.formatChunk.nAvgBytesPerSec == 22050u);
|
||||
CHECK(header.formatChunk.nBlockAlign == 1u);
|
||||
CHECK(header.formatChunk.wBitsPerSample == 8u);
|
||||
CHECK(header.subChunkHeader.chunkID == WAV_CHUNK_ID_DATA);
|
||||
REQUIRE(header.subChunkHeader.chunkSize == samples.size());
|
||||
CHECK(bytes.substr(sizeof(header), samples.size()) == samples);
|
||||
CHECK(bytes.size() % 2u == 0u);
|
||||
if (samples.size() & 1u)
|
||||
CHECK(bytes.back() == '\0');
|
||||
else
|
||||
CHECK(bytes.size() == sizeof(header) + samples.size());
|
||||
}
|
||||
Reference in new issue
Block a user