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