DSPark 1.8.0
Header-only C++20 DSP for real-time and offline audio
Loading...
Searching...
No Matches
FlacFile.h
1// DSPark - Professional Audio DSP Framework
2// Copyright (c) 2026 Cristian Moresi - MIT License
3
4#pragma once
5
25#include "AudioFile.h"
26
27#include <algorithm>
28#include <array>
29#include <cmath>
30#include <cstddef>
31#include <cstdint>
32#include <cstring>
33#include <filesystem>
34#include <fstream>
35#include <limits>
36#include <span>
37#include <string_view>
38#include <utility>
39#include <vector>
40
41namespace dspark {
42
47class FlacFile : public AudioFile
48{
49public:
51 static constexpr uint64_t kMaxInputBytes = 256ull * 1024 * 1024;
53 static constexpr uint64_t kMaxMetadataBlocks = 65536;
55 static constexpr uint64_t kMaxFrames = 1048576;
57 static constexpr uint64_t kMaxInterchannelSamples = 1ull << 31;
59 static constexpr uint64_t kMaxDecodedPcmBytes = 8ull << 30;
61 static constexpr uint64_t kMaxRiceUnaryZeros = 1ull << 20;
63 static constexpr std::string_view kWriteUnsupportedReason =
64 "FlacFile is decode-only; FLAC encoding is not supported.";
65
66 ~FlacFile() override { close(); }
67
72 [[nodiscard]] bool openRead(const std::filesystem::path& path) override
73 {
74 close();
75
76 std::ifstream input(path, std::ios::binary | std::ios::ate);
77 if (!input.is_open())
78 return false;
79 const std::streampos end = input.tellg();
80 if (end <= std::streampos(0))
81 return false;
82 const uint64_t fileSize = static_cast<uint64_t>(end);
83 if (fileSize > kMaxInputBytes
84 || fileSize > static_cast<uint64_t>(std::numeric_limits<size_t>::max()))
85 return false;
86
87 OpenState candidate;
88 candidate.bytes.resize(static_cast<size_t>(fileSize));
89 input.seekg(0, std::ios::beg);
90 input.read(reinterpret_cast<char*>(candidate.bytes.data()),
91 static_cast<std::streamsize>(candidate.bytes.size()));
92 if (input.gcount() != static_cast<std::streamsize>(candidate.bytes.size()))
93 return false;
94 if (!validateOpen(candidate))
95 return false;
96
97 bytes_ = std::move(candidate.bytes);
98 frames_ = std::move(candidate.frames);
99 stream_ = candidate.stream;
100 info_ = candidate.info;
101 open_ = true;
102 return true;
103 }
104
110 [[nodiscard]] bool openWrite(const std::filesystem::path& path,
111 const AudioFileInfo& info) override
112 {
113 (void)path;
114 (void)info;
115 close();
116 return false;
117 }
118
120 [[nodiscard]] AudioFileInfo getInfo() const override { return info_; }
121
123 [[nodiscard]] bool readSamples(AudioBufferView<float> dest) override
124 {
125 if (!open_) return false;
126 return readSamples(dest, 0, info_.numSamples);
127 }
128
135 [[nodiscard]] bool readSamples(AudioBufferView<float> dest,
136 int64_t startFrame,
137 int64_t numFrames) override
138 {
139 if (!open_ || startFrame < 0 || numFrames <= 0)
140 return false;
141 if (startFrame > info_.numSamples
142 || numFrames > info_.numSamples - startFrame)
143 return false;
144
145 const int destChannels = std::max(0, dest.getNumChannels());
146 const int destSamples = std::max(0, dest.getNumSamples());
147 const int channelsToCopy = std::min(destChannels,
148 static_cast<int>(info_.numChannels));
149 const int64_t framesToCopy = std::min<int64_t>(numFrames, destSamples);
150 if (channelsToCopy == 0 || framesToCopy == 0)
151 return true;
152
153 const uint64_t first = static_cast<uint64_t>(startFrame);
154 const uint64_t last = first + static_cast<uint64_t>(framesToCopy);
155 auto it = std::lower_bound(frames_.begin(), frames_.end(), first,
156 [](const FrameIndex& frame, uint64_t sample) {
157 return frame.firstSample + frame.blockSize <= sample;
158 });
159
160 int64_t written = 0;
161 std::vector<int64_t> pcm;
162 while (it != frames_.end() && it->firstSample < last)
163 {
164 const size_t frameNumber = static_cast<size_t>(it - frames_.begin());
165 DecodedFrame decoded;
166 if (!decodeFrame(bytes_, it->offset, stream_, frameNumber,
167 it->firstSample, pcm, decoded)
168 || decoded.endOffset != it->offset + it->size
169 || decoded.blockSize != it->blockSize)
170 {
171 close();
172 return false;
173 }
174
175 const uint64_t overlapBegin = std::max(first, it->firstSample);
176 const uint64_t overlapEnd = std::min(last,
177 it->firstSample + static_cast<uint64_t>(it->blockSize));
178 const size_t sourceOffset = static_cast<size_t>(overlapBegin - it->firstSample);
179 const size_t count = static_cast<size_t>(overlapEnd - overlapBegin);
180 const size_t destinationOffset = static_cast<size_t>(overlapBegin - first);
181
182 for (int channel = 0; channel < channelsToCopy; ++channel)
183 {
184 float* output = dest.getChannel(channel) + destinationOffset;
185 const int64_t* source = pcm.data()
186 + static_cast<size_t>(channel) * it->blockSize + sourceOffset;
187 for (size_t i = 0; i < count; ++i)
188 output[i] = std::ldexp(static_cast<float>(source[i]),
189 1 - static_cast<int>(stream_.bitsPerSample));
190 }
191 written += static_cast<int64_t>(count);
192 ++it;
193 }
194 if (written != framesToCopy)
195 {
196 close();
197 return false;
198 }
199 return true;
200 }
201
203 [[nodiscard]] bool writeSamples(AudioBufferView<const float> src) override
204 {
205 (void)src;
206 return false;
207 }
208
210 void close() override
211 {
212 std::vector<uint8_t>().swap(bytes_);
213 std::vector<FrameIndex>().swap(frames_);
214 stream_ = {};
215 info_ = {};
216 open_ = false;
217 }
218
220 [[nodiscard]] bool isOpen() const noexcept override { return open_; }
221
222private:
223 struct StreamInfo
224 {
225 uint16_t minBlockSize = 0;
226 uint16_t maxBlockSize = 0;
227 uint32_t minFrameSize = 0;
228 uint32_t maxFrameSize = 0;
229 uint32_t sampleRate = 0;
230 uint8_t channels = 0;
231 uint8_t bitsPerSample = 0;
232 uint64_t declaredSamples = 0;
233 std::array<uint8_t, 16> md5 {};
234 };
235
236 struct FrameIndex
237 {
238 size_t offset = 0;
239 size_t size = 0;
240 uint64_t firstSample = 0;
241 uint32_t blockSize = 0;
242 };
243
244 struct DecodedFrame
245 {
246 size_t endOffset = 0;
247 uint32_t blockSize = 0;
248 bool variableBlock = false;
249 };
250
251 struct OpenState
252 {
253 std::vector<uint8_t> bytes;
254 std::vector<FrameIndex> frames;
255 StreamInfo stream;
256 AudioFileInfo info;
257 };
258
259 class ByteCursor
260 {
261 public:
262 ByteCursor(const std::vector<uint8_t>& bytes, size_t begin, size_t end)
263 : bytes_(bytes), pos_(begin), end_(end)
264 {
265 }
266
267 [[nodiscard]] size_t position() const noexcept { return pos_; }
268 [[nodiscard]] size_t remaining() const noexcept { return end_ - pos_; }
269
270 bool readU8(uint8_t& value) noexcept
271 {
272 if (pos_ >= end_) return false;
273 value = bytes_[pos_++];
274 return true;
275 }
276
277 bool readBE16(uint16_t& value) noexcept
278 {
279 uint8_t a = 0, b = 0;
280 if (!readU8(a) || !readU8(b)) return false;
281 value = static_cast<uint16_t>((static_cast<uint16_t>(a) << 8)
282 | static_cast<uint16_t>(b));
283 return true;
284 }
285
286 bool readBE24(uint32_t& value) noexcept
287 {
288 uint8_t a = 0, b = 0, c = 0;
289 if (!readU8(a) || !readU8(b) || !readU8(c)) return false;
290 value = (static_cast<uint32_t>(a) << 16)
291 | (static_cast<uint32_t>(b) << 8)
292 | static_cast<uint32_t>(c);
293 return true;
294 }
295
296 bool readSpan(size_t length, const uint8_t*& data) noexcept
297 {
298 if (length > remaining()) return false;
299 data = bytes_.data() + pos_;
300 pos_ += length;
301 return true;
302 }
303
304 bool skip(size_t length) noexcept
305 {
306 const uint8_t* ignored = nullptr;
307 return readSpan(length, ignored);
308 }
309
310 private:
311 const std::vector<uint8_t>& bytes_;
312 size_t pos_ = 0;
313 size_t end_ = 0;
314 };
315
316 class BitCursor
317 {
318 public:
319 BitCursor(const std::vector<uint8_t>& bytes, size_t beginByte,
320 size_t endByte)
321 : bytes_(bytes), bitPos_(beginByte * 8), endBit_(endByte * 8)
322 {
323 }
324
325 [[nodiscard]] size_t bitPosition() const noexcept { return bitPos_; }
326 [[nodiscard]] size_t bytePosition() const noexcept { return bitPos_ / 8; }
327 [[nodiscard]] uint64_t remainingBits() const noexcept
328 {
329 return static_cast<uint64_t>(endBit_ - bitPos_);
330 }
331
332 bool readBits(unsigned count, uint64_t& value) noexcept
333 {
334 value = 0;
335 if (count > 64 || static_cast<uint64_t>(count) > remainingBits())
336 return false;
337 for (unsigned i = 0; i < count; ++i)
338 {
339 const uint8_t byte = bytes_[bitPos_ / 8];
340 const unsigned shift = 7u - static_cast<unsigned>(bitPos_ & 7u);
341 value = (value << 1) | ((byte >> shift) & 1u);
342 ++bitPos_;
343 }
344 return true;
345 }
346
347 bool readSigned(unsigned count, int64_t& value) noexcept
348 {
349 if (count == 0)
350 {
351 value = 0;
352 return true;
353 }
354 if (count > 63) return false;
355 uint64_t raw = 0;
356 if (!readBits(count, raw)) return false;
357 const uint64_t sign = uint64_t { 1 } << (count - 1);
358 if ((raw & sign) == 0)
359 {
360 value = static_cast<int64_t>(raw);
361 return true;
362 }
363 const uint64_t magnitude = (uint64_t { 1 } << count) - raw;
364 value = -static_cast<int64_t>(magnitude);
365 return true;
366 }
367
368 bool readUnary(uint64_t limit, uint64_t& zeros) noexcept
369 {
370 zeros = 0;
371 for (;;)
372 {
373 uint64_t bit = 0;
374 if (!readBits(1, bit)) return false;
375 if (bit != 0) return true;
376 if (zeros >= limit) return false;
377 ++zeros;
378 }
379 }
380
381 bool alignWithZeroPadding() noexcept
382 {
383 while ((bitPos_ & 7u) != 0)
384 {
385 uint64_t bit = 0;
386 if (!readBits(1, bit) || bit != 0) return false;
387 }
388 return true;
389 }
390
391 private:
392 const std::vector<uint8_t>& bytes_;
393 size_t bitPos_ = 0;
394 size_t endBit_ = 0;
395 };
396
397 class Md5
398 {
399 public:
400 Md5() noexcept { reset(); }
401
402 void update(std::span<const uint8_t> data) noexcept
403 {
404 totalBytes_ += data.size();
405 while (!data.empty())
406 {
407 const size_t take = std::min(data.size(), buffer_.size() - bufferSize_);
408 std::memcpy(buffer_.data() + bufferSize_, data.data(), take);
409 bufferSize_ += take;
410 data = data.subspan(take);
411 if (bufferSize_ == buffer_.size())
412 {
413 transform(buffer_.data());
414 bufferSize_ = 0;
415 }
416 }
417 }
418
419 [[nodiscard]] std::array<uint8_t, 16> finish() noexcept
420 {
421 const uint64_t messageBits = totalBytes_ * 8;
422 std::array<uint8_t, 120> padding {};
423 padding[0] = 0x80;
424 const size_t paddingSize = bufferSize_ < 56
425 ? 56 - bufferSize_ : 64 + 56 - bufferSize_;
426 update(std::span<const uint8_t>(padding.data(), paddingSize));
427 uint8_t length[8] = {};
428 for (unsigned i = 0; i < 8; ++i)
429 length[i] = static_cast<uint8_t>(messageBits >> (8 * i));
430 update(length);
431
432 std::array<uint8_t, 16> digest {};
433 for (size_t word = 0; word < 4; ++word)
434 for (unsigned byte = 0; byte < 4; ++byte)
435 digest[word * 4 + byte] = static_cast<uint8_t>(
436 state_[word] >> (8 * byte));
437 return digest;
438 }
439
440 private:
441 std::array<uint32_t, 4> state_ {};
442 std::array<uint8_t, 64> buffer_ {};
443 size_t bufferSize_ = 0;
444 uint64_t totalBytes_ = 0;
445
446 void reset() noexcept
447 {
448 state_ = { 0x67452301u, 0xefcdab89u, 0x98badcfeu, 0x10325476u };
449 bufferSize_ = 0;
450 totalBytes_ = 0;
451 }
452
453 static uint32_t rotateLeft(uint32_t value, unsigned count) noexcept
454 {
455 return static_cast<uint32_t>((value << count) | (value >> (32 - count)));
456 }
457
458 void transform(const uint8_t* block) noexcept
459 {
460 static constexpr uint32_t constants[64] = {
461 0xd76aa478u,0xe8c7b756u,0x242070dbu,0xc1bdceeeu,
462 0xf57c0fafu,0x4787c62au,0xa8304613u,0xfd469501u,
463 0x698098d8u,0x8b44f7afu,0xffff5bb1u,0x895cd7beu,
464 0x6b901122u,0xfd987193u,0xa679438eu,0x49b40821u,
465 0xf61e2562u,0xc040b340u,0x265e5a51u,0xe9b6c7aau,
466 0xd62f105du,0x02441453u,0xd8a1e681u,0xe7d3fbc8u,
467 0x21e1cde6u,0xc33707d6u,0xf4d50d87u,0x455a14edu,
468 0xa9e3e905u,0xfcefa3f8u,0x676f02d9u,0x8d2a4c8au,
469 0xfffa3942u,0x8771f681u,0x6d9d6122u,0xfde5380cu,
470 0xa4beea44u,0x4bdecfa9u,0xf6bb4b60u,0xbebfbc70u,
471 0x289b7ec6u,0xeaa127fau,0xd4ef3085u,0x04881d05u,
472 0xd9d4d039u,0xe6db99e5u,0x1fa27cf8u,0xc4ac5665u,
473 0xf4292244u,0x432aff97u,0xab9423a7u,0xfc93a039u,
474 0x655b59c3u,0x8f0ccc92u,0xffeff47du,0x85845dd1u,
475 0x6fa87e4fu,0xfe2ce6e0u,0xa3014314u,0x4e0811a1u,
476 0xf7537e82u,0xbd3af235u,0x2ad7d2bbu,0xeb86d391u
477 };
478 static constexpr unsigned shifts[64] = {
479 7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,
480 5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
481 4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,
482 6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21
483 };
484
485 uint32_t words[16] = {};
486 for (size_t i = 0; i < 16; ++i)
487 words[i] = static_cast<uint32_t>(block[i * 4])
488 | (static_cast<uint32_t>(block[i * 4 + 1]) << 8)
489 | (static_cast<uint32_t>(block[i * 4 + 2]) << 16)
490 | (static_cast<uint32_t>(block[i * 4 + 3]) << 24);
491
492 uint32_t a = state_[0], b = state_[1], c = state_[2], d = state_[3];
493 for (unsigned i = 0; i < 64; ++i)
494 {
495 uint32_t f = 0;
496 unsigned index = 0;
497 if (i < 16) { f = (b & c) | (~b & d); index = i; }
498 else if (i < 32) { f = (d & b) | (~d & c); index = (5 * i + 1) & 15u; }
499 else if (i < 48) { f = b ^ c ^ d; index = (3 * i + 5) & 15u; }
500 else { f = c ^ (b | ~d); index = (7 * i) & 15u; }
501 const uint32_t next = b + rotateLeft(a + f + constants[i]
502 + words[index], shifts[i]);
503 a = d; d = c; c = b; b = next;
504 }
505 state_[0] += a;
506 state_[1] += b;
507 state_[2] += c;
508 state_[3] += d;
509 }
510 };
511
512 std::vector<uint8_t> bytes_;
513 std::vector<FrameIndex> frames_;
514 StreamInfo stream_;
515 AudioFileInfo info_ {};
516 bool open_ = false;
517
518 static uint8_t crc8(std::span<const uint8_t> data) noexcept
519 {
520 uint8_t crc = 0;
521 for (uint8_t byte : data)
522 {
523 crc ^= byte;
524 for (unsigned bit = 0; bit < 8; ++bit)
525 crc = static_cast<uint8_t>((crc & 0x80u) != 0
526 ? static_cast<uint8_t>((crc << 1) ^ 0x07u)
527 : static_cast<uint8_t>(crc << 1));
528 }
529 return crc;
530 }
531
532 static uint16_t crc16(std::span<const uint8_t> data) noexcept
533 {
534 uint16_t crc = 0;
535 for (uint8_t byte : data)
536 {
537 crc ^= static_cast<uint16_t>(byte) << 8;
538 for (unsigned bit = 0; bit < 8; ++bit)
539 crc = static_cast<uint16_t>((crc & 0x8000u) != 0
540 ? static_cast<uint16_t>((crc << 1) ^ 0x8005u)
541 : static_cast<uint16_t>(crc << 1));
542 }
543 return crc;
544 }
545
546 static bool checkedAdd(int64_t a, int64_t b, int64_t& result) noexcept
547 {
548 if ((b > 0 && a > std::numeric_limits<int64_t>::max() - b)
549 || (b < 0 && a < std::numeric_limits<int64_t>::min() - b))
550 return false;
551 result = a + b;
552 return true;
553 }
554
555 static bool checkedSubtract(int64_t a, int64_t b, int64_t& result) noexcept
556 {
557 if (b == std::numeric_limits<int64_t>::min())
558 return false;
559 return checkedAdd(a, -b, result);
560 }
561
562 static bool checkedMultiply(int64_t a, int64_t b, int64_t& result) noexcept
563 {
564 if (a == 0 || b == 0) { result = 0; return true; }
565 if (a == -1 && b == std::numeric_limits<int64_t>::min()) return false;
566 if (b == -1 && a == std::numeric_limits<int64_t>::min()) return false;
567 if (a > 0)
568 {
569 if ((b > 0 && a > std::numeric_limits<int64_t>::max() / b)
570 || (b < 0 && b < std::numeric_limits<int64_t>::min() / a))
571 return false;
572 }
573 else
574 {
575 if ((b > 0 && a < std::numeric_limits<int64_t>::min() / b)
576 || (b < 0 && a < std::numeric_limits<int64_t>::max() / b))
577 return false;
578 }
579 result = a * b;
580 return true;
581 }
582
583 static bool floorDividePowerOfTwo(int64_t value, unsigned shift,
584 int64_t& result) noexcept
585 {
586 if (shift > 62) return false;
587 if (shift == 0) { result = value; return true; }
588 const int64_t divisor = int64_t { 1 } << shift;
589 result = value / divisor;
590 if (value < 0 && value % divisor != 0)
591 --result;
592 return true;
593 }
594
595 static bool sampleInDepth(int64_t value, unsigned depth) noexcept
596 {
597 if (depth == 0 || depth > 33) return false;
598 const int64_t magnitude = int64_t { 1 } << (depth - 1);
599 return value >= -magnitude && value <= magnitude - 1;
600 }
601
602 static bool parseStreamInfo(const uint8_t* data, StreamInfo& info) noexcept
603 {
604 info.minBlockSize = static_cast<uint16_t>(
605 (static_cast<uint16_t>(data[0]) << 8) | data[1]);
606 info.maxBlockSize = static_cast<uint16_t>(
607 (static_cast<uint16_t>(data[2]) << 8) | data[3]);
608 info.minFrameSize = (static_cast<uint32_t>(data[4]) << 16)
609 | (static_cast<uint32_t>(data[5]) << 8) | data[6];
610 info.maxFrameSize = (static_cast<uint32_t>(data[7]) << 16)
611 | (static_cast<uint32_t>(data[8]) << 8) | data[9];
612 const uint64_t packed = (static_cast<uint64_t>(data[10]) << 56)
613 | (static_cast<uint64_t>(data[11]) << 48)
614 | (static_cast<uint64_t>(data[12]) << 40)
615 | (static_cast<uint64_t>(data[13]) << 32)
616 | (static_cast<uint64_t>(data[14]) << 24)
617 | (static_cast<uint64_t>(data[15]) << 16)
618 | (static_cast<uint64_t>(data[16]) << 8)
619 | static_cast<uint64_t>(data[17]);
620 info.sampleRate = static_cast<uint32_t>(packed >> 44);
621 info.channels = static_cast<uint8_t>(((packed >> 41) & 7u) + 1u);
622 info.bitsPerSample = static_cast<uint8_t>(((packed >> 36) & 31u) + 1u);
623 info.declaredSamples = packed & 0xfffffffffull;
624 std::copy(data + 18, data + 34, info.md5.begin());
625
626 if (info.minBlockSize < 16 || info.maxBlockSize < 16
627 || info.minBlockSize > info.maxBlockSize)
628 return false;
629 if (info.minFrameSize != 0 && info.maxFrameSize != 0
630 && info.minFrameSize > info.maxFrameSize)
631 return false;
632 if (info.sampleRate == 0 || info.sampleRate > 1048575
633 || info.channels == 0 || info.channels > 8
634 || info.bitsPerSample < 4 || info.bitsPerSample > 32
635 || info.declaredSamples > kMaxInterchannelSamples)
636 return false;
637 return true;
638 }
639
640 static bool parseCodedNumber(ByteCursor& cursor, bool variable,
641 uint64_t& value) noexcept
642 {
643 uint8_t first = 0;
644 if (!cursor.readU8(first)) return false;
645 unsigned length = 0;
646 uint64_t minimum = 0;
647 if (first < 0x80) { length = 1; value = first; }
648 else if ((first & 0xe0u) == 0xc0u) { length = 2; value = first & 0x1fu; minimum = 0x80; }
649 else if ((first & 0xf0u) == 0xe0u) { length = 3; value = first & 0x0fu; minimum = 0x800; }
650 else if ((first & 0xf8u) == 0xf0u) { length = 4; value = first & 0x07u; minimum = 0x10000; }
651 else if ((first & 0xfcu) == 0xf8u) { length = 5; value = first & 0x03u; minimum = 0x200000; }
652 else if ((first & 0xfeu) == 0xfcu) { length = 6; value = first & 0x01u; minimum = 0x4000000; }
653 else if (first == 0xfeu) { length = 7; value = 0; minimum = 0x80000000ull; }
654 else return false;
655
656 for (unsigned i = 1; i < length; ++i)
657 {
658 uint8_t continuation = 0;
659 if (!cursor.readU8(continuation) || (continuation & 0xc0u) != 0x80u)
660 return false;
661 value = (value << 6) | (continuation & 0x3fu);
662 }
663 if (length > 1 && value < minimum)
664 return false;
665 if (variable)
666 return value <= 0xfffffffffull;
667 return length <= 6 && value <= 0x7fffffffu;
668 }
669
670 static bool decodeBlockSize(uint8_t code, ByteCursor& cursor,
671 uint32_t& blockSize) noexcept
672 {
673 if (code == 0) return false;
674 if (code == 1) blockSize = 192;
675 else if (code >= 2 && code <= 5)
676 blockSize = 576u << (code - 2u);
677 else if (code == 6)
678 {
679 uint8_t value = 0;
680 if (!cursor.readU8(value)) return false;
681 blockSize = static_cast<uint32_t>(value) + 1u;
682 }
683 else if (code == 7)
684 {
685 uint16_t value = 0;
686 if (!cursor.readBE16(value) || value == 0xffffu) return false;
687 blockSize = static_cast<uint32_t>(value) + 1u;
688 }
689 else
690 blockSize = 1u << code;
691 return blockSize > 0 && blockSize <= 65535;
692 }
693
694 static bool decodeSampleRate(uint8_t code, ByteCursor& cursor,
695 const StreamInfo& stream,
696 uint32_t& sampleRate) noexcept
697 {
698 static constexpr uint32_t rates[12] = {
699 0, 88200, 176400, 192000, 8000, 16000,
700 22050, 24000, 32000, 44100, 48000, 96000
701 };
702 if (code <= 11)
703 sampleRate = code == 0 ? stream.sampleRate : rates[code];
704 else if (code == 12)
705 {
706 uint8_t value = 0;
707 if (!cursor.readU8(value)) return false;
708 sampleRate = static_cast<uint32_t>(value) * 1000u;
709 }
710 else if (code == 13)
711 {
712 uint16_t value = 0;
713 if (!cursor.readBE16(value)) return false;
714 sampleRate = value;
715 }
716 else if (code == 14)
717 {
718 uint16_t value = 0;
719 if (!cursor.readBE16(value)) return false;
720 sampleRate = static_cast<uint32_t>(value) * 10u;
721 }
722 else return false;
723 return sampleRate != 0 && sampleRate == stream.sampleRate;
724 }
725
726 static bool decodeResidual(BitCursor& bits, uint32_t blockSize,
727 unsigned predictorOrder,
728 std::span<int64_t> output) noexcept
729 {
730 uint64_t method = 0, partitionOrder = 0;
731 if (!bits.readBits(2, method) || method > 1
732 || !bits.readBits(4, partitionOrder))
733 return false;
734 const uint32_t partitions = 1u << static_cast<unsigned>(partitionOrder);
735 if (partitions == 0 || blockSize % partitions != 0)
736 return false;
737 const uint32_t partitionSize = blockSize / partitions;
738 if (partitionSize <= predictorOrder)
739 return false;
740 const unsigned parameterBits = method == 0 ? 4u : 5u;
741 const uint64_t escape = (uint64_t { 1 } << parameterBits) - 1u;
742 size_t outputIndex = predictorOrder;
743
744 for (uint32_t partition = 0; partition < partitions; ++partition)
745 {
746 uint64_t parameter = 0;
747 if (!bits.readBits(parameterBits, parameter)) return false;
748 const uint32_t count = partitionSize
749 - (partition == 0 ? static_cast<uint32_t>(predictorOrder) : 0u);
750 if (parameter == escape)
751 {
752 uint64_t width = 0;
753 if (!bits.readBits(5, width) || width > 31) return false;
754 for (uint32_t i = 0; i < count; ++i)
755 {
756 int64_t residual = 0;
757 if (!bits.readSigned(static_cast<unsigned>(width), residual)
758 || residual < -2147483647ll || residual > 2147483647ll)
759 return false;
760 output[outputIndex++] = residual;
761 }
762 }
763 else
764 {
765 for (uint32_t i = 0; i < count; ++i)
766 {
767 uint64_t quotient = 0, remainder = 0;
768 if (!bits.readUnary(kMaxRiceUnaryZeros, quotient)
769 || !bits.readBits(static_cast<unsigned>(parameter), remainder)
770 || quotient > (std::numeric_limits<uint64_t>::max()
771 >> static_cast<unsigned>(parameter)))
772 return false;
773 const uint64_t folded = (quotient << static_cast<unsigned>(parameter))
774 | remainder;
775 if (folded > 4294967294ull)
776 return false;
777 const int64_t residual = (folded & 1u) == 0
778 ? static_cast<int64_t>(folded / 2u)
779 : -static_cast<int64_t>(folded / 2u) - 1;
780 if (residual < -2147483647ll || residual > 2147483647ll)
781 return false;
782 output[outputIndex++] = residual;
783 }
784 }
785 }
786 return outputIndex == output.size();
787 }
788
789 static bool decodeSubframe(BitCursor& bits, uint32_t blockSize,
790 unsigned storedDepth,
791 std::span<int64_t> output) noexcept
792 {
793 uint64_t zero = 0, type = 0, wastedFlag = 0;
794 if (!bits.readBits(1, zero) || zero != 0
795 || !bits.readBits(6, type)
796 || !bits.readBits(1, wastedFlag))
797 return false;
798
799 unsigned wasted = 0;
800 if (wastedFlag != 0)
801 {
802 uint64_t zeros = 0;
803 if (!bits.readUnary(32, zeros) || zeros >= storedDepth)
804 return false;
805 wasted = static_cast<unsigned>(zeros + 1);
806 }
807 if (storedDepth <= wasted)
808 return false;
809 const unsigned depth = storedDepth - wasted;
810
811 unsigned predictorOrder = 0;
812 if (type == 0)
813 {
814 int64_t value = 0;
815 if (!bits.readSigned(depth, value)) return false;
816 std::fill(output.begin(), output.end(), value);
817 }
818 else if (type == 1)
819 {
820 for (int64_t& value : output)
821 if (!bits.readSigned(depth, value)) return false;
822 }
823 else if (type >= 8 && type <= 12)
824 {
825 predictorOrder = static_cast<unsigned>(type - 8);
826 if (predictorOrder > blockSize) return false;
827 for (unsigned i = 0; i < predictorOrder; ++i)
828 if (!bits.readSigned(depth, output[i])) return false;
829 if (!decodeResidual(bits, blockSize, predictorOrder, output))
830 return false;
831 static constexpr int coefficients[5][4] = {
832 { 0, 0, 0, 0 }, { 1, 0, 0, 0 }, { 2, -1, 0, 0 },
833 { 3, -3, 1, 0 }, { 4, -6, 4, -1 }
834 };
835 for (size_t i = predictorOrder; i < output.size(); ++i)
836 {
837 int64_t prediction = 0;
838 for (unsigned j = 0; j < predictorOrder; ++j)
839 {
840 int64_t product = 0;
841 if (!checkedMultiply(output[i - j - 1], coefficients[predictorOrder][j], product)
842 || !checkedAdd(prediction, product, prediction))
843 return false;
844 }
845 if (!checkedAdd(prediction, output[i], output[i])
846 || !sampleInDepth(output[i], depth))
847 return false;
848 }
849 }
850 else if (type >= 32 && type <= 63)
851 {
852 predictorOrder = static_cast<unsigned>(type - 31);
853 if (predictorOrder > blockSize) return false;
854 for (unsigned i = 0; i < predictorOrder; ++i)
855 if (!bits.readSigned(depth, output[i])) return false;
856 uint64_t precisionMinusOne = 0;
857 int64_t shift = 0;
858 if (!bits.readBits(4, precisionMinusOne) || precisionMinusOne == 15
859 || !bits.readSigned(5, shift) || shift < 0)
860 return false;
861 const unsigned precision = static_cast<unsigned>(precisionMinusOne + 1);
862 std::array<int64_t, 32> coefficients {};
863 for (unsigned i = 0; i < predictorOrder; ++i)
864 if (!bits.readSigned(precision, coefficients[i])) return false;
865 if (!decodeResidual(bits, blockSize, predictorOrder, output))
866 return false;
867 for (size_t i = predictorOrder; i < output.size(); ++i)
868 {
869 int64_t sum = 0;
870 for (unsigned j = 0; j < predictorOrder; ++j)
871 {
872 int64_t product = 0;
873 if (!checkedMultiply(coefficients[j], output[i - j - 1], product)
874 || !checkedAdd(sum, product, sum))
875 return false;
876 }
877 int64_t prediction = 0;
878 if (!floorDividePowerOfTwo(sum, static_cast<unsigned>(shift), prediction)
879 || !checkedAdd(prediction, output[i], output[i])
880 || !sampleInDepth(output[i], depth))
881 return false;
882 }
883 }
884 else
885 return false;
886
887 if (type <= 1)
888 for (int64_t value : output)
889 if (!sampleInDepth(value, depth)) return false;
890
891 if (wasted != 0)
892 {
893 const int64_t factor = int64_t { 1 } << wasted;
894 for (int64_t& value : output)
895 if (!checkedMultiply(value, factor, value)
896 || !sampleInDepth(value, storedDepth))
897 return false;
898 }
899 return true;
900 }
901
902 static bool restoreChannels(uint8_t channelCode, uint8_t bitsPerSample,
903 uint32_t blockSize,
904 std::vector<int64_t>& pcm) noexcept
905 {
906 if (channelCode < 8)
907 {
908 for (int64_t value : pcm)
909 if (!sampleInDepth(value, bitsPerSample)) return false;
910 return true;
911 }
912
913 int64_t* first = pcm.data();
914 int64_t* second = pcm.data() + blockSize;
915 for (uint32_t i = 0; i < blockSize; ++i)
916 {
917 int64_t left = 0, right = 0;
918 if (channelCode == 8)
919 {
920 left = first[i];
921 if (!checkedSubtract(left, second[i], right)) return false;
922 }
923 else if (channelCode == 9)
924 {
925 right = second[i];
926 if (!checkedAdd(first[i], right, left)) return false;
927 }
928 else
929 {
930 int64_t doubledMid = 0, adjustedMid = 0;
931 if (!checkedMultiply(first[i], 2, doubledMid)
932 || !checkedAdd(doubledMid, second[i] % 2 != 0 ? 1 : 0, adjustedMid))
933 return false;
934 int64_t leftNumerator = 0, rightNumerator = 0;
935 if (!checkedAdd(adjustedMid, second[i], leftNumerator)
936 || !checkedSubtract(adjustedMid, second[i], rightNumerator)
937 || !floorDividePowerOfTwo(leftNumerator, 1, left)
938 || !floorDividePowerOfTwo(rightNumerator, 1, right))
939 return false;
940 }
941 if (!sampleInDepth(left, bitsPerSample)
942 || !sampleInDepth(right, bitsPerSample))
943 return false;
944 first[i] = left;
945 second[i] = right;
946 }
947 return true;
948 }
949
950 static bool decodeFrame(const std::vector<uint8_t>& bytes, size_t offset,
951 const StreamInfo& stream, size_t expectedFrameNumber,
952 uint64_t expectedFirstSample,
953 std::vector<int64_t>& pcm,
954 DecodedFrame& decoded)
955 {
956 if (offset > bytes.size() || bytes.size() - offset < 8)
957 return false;
958 ByteCursor cursor(bytes, offset, bytes.size());
959 uint8_t first = 0, second = 0, third = 0, fourth = 0;
960 if (!cursor.readU8(first) || !cursor.readU8(second)
961 || !cursor.readU8(third) || !cursor.readU8(fourth)
962 || first != 0xff || (second & 0xfeu) != 0xf8u
963 || (fourth & 1u) != 0)
964 return false;
965 const bool variable = (second & 1u) != 0;
966 if (variable == (stream.minBlockSize == stream.maxBlockSize))
967 return false;
968 const uint8_t blockCode = third >> 4;
969 const uint8_t rateCode = third & 0x0fu;
970 const uint8_t channelCode = fourth >> 4;
971 const uint8_t depthCode = (fourth >> 1) & 7u;
972 if (channelCode > 10 || depthCode == 3)
973 return false;
974 const uint8_t channels = channelCode <= 7
975 ? static_cast<uint8_t>(channelCode + 1) : uint8_t { 2 };
976 static constexpr uint8_t depths[8] = { 0, 8, 12, 0, 16, 20, 24, 32 };
977 const uint8_t bitDepth = depthCode == 0 ? stream.bitsPerSample : depths[depthCode];
978 if (channels != stream.channels || bitDepth != stream.bitsPerSample)
979 return false;
980
981 uint64_t codedNumber = 0;
982 if (!parseCodedNumber(cursor, variable, codedNumber))
983 return false;
984 if ((variable && codedNumber != expectedFirstSample)
985 || (!variable && codedNumber != expectedFrameNumber))
986 return false;
987
988 uint32_t blockSize = 0, sampleRate = 0;
989 if (!decodeBlockSize(blockCode, cursor, blockSize)
990 || !decodeSampleRate(rateCode, cursor, stream, sampleRate)
991 || blockSize > stream.maxBlockSize)
992 return false;
993
994 const size_t crcPosition = cursor.position();
995 uint8_t storedCrc8 = 0;
996 if (!cursor.readU8(storedCrc8)
997 || crc8(std::span<const uint8_t>(bytes.data() + offset,
998 crcPosition - offset)) != storedCrc8)
999 return false;
1000
1001 if (static_cast<uint64_t>(blockSize) * channels
1002 > static_cast<uint64_t>(std::numeric_limits<size_t>::max()))
1003 return false;
1004 pcm.resize(static_cast<size_t>(blockSize) * channels);
1005 BitCursor bits(bytes, cursor.position(), bytes.size());
1006 for (uint8_t channel = 0; channel < channels; ++channel)
1007 {
1008 unsigned depth = bitDepth;
1009 if ((channelCode == 8 && channel == 1)
1010 || (channelCode == 9 && channel == 0)
1011 || (channelCode == 10 && channel == 1))
1012 ++depth;
1013 std::span<int64_t> channelPcm(
1014 pcm.data() + static_cast<size_t>(channel) * blockSize, blockSize);
1015 if (!decodeSubframe(bits, blockSize, depth, channelPcm))
1016 return false;
1017 }
1018 if (!bits.alignWithZeroPadding())
1019 return false;
1020 const size_t footerPosition = bits.bytePosition();
1021 if (footerPosition > bytes.size() || bytes.size() - footerPosition < 2)
1022 return false;
1023 const uint16_t storedCrc16 = static_cast<uint16_t>(
1024 (static_cast<uint16_t>(bytes[footerPosition]) << 8)
1025 | bytes[footerPosition + 1]);
1026 if (crc16(std::span<const uint8_t>(bytes.data() + offset,
1027 footerPosition - offset)) != storedCrc16)
1028 return false;
1029 if (!restoreChannels(channelCode, bitDepth, blockSize, pcm))
1030 return false;
1031
1032 decoded.endOffset = footerPosition + 2;
1033 decoded.blockSize = blockSize;
1034 decoded.variableBlock = variable;
1035 return true;
1036 }
1037
1038 static bool updateMd5(Md5& md5, const std::vector<int64_t>& pcm,
1039 uint8_t channels, uint8_t bitsPerSample,
1040 uint32_t blockSize) noexcept
1041 {
1042 const unsigned bytesPerSample = (bitsPerSample + 7u) / 8u;
1043 uint8_t encoded[4] = {};
1044 for (uint32_t sample = 0; sample < blockSize; ++sample)
1045 {
1046 for (uint8_t channel = 0; channel < channels; ++channel)
1047 {
1048 const int64_t value = pcm[static_cast<size_t>(channel) * blockSize + sample];
1049 const uint64_t representation = static_cast<uint64_t>(value);
1050 encoded[0] = static_cast<uint8_t>(representation);
1051 encoded[1] = static_cast<uint8_t>(representation >> 8);
1052 encoded[2] = static_cast<uint8_t>(representation >> 16);
1053 encoded[3] = static_cast<uint8_t>(representation >> 24);
1054 switch (bytesPerSample)
1055 {
1056 case 1: md5.update(std::span<const uint8_t>(encoded, 1)); break;
1057 case 2: md5.update(std::span<const uint8_t>(encoded, 2)); break;
1058 case 3: md5.update(std::span<const uint8_t>(encoded, 3)); break;
1059 case 4: md5.update(std::span<const uint8_t>(encoded, 4)); break;
1060 default: return false;
1061 }
1062 }
1063 }
1064 return true;
1065 }
1066
1067 static bool allZero(const std::array<uint8_t, 16>& value) noexcept
1068 {
1069 for (uint8_t byte : value) if (byte != 0) return false;
1070 return true;
1071 }
1072
1073 static bool validateOpen(OpenState& state)
1074 {
1075 if (state.bytes.size() < 4
1076 || std::memcmp(state.bytes.data(), "fLaC", 4) != 0)
1077 return false;
1078 ByteCursor cursor(state.bytes, 4, state.bytes.size());
1079 uint64_t metadataBlocks = 0;
1080 bool sawStreamInfo = false;
1081 bool lastMetadata = false;
1082
1083 while (!lastMetadata)
1084 {
1085 if (metadataBlocks >= kMaxMetadataBlocks) return false;
1086 ++metadataBlocks;
1087 uint8_t header = 0;
1088 uint32_t length = 0;
1089 if (!cursor.readU8(header) || !cursor.readBE24(length))
1090 return false;
1091 lastMetadata = (header & 0x80u) != 0;
1092 const uint8_t type = header & 0x7fu;
1093 if (type == 127 || length > cursor.remaining())
1094 return false;
1095 const uint8_t* data = nullptr;
1096 if (!cursor.readSpan(length, data))
1097 return false;
1098 if (!sawStreamInfo)
1099 {
1100 if (type != 0 || length != 34
1101 || !parseStreamInfo(data, state.stream))
1102 return false;
1103 sawStreamInfo = true;
1104 }
1105 else if (type == 0)
1106 return false;
1107 }
1108 if (!sawStreamInfo || cursor.remaining() == 0)
1109 return false;
1110
1111 Md5 md5;
1112 uint64_t coverage = 0;
1113 bool haveStrategy = false;
1114 bool strategy = false;
1115 uint32_t previousBlockSize = 0;
1116 std::vector<int64_t> pcm;
1117 while (cursor.remaining() > 0)
1118 {
1119 if (state.frames.size() >= kMaxFrames)
1120 return false;
1121 if (haveStrategy && previousBlockSize < state.stream.minBlockSize)
1122 return false;
1123 if (haveStrategy && !strategy
1124 && previousBlockSize != state.stream.minBlockSize)
1125 return false;
1126
1127 DecodedFrame decoded;
1128 const size_t frameOffset = cursor.position();
1129 if (!decodeFrame(state.bytes, frameOffset, state.stream,
1130 state.frames.size(), coverage, pcm, decoded)
1131 || decoded.endOffset <= frameOffset
1132 || decoded.endOffset > state.bytes.size())
1133 return false;
1134 if (haveStrategy && decoded.variableBlock != strategy)
1135 return false;
1136 haveStrategy = true;
1137 strategy = decoded.variableBlock;
1138 previousBlockSize = decoded.blockSize;
1139 const size_t frameSize = decoded.endOffset - frameOffset;
1140 if ((state.stream.minFrameSize != 0
1141 && frameSize < state.stream.minFrameSize)
1142 || (state.stream.maxFrameSize != 0
1143 && frameSize > state.stream.maxFrameSize))
1144 return false;
1145 if (coverage > kMaxInterchannelSamples - decoded.blockSize)
1146 return false;
1147 const uint64_t firstSample = coverage;
1148 coverage += decoded.blockSize;
1149 const uint64_t bytesPerSample = (state.stream.bitsPerSample + 7u) / 8u;
1150 if (coverage > kMaxDecodedPcmBytes
1151 / (static_cast<uint64_t>(state.stream.channels) * bytesPerSample))
1152 return false;
1153 if (!updateMd5(md5, pcm, state.stream.channels,
1154 state.stream.bitsPerSample, decoded.blockSize))
1155 return false;
1156 state.frames.push_back({ frameOffset, frameSize, firstSample,
1157 decoded.blockSize });
1158 if (!cursor.skip(frameSize))
1159 return false;
1160 }
1161
1162 if (state.frames.empty() || coverage == 0
1163 || (state.stream.declaredSamples != 0
1164 && state.stream.declaredSamples != coverage))
1165 return false;
1166 const std::array<uint8_t, 16> digest = md5.finish();
1167 if (!allZero(state.stream.md5) && digest != state.stream.md5)
1168 return false;
1169
1170 state.info.sampleRate = static_cast<double>(state.stream.sampleRate);
1171 state.info.numChannels = state.stream.channels;
1172 state.info.numSamples = static_cast<int64_t>(coverage);
1173 state.info.bitsPerSample = state.stream.bitsPerSample;
1174 state.info.isFloatingPoint = false;
1175 return true;
1176 }
1177};
1178
1179} // namespace dspark
Non-owning view over audio channel data.
Definition AudioBuffer.h:50
int getNumSamples() const noexcept
Returns the number of samples per channel.
int getNumChannels() const noexcept
Returns the number of channels in this view.
T * getChannel(int ch) const noexcept
Returns a pointer to the sample data for the given channel.
Abstract base class for audio file readers and writers.
Definition AudioFile.h:107
Decode-only native FLAC implementation with eager integrity checks.
Definition FlacFile.h:48
static constexpr uint64_t kMaxDecodedPcmBytes
Maximum logical decoded PCM byte count.
Definition FlacFile.h:59
bool isOpen() const noexcept override
True only after a complete stream has passed eager validation.
Definition FlacFile.h:220
bool openRead(const std::filesystem::path &path) override
Opens and fully validates a native FLAC stream.
Definition FlacFile.h:72
static constexpr uint64_t kMaxInterchannelSamples
Maximum decoded interchannel sample count.
Definition FlacFile.h:57
static constexpr uint64_t kMaxFrames
Maximum number of indexed audio frames.
Definition FlacFile.h:55
bool openWrite(const std::filesystem::path &path, const AudioFileInfo &info) override
FLAC encoding is unsupported; closes prior state and returns false.
Definition FlacFile.h:110
static constexpr uint64_t kMaxMetadataBlocks
Maximum number of metadata block headers scanned.
Definition FlacFile.h:53
AudioFileInfo getInfo() const override
Returns current stream metadata, or default metadata when closed.
Definition FlacFile.h:120
static constexpr uint64_t kMaxInputBytes
Maximum compressed input retained by one instance.
Definition FlacFile.h:51
static constexpr std::string_view kWriteUnsupportedReason
Stable explanation returned by documentation for write failure.
Definition FlacFile.h:63
bool readSamples(AudioBufferView< float > dest) override
Reads from sample zero, clamped to the destination dimensions.
Definition FlacFile.h:123
~FlacFile() override
Definition FlacFile.h:66
bool writeSamples(AudioBufferView< const float > src) override
Always returns false because this class is decode-only.
Definition FlacFile.h:203
bool readSamples(AudioBufferView< float > dest, int64_t startFrame, int64_t numFrames) override
Decodes an exact valid file range into the destination capacity.
Definition FlacFile.h:135
void close() override
Releases compressed data, metadata, and the frame index.
Definition FlacFile.h:210
static constexpr uint64_t kMaxRiceUnaryZeros
Maximum zero run accepted in one Rice unary code.
Definition FlacFile.h:61
Main namespace for the DSPark framework.
Metadata describing an audio file's format and dimensions.
Definition AudioFile.h:39
uint32_t numChannels
Number of audio channels (1 = mono, 2 = stereo).
Definition AudioFile.h:44
int64_t numSamples
Total number of sample frames in the file.
Definition AudioFile.h:47