Migrated disassemble audio into audio manager class

This commit is contained in:
KiritoDv
2023-09-01 00:51:51 -06:00
parent 3bb2a8a13c
commit 1134723029
9 changed files with 693 additions and 310 deletions
+1 -1
View File
@@ -1121,7 +1121,7 @@
"sound/sequences/us/20_cutscene_ending.m64": [1882,{"us":[8174192]}],
"sound/sequences/us/21_menu_file_select.m64": [781,{"us":[8176080]}],
"sound/sequences/us/22_cutscene_lakitu.m64": [313,{"us":[8176864]}],
"@sound.aiff": {
"@sound_data": {
"us": {
"bank_sets": [8177184, 160],
"sound_ctl": [5748512, 97856],
+23
View File
@@ -2,6 +2,7 @@
#include "MemoryStream.h"
#include <cmath>
#include <stdexcept>
#include <locale>
LUS::BinaryReader::BinaryReader(char* nBuffer, size_t nBufferSize) {
mStream = std::make_shared<MemoryStream>(nBuffer, nBufferSize);
@@ -113,6 +114,28 @@ uint64_t LUS::BinaryReader::ReadUInt64() {
return result;
}
unsigned short LUS::BinaryReader::ReadUShort() {
unsigned short result = 0;
mStream->Read((char*)&result, sizeof(unsigned short));
if (mEndianness != Endianness::Native) {
result = BSWAP16(result);
}
return result;
}
short LUS::BinaryReader::ReadShort() {
short result = 0;
mStream->Read((char*)&result, sizeof(short));
if (mEndianness != Endianness::Native) {
result = BSWAP16(result);
}
return result;
}
float LUS::BinaryReader::ReadFloat() {
float result = NAN;
+2
View File
@@ -34,6 +34,8 @@ class BinaryReader {
uint16_t ReadUInt16();
uint32_t ReadUInt32();
uint64_t ReadUInt64();
unsigned short ReadUShort();
short ReadShort();
float ReadFloat();
double ReadDouble();
std::string ReadString();
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include <vector>
namespace PyUtils {
template<typename T>
std::vector<T> slice(std::vector<T> const &v, uint32_t m, uint32_t n = -1) {
auto first = v.cbegin() + m;
auto last = n == -1 ? v.cend() : v.cbegin() + n;
std::vector<T> vec(first, last);
return vec;
}
std::vector<uint32_t> range(uint32_t start, uint32_t end) {
std::vector<uint32_t> result;
for (uint32_t i = start; i < end; ++i) {
result.push_back(i);
}
return result;
}
}
+60
View File
@@ -0,0 +1,60 @@
#pragma once
#include <algorithm>
#include <any>
#include <iostream>
#include <vector>
#include <tuple>
template<class TupType, size_t... I>
void print_tuple(const TupType& _tup, std::index_sequence<I...>)
{
std::cout << "(";
(..., (std::cout << (I == 0? "" : ", ") << std::get<I>(_tup)));
std::cout << ")\n";
}
template<class... T>
void print_tuple(const std::tuple<T...>& _tup)
{
print_tuple(_tup, std::make_index_sequence<sizeof...(T)>());
}
template<typename T>
void variatic_min(std::vector<T> v, int &m) {
if (m > v.size()) m = v.size();
}
template<int N, typename... Ts>
using NthTypeOf = typename std::tuple_element<N, std::tuple<Ts...>>::type;
template <typename... Result, std::size_t... Indices>
auto vec_to_tup_helper(const std::vector<std::any>& v, std::index_sequence<Indices...>) {
return std::make_tuple(
std::any_cast<NthTypeOf<Indices, Result...>>(v[Indices])...
);
}
template <typename ...Result>
std::tuple<Result...> vec_to_tup(std::vector<std::any>& values) {
return vec_to_tup_helper<Result...>(values, std::make_index_sequence<sizeof...(Result)>());
}
template <typename T>
void celem_at(std::vector<T> v, int i, std::vector<std::any> &r) {
r.push_back(v[i]);
}
template<typename... Types>
std::vector<std::tuple<Types...>> zip(std::vector<Types>... args) {
int m = 0xFFFFFFFF;
std::vector<std::tuple<Types...>> res;
(variatic_min(args, m), ...);
for (int i = 0; i < m; i++) {
std::vector<std::any> vaux;
(celem_at(args, i, vaux), ...);
res.push_back(vec_to_tup<Types...>(vaux));
}
return res;
}
+4
View File
@@ -2,6 +2,7 @@
#include "storm/SWrapper.h"
#include "utils/MIODecoder.h"
#include "audio/AudioManager.h"
#include "factories/RawFactory.h"
#include "factories/BlobFactory.h"
#include "factories/AudioFactory.h"
@@ -36,6 +37,9 @@ void Companion::Start() {
std::cout << "Detected Rom Size: " << this->gRomData.size() << '\n';
AudioManager::Instance = new AudioManager();
AudioManager::Instance->initialize(this->gRomData);
this->ProcessAssets();
}
+442
View File
@@ -0,0 +1,442 @@
#include "AudioManager.h"
#include <vector>
#include <sstream>
#include <fstream>
#include <iostream>
#include <filesystem>
#include "hj/pyutils.h"
#include "binarytools/BinaryReader.h"
#include "hj/zip.h"
#include <nlohmann/json.hpp>
std::unordered_map<std::string, uint32_t> name_table;
AudioManager* AudioManager::Instance;
std::string gen_name(const std::string& prefix){
if(!name_table.contains(prefix)){
name_table[prefix] = 0;
}
name_table[prefix] += 1;
return prefix + "-" + std::to_string(name_table[prefix]);
}
AifcEntry SampleBank::AddSample(uint32_t addr, size_t sampleSize, const Book& book, const Loop& loop){
assert(sampleSize % 2 == 0);
if(sampleSize % 9 != 0){
assert(sampleSize % 9 == 1);
sampleSize -= 1;
}
AifcEntry entry;
if(this->entries.contains(addr)){
entry = this->entries[addr];
assert(entry.book == book);
assert(entry.loop == loop);
assert(entry.data.size() == sampleSize);
} else {
entry = { gen_name("aifc"), PyUtils::slice(this->data, addr, addr + sampleSize), book, loop };
this->entries[addr] = entry;
}
return entry;
}
void Bank::print() const {
std::cout << "Bank: " << name << std::endl;
std::cout << "Instruments: " << std::to_string(insts.size()) << std::endl;
std::cout << "Drums: " << std::to_string(drums.size()) << std::endl;
std::cout << "Samples: " << std::to_string(samples.size()) << std::endl;
std::cout << "Envelopes: " << std::to_string(envelopes.size()) << std::endl;
std::cout << "All Instruments: " << std::to_string(allInsts.size()) << std::endl;
std::cout << "Inst Offsets: " << std::to_string(instOffsets.size()) << std::endl;
std::cout << "Sample Bank: " << sampleBank.name << std::endl;
std::cout << "Sample Bank Offset: " << std::to_string(sampleBank.offset) << std::endl;
}
std::vector<Entry> AudioManager::parse_seq_file(std::vector<uint8_t>& buffer, uint32_t offset, bool isCTL){
std::vector<Entry> entries;
LUS::BinaryReader reader((char*) buffer.data(), buffer.size());
reader.SetEndianness(LUS::Endianness::Big);
reader.Seek(offset, LUS::SeekOffsetType::Start);
unsigned short magic = reader.ReadUInt16();
unsigned short num_entries = reader.ReadUInt16();
uint32_t prev = ALIGN(4 + num_entries * 8, 16);
assert(magic == (isCTL ? 1 : 2));
for (int i = 0; i < num_entries; ++i) {
reader.Seek((offset + 4) + (i * 8), LUS::SeekOffsetType::Start);
uint32_t addr = reader.ReadUInt32();
uint32_t length = reader.ReadUInt32();
if(isCTL){
assert(addr == prev);
} else {
assert(addr <= prev);
}
prev = std::max(prev, addr + length);
entries.push_back({addr, length});
}
reader.Close();
return entries;
}
CTLHeader AudioManager::parse_ctl_header(std::vector<uint8_t>& data){
LUS::BinaryReader reader((char*) data.data(), data.size());
reader.SetEndianness(LUS::Endianness::Big);
CTLHeader header = { reader.ReadUInt32(), reader.ReadUInt32(), reader.ReadUInt32() };
reader.Close();
return header;
}
std::optional<Sound> AudioManager::parse_sound(std::vector<uint8_t> data) {
LUS::BinaryReader reader((char*) data.data(), data.size());
reader.SetEndianness(LUS::Endianness::Big);
uint32_t addr = reader.ReadUInt32();
float tuning = reader.ReadFloat();
if(addr == 0){
assert(tuning == 0.0f);
return std::nullopt;
}
Sound sound = { addr, tuning };
reader.Close();
return sound;
}
Drum AudioManager::parse_drum(std::vector<uint8_t>& data, uint32_t addr) {
LUS::BinaryReader reader((char*) data.data(), data.size());
reader.SetEndianness(LUS::Endianness::Big);
std::string name = gen_name("drum");
uint8_t releaseRate = reader.ReadInt8();
uint8_t pan = reader.ReadInt8();
reader.Seek(12, LUS::SeekOffsetType::Start);
Sound sound = parse_sound(PyUtils::slice(data, 4, 12)).value();
uint32_t envOffset = reader.ReadInt32();
assert(envOffset != 0);
Drum drum = { name, addr, releaseRate, pan, envOffset, sound };
return drum;
}
Inst AudioManager::parse_inst(std::vector<uint8_t>& data, uint32_t addr) {
std::string name = gen_name("inst");
uint8_t normalRangeLo = data[1];
uint8_t normalRangeHi = data[2];
uint8_t releaseRate = data[3];
uint32_t envAddr;
memcpy(&envAddr, (char*) data.data() + 4, 4);
envAddr = BSWAP32(envAddr);
assert(envAddr != 0);
auto soundLo = parse_sound(PyUtils::slice(data, 8, 16));
auto soundMed = parse_sound(PyUtils::slice(data, 16, 24));
auto soundHi = parse_sound(PyUtils::slice(data, 24));
if (soundLo == std::nullopt) {
assert(normalRangeLo == 0);
}
if (soundHi == std::nullopt) {
assert(normalRangeHi == 127);
}
Inst inst = { name, addr, releaseRate, normalRangeLo, normalRangeHi, envAddr, soundLo, soundMed, soundHi };
return inst;
}
Loop AudioManager::parse_loop(uint32_t addr, std::vector<uint8_t>& bankData){
LUS::BinaryReader reader((char*) bankData.data(), bankData.size());
reader.SetEndianness(LUS::Endianness::Big);
reader.Seek(addr, LUS::SeekOffsetType::Start);
std::optional<std::vector<short>> state = std::nullopt;
uint32_t start = reader.ReadUInt32();
uint32_t end = reader.ReadUInt32();
int32_t count = reader.ReadInt32();
uint32_t pad = reader.ReadUInt32();
if(count != 0){
state = std::vector<short>();
for (size_t i = 0; i < 16; ++i) {
state.value().push_back(reader.ReadShort());
}
}
Loop loop = { start, end, count, pad, state };
return loop;
}
Book AudioManager::parse_book(uint32_t addr, std::vector<uint8_t>& bankData){
LUS::BinaryReader reader((char*) bankData.data(), bankData.size());
reader.SetEndianness(LUS::Endianness::Big);
reader.Seek(addr, LUS::SeekOffsetType::Start);
uint32_t order = reader.ReadInt32();
uint32_t npredictors = reader.ReadInt32();
std::vector<short> table;
std::vector<uint8_t> tableData = PyUtils::slice(bankData, addr + 8, addr + 8 + 16 * order * npredictors);
for (size_t i = 0; i < ( 16 * order * npredictors ); i += 2) {
short dtable;
memcpy(&dtable, tableData.data() + i, 2);
table.push_back(BSWAP16(dtable));
}
Book book = { order, npredictors, table };
return book;
}
AifcEntry AudioManager::parse_sample(std::vector<uint8_t>& data, std::vector<uint8_t>& bankData, SampleBank& sampleBank){
LUS::BinaryReader reader((char*) data.data(), data.size());
reader.SetEndianness(LUS::Endianness::Big);
uint32_t zero = reader.ReadUInt32();
uint32_t addr = reader.ReadUInt32();
uint32_t loop = reader.ReadUInt32();
uint32_t book = reader.ReadUInt32();
uint32_t sampleSize = reader.ReadUInt32();
assert(zero == 0);
assert(loop != 0);
assert(book != 0);
Loop loopData = parse_loop(loop, bankData);
Book bookData = parse_book(book, bankData);
reader.Close();
return sampleBank.AddSample(addr, sampleSize, bookData, loopData);
}
std::vector<EnvelopeData> AudioManager::parse_envelope(uint32_t addr, std::vector<uint8_t>& dataBank){
std::vector<EnvelopeData> entries;
LUS::BinaryReader reader((char*) dataBank.data(), dataBank.size());
reader.SetEndianness(LUS::Endianness::Big);
while(true){
reader.Seek(addr, LUS::SeekOffsetType::Start);
unsigned short delay = reader.ReadUShort();
unsigned short arg = reader.ReadUShort();
entries.emplace_back(delay, arg);
addr += 4;
if (1 <= (-delay) % ((uint32_t) pow(2, 16)) <= 3.0){
break;
}
}
reader.Close();
return entries;
}
Bank AudioManager::parse_ctl(CTLHeader header, std::vector<uint8_t> data, SampleBank bank, uint32_t index) {
name_table.clear();
std::ostringstream ss;
ss << std::hex << std::setw(2) << std::setfill('0') << index;
std::string name = ss.str();
uint32_t numInstruments = header.instruments;
uint32_t numDrums = header.numDrums;
char* rawData = (char*) data.data();
uint32_t drumBaseAddr;
memcpy(&drumBaseAddr, rawData, 4);
drumBaseAddr = BSWAP32(drumBaseAddr);
std::vector<uint32_t> drumOffsets;
if(numDrums != 0){
for (size_t i = 0; i < numDrums; ++i) {
uint32_t drumOffset;
memcpy(&drumOffset, rawData + drumBaseAddr + (i * 4), 4);
drumOffsets.push_back(BSWAP32(drumOffset));
}
}
uint32_t instrumentBaseAddr = 4;
std::vector<uint32_t> instrumentOffsets;
std::vector<uint32_t> instrumentList;
for (size_t i = 0; i < numInstruments; ++i) {
uint32_t instOffset;
memcpy(&instOffset, rawData + (instrumentBaseAddr + (i * 4)), 4);
instOffset = BSWAP32(instOffset);
if(instOffset == 0){
instrumentList.push_back(NONE);
} else {
instrumentOffsets.push_back(instOffset);
instrumentList.push_back(instOffset);
}
}
std::sort(instrumentOffsets.begin(), instrumentOffsets.end());
std::vector<Inst> insts;
for(auto &offset : instrumentOffsets){
auto rInst = PyUtils::slice(data, offset, offset + 32);
Inst inst = parse_inst(rInst, offset);
insts.push_back(inst);
}
std::vector<Drum> drums;
for(auto &offset : drumOffsets){
auto rDrum = PyUtils::slice(data, offset, offset + 16);
Drum drum = parse_drum(rDrum, offset);
drums.push_back(drum);
}
auto envOffsets = std::vector<uint32_t>();
auto sampleOffsets = std::vector<uint32_t>();
auto tunings = std::unordered_map<uint32_t, float>();
for(auto &inst : insts){
for(auto &sound : {inst.soundLo, inst.soundMed, inst.soundHi}){
if(sound.has_value()){
sampleOffsets.push_back(sound.value().offset);
tunings[sound.value().offset] = sound.value().tuning;
}
}
envOffsets.push_back(inst.envelope);
}
for(auto &drum : drums){
sampleOffsets.push_back(drum.sound.offset);
tunings[drum.sound.offset] = drum.sound.tuning;
envOffsets.push_back(drum.envelope);
}
// Put drums somewhere in the middle of the instruments to make sample
// addresses come in increasing order. (This logic isn't totally right,
// but it works for our purposes.)
std::vector<std::variant<Inst, std::vector<Drum>>> allInsts;
bool needDrums = !drums.empty();
for(auto &inst : insts){
std::vector<std::optional<Sound>> sounds = {inst.soundLo, inst.soundMed, inst.soundHi};
if(needDrums && std::any_of(sounds.cbegin(), sounds.cend(), [&drums](std::optional<Sound> sound){ return sound.has_value() && sound.value().offset > drums[0].sound.offset; })){
allInsts.emplace_back(drums);
needDrums = false;
}
allInsts.emplace_back(inst);
}
if(needDrums){
allInsts.emplace_back(drums);
}
std::unordered_map<uint32_t, AifcEntry> samples;
std::sort(sampleOffsets.begin(), sampleOffsets.end());
for(auto &offset : sampleOffsets){
auto rSample = PyUtils::slice(data, offset, offset + 20);
AifcEntry sample = parse_sample(rSample, data, bank);
for(auto &tuning : tunings){
sample.tunings.push_back(tuning.second);
}
samples[offset] = sample;
}
std::unordered_map<uint32_t, std::vector<EnvelopeData>> envData;
std::vector<uint32_t> usedEnvOffsets;
std::sort(envOffsets.begin(), envOffsets.end());
for(auto &offset : envOffsets){
auto env = parse_envelope(offset, data);
envData[offset] = env;
for(int i = 0; i < ALIGN(env.size(), 4); i++){
usedEnvOffsets.push_back(offset + (i * 4));
}
}
std::vector<uint32_t> unusedEnvOffsets;
if(!usedEnvOffsets.empty()){
size_t min = std::min_element(usedEnvOffsets.begin(), usedEnvOffsets.end()) - usedEnvOffsets.begin();
size_t max = std::max_element(usedEnvOffsets.begin(), usedEnvOffsets.end()) - usedEnvOffsets.begin();
for(size_t idx = min + 4; idx < max; idx += 4){
uint32_t addr = usedEnvOffsets[idx];
if(std::find(usedEnvOffsets.begin(), usedEnvOffsets.end(), addr) == usedEnvOffsets.end()){
unusedEnvOffsets.push_back(addr);
uint32_t stubMarker;
memcpy(&stubMarker, rawData + addr, 4);
stubMarker = BSWAP32(stubMarker);
assert(stubMarker == 0);
auto env = parse_envelope(addr, data);
envData[addr] = env;
for(int i = 0; i < ALIGN(env.size(), 4); i++){
usedEnvOffsets.push_back(addr + (i * 4));
}
}
}
}
std::unordered_map<uint32_t, Envelope> envelopes;
for(auto &entry : envData){
Envelope env = { gen_name("envelope"), entry.second };
envelopes[entry.first] = env;
}
Bank bankData = { name, bank, insts, drums, allInsts, instrumentList, envelopes, samples };
return bankData;
}
TBLFile AudioManager::parse_tbl(std::vector<uint8_t>& data, std::vector<Entry>& entries) {
TBLFile tbl;
std::unordered_map<uint32_t, std::string> cache;
for(auto &entry : entries){
if(!cache.contains(entry.offset)){
std::string name = gen_name("sample_bank");
SampleBank sampleBank = {
name, entry.offset, PyUtils::slice(data, entry.offset, entry.offset + entry.length)
};
tbl.banks.push_back(sampleBank);
tbl.map[name] = tbl.banks.size() - 1;
cache[entry.offset] = name;
}
tbl.tbls.push_back(cache[entry.offset]);
}
cache.clear();
return tbl;
}
void AudioManager::initialize(std::vector<uint8_t>& buffer) {
auto assets = nlohmann::json::parse(std::ifstream( "assets.json" ));
auto sound_data = assets["@sound_data"]["us"];
auto sound_tbl = sound_data["sound_tbl"];
auto sound_ctl = sound_data["sound_ctl"];
std::vector<Entry> tbl = parse_seq_file(buffer, sound_tbl[0], false);
std::vector<Entry> ctl = parse_seq_file(buffer, sound_ctl[0], true);
std::cout << "Table entries: " << tbl.size() << std::endl;
std::cout << "Control entries: " << ctl.size() << std::endl;
std::vector<uint8_t> tbl_data = PyUtils::slice(buffer, sound_tbl[0], (size_t) sound_tbl[0] + (size_t) sound_tbl[1]);
std::vector<uint8_t> ctl_data = PyUtils::slice(buffer, sound_ctl[0], (size_t) sound_ctl[0] + (size_t) sound_ctl[1]);
TBLFile tbl_file = parse_tbl(tbl_data, tbl);
std::cout << "TBLs: " << tbl_file.tbls.size() << std::endl;
std::cout << "Banks: " << tbl_file.banks.size() << std::endl;
std::cout << "Map: " << tbl_file.map.size() << std::endl;
auto zipped = zip(PyUtils::range(0, ctl.size()), ctl, tbl_file.tbls);
std::cout << "================================" << std::endl;
for (const auto& item : zipped) {
auto [index, ctrl, sample_bank_name] = item;
auto sample_bank = tbl_file.map[sample_bank_name];
auto entry = PyUtils::slice(ctl_data, ctrl.offset, ctrl.offset + ctrl.length);
auto headerRaw = PyUtils::slice(entry, 0, 16);
auto header = parse_ctl_header(headerRaw);
auto bank = parse_ctl(header, PyUtils::slice(entry, 16), tbl_file.banks[sample_bank], index);
banks.push_back(bank);
bank.print();
std::cout << "================================" << std::endl;
}
}
+135
View File
@@ -0,0 +1,135 @@
#pragma once
#include <vector>
#include <string>
#include <iostream>
#include <unordered_map>
#define NONE 0xFFFF
#define ALIGN(val, al) (size_t) ((val + (al - 1)) & -al)
struct Entry {
uint32_t offset;
uint32_t length;
};
struct Sound {
uint32_t offset;
float tuning;
bool operator==(const Sound& other) const {
return offset == other.offset && tuning == other.tuning;
}
};
struct Inst {
std::string name;
uint32_t offset;
uint8_t releaseRate;
uint8_t normalRangeLo;
uint8_t normalRangeHi;
uint32_t envelope;
std::optional<Sound> soundLo;
std::optional<Sound> soundMed;
std::optional<Sound> soundHi;
};
struct Book {
uint32_t order{};
uint32_t npredictors{};
std::vector<short> table;
bool operator==(const Book& other) const {
return order == other.order && npredictors == other.npredictors && table == other.table;
}
};
struct Drum {
std::string name;
uint32_t offset;
uint8_t releaseRate;
uint8_t pan;
uint32_t envelope;
Sound sound;
bool operator==(const Drum& other) const {
return name == other.name && offset == other.offset && releaseRate == other.releaseRate && pan == other.pan && envelope == other.envelope && sound == other.sound;
}
};
struct Loop {
uint32_t start{};
uint32_t end{};
int32_t count{};
uint32_t pad{};
std::optional<std::vector<short>> state;
bool operator==(const Loop& other) const {
return start == other.start && end == other.end && count == other.count && pad == other.pad && state == other.state;
}
};
struct AifcEntry {
std::string name;
std::vector<uint8_t> data;
Book book;
Loop loop;
std::vector<float> tunings;
};
struct SampleBank {
std::string name;
uint32_t offset;
std::vector<uint8_t> data;
std::unordered_map<uint32_t, AifcEntry> entries;
AifcEntry AddSample(uint32_t addr, size_t sampleSize, const Book& book, const Loop& loop);
};
struct TBLFile {
std::vector<SampleBank> banks;
std::vector<std::string> tbls;
std::unordered_map<std::string, uint32_t> map;
};
struct CTLHeader {
uint32_t instruments;
uint32_t numDrums;
uint32_t shared;
};
typedef std::pair<unsigned short, unsigned short> EnvelopeData;
struct Envelope {
std::string name;
std::vector<EnvelopeData> entries;
};
struct Bank {
std::string name;
SampleBank sampleBank;
std::vector<Inst> insts;
std::vector<Drum> drums;
std::vector<std::variant<Inst, std::vector<Drum>>> allInsts;
std::vector<uint32_t> instOffsets;
std::unordered_map<uint32_t, Envelope> envelopes;
std::unordered_map<uint32_t, AifcEntry> samples;
void print() const;
};
class AudioManager {
public:
static AudioManager* Instance;
void initialize(std::vector<uint8_t>& buffer);
private:
static std::vector<Entry> parse_seq_file(std::vector<uint8_t>& buffer, uint32_t offset, bool isCTL);
static CTLHeader parse_ctl_header(std::vector<uint8_t>& data);
static std::optional<Sound> parse_sound(std::vector<uint8_t> data);
static Drum parse_drum(std::vector<uint8_t>& data, uint32_t addr);
static Inst parse_inst(std::vector<uint8_t>& data, uint32_t addr);
static Loop parse_loop(uint32_t addr, std::vector<uint8_t>& bankData);
static Book parse_book(uint32_t addr, std::vector<uint8_t>& bankData);
static AifcEntry parse_sample(std::vector<uint8_t>& data, std::vector<uint8_t>& bankData, SampleBank& sampleBank);
static std::vector<EnvelopeData> parse_envelope(uint32_t addr, std::vector<uint8_t>& dataBank);
static Bank parse_ctl(CTLHeader header, std::vector<uint8_t> data, SampleBank bank, uint32_t index);
static TBLFile parse_tbl(std::vector<uint8_t>& data, std::vector<Entry>& entries);
std::vector<Bank> banks;
};
+3 -309
View File
@@ -1,313 +1,12 @@
#include "AudioFactory.h"
#include <vector>
#include <iostream>
#include <filesystem>
#include <sstream>
#include "Companion.h"
#include "utils/MIODecoder.h"
#include "binarytools/BinaryReader.h"
namespace fs = std::filesystem;
#define NONE 0xFFFF
std::unordered_map<std::string, uint32_t> nameTable;
struct ALSeqData {
uint8_t *offset;
uint32_t len;
};
struct Entry {
uint32_t offset;
uint32_t length;
};
struct SampleBank {
std::string name;
uint32_t offset;
std::vector<uint8_t> data;
};
struct TBLFile {
std::vector<SampleBank> banks;
std::vector<std::string> tbls;
std::unordered_map<std::string, uint32_t> map;
};
struct CTLHeader {
uint32_t instruments;
uint32_t numDrums;
uint32_t shared;
};
struct Sound {
int32_t offset;
float tuning;
};
struct Bank {
std::string name;
SampleBank sampleBank;
};
struct Inst {
std::string name;
uint32_t offset;
uint8_t releaseRate;
uint8_t normalRangeLo;
uint8_t normalRangeHi;
int32_t envelope;
std::optional<Sound> soundLo;
std::optional<Sound> soundMed;
std::optional<Sound> soundHi;
};
struct Drum {
std::string name;
uint32_t offset;
uint8_t releaseRate;
uint8_t pan;
int32_t envelope;
Sound sound;
};
template<typename T>
std::vector<T> slice(std::vector<T> const &v, uint32_t m, uint32_t n) {
auto first = v.cbegin() + m;
auto last = v.cbegin() + n;
std::vector<T> vec(first, last);
return vec;
}
std::vector<uint32_t> range(uint32_t start, uint32_t end) {
std::vector<uint32_t> result;
for (uint32_t i = start; i < end; ++i) {
result.push_back(i);
}
return result;
}
template <typename T1, typename T2, typename T3>
std::vector<std::tuple<size_t, T2, T3>> zip(const std::vector<T1>& container1, const std::vector<T2>& container2, const std::vector<T3>& container3) {
std::vector<std::tuple<size_t, T2, T3>> result;
size_t minSize = std::min({container1.size(), container2.size(), container3.size()});
for (size_t i = 0; i < minSize; ++i) {
result.emplace_back(container1[i], container2[i], container3[i]);
}
return result;
}
std::vector<Entry> parseSeqFile(std::vector<uint8_t>& buffer, int32_t offset){
std::vector<Entry> entries;
LUS::BinaryReader reader((char*) buffer.data(), buffer.size());
reader.SetEndianness(LUS::Endianness::Big);
reader.Seek(offset, LUS::SeekOffsetType::Start);
uint16_t magic = reader.ReadUInt16();
uint16_t num_entries = reader.ReadUInt16();
for (int i = 0; i < num_entries; ++i) {
reader.Seek((offset + 4) + (i * 8), LUS::SeekOffsetType::Start);
entries.push_back({reader.ReadUInt32(), reader.ReadUInt32()});
}
reader.Close();
return entries;
}
CTLHeader parseCTLHeader(std::vector<uint8_t>& data){
LUS::BinaryReader reader((char*) data.data(), data.size());
reader.SetEndianness(LUS::Endianness::Big);
CTLHeader header = { reader.ReadUInt32(), reader.ReadUInt32(), reader.ReadUInt32() };
reader.Close();
return header;
}
std::string gen_name(const std::string& prefix){
if(!nameTable.contains(prefix)){
nameTable[prefix] = 0;
}
nameTable[prefix] += 1;
return prefix + "-" + std::to_string(nameTable[prefix]);
}
std::optional<Sound> parseSound(std::vector<uint8_t> data) {
LUS::BinaryReader reader((char*) data.data(), data.size());
reader.SetEndianness(LUS::Endianness::Big);
int32_t addr = reader.ReadInt32();
float tuning = reader.ReadFloat();
if(addr == 0){
assert(tuning == 0.0f);
return std::nullopt;
}
Sound sound = { addr, tuning };
reader.Close();
return sound;
}
Drum parseDrum(std::vector<uint8_t>& data, uint32_t addr) {
LUS::BinaryReader reader((char*) data.data(), data.size());
reader.SetEndianness(LUS::Endianness::Big);
std::string name = gen_name("drum");
uint8_t releaseRate = reader.ReadInt8();
uint8_t pan = reader.ReadInt8();
reader.Seek(12, LUS::SeekOffsetType::Start);
Sound sound = parseSound(slice(data, 4, 12)).value();
int32_t envOffset = reader.ReadInt32();
assert(envOffset != 0);
Drum drum = { name, addr, releaseRate, pan, envOffset, sound };
return drum;
}
Inst parseInst(std::vector<uint8_t>& data, uint32_t addr) {
std::string name = gen_name("inst");
char* rawData = (char*) data.data();
uint8_t normalRangeLo = data[1];
uint8_t normalRangeHi = data[2];
uint8_t releaseRate = data[3];
int32_t envAddr;
memcpy(&envAddr, rawData + 4, 4);
envAddr = BSWAP32(envAddr);
auto soundLo = parseSound(slice(data, 8, 16));
auto soundMed = parseSound(slice(data, 16, 24));
auto soundHi = parseSound(slice(data, 24, 32));
if (soundLo == std::nullopt) {
assert(normalRangeLo == 0);
}
if (soundHi == std::nullopt) {
assert(normalRangeHi == 127);
}
Inst inst = { name, addr, releaseRate, normalRangeLo, normalRangeHi, envAddr, soundLo, soundMed, soundHi };
return inst;
}
void parseCTL(CTLHeader header, std::vector<uint8_t>& data, SampleBank bank, uint32_t index) {
nameTable.clear();
std::ostringstream ss;
ss << std::hex << std::setw(2) << std::setfill('0') << index;
std::string name = ss.str();
uint32_t numInstruments = header.instruments;
uint32_t numDrums = header.numDrums;
char* rawData = (char*) data.data();
uint32_t drumBaseAddr;
memcpy(&drumBaseAddr, rawData, 4);
drumBaseAddr = BSWAP32(drumBaseAddr);
std::vector<uint32_t> drumOffsets;
if(numDrums != 0){
for (size_t i = 0; i < numDrums; ++i) {
uint32_t drumOffset;
memcpy(&drumOffset, rawData + drumBaseAddr + (i * 4), 4);
drumOffsets.push_back(BSWAP32(drumOffset));
}
}
uint32_t instrumentBaseAddr = 4;
std::vector<uint32_t> instrumentOffsets;
std::vector<uint32_t> instrumentList;
for (size_t i = 0; i < numInstruments; ++i) {
uint32_t instOffset;
memcpy(&instOffset, rawData + instrumentBaseAddr + (i * 4), 4);
instOffset = BSWAP32(instOffset);
if(instOffset == 0){
instrumentList.push_back(NONE);
} else {
instrumentOffsets.push_back(instOffset);
instrumentList.push_back(instOffset);
}
}
std::sort(instrumentOffsets.begin(), instrumentOffsets.end());
std::vector<Inst> insts;
for(auto &offset : instrumentOffsets){
auto rInst = slice(data, offset, offset + 32);
Inst inst = parseInst(rInst, offset);
insts.push_back(inst);
}
std::vector<Drum> drums;
for(auto &offset : drumOffsets){
auto rDrum = slice(data, offset, offset + 16);
Drum drum = parseDrum(rDrum, offset);
drums.push_back(drum);
}
}
TBLFile parseTBL(std::vector<uint8_t>& data, std::vector<Entry>& entries) {
TBLFile tbl;
std::unordered_map<uint32_t, std::string> cache;
for(auto &entry : entries){
if(!cache.contains(entry.offset)){
std::string name = gen_name("sample_bank");
tbl.banks.push_back({name, entry.offset, slice(data, entry.offset, entry.offset + entry.length)});
tbl.map[name] = tbl.banks.size() - 1;
cache[entry.offset] = name;
}
tbl.tbls.push_back(cache[entry.offset]);
}
cache.clear();
return tbl;
}
bool AudioFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std::vector<uint8_t>& buffer) {
auto metadata = data["offsets"];
std::string path = data["path"];
if(path.find("@sound") != std::string::npos){
auto offsets = metadata["us"];
std::vector<Entry> tbl = parseSeqFile(buffer, offsets["sound_tbl"][0]);
std::vector<Entry> ctl = parseSeqFile(buffer, offsets["sound_ctl"][0]);
std::cout << "Table entries: " << tbl.size() << std::endl;
std::cout << "Control entries: " << ctl.size() << std::endl;
std::vector<uint8_t> tblData = slice(buffer, offsets["sound_tbl"][0], (size_t) offsets["sound_tbl"][0] + (size_t) offsets["sound_tbl"][1]);
std::vector<uint8_t> ctlData = slice(buffer, offsets["sound_ctl"][0], (size_t) offsets["sound_ctl"][0] + (size_t) offsets["sound_ctl"][1]);
TBLFile tblFile = parseTBL(tblData, tbl);
std::cout << "TBLs: " << tblFile.tbls.size() << std::endl;
std::cout << "Banks: " << tblFile.banks.size() << std::endl;
std::cout << "Map: " << tblFile.map.size() << std::endl;
auto zipped = zip(range(0, ctl.size()), ctl, tblFile.tbls);
for (const auto& item : zipped) {
auto [index, ctrl, sample_bank_name] = item;
auto sample_bank = tblFile.map[sample_bank_name];
auto entry = slice(ctlData, ctrl.offset, ctrl.offset + ctrl.length);
auto headerRaw = slice(entry, 0, 16);
auto header = parseCTLHeader(headerRaw);
std::cout << "Instruments: " << header.instruments << std::endl;
std::cout << "Drums: " << header.numDrums << std::endl;
}
exit(0);
}
if(path.find("bank_sets") != std::string::npos){
WRITE_HEADER(LUS::ResourceType::Blob, 0);
char* raw = (char*) (buffer.data() + metadata[1]["us"][0]);
@@ -320,14 +19,9 @@ bool AudioFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std:
writer->Write(raw + 0x46, 0x5A);
}
if(path.find("sound_tbl") != std::string::npos){
// WRITE_HEADER(LUS::ResourceType::Blob, 0);
// std::vector<Entry> entries = parseSeqFile(buffer, metadata[1]["us"][0]);
// for(auto & entry : entries){
// std::cout << "Offset: " << entrie.offset << std::endl;
// std::cout << "Len: " << entrie.length << std::endl;
// }
}
if(path.ends_with(".aiff")){
}
return false;
}