#include "FlacDecoder.h" #include "Utils/Alignment.h" #include "Utils/ClassUtils.h" #include "Utils/Endianness.h" #include "Utils/FileUtils.h" #include #include #include namespace { constexpr auto FLAC_MAGIC = FileUtils::MakeMagic32('f', 'L', 'a', 'C'); enum class MetaDataBlockType : unsigned { STREAMINFO = 0, PADDING = 1, APPLICATION = 2, SEEKTABLE = 3, VORBIS_COMMENT = 4, CUESHEET = 5, PICTURE = 6 }; struct MetaDataBlockHeader { uint8_t isLastMetaDataBlock; MetaDataBlockType blockType; uint32_t blockLength; }; constexpr auto STREAM_INFO_BLOCK_SIZE = 34; class FlacReadingException final : public std::exception { public: explicit FlacReadingException(std::string message) : m_message(std::move(message)) { } _NODISCARD char const* what() const noexcept override { return m_message.c_str(); } private: std::string m_message; }; class FlacBitReader { public: explicit FlacBitReader(std::istream& stream) : m_stream(stream), m_last_byte(0u), m_remaining_bits_last_byte(0u) { } template T ReadBits(const size_t bitCount) { union { uint8_t buffer[sizeof(T)]; T result; } data{}; const auto byteCount = utils::Align(bitCount, 8u) / 8u; assert(byteCount <= sizeof(T)); const auto shiftCount = (8u - bitCount % 8) % 8; auto remainingBits = bitCount; #if HOST_ENDIANNESS == LITTLE_ENDIAN_ENDIANNESS auto offset = byteCount - 1; #else auto offset = 0u; #endif while (remainingBits > 0) { const auto curBits = static_cast(std::min(remainingBits, 8u)); if (m_remaining_bits_last_byte > 0) { if (m_remaining_bits_last_byte < curBits) { const auto bitsFromFirstByte = m_remaining_bits_last_byte; data.buffer[offset] = static_cast(m_last_byte << (8u - bitsFromFirstByte)); m_stream.read(reinterpret_cast(&m_last_byte), sizeof(m_last_byte)); if (m_stream.gcount() != sizeof(m_last_byte)) throw FlacReadingException("Unexpected eof"); const auto bitsFromSecondByte = static_cast(curBits - m_remaining_bits_last_byte); m_remaining_bits_last_byte = 8u - bitsFromSecondByte; const auto maskForSecondByte = static_cast(0xFF << (8u - bitsFromSecondByte)); data.buffer[offset] |= (m_last_byte & maskForSecondByte) >> bitsFromFirstByte; } else if (m_remaining_bits_last_byte == curBits) { data.buffer[offset] = static_cast(m_last_byte << (8u - curBits)); m_remaining_bits_last_byte = 0u; } else // m_remaining_bits_last_byte > curBits { const auto maskForCurBits = 0xFF >> (8u - curBits); const auto maskForCurBitsInRemainingBits = static_cast(maskForCurBits << (m_remaining_bits_last_byte - curBits)); const auto selectedData = static_cast(m_last_byte & maskForCurBitsInRemainingBits); data.buffer[offset] = static_cast(selectedData << (8u - m_remaining_bits_last_byte)); m_remaining_bits_last_byte -= curBits; } } else if (curBits >= 8u) { m_stream.read(reinterpret_cast(&data.buffer[offset]), sizeof(uint8_t)); if (m_stream.gcount() != sizeof(uint8_t)) throw FlacReadingException("Unexpected eof"); } else { m_stream.read(reinterpret_cast(&m_last_byte), sizeof(m_last_byte)); if (m_stream.gcount() != sizeof(m_last_byte)) throw FlacReadingException("Unexpected eof"); data.buffer[offset] = m_last_byte & (0xFF << (8u - curBits)); m_remaining_bits_last_byte = static_cast(8u - curBits); } remainingBits -= curBits; #if HOST_ENDIANNESS == LITTLE_ENDIAN_ENDIANNESS --offset; #else ++offset; #endif } data.result >>= shiftCount; return data.result; } void ReadBuffer(void* buffer, const size_t bitCount) { assert(m_remaining_bits_last_byte == 0); assert(bitCount % 8 == 0); m_remaining_bits_last_byte = 0; m_stream.read(static_cast(buffer), bitCount / 8); } void Seek(const size_t offset) { assert(m_remaining_bits_last_byte == 0); m_remaining_bits_last_byte = 0; m_stream.seekg(offset, std::ios::cur); } private: std::istream& m_stream; uint8_t m_last_byte; uint8_t m_remaining_bits_last_byte; }; } // namespace namespace flac { FlacMetaData::FlacMetaData() : m_minimum_block_size(), m_maximum_block_size(), m_minimum_frame_size(), m_maximum_frame_size(), m_sample_rate(), m_number_of_channels(), m_bits_per_sample(), m_total_samples(), m_md5_signature{} { } void FlacReadStreamInfo(FlacBitReader& reader, FlacMetaData& metaData) { metaData.m_minimum_block_size = reader.ReadBits(16); metaData.m_maximum_block_size = reader.ReadBits(16); metaData.m_minimum_frame_size = reader.ReadBits(24); metaData.m_maximum_frame_size = reader.ReadBits(24); metaData.m_sample_rate = reader.ReadBits(20); metaData.m_number_of_channels = static_cast(reader.ReadBits(3) + 1); metaData.m_bits_per_sample = static_cast(reader.ReadBits(5) + 1); metaData.m_total_samples = reader.ReadBits(36); reader.ReadBuffer(metaData.m_md5_signature, 128); } bool GetFlacMetaData(std::istream& stream, FlacMetaData& metaData) { try { uint32_t readMagic; stream.read(reinterpret_cast(&readMagic), sizeof(readMagic)); if (stream.gcount() != sizeof(readMagic) || readMagic != FLAC_MAGIC) throw FlacReadingException("Invalid flac magic"); FlacBitReader reader(stream); while (true) { const MetaDataBlockHeader header{ reader.ReadBits(1), static_cast(reader.ReadBits(7)), reader.ReadBits(24), }; if (header.blockType == MetaDataBlockType::STREAMINFO) { if (header.blockLength != STREAM_INFO_BLOCK_SIZE) throw FlacReadingException("Flac stream info block size invalid"); FlacReadStreamInfo(reader, metaData); return true; } reader.Seek(header.blockLength * 8u); if (header.isLastMetaDataBlock) break; } throw FlacReadingException("Missing flac stream info block"); } catch (const FlacReadingException& e) { std::cerr << e.what() << "\n"; } return false; } bool GetFlacMetaData(const void* data, const size_t dataSize, FlacMetaData& metaData) { std::istringstream ss(std::string(static_cast(data), dataSize)); return GetFlacMetaData(ss, metaData); } } // namespace flac