20constexpr std::byte operator
""_byte(
unsigned long long value)
22 return std::byte(value);
27constexpr quint8 byteAt(QSpan<
const std::byte> data, qsizetype index)
29 return std::to_integer<quint8>(data[index]);
33bool hasMarker(QSpan<
const std::byte> data, QByteArrayView marker)
35 return QByteArrayView(data).startsWith(marker);
41std::optional<qsizetype> findSyncWord(QSpan<
const std::byte> data, qsizetype from,
42 std::byte secondByteMask,
std::byte secondByteValue,
44 qsizetype (*frameSize)(QSpan<
const std::byte>, qsizetype))
46 const QByteArrayView view(data);
47 const qsizetype last = view.size() - headerSize;
49 for (qsizetype i = qMax<qsizetype>(from, 0); i <= last;) {
50 const qsizetype sync = view.indexOf(
char(0xFF), i);
51 if (sync < 0 || sync > last)
53 if ((data[sync + 1] & secondByteMask) == secondByteValue && frameSize(data, sync) > 0) {
62quint32 fromBigEndian(QSpan<
const std::byte> data)
64 Q_ASSERT(data.size() <= 4);
66 for (std::byte b : data)
67 value = (value << 8) | std::to_integer<quint32>(b);
72quint32 fromLittleEndian(QSpan<
const std::byte> data)
74 Q_ASSERT(data.size() <= 4);
76 for (qsizetype i = data.size() - 1; i >= 0; --i)
77 value = (value << 8) | std::to_integer<quint32>(data[i]);
83quint8 crc8(QSpan<
const std::byte> data)
86 for (std::byte b : data) {
87 crc ^= std::to_integer<quint8>(b);
88 for (
int bit = 0; bit < 8; ++bit)
89 crc = (crc & 0x80) ? quint8((crc << 1) ^ 0x07) : quint8(crc << 1);
99qsizetype flacFrameHeaderSize(QSpan<
const std::byte> data, qsizetype offset)
104 const QSpan<
const std::byte> frame = drop(data, offset);
105 if (frame.size() < 6)
110 if (frame[0] != 0xFF_byte || (frame[1] & 0xFE_byte) != 0xF8_byte)
113 const quint8 blockSizeCode = byteAt(frame, 2) >> 4;
114 const quint8 sampleRateCode = byteAt(frame, 2) & 0x0F;
115 const quint8 channelCode = byteAt(frame, 3) >> 4;
116 const quint8 sampleSizeCode = (byteAt(frame, 3) >> 1) & 0x07;
120 if (blockSizeCode == 0 || sampleRateCode == 0x0F || channelCode > 10
121 || sampleSizeCode == 3 || (byteAt(frame, 3) & 0x01) != 0) {
127 const std::byte codedNumberLead = frame[4];
128 qsizetype codedNumberSize = 0;
129 if ((codedNumberLead & 0x80_byte) == 0x00_byte)
131 else if ((codedNumberLead & 0xE0_byte) == 0xC0_byte)
133 else if ((codedNumberLead & 0xF0_byte) == 0xE0_byte)
135 else if ((codedNumberLead & 0xF8_byte) == 0xF0_byte)
137 else if ((codedNumberLead & 0xFC_byte) == 0xF8_byte)
139 else if ((codedNumberLead & 0xFE_byte) == 0xFC_byte)
141 else if (codedNumberLead == 0xFE_byte)
146 if (frame.size() < 4 + codedNumberSize)
148 const bool continuationBytesValid =
150 return (b &
std::byte{ 0xC0 }) ==
std::byte{ 0x80 };
152 if (!continuationBytesValid)
155 qsizetype headerSize = 4 + codedNumberSize;
159 if (blockSizeCode == 6)
161 else if (blockSizeCode == 7)
164 if (sampleRateCode == 12)
166 else if (sampleRateCode == 13 || sampleRateCode == 14)
172 if (frame.size() < headerSize + 1)
174 if (crc8(take(frame, headerSize)) != byteAt(frame, headerSize))
177 return headerSize + 1;
184 if (header.size() < 4)
188 if (hasMarker(header,
"ID3"))
194 if (header[0] == 0xFF_byte && (header[1] & 0xF6_byte) == 0xF0_byte)
198 if (header[0] == 0xFF_byte && (header[1] & 0xE0_byte) == 0xE0_byte)
202 if (hasMarker(header,
"fLaC"))
208 if (hasMarker(header,
"OggS")) {
214 if (hasMarker(header,
"RIFF"))
229 if (data.size() < 27 || !hasMarker(data,
"OggS"))
232 const qsizetype packetOffset = 27 + byteAt(data, 26);
233 const QSpan<
const std::byte> packet = drop(data, packetOffset);
236 if (packet.size() < 16)
241 const auto rateAt12 = [&packet] {
242 return int(fromLittleEndian(packet.subspan(12, 4)));
246 if (hasMarker(packet, QByteArrayView(
"\x01vorbis", 7))) {
249 info.numChannels = byteAt(packet, 11);
257 if (hasMarker(packet,
"OpusHead")) {
260 info.numChannels = byteAt(packet, 9);
273 if (data.size() < 42 || !hasMarker(data,
"fLaC"))
278 if ((data[4] & 0x7F_byte) != 0x00_byte)
281 const qsizetype blockLength = fromBigEndian(data.subspan(5, 3));
282 if (blockLength < 34 || data.size() < 8 + blockLength)
288 info.sampleRate = fromBigEndian(data.subspan(18, 3)) >> 4;
293 info.totalSamples = (qint64(byteAt(data, 21) & 0x0F) << 32)
294 | (qint64(byteAt(data, 22)) << 24) | (byteAt(data, 23) << 16)
295 | (byteAt(data, 24) << 8) | byteAt(data, 25);
301 if (data.size() < 8 || !hasMarker(data,
"fLaC"))
304 qsizetype offset = 4;
305 while (offset + 4 <= data.size()) {
306 const bool isLast = (data[offset] & 0x80_byte) != 0x00_byte;
307 const qsizetype blockLength = fromBigEndian(data.subspan(offset + 1, 3));
308 offset += 4 + blockLength;
310 return offset <= data.size() ? std::optional(offset) : std::nullopt;
317 const QByteArrayView view(data);
318 for (qsizetype i = qMax<qsizetype>(from, 0); i < view.size();) {
319 const qsizetype sync = view.indexOf(
char(0xFF), i);
322 if (flacFrameHeaderSize(data, sync) > 0)
334 const QSpan<
const std::byte> header = take(drop(data, offset), 4);
335 if (header.size() < 4)
338 if (header[0] != 0xFF_byte || (header[1] & 0xE0_byte) != 0xE0_byte)
341 const int versionBits = (byteAt(header, 1) >> 3) & 0x3;
342 const int layerBits = (byteAt(header, 1) >> 1) & 0x3;
343 const int bitrateIndex = (byteAt(header, 2) >> 4) & 0xF;
344 const int sampleRateIndex = (byteAt(header, 2) >> 2) & 0x3;
345 const int padding = (byteAt(header, 2) >> 1) & 0x1;
349 if (sampleRateIndex == 3)
351 if (bitrateIndex == 0 || bitrateIndex == 15)
354 static constexpr std::array<
int, 16> bitratesV1 =
355 { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 };
356 static constexpr std::array<
int, 16> bitratesV2 =
357 { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 };
359 static constexpr std::array<
std::array<
int, 3>, 4> sampleRates = {{
360 { 11025, 12000, 8000 },
362 { 22050, 24000, 16000 },
363 { 44100, 48000, 32000 },
366 const int sampleRate = sampleRates[versionBits][sampleRateIndex];
370 const bool isMpeg1 = (versionBits == 3);
371 const int bitrateBps = (isMpeg1 ? bitratesV1 : bitratesV2)[bitrateIndex] * 1000;
377 const int coefficient = isMpeg1 ? 144 : 72;
378 const qsizetype frameSize = (coefficient * bitrateBps / sampleRate) + padding;
379 return frameSize > 4 ? frameSize : 0;
384 return findSyncWord(data, from, 0xE0_byte, 0xE0_byte, 4, mpegFrameSize);
392 const QSpan<
const std::byte> header = take(drop(data, offset), 7);
393 if (header.size() < 7)
397 if (header[0] != 0xFF_byte || (header[1] & 0xF0_byte) != 0xF0_byte)
402 const qsizetype frameLength = ((byteAt(header, 3) & 0x03) << 11) | (byteAt(header, 4) << 3)
403 | (byteAt(header, 5) >> 5);
404 return frameLength >= 7 ? frameLength : 0;
409 return findSyncWord(data, from, 0xF0_byte, 0xF0_byte, 7, adtsFrameSize);