SourcePP
Several modern C++20 libraries for sanely parsing Valve's formats.
Loading...
Searching...
No Matches
WAV.h
Go to the documentation of this file.
1#pragma once
2
3#include <array>
4#include <cctype>
5#include <optional>
6
7#include <BufferStream.h>
8
9#include "RIFF.h"
10
11namespace sndpp {
12
14
15class WAV : public RIFF {
16public:
17 enum ChunkType : uint32_t {
20 CHUNK_CUE = sourcepp::parser::binary::makeFourCC("cue "), // Cue points / markers
21 CHUNK_PLST = sourcepp::parser::binary::makeFourCC("plst"), // Playlist for cue points
22 CHUNK_FACT = sourcepp::parser::binary::makeFourCC("fact"), // More track details
30 CHUNK_VDAT = sourcepp::parser::binary::makeFourCC("VDAT"), // Valve lipsync data
31
32 //CHUNK_LIST = sourcepp::parser::binary::makeFourCC("LIST"), // Metadata
33 //CHUNK_LIST_WAVL = sourcepp::parser::binary::makeFourCC("WAVL"), // Wave list
34
35 //CHUNK_LIST_INFO = sourcepp::parser::binary::makeFourCC("INFO"), // Overall metadata
36 //CHUNK_LIST_INFO_IARL = sourcepp::parser::binary::makeFourCC("IARL"), // Archive location
37 //CHUNK_LIST_INFO_IGNR = sourcepp::parser::binary::makeFourCC("IGNR"), // Genre
38 //CHUNK_LIST_INFO_IKEY = sourcepp::parser::binary::makeFourCC("IKEY"), // Keywords
39 //CHUNK_LIST_INFO_ILGT = sourcepp::parser::binary::makeFourCC("ILGT"), // Lightness
40 //CHUNK_LIST_INFO_IMED = sourcepp::parser::binary::makeFourCC("IMED"), // Medium
41 //CHUNK_LIST_INFO_INAM = sourcepp::parser::binary::makeFourCC("INAM"), // Title
42 //CHUNK_LIST_INFO_IPLT = sourcepp::parser::binary::makeFourCC("IPLT"), // Palette setting
43 //CHUNK_LIST_INFO_IPRD = sourcepp::parser::binary::makeFourCC("IPRD"), // Product
44 //CHUNK_LIST_INFO_ISBJ = sourcepp::parser::binary::makeFourCC("ISBJ"), // Subject
45 //CHUNK_LIST_INFO_ISFT = sourcepp::parser::binary::makeFourCC("ISFT"), // Software used
46 //CHUNK_LIST_INFO_ISHP = sourcepp::parser::binary::makeFourCC("ISHP"), // Sharpness
47 //CHUNK_LIST_INFO_IART = sourcepp::parser::binary::makeFourCC("IART"), // Artist
48 //CHUNK_LIST_INFO_ISRC = sourcepp::parser::binary::makeFourCC("ISRC"), // Source (file supplier)
49 //CHUNK_LIST_INFO_ISRF = sourcepp::parser::binary::makeFourCC("ISRF"), // Source form
50 //CHUNK_LIST_INFO_ITCH = sourcepp::parser::binary::makeFourCC("ITCH"), // Technician
51 //CHUNK_LIST_INFO_ICMS = sourcepp::parser::binary::makeFourCC("ICMS"), // Commissioner
52 //CHUNK_LIST_INFO_ICMT = sourcepp::parser::binary::makeFourCC("ICMT"), // Comments
53 //CHUNK_LIST_INFO_ICOP = sourcepp::parser::binary::makeFourCC("ICOP"), // Copyright
54 //CHUNK_LIST_INFO_ICRP = sourcepp::parser::binary::makeFourCC("ICRP"), // Crop
55 //CHUNK_LIST_INFO_IDPI = sourcepp::parser::binary::makeFourCC("IDPI"), // Dots per inch
56 //CHUNK_LIST_INFO_IENG = sourcepp::parser::binary::makeFourCC("IENG"), // Engineer
57 //CHUNK_LIST_INFO_ISMP = sourcepp::parser::binary::makeFourCC("ISMP"), // Digitization start time (SMPTE time code)
58 //CHUNK_LIST_INFO_IDIT = sourcepp::parser::binary::makeFourCC("IDIT"), // Digitization start time (26 characters, formatted like "Wed Jan 02 02:03:55 1990\n\0")
59 //CHUNK_LIST_INFO_ICRD = sourcepp::parser::binary::makeFourCC("ICRD"), // Creation date (YYYY, YYYY-MM, YYYY-MM-DD)
60 //CHUNK_LIST_INFO_ITRK = sourcepp::parser::binary::makeFourCC("ITRK"), // Track number
61
62 //CHUNK_LIST_ADTL = sourcepp::parser::binary::makeFourCC("adtl"), // Cue points metadata
63 //CHUNK_LIST_ADTL_LABL = sourcepp::parser::binary::makeFourCC("labl"), // Cue point label
64 //CHUNK_LIST_ADTL_NOTE = sourcepp::parser::binary::makeFourCC("note"), // Cue point note
65 //CHUNK_LIST_ADTL_LTXT = sourcepp::parser::binary::makeFourCC("ltxt"), // Section label
66 };
67
68 struct ChunkFMT {
69 uint16_t format;
70 uint16_t channels;
73 uint16_t blockAlign;
74 uint16_t bitsPerSample;
75 std::vector<std::byte> extraCompressionInfo;
76 };
77
78 struct ChunkCUE {
79 struct CuePoint {
80 uint32_t id;
81 int32_t position;
83 uint32_t chunkStart;
84 uint32_t blockStart;
85 uint32_t sampleOffset;
86 };
87
88 std::vector<CuePoint> cuePoints;
89 };
90
91 struct ChunkPLST {
92 struct Segment {
93 uint32_t id;
94 uint32_t length;
95 uint32_t repeats;
96 };
97
98 std::vector<Segment> segments;
99 };
100
101 struct ChunkFACT {
102 uint32_t samples;
103 std::vector<std::byte> extraCompressionInfo;
104 };
105
106 struct ChunkSMPL {
107 struct SampleLoop {
108 uint32_t id;
109 uint32_t type;
110 uint32_t start;
111 uint32_t end;
112 uint32_t fraction;
113 uint32_t playCount;
114 };
115
116 uint32_t manufacturer;
117 uint32_t product;
118 uint32_t samplePeriod;
121 uint32_t smpteFormat;
122 uint32_t smpteOffset;
123 uint32_t sampleLoops;
124 uint32_t samplerData;
125 std::vector<SampleLoop> loops;
126 };
127
128 struct ChunkINST {
130 int8_t fineTune;
131 int8_t gain;
132 uint8_t lowNote;
133 uint8_t highNote;
134 uint8_t lowVelocity;
136 };
137
138 struct ChunkCSET {
139 uint16_t codePage;
140 uint16_t countryCode;
141 uint16_t language;
142 uint16_t dialect;
143 };
144
145 struct ChunkMD5 {
146 std::array<uint8_t, 16> md5;
147 };
148
149 struct ChunkVDAT {
150 struct Emphasis {
151 float time;
152 float value;
153 };
154
155 struct Phoneme {
156 float startTime{};
157 float endTime{};
158 float volume{};
159 uint32_t code{};
160 std::string name;
161 };
162
163 struct Word {
164 float startTime{};
165 float endTime{};
166 std::string word;
167 std::vector<Phoneme> phonemes;
168 };
169
170 float version;
171 std::string plaintext;
172 std::vector<Word> words;
173 std::vector<Emphasis> emphasis;
174 std::unordered_map<std::string, std::string> options;
175 };
176
177 //struct ChunkLISTINFO {
178 // struct META {
179 // ChunkType infoType;
180 // std::string metadata;
181 // };
182 // std::vector<META> metadata;
183 //};
184
185 //struct ChunkLISTADTL {
186 // struct LTXT {
187 // uint32_t sampleLength;
188 // uint32_t purpose;
189 // uint16_t country;
190 // uint16_t language;
191 // uint16_t dialect;
192 // uint16_t codePage;
193 // std::string label;
194 // };
195 // std::vector<std::string> labels;
196 // std::vector<std::string> notes;
197 // std::vector<LTXT> labelTexts;
198 //};
199
200 explicit WAV(uint32_t signature = RIFF_SIGNATURE);
201
202 explicit WAV(std::vector<std::byte>&& wavData);
203
204 explicit WAV(std::span<const std::byte> wavData);
205
206 explicit WAV(const std::filesystem::path& wavPath);
207
208 template<ChunkType Type>
209 [[nodiscard]] auto getFirstWAVChunk() const {
210 return this->getNthWAVChunk<Type>(0);
211 }
212
213 template<ChunkType Type>
214 [[nodiscard]] auto getNthWAVChunk(uint32_t n) const {
215 auto chunkData = this->getNthChunk(Type, n);
216 BufferStreamReadOnly stream{chunkData};
217 if constexpr (Type == CHUNK_FMT) {
218 std::optional<ChunkFMT> chunk = std::nullopt;
219 if (chunkData.size() >= sizeof(uint32_t) * 2 + sizeof(uint16_t) * 4) {
220 chunk = ChunkFMT{};
221 const auto fmtExtraCompressionInfoLength = stream.size() - 16; // size of the standard FMT header
222 stream >> chunk->format >> chunk->channels >> chunk->samplesPerSecond >> chunk->averageBytesPerSecond >> chunk->blockAlign >> chunk->bitsPerSample;
223 if (fmtExtraCompressionInfoLength) {
224 chunk->extraCompressionInfo = stream.read_bytes(fmtExtraCompressionInfoLength);
225 }
226 }
227 return chunk;
228 } else if constexpr (Type == CHUNK_CUE) {
229 std::optional<ChunkCUE> chunk = std::nullopt;
230 if (stream.size() >= sizeof(uint32_t) && stream.size() - sizeof(uint32_t) % sizeof(ChunkCUE::CuePoint) == 0) {
231 chunk = ChunkCUE{};
232 const auto cueNumPoints = stream.read<uint32_t>();
233 for (uint32_t i = 0; i < cueNumPoints; i++) {
234 ChunkCUE::CuePoint point{};
235 stream >> point.id >> point.position >> point.chunk >> point.chunkStart >> point.blockStart >> point.sampleOffset;
236 chunk->cuePoints.push_back(point);
237 }
238 }
239 return chunk;
240 } else if constexpr (Type == CHUNK_PLST) {
241 std::optional<ChunkPLST> chunk = std::nullopt;
242 if (stream.size() >= sizeof(uint32_t) && (stream.size() - sizeof(uint32_t)) % sizeof(ChunkPLST::Segment) == 0) {
243 chunk = ChunkPLST{};
244 const auto plstNumSegments = stream.read<uint32_t>();
245 for (uint32_t i = 0; i < plstNumSegments; i++) {
246 ChunkPLST::Segment segment{};
247 stream >> segment.id >> segment.length >> segment.repeats;
248 chunk->segments.push_back(segment);
249 }
250 }
251 return chunk;
252 } else if constexpr (Type == CHUNK_FACT) {
253 std::optional<ChunkFACT> chunk = std::nullopt;
254 if (stream.size() >= sizeof(uint32_t)) {
255 chunk = ChunkFACT{};
256 const auto factExtraCompressionInfoLength = stream.size() - sizeof(uint32_t);
257 stream >> chunk->samples;
258 if (factExtraCompressionInfoLength) {
259 chunk->extraCompressionInfo = stream.read_bytes(factExtraCompressionInfoLength);
260 }
261 }
262 return chunk;
263 } else if constexpr (Type == CHUNK_SMPL) {
264 std::optional<ChunkSMPL> chunk = std::nullopt;
265 if (stream.size() >= sizeof(uint32_t) * 10) {
266 chunk = ChunkSMPL{};
267 stream >> chunk->manufacturer >> chunk->product >> chunk->samplePeriod >> chunk->midiUnityNote >> chunk->midiPitchFraction >> chunk->smpteFormat >> chunk->smpteOffset >> chunk->sampleLoops >> chunk->samplerData;
268 const auto smplNumLoops = stream.read<uint32_t>();
269 for (uint32_t i = 0; i < smplNumLoops; i++) {
270 // ReSharper disable once CppUseStructuredBinding
271 auto& sampleLoop = chunk->loops.emplace_back();
272 stream >> sampleLoop.id >> sampleLoop.type >> sampleLoop.start >> sampleLoop.end >> sampleLoop.fraction >> sampleLoop.playCount;
273 }
274 }
275 return chunk;
276 } else if constexpr (Type == CHUNK_INST) {
277 std::optional<ChunkINST> chunk = std::nullopt;
278 if (stream.size() == sizeof(uint8_t) * 7) {
279 chunk = ChunkINST{};
280 stream >> chunk->unshiftedNote >> chunk->fineTune >> chunk->gain >> chunk->lowNote >> chunk->highNote >> chunk->lowVelocity >> chunk->highVelocity;
281 }
282 return chunk;
283 } else if constexpr (Type == CHUNK_CSET) {
284 std::optional<ChunkCSET> chunk = std::nullopt;
285 if (stream.size() == sizeof(uint16_t) * 4) {
286 chunk = ChunkCSET{};
287 stream >> chunk->codePage >> chunk->countryCode >> chunk->language >> chunk->dialect;
288 }
289 return chunk;
290 } else if constexpr (Type == CHUNK_MD5) {
291 std::optional<ChunkMD5> chunk = std::nullopt;
292 if (stream.size() == sizeof(uint8_t) * 16) {
293 chunk = ChunkMD5{};
294 stream >> chunk->md5;
295 }
296 return chunk;
297 } else if constexpr (Type == CHUNK_VDAT) {
298 std::optional<ChunkVDAT> chunk = std::nullopt;
299 if (stream.size() > 0) {
300 chunk = ChunkVDAT{};
301 const auto getToken = [&stream] {
302 std::string token;
303 if (stream.tell() >= stream.size()) {
304 return token;
305 }
306 bool gotToken = false;
307 char c = stream.read<char>();
308 while (true) {
309 if (!gotToken && std::isspace(c)) {
310 // skip leading whitespace
311 } else if (!gotToken && !std::isspace(c)) {
312 gotToken = true;
313 token += c;
314 } else if (gotToken && !std::isspace(c)) {
315 token += c;
316 } else if (gotToken && std::isspace(c)) {
317 break;
318 }
319 c = stream.read<char>();
320 }
321 return token;
322 };
323 std::string token = getToken();
324 while (!token.empty()) {
325 if (token == "VERSION") {
326 token = getToken();
327 chunk->version = std::stof(token);
328 } else if (token == "PLAINTEXT") {
329 token = getToken(); // opening brace
330 token = getToken(); // first token
331 while (true) {
332 chunk->plaintext += token;
333 token = getToken();
334 if (token.empty() || token == "}")
335 break;
336 chunk->plaintext += ' ';
337 }
338 } else if (token == "WORDS") {
339 token = getToken(); // opening brace
340 token = getToken(); // first token
341 while (token == "WORD") {
342 ChunkVDAT::Word word;
343 word.word = getToken();
344 word.startTime = std::stof(getToken());
345 word.endTime = std::stof(getToken());
346 token = getToken(); // opening brace
347 token = getToken(); // first token
348 while (token != "}") {
349 ChunkVDAT::Phoneme phoneme;
350 phoneme.code = std::stoi(token);
351 phoneme.name = getToken();
352 phoneme.startTime = std::stof(getToken());
353 phoneme.endTime = std::stof(getToken());
354 phoneme.volume = std::stof(getToken());
355 word.phonemes.push_back(phoneme);
356 token = getToken();
357 }
358 chunk->words.push_back(word);
359 token = getToken();
360 }
361 } else if (token == "EMPHASIS") {
362 token = getToken(); // opening brace
363 token = getToken(); // first token
364 while (token != "}") {
365 ChunkVDAT::Emphasis emphasis{};
366 emphasis.time = std::stof(token);
367 emphasis.value = std::stof(getToken());
368 chunk->emphasis.push_back(emphasis);
369 token = getToken();
370 }
371 } else if (token == "OPTIONS") {
372 token = getToken(); // opening brace
373 token = getToken(); // first token
374 while (token != "}") {
375 chunk->options[token] = getToken();
376 token = getToken();
377 }
378 }
379 token = getToken();
380 }
381 }
382 return chunk;
383 } else {
384 return this->hasNthChunk(Type, n) ? std::optional{std::move(chunkData)} : std::nullopt;
385 }
386 }
387};
388
389} // namespace sndpp
std::vector< std::byte > getNthChunk(uint32_t chunkType, uint32_t n) const
Definition RIFF.cpp:91
uint32_t signature
Definition RIFF.h:43
RIFF(uint32_t type, uint32_t signature=RIFF_SIGNATURE)
Definition RIFF.cpp:11
bool hasNthChunk(uint32_t chunkType, uint32_t n) const
Definition RIFF.cpp:84
WAV(uint32_t signature=RIFF_SIGNATURE)
Definition WAV.cpp:5
ChunkType
Definition WAV.h:17
@ CHUNK_FACT
Definition WAV.h:22
@ CHUNK_JUNK
Definition WAV.h:28
@ CHUNK_PLST
Definition WAV.h:21
@ CHUNK_FMT
Definition WAV.h:18
@ CHUNK_MD5
Definition WAV.h:26
@ CHUNK_SMPL
Definition WAV.h:23
@ CHUNK_CSET
Definition WAV.h:25
@ CHUNK_DATA
Definition WAV.h:19
@ CHUNK_PAD
Definition WAV.h:27
@ CHUNK_FLLR
Definition WAV.h:29
@ CHUNK_INST
Definition WAV.h:24
@ CHUNK_CUE
Definition WAV.h:20
@ CHUNK_VDAT
Definition WAV.h:30
auto getFirstWAVChunk() const
Definition WAV.h:209
auto getNthWAVChunk(uint32_t n) const
Definition WAV.h:214
Definition RIFF.h:10
constexpr auto RIFF_SIGNATURE
Definition RIFF.h:12
constexpr auto WAV_TYPE
Definition WAV.h:13
consteval uint32_t makeFourCC(const char fourCC[4])
Creates a FourCC identifier from a string of 4 characters.
Definition Binary.h:20
uint16_t codePage
Definition WAV.h:139
uint16_t dialect
Definition WAV.h:142
uint16_t language
Definition WAV.h:141
uint16_t countryCode
Definition WAV.h:140
std::vector< CuePoint > cuePoints
Definition WAV.h:88
std::vector< std::byte > extraCompressionInfo
Definition WAV.h:103
uint32_t samples
Definition WAV.h:102
uint16_t channels
Definition WAV.h:70
uint16_t format
Definition WAV.h:69
uint32_t samplesPerSecond
Definition WAV.h:71
uint16_t bitsPerSample
Definition WAV.h:74
uint16_t blockAlign
Definition WAV.h:73
std::vector< std::byte > extraCompressionInfo
Definition WAV.h:75
uint32_t averageBytesPerSecond
Definition WAV.h:72
uint8_t highVelocity
Definition WAV.h:135
uint8_t unshiftedNote
Definition WAV.h:129
uint8_t highNote
Definition WAV.h:133
uint8_t lowNote
Definition WAV.h:132
uint8_t lowVelocity
Definition WAV.h:134
std::array< uint8_t, 16 > md5
Definition WAV.h:146
std::vector< Segment > segments
Definition WAV.h:98
uint32_t product
Definition WAV.h:117
uint32_t midiUnityNote
Definition WAV.h:119
uint32_t samplerData
Definition WAV.h:124
std::vector< SampleLoop > loops
Definition WAV.h:125
uint32_t smpteFormat
Definition WAV.h:121
uint32_t midiPitchFraction
Definition WAV.h:120
uint32_t sampleLoops
Definition WAV.h:123
uint32_t samplePeriod
Definition WAV.h:118
uint32_t manufacturer
Definition WAV.h:116
uint32_t smpteOffset
Definition WAV.h:122
std::vector< Phoneme > phonemes
Definition WAV.h:167
std::unordered_map< std::string, std::string > options
Definition WAV.h:174
std::vector< Word > words
Definition WAV.h:172
std::vector< Emphasis > emphasis
Definition WAV.h:173
std::string plaintext
Definition WAV.h:171