add flac decoding to correctly add flac sounds to sound banks

This commit is contained in:
Alex 2024-02-09 10:49:55 -05:00
parent aa2bebedde
commit 42c4068d2a
8 changed files with 184 additions and 20 deletions

3
.gitmodules vendored
View File

@ -13,3 +13,6 @@
[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,6 +83,7 @@ workspace "OpenAssetTools"
-- ThirdParty
-- ========================
include "thirdparty/catch2.lua"
include "thirdparty/flac.lua"
include "thirdparty/libtomcrypt.lua"
include "thirdparty/libtommath.lua"
include "thirdparty/json.lua"
@ -94,6 +95,7 @@ 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,6 +15,7 @@ function ObjCommon:link(links)
links:linkto(Utils)
links:linkto(Common)
links:linkto(minizip)
links:linkto(flac)
end
function ObjCommon:use()
@ -48,4 +49,5 @@ function ObjCommon:project()
self:include(includes)
Utils:include(includes)
flac:include(includes)
end

View File

@ -0,0 +1,77 @@
#include <foxen/flac.h>
#include "FlacDecoder.h"
class fx_flac_raii
{
public:
fx_flac_raii()
{
ptr = FX_FLAC_ALLOC_DEFAULT();
}
~fx_flac_raii()
{
free(ptr);
}
operator fx_flac_t* ()
{
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)
{
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;
}
};
std::unique_ptr<FlacDecoder> FlacDecoder::Create(void* data, size_t length)
{
return std::make_unique<FlacDecoderImpl>(data, length);
}

View File

@ -0,0 +1,21 @@
#pragma once
#include <istream>
class FlacDecoder
{
public:
FlacDecoder() = default;
virtual ~FlacDecoder() = default;
FlacDecoder(const FlacDecoder& other) = default;
FlacDecoder(FlacDecoder&& other) noexcept = default;
FlacDecoder& operator=(const FlacDecoder& other) = default;
FlacDecoder& operator=(FlacDecoder&& other) noexcept = default;
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);
};

View File

@ -3,9 +3,11 @@
#include "Crypto.h"
#include "ObjContainer/SoundBank/SoundBankTypes.h"
#include "Sound/WavTypes.h"
#include "Sound/FlacDecoder.h"
#include "Utils/Alignment.h"
#include "Utils/FileUtils.h"
#include <cstring>
#include <filesystem>
#include <iostream>
@ -128,13 +130,6 @@ public:
soundSize = static_cast<size_t>(wavFile.m_length - sizeof(WavHeader));
auto frameCount = soundSize / (header.formatChunk.nChannels * (header.formatChunk.wBitsPerSample / 8));
if (!sound.streamed && header.formatChunk.nSamplesPerSec != 48000)
{
std::cout << "WARNING: \"" << soundFilePath << "\" has a framerate of " << header.formatChunk.nSamplesPerSec
<< ". Loaded sounds are recommended to have a framerate of 48000. This sound may not work on all games!" << std::endl;
}
auto frameRateIndex = INDEX_FOR_FRAMERATE[header.formatChunk.nSamplesPerSec];
SoundAssetBankEntry entry{
@ -161,21 +156,31 @@ public:
{
soundSize = static_cast<size_t>(flacFile.m_length);
SoundAssetBankEntry entry{
soundId,
soundSize,
static_cast<size_t>(m_current_offset),
0,
0,
0,
0,
8,
};
m_entries.push_back(entry);
soundData = std::make_unique<char[]>(soundSize);
flacFile.m_stream->read(soundData.get(), soundSize);
auto decoder = FlacDecoder::Create(soundData.get(), soundSize);
if (decoder->Decode())
{
auto frameRateIndex = INDEX_FOR_FRAMERATE[decoder->GetFrameRate()];
SoundAssetBankEntry entry{
soundId,
soundSize,
static_cast<size_t>(m_current_offset),
decoder->GetFrameCount(),
frameRateIndex,
decoder->GetNumChannels(),
sound.looping,
8,
};
m_entries.push_back(entry);
}
else
{
std::cerr << "Unable to decode .flac file for sound " << soundFilePath << std::endl;
return false;
}
}
else
{
@ -184,6 +189,12 @@ public:
}
}
auto lastEntry = m_entries.rbegin();
if (!sound.streamed && lastEntry->frameRateIndex != 6)
{
std::cout << "WARNING: Loaded sound \"" << soundFilePath << "\" should have a framerate of 48000 but doesn't. This sound may not work on all games!" << std::endl;
}
// calculate checksum
SoundAssetBankChecksum checksum{};

1
thirdparty/flac vendored Submodule

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

47
thirdparty/flac.lua vendored Normal file
View File

@ -0,0 +1,47 @@
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