mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-04-19 15:52:53 +00:00
chore: drop third party lib for reading flac header
This commit is contained in:
parent
05ce73049d
commit
4591787989
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
};
|
||||
|
39
test/ObjCommonTests/Sound/FlacDecoderTest.cpp
Normal file
39
test/ObjCommonTests/Sound/FlacDecoderTest.cpp
Normal 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
thirdparty/flac
vendored
@ -1 +0,0 @@
|
||||
Subproject commit 1b04fafb51aac0c3b7f0118a7bf7c93f6a60d824
|
47
thirdparty/flac.lua
vendored
47
thirdparty/flac.lua
vendored
@ -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
|
Loading…
x
Reference in New Issue
Block a user