summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKiritoDv <kiritodev01@gmail.com>2023-09-01 00:51:51 -0600
committerKiritoDv <kiritodev01@gmail.com>2023-09-01 00:51:51 -0600
commit113472302904ac84d4ddf70df32ca245ad156bf2 (patch)
tree9a82a79c2542749d26db93b414dc52e0ec72ed7f
parent3bb2a8a13c4ff3b637bf02b9c01d632585b269bb (diff)
Migrated disassemble audio into audio manager class
-rw-r--r--assets.json2
-rw-r--r--lib/binarytools/BinaryReader.cpp23
-rw-r--r--lib/binarytools/BinaryReader.h2
-rw-r--r--lib/hj/pyutils.h23
-rw-r--r--lib/hj/zip.h60
-rw-r--r--src/Companion.cpp4
-rw-r--r--src/audio/AudioManager.cpp442
-rw-r--r--src/audio/AudioManager.h135
-rw-r--r--src/factories/AudioFactory.cpp312
9 files changed, 693 insertions, 310 deletions
diff --git a/assets.json b/assets.json
index 7ee9491..81bcd62 100644
--- a/assets.json
+++ b/assets.json
@@ -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],
diff --git a/lib/binarytools/BinaryReader.cpp b/lib/binarytools/BinaryReader.cpp
index 3aff001..75044b0 100644
--- a/lib/binarytools/BinaryReader.cpp
+++ b/lib/binarytools/BinaryReader.cpp
@@ -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;
diff --git a/lib/binarytools/BinaryReader.h b/lib/binarytools/BinaryReader.h
index 59eeb52..91e2a40 100644
--- a/lib/binarytools/BinaryReader.h
+++ b/lib/binarytools/BinaryReader.h
@@ -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();
diff --git a/lib/hj/pyutils.h b/lib/hj/pyutils.h
new file mode 100644
index 0000000..c5da4ee
--- /dev/null
+++ b/lib/hj/pyutils.h
@@ -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;
+}
+
+} \ No newline at end of file
diff --git a/lib/hj/zip.h b/lib/hj/zip.h
new file mode 100644
index 0000000..0cc7ee2
--- /dev/null
+++ b/lib/hj/zip.h
@@ -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;
+} \ No newline at end of file
diff --git a/src/Companion.cpp b/src/Companion.cpp
index 218f018..fe47842 100644
--- a/src/Companion.cpp
+++ b/src/Companion.cpp
@@ -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();
}
diff --git a/src/audio/AudioManager.cpp b/src/audio/AudioManager.cpp
new file mode 100644
index 0000000..1f3e382
--- /dev/null
+++ b/src/audio/AudioManager.cpp
@@ -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;
+ }
+}
diff --git a/src/audio/AudioManager.h b/src/audio/AudioManager.h
new file mode 100644
index 0000000..decb0b5
--- /dev/null
+++ b/src/audio/AudioManager.h
@@ -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;
+}; \ No newline at end of file
diff --git a/src/factories/AudioFactory.cpp b/src/factories/AudioFactory.cpp
index 62ff3d3..185d740 100644
--- a/src/factories/AudioFactory.cpp
+++ b/src/factories/AudioFactory.cpp
@@ -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;
}