#pragma once #include #include #include #include #include #include struct PcmWav { int sample_rate = 0; std::vector samples; }; inline uint16_t ReadLe16(std::istream &input) { uint8_t bytes[2]{}; input.read(reinterpret_cast(bytes), 2); return static_cast(bytes[0]) | (static_cast(bytes[1]) << 8); } inline uint32_t ReadLe32(std::istream &input) { uint8_t bytes[4]{}; input.read(reinterpret_cast(bytes), 4); return static_cast(bytes[0]) | (static_cast(bytes[1]) << 8) | (static_cast(bytes[2]) << 16) | (static_cast(bytes[3]) << 24); } inline PcmWav ReadPcmWav(const std::string &path) { std::ifstream input(path, std::ios::binary); if (!input) throw std::runtime_error("Cannot open WAV: " + path); char riff[4]{}; char wave[4]{}; input.read(riff, 4); ReadLe32(input); input.read(wave, 4); if (std::memcmp(riff, "RIFF", 4) != 0 || std::memcmp(wave, "WAVE", 4) != 0) { throw std::runtime_error("Invalid RIFF/WAVE file: " + path); } uint16_t format = 0; uint16_t channels = 0; uint16_t bits = 0; uint32_t sample_rate = 0; std::vector data; while (input && (!format || data.empty())) { char id[4]{}; input.read(id, 4); if (!input) break; const uint32_t size = ReadLe32(input); if (std::memcmp(id, "fmt ", 4) == 0) { if (size < 16) throw std::runtime_error("Invalid WAV fmt chunk"); format = ReadLe16(input); channels = ReadLe16(input); sample_rate = ReadLe32(input); ReadLe32(input); ReadLe16(input); bits = ReadLe16(input); input.seekg(size - 16, std::ios::cur); } else if (std::memcmp(id, "data", 4) == 0) { data.resize(size); input.read(reinterpret_cast(data.data()), size); } else { input.seekg(size, std::ios::cur); } if (size & 1U) input.seekg(1, std::ios::cur); } if (format != 1 || channels != 1 || bits != 16 || sample_rate == 0 || data.empty() || data.size() % sizeof(int16_t) != 0) { throw std::runtime_error("Expected mono 16-bit PCM WAV: " + path); } PcmWav result; result.sample_rate = static_cast(sample_rate); result.samples.resize(data.size() / sizeof(int16_t)); std::memcpy(result.samples.data(), data.data(), data.size()); return result; }