Recreated audio export system to something more generic

This commit is contained in:
KiritoDv
2024-11-21 00:05:17 -06:00
committed by Lywx
parent 4451ae28db
commit 528ea6090b
33 changed files with 1144 additions and 12 deletions
+34 -8
View File
@@ -26,10 +26,6 @@
#include "factories/AssetArrayFactory.h"
#include "factories/ViewportFactory.h"
#include "factories/sm64/audio/BankFactory.h"
#include "factories/sm64/audio/AudioHeaderFactory.h"
#include "factories/sm64/audio/SampleFactory.h"
#include "factories/sm64/audio/SequenceFactory.h"
#include "factories/sm64/AnimationFactory.h"
#include "factories/sm64/BehaviorScriptFactory.h"
#include "factories/sm64/CollisionFactory.h"
@@ -66,6 +62,21 @@
#include "factories/sf64/ObjInitFactory.h"
#include "factories/sf64/TriangleFactory.h"
#include "factories/naudio/v0/AudioHeaderFactory.h"
#include "factories/naudio/v0/BankFactory.h"
#include "factories/naudio/v0/SampleFactory.h"
#include "factories/naudio/v0/SequenceFactory.h"
#include "factories/naudio/v1/AudioContext.h"
#include "factories/naudio/v1/SoundFontFactory.h"
#include "factories/naudio/v1/AudioTableFactory.h"
#include "factories/naudio/v1/InstrumentFactory.h"
#include "factories/naudio/v1/SampleFactory.h"
#include "factories/naudio/v1/DrumFactory.h"
#include "factories/naudio/v1/EnvelopeFactory.h"
#include "factories/naudio/v1/LoopFactory.h"
#include "factories/naudio/v1/BookFactory.h"
using namespace std::chrono;
namespace fs = std::filesystem;
@@ -95,10 +106,6 @@ void Companion::Init(const ExportType type) {
this->RegisterFactory("VP", std::make_shared<ViewportFactory>());
// SM64 specific
this->RegisterFactory("SM64:AUDIO_HEADER", std::make_shared<AudioHeaderFactory>());
this->RegisterFactory("SM64:SEQUENCE", std::make_shared<SequenceFactory>());
this->RegisterFactory("SM64:SAMPLE", std::make_shared<SampleFactory>());
this->RegisterFactory("SM64:BANK", std::make_shared<BankFactory>());
this->RegisterFactory("SM64:DIALOG", std::make_shared<SM64::DialogFactory>());
this->RegisterFactory("SM64:TEXT", std::make_shared<SM64::TextFactory>());
this->RegisterFactory("SM64:DICTIONARY", std::make_shared<SM64::DictionaryFactory>());
@@ -137,6 +144,22 @@ void Companion::Init(const ExportType type) {
this->RegisterFactory("SF64:COLPOLY", std::make_shared<SF64::ColPolyFactory>());
this->RegisterFactory("SF64:TRIANGLE", std::make_shared<SF64::TriangleFactory>());
// NAudio specific
this->RegisterFactory("NAUDIO:V0:AUDIO_HEADER", std::make_shared<AudioHeaderFactory>());
this->RegisterFactory("NAUDIO:V0:SEQUENCE", std::make_shared<SequenceFactory>());
this->RegisterFactory("NAUDIO:V0:SAMPLE", std::make_shared<SampleFactory>());
this->RegisterFactory("NAUDIO:V0:BANK", std::make_shared<BankFactory>());
this->RegisterFactory("NAUDIO:V1:AUDIO_SETUP", std::make_shared<AudioContextFactory>());
this->RegisterFactory("NAUDIO:V1:AUDIO_TABLE", std::make_shared<AudioTableFactory>());
this->RegisterFactory("NAUDIO:V1:SOUND_FONT", std::make_shared<SoundFontFactory>());
this->RegisterFactory("NAUDIO:V1:INSTRUMENT", std::make_shared<InstrumentFactory>());
this->RegisterFactory("NAUDIO:V1:DRUM", std::make_shared<DrumFactory>());
this->RegisterFactory("NAUDIO:V1:SAMPLE", std::make_shared<NSampleFactory>());
this->RegisterFactory("NAUDIO:V1:ENVELOPE", std::make_shared<EnvelopeFactory>());
this->RegisterFactory("NAUDIO:V1:ADPCM_LOOP", std::make_shared<ADPCMLoopFactory>());
this->RegisterFactory("NAUDIO:V1:ADPCM_BOOK", std::make_shared<ADPCMBookFactory>());
this->Process();
}
@@ -1451,6 +1474,9 @@ std::optional<YAML::Node> Companion::AddAsset(YAML::Node asset) {
} else {
output = this->NormalizeAsset(typeId + "_" + Torch::to_hex(offset, false));
}
std::replace(output.begin(), output.end(), ':', '_');
asset["autogen"] = true;
asset["symbol"] = output;
+3 -1
View File
@@ -111,5 +111,7 @@ public:
return std::nullopt;
}
private:
virtual std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() = 0;
virtual std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() {
return {};
}
};
+13 -3
View File
@@ -15,9 +15,6 @@ enum class ResourceType {
Array = 0x4F415252, // OARR
Blob = 0x4F424C42, // OBLB
Texture = 0x4F544558, // OTEX
Bank = 0x42414E4B, // BANK
Sample = 0x41554643, // AIFC
Sequence = 0x53455143, // SEQC
Lights = 0x46669697, // LGTS
Vec3f = 0x56433346, // VC3F
Vec3s = 0x56433353, // VC3S
@@ -63,5 +60,18 @@ enum class ResourceType {
ScriptCmd = 0x53434D44, // SCMD
Hitbox = 0x48544258, // HTBX
ObjectInit = 0x4F42494E, // OBIN
// NAudio v0
Bank = 0x42414E4B, // BANK
Sample = 0x41554643, // AIFC
Sequence = 0x53455143, // SEQC
// NAudio v1
SoundFont = 0x53464E54, // SFNT
Drum = 0x4452554D, // DRUM
Instrument = 0x494E5354, // INST
AdpcmLoop = 0x4150434C, // APCL
AdpcmBook = 0x41504342, // APCB
Envelope = 0x45564C50, // EVLP
};
} // namespace LUS
+78
View File
@@ -0,0 +1,78 @@
#include "AudioContext.h"
#include "spdlog/spdlog.h"
#include "Companion.h"
std::unordered_map<AudioTableType, std::unordered_map<uint32_t, AudioTableEntry>> AudioContext::tables;
std::unordered_map<AudioTableType, std::vector<uint8_t>> AudioContext::data;
std::unordered_map<AudioTableType, AudioTableData> AudioContext::tableData;
std::optional<std::shared_ptr<IParsedData>> AudioContextFactory::parse(std::vector<uint8_t>& buffer, YAML::Node& node) {
auto seq = GetSafeNode<YAML::Node>(node, "audio_seq");
auto seqSize = GetSafeNode<uint32_t>(seq, "size");
auto seqOffset = GetSafeNode<uint32_t>(seq, "offset");
auto bank = GetSafeNode<YAML::Node>(node, "audio_bank");
auto bankSize = GetSafeNode<uint32_t>(bank, "size");
auto bankOffset = GetSafeNode<uint32_t>(bank, "offset");
auto table = GetSafeNode<YAML::Node>(node, "audio_table");
auto tableSize = GetSafeNode<uint32_t>(table, "size");
auto tableOffset = GetSafeNode<uint32_t>(table, "offset");
AudioContext::data[AudioTableType::SEQ_TABLE] = std::vector<uint8_t>(buffer.begin() + seqOffset, buffer.begin() + seqOffset + seqSize);
AudioContext::data[AudioTableType::FONT_TABLE] = std::vector<uint8_t>(buffer.begin() + bankOffset, buffer.begin() + bankOffset + bankSize);
AudioContext::data[AudioTableType::SAMPLE_TABLE] = std::vector<uint8_t>(buffer.begin() + tableOffset, buffer.begin() + tableOffset + tableSize);
SPDLOG_INFO("Sequence Table 0x{:X}", seqOffset);
SPDLOG_INFO("Sound Font Table 0x{:X}", bankOffset);
SPDLOG_INFO("Sample Bank Table 0x{:X}", tableOffset);
return std::nullopt;
}
LUS::BinaryReader AudioContext::MakeReader(AudioTableType type, uint32_t offset) {
auto entry = AudioContext::data[type];
LUS::BinaryReader reader(entry.data(), entry.size());
reader.SetEndianness(Torch::Endianness::Big);
reader.Seek(offset, LUS::SeekOffsetType::Start);
return reader;
}
TunedSample AudioContext::LoadTunedSample(LUS::BinaryReader& reader, uint32_t parent, uint32_t sampleBankId) {
auto sampleAddr = reader.ReadUInt32();
auto tuning = reader.ReadFloat();
if(sampleAddr == 0){
if(tuning != 0.0f){
throw std::runtime_error("The provided tuned sample is invalid");
}
return { 0, 0, 0.0f };
}
YAML::Node node;
node["type"] = "NAUDIO:V1:SAMPLE";
node["parent"] = parent;
node["offset"] = parent + sampleAddr;
node["sampleBankId"] = sampleBankId;
Companion::Instance->AddAsset(node);
return { parent + sampleAddr, sampleBankId, tuning };
}
uint64_t AudioContext::GetPathByAddr(uint32_t addr) {
if(addr == 0){
return 0;
}
auto dec = Companion::Instance->GetNodeByAddr(addr);
if (dec.has_value()) {
std::string path = std::get<0>(dec.value());
uint64_t hash = CRC64(path.c_str());
SPDLOG_INFO("Found path of 0x{:X} {}", addr, path);
return hash;
} else {
throw std::runtime_error("Failed to find node by addr");
}
}
+35
View File
@@ -0,0 +1,35 @@
#pragma once
#include "AudioTableFactory.h"
#include "factories/naudio/v0/AudioHeaderFactory.h"
#include "factories/BaseFactory.h"
struct TunedSample {
uint32_t sample;
uint32_t sampleBankId;
float tuning;
};
class AudioContext {
public:
static std::unordered_map<AudioTableType, std::unordered_map<uint32_t, AudioTableEntry>> tables;
static std::unordered_map<AudioTableType, std::shared_ptr<AudioTableData>> tableData;
static std::unordered_map<AudioTableType, std::vector<uint8_t>> data;
static LUS::BinaryReader MakeReader(AudioTableType type, uint32_t offset);
static TunedSample LoadTunedSample(LUS::BinaryReader& reader, uint32_t parent, uint32_t sampleBankId);
static uint64_t GetPathByAddr(uint32_t addr);
};
class AudioContextFactory : public BaseFactory {
public:
std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override;
inline std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override {
return {
REGISTER(Header, AudioDummyExporter)
REGISTER(Binary, AudioDummyExporter)
REGISTER(Code, AudioDummyExporter)
};
}
};
@@ -0,0 +1,131 @@
#include "AudioTableFactory.h"
#include "utils/Decompressor.h"
#include "spdlog/spdlog.h"
#include "AudioContext.h"
#include "Companion.h"
#include <iomanip>
#define INT2(c) std::dec << std::setfill(' ') << c
#define HEX(c) "0x" << std::hex << std::setw(4) << std::setfill('0') << c
#define ADDR(c) "0x" << std::hex << std::setw(5) << std::setfill('0') << c
static const std::unordered_map <std::string, AudioTableType> gTableTypes = {
{ "SOUNDFONT", AudioTableType::FONT_TABLE },
{ "SAMPLE", AudioTableType::SAMPLE_TABLE },
{ "SEQUENCE", AudioTableType::SEQ_TABLE }
};
ExportResult AudioTableHeaderExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement ) {
const auto symbol = GetSafeNode(node, "symbol", entryName);
if(Companion::Instance->IsOTRMode()){
write << "static const ALIGN_ASSET(2) char " << symbol << "[] = \"__OTR__" << (*replacement) << "\";\n\n";
return std::nullopt;
}
write << "extern AudioTable " << symbol << ";\n";
return std::nullopt;
}
ExportResult AudioTableCodeExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement ) {
const auto symbol = GetSafeNode(node, "symbol", entryName);
const auto offset = GetSafeNode<uint32_t>(node, "offset");
auto table = std::static_pointer_cast<AudioTableData>(raw);
if(table->type == AudioTableType::FONT_TABLE){
write << "#define SOUNDFONT_ENTRY(offset, size, medium, cachePolicy, bank1, bank2, numInst, numDrums) { \\\n";
write << fourSpaceTab << "offset, size, medium, cachePolicy, (((bank1) &0xFF) << 8) | ((bank2) &0xFF), \\\n";
write << fourSpaceTab << fourSpaceTab << "(((numInst) &0xFF) << 8) | ((numDrums) &0xFF) \\\n}\n\n";
}
write << "AudioTable " << symbol << " = {\n";
write << fourSpaceTab << "{ " << table->entries.size() << ", " << table->medium << ", " << table->addr << " },\n";
write << fourSpaceTab << "{ \n";
for(size_t i = 0; i < table->entries.size(); i++){
auto &entry = table->entries[i];
if(table->type == AudioTableType::FONT_TABLE) {
uint32_t numInstruments = (entry.shortData2 >> 8) & 0xFFu;
uint32_t numDrums = entry.shortData2 & 0xFFu;
uint32_t sampleBankId1 = (entry.shortData1 >> 8) & 0xFFu;
uint32_t sampleBankId2 = entry.shortData1 & 0xFFu;
write << fourSpaceTab << fourSpaceTab << "SOUNDFONT_ENTRY(" << ADDR(entry.addr) << ", " << HEX(entry.size) << ", " << INT2((uint32_t) entry.medium) << ", " << INT2((uint32_t) entry.cachePolicy) << ", " << INT2(numInstruments) << ", " << INT2(numDrums) << ", " << INT2(sampleBankId1) << ", " << INT2(sampleBankId2) << "),\n" ;
} else {
write << fourSpaceTab << fourSpaceTab << "{ " << ADDR(entry.addr) << ", " << HEX(entry.size) << ", " << (uint32_t) entry.medium << ", " << (uint32_t) entry.cachePolicy << " },\n";
}
}
write << fourSpaceTab << "},\n";
write << "};\n";
if (Companion::Instance->IsDebug()) {
write << "// Count: " << table->entries.size() << "\n";
}
return offset + 0x10 + (table->entries.size() * sizeof(AudioTableEntry));
}
ExportResult AudioTableBinaryExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement ) {
auto writer = LUS::BinaryWriter();
// auto data = std::static_pointer_cast<AudioTableEntry>(raw)->mBuffer;
// WriteHeader(writer, Torch::ResourceType::Blob, 0);
// writer.Write((uint32_t) data.size());
// writer.Write((char*) data.data(), data.size());
// writer.Finish(write);
return std::nullopt;
}
std::optional<std::shared_ptr<IParsedData>> AudioTableFactory::parse(std::vector<uint8_t>& buffer, YAML::Node& node) {
// Parse table entry
auto offset = GetSafeNode<uint32_t>(node, "offset");
auto [_, segment] = Decompressor::AutoDecode(node, buffer);
LUS::BinaryReader reader(segment.data, segment.size);
reader.SetEndianness(Torch::Endianness::Big);
auto format = GetSafeNode<std::string>(node, "format");
std::transform(format.begin(), format.end(), format.begin(), ::toupper);
if(!gTableTypes.contains(format)) {
return std::nullopt;
}
auto type = gTableTypes.at(format);
auto count = reader.ReadInt16();
auto bmedium = reader.ReadInt16();
auto baddr = reader.ReadUInt32();
reader.Read(0x8); // PAD
SPDLOG_INFO("count: {}", count);
SPDLOG_INFO("medium: {}", bmedium);
SPDLOG_INFO("addr: {}", baddr);
std::vector<AudioTableEntry> entries;
for(size_t i = 0; i < count; i++){
auto addr = reader.ReadUInt32();
auto size = reader.ReadUInt32();
auto medium = reader.ReadInt8();
auto policy = reader.ReadInt8();
auto sd1 = reader.ReadInt16();
auto sd2 = reader.ReadInt16();
auto sd3 = reader.ReadInt16();
auto entry = AudioTableEntry { addr, size, medium, policy, sd1, sd2, sd3 };
entries.push_back(entry);
if(type == AudioTableType::FONT_TABLE){
YAML::Node font;
font["type"] = "NAUDIO:V1:SOUND_FONT";
font["offset"] = addr;
Companion::Instance->AddAsset(font);
}
AudioContext::tables[type][addr] = entry;
}
auto table = std::make_shared<AudioTableData>(bmedium, offset, type, entries);
AudioContext::tableData[type] = table;
return table;
}
@@ -0,0 +1,53 @@
#pragma once
#include <factories/BaseFactory.h>
struct AudioTableEntry {
uint32_t addr;
uint32_t size;
int8_t medium;
int8_t cachePolicy;
int16_t shortData1;
int16_t shortData2;
int16_t shortData3;
};
enum class AudioTableType {
SAMPLE_TABLE,
SEQ_TABLE,
FONT_TABLE
};
class AudioTableData : public IParsedData {
public:
int16_t medium;
uint32_t addr;
AudioTableType type;
std::vector<AudioTableEntry> entries;
AudioTableData(int16_t medium, uint32_t addr, AudioTableType type, std::vector<AudioTableEntry> entries) : medium(medium), addr(addr), type(type), entries(entries) {}
};
class AudioTableHeaderExporter : public BaseExporter {
ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
};
class AudioTableBinaryExporter : public BaseExporter {
ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
};
class AudioTableCodeExporter : public BaseExporter {
ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
};
class AudioTableFactory : public BaseFactory {
public:
std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override;
inline std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override {
return {
REGISTER(Header, AudioTableHeaderExporter)
REGISTER(Binary, AudioTableBinaryExporter)
REGISTER(Code, AudioTableCodeExporter)
};
}
};
+58
View File
@@ -0,0 +1,58 @@
#include "BookFactory.h"
#include "utils/Decompressor.h"
#include "Companion.h"
ExportResult ADPCMBookHeaderExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement) {
const auto symbol = GetSafeNode(node, "symbol", entryName);
if(Companion::Instance->IsOTRMode()){
write << "static const ALIGN_ASSET(2) char " << symbol << "[] = \"__OTR__" << (*replacement) << "\";\n\n";
return std::nullopt;
}
write << "extern AdpcmBook " << symbol << ";\n";
return std::nullopt;
}
ExportResult ADPCMBookCodeExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement ) {
return std::nullopt;
}
ExportResult ADPCMBookBinaryExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement ) {
auto writer = LUS::BinaryWriter();
auto data = std::static_pointer_cast<ADPCMBookData>(raw);
WriteHeader(writer, Torch::ResourceType::AdpcmBook, 0);
writer.Write(data->order);
writer.Write(data->numPredictors);
writer.Write((uint32_t) data->book.size());
for(auto& page : data->book){
writer.Write(page);
}
writer.Finish(write);
return std::nullopt;
}
std::optional<std::shared_ptr<IParsedData>> ADPCMBookFactory::parse(std::vector<uint8_t>& buffer, YAML::Node& node) {
auto offset = GetSafeNode<uint32_t>(node, "offset");
auto reader = AudioContext::MakeReader(AudioTableType::FONT_TABLE, offset);
auto book = std::make_shared<ADPCMBookData>();
book->order = reader.ReadInt32();
book->numPredictors = reader.ReadInt32();
size_t length = 8 * book->order * book->numPredictors;
if(length > 0x40){
return std::nullopt;
}
for(size_t i = 0; i < length; i++){
book->book.push_back(reader.ReadInt16());
}
return book;
}

Some files were not shown because too many files have changed in this diff Show More