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 const auto type = stream.read<ChunkType>();
40 switch (type) {
41 using enum ChunkType;
42 case FMT: {
44 // NOLINTNEXTLINE(*-sizeof-container)
45 const auto fmtExtraCompressionInfoLength = stream.read<uint32_t>() - 16; // size of the standard FMT header
46 stream >> FMT->format >> FMT->channels >> FMT->samplesPerSecond >> FMT->averageBytesPerSecond >> FMT->blockAlign >> FMT->bitsPerSample;
47 if (fmtExtraCompressionInfoLength) {
48 FMT->extraCompressionInfo = stream.read_bytes(fmtExtraCompressionInfoLength);
49 }
50 break;
51 }
52 case DATA: {
54 DATA->data = stream.read_bytes(stream.read<uint32_t>());
55 break;
56 }
57 case CUE: {
59 if ((stream.read<uint32_t>() - sizeof(uint32_t)) % sizeof(ChunkCUE::CuePoint) != 0) {
60 goto fail;
61 }
62 const auto cueNumPoints = stream.read<uint32_t>();
63 for (uint32_t i = 0; i < cueNumPoints; i++) {
64 ChunkCUE::CuePoint point{};
65 stream >> point.id >> point.position >> point.chunk >> point.chunkStart >> point.blockStart >> point.sampleOffset;
66 CUE->cuePoints.push_back(point);
67 }
68 break;
69 }
70 case PLST: {
72 if ((stream.read<uint32_t>() - sizeof(uint32_t)) % sizeof(ChunkPLST::Segment) != 0) {
73 goto fail;
74 }
75 const auto plstNumSegments = stream.read<uint32_t>();
76 for (uint32_t i = 0; i < plstNumSegments; i++) {
77 ChunkPLST::Segment segment{};
78 stream >> segment.id >> segment.length >> segment.repeats;
79 PLST->segments.push_back(segment);
80 }
81 break;
82 }
83 case FACT: {
85 const auto factExtraCompressionInfoLength = stream.read<uint32_t>() - sizeof(uint32_t);
86 stream >> FACT->samples;
87 if (factExtraCompressionInfoLength) {
88 FACT->extraCompressionInfo = stream.read_bytes(factExtraCompressionInfoLength);
89 }
90 break;
91 }
92 case SMPL: {
94 stream.skip<uint32_t>();
95 stream >> SMPL->manufacturer >> SMPL->product >> SMPL->samplePeriod >> SMPL->midiUnityNote >> SMPL->midiPitchFraction
96 >> SMPL->smpteFormat >> SMPL->smpteOffset >> SMPL->sampleLoops >> SMPL->samplerData;
97 const auto smplNumLoops = stream.read<uint32_t>();
98 for (uint32_t i = 0; i < smplNumLoops; i++) {
99 ChunkSMPL::SampleLoop sampleLoop{};
100 stream >> sampleLoop.id >> sampleLoop.type >> sampleLoop.start >> sampleLoop.end >> sampleLoop.fraction >> sampleLoop.playCount;
101 SMPL->loops.push_back(sampleLoop);
102 }
103 break;
104 }
105 case INST: {
107 if (stream.read<uint32_t>() != sizeof(ChunkINST)) {
108 goto fail;
109 }
110 stream >> INST->unshiftedNote >> INST->fineTune >> INST->gain >> INST->lowNote >> INST->highNote >> INST->lowVelocity >> INST->highVelocity;
111 break;
112 }
113 case CSET: {
115 if (stream.read<uint32_t>() != sizeof(ChunkCSET)) {
116 goto fail;
117 }
118 stream >> CSET->codePage >> CSET->countryCode >> CSET->language >> CSET->dialect;
119 break;
120 }
121 case MD5: {
123 if (stream.read<uint32_t>() != sizeof(ChunkMD5::md5)) {
124 goto fail;
125 }
126 stream >> MD5->md5;
127 break;
128 }
129 case JUNK: {
131 JUNK->junk = stream.read_bytes(stream.read<uint32_t>());
132 break;
133 }
134 case PAD: {
136 PAD->padding = stream.read_bytes(stream.read<uint32_t>());
137 break;
138 }
139 case FLLR: {
141 FLLR->filler = stream.read_bytes(stream.read<uint32_t>());
142 break;
143 }
144 case LIST: {
145 const auto listLength = stream.read<uint32_t>();
146 const auto listType = stream.read<ChunkListType>();
147 switch (listType) {
148 using enum ChunkListType;
149 default: // Not in the "spec"
150 case WAVL: // No wave lists!
151 goto fail;
152 case INFO: {
154 const auto infoStart = stream.tell() + listLength - sizeof(ChunkListInfoType);
155 while (stream.tell() < infoStart) {
156 const auto infoType = stream.read<ChunkListInfoType>();
157 const auto metadata = stream.read_string(stream.read<uint32_t>());
158 INFO->metadata.push_back({infoType, metadata});
159 }
160 break;
161 }
162 case ADTL: {
164 const auto adtlStart = stream.tell() + listLength - sizeof(ChunkListADTLType);
165 while (stream.tell() < adtlStart) {
166 const auto adtlType = stream.read<ChunkListADTLType>();
167 switch (adtlType) {
168 using enum ChunkListADTLType;
169 case LABL: {
170 const auto labelLength = stream.read<uint32_t>();
171 const auto labelID = stream.read<uint32_t>();
172 const auto labelText = stream.read_string(labelLength - sizeof(uint32_t));
173 ADTL->labels.push_back({labelID, labelText});
174 break;
175 }
176 case NOTE: {
177 const auto noteLength = stream.read<uint32_t>();
178 const auto noteID = stream.read<uint32_t>();
179 const auto noteText = stream.read_string(noteLength - sizeof(uint32_t));
180 ADTL->notes.push_back({noteID, noteText});
181 break;
182 }
183 case LTXT: {
184 const auto ltxtLength = stream.read<uint32_t>();
185 ChunkADTL::LTXT ltxt;
186 stream >> ltxt.id >> ltxt.sampleLength >> ltxt.purpose >> ltxt.country >> ltxt.language >> ltxt.dialect >> ltxt.codePage;
187 ltxt.label = stream.read_string(ltxtLength - (sizeof(ChunkADTL::LTXT) - sizeof(std::string)));
188 ADTL->labelTexts.push_back(ltxt);
189 break;
190 }
191 }
192 }
193 break;
194 }
195 }
196 }
197 default: {
198 SNDPP_CHUNK_SETUP(Unknown);
199 Unknown->data = stream.read_bytes(stream.read<uint32_t>());
200 break;
201 }
202 }
203
204 #undef SNDPP_CHUNK_SETUP
205
206 if (ptr) {
207 this->chunks.push_back(std::move(ptr));
208 }
209
210 if (stream.tell() % 2 != 0) {
211 stream.skip();
212 }
213 }
214
215 // FMT chunk and DATA chunk are required
217 goto fail;
218 }
219}
220
222 : signature(RIFF_SIGNATURE) {}
223
224WAV::WAV(const std::string& wavPath)
225 : WAV(fs::readFileBuffer(wavPath)) {}
226
227WAV::operator bool() const {
228 return this->signature;
229}
230
231const std::vector<std::unique_ptr<WAV::ChunkBase>>& WAV::getChunks() const {
232 return this->chunks;
233}
234
235std::vector<std::unique_ptr<WAV::ChunkBase>>& WAV::getChunks() {
236 return this->chunks;
237}
238
240 if (const auto it = std::find_if(this->chunks.begin(), this->chunks.end(), [type](const std::unique_ptr<ChunkBase>& chunk) {
241 return chunk->type == type;
242 }); it != this->chunks.end()) {
243 return it->get();
244 }
245 return nullptr;
246}
247
248uint32_t WAV::getSignature() const {
249 return this->signature;
250}
251
252void WAV::setSignature(uint32_t newSignature) {
253 this->signature = newSignature;
254}
255
256#define SNDPP_CHUNK_GETTER(TYPE) \
257 const WAV::Chunk##TYPE* WAV::getChunk##TYPE() const { \
258 return dynamic_cast<const Chunk##TYPE*>(this->getFirstChunk(WAV::ChunkType::TYPE)); \
259 }
260
274
275#undef SNDPP_CHUNK_GETTER
#define SNDPP_CHUNK_GETTER(TYPE)
Definition: WAV.cpp:256
#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:231
const ChunkBase * getFirstChunk(WAV::ChunkType type) const
Definition: WAV.cpp:239
ChunkListInfoType
Definition: WAV.h:44
uint32_t getSignature() const
Definition: WAV.cpp:248
void setSignature(uint32_t newSignature)
Definition: WAV.cpp:252
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