chore: drop third party lib for reading flac header

This commit is contained in:
Jan 2024-02-10 16:33:26 +01:00
parent 05ce73049d
commit 4591787989
No known key found for this signature in database
GPG Key ID: 44B581F78FF5C57C
9 changed files with 295 additions and 139 deletions

3
.gitmodules vendored
View File

@ -13,6 +13,3 @@
[submodule "thirdparty/json"]
path = thirdparty/json
url = https://github.com/nlohmann/json.git
[submodule "thirdparty/flac"]
path = thirdparty/flac
url = https://github.com/astoeckel/libfoxenflac.git

View File

@ -83,7 +83,6 @@ workspace "OpenAssetTools"
-- ThirdParty
-- ========================
include "thirdparty/catch2.lua"
include "thirdparty/flac.lua"
include "thirdparty/libtomcrypt.lua"
include "thirdparty/libtommath.lua"
include "thirdparty/json.lua"
@ -95,7 +94,6 @@ include "thirdparty/zlib.lua"
-- ThirdParty group: All projects that are external dependencies
group "ThirdParty"
catch2:project()
flac:project()
libtommath:project()
libtomcrypt:project()
json:project()

View File

@ -15,7 +15,6 @@ function ObjCommon:link(links)
links:linkto(Utils)
links:linkto(Common)
links:linkto(minizip)
links:linkto(flac)
end
function ObjCommon:use()
@ -49,5 +48,4 @@ function ObjCommon:project()
self:include(includes)
Utils:include(includes)
flac:include(includes)
end

View File

@ -1,77 +1,245 @@
#include "FlacDecoder.h"
#include <foxen/flac.h>
#include "Utils/Alignment.h"
#include "Utils/Endianness.h"
#include "Utils/FileUtils.h"
class fx_flac_raii
#include <cassert>
#include <iostream>
#include <sstream>
namespace
{
public:
fx_flac_raii()
constexpr auto FLAC_MAGIC = FileUtils::MakeMagic32('f', 'L', 'a', 'C');
enum class MetaDataBlockType : unsigned
{
ptr = FX_FLAC_ALLOC_DEFAULT();
}
STREAMINFO = 0,
PADDING = 1,
APPLICATION = 2,
SEEKTABLE = 3,
VORBIS_COMMENT = 4,
CUESHEET = 5,
PICTURE = 6
};
~fx_flac_raii()
struct MetaDataBlockHeader
{
free(ptr);
}
uint8_t isLastMetaDataBlock;
MetaDataBlockType blockType;
uint32_t blockLength;
};
operator fx_flac_t*()
constexpr auto STREAM_INFO_BLOCK_SIZE = 34;
class FlacReadingException final : public std::exception
{
return ptr;
}
private:
fx_flac_t* ptr;
};
class FlacDecoderImpl : public FlacDecoder
{
private:
void* m_data;
size_t m_length;
std::unique_ptr<fx_flac_raii> m_flac;
public:
explicit FlacDecoderImpl(void* data, size_t length)
: m_data(data),
m_length(length)
{
}
unsigned int GetFrameCount() override
{
return static_cast<unsigned int>(fx_flac_get_streaminfo(*m_flac.get(), FLAC_KEY_N_SAMPLES));
}
unsigned int GetFrameRate() override
{
return static_cast<unsigned int>(fx_flac_get_streaminfo(*m_flac.get(), FLAC_KEY_SAMPLE_RATE));
}
unsigned int GetNumChannels() override
{
return static_cast<unsigned int>(fx_flac_get_streaminfo(*m_flac.get(), FLAC_KEY_N_CHANNELS));
}
bool Decode() override
{
m_flac = std::make_unique<fx_flac_raii>();
while (true)
public:
explicit FlacReadingException(std::string message)
: m_message(std::move(message))
{
auto res = fx_flac_process(*m_flac.get(), reinterpret_cast<uint8_t*>(m_data), &m_length, nullptr, nullptr);
if (res == FLAC_ERR)
return false;
if (res == FLAC_END_OF_METADATA)
break;
}
return true;
}
};
_NODISCARD char const* what() const noexcept override
{
return m_message.c_str();
}
std::unique_ptr<FlacDecoder> FlacDecoder::Create(void* data, size_t length)
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<typename T> 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<uint8_t>(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<uint8_t>(m_last_byte << (8u - bitsFromFirstByte));
m_stream.read(reinterpret_cast<char*>(&m_last_byte), sizeof(m_last_byte));
if (m_stream.gcount() != sizeof(m_last_byte))
throw FlacReadingException("Unexpected eof");
const auto bitsFromSecondByte = static_cast<uint8_t>(curBits - m_remaining_bits_last_byte);
m_remaining_bits_last_byte = 8u - bitsFromSecondByte;
const auto maskForSecondByte = static_cast<uint8_t>(0xFF << (8u - bitsFromSecondByte));
data.buffer[offset] |= (m_last_byte & maskForSecondByte) >> bitsFromFirstByte;
}
else if (m_remaining_bits_last_byte == curBits)
{
data.buffer[offset] = static_cast<uint8_t>(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<uint8_t>(maskForCurBits << (m_remaining_bits_last_byte - curBits));
const auto selectedData = static_cast<uint8_t>(m_last_byte & maskForCurBitsInRemainingBits);
data.buffer[offset] = static_cast<uint8_t>(selectedData << (8u - m_remaining_bits_last_byte));
m_remaining_bits_last_byte -= curBits;
}
}
else if (curBits >= 8u)
{
m_stream.read(reinterpret_cast<char*>(&data.buffer[offset]), sizeof(uint8_t));
if (m_stream.gcount() != sizeof(uint8_t))
throw FlacReadingException("Unexpected eof");
}
else
{
m_stream.read(reinterpret_cast<char*>(&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<uint8_t>(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<char*>(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
{
return std::make_unique<FlacDecoderImpl>(data, length);
}
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<uint16_t>(16);
metaData.m_maximum_block_size = reader.ReadBits<uint16_t>(16);
metaData.m_minimum_frame_size = reader.ReadBits<uint32_t>(24);
metaData.m_maximum_frame_size = reader.ReadBits<uint32_t>(24);
metaData.m_sample_rate = reader.ReadBits<uint32_t>(20);
metaData.m_number_of_channels = static_cast<uint8_t>(reader.ReadBits<uint8_t>(3) + 1);
metaData.m_bits_per_sample = static_cast<uint8_t>(reader.ReadBits<uint8_t>(5) + 1);
metaData.m_total_samples = reader.ReadBits<uint64_t>(36);
reader.ReadBuffer(metaData.m_md5_signature, 128);
}
bool GetFlacMetaData(std::istream& stream, FlacMetaData& metaData)
{
try
{
uint32_t readMagic;
stream.read(reinterpret_cast<char*>(&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<uint8_t>(1),
static_cast<MetaDataBlockType>(reader.ReadBits<uint8_t>(7)),
reader.ReadBits<uint32_t>(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<const char*>(data), dataSize));
return GetFlacMetaData(ss, metaData);
}
} // namespace flac

View File

@ -1,22 +1,26 @@
#pragma once
#include <cstdint>
#include <istream>
#include <memory>
class FlacDecoder
namespace flac
{
public:
FlacDecoder() = default;
virtual ~FlacDecoder() = default;
class FlacMetaData
{
public:
uint16_t m_minimum_block_size;
uint16_t m_maximum_block_size;
uint32_t m_minimum_frame_size;
uint32_t m_maximum_frame_size;
uint32_t m_sample_rate;
uint8_t m_number_of_channels;
uint8_t m_bits_per_sample;
uint64_t m_total_samples;
uint8_t m_md5_signature[16];
FlacDecoder(const FlacDecoder& other) = default;
FlacDecoder(FlacDecoder&& other) noexcept = default;
FlacDecoder& operator=(const FlacDecoder& other) = default;
FlacDecoder& operator=(FlacDecoder&& other) noexcept = default;
FlacMetaData();
};
virtual bool Decode() = 0;
virtual unsigned int GetFrameCount() = 0;
virtual unsigned int GetFrameRate() = 0;
virtual unsigned int GetNumChannels() = 0;
static std::unique_ptr<FlacDecoder> Create(void* data, size_t length);
};
bool GetFlacMetaData(std::istream& stream, FlacMetaData& metaData);
bool GetFlacMetaData(const void* data, size_t dataSize, FlacMetaData& metaData);
} // namespace flac

View File

@ -159,17 +159,17 @@ public:
soundData = std::make_unique<char[]>(soundSize);
flacFile.m_stream->read(soundData.get(), soundSize);
const auto decoder = FlacDecoder::Create(soundData.get(), soundSize);
if (decoder->Decode())
flac::FlacMetaData metaData;
if (flac::GetFlacMetaData(soundData.get(), soundSize, metaData))
{
const auto frameRateIndex = INDEX_FOR_FRAMERATE[decoder->GetFrameRate()];
const auto frameRateIndex = INDEX_FOR_FRAMERATE[metaData.m_sample_rate];
SoundAssetBankEntry entry{
soundId,
soundSize,
static_cast<size_t>(m_current_offset),
decoder->GetFrameCount(),
static_cast<unsigned>(metaData.m_total_samples),
frameRateIndex,
static_cast<unsigned char>(decoder->GetNumChannels()),
metaData.m_number_of_channels,
sound.m_looping,
8,
};

View File

@ -0,0 +1,39 @@
#include "Sound/FlacDecoder.h"
#include <catch2/catch_test_macros.hpp>
#include <catch2/generators/catch_generators.hpp>
namespace flac
{
TEST_CASE("FlacDecoder: Ensure properly decodes flac stream info", "[sound][flac]")
{
// clang-format off
constexpr uint8_t testData[]
{
// Magic
'f', 'L', 'a', 'C',
// Block header
0x00, 0x00, 0x00, 0x22,
// StreamInfo block
0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0B, 0xB8, 0x02, 0xF0, 0x00,
0x02, 0xF9, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
};
// clang-format on
FlacMetaData metaData;
const auto result = GetFlacMetaData(testData, sizeof(testData), metaData);
REQUIRE(result == true);
REQUIRE(metaData.m_minimum_block_size == 1024);
REQUIRE(metaData.m_maximum_block_size == 1024);
REQUIRE(metaData.m_minimum_frame_size == 0);
REQUIRE(metaData.m_maximum_frame_size == 0);
REQUIRE(metaData.m_sample_rate == 48000);
REQUIRE(metaData.m_number_of_channels == 2);
REQUIRE(metaData.m_bits_per_sample == 16);
REQUIRE(metaData.m_total_samples == 194870);
}
} // namespace flac

1
thirdparty/flac vendored

@ -1 +0,0 @@
Subproject commit 1b04fafb51aac0c3b7f0118a7bf7c93f6a60d824

47
thirdparty/flac.lua vendored
View File

@ -1,47 +0,0 @@
flac = {}
function flac:include(includes)
if includes:handle(self:name()) then
includedirs {
path.join(ThirdPartyFolder(), "flac")
}
end
end
function flac:link(links)
links:add(self:name())
end
function flac:use()
end
function flac:name()
return "flac"
end
function flac:project()
local folder = ThirdPartyFolder()
local includes = Includes:create()
project(self:name())
targetdir(TargetDirectoryLib)
location "%{wks.location}/thirdparty/%{prj.name}"
kind "StaticLib"
language "C"
files {
path.join(folder, "flac/foxen/*.h"),
path.join(folder, "flac/foxen/*.c")
}
defines {
"_CRT_SECURE_NO_WARNINGS",
"_CRT_NONSTDC_NO_DEPRECATE"
}
self:include(includes)
-- Disable warnings. They do not have any value to us since it is not our code.
warnings "off"
end