SourcePP
Several modern C++20 libraries for sanely parsing Valve's formats.
Loading...
Searching...
No Matches
WAV.cpp
Go to the documentation of this file.
1#include <sndpp/WAV.h>
2
3#include <utility>
4
5#include <BufferStream.h>
6#include <sourcepp/FS.h>
7
8using namespace sndpp;
9using namespace sourcepp;
10
11WAV::WAV(std::span<const std::byte> wavData) {
12 goto start;
13fail:
14 this->signature = 0;
15 return;
16start:
17 BufferStreamReadOnly stream{wavData.data(), wavData.size()};
18
19 this->signature = stream.read<uint32_t>();
20 if (this->signature == RIFX_SIGNATURE || this->signature == FFIR_SIGNATURE) {
21 stream.set_big_endian(true);
22 } else if (this->signature != RIFF_SIGNATURE) {
23 goto fail;
24 }
25
26 const auto totalLength = stream.read<uint32_t>();
27 if (stream.read<uint32_t>() != RIFF_TYPE) {
28 goto fail;
29 }
30
31 while (stream.tell() < totalLength) {
32 std::unique_ptr<ChunkBase> ptr;
33
34 #define SNDPP_CHUNK_SETUP(ID) \
35 auto* ID = new Chunk##ID; \
36 ID->type = type; \
37 ptr.reset(ID)
38
39 switch (const auto type = stream.read<ChunkType>()) {
40 using enum ChunkType;
41 case FMT: {
43 const auto fmtExtraCompressionInfoLength = stream.read<uint32_t>() - 16; // size of the standard FMT header
44 stream >> FMT->format >> FMT->channels >> FMT->samplesPerSecond >> FMT->averageBytesPerSecond >> FMT->blockAlign >> FMT->bitsPerSample;
45 if (fmtExtraCompressionInfoLength) {
46 FMT->extraCompressionInfo = stream.read_bytes(fmtExtraCompressionInfoLength);
47 }
48 break;
49 }
50 case DATA: {
52 DATA->data = stream.read_bytes(stream.read<uint32_t>());
53 break;
54 }
55 case CUE: {
57 if ((stream.read<uint32_t>() - sizeof(uint32_t)) % sizeof(ChunkCUE::CuePoint) != 0) {
58 goto fail;
59 }
60 const auto cueNumPoints = stream.read<uint32_t>();
61 for (uint32_t i = 0; i < cueNumPoints; i++) {
62 ChunkCUE::CuePoint point{};
63 stream >> point.id >> point.position >> point.chunk >> point.chunkStart >> point.blockStart >> point.sampleOffset;
64 CUE->cuePoints.push_back(point);
65 }
66 break;
67 }
68 case PLST: {
70 if ((stream.read<uint32_t>() - sizeof(uint32_t)) % sizeof(ChunkPLST::Segment) != 0) {
71 goto fail;
72 }
73 const auto plstNumSegments = stream.read<uint32_t>();
74 for (uint32_t i = 0; i < plstNumSegments; i++) {
75 ChunkPLST::Segment segment{};
76 stream >> segment.id >> segment.length >> segment.repeats;
77 PLST->segments.push_back(segment);
78 }
79 break;
80 }
81 case FACT: {
83 const auto factExtraCompressionInfoLength = stream.read<uint32_t>() - sizeof(uint32_t);
84 stream >> FACT->samples;
85 if (factExtraCompressionInfoLength) {
86 FACT->extraCompressionInfo = stream.read_bytes(factExtraCompressionInfoLength);
87 }
88 break;
89 }
90 case SMPL: {
92 stream.skip<uint32_t>();
93 stream >> SMPL->manufacturer >> SMPL->product >> SMPL->samplePeriod >> SMPL->midiUnityNote >> SMPL->midiPitchFraction
94 >> SMPL->smpteFormat >> SMPL->smpteOffset >> SMPL->sampleLoops >> SMPL->samplerData;
95 const auto smplNumLoops = stream.read<uint32_t>();
96 for (uint32_t i = 0; i < smplNumLoops; i++) {
97 ChunkSMPL::SampleLoop sampleLoop{};
98 stream >> sampleLoop.id >> sampleLoop.type >> sampleLoop.start >> sampleLoop.end >> sampleLoop.fraction >> sampleLoop.playCount;
99 SMPL->loops.push_back(sampleLoop);
100 }
101 break;
102 }
103 case INST: {
105 if (stream.read<uint32_t>() != sizeof(ChunkINST)) {
106 goto fail;
107 }
108 stream >> INST->unshiftedNote >> INST->fineTune >> INST->gain >> INST->lowNote >> INST->highNote >> INST->lowVelocity >> INST->highVelocity;
109 break;
110 }
111 case CSET: {
113 if (stream.read<uint32_t>() != sizeof(ChunkCSET)) {
114 goto fail;
115 }
116 stream >> CSET->codePage >> CSET->countryCode >> CSET->language >> CSET->dialect;
117 break;
118 }
119 case MD5: {
121 if (stream.read<uint32_t>() != sizeof(ChunkMD5::md5)) {
122 goto fail;
123 }
124 stream >> MD5->md5;
125 break;
126 }
127 case JUNK: {
129 JUNK->junk = stream.read_bytes(stream.read<uint32_t>());
130 break;
131 }
132 case PAD: {
134 PAD->padding = stream.read_bytes(stream.read<uint32_t>());
135 break;
136 }
137 case FLLR: {
139 FLLR->filler = stream.read_bytes(stream.read<uint32_t>());
140 break;
141 }
142 case LIST: {
143 const auto listLength = stream.read<uint32_t>();
144 // todo(sndpp): LIST subchunks are using the LIST id thanks to the macro, is this correct?
145 switch (stream.read<ChunkListType>()) {
146 using enum ChunkListType;
147 default: // Not in the "spec"
148 case WAVL: // No wave lists!
149 goto fail;
150 case INFO: {
152 const auto infoStart = stream.tell() + listLength - sizeof(ChunkListInfoType);
153 while (stream.tell() < infoStart) {
154 const auto infoType = stream.read<ChunkListInfoType>();
155 const auto metadata = stream.read_string(stream.read<uint32_t>());
156 INFO->metadata.push_back({infoType, metadata});
157 }
158 break;
159 }
160 case ADTL: {
162 const auto adtlStart = stream.tell() + listLength - sizeof(ChunkListADTLType);
163 while (stream.tell() < adtlStart) {
164 switch (stream.read<ChunkListADTLType>()) {
165 using enum ChunkListADTLType;
166 case LABL: {
167 const auto labelLength = stream.read<uint32_t>();
168 const auto labelID = stream.read<uint32_t>();
169 const auto labelText = stream.read_string(labelLength - sizeof(uint32_t));
170 ADTL->labels.push_back({labelID, labelText});
171 break;
172 }
173 case NOTE: {
174 const auto noteLength = stream.read<uint32_t>();
175 const auto noteID = stream.read<uint32_t>();
176 const auto noteText = stream.read_string(noteLength - sizeof(uint32_t));
177 ADTL->notes.push_back({noteID, noteText});
178 break;
179 }
180 case LTXT: {
181 const auto ltxtLength = stream.read<uint32_t>();
182 ChunkADTL::LTXT ltxt;
183 stream >> ltxt.id >> ltxt.sampleLength >> ltxt.purpose >> ltxt.country >> ltxt.language >> ltxt.dialect >> ltxt.codePage;
184 ltxt.label = stream.read_string(ltxtLength - (sizeof(ChunkADTL::LTXT) - sizeof(std::string)));
185 ADTL->labelTexts.push_back(ltxt);
186 break;
187 }
188 }
189 }
190 break;
191 }
192 }
193 }
194 default: {
195 SNDPP_CHUNK_SETUP(Unknown);
196 Unknown->data = stream.read_bytes(stream.read<uint32_t>());
197 break;
198 }
199 }
200
201 #undef SNDPP_CHUNK_SETUP
202
203 if (ptr) {
204 this->chunks.push_back(std::move(ptr));
205 }
206
207 if (stream.tell() % 2 != 0) {
208 stream.skip();
209 }
210 }
211
212 // FMT chunk and DATA chunk are required
214 goto fail;
215 }
216}
217
219 : signature(RIFF_SIGNATURE) {}
220
221WAV::WAV(const std::string& wavPath)
222 : WAV(fs::readFileBuffer(wavPath)) {}
223
224WAV::operator bool() const {
225 return this->signature;
226}
227
228const std::vector<std::unique_ptr<WAV::ChunkBase>>& WAV::getChunks() const {
229 return this->chunks;
230}
231
232std::vector<std::unique_ptr<WAV::ChunkBase>>& WAV::getChunks() {
233 return this->chunks;
234}
235
237 if (const auto it = std::ranges::find_if(this->chunks, [type](const std::unique_ptr<ChunkBase>& chunk) {
238 return chunk->type == type;
239 }); it != this->chunks.end()) {
240 return it->get();
241 }
242 return nullptr;
243}
244
245uint32_t WAV::getSignature() const {
246 return this->signature;
247}
248
249void WAV::setSignature(uint32_t newSignature) {
250 this->signature = newSignature;
251}
252
253#define SNDPP_CHUNK_GETTER(TYPE) \
254 const WAV::Chunk##TYPE* WAV::getChunk##TYPE() const { \
255 return dynamic_cast<const Chunk##TYPE*>(this->getFirstChunk(WAV::ChunkType::TYPE)); \
256 }
257
271
272#undef SNDPP_CHUNK_GETTER
#define SNDPP_CHUNK_GETTER(TYPE)
Definition: WAV.cpp:253
#define SNDPP_CHUNK_SETUP(ID)
Definition: WAV.h:18
ChunkListADTLType
Definition: WAV.h:72
std::vector< std::unique_ptr< ChunkBase > > chunks
Definition: WAV.h:256
const std::vector< std::unique_ptr< ChunkBase > > & getChunks() const
Definition: WAV.cpp:228
const ChunkBase * getFirstChunk(WAV::ChunkType type) const
Definition: WAV.cpp:236
ChunkListInfoType
Definition: WAV.h:44
uint32_t getSignature() const
Definition: WAV.cpp:245
void setSignature(uint32_t newSignature)
Definition: WAV.cpp:249
ChunkListType
Definition: WAV.h:38
static constexpr auto RIFF_TYPE
Definition: WAV.h:20
ChunkType
Definition: WAV.h:22
uint32_t signature
Definition: WAV.h:257
Definition: WAV.h:12
constexpr auto FFIR_SIGNATURE
Definition: WAV.h:16
constexpr auto RIFX_SIGNATURE
Definition: WAV.h:15
constexpr auto RIFF_SIGNATURE
Definition: WAV.h:14
Definition: LZMA.h:11
std::array< uint8_t, 16 > md5
Definition: WAV.h:166