From 3bb2a8a13c4ff3b637bf02b9c01d632585b269bb Mon Sep 17 00:00:00 2001 From: KiritoDv Date: Wed, 30 Aug 2023 21:26:38 -0600 Subject: [PATCH 1/6] [WIP] Audio Extractor --- assets.json | 10 +- sequences_table.h | 141 ++++++++++++++ src/Companion.cpp | 14 +- src/factories/AudioFactory.cpp | 333 +++++++++++++++++++++++++++++++++ src/factories/AudioFactory.h | 9 + src/factories/BlobFactory.cpp | 6 +- src/factories/RawFactory.h | 1 + tools/m64test.js | 32 ++++ 8 files changed, 534 insertions(+), 12 deletions(-) create mode 100644 sequences_table.h create mode 100644 src/factories/AudioFactory.cpp create mode 100644 src/factories/AudioFactory.h create mode 100644 tools/m64test.js diff --git a/assets.json b/assets.json index 2db3c3b..7ee9491 100644 --- a/assets.json +++ b/assets.json @@ -525,8 +525,6 @@ "actors/yoshi_egg/yoshi_egg_5_unused.rgba16.png": [32,32,2048,{"jp":[1215456,32696],"us":[1222624,32696],"eu":[1094592,32696],"sh":[1071104,32696]}], "actors/yoshi_egg/yoshi_egg_6_unused.rgba16.png": [32,32,2048,{"jp":[1215456,34744],"us":[1222624,34744],"eu":[1094592,34744],"sh":[1071104,34744]}], "actors/yoshi_egg/yoshi_egg_7_unused.rgba16.png": [32,32,2048,{"jp":[1215456,36792],"us":[1222624,36792],"eu":[1094592,36792],"sh":[1071104,36792]}], -"sound/sequences.aud": [114432, { "us": [8063072] }], -"sound/bank_sets.aud": [159, { "us": [8177185] }], "assets/demos/bbh.bin": [988,{"jp":[5733368],"us":[5741664],"eu":[5620584],"sh":[5589632]}], "assets/demos/bitdw.bin": [1412,{"us":[5747100],"sh":[5595068]}], "assets/demos/ccm.bin": [1320,{"jp":[5734356],"us":[5742652],"eu":[5621572],"sh":[5590620]}], @@ -1088,6 +1086,7 @@ "sound/sequences/sh/20_cutscene_ending.m64": [1887,{"sh":[7981824]}], "sound/sequences/sh/21_menu_file_select.m64": [786,{"sh":[7983712]}], "sound/sequences/sh/22_cutscene_lakitu.m64": [317,{"sh":[7984512]}], +"sound/sequences/us/00_sound_player.m64": [13452,{"us":[8063360]}], "sound/sequences/us/01_cutscene_collect_star.m64": [619,{"us":[8076816]}], "sound/sequences/us/02_menu_title_screen.m64": [8254,{"us":[8077440]}], "sound/sequences/us/03_level_grass.m64": [5122,{"us":[8085696]}], @@ -1122,6 +1121,13 @@ "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": { + "us": { + "bank_sets": [8177184, 160], + "sound_ctl": [5748512, 97856], + "sound_tbl": [5846368, 2216704] + } +}, "textures/cave/hmc_textures.00000.rgba16.png": [32,64,4096,{"jp":[3432432,0],"us":[3439184,0],"eu":[3312784,0],"sh":[3287152,0]}], "textures/cave/hmc_textures.01000.rgba16.png": [32,32,2048,{"jp":[3432432,4096],"us":[3439184,4096],"eu":[3312784,4096],"sh":[3287152,4096]}], "textures/cave/hmc_textures.01800.rgba16.png": [32,64,4096,{"jp":[3432432,6144],"us":[3439184,6144],"eu":[3312784,6144],"sh":[3287152,6144]}], diff --git a/sequences_table.h b/sequences_table.h new file mode 100644 index 0000000..f252b20 --- /dev/null +++ b/sequences_table.h @@ -0,0 +1,141 @@ +#pragma once +#include "align_asset_macro.h" + +#define dgSequence01 "__OTR__sound/sequences/01_cutscene_collect_star.m64" +static const ALIGN_ASSET(2) char gSequence01[] = dgSequence01; + +#define dgSequence02 "__OTR__sound/sequences/02_menu_title_screen.m64" +static const ALIGN_ASSET(2) char gSequence02[] = dgSequence02; + +#define dgSequence03 "__OTR__sound/sequences/03_level_grass.m64" +static const ALIGN_ASSET(2) char gSequence03[] = dgSequence03; + +#define dgSequence04 "__OTR__sound/sequences/04_level_inside_castle.m64" +static const ALIGN_ASSET(2) char gSequence04[] = dgSequence04; + +#define dgSequence05 "__OTR__sound/sequences/05_level_water.m64" +static const ALIGN_ASSET(2) char gSequence05[] = dgSequence05; + +#define dgSequence06 "__OTR__sound/sequences/06_level_hot.m64" +static const ALIGN_ASSET(2) char gSequence06[] = dgSequence06; + +#define dgSequence07 "__OTR__sound/sequences/07_level_boss_koopa.m64" +static const ALIGN_ASSET(2) char gSequence07[] = dgSequence07; + +#define dgSequence08 "__OTR__sound/sequences/08_level_snow.m64" +static const ALIGN_ASSET(2) char gSequence08[] = dgSequence08; + +#define dgSequence09 "__OTR__sound/sequences/09_level_slide.m64" +static const ALIGN_ASSET(2) char gSequence09[] = dgSequence09; + +#define dgSequence0A "__OTR__sound/sequences/0A_level_spooky.m64" +static const ALIGN_ASSET(2) char gSequence0A[] = dgSequence0A; + +#define dgSequence0B "__OTR__sound/sequences/0B_event_piranha_plant.m64" +static const ALIGN_ASSET(2) char gSequence0B[] = dgSequence0B; + +#define dgSequence0C "__OTR__sound/sequences/0C_level_underground.m64" +static const ALIGN_ASSET(2) char gSequence0C[] = dgSequence0C; + +#define dgSequence0D "__OTR__sound/sequences/0D_menu_star_select.m64" +static const ALIGN_ASSET(2) char gSequence0D[] = dgSequence0D; + +#define dgSequence0E "__OTR__sound/sequences/0E_event_powerup.m64" +static const ALIGN_ASSET(2) char gSequence0E[] = dgSequence0E; + +#define dgSequence0F "__OTR__sound/sequences/0F_event_metal_cap.m64" +static const ALIGN_ASSET(2) char gSequence0F[] = dgSequence0F; + +#define dgSequence10 "__OTR__sound/sequences/10_event_koopa_message.m64" +static const ALIGN_ASSET(2) char gSequence10[] = dgSequence10; + +#define dgSequence11 "__OTR__sound/sequences/11_level_koopa_road.m64" +static const ALIGN_ASSET(2) char gSequence11[] = dgSequence11; + +#define dgSequence12 "__OTR__sound/sequences/12_event_high_score.m64" +static const ALIGN_ASSET(2) char gSequence12[] = dgSequence12; + +#define dgSequence13 "__OTR__sound/sequences/13_event_merry_go_round.m64" +static const ALIGN_ASSET(2) char gSequence13[] = dgSequence13; + +#define dgSequence14 "__OTR__sound/sequences/14_event_race.m64" +static const ALIGN_ASSET(2) char gSequence14[] = dgSequence14; + +#define dgSequence15 "__OTR__sound/sequences/15_cutscene_star_spawn.m64" +static const ALIGN_ASSET(2) char gSequence15[] = dgSequence15; + +#define dgSequence16 "__OTR__sound/sequences/16_event_boss.m64" +static const ALIGN_ASSET(2) char gSequence16[] = dgSequence16; + +#define dgSequence17 "__OTR__sound/sequences/17_cutscene_collect_key.m64" +static const ALIGN_ASSET(2) char gSequence17[] = dgSequence17; + +#define dgSequence18 "__OTR__sound/sequences/18_event_endless_stairs.m64" +static const ALIGN_ASSET(2) char gSequence18[] = dgSequence18; + +#define dgSequence19 "__OTR__sound/sequences/19_level_boss_koopa_final.m64" +static const ALIGN_ASSET(2) char gSequence19[] = dgSequence19; + +#define dgSequence1A "__OTR__sound/sequences/1A_cutscene_credits.m64" +static const ALIGN_ASSET(2) char gSequence1A[] = dgSequence1A; + +#define dgSequence1B "__OTR__sound/sequences/1B_event_solve_puzzle.m64" +static const ALIGN_ASSET(2) char gSequence1B[] = dgSequence1B; + +#define dgSequence1C "__OTR__sound/sequences/1C_event_toad_message.m64" +static const ALIGN_ASSET(2) char gSequence1C[] = dgSequence1C; + +#define dgSequence1D "__OTR__sound/sequences/1D_event_peach_message.m64" +static const ALIGN_ASSET(2) char gSequence1D[] = dgSequence1D; + +#define dgSequence1E "__OTR__sound/sequences/1E_cutscene_intro.m64" +static const ALIGN_ASSET(2) char gSequence1E[] = dgSequence1E; + +#define dgSequence1F "__OTR__sound/sequences/1F_cutscene_victory.m64" +static const ALIGN_ASSET(2) char gSequence1F[] = dgSequence1F; + +#define dgSequence20 "__OTR__sound/sequences/20_cutscene_ending.m64" +static const ALIGN_ASSET(2) char gSequence20[] = dgSequence20; + +#define dgSequence21 "__OTR__sound/sequences/21_menu_file_select.m64" +static const ALIGN_ASSET(2) char gSequence21[] = dgSequence21; + +#define dgSequence22 "__OTR__sound/sequences/22_cutscene_lakitu.m64" +static const ALIGN_ASSET(2) char gSequence22[] = dgSequence22; + +static const char* gSequenceTable[] = { + gSequence01, + gSequence02, + gSequence03, + gSequence04, + gSequence05, + gSequence06, + gSequence07, + gSequence08, + gSequence09, + gSequence0A, + gSequence0B, + gSequence0C, + gSequence0D, + gSequence0E, + gSequence0F, + gSequence10, + gSequence11, + gSequence12, + gSequence13, + gSequence14, + gSequence15, + gSequence16, + gSequence17, + gSequence18, + gSequence19, + gSequence1A, + gSequence1B, + gSequence1C, + gSequence1D, + gSequence1E, + gSequence1F, + gSequence20, + gSequence21, + gSequence22, +}; diff --git a/src/Companion.cpp b/src/Companion.cpp index 16d6c11..218f018 100644 --- a/src/Companion.cpp +++ b/src/Companion.cpp @@ -4,6 +4,7 @@ #include "utils/MIODecoder.h" #include "factories/RawFactory.h" #include "factories/BlobFactory.h" +#include "factories/AudioFactory.h" #include "factories/TextureFactory.h" #include "factories/AnimFactory.h" @@ -18,9 +19,10 @@ namespace fs = std::filesystem; static const std::unordered_map gFactories = { { ".png", new TextureFactory() }, { ".anim", new AnimFactory() }, + { ".aiff", new AudioFactory() }, { ".bin", new BlobFactory(LUS::ResourceType::Blob) }, + { ".m64", new BlobFactory(LUS::ResourceType::Blob) }, { ".sbox", new BlobFactory(LUS::ResourceType::Blob, true) }, - { ".aud", new BlobFactory(LUS::ResourceType::Blob, false) }, }; void Companion::Start() { @@ -45,7 +47,7 @@ void Companion::ProcessAssets() { for( auto& [asset, data] : assets.items() ) { std::string extension = fs::path(asset).extension().string(); if( gFactories.find(extension) == gFactories.end() ) { - std::cout << "No factory found for " << asset << '\n'; + // std::cout << "No factory found for " << asset << '\n'; continue; } LUS::BinaryWriter write = LUS::BinaryWriter(); @@ -57,7 +59,7 @@ void Companion::ProcessAssets() { RawFactory* factory = gFactories.at(extension); if(!factory->process(&write, entry, this->gRomData)){ - std::cout << "Failed to process " << asset << '\n'; + // std::cout << "Failed to process " << asset << '\n'; continue; } @@ -70,13 +72,15 @@ void Companion::ProcessAssets() { if(!isTexture) { // Write to file too std::string dpath = "debug/" + path; - fs::create_directories(fs::path(dpath).parent_path()); + if(!fs::exists(fs::path(dpath).parent_path())){ + fs::create_directories(fs::path(dpath).parent_path()); + } std::ofstream output(dpath, std::ios::binary); output.write((char*)buffer.data(), buffer.size()); output.close(); } - std::cout << "Processed " << path << std::endl; + // std::cout << "Processed " << path << std::endl; write.Close(); } diff --git a/src/factories/AudioFactory.cpp b/src/factories/AudioFactory.cpp new file mode 100644 index 0000000..62ff3d3 --- /dev/null +++ b/src/factories/AudioFactory.cpp @@ -0,0 +1,333 @@ +#include "AudioFactory.h" + +#include +#include +#include +#include +#include "Companion.h" +#include "utils/MIODecoder.h" +#include "binarytools/BinaryReader.h" + +namespace fs = std::filesystem; + +#define NONE 0xFFFF + +std::unordered_map 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 data; +}; + +struct TBLFile { + std::vector banks; + std::vector tbls; + std::unordered_map 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 soundLo; + std::optional soundMed; + std::optional soundHi; +}; + +struct Drum { + std::string name; + uint32_t offset; + uint8_t releaseRate; + uint8_t pan; + int32_t envelope; + Sound sound; +}; + +template +std::vector slice(std::vector const &v, uint32_t m, uint32_t n) { + auto first = v.cbegin() + m; + auto last = v.cbegin() + n; + + std::vector vec(first, last); + return vec; +} + +std::vector range(uint32_t start, uint32_t end) { + std::vector result; + for (uint32_t i = start; i < end; ++i) { + result.push_back(i); + } + return result; +} + +template +std::vector> zip(const std::vector& container1, const std::vector& container2, const std::vector& container3) { + std::vector> 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 parseSeqFile(std::vector& buffer, int32_t offset){ + std::vector 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& 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 parseSound(std::vector 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& 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& 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& 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 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 instrumentOffsets; + std::vector 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 insts; + for(auto &offset : instrumentOffsets){ + auto rInst = slice(data, offset, offset + 32); + Inst inst = parseInst(rInst, offset); + insts.push_back(inst); + } + + std::vector 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& data, std::vector& entries) { + TBLFile tbl; + std::unordered_map 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& buffer) { + auto metadata = data["offsets"]; + std::string path = data["path"]; + + if(path.find("@sound") != std::string::npos){ + auto offsets = metadata["us"]; + std::vector tbl = parseSeqFile(buffer, offsets["sound_tbl"][0]); + std::vector 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 tblData = slice(buffer, offsets["sound_tbl"][0], (size_t) offsets["sound_tbl"][0] + (size_t) offsets["sound_tbl"][1]); + std::vector 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]); + WRITE_U32(metadata[0]); + for(int i = 0; i < 0x23; i++){ + int16_t value; + memcpy(&value, raw + (i * 2), 2); + WRITE_I16(BSWAP16(value)); + } + writer->Write(raw + 0x46, 0x5A); + } + + if(path.find("sound_tbl") != std::string::npos){ + // WRITE_HEADER(LUS::ResourceType::Blob, 0); + // std::vector 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; + // } + } + + return false; +} diff --git a/src/factories/AudioFactory.h b/src/factories/AudioFactory.h new file mode 100644 index 0000000..fdfdb9c --- /dev/null +++ b/src/factories/AudioFactory.h @@ -0,0 +1,9 @@ +#pragma once + +#include "RawFactory.h" + +class AudioFactory : public RawFactory { +public: + AudioFactory() = default; + bool process(LUS::BinaryWriter* write, nlohmann::json& data, std::vector& buffer) override; +}; \ No newline at end of file diff --git a/src/factories/BlobFactory.cpp b/src/factories/BlobFactory.cpp index 7071a45..30714b1 100644 --- a/src/factories/BlobFactory.cpp +++ b/src/factories/BlobFactory.cpp @@ -13,11 +13,7 @@ bool BlobFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std:: return false; } - if(this->bigEndian){ - WRITE_BHEADER(this->type, 0); - } else { - WRITE_HEADER(this->type, 0); - } + WRITE_HEADER(this->type, 0); size_t size = metadata[0]; auto offsets = metadata[1]["us"]; diff --git a/src/factories/RawFactory.h b/src/factories/RawFactory.h index 7057740..fe6ec98 100644 --- a/src/factories/RawFactory.h +++ b/src/factories/RawFactory.h @@ -11,6 +11,7 @@ #define WRITE_U16(value) writer->Write((uint16_t) value) #define WRITE_U32(value) writer->Write((uint32_t) value) #define WRITE_U64(value) writer->Write((uint64_t) value) +#define WRITE_I8(value) writer->Write((int8_t) value) #define WRITE_I16(value) writer->Write((int16_t) value) #define WRITE_DATA(vec) writer->Write((char*) vec.data(), data.size()) #define WRITE_ARRAY(data, size) writer->Write((char*) data, size) diff --git a/tools/m64test.js b/tools/m64test.js new file mode 100644 index 0000000..0e4a6f3 --- /dev/null +++ b/tools/m64test.js @@ -0,0 +1,32 @@ +const fs = require('fs'); + +const bytes = fs.readFileSync('./baserom.us.z64'); +const bpath = '../sound/sequences/us/' +const header = [ + '#pragma once', + '#include "align_asset_macro.h"', + '', +] +const sequences = [] + +fs.readdirSync(bpath).forEach(seq => { + const seqData = fs.readFileSync(bpath+seq); + const seqOffset = bytes.indexOf(seqData); + const output = `sound/sequences/${seq}` + const seqId = seq.split("_")[0] + console.log(`"${output}": ${JSON.stringify([ + seqData.length, + { "us": [ seqOffset ]} + ])},`); + sequences.push(`gSequence${seqId}`); + header.push(`#define dgSequence${seqId} "__OTR__${output}"`); + header.push(`static const ALIGN_ASSET(2) char gSequence${seqId}[] = dgSequence${seqId};\n`); +}); + +header.push('static const char* gSequenceTable[] = {'); +for (const seq of sequences.sort((a, b) => b.localeCompare(a))) { + header.push(` ${seq},`); +} +header.push('};\n'); + +fs.writeFileSync('./sequences_table.h', header.join('\n')); \ No newline at end of file From 113472302904ac84d4ddf70df32ca245ad156bf2 Mon Sep 17 00:00:00 2001 From: KiritoDv Date: Fri, 1 Sep 2023 00:51:51 -0600 Subject: [PATCH 2/6] Migrated disassemble audio into audio manager class --- assets.json | 2 +- lib/binarytools/BinaryReader.cpp | 23 ++ lib/binarytools/BinaryReader.h | 2 + lib/hj/pyutils.h | 23 ++ lib/hj/zip.h | 60 +++++ src/Companion.cpp | 4 + src/audio/AudioManager.cpp | 442 +++++++++++++++++++++++++++++++ src/audio/AudioManager.h | 135 ++++++++++ src/factories/AudioFactory.cpp | 312 +--------------------- 9 files changed, 693 insertions(+), 310 deletions(-) create mode 100644 lib/hj/pyutils.h create mode 100644 lib/hj/zip.h create mode 100644 src/audio/AudioManager.cpp create mode 100644 src/audio/AudioManager.h 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 #include +#include LUS::BinaryReader::BinaryReader(char* nBuffer, size_t nBufferSize) { mStream = std::make_shared(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 + +namespace PyUtils { +template +std::vector slice(std::vector 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 vec(first, last); + return vec; +} + +std::vector range(uint32_t start, uint32_t end) { + std::vector 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 +#include +#include +#include +#include + +template +void print_tuple(const TupType& _tup, std::index_sequence) +{ + std::cout << "("; + (..., (std::cout << (I == 0? "" : ", ") << std::get(_tup))); + std::cout << ")\n"; +} + +template +void print_tuple(const std::tuple& _tup) +{ + print_tuple(_tup, std::make_index_sequence()); +} + +template +void variatic_min(std::vector v, int &m) { + if (m > v.size()) m = v.size(); +} + +template +using NthTypeOf = typename std::tuple_element>::type; + +template +auto vec_to_tup_helper(const std::vector& v, std::index_sequence) { + return std::make_tuple( + std::any_cast>(v[Indices])... + ); +} + +template +std::tuple vec_to_tup(std::vector& values) { + return vec_to_tup_helper(values, std::make_index_sequence()); +} + +template +void celem_at(std::vector v, int i, std::vector &r) { + r.push_back(v[i]); +} + +template +std::vector> zip(std::vector... args) { + int m = 0xFFFFFFFF; + std::vector> res; + + (variatic_min(args, m), ...); + + for (int i = 0; i < m; i++) { + std::vector vaux; + (celem_at(args, i, vaux), ...); + res.push_back(vec_to_tup(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 +#include +#include +#include +#include +#include "hj/pyutils.h" +#include "binarytools/BinaryReader.h" +#include "hj/zip.h" +#include + +std::unordered_map 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 AudioManager::parse_seq_file(std::vector& buffer, uint32_t offset, bool isCTL){ + std::vector 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& 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 AudioManager::parse_sound(std::vector 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& 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& 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& bankData){ + LUS::BinaryReader reader((char*) bankData.data(), bankData.size()); + reader.SetEndianness(LUS::Endianness::Big); + reader.Seek(addr, LUS::SeekOffsetType::Start); + + std::optional> 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(); + 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& 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 table; + std::vector 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& data, std::vector& 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 AudioManager::parse_envelope(uint32_t addr, std::vector& dataBank){ + std::vector 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 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 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 instrumentOffsets; + std::vector 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 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 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(); + auto sampleOffsets = std::vector(); + auto tunings = std::unordered_map(); + + 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>> allInsts; + bool needDrums = !drums.empty(); + for(auto &inst : insts){ + std::vector> sounds = {inst.soundLo, inst.soundMed, inst.soundHi}; + + if(needDrums && std::any_of(sounds.cbegin(), sounds.cend(), [&drums](std::optional 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 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> envData; + std::vector 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 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 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& data, std::vector& entries) { + TBLFile tbl; + std::unordered_map 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& 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 tbl = parse_seq_file(buffer, sound_tbl[0], false); + std::vector 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 tbl_data = PyUtils::slice(buffer, sound_tbl[0], (size_t) sound_tbl[0] + (size_t) sound_tbl[1]); + std::vector 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 +#include +#include +#include + +#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 soundLo; + std::optional soundMed; + std::optional soundHi; +}; + +struct Book { + uint32_t order{}; + uint32_t npredictors{}; + std::vector 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> 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 data; + Book book; + Loop loop; + std::vector tunings; +}; + +struct SampleBank { + std::string name; + uint32_t offset; + std::vector data; + std::unordered_map entries; + + AifcEntry AddSample(uint32_t addr, size_t sampleSize, const Book& book, const Loop& loop); +}; + +struct TBLFile { + std::vector banks; + std::vector tbls; + std::unordered_map map; +}; + +struct CTLHeader { + uint32_t instruments; + uint32_t numDrums; + uint32_t shared; +}; + +typedef std::pair EnvelopeData; + +struct Envelope { + std::string name; + std::vector entries; +}; + +struct Bank { + std::string name; + SampleBank sampleBank; + std::vector insts; + std::vector drums; + std::vector>> allInsts; + std::vector instOffsets; + std::unordered_map envelopes; + std::unordered_map samples; + void print() const; +}; + +class AudioManager { +public: + static AudioManager* Instance; + void initialize(std::vector& buffer); + +private: + static std::vector parse_seq_file(std::vector& buffer, uint32_t offset, bool isCTL); + static CTLHeader parse_ctl_header(std::vector& data); + static std::optional parse_sound(std::vector data); + static Drum parse_drum(std::vector& data, uint32_t addr); + static Inst parse_inst(std::vector& data, uint32_t addr); + static Loop parse_loop(uint32_t addr, std::vector& bankData); + static Book parse_book(uint32_t addr, std::vector& bankData); + static AifcEntry parse_sample(std::vector& data, std::vector& bankData, SampleBank& sampleBank); + static std::vector parse_envelope(uint32_t addr, std::vector& dataBank); + static Bank parse_ctl(CTLHeader header, std::vector data, SampleBank bank, uint32_t index); + static TBLFile parse_tbl(std::vector& data, std::vector& entries); + + std::vector 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 -#include -#include -#include -#include "Companion.h" -#include "utils/MIODecoder.h" #include "binarytools/BinaryReader.h" -namespace fs = std::filesystem; - -#define NONE 0xFFFF - -std::unordered_map 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 data; -}; - -struct TBLFile { - std::vector banks; - std::vector tbls; - std::unordered_map 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 soundLo; - std::optional soundMed; - std::optional soundHi; -}; - -struct Drum { - std::string name; - uint32_t offset; - uint8_t releaseRate; - uint8_t pan; - int32_t envelope; - Sound sound; -}; - -template -std::vector slice(std::vector const &v, uint32_t m, uint32_t n) { - auto first = v.cbegin() + m; - auto last = v.cbegin() + n; - - std::vector vec(first, last); - return vec; -} - -std::vector range(uint32_t start, uint32_t end) { - std::vector result; - for (uint32_t i = start; i < end; ++i) { - result.push_back(i); - } - return result; -} - -template -std::vector> zip(const std::vector& container1, const std::vector& container2, const std::vector& container3) { - std::vector> 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 parseSeqFile(std::vector& buffer, int32_t offset){ - std::vector 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& 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 parseSound(std::vector 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& 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& 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& 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 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 instrumentOffsets; - std::vector 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 insts; - for(auto &offset : instrumentOffsets){ - auto rInst = slice(data, offset, offset + 32); - Inst inst = parseInst(rInst, offset); - insts.push_back(inst); - } - - std::vector 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& data, std::vector& entries) { - TBLFile tbl; - std::unordered_map 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& buffer) { auto metadata = data["offsets"]; std::string path = data["path"]; - if(path.find("@sound") != std::string::npos){ - auto offsets = metadata["us"]; - std::vector tbl = parseSeqFile(buffer, offsets["sound_tbl"][0]); - std::vector 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 tblData = slice(buffer, offsets["sound_tbl"][0], (size_t) offsets["sound_tbl"][0] + (size_t) offsets["sound_tbl"][1]); - std::vector 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 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; } From 063224c69d564d6e10dd4febf25caa9f59fccfda Mon Sep 17 00:00:00 2001 From: KiritoDv Date: Sun, 3 Sep 2023 20:11:35 -0600 Subject: [PATCH 3/6] Fixed AIFC Extraction --- lib/binarytools/BinaryWriter.cpp | 13 +- lib/binarytools/BinaryWriter.h | 3 +- lib/hj/pyutils.h | 42 +++++- src/Companion.cpp | 6 +- src/audio/AIFCWriter.cpp | 48 +++++++ src/audio/AIFCWriter.h | 36 +++++ src/audio/AudioManager.cpp | 231 +++++++++++++++++++++++++++---- src/audio/AudioManager.h | 23 +-- src/factories/AudioFactory.cpp | 9 +- 9 files changed, 358 insertions(+), 53 deletions(-) create mode 100644 src/audio/AIFCWriter.cpp create mode 100644 src/audio/AIFCWriter.h diff --git a/lib/binarytools/BinaryWriter.cpp b/lib/binarytools/BinaryWriter.cpp index 362824f..6e0140f 100644 --- a/lib/binarytools/BinaryWriter.cpp +++ b/lib/binarytools/BinaryWriter.cpp @@ -132,10 +132,11 @@ void LUS::BinaryWriter::Write(double value) { mStream->Write((char*)&value, sizeof(double)); } -void LUS::BinaryWriter::Write(const std::string& str) { - int strLen = str.size(); - Write(strLen); - +void LUS::BinaryWriter::Write(const std::string& str, bool writeLength) { + if(writeLength){ + int strLen = str.size(); + Write(strLen); + } for (char c : str) { mStream->WriteByte(c); } @@ -145,6 +146,10 @@ void LUS::BinaryWriter::Write(char* srcBuffer, size_t length) { mStream->Write(srcBuffer, length); } +void LUS::BinaryWriter::WriteByte(char value) { + mStream->WriteByte(value); +} + std::vector LUS::BinaryWriter::ToVector() { return mStream->ToVector(); } diff --git a/lib/binarytools/BinaryWriter.h b/lib/binarytools/BinaryWriter.h index fbd4f65..dca683f 100644 --- a/lib/binarytools/BinaryWriter.h +++ b/lib/binarytools/BinaryWriter.h @@ -33,8 +33,9 @@ class BinaryWriter { void Write(uint64_t value); void Write(float value); void Write(double value); - void Write(const std::string& str); + void Write(const std::string& str, bool writeLength = true); void Write(char* srcBuffer, size_t length); + void WriteByte(char value); std::vector ToVector(); diff --git a/lib/hj/pyutils.h b/lib/hj/pyutils.h index c5da4ee..f7dafbf 100644 --- a/lib/hj/pyutils.h +++ b/lib/hj/pyutils.h @@ -1,6 +1,7 @@ #pragma once #include +#include namespace PyUtils { template @@ -12,12 +13,45 @@ std::vector slice(std::vector const &v, uint32_t m, uint32_t n = -1) { return vec; } -std::vector range(uint32_t start, uint32_t end) { - std::vector result; - for (uint32_t i = start; i < end; ++i) { - result.push_back(i); +std::vector range(uint32_t start, uint32_t end); + +template +T max(std::vector const &v) { + T max = v[0]; + for (auto const &value: v) { + if (value > max) { + max = value; + } + } + return max; +} +template +T min(std::vector const &v) { + T min = v[0]; + for (auto const &value: v) { + if (value < min) { + min = value; + } + } + return min; +} + +template +std::vector keys(std::unordered_map const &map) { + std::vector result; + for (auto const &pair: map) { + result.push_back(pair.first); } return result; } +} +template +std::vector operator+(std::vector const &x, std::vector const &y) +{ + std::vector vec; + vec.reserve(x.size() + y.size()); + vec.insert(vec.end(), x.begin(), x.end()); + vec.insert(vec.end(), y.begin(), y.end()); + return vec; } \ No newline at end of file diff --git a/src/Companion.cpp b/src/Companion.cpp index fe47842..1d2f371 100644 --- a/src/Companion.cpp +++ b/src/Companion.cpp @@ -51,7 +51,7 @@ void Companion::ProcessAssets() { for( auto& [asset, data] : assets.items() ) { std::string extension = fs::path(asset).extension().string(); if( gFactories.find(extension) == gFactories.end() ) { - // std::cout << "No factory found for " << asset << '\n'; + std::cout << "No factory found for " << asset << '\n'; continue; } LUS::BinaryWriter write = LUS::BinaryWriter(); @@ -63,7 +63,7 @@ void Companion::ProcessAssets() { RawFactory* factory = gFactories.at(extension); if(!factory->process(&write, entry, this->gRomData)){ - // std::cout << "Failed to process " << asset << '\n'; + std::cout << "Failed to process " << asset << '\n'; continue; } @@ -84,7 +84,7 @@ void Companion::ProcessAssets() { output.close(); } - // std::cout << "Processed " << path << std::endl; + std::cout << "Processed " << path << std::endl; write.Close(); } diff --git a/src/audio/AIFCWriter.cpp b/src/audio/AIFCWriter.cpp new file mode 100644 index 0000000..1df5813 --- /dev/null +++ b/src/audio/AIFCWriter.cpp @@ -0,0 +1,48 @@ +#include "AIFCWriter.h" + +#include "hj/pyutils.h" + +#define ALIGN(val, al) (size_t) ((val + (al - 1)) & -al) + +std::vector AIFCWriter::pack(const std::string& str) { + std::vector result; + result.insert(result.end(), str.begin(), str.end()); + return result; +} + +std::vector AIFCWriter::pstring(const std::vector& data) { + std::vector result; + result.insert(result.end(), data.begin(), data.end()); + if (data.size() % 2 == 0) { + result.push_back('\0'); + } + return result; +} + +void AIFCWriter::add_section(AIFC::MagicValues magic, const std::vector& data) { + sections.emplace_back(magic, data); + total_size += ALIGN(data.size(), 2) + 8; +} + +void AIFCWriter::add_custom_section(const std::string& section, const std::vector& data) { + this->add_section(AIFC::MagicValues::AAPL, pack("stoc") + pstring(pack(section)) + data); +} + +void AIFCWriter::finish() { + // total_size isn't used and is regularly wrong. + // In particular, vadpcm_enc preserves the size of the input file... + total_size += 4; + + out.Write(BSWAP32(AIFC::MagicValues::FORM)); + out.Write((uint32_t) BSWAP32(total_size)); + out.Write(BSWAP32(AIFC::MagicValues::AIFC)); + + for (const auto& section : sections) { + out.Write((uint32_t) BSWAP32(section.first)); + out.Write((uint32_t) BSWAP32(section.second.size())); + out.Write((char*) section.second.data(), section.second.size()); + if(section.second.size() % 2) { + out.Write('\0'); + } + } +} diff --git a/src/audio/AIFCWriter.h b/src/audio/AIFCWriter.h new file mode 100644 index 0000000..9ea7843 --- /dev/null +++ b/src/audio/AIFCWriter.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include +#include "binarytools/BinaryWriter.h" + +namespace AIFC { + enum MagicValues { + FORM = 0x464f524d, + AIFC = 0x41494643, + COMM = 0x434f4d4d, + INST = 0x494e5354, + VAPC = 0x56415043, + SSND = 0x53534e44, + AAPL = 0x4150504c, + stoc = 0x73746f63, + }; +} + +class AIFCWriter { +public: + AIFCWriter(LUS::BinaryWriter& out) : out(out), total_size(0) {} + + static std::vector pack(const std::string& str); + static std::vector pstring(const std::vector& data); + void add_section(AIFC::MagicValues, const std::vector& data); + void add_custom_section(const std::string& section, const std::vector& data); + void finish(); + +private: + LUS::BinaryWriter& out; + std::vector>> sections; + std::size_t total_size; +}; \ No newline at end of file diff --git a/src/audio/AudioManager.cpp b/src/audio/AudioManager.cpp index 1f3e382..f241a9b 100644 --- a/src/audio/AudioManager.cpp +++ b/src/audio/AudioManager.cpp @@ -8,35 +8,43 @@ #include "hj/pyutils.h" #include "binarytools/BinaryReader.h" #include "hj/zip.h" +#include "AIFCWriter.h" #include std::unordered_map name_table; AudioManager* AudioManager::Instance; +std::vector PyUtils::range(uint32_t start, uint32_t end) { + std::vector result; + for (uint32_t i = start; i < end; ++i) { + result.push_back(i); + } + return result; +} + 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]); + return prefix + std::to_string(name_table[prefix]++); } -AifcEntry SampleBank::AddSample(uint32_t addr, size_t sampleSize, const Book& book, const Loop& loop){ +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; + AifcEntry* entry; if(this->entries.contains(addr)){ entry = this->entries[addr]; - assert(entry.book == book); - assert(entry.loop == loop); - assert(entry.data.size() == sampleSize); + 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 }; + entry = new AifcEntry{ gen_name("aifc"), PyUtils::slice(this->data, addr, addr + sampleSize), book, loop }; this->entries[addr] = entry; } @@ -51,8 +59,8 @@ void Bank::print() const { 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::cout << "Sample Bank: " << sampleBank->name << std::endl; + std::cout << "Sample Bank Offset: " << std::to_string(sampleBank->offset) << std::endl; } std::vector AudioManager::parse_seq_file(std::vector& buffer, uint32_t offset, bool isCTL){ @@ -198,7 +206,7 @@ Book AudioManager::parse_book(uint32_t addr, std::vector& bankData){ return book; } -AifcEntry AudioManager::parse_sample(std::vector& data, std::vector& bankData, SampleBank& sampleBank){ +AifcEntry* AudioManager::parse_sample(std::vector& data, std::vector& bankData, SampleBank* sampleBank){ LUS::BinaryReader reader((char*) data.data(), data.size()); reader.SetEndianness(LUS::Endianness::Big); @@ -215,7 +223,7 @@ AifcEntry AudioManager::parse_sample(std::vector& data, std::vectorAddSample(addr, sampleSize, bookData, loopData); } @@ -239,7 +247,7 @@ std::vector AudioManager::parse_envelope(uint32_t addr, std::vecto return entries; } -Bank AudioManager::parse_ctl(CTLHeader header, std::vector data, SampleBank bank, uint32_t index) { +Bank AudioManager::parse_ctl(CTLHeader header, std::vector data, SampleBank* bank, uint32_t index) { name_table.clear(); std::ostringstream ss; ss << std::hex << std::setw(2) << std::setfill('0') << index; @@ -333,13 +341,13 @@ Bank AudioManager::parse_ctl(CTLHeader header, std::vector data, Sample allInsts.emplace_back(drums); } - std::unordered_map samples; + std::unordered_map 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); + AifcEntry* sample = parse_sample(rSample, data, bank); for(auto &tuning : tunings){ - sample.tunings.push_back(tuning.second); + sample->tunings.push_back(tuning.second); } samples[offset] = sample; } @@ -392,11 +400,11 @@ TBLFile AudioManager::parse_tbl(std::vector& data, std::vector& 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) + auto* sampleBank = new 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; + tbl.map[name] = sampleBank; cache[entry.offset] = name; } tbl.tbls.push_back(cache[entry.offset]); @@ -419,24 +427,189 @@ void AudioManager::initialize(std::vector& buffer) { std::vector tbl_data = PyUtils::slice(buffer, sound_tbl[0], (size_t) sound_tbl[0] + (size_t) sound_tbl[1]); std::vector 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); + this->loaded_tbl = 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; + std::cout << "TBLs: " << this->loaded_tbl.tbls.size() << std::endl; + std::cout << "Banks: " << this->loaded_tbl.banks.size() << std::endl; + std::cout << "Map: " << this->loaded_tbl.map.size() << std::endl; - auto zipped = zip(PyUtils::range(0, ctl.size()), ctl, tbl_file.tbls); + auto zipped = zip(PyUtils::range(0, ctl.size()), ctl, this->loaded_tbl.tbls); - std::cout << "================================" << std::endl; +// 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 sample_bank = this->loaded_tbl.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); + auto bank = parse_ctl(header, PyUtils::slice(entry, 16), sample_bank, index); banks.push_back(bank); - bank.print(); - std::cout << "================================" << std::endl; +// bank.print(); +// std::cout << "================================" << std::endl; + } + + +} + +void serialize_f80(double num, LUS::BinaryWriter &writer) { + // Convert the input double to a uint64_t + std::uint64_t f64; + std::memcpy(&f64, &num, sizeof(double)); + + std::uint64_t f64_sign_bit = f64 & (std::uint64_t) pow(2, 63); + if (num == 0.0) { + if (f64_sign_bit) { + writer.Write(0x80000000); + } else { + writer.Write(0x00000000); + } + } + + std::uint64_t exponent = ((f64 ^ f64_sign_bit) >> 52); + + assert(exponent != 0); + assert(exponent != 0x7FF); + + exponent -= 1023; + uint64_t f64_mantissa_bits = f64 & (uint64_t) pow(2, 52) - 1; + uint64_t f80_sign_bit = f64_sign_bit << (80 - 64); + uint64_t f80_exponent = (exponent + 0x3FFF) << 64; + uint64_t f80_mantissa_bits = (uint64_t) pow(2, 63) | (f64_mantissa_bits << (63 - 52)); + uint64_t f80 = f80_sign_bit | f80_exponent | f80_mantissa_bits; + + // Split the f80 representation into two parts (high and low) + uint16_t high = BSWAP16((uint16_t) f80 >> 64); + writer.Write((char*) &high, 2); + uint64_t low = BSWAP64(f80 & ((uint64_t) pow(2, 64) - 1)); + writer.Write((char*) &low, 8); +} + +#define START_SECTION(section) \ + { \ + out.Write(BSWAP32(section)); \ + LUS::BinaryWriter tmp = LUS::BinaryWriter(); \ + tmp.SetEndianness(LUS::Endianness::Big); \ + +#define START_CUSTOM_SECTION(section) \ + { \ + LUS::BinaryWriter tmp = LUS::BinaryWriter(); \ + tmp.SetEndianness(LUS::Endianness::Big); \ + out.Write(BSWAP32(AIFC::MagicValues::AAPL)); \ + tmp.Write(AIFC::MagicValues::stoc); \ + tmp.Write(section, false); \ + +#define END_SECTION() \ + auto odata = tmp.ToVector(); \ + size_t size = odata.size(); \ + len += ALIGN(size, 2) + 8; \ + out.Write(BSWAP32((uint32_t) size)); \ + out.Write(odata.data(), odata.size()); \ + if(size % 2){ \ + out.WriteByte(0); \ + } \ + } \ + +void AudioManager::write_aifc(AifcEntry* entry, LUS::BinaryWriter &out) { + int16_t num_channels = 1; + auto data = entry->data; + size_t len = 0; + assert(data.size() % 9 == 0); + if(data.size() % 2 == 1){ + data.push_back('\0'); + } + uint32_t num_frames = data.size() * 16 / 9; + int16_t sample_size = 16; + + uint32_t sample_rate = -1; + if(entry->tunings.size() == 1){ + sample_rate = 32000 * entry->tunings[0]; + } else { + float tmin = PyUtils::min(entry->tunings); + float tmax = PyUtils::max(entry->tunings); + + if(tmin <= 0.5f <= tmax){ + sample_rate = 16000; + } else if(tmin <= 1.0f <= tmax){ + sample_rate = 32000; + } else if(tmin <= 1.5f <= tmax){ + sample_rate = 48000; + } else if(tmin <= 2.5f <= tmax){ + sample_rate = 80000; + } else { + sample_rate = 16000 * (tmin + tmax); + } + } + + out.Write(BSWAP32(AIFC::MagicValues::FORM)); + // This should be where the size is, but we need to write it later + out.Write((uint32_t) 0); + out.Write(BSWAP32(AIFC::MagicValues::AIFC)); + + START_SECTION(AIFC::MagicValues::COMM); + + tmp.Write((uint16_t) num_channels); + tmp.Write((uint32_t) num_frames); + + tmp.Write((uint16_t) sample_size); + serialize_f80(sample_rate, tmp); + tmp.Write(AIFC::MagicValues::VAPC); + tmp.Write("\u000BVADPCM ~4-1", false); + + END_SECTION(); + + START_SECTION(AIFC::MagicValues::INST) + tmp.Write(std::string(20, '\0'), false); + END_SECTION(); + + START_CUSTOM_SECTION("\u000BVADPCMCODES") + tmp.Write((uint16_t) 1); + tmp.Write((uint16_t) entry->book.order); + tmp.Write((uint16_t) entry->book.npredictors); + + for(auto x : entry->book.table){ + tmp.Write((int16_t) x); + } + END_SECTION(); + + START_SECTION(AIFC::MagicValues::SSND) + uint32_t zero = 0; + tmp.Write((char*) &zero, 4); + tmp.Write((char*) &zero, 4); + tmp.Write((char*) data.data(), data.size()); + END_SECTION(); + + if(entry->loop.count != 0){ + START_CUSTOM_SECTION("\u000BVADPCMLOOPS") + uint16_t one = BSWAP16(1); + tmp.Write((char*) &one, 2); + tmp.Write((char*) &one, 2); + tmp.Write((uint32_t) entry->loop.start); + tmp.Write((uint32_t) entry->loop.end); + tmp.Write((int32_t) entry->loop.count); + for(size_t i = 0; i < 16; i++){ + int16_t loop = BSWAP16(entry->loop.state.value()[i]); + tmp.Write((char*) &loop, 2); + } + END_SECTION(); + } + + len += 4; + out.Seek(4, LUS::SeekOffsetType::Start); + out.Write((uint32_t) BSWAP32(len)); + +} + +void AudioManager::create_aifc(int32_t index, LUS::BinaryWriter &out) { + int32_t idx = -1; + for(auto &sample_bank : this->loaded_tbl.banks){ + auto offsets = PyUtils::keys(sample_bank->entries); + std::sort(offsets.begin(), offsets.end()); + + for(auto &offset : offsets){ + if(++idx == index){ + write_aifc(sample_bank->entries[offset], out); + return; + } + } } } diff --git a/src/audio/AudioManager.h b/src/audio/AudioManager.h index decb0b5..6a1c559 100644 --- a/src/audio/AudioManager.h +++ b/src/audio/AudioManager.h @@ -4,6 +4,7 @@ #include #include #include +#include "binarytools/BinaryWriter.h" #define NONE 0xFFFF #define ALIGN(val, al) (size_t) ((val + (al - 1)) & -al) @@ -77,15 +78,15 @@ struct SampleBank { std::string name; uint32_t offset; std::vector data; - std::unordered_map entries; + std::unordered_map entries; - AifcEntry AddSample(uint32_t addr, size_t sampleSize, const Book& book, const Loop& loop); + AifcEntry* AddSample(uint32_t addr, size_t sampleSize, const Book& book, const Loop& loop); }; struct TBLFile { - std::vector banks; + std::vector banks; std::vector tbls; - std::unordered_map map; + std::unordered_map map; }; struct CTLHeader { @@ -103,13 +104,13 @@ struct Envelope { struct Bank { std::string name; - SampleBank sampleBank; + SampleBank* sampleBank; std::vector insts; std::vector drums; std::vector>> allInsts; std::vector instOffsets; std::unordered_map envelopes; - std::unordered_map samples; + std::unordered_map samples; void print() const; }; @@ -117,8 +118,12 @@ class AudioManager { public: static AudioManager* Instance; void initialize(std::vector& buffer); + void create_aifc(int32_t index, LUS::BinaryWriter& writer); private: + std::vector banks; + TBLFile loaded_tbl; + static std::vector parse_seq_file(std::vector& buffer, uint32_t offset, bool isCTL); static CTLHeader parse_ctl_header(std::vector& data); static std::optional parse_sound(std::vector data); @@ -126,10 +131,10 @@ private: static Inst parse_inst(std::vector& data, uint32_t addr); static Loop parse_loop(uint32_t addr, std::vector& bankData); static Book parse_book(uint32_t addr, std::vector& bankData); - static AifcEntry parse_sample(std::vector& data, std::vector& bankData, SampleBank& sampleBank); + static AifcEntry* parse_sample(std::vector& data, std::vector& bankData, SampleBank* sampleBank); static std::vector parse_envelope(uint32_t addr, std::vector& dataBank); - static Bank parse_ctl(CTLHeader header, std::vector data, SampleBank bank, uint32_t index); + static Bank parse_ctl(CTLHeader header, std::vector data, SampleBank* bank, uint32_t index); static TBLFile parse_tbl(std::vector& data, std::vector& entries); - std::vector banks; + static void write_aifc(AifcEntry* entry, LUS::BinaryWriter& writer); }; \ No newline at end of file diff --git a/src/factories/AudioFactory.cpp b/src/factories/AudioFactory.cpp index 185d740..64c940e 100644 --- a/src/factories/AudioFactory.cpp +++ b/src/factories/AudioFactory.cpp @@ -1,6 +1,7 @@ #include "AudioFactory.h" #include +#include "audio/AudioManager.h" #include "binarytools/BinaryReader.h" bool AudioFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std::vector& buffer) { @@ -17,11 +18,13 @@ bool AudioFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std: WRITE_I16(BSWAP16(value)); } writer->Write(raw + 0x46, 0x5A); + return true; } - if(path.ends_with(".aiff")){ - + if(!metadata[1].contains("us")){ + return false; } - return false; + AudioManager::Instance->create_aifc(metadata[1]["us"][1], *writer); + return true; } From 50b6c6f65bc0da4a2c07e24c75dcd19baba60083 Mon Sep 17 00:00:00 2001 From: KiritoDv Date: Sun, 3 Sep 2023 23:29:11 -0600 Subject: [PATCH 4/6] Added full SM64 -> AIFF Decoding --- lib/binarytools/BinaryReader.cpp | 8 +- lib/binarytools/BinaryReader.h | 1 + src/audio/AIFCDecode.cpp | 656 +++++++++++++++++++++++++++++++ src/audio/AIFCDecode.h | 6 + src/audio/AIFCWriter.cpp | 48 --- src/audio/AIFCWriter.h | 36 -- src/audio/AudioManager.cpp | 4 - src/audio/AudioManager.h | 13 + src/factories/AudioFactory.cpp | 9 +- 9 files changed, 690 insertions(+), 91 deletions(-) create mode 100644 src/audio/AIFCDecode.cpp create mode 100644 src/audio/AIFCDecode.h delete mode 100644 src/audio/AIFCWriter.cpp delete mode 100644 src/audio/AIFCWriter.h diff --git a/lib/binarytools/BinaryReader.cpp b/lib/binarytools/BinaryReader.cpp index 75044b0..f802885 100644 --- a/lib/binarytools/BinaryReader.cpp +++ b/lib/binarytools/BinaryReader.cpp @@ -58,7 +58,6 @@ int16_t LUS::BinaryReader::ReadInt16() { if (mEndianness != Endianness::Native) { result = BSWAP16(result); } - return result; } @@ -192,6 +191,7 @@ std::string LUS::BinaryReader::ReadString() { for (int i = 0; i < numChars; i++) { res += ReadChar(); } + return res; } @@ -211,6 +211,10 @@ std::string LUS::BinaryReader::ReadCString() { return res; } +size_t LUS::BinaryReader::GetLength() { + return mStream->GetLength(); +} + std::vector LUS::BinaryReader::ToVector() { return mStream->ToVector(); -} +} \ No newline at end of file diff --git a/lib/binarytools/BinaryReader.h b/lib/binarytools/BinaryReader.h index 91e2a40..02e65cc 100644 --- a/lib/binarytools/BinaryReader.h +++ b/lib/binarytools/BinaryReader.h @@ -23,6 +23,7 @@ class BinaryReader { void Seek(int32_t offset, SeekOffsetType seekType); uint32_t GetBaseAddress(); + size_t GetLength(); void Read(int32_t length); void Read(char* buffer, int32_t length); diff --git a/src/audio/AIFCDecode.cpp b/src/audio/AIFCDecode.cpp new file mode 100644 index 0000000..21eec08 --- /dev/null +++ b/src/audio/AIFCDecode.cpp @@ -0,0 +1,656 @@ +/** + * Bruteforcing decoder for converting ADPCM-encoded AIFC into AIFF, in a way + * that roundtrips with vadpcm_enc. + */ +#include "AIFCDecode.h" +#include +#include +#include +#include +#include +#include "binarytools/BinaryWriter.h" +#include "BinaryReader.h" + +typedef signed char s8; +typedef short s16; +typedef int s32; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef float f32; + +#undef BSWAP16 +#undef BSWAP32 +#undef BSWAP64 + +#define bswap16(x) __builtin_bswap16(x) +#define bswap32(x) __builtin_bswap32(x) +#define BSWAP16(x) x = bswap16(x) +#define BSWAP32(x) x = bswap32(x) + +#define BSWAP16_MANY(x, n) for (s32 _i = 0; _i < n; _i++) BSWAP16((x)[_i]) + +#ifndef WIN32 +#define NORETURN __attribute__((noreturn)) +#define UNUSED __attribute__((unused)) +#else +#define NORETURN +#define UNUSED +#endif + +typedef struct { + u32 ckID; + u32 ckSize; +} ChunkHeader; + +typedef struct { + u32 ckID; + u32 ckSize; + u32 formType; +} Chunk; + +typedef struct { + s16 numChannels; + u16 numFramesH; + u16 numFramesL; + s16 sampleSize; + s16 sampleRate[5]; // 80-bit float + u16 compressionTypeH; + u16 compressionTypeL; +} CommonChunk; + +typedef struct { + s16 MarkerID; + u16 positionH; + u16 positionL; +} Marker; + +typedef struct { + s16 playMode; + s16 beginLoop; + s16 endLoop; +} Loop; + +typedef struct { + s8 baseNote; + s8 detune; + s8 lowNote; + s8 highNote; + s8 lowVelocity; + s8 highVelocity; + s16 gain; + Loop sustainLoop; + Loop releaseLoop; +} InstrumentChunk; + +typedef struct { + s32 offset; + s32 blockSize; +} SoundDataChunk; + +typedef struct { + s16 version; + s16 order; + s16 nEntries; +} CodeChunk; + +typedef struct +{ + u32 start; + u32 end; + u32 count; + s16 state[16]; +} ALADPCMloop; + +#define checked_fread(a, b, c, d) d.Read((char*) a, b * c) + +NORETURN +void fail_parse(const char *fmt, ...) +{ + char *formatted = nullptr; + va_list ap; + va_start(ap, fmt); + int size = vsnprintf(nullptr, 0, fmt, ap); + va_end(ap); + if (size >= 0) { + size++; + formatted = static_cast(malloc(size)); + if (formatted != nullptr) { + va_start(ap, fmt); + size = vsnprintf(formatted, size, fmt, ap); + va_end(ap); + if (size < 0) { + free(formatted); + formatted = nullptr; + } + } + } + + if (formatted != nullptr) { + throw std::runtime_error(formatted); + free(formatted); + } + throw std::runtime_error("Error parsing file"); +} + +s32 myrand() +{ + static u64 state = 1619236481962341ULL; + state *= 3123692312231ULL; + state++; + return state >> 33; +} + +s16 qsample(s32 x, s32 scale) +{ + // Compute x / 2^scale rounded to the nearest integer, breaking ties towards zero. + if (scale == 0) return x; + return (x + (1 << (scale - 1)) - (x > 0)) >> scale; +} + +s16 clamp_to_s16(s32 x) +{ + if (x < -0x8000) return -0x8000; + if (x > 0x7fff) return 0x7fff; + return (s16) x; +} + +s32 toi4(s32 x) +{ + if (x >= 8) return x - 16; + return x; +} + +s32 readaifccodebook(LUS::BinaryReader& fhandle, s32 ****table, s16 *order, s16 *npredictors) +{ + checked_fread(order, sizeof(s16), 1, fhandle); + BSWAP16(*order); + checked_fread(npredictors, sizeof(s16), 1, fhandle); + BSWAP16(*npredictors); + *table = static_cast(malloc(*npredictors * sizeof(s32 **))); + for (s32 i = 0; i < *npredictors; i++) { + (*table)[i] = static_cast(malloc(8 * sizeof(s32 *))); + for (s32 j = 0; j < 8; j++) { + (*table)[i][j] = static_cast(malloc((*order + 8) * sizeof(s32))); + } + } + + for (s32 i = 0; i < *npredictors; i++) { + s32 **table_entry = (*table)[i]; + for (s32 j = 0; j < *order; j++) { + for (s32 k = 0; k < 8; k++) { + s16 ts; + checked_fread(&ts, sizeof(s16), 1, fhandle); + BSWAP16(ts); + table_entry[k][j] = ts; + } + } + + for (s32 k = 1; k < 8; k++) { + table_entry[k][*order] = table_entry[k - 1][*order - 1]; + } + + table_entry[0][*order] = 1 << 11; + + for (s32 k = 1; k < 8; k++) { + s32 j = 0; + for (; j < k; j++) { + table_entry[j][k + *order] = 0; + } + + for (; j < 8; j++) { + table_entry[j][k + *order] = table_entry[j - k][*order]; + } + } + } + return 0; +} + +ALADPCMloop *readlooppoints(LUS::BinaryReader& reader, s16 *nloops) { + BSWAP16(*nloops); + checked_fread(nloops, sizeof(s16), 1, reader); + auto *al = static_cast(malloc(*nloops * sizeof(ALADPCMloop))); + for (s32 i = 0; i < *nloops; i++) { + checked_fread(&al[i], sizeof(ALADPCMloop), 1, reader); + BSWAP32(al[i].start); + BSWAP32(al[i].end); + BSWAP32(al[i].count); + BSWAP16_MANY(al[i].state, 16); + } + return al; +} + +s32 inner_product(s32 length, s32 *v1, s32 *v2) +{ + s32 out = 0; + for (s32 i = 0; i < length; i++) { + out += v1[i] * v2[i]; + } + + // Compute "out / 2^11", rounded down. + s32 dout = out / (1 << 11); + s32 fiout = dout * (1 << 11); + return dout - (out - fiout < 0); +} + +void my_decodeframe(u8 *frame, s32 *state, s32 order, s32 ***coefTable) +{ + s32 ix[16]; + + u8 header = frame[0]; + s32 scale = 1 << (header >> 4); + s32 optimalp = header & 0xf; + + for (s32 i = 0; i < 16; i += 2) { + u8 c = frame[1 + i/2]; + ix[i] = c >> 4; + ix[i + 1] = c & 0xf; + } + + for (s32 i = 0; i < 16; i++) { + if (ix[i] >= 8) ix[i] -= 16; + ix[i] *= scale; + } + + for (s32 j = 0; j < 2; j++) { + s32 in_vec[16]; + if (j == 0) { + for (s32 i = 0; i < order; i++) { + in_vec[i] = state[16 - order + i]; + } + } else { + for (s32 i = 0; i < order; i++) { + in_vec[i] = state[8 - order + i]; + } + } + + for (s32 i = 0; i < 8; i++) { + s32 ind = j * 8 + i; + in_vec[order + i] = ix[ind]; + state[ind] = inner_product(order + i, coefTable[optimalp][i], in_vec) + ix[ind]; + } + } +} + +void my_encodeframe(u8 *out, s16 *inBuffer, s32 *state, s32 ***coefTable, s32 order, s32 npredictors) +{ + s16 ix[16]; + s32 prediction[16]; + s32 inVector[16]; + s32 saveState[16]; + s32 optimalp = 0; + s32 scale; + s32 ie[16]; + s32 e[16]; + f32 min = 1e30; + + for (s32 k = 0; k < npredictors; k++) { + for (s32 j = 0; j < 2; j++) { + for (s32 i = 0; i < order; i++) { + inVector[i] = (j == 0 ? state[16 - order + i] : inBuffer[8 - order + i]); + } + + for (s32 i = 0; i < 8; i++) { + prediction[j * 8 + i] = inner_product(order + i, coefTable[k][i], inVector); + e[j * 8 + i] = inVector[i + order] = inBuffer[j * 8 + i] - prediction[j * 8 + i]; + } + } + + f32 se = 0.0f; + for (s32 j = 0; j < 16; j++) { + se += (f32) e[j] * (f32) e[j]; + } + + if (se < min) { + min = se; + optimalp = k; + } + } + + for (s32 j = 0; j < 2; j++) { + for (s32 i = 0; i < order; i++) { + inVector[i] = (j == 0 ? state[16 - order + i] : inBuffer[8 - order + i]); + } + + for (s32 i = 0; i < 8; i++) { + prediction[j * 8 + i] = inner_product(order + i, coefTable[optimalp][i], inVector); + e[j * 8 + i] = inVector[i + order] = inBuffer[j * 8 + i] - prediction[j * 8 + i]; + } + } + + for (s32 i = 0; i < 16; i++) { + ie[i] = clamp_to_s16(e[i]); + } + + s32 max = 0; + for (s32 i = 0; i < 16; i++) { + if (abs(ie[i]) > abs(max)) { + max = ie[i]; + } + } + + for (scale = 0; scale <= 12; scale++) { + if (max <= 7 && max >= -8) break; + max /= 2; + } + + for (s32 i = 0; i < 16; i++) { + saveState[i] = state[i]; + } + + for (s32 nIter = 0, again = 1; nIter < 2 && again; nIter++) { + again = 0; + if (nIter == 1) scale++; + if (scale > 12) { + scale = 12; + } + + for (s32 j = 0; j < 2; j++) { + s32 base = j * 8; + for (s32 i = 0; i < order; i++) { + inVector[i] = (j == 0 ? + saveState[16 - order + i] : state[8 - order + i]); + } + + for (s32 i = 0; i < 8; i++) { + prediction[base + i] = inner_product(order + i, coefTable[optimalp][i], inVector); + s32 se = inBuffer[base + i] - prediction[base + i]; + ix[base + i] = qsample(se, scale); + s32 cV = clamp_to_s16(ix[base + i]) - ix[base + i]; + if (cV > 1 || cV < -1) again = 1; + ix[base + i] += cV; + inVector[i + order] = ix[base + i] * (1 << scale); + state[base + i] = prediction[base + i] + inVector[i + order]; + } + } + } + + u8 header = (scale << 4) | (optimalp & 0xf); + out[0] = header; + for (s32 i = 0; i < 16; i += 2) { + u8 c = ((ix[i] & 0xf) << 4) | (ix[i + 1] & 0xf); + out[1 + i/2] = c; + } +} + +void permute(s16 *out, s32 *in, s32 scale) +{ + for (s32 i = 0; i < 16; i++) { + out[i] = clamp_to_s16(in[i] - scale / 2 + myrand() % (scale + 1)); + } +} + +void write_header(LUS::BinaryWriter& writer, const char *id, s32 size) { + writer.Write((char*) id, (size_t) 4); + BSWAP32(size); + writer.Write(size); +} + +void write_aiff(std::vector data, LUS::BinaryWriter& writer) { + s16 order = -1; + s16 nloops = 0; + ALADPCMloop *aloops = nullptr; + s16 npredictors = -1; + s32 ***coefTable = nullptr; + s32 state[16]; + s32 soundPointer = -1; + s32 currPos = 0; + s32 nSamples = 0; + Chunk FormChunk; + ChunkHeader Header; + CommonChunk CommChunk; + InstrumentChunk InstChunk; + SoundDataChunk SndDChunk; + + memset(&InstChunk, 0, sizeof(InstChunk)); + LUS::BinaryReader reader((char*)data.data(), data.size()); + + checked_fread(&FormChunk, sizeof(FormChunk), 1, reader); + BSWAP32(FormChunk.ckID); + BSWAP32(FormChunk.formType); + if ((FormChunk.ckID != 0x464f524d) || (FormChunk.formType != 0x41494643)) { // FORM, AIFC + fail_parse("not an AIFF-C file"); + } + + for (;;) { + if (reader.GetBaseAddress() >= reader.GetLength()) { + break; + } + reader.Read((char*) &Header, sizeof(Header)); + u32 ts; + BSWAP32(Header.ckID); + BSWAP32(Header.ckSize); + + Header.ckSize++; + Header.ckSize &= ~1; + s32 offset = reader.GetBaseAddress(); + + if(Header.ckID == 0x434f4d4d) { // COMM + checked_fread(&CommChunk, sizeof(CommChunk), 1, reader); + BSWAP16(CommChunk.numChannels); + BSWAP16(CommChunk.numFramesH); + BSWAP16(CommChunk.numFramesL); + BSWAP16(CommChunk.sampleSize); + BSWAP16(CommChunk.compressionTypeH); + BSWAP16(CommChunk.compressionTypeL); + s32 cType = (CommChunk.compressionTypeH << 16) + CommChunk.compressionTypeL; + if (cType != 0x56415043) { // VAPC + fail_parse("file is of the wrong compression type"); + } + if (CommChunk.numChannels != 1) { + fail_parse("file contains %d channels, only 1 channel supported", CommChunk.numChannels); + } + if (CommChunk.sampleSize != 16) { + fail_parse("file contains %d bit samples, only 16 bit samples supported", CommChunk.sampleSize); + } + + nSamples = (CommChunk.numFramesH << 16) + CommChunk.numFramesL; + + // Allow broken input lengths + if (nSamples % 16) { + nSamples--; + } + + if (nSamples % 16 != 0) { + fail_parse("number of chunks must be a multiple of 16, found %d", nSamples); + } + } + + if(Header.ckID == 0x53534e44) { //SSND + checked_fread(&SndDChunk, sizeof(SndDChunk), 1, reader); + BSWAP32(SndDChunk.offset); + BSWAP32(SndDChunk.blockSize); + assert(SndDChunk.offset == 0); + assert(SndDChunk.blockSize == 0); + soundPointer = reader.GetBaseAddress(); + } + + if(Header.ckID == 0x4150504c){ // APPL + checked_fread(&ts, sizeof(u32), 1, reader); + BSWAP32(ts); + if (ts == 0x73746f63) { // stoc + u8 len; + checked_fread(&len, 1, 1, reader); + if (len == 11) { + char ChunkName[12]; + s16 version; + checked_fread(ChunkName, 11, 1, reader); + ChunkName[11] = '\0'; + if (strcmp("VADPCMCODES", ChunkName) == 0) { + checked_fread(&version, sizeof(s16), 1, reader); + BSWAP16(version); + if (version != 1) { + fail_parse("Unknown codebook chunk version"); + } + readaifccodebook(reader, &coefTable, &order, &npredictors); + } + else if (strcmp("VADPCMLOOPS", ChunkName) == 0) { + checked_fread(&version, sizeof(s16), 1, reader); + BSWAP16(version); + if (version != 1) { + fail_parse("Unknown loop chunk version"); + } + aloops = readlooppoints(reader, &nloops); + BSWAP16(nloops); + if (nloops != 1) { + fail_parse("Only a single loop supported"); + } + } + } + } + } + + reader.Seek(offset + Header.ckSize, LUS::SeekOffsetType::Start); + } + + if (coefTable == nullptr) { + fail_parse("Codebook missing from bitstream"); + } + + for (s32 i = 0; i < order; i++) { + state[15 - i] = 0; + } + + u32 outputBytes = nSamples * sizeof(s16); + + reader.Seek(soundPointer, LUS::SeekOffsetType::Start); + + while (currPos < nSamples) { + u8 input[9]; + u8 encoded[9]; + s32 lastState[16]; + s32 decoded[16]; + s16 guess[16]; + s16 origGuess[16]; + + memcpy(lastState, state, sizeof(lastState)); + checked_fread(input, 9, 1, reader); + + // Decode for real + my_decodeframe(input, state, order, coefTable); + memcpy(decoded, state, sizeof(lastState)); + + // Create a guess from that, by clamping to 16 bits + for (s32 i = 0; i < 16; i++) { + origGuess[i] = clamp_to_s16(state[i]); + } + + // Encode the guess + memcpy(state, lastState, sizeof(lastState)); + memcpy(guess, origGuess, sizeof(guess)); + my_encodeframe(encoded, guess, state, coefTable, order, npredictors); + + // If it doesn't match, randomly round numbers until it does. + if (memcmp(input, encoded, 9) != 0) { + s32 scale = 1 << (input[0] >> 4); + do { + permute(guess, decoded, scale); + memcpy(state, lastState, sizeof(lastState)); + my_encodeframe(encoded, guess, state, coefTable, order, npredictors); + } while (memcmp(input, encoded, 9) != 0); + + // Bring the matching closer to the original decode (not strictly + // necessary, but it will move us closer to the target on average). + for (s32 failures = 0; failures < 50; failures++) { + s32 ind = myrand() % 16; + s32 old = guess[ind]; + if (old == origGuess[ind]) continue; + guess[ind] = origGuess[ind]; + if (myrand() % 2) guess[ind] += (old - origGuess[ind]) / 2; + memcpy(state, lastState, sizeof(lastState)); + my_encodeframe(encoded, guess, state, coefTable, order, npredictors); + if (memcmp(input, encoded, 9) == 0) { + failures = -1; + } + else { + guess[ind] = old; + } + } + } + + memcpy(state, decoded, sizeof(lastState)); + BSWAP16_MANY(guess, 16); + writer.Seek(currPos * 2, LUS::SeekOffsetType::Start); + writer.Write((char*) guess, sizeof(guess)); + currPos += 16; + } + + // Write an incomplete file header. We'll fill in the size later. + writer.Seek(0, LUS::SeekOffsetType::Start); + writer.Write((char*) "FORM\0\0\0\0AIFF", 12); + + // Subtract 4 from the COMM size to skip the compression field. + write_header(writer, "COMM", sizeof(CommonChunk) - 4); + CommChunk.numFramesH = nSamples >> 16; + CommChunk.numFramesL = nSamples & 0xffff; + BSWAP16(CommChunk.numChannels); + BSWAP16(CommChunk.numFramesH); + BSWAP16(CommChunk.numFramesL); + BSWAP16(CommChunk.sampleSize); + writer.Write((char*) &CommChunk, sizeof(CommonChunk) - 4); + + if (nloops > 0) { + s32 startPos = aloops[0].start, endPos = aloops[0].end; + const char *markerNames[2] = {"start", "end"}; + Marker markers[2] = { + {1, static_cast((u16) startPos >> 16), static_cast((u16) startPos & 0xffff)}, + {2, static_cast(endPos >> 16), static_cast(endPos & 0xffff)} + }; + write_header(writer, "MARK", 2 + 2 * sizeof(Marker) + 1 + 5 + 1 + 3); + s16 numMarkers = bswap16(2); + writer.Write(numMarkers); + for (s32 i = 0; i < 2; i++) { + u8 len = (u8) strlen(markerNames[i]); + BSWAP16(markers[i].MarkerID); + BSWAP16(markers[i].positionH); + BSWAP16(markers[i].positionL); + writer.Write(markers[i].MarkerID); + writer.Write(markers[i].positionH); + writer.Write(markers[i].positionL); + writer.Write(len); + writer.Write((char*) markerNames[i], len); + } + + write_header(writer, "INST", sizeof(InstrumentChunk)); + InstChunk.sustainLoop.playMode = bswap16(1); + InstChunk.sustainLoop.beginLoop = bswap16(1); + InstChunk.sustainLoop.endLoop = bswap16(2); + InstChunk.releaseLoop.playMode = 0; + InstChunk.releaseLoop.beginLoop = 0; + InstChunk.releaseLoop.endLoop = 0; + writer.Write((char*) &InstChunk, sizeof(InstrumentChunk)); + } + + // Save the coefficient table for use when encoding. Ideally this wouldn't + // be needed and "tabledesign -s 1" would generate the right table, but in + // practice it's difficult to adjust samples to make that happen. + write_header(writer, "APPL", 4 + 12 + sizeof(CodeChunk) + npredictors * order * 8 * 2); + writer.Write("stoc", false); + CodeChunk cChunk; + cChunk.version = bswap16(1); + cChunk.order = bswap16(order); + cChunk.nEntries = bswap16(npredictors); + writer.Write("\x0bVADPCMCODES", false); + writer.Write((char*) &cChunk, sizeof(CodeChunk)); + for (s32 i = 0; i < npredictors; i++) { + for (s32 j = 0; j < order; j++) { + for (s32 k = 0; k < 8; k++) { + s16 ts = bswap16(coefTable[i][k][j]); + writer.Write(ts); + } + } + } + + write_header(writer, "SSND", outputBytes + 8); + SndDChunk.offset = 0; + SndDChunk.blockSize = 0; + writer.Write((char*) &SndDChunk, sizeof(SoundDataChunk)); + + // Fix the size in the header + s32 fileSize = bswap32(writer.GetLength() - 8); + writer.Seek(4, LUS::SeekOffsetType::Start); + writer.Write(fileSize); + + reader.Close(); +} diff --git a/src/audio/AIFCDecode.h b/src/audio/AIFCDecode.h new file mode 100644 index 0000000..e141a80 --- /dev/null +++ b/src/audio/AIFCDecode.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include "BinaryWriter.h" + +void write_aiff(std::vector data, LUS::BinaryWriter& writer); \ No newline at end of file diff --git a/src/audio/AIFCWriter.cpp b/src/audio/AIFCWriter.cpp deleted file mode 100644 index 1df5813..0000000 --- a/src/audio/AIFCWriter.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "AIFCWriter.h" - -#include "hj/pyutils.h" - -#define ALIGN(val, al) (size_t) ((val + (al - 1)) & -al) - -std::vector AIFCWriter::pack(const std::string& str) { - std::vector result; - result.insert(result.end(), str.begin(), str.end()); - return result; -} - -std::vector AIFCWriter::pstring(const std::vector& data) { - std::vector result; - result.insert(result.end(), data.begin(), data.end()); - if (data.size() % 2 == 0) { - result.push_back('\0'); - } - return result; -} - -void AIFCWriter::add_section(AIFC::MagicValues magic, const std::vector& data) { - sections.emplace_back(magic, data); - total_size += ALIGN(data.size(), 2) + 8; -} - -void AIFCWriter::add_custom_section(const std::string& section, const std::vector& data) { - this->add_section(AIFC::MagicValues::AAPL, pack("stoc") + pstring(pack(section)) + data); -} - -void AIFCWriter::finish() { - // total_size isn't used and is regularly wrong. - // In particular, vadpcm_enc preserves the size of the input file... - total_size += 4; - - out.Write(BSWAP32(AIFC::MagicValues::FORM)); - out.Write((uint32_t) BSWAP32(total_size)); - out.Write(BSWAP32(AIFC::MagicValues::AIFC)); - - for (const auto& section : sections) { - out.Write((uint32_t) BSWAP32(section.first)); - out.Write((uint32_t) BSWAP32(section.second.size())); - out.Write((char*) section.second.data(), section.second.size()); - if(section.second.size() % 2) { - out.Write('\0'); - } - } -} diff --git a/src/audio/AIFCWriter.h b/src/audio/AIFCWriter.h deleted file mode 100644 index 9ea7843..0000000 --- a/src/audio/AIFCWriter.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include "binarytools/BinaryWriter.h" - -namespace AIFC { - enum MagicValues { - FORM = 0x464f524d, - AIFC = 0x41494643, - COMM = 0x434f4d4d, - INST = 0x494e5354, - VAPC = 0x56415043, - SSND = 0x53534e44, - AAPL = 0x4150504c, - stoc = 0x73746f63, - }; -} - -class AIFCWriter { -public: - AIFCWriter(LUS::BinaryWriter& out) : out(out), total_size(0) {} - - static std::vector pack(const std::string& str); - static std::vector pstring(const std::vector& data); - void add_section(AIFC::MagicValues, const std::vector& data); - void add_custom_section(const std::string& section, const std::vector& data); - void finish(); - -private: - LUS::BinaryWriter& out; - std::vector>> sections; - std::size_t total_size; -}; \ No newline at end of file diff --git a/src/audio/AudioManager.cpp b/src/audio/AudioManager.cpp index f241a9b..4549658 100644 --- a/src/audio/AudioManager.cpp +++ b/src/audio/AudioManager.cpp @@ -8,7 +8,6 @@ #include "hj/pyutils.h" #include "binarytools/BinaryReader.h" #include "hj/zip.h" -#include "AIFCWriter.h" #include std::unordered_map name_table; @@ -435,7 +434,6 @@ void AudioManager::initialize(std::vector& buffer) { auto zipped = zip(PyUtils::range(0, ctl.size()), ctl, this->loaded_tbl.tbls); -// std::cout << "================================" << std::endl; for (const auto& item : zipped) { auto [index, ctrl, sample_bank_name] = item; auto sample_bank = this->loaded_tbl.map[sample_bank_name]; @@ -444,8 +442,6 @@ void AudioManager::initialize(std::vector& buffer) { auto header = parse_ctl_header(headerRaw); auto bank = parse_ctl(header, PyUtils::slice(entry, 16), 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 index 6a1c559..4c3265c 100644 --- a/src/audio/AudioManager.h +++ b/src/audio/AudioManager.h @@ -9,6 +9,19 @@ #define NONE 0xFFFF #define ALIGN(val, al) (size_t) ((val + (al - 1)) & -al) +namespace AIFC { + enum MagicValues { + FORM = 0x464f524d, + AIFC = 0x41494643, + COMM = 0x434f4d4d, + INST = 0x494e5354, + VAPC = 0x56415043, + SSND = 0x53534e44, + AAPL = 0x4150504c, + stoc = 0x73746f63, + }; +} + struct Entry { uint32_t offset; uint32_t length; diff --git a/src/factories/AudioFactory.cpp b/src/factories/AudioFactory.cpp index 64c940e..d1d2111 100644 --- a/src/factories/AudioFactory.cpp +++ b/src/factories/AudioFactory.cpp @@ -2,6 +2,7 @@ #include #include "audio/AudioManager.h" +#include "audio/AIFCDecode.h" #include "binarytools/BinaryReader.h" bool AudioFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std::vector& buffer) { @@ -25,6 +26,12 @@ bool AudioFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std: return false; } - AudioManager::Instance->create_aifc(metadata[1]["us"][1], *writer); + LUS::BinaryWriter aifcWriter; + AudioManager::Instance->create_aifc(metadata[1]["us"][1], aifcWriter); + std::vector aifcData = aifcWriter.ToVector(); + aifcWriter.Close(); + + write_aiff(aifcData, *writer); + return true; } From fc7bf4c6ec1a637bb9eb240220533af856dc5a66 Mon Sep 17 00:00:00 2001 From: KiritoDv Date: Fri, 15 Sep 2023 20:46:06 -0600 Subject: [PATCH 5/6] Added full bank extraction --- assets.json | 39 ++ banks_table.h | 129 ++++++ lib/hj/pyutils.h | 8 + samples_table.h | 717 ++++++++++++++++++++++++++++++++ src/Companion.cpp | 2 + src/audio/AudioManager.cpp | 387 ++++++++++-------- src/audio/AudioManager.h | 80 ++-- src/audio/samples_table.h | 722 +++++++++++++++++++++++++++++++++ src/factories/AudioFactory.cpp | 156 ++++++- src/factories/RawFactory.h | 2 + src/factories/ResourceType.h | 2 + tools/gen_banks.js | 37 ++ tools/gen_samples.js | 37 ++ 13 files changed, 2107 insertions(+), 211 deletions(-) create mode 100644 banks_table.h create mode 100644 samples_table.h create mode 100644 src/audio/samples_table.h create mode 100644 tools/gen_banks.js create mode 100644 tools/gen_samples.js diff --git a/assets.json b/assets.json index 81bcd62..43208d4 100644 --- a/assets.json +++ b/assets.json @@ -1121,6 +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/bank_sets.bset": [159, { "us": [8177185] }], "@sound_data": { "us": { "bank_sets": [8177184, 160], @@ -1128,6 +1129,44 @@ "sound_tbl": [5846368, 2216704] } }, +"sound/banks/us/00_bank.bnk": {"us": [0]}, +"sound/banks/us/01_bank.bnk": {"us": [1]}, +"sound/banks/us/02_bank.bnk": {"us": [2]}, +"sound/banks/us/03_bank.bnk": {"us": [3]}, +"sound/banks/us/04_bank.bnk": {"us": [4]}, +"sound/banks/us/05_bank.bnk": {"us": [5]}, +"sound/banks/us/06_bank.bnk": {"us": [6]}, +"sound/banks/us/07_bank.bnk": {"us": [7]}, +"sound/banks/us/08_bank.bnk": {"us": [8]}, +"sound/banks/us/09_bank.bnk": {"us": [9]}, +"sound/banks/us/0A_bank.bnk": {"us": [10]}, +"sound/banks/us/0B_bank.bnk": {"us": [11]}, +"sound/banks/us/0C_bank.bnk": {"us": [12]}, +"sound/banks/us/0D_bank.bnk": {"us": [13]}, +"sound/banks/us/0E_bank.bnk": {"us": [14]}, +"sound/banks/us/0F_bank.bnk": {"us": [15]}, +"sound/banks/us/10_bank.bnk": {"us": [16]}, +"sound/banks/us/11_bank.bnk": {"us": [17]}, +"sound/banks/us/12_bank.bnk": {"us": [18]}, +"sound/banks/us/13_bank.bnk": {"us": [19]}, +"sound/banks/us/14_bank.bnk": {"us": [20]}, +"sound/banks/us/15_bank.bnk": {"us": [21]}, +"sound/banks/us/16_bank.bnk": {"us": [22]}, +"sound/banks/us/17_bank.bnk": {"us": [23]}, +"sound/banks/us/18_bank.bnk": {"us": [24]}, +"sound/banks/us/19_bank.bnk": {"us": [25]}, +"sound/banks/us/1A_bank.bnk": {"us": [26]}, +"sound/banks/us/1B_bank.bnk": {"us": [27]}, +"sound/banks/us/1C_bank.bnk": {"us": [28]}, +"sound/banks/us/1D_bank.bnk": {"us": [29]}, +"sound/banks/us/1E_bank.bnk": {"us": [30]}, +"sound/banks/us/1F_bank.bnk": {"us": [31]}, +"sound/banks/us/20_bank.bnk": {"us": [32]}, +"sound/banks/us/21_bank.bnk": {"us": [33]}, +"sound/banks/us/22_bank.bnk": {"us": [34]}, +"sound/banks/us/23_bank.bnk": {"us": [35]}, +"sound/banks/us/24_bank.bnk": {"us": [36]}, +"sound/banks/us/25_bank.bnk": {"us": [37]}, "textures/cave/hmc_textures.00000.rgba16.png": [32,64,4096,{"jp":[3432432,0],"us":[3439184,0],"eu":[3312784,0],"sh":[3287152,0]}], "textures/cave/hmc_textures.01000.rgba16.png": [32,32,2048,{"jp":[3432432,4096],"us":[3439184,4096],"eu":[3312784,4096],"sh":[3287152,4096]}], "textures/cave/hmc_textures.01800.rgba16.png": [32,64,4096,{"jp":[3432432,6144],"us":[3439184,6144],"eu":[3312784,6144],"sh":[3287152,6144]}], diff --git a/banks_table.h b/banks_table.h new file mode 100644 index 0000000..1c05cb1 --- /dev/null +++ b/banks_table.h @@ -0,0 +1,129 @@ +#pragma once +#include "align_asset_macro.h" + +#define dgBank00 "__OTR__sound/banks/us/00_bank" +static const ALIGN_ASSET(2) char gBank00[] = dgBank00; + +#define dgBank01 "__OTR__sound/banks/us/01_bank" +static const ALIGN_ASSET(2) char gBank01[] = dgBank01; + +#define dgBank02 "__OTR__sound/banks/us/02_bank" +static const ALIGN_ASSET(2) char gBank02[] = dgBank02; + +#define dgBank03 "__OTR__sound/banks/us/03_bank" +static const ALIGN_ASSET(2) char gBank03[] = dgBank03; + +#define dgBank04 "__OTR__sound/banks/us/04_bank" +static const ALIGN_ASSET(2) char gBank04[] = dgBank04; + +#define dgBank05 "__OTR__sound/banks/us/05_bank" +static const ALIGN_ASSET(2) char gBank05[] = dgBank05; + +#define dgBank06 "__OTR__sound/banks/us/06_bank" +static const ALIGN_ASSET(2) char gBank06[] = dgBank06; + +#define dgBank07 "__OTR__sound/banks/us/07_bank" +static const ALIGN_ASSET(2) char gBank07[] = dgBank07; + +#define dgBank08 "__OTR__sound/banks/us/08_bank" +static const ALIGN_ASSET(2) char gBank08[] = dgBank08; + +#define dgBank09 "__OTR__sound/banks/us/09_bank" +static const ALIGN_ASSET(2) char gBank09[] = dgBank09; + +#define dgBank0A "__OTR__sound/banks/us/0A_bank" +static const ALIGN_ASSET(2) char gBank0A[] = dgBank0A; + +#define dgBank0B "__OTR__sound/banks/us/0B_bank" +static const ALIGN_ASSET(2) char gBank0B[] = dgBank0B; + +#define dgBank0C "__OTR__sound/banks/us/0C_bank" +static const ALIGN_ASSET(2) char gBank0C[] = dgBank0C; + +#define dgBank0D "__OTR__sound/banks/us/0D_bank" +static const ALIGN_ASSET(2) char gBank0D[] = dgBank0D; + +#define dgBank0E "__OTR__sound/banks/us/0E_bank" +static const ALIGN_ASSET(2) char gBank0E[] = dgBank0E; + +#define dgBank0F "__OTR__sound/banks/us/0F_bank" +static const ALIGN_ASSET(2) char gBank0F[] = dgBank0F; + +#define dgBank10 "__OTR__sound/banks/us/10_bank" +static const ALIGN_ASSET(2) char gBank10[] = dgBank10; + +#define dgBank11 "__OTR__sound/banks/us/11_bank" +static const ALIGN_ASSET(2) char gBank11[] = dgBank11; + +#define dgBank12 "__OTR__sound/banks/us/12_bank" +static const ALIGN_ASSET(2) char gBank12[] = dgBank12; + +#define dgBank13 "__OTR__sound/banks/us/13_bank" +static const ALIGN_ASSET(2) char gBank13[] = dgBank13; + +#define dgBank14 "__OTR__sound/banks/us/14_bank" +static const ALIGN_ASSET(2) char gBank14[] = dgBank14; + +#define dgBank15 "__OTR__sound/banks/us/15_bank" +static const ALIGN_ASSET(2) char gBank15[] = dgBank15; + +#define dgBank16 "__OTR__sound/banks/us/16_bank" +static const ALIGN_ASSET(2) char gBank16[] = dgBank16; + +#define dgBank17 "__OTR__sound/banks/us/17_bank" +static const ALIGN_ASSET(2) char gBank17[] = dgBank17; + +#define dgBank18 "__OTR__sound/banks/us/18_bank" +static const ALIGN_ASSET(2) char gBank18[] = dgBank18; + +#define dgBank19 "__OTR__sound/banks/us/19_bank" +static const ALIGN_ASSET(2) char gBank19[] = dgBank19; + +#define dgBank1A "__OTR__sound/banks/us/1A_bank" +static const ALIGN_ASSET(2) char gBank1A[] = dgBank1A; + +#define dgBank1B "__OTR__sound/banks/us/1B_bank" +static const ALIGN_ASSET(2) char gBank1B[] = dgBank1B; + +#define dgBank1C "__OTR__sound/banks/us/1C_bank" +static const ALIGN_ASSET(2) char gBank1C[] = dgBank1C; + +#define dgBank1D "__OTR__sound/banks/us/1D_bank" +static const ALIGN_ASSET(2) char gBank1D[] = dgBank1D; + +#define dgBank1E "__OTR__sound/banks/us/1E_bank" +static const ALIGN_ASSET(2) char gBank1E[] = dgBank1E; + +#define dgBank1F "__OTR__sound/banks/us/1F_bank" +static const ALIGN_ASSET(2) char gBank1F[] = dgBank1F; + +#define dgBank20 "__OTR__sound/banks/us/20_bank" +static const ALIGN_ASSET(2) char gBank20[] = dgBank20; + +#define dgBank21 "__OTR__sound/banks/us/21_bank" +static const ALIGN_ASSET(2) char gBank21[] = dgBank21; + +#define dgBank22 "__OTR__sound/banks/us/22_bank" +static const ALIGN_ASSET(2) char gBank22[] = dgBank22; + +#define dgBank23 "__OTR__sound/banks/us/23_bank" +static const ALIGN_ASSET(2) char gBank23[] = dgBank23; + +#define dgBank24 "__OTR__sound/banks/us/24_bank" +static const ALIGN_ASSET(2) char gBank24[] = dgBank24; + +#define dgBank25 "__OTR__sound/banks/us/25_bank" +static const ALIGN_ASSET(2) char gBank25[] = dgBank25; + +static const char* gBankTable[] = { + gBank00, gBank01, gBank02, gBank03, + gBank04, gBank05, gBank06, gBank07, + gBank08, gBank09, gBank0A, gBank0B, + gBank0C, gBank0D, gBank0E, gBank0F, + gBank10, gBank11, gBank12, gBank13, + gBank14, gBank15, gBank16, gBank17, + gBank18, gBank19, gBank1A, gBank1B, + gBank1C, gBank1D, gBank1E, gBank1F, + gBank20, gBank21, gBank22, gBank23, + gBank24, gBank25, +}; diff --git a/lib/hj/pyutils.h b/lib/hj/pyutils.h index f7dafbf..f34c1d8 100644 --- a/lib/hj/pyutils.h +++ b/lib/hj/pyutils.h @@ -44,6 +44,14 @@ std::vector keys(std::unordered_map const &map) { } return result; } +template +std::vector keys(std::map const &map) { + std::vector result; + for (auto const &pair: map) { + result.push_back(pair.first); + } + return result; +} } template diff --git a/samples_table.h b/samples_table.h new file mode 100644 index 0000000..5f1a3ae --- /dev/null +++ b/samples_table.h @@ -0,0 +1,717 @@ +#pragma once +#include "align_asset_macro.h" + +#define dgSample00 "__OTR__sound/samples/sfx_1/00_twirl" +static const ALIGN_ASSET(2) char gSample00[] = dgSample00; + +#define dgSample01 "__OTR__sound/samples/sfx_1/01_brushing" +static const ALIGN_ASSET(2) char gSample01[] = dgSample01; + +#define dgSample02 "__OTR__sound/samples/sfx_1/02_hand_touch" +static const ALIGN_ASSET(2) char gSample02[] = dgSample02; + +#define dgSample03 "__OTR__sound/samples/sfx_1/03_yoshi" +static const ALIGN_ASSET(2) char gSample03[] = dgSample03; + +#define dgSample04 "__OTR__sound/samples/sfx_1/04_plop" +static const ALIGN_ASSET(2) char gSample04[] = dgSample04; + +#define dgSample05 "__OTR__sound/samples/sfx_1/05_heavy_landing" +static const ALIGN_ASSET(2) char gSample05[] = dgSample05; + +#define dgSample06 "__OTR__sound/samples/sfx_terrain/00_step_default" +static const ALIGN_ASSET(2) char gSample06[] = dgSample06; + +#define dgSample07 "__OTR__sound/samples/sfx_terrain/01_step_grass" +static const ALIGN_ASSET(2) char gSample07[] = dgSample07; + +#define dgSample08 "__OTR__sound/samples/sfx_terrain/02_step_stone" +static const ALIGN_ASSET(2) char gSample08[] = dgSample08; + +#define dgSample09 "__OTR__sound/samples/sfx_terrain/03_step_spooky" +static const ALIGN_ASSET(2) char gSample09[] = dgSample09; + +#define dgSample0A "__OTR__sound/samples/sfx_terrain/04_step_snow" +static const ALIGN_ASSET(2) char gSample0A[] = dgSample0A; + +#define dgSample0B "__OTR__sound/samples/sfx_terrain/05_step_ice" +static const ALIGN_ASSET(2) char gSample0B[] = dgSample0B; + +#define dgSample0C "__OTR__sound/samples/sfx_terrain/06_step_metal" +static const ALIGN_ASSET(2) char gSample0C[] = dgSample0C; + +#define dgSample0D "__OTR__sound/samples/sfx_terrain/07_step_sand" +static const ALIGN_ASSET(2) char gSample0D[] = dgSample0D; + +#define dgSample0E "__OTR__sound/samples/sfx_water/00_plunge" +static const ALIGN_ASSET(2) char gSample0E[] = dgSample0E; + +#define dgSample0F "__OTR__sound/samples/sfx_water/01_splash" +static const ALIGN_ASSET(2) char gSample0F[] = dgSample0F; + +#define dgSample10 "__OTR__sound/samples/sfx_water/02_swim" +static const ALIGN_ASSET(2) char gSample10[] = dgSample10; + +#define dgSample11 "__OTR__sound/samples/sfx_4/00" +static const ALIGN_ASSET(2) char gSample11[] = dgSample11; + +#define dgSample12 "__OTR__sound/samples/sfx_4/01" +static const ALIGN_ASSET(2) char gSample12[] = dgSample12; + +#define dgSample13 "__OTR__sound/samples/sfx_4/02" +static const ALIGN_ASSET(2) char gSample13[] = dgSample13; + +#define dgSample14 "__OTR__sound/samples/sfx_4/03" +static const ALIGN_ASSET(2) char gSample14[] = dgSample14; + +#define dgSample15 "__OTR__sound/samples/sfx_4/04" +static const ALIGN_ASSET(2) char gSample15[] = dgSample15; + +#define dgSample16 "__OTR__sound/samples/sfx_4/05" +static const ALIGN_ASSET(2) char gSample16[] = dgSample16; + +#define dgSample17 "__OTR__sound/samples/sfx_4/06" +static const ALIGN_ASSET(2) char gSample17[] = dgSample17; + +#define dgSample18 "__OTR__sound/samples/sfx_4/07" +static const ALIGN_ASSET(2) char gSample18[] = dgSample18; + +#define dgSample19 "__OTR__sound/samples/sfx_4/08" +static const ALIGN_ASSET(2) char gSample19[] = dgSample19; + +#define dgSample1A "__OTR__sound/samples/sfx_4/09" +static const ALIGN_ASSET(2) char gSample1A[] = dgSample1A; + +#define dgSample1B "__OTR__sound/samples/sfx_5/00" +static const ALIGN_ASSET(2) char gSample1B[] = dgSample1B; + +#define dgSample1C "__OTR__sound/samples/sfx_5/01" +static const ALIGN_ASSET(2) char gSample1C[] = dgSample1C; + +#define dgSample1D "__OTR__sound/samples/sfx_5/02" +static const ALIGN_ASSET(2) char gSample1D[] = dgSample1D; + +#define dgSample1E "__OTR__sound/samples/sfx_5/03" +static const ALIGN_ASSET(2) char gSample1E[] = dgSample1E; + +#define dgSample1F "__OTR__sound/samples/sfx_5/04" +static const ALIGN_ASSET(2) char gSample1F[] = dgSample1F; + +#define dgSample20 "__OTR__sound/samples/sfx_5/05" +static const ALIGN_ASSET(2) char gSample20[] = dgSample20; + +#define dgSample21 "__OTR__sound/samples/sfx_5/06" +static const ALIGN_ASSET(2) char gSample21[] = dgSample21; + +#define dgSample22 "__OTR__sound/samples/sfx_5/07" +static const ALIGN_ASSET(2) char gSample22[] = dgSample22; + +#define dgSample23 "__OTR__sound/samples/sfx_5/08" +static const ALIGN_ASSET(2) char gSample23[] = dgSample23; + +#define dgSample24 "__OTR__sound/samples/sfx_5/09" +static const ALIGN_ASSET(2) char gSample24[] = dgSample24; + +#define dgSample25 "__OTR__sound/samples/sfx_5/0A" +static const ALIGN_ASSET(2) char gSample25[] = dgSample25; + +#define dgSample26 "__OTR__sound/samples/sfx_5/0B" +static const ALIGN_ASSET(2) char gSample26[] = dgSample26; + +#define dgSample27 "__OTR__sound/samples/sfx_5/0C" +static const ALIGN_ASSET(2) char gSample27[] = dgSample27; + +#define dgSample28 "__OTR__sound/samples/sfx_5/0D" +static const ALIGN_ASSET(2) char gSample28[] = dgSample28; + +#define dgSample29 "__OTR__sound/samples/sfx_5/0E" +static const ALIGN_ASSET(2) char gSample29[] = dgSample29; + +#define dgSample2A "__OTR__sound/samples/sfx_5/0F" +static const ALIGN_ASSET(2) char gSample2A[] = dgSample2A; + +#define dgSample2B "__OTR__sound/samples/sfx_5/10" +static const ALIGN_ASSET(2) char gSample2B[] = dgSample2B; + +#define dgSample2C "__OTR__sound/samples/sfx_5/11" +static const ALIGN_ASSET(2) char gSample2C[] = dgSample2C; + +#define dgSample2D "__OTR__sound/samples/sfx_5/12" +static const ALIGN_ASSET(2) char gSample2D[] = dgSample2D; + +#define dgSample2E "__OTR__sound/samples/sfx_5/13" +static const ALIGN_ASSET(2) char gSample2E[] = dgSample2E; + +#define dgSample2F "__OTR__sound/samples/sfx_5/14" +static const ALIGN_ASSET(2) char gSample2F[] = dgSample2F; + +#define dgSample30 "__OTR__sound/samples/sfx_5/15" +static const ALIGN_ASSET(2) char gSample30[] = dgSample30; + +#define dgSample31 "__OTR__sound/samples/sfx_5/16" +static const ALIGN_ASSET(2) char gSample31[] = dgSample31; + +#define dgSample32 "__OTR__sound/samples/sfx_5/17" +static const ALIGN_ASSET(2) char gSample32[] = dgSample32; + +#define dgSample33 "__OTR__sound/samples/sfx_5/18" +static const ALIGN_ASSET(2) char gSample33[] = dgSample33; + +#define dgSample34 "__OTR__sound/samples/sfx_5/19" +static const ALIGN_ASSET(2) char gSample34[] = dgSample34; + +#define dgSample35 "__OTR__sound/samples/sfx_5/1A" +static const ALIGN_ASSET(2) char gSample35[] = dgSample35; + +#define dgSample36 "__OTR__sound/samples/sfx_5/1B" +static const ALIGN_ASSET(2) char gSample36[] = dgSample36; + +#define dgSample37 "__OTR__sound/samples/sfx_5/1C" +static const ALIGN_ASSET(2) char gSample37[] = dgSample37; + +#define dgSample38 "__OTR__sound/samples/sfx_6/00" +static const ALIGN_ASSET(2) char gSample38[] = dgSample38; + +#define dgSample39 "__OTR__sound/samples/sfx_6/01" +static const ALIGN_ASSET(2) char gSample39[] = dgSample39; + +#define dgSample3A "__OTR__sound/samples/sfx_6/02" +static const ALIGN_ASSET(2) char gSample3A[] = dgSample3A; + +#define dgSample3B "__OTR__sound/samples/sfx_6/03" +static const ALIGN_ASSET(2) char gSample3B[] = dgSample3B; + +#define dgSample3C "__OTR__sound/samples/sfx_6/04" +static const ALIGN_ASSET(2) char gSample3C[] = dgSample3C; + +#define dgSample3D "__OTR__sound/samples/sfx_6/05" +static const ALIGN_ASSET(2) char gSample3D[] = dgSample3D; + +#define dgSample3E "__OTR__sound/samples/sfx_6/06" +static const ALIGN_ASSET(2) char gSample3E[] = dgSample3E; + +#define dgSample3F "__OTR__sound/samples/sfx_6/07" +static const ALIGN_ASSET(2) char gSample3F[] = dgSample3F; + +#define dgSample40 "__OTR__sound/samples/sfx_6/08" +static const ALIGN_ASSET(2) char gSample40[] = dgSample40; + +#define dgSample41 "__OTR__sound/samples/sfx_6/09" +static const ALIGN_ASSET(2) char gSample41[] = dgSample41; + +#define dgSample42 "__OTR__sound/samples/sfx_6/0A" +static const ALIGN_ASSET(2) char gSample42[] = dgSample42; + +#define dgSample43 "__OTR__sound/samples/sfx_6/0B" +static const ALIGN_ASSET(2) char gSample43[] = dgSample43; + +#define dgSample44 "__OTR__sound/samples/sfx_6/0C" +static const ALIGN_ASSET(2) char gSample44[] = dgSample44; + +#define dgSample45 "__OTR__sound/samples/sfx_6/0D" +static const ALIGN_ASSET(2) char gSample45[] = dgSample45; + +#define dgSample46 "__OTR__sound/samples/sfx_7/00" +static const ALIGN_ASSET(2) char gSample46[] = dgSample46; + +#define dgSample47 "__OTR__sound/samples/sfx_7/01" +static const ALIGN_ASSET(2) char gSample47[] = dgSample47; + +#define dgSample48 "__OTR__sound/samples/sfx_7/02" +static const ALIGN_ASSET(2) char gSample48[] = dgSample48; + +#define dgSample49 "__OTR__sound/samples/sfx_7/03" +static const ALIGN_ASSET(2) char gSample49[] = dgSample49; + +#define dgSample4A "__OTR__sound/samples/sfx_7/04" +static const ALIGN_ASSET(2) char gSample4A[] = dgSample4A; + +#define dgSample4B "__OTR__sound/samples/sfx_7/05" +static const ALIGN_ASSET(2) char gSample4B[] = dgSample4B; + +#define dgSample4C "__OTR__sound/samples/sfx_7/06" +static const ALIGN_ASSET(2) char gSample4C[] = dgSample4C; + +#define dgSample4D "__OTR__sound/samples/sfx_7/07" +static const ALIGN_ASSET(2) char gSample4D[] = dgSample4D; + +#define dgSample4E "__OTR__sound/samples/sfx_7/08" +static const ALIGN_ASSET(2) char gSample4E[] = dgSample4E; + +#define dgSample4F "__OTR__sound/samples/sfx_7/09" +static const ALIGN_ASSET(2) char gSample4F[] = dgSample4F; + +#define dgSample50 "__OTR__sound/samples/sfx_7/0A" +static const ALIGN_ASSET(2) char gSample50[] = dgSample50; + +#define dgSample51 "__OTR__sound/samples/sfx_7/0B" +static const ALIGN_ASSET(2) char gSample51[] = dgSample51; + +#define dgSample52 "__OTR__sound/samples/sfx_7/0C" +static const ALIGN_ASSET(2) char gSample52[] = dgSample52; + +#define dgSample53 "__OTR__sound/samples/sfx_7/0D_chain_chomp_bark" +static const ALIGN_ASSET(2) char gSample53[] = dgSample53; + +#define dgSample54 "__OTR__sound/samples/sfx_mario/00_mario_jump_hoo" +static const ALIGN_ASSET(2) char gSample54[] = dgSample54; + +#define dgSample55 "__OTR__sound/samples/sfx_mario/01_mario_jump_wah" +static const ALIGN_ASSET(2) char gSample55[] = dgSample55; + +#define dgSample56 "__OTR__sound/samples/sfx_mario/02_mario_yah" +static const ALIGN_ASSET(2) char gSample56[] = dgSample56; + +#define dgSample57 "__OTR__sound/samples/sfx_mario/03_mario_haha" +static const ALIGN_ASSET(2) char gSample57[] = dgSample57; + +#define dgSample58 "__OTR__sound/samples/sfx_mario/04_mario_yahoo" +static const ALIGN_ASSET(2) char gSample58[] = dgSample58; + +#define dgSample59 "__OTR__sound/samples/sfx_mario/05_mario_uh" +static const ALIGN_ASSET(2) char gSample59[] = dgSample59; + +#define dgSample5A "__OTR__sound/samples/sfx_mario/06_mario_hrmm" +static const ALIGN_ASSET(2) char gSample5A[] = dgSample5A; + +#define dgSample5B "__OTR__sound/samples/sfx_mario/07_mario_wah2" +static const ALIGN_ASSET(2) char gSample5B[] = dgSample5B; + +#define dgSample5C "__OTR__sound/samples/sfx_mario/08_mario_whoa" +static const ALIGN_ASSET(2) char gSample5C[] = dgSample5C; + +#define dgSample5D "__OTR__sound/samples/sfx_mario/09_mario_eeuh" +static const ALIGN_ASSET(2) char gSample5D[] = dgSample5D; + +#define dgSample5E "__OTR__sound/samples/sfx_mario/0A_mario_attacked" +static const ALIGN_ASSET(2) char gSample5E[] = dgSample5E; + +#define dgSample5F "__OTR__sound/samples/sfx_mario/0B_mario_ooof" +static const ALIGN_ASSET(2) char gSample5F[] = dgSample5F; + +#define dgSample60 "__OTR__sound/samples/sfx_mario/0C_mario_here_we_go" +static const ALIGN_ASSET(2) char gSample60[] = dgSample60; + +#define dgSample61 "__OTR__sound/samples/sfx_mario/0D_mario_yawning" +static const ALIGN_ASSET(2) char gSample61[] = dgSample61; + +#define dgSample62 "__OTR__sound/samples/sfx_mario/0E_mario_snoring1" +static const ALIGN_ASSET(2) char gSample62[] = dgSample62; + +#define dgSample63 "__OTR__sound/samples/sfx_mario/0F_mario_snoring2" +static const ALIGN_ASSET(2) char gSample63[] = dgSample63; + +#define dgSample64 "__OTR__sound/samples/sfx_mario/10_mario_doh" +static const ALIGN_ASSET(2) char gSample64[] = dgSample64; + +#define dgSample65 "__OTR__sound/samples/sfx_mario/11_mario_game_over" +static const ALIGN_ASSET(2) char gSample65[] = dgSample65; + +#define dgSample66 "__OTR__sound/samples/sfx_mario/12_mario_hello" +static const ALIGN_ASSET(2) char gSample66[] = dgSample66; + +#define dgSample67 "__OTR__sound/samples/sfx_mario/13_mario_press_start_to_play" +static const ALIGN_ASSET(2) char gSample67[] = dgSample67; + +#define dgSample68 "__OTR__sound/samples/sfx_mario/14_mario_twirl_bounce" +static const ALIGN_ASSET(2) char gSample68[] = dgSample68; + +#define dgSample69 "__OTR__sound/samples/sfx_mario/15_mario_snoring3" +static const ALIGN_ASSET(2) char gSample69[] = dgSample69; + +#define dgSample6A "__OTR__sound/samples/sfx_mario/16_mario_so_longa_bowser" +static const ALIGN_ASSET(2) char gSample6A[] = dgSample6A; + +#define dgSample6B "__OTR__sound/samples/sfx_mario/17_mario_ima_tired" +static const ALIGN_ASSET(2) char gSample6B[] = dgSample6B; + +#define dgSample6C "__OTR__sound/samples/sfx_mario/18_mario_waha" +static const ALIGN_ASSET(2) char gSample6C[] = dgSample6C; + +#define dgSample6D "__OTR__sound/samples/sfx_mario/19_mario_yippee" +static const ALIGN_ASSET(2) char gSample6D[] = dgSample6D; + +#define dgSample6E "__OTR__sound/samples/sfx_mario/1A_mario_lets_a_go" +static const ALIGN_ASSET(2) char gSample6E[] = dgSample6E; + +#define dgSample6F "__OTR__sound/samples/sfx_9/00" +static const ALIGN_ASSET(2) char gSample6F[] = dgSample6F; + +#define dgSample70 "__OTR__sound/samples/sfx_9/01" +static const ALIGN_ASSET(2) char gSample70[] = dgSample70; + +#define dgSample71 "__OTR__sound/samples/sfx_9/02" +static const ALIGN_ASSET(2) char gSample71[] = dgSample71; + +#define dgSample72 "__OTR__sound/samples/sfx_9/03" +static const ALIGN_ASSET(2) char gSample72[] = dgSample72; + +#define dgSample73 "__OTR__sound/samples/sfx_9/04_camera_buzz" +static const ALIGN_ASSET(2) char gSample73[] = dgSample73; + +#define dgSample74 "__OTR__sound/samples/sfx_9/05_camera_shutter" +static const ALIGN_ASSET(2) char gSample74[] = dgSample74; + +#define dgSample75 "__OTR__sound/samples/sfx_9/06" +static const ALIGN_ASSET(2) char gSample75[] = dgSample75; + +#define dgSample76 "__OTR__sound/samples/sfx_mario_peach/00_mario_waaaooow" +static const ALIGN_ASSET(2) char gSample76[] = dgSample76; + +#define dgSample77 "__OTR__sound/samples/sfx_mario_peach/01_mario_hoohoo" +static const ALIGN_ASSET(2) char gSample77[] = dgSample77; + +#define dgSample78 "__OTR__sound/samples/sfx_mario_peach/02_mario_panting" +static const ALIGN_ASSET(2) char gSample78[] = dgSample78; + +#define dgSample79 "__OTR__sound/samples/sfx_mario_peach/03_mario_dying" +static const ALIGN_ASSET(2) char gSample79[] = dgSample79; + +#define dgSample7A "__OTR__sound/samples/sfx_mario_peach/04_mario_on_fire" +static const ALIGN_ASSET(2) char gSample7A[] = dgSample7A; + +#define dgSample7B "__OTR__sound/samples/sfx_mario_peach/05_mario_uh2" +static const ALIGN_ASSET(2) char gSample7B[] = dgSample7B; + +#define dgSample7C "__OTR__sound/samples/sfx_mario_peach/06_mario_coughing" +static const ALIGN_ASSET(2) char gSample7C[] = dgSample7C; + +#define dgSample7D "__OTR__sound/samples/sfx_mario_peach/07_mario_its_a_me_mario" +static const ALIGN_ASSET(2) char gSample7D[] = dgSample7D; + +#define dgSample7E "__OTR__sound/samples/sfx_mario_peach/08_mario_punch_yah" +static const ALIGN_ASSET(2) char gSample7E[] = dgSample7E; + +#define dgSample7F "__OTR__sound/samples/sfx_mario_peach/09_mario_punch_hoo" +static const ALIGN_ASSET(2) char gSample7F[] = dgSample7F; + +#define dgSample80 "__OTR__sound/samples/sfx_mario_peach/0A_mario_mama_mia" +static const ALIGN_ASSET(2) char gSample80[] = dgSample80; + +#define dgSample81 "__OTR__sound/samples/sfx_mario_peach/0B_mario_okey_dokey" +static const ALIGN_ASSET(2) char gSample81[] = dgSample81; + +#define dgSample82 "__OTR__sound/samples/sfx_mario_peach/0C_mario_drowning" +static const ALIGN_ASSET(2) char gSample82[] = dgSample82; + +#define dgSample83 "__OTR__sound/samples/sfx_mario_peach/0D_mario_thank_you_playing_my_game" +static const ALIGN_ASSET(2) char gSample83[] = dgSample83; + +#define dgSample84 "__OTR__sound/samples/sfx_mario_peach/0E_peach_dear_mario" +static const ALIGN_ASSET(2) char gSample84[] = dgSample84; + +#define dgSample85 "__OTR__sound/samples/sfx_mario_peach/0F_peach_mario" +static const ALIGN_ASSET(2) char gSample85[] = dgSample85; + +#define dgSample86 "__OTR__sound/samples/sfx_mario_peach/10_peach_power_of_the_stars" +static const ALIGN_ASSET(2) char gSample86[] = dgSample86; + +#define dgSample87 "__OTR__sound/samples/sfx_mario_peach/11_peach_thanks_to_you" +static const ALIGN_ASSET(2) char gSample87[] = dgSample87; + +#define dgSample88 "__OTR__sound/samples/sfx_mario_peach/12_peach_thank_you_mario" +static const ALIGN_ASSET(2) char gSample88[] = dgSample88; + +#define dgSample89 "__OTR__sound/samples/sfx_mario_peach/13_peach_something_special" +static const ALIGN_ASSET(2) char gSample89[] = dgSample89; + +#define dgSample8A "__OTR__sound/samples/sfx_mario_peach/14_peach_bake_a_cake" +static const ALIGN_ASSET(2) char gSample8A[] = dgSample8A; + +#define dgSample8B "__OTR__sound/samples/sfx_mario_peach/15_peach_for_mario" +static const ALIGN_ASSET(2) char gSample8B[] = dgSample8B; + +#define dgSample8C "__OTR__sound/samples/sfx_mario_peach/16_peach_mario2" +static const ALIGN_ASSET(2) char gSample8C[] = dgSample8C; + +#define dgSample8D "__OTR__sound/samples/instruments/00" +static const ALIGN_ASSET(2) char gSample8D[] = dgSample8D; + +#define dgSample8E "__OTR__sound/samples/instruments/01_banjo_1" +static const ALIGN_ASSET(2) char gSample8E[] = dgSample8E; + +#define dgSample8F "__OTR__sound/samples/instruments/02" +static const ALIGN_ASSET(2) char gSample8F[] = dgSample8F; + +#define dgSample90 "__OTR__sound/samples/instruments/03_human_whistle" +static const ALIGN_ASSET(2) char gSample90[] = dgSample90; + +#define dgSample91 "__OTR__sound/samples/instruments/04_bright_piano" +static const ALIGN_ASSET(2) char gSample91[] = dgSample91; + +#define dgSample92 "__OTR__sound/samples/instruments/05_acoustic_bass" +static const ALIGN_ASSET(2) char gSample92[] = dgSample92; + +#define dgSample93 "__OTR__sound/samples/instruments/06_kick_drum_1" +static const ALIGN_ASSET(2) char gSample93[] = dgSample93; + +#define dgSample94 "__OTR__sound/samples/instruments/07_rimshot" +static const ALIGN_ASSET(2) char gSample94[] = dgSample94; + +#define dgSample95 "__OTR__sound/samples/instruments/08" +static const ALIGN_ASSET(2) char gSample95[] = dgSample95; + +#define dgSample96 "__OTR__sound/samples/instruments/09" +static const ALIGN_ASSET(2) char gSample96[] = dgSample96; + +#define dgSample97 "__OTR__sound/samples/instruments/0A_tambourine" +static const ALIGN_ASSET(2) char gSample97[] = dgSample97; + +#define dgSample98 "__OTR__sound/samples/instruments/0B" +static const ALIGN_ASSET(2) char gSample98[] = dgSample98; + +#define dgSample99 "__OTR__sound/samples/instruments/0C_conga_stick" +static const ALIGN_ASSET(2) char gSample99[] = dgSample99; + +#define dgSample9A "__OTR__sound/samples/instruments/0D_clave" +static const ALIGN_ASSET(2) char gSample9A[] = dgSample9A; + +#define dgSample9B "__OTR__sound/samples/instruments/0E_hihat_closed" +static const ALIGN_ASSET(2) char gSample9B[] = dgSample9B; + +#define dgSample9C "__OTR__sound/samples/instruments/0F_hihat_open" +static const ALIGN_ASSET(2) char gSample9C[] = dgSample9C; + +#define dgSample9D "__OTR__sound/samples/instruments/10_cymbal_bell" +static const ALIGN_ASSET(2) char gSample9D[] = dgSample9D; + +#define dgSample9E "__OTR__sound/samples/instruments/11_splash_cymbal" +static const ALIGN_ASSET(2) char gSample9E[] = dgSample9E; + +#define dgSample9F "__OTR__sound/samples/instruments/12_snare_drum_1" +static const ALIGN_ASSET(2) char gSample9F[] = dgSample9F; + +#define dgSampleA0 "__OTR__sound/samples/instruments/13_snare_drum_2" +static const ALIGN_ASSET(2) char gSampleA0[] = dgSampleA0; + +#define dgSampleA1 "__OTR__sound/samples/instruments/14_strings_5" +static const ALIGN_ASSET(2) char gSampleA1[] = dgSampleA1; + +#define dgSampleA2 "__OTR__sound/samples/instruments/15_strings_4" +static const ALIGN_ASSET(2) char gSampleA2[] = dgSampleA2; + +#define dgSampleA3 "__OTR__sound/samples/instruments/16_french_horns" +static const ALIGN_ASSET(2) char gSampleA3[] = dgSampleA3; + +#define dgSampleA4 "__OTR__sound/samples/instruments/17_trumpet" +static const ALIGN_ASSET(2) char gSampleA4[] = dgSampleA4; + +#define dgSampleA5 "__OTR__sound/samples/instruments/18_timpani" +static const ALIGN_ASSET(2) char gSampleA5[] = dgSampleA5; + +#define dgSampleA6 "__OTR__sound/samples/instruments/19_brass" +static const ALIGN_ASSET(2) char gSampleA6[] = dgSampleA6; + +#define dgSampleA7 "__OTR__sound/samples/instruments/1A_slap_bass" +static const ALIGN_ASSET(2) char gSampleA7[] = dgSampleA7; + +#define dgSampleA8 "__OTR__sound/samples/instruments/1B_organ_2" +static const ALIGN_ASSET(2) char gSampleA8[] = dgSampleA8; + +#define dgSampleA9 "__OTR__sound/samples/instruments/1C" +static const ALIGN_ASSET(2) char gSampleA9[] = dgSampleA9; + +#define dgSampleAA "__OTR__sound/samples/instruments/1D" +static const ALIGN_ASSET(2) char gSampleAA[] = dgSampleAA; + +#define dgSampleAB "__OTR__sound/samples/instruments/1E_closed_triangle" +static const ALIGN_ASSET(2) char gSampleAB[] = dgSampleAB; + +#define dgSampleAC "__OTR__sound/samples/instruments/1F_open_triangle" +static const ALIGN_ASSET(2) char gSampleAC[] = dgSampleAC; + +#define dgSampleAD "__OTR__sound/samples/instruments/20_cabasa" +static const ALIGN_ASSET(2) char gSampleAD[] = dgSampleAD; + +#define dgSampleAE "__OTR__sound/samples/instruments/21_sine_bass" +static const ALIGN_ASSET(2) char gSampleAE[] = dgSampleAE; + +#define dgSampleAF "__OTR__sound/samples/instruments/22_boys_choir" +static const ALIGN_ASSET(2) char gSampleAF[] = dgSampleAF; + +#define dgSampleB0 "__OTR__sound/samples/instruments/23_strings_1" +static const ALIGN_ASSET(2) char gSampleB0[] = dgSampleB0; + +#define dgSampleB1 "__OTR__sound/samples/instruments/24_strings_2" +static const ALIGN_ASSET(2) char gSampleB1[] = dgSampleB1; + +#define dgSampleB2 "__OTR__sound/samples/instruments/25_strings_3" +static const ALIGN_ASSET(2) char gSampleB2[] = dgSampleB2; + +#define dgSampleB3 "__OTR__sound/samples/instruments/26_crystal_rhodes" +static const ALIGN_ASSET(2) char gSampleB3[] = dgSampleB3; + +#define dgSampleB4 "__OTR__sound/samples/instruments/27_harpsichord" +static const ALIGN_ASSET(2) char gSampleB4[] = dgSampleB4; + +#define dgSampleB5 "__OTR__sound/samples/instruments/28_sitar_1" +static const ALIGN_ASSET(2) char gSampleB5[] = dgSampleB5; + +#define dgSampleB6 "__OTR__sound/samples/instruments/29_orchestra_hit" +static const ALIGN_ASSET(2) char gSampleB6[] = dgSampleB6; + +#define dgSampleB7 "__OTR__sound/samples/instruments/2A" +static const ALIGN_ASSET(2) char gSampleB7[] = dgSampleB7; + +#define dgSampleB8 "__OTR__sound/samples/instruments/2B" +static const ALIGN_ASSET(2) char gSampleB8[] = dgSampleB8; + +#define dgSampleB9 "__OTR__sound/samples/instruments/2C" +static const ALIGN_ASSET(2) char gSampleB9[] = dgSampleB9; + +#define dgSampleBA "__OTR__sound/samples/instruments/2D_trombone" +static const ALIGN_ASSET(2) char gSampleBA[] = dgSampleBA; + +#define dgSampleBB "__OTR__sound/samples/instruments/2E_accordion" +static const ALIGN_ASSET(2) char gSampleBB[] = dgSampleBB; + +#define dgSampleBC "__OTR__sound/samples/instruments/2F_sleigh_bells" +static const ALIGN_ASSET(2) char gSampleBC[] = dgSampleBC; + +#define dgSampleBD "__OTR__sound/samples/instruments/30_rarefaction-lahna" +static const ALIGN_ASSET(2) char gSampleBD[] = dgSampleBD; + +#define dgSampleBE "__OTR__sound/samples/instruments/31_rarefaction-convolution" +static const ALIGN_ASSET(2) char gSampleBE[] = dgSampleBE; + +#define dgSampleBF "__OTR__sound/samples/instruments/32_metal_rimshot" +static const ALIGN_ASSET(2) char gSampleBF[] = dgSampleBF; + +#define dgSampleC0 "__OTR__sound/samples/instruments/33_kick_drum_2" +static const ALIGN_ASSET(2) char gSampleC0[] = dgSampleC0; + +#define dgSampleC1 "__OTR__sound/samples/instruments/34_alto_flute" +static const ALIGN_ASSET(2) char gSampleC1[] = dgSampleC1; + +#define dgSampleC2 "__OTR__sound/samples/instruments/35_gospel_organ" +static const ALIGN_ASSET(2) char gSampleC2[] = dgSampleC2; + +#define dgSampleC3 "__OTR__sound/samples/instruments/36_sawtooth_synth" +static const ALIGN_ASSET(2) char gSampleC3[] = dgSampleC3; + +#define dgSampleC4 "__OTR__sound/samples/instruments/37_square_synth" +static const ALIGN_ASSET(2) char gSampleC4[] = dgSampleC4; + +#define dgSampleC5 "__OTR__sound/samples/instruments/38_electric_kick_drum" +static const ALIGN_ASSET(2) char gSampleC5[] = dgSampleC5; + +#define dgSampleC6 "__OTR__sound/samples/instruments/39_sitar_2" +static const ALIGN_ASSET(2) char gSampleC6[] = dgSampleC6; + +#define dgSampleC7 "__OTR__sound/samples/instruments/3A_music_box" +static const ALIGN_ASSET(2) char gSampleC7[] = dgSampleC7; + +#define dgSampleC8 "__OTR__sound/samples/instruments/3B_banjo_2" +static const ALIGN_ASSET(2) char gSampleC8[] = dgSampleC8; + +#define dgSampleC9 "__OTR__sound/samples/instruments/3C_acoustic_guitar" +static const ALIGN_ASSET(2) char gSampleC9[] = dgSampleC9; + +#define dgSampleCA "__OTR__sound/samples/instruments/3D" +static const ALIGN_ASSET(2) char gSampleCA[] = dgSampleCA; + +#define dgSampleCB "__OTR__sound/samples/instruments/3E_monk_choir" +static const ALIGN_ASSET(2) char gSampleCB[] = dgSampleCB; + +#define dgSampleCC "__OTR__sound/samples/instruments/3F" +static const ALIGN_ASSET(2) char gSampleCC[] = dgSampleCC; + +#define dgSampleCD "__OTR__sound/samples/instruments/40_bell" +static const ALIGN_ASSET(2) char gSampleCD[] = dgSampleCD; + +#define dgSampleCE "__OTR__sound/samples/instruments/41_pan_flute" +static const ALIGN_ASSET(2) char gSampleCE[] = dgSampleCE; + +#define dgSampleCF "__OTR__sound/samples/instruments/42_vibraphone" +static const ALIGN_ASSET(2) char gSampleCF[] = dgSampleCF; + +#define dgSampleD0 "__OTR__sound/samples/instruments/43_harmonica" +static const ALIGN_ASSET(2) char gSampleD0[] = dgSampleD0; + +#define dgSampleD1 "__OTR__sound/samples/instruments/44_grand_piano" +static const ALIGN_ASSET(2) char gSampleD1[] = dgSampleD1; + +#define dgSampleD2 "__OTR__sound/samples/instruments/45_french_horns_lq" +static const ALIGN_ASSET(2) char gSampleD2[] = dgSampleD2; + +#define dgSampleD3 "__OTR__sound/samples/instruments/46_pizzicato_strings_1" +static const ALIGN_ASSET(2) char gSampleD3[] = dgSampleD3; + +#define dgSampleD4 "__OTR__sound/samples/instruments/47_pizzicato_strings_2" +static const ALIGN_ASSET(2) char gSampleD4[] = dgSampleD4; + +#define dgSampleD5 "__OTR__sound/samples/instruments/48_steel_drum" +static const ALIGN_ASSET(2) char gSampleD5[] = dgSampleD5; + +#define dgSampleD6 "__OTR__sound/samples/piranha_music_box/00_music_box" +static const ALIGN_ASSET(2) char gSampleD6[] = dgSampleD6; + +#define dgSampleD7 "__OTR__sound/samples/course_start/00_la" +static const ALIGN_ASSET(2) char gSampleD7[] = dgSampleD7; + +#define dgSampleD8 "__OTR__sound/samples/bowser_organ/00_organ_1" +static const ALIGN_ASSET(2) char gSampleD8[] = dgSampleD8; + +#define dgSampleD9 "__OTR__sound/samples/bowser_organ/01_organ_1_lq" +static const ALIGN_ASSET(2) char gSampleD9[] = dgSampleD9; + +#define dgSampleDA "__OTR__sound/samples/bowser_organ/02_boys_choir" +static const ALIGN_ASSET(2) char gSampleDA[] = dgSampleDA; + +static const char* gSampleTable[] = { + gSample00, gSample01, gSample02, gSample03, + gSample04, gSample05, gSample06, gSample07, + gSample08, gSample09, gSample0A, gSample0B, + gSample0C, gSample0D, gSample0E, gSample0F, + gSample10, gSample11, gSample12, gSample13, + gSample14, gSample15, gSample16, gSample17, + gSample18, gSample19, gSample1A, gSample1B, + gSample1C, gSample1D, gSample1E, gSample1F, + gSample20, gSample21, gSample22, gSample23, + gSample24, gSample25, gSample26, gSample27, + gSample28, gSample29, gSample2A, gSample2B, + gSample2C, gSample2D, gSample2E, gSample2F, + gSample30, gSample31, gSample32, gSample33, + gSample34, gSample35, gSample36, gSample37, + gSample38, gSample39, gSample3A, gSample3B, + gSample3C, gSample3D, gSample3E, gSample3F, + gSample40, gSample41, gSample42, gSample43, + gSample44, gSample45, gSample46, gSample47, + gSample48, gSample49, gSample4A, gSample4B, + gSample4C, gSample4D, gSample4E, gSample4F, + gSample50, gSample51, gSample52, gSample53, + gSample54, gSample55, gSample56, gSample57, + gSample58, gSample59, gSample5A, gSample5B, + gSample5C, gSample5D, gSample5E, gSample5F, + gSample60, gSample61, gSample62, gSample63, + gSample64, gSample65, gSample66, gSample67, + gSample68, gSample69, gSample6A, gSample6B, + gSample6C, gSample6D, gSample6E, gSample6F, + gSample70, gSample71, gSample72, gSample73, + gSample74, gSample75, gSample76, gSample77, + gSample78, gSample79, gSample7A, gSample7B, + gSample7C, gSample7D, gSample7E, gSample7F, + gSample80, gSample81, gSample82, gSample83, + gSample84, gSample85, gSample86, gSample87, + gSample88, gSample89, gSample8A, gSample8B, + gSample8C, gSample8D, gSample8E, gSample8F, + gSample90, gSample91, gSample92, gSample93, + gSample94, gSample95, gSample96, gSample97, + gSample98, gSample99, gSample9A, gSample9B, + gSample9C, gSample9D, gSample9E, gSample9F, + gSampleA0, gSampleA1, gSampleA2, gSampleA3, + gSampleA4, gSampleA5, gSampleA6, gSampleA7, + gSampleA8, gSampleA9, gSampleAA, gSampleAB, + gSampleAC, gSampleAD, gSampleAE, gSampleAF, + gSampleB0, gSampleB1, gSampleB2, gSampleB3, + gSampleB4, gSampleB5, gSampleB6, gSampleB7, + gSampleB8, gSampleB9, gSampleBA, gSampleBB, + gSampleBC, gSampleBD, gSampleBE, gSampleBF, + gSampleC0, gSampleC1, gSampleC2, gSampleC3, + gSampleC4, gSampleC5, gSampleC6, gSampleC7, + gSampleC8, gSampleC9, gSampleCA, gSampleCB, + gSampleCC, gSampleCD, gSampleCE, gSampleCF, + gSampleD0, gSampleD1, gSampleD2, gSampleD3, + gSampleD4, gSampleD5, gSampleD6, gSampleD7, + gSampleD8, gSampleD9, gSampleDA, +}; diff --git a/src/Companion.cpp b/src/Companion.cpp index 1d2f371..05a100c 100644 --- a/src/Companion.cpp +++ b/src/Companion.cpp @@ -21,6 +21,8 @@ static const std::unordered_map gFactories = { { ".png", new TextureFactory() }, { ".anim", new AnimFactory() }, { ".aiff", new AudioFactory() }, + { ".bnk", new AudioFactory() }, + { ".bset", new AudioFactory() }, { ".bin", new BlobFactory(LUS::ResourceType::Blob) }, { ".m64", new BlobFactory(LUS::ResourceType::Blob) }, { ".sbox", new BlobFactory(LUS::ResourceType::Blob, true) }, diff --git a/src/audio/AudioManager.cpp b/src/audio/AudioManager.cpp index 4549658..d72bda6 100644 --- a/src/audio/AudioManager.cpp +++ b/src/audio/AudioManager.cpp @@ -28,14 +28,14 @@ std::string gen_name(const std::string& prefix){ return prefix + std::to_string(name_table[prefix]++); } -AifcEntry* SampleBank::AddSample(uint32_t addr, size_t sampleSize, const Book& book, const Loop& loop){ +AudioBankSample* SampleBank::AddSample(uint32_t addr, size_t sampleSize, const AdpcmBook& book, const AdpcmLoop& loop){ assert(sampleSize % 2 == 0); if(sampleSize % 9 != 0){ assert(sampleSize % 9 == 1); sampleSize -= 1; } - AifcEntry* entry; + AudioBankSample* entry; if(this->entries.contains(addr)){ entry = this->entries[addr]; @@ -43,7 +43,7 @@ AifcEntry* SampleBank::AddSample(uint32_t addr, size_t sampleSize, const Book& b assert(entry->loop == loop); assert(entry->data.size() == sampleSize); } else { - entry = new AifcEntry{ gen_name("aifc"), PyUtils::slice(this->data, addr, addr + sampleSize), book, loop }; + entry = new AudioBankSample{ gen_name("aifc"), PyUtils::slice(this->data, addr, addr + sampleSize), book, loop }; this->entries[addr] = entry; } @@ -68,8 +68,8 @@ std::vector AudioManager::parse_seq_file(std::vector& buffer, ui reader.SetEndianness(LUS::Endianness::Big); reader.Seek(offset, LUS::SeekOffsetType::Start); - unsigned short magic = reader.ReadUInt16(); - unsigned short num_entries = reader.ReadUInt16(); + uint16_t magic = reader.ReadUInt16(); + uint16_t num_entries = reader.ReadUInt16(); uint32_t prev = ALIGN(4 + num_entries * 8, 16); assert(magic == (isCTL ? 1 : 2)); @@ -103,149 +103,6 @@ CTLHeader AudioManager::parse_ctl_header(std::vector& data){ return header; } -std::optional AudioManager::parse_sound(std::vector 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& 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& 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& bankData){ - LUS::BinaryReader reader((char*) bankData.data(), bankData.size()); - reader.SetEndianness(LUS::Endianness::Big); - reader.Seek(addr, LUS::SeekOffsetType::Start); - - std::optional> 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(); - 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& 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 table; - std::vector 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& data, std::vector& 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 AudioManager::parse_envelope(uint32_t addr, std::vector& dataBank){ - std::vector 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 data, SampleBank* bank, uint32_t index) { name_table.clear(); std::ostringstream ss; @@ -262,11 +119,16 @@ Bank AudioManager::parse_ctl(CTLHeader header, std::vector data, Sample std::vector drumOffsets; if(numDrums != 0){ + assert(drumBaseAddr != 0); for (size_t i = 0; i < numDrums; ++i) { uint32_t drumOffset; - memcpy(&drumOffset, rawData + drumBaseAddr + (i * 4), 4); - drumOffsets.push_back(BSWAP32(drumOffset)); + memcpy(&drumOffset, rawData + drumBaseAddr + i * 4, 4); + drumOffset = BSWAP32(drumOffset); + assert(drumOffset != 0); + drumOffsets.push_back(drumOffset); } + } else { + assert(drumBaseAddr == 0); } uint32_t instrumentBaseAddr = 4; @@ -275,22 +137,28 @@ Bank AudioManager::parse_ctl(CTLHeader header, std::vector data, Sample for (size_t i = 0; i < numInstruments; ++i) { uint32_t instOffset; - memcpy(&instOffset, rawData + (instrumentBaseAddr + (i * 4)), 4); + memcpy(&instOffset, rawData + (instrumentBaseAddr + i * 4), 4); instOffset = BSWAP32(instOffset); if(instOffset == 0){ instrumentList.push_back(NONE); + instrumentOffsets.push_back(NONE); } else { instrumentOffsets.push_back(instOffset); instrumentList.push_back(instOffset); } } - std::sort(instrumentOffsets.begin(), instrumentOffsets.end()); +// std::sort(instrumentOffsets.begin(), instrumentOffsets.end()); - std::vector insts; + std::vector insts; for(auto &offset : instrumentOffsets){ + if(offset == NONE){ + Instrument invalid = { .valid = false }; + insts.push_back(invalid); + continue; + } auto rInst = PyUtils::slice(data, offset, offset + 32); - Inst inst = parse_inst(rInst, offset); + Instrument inst = parse_inst(rInst, offset); insts.push_back(inst); } @@ -322,12 +190,12 @@ Bank AudioManager::parse_ctl(CTLHeader header, std::vector data, Sample // 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>> allInsts; + std::vector>> allInsts; bool needDrums = !drums.empty(); for(auto &inst : insts){ - std::vector> sounds = {inst.soundLo, inst.soundMed, inst.soundHi}; + std::vector> sounds = {inst.soundLo, inst.soundMed, inst.soundHi}; - if(needDrums && std::any_of(sounds.cbegin(), sounds.cend(), [&drums](std::optional sound){ return sound.has_value() && sound.value().offset > drums[0].sound.offset; })){ + if(needDrums && std::any_of(sounds.cbegin(), sounds.cend(), [&drums](std::optional sound){ return sound.has_value() && sound.value().offset > drums[0].sound.offset; })){ allInsts.emplace_back(drums); needDrums = false; } @@ -340,18 +208,18 @@ Bank AudioManager::parse_ctl(CTLHeader header, std::vector data, Sample allInsts.emplace_back(drums); } - std::unordered_map samples; + std::map 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); + AudioBankSample* sample = parse_sample(rSample, data, bank); for(auto &tuning : tunings){ sample->tunings.push_back(tuning.second); } samples[offset] = sample; } - std::unordered_map> envData; + std::unordered_map> envData; std::vector usedEnvOffsets; std::sort(envOffsets.begin(), envOffsets.end()); for(auto &offset : envOffsets){ @@ -383,7 +251,7 @@ Bank AudioManager::parse_ctl(CTLHeader header, std::vector data, Sample } } - std::unordered_map envelopes; + std::map envelopes; for(auto &entry : envData){ Envelope env = { gen_name("envelope"), entry.second }; envelopes[entry.first] = env; @@ -393,6 +261,156 @@ Bank AudioManager::parse_ctl(CTLHeader header, std::vector data, Sample return bankData; } +std::optional AudioManager::parse_sound(std::vector 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; + } + + AudioBankSound sound = { addr, tuning }; + + reader.Close(); + return sound; +} + +Drum AudioManager::parse_drum(std::vector& 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); + AudioBankSound 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; +} + +Instrument AudioManager::parse_inst(std::vector& 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); + } + + Instrument inst = { true, name, addr, releaseRate, normalRangeLo, normalRangeHi, envAddr, soundLo, soundMed, soundHi }; + return inst; +} + +AdpcmLoop AudioManager::parse_loop(uint32_t addr, std::vector& bankData){ + LUS::BinaryReader reader((char*) bankData.data(), bankData.size()); + reader.SetEndianness(LUS::Endianness::Big); + reader.Seek(addr, LUS::SeekOffsetType::Start); + + std::optional> state = std::nullopt; + uint32_t start = reader.ReadUInt32(); + uint32_t end = reader.ReadUInt32(); + int32_t count = reader.ReadInt32(); + uint32_t pad = reader.ReadUInt32(); + + std::cout << "Loop End: " << end << '\n'; + if(count != 0){ + state = std::vector(); + for (size_t i = 0; i < 16; ++i) { + state.value().push_back(reader.ReadInt16()); + } + } + AdpcmLoop loop = { start, end, count, pad, state }; + return loop; +} + +AdpcmBook AudioManager::parse_book(uint32_t addr, std::vector& bankData){ + LUS::BinaryReader reader((char*) bankData.data(), bankData.size()); + reader.SetEndianness(LUS::Endianness::Big); + reader.Seek(addr, LUS::SeekOffsetType::Start); + + int32_t order = reader.ReadInt32(); + int32_t npredictors = reader.ReadInt32(); + + assert(order == 2); + assert(npredictors == 2); + + std::vector table; + std::vector tableData = PyUtils::slice(bankData, addr + 8, addr + 8 + 16 * order * npredictors); + for (size_t i = 0; i < ( 16 * order * npredictors ); i += 2) { + int16_t dtable; + memcpy(&dtable, tableData.data() + i, 2); + table.push_back(BSWAP16(dtable)); + } + + AdpcmBook book = { order, npredictors, table }; + return book; +} + +AudioBankSample* AudioManager::parse_sample(std::vector& data, std::vector& 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); + + AdpcmLoop loopData = parse_loop(loop, bankData); + AdpcmBook bookData = parse_book(book, bankData); + + reader.Close(); + return sampleBank->AddSample(addr, sampleSize, bookData, loopData); +} + + +std::vector AudioManager::parse_envelope(uint32_t addr, std::vector& dataBank){ + std::vector entries; + LUS::BinaryReader reader((char*) dataBank.data(), dataBank.size()); + reader.SetEndianness(LUS::Endianness::Big); + + while(true){ + reader.Seek(addr, LUS::SeekOffsetType::Start); + int16_t delay = reader.ReadInt16(); + int16_t arg = reader.ReadInt16(); + AdsrEnvelope entry = { delay, arg }; + entries.push_back(entry); + addr += 4; + if (1 <= (-delay) % (1 << 16) && (-delay) % (1 << 16) <= 3){ + break; + } + } + + reader.Close(); + return entries; +} + TBLFile AudioManager::parse_tbl(std::vector& data, std::vector& entries) { TBLFile tbl; std::unordered_map cache; @@ -441,14 +459,22 @@ void AudioManager::initialize(std::vector& buffer) { auto headerRaw = PyUtils::slice(entry, 0, 16); auto header = parse_ctl_header(headerRaw); auto bank = parse_ctl(header, PyUtils::slice(entry, 16), sample_bank, index); - banks.push_back(bank); + banks[index] = bank; } + int32_t idx = -1; + for(auto &sample_bank : this->loaded_tbl.banks){ + auto offsets = PyUtils::keys(sample_bank->entries); + std::sort(offsets.begin(), offsets.end()); + for(auto &offset : offsets){ + this->sampleMap[sample_bank->entries[offset]] = ++idx; + } + } } void serialize_f80(double num, LUS::BinaryWriter &writer) { - // Convert the input double to a uint64_t + // Convert the input double to an uint64_t std::uint64_t f64; std::memcpy(&f64, &num, sizeof(double)); @@ -505,7 +531,7 @@ void serialize_f80(double num, LUS::BinaryWriter &writer) { } \ } \ -void AudioManager::write_aifc(AifcEntry* entry, LUS::BinaryWriter &out) { +void AudioManager::write_aifc(AudioBankSample* entry, LUS::BinaryWriter &out) { int16_t num_channels = 1; auto data = entry->data; size_t len = 0; @@ -549,7 +575,7 @@ void AudioManager::write_aifc(AifcEntry* entry, LUS::BinaryWriter &out) { tmp.Write((uint16_t) sample_size); serialize_f80(sample_rate, tmp); tmp.Write(AIFC::MagicValues::VAPC); - tmp.Write("\u000BVADPCM ~4-1", false); + tmp.Write("\x0bVADPCM ~4-1", false); END_SECTION(); @@ -557,7 +583,7 @@ void AudioManager::write_aifc(AifcEntry* entry, LUS::BinaryWriter &out) { tmp.Write(std::string(20, '\0'), false); END_SECTION(); - START_CUSTOM_SECTION("\u000BVADPCMCODES") + START_CUSTOM_SECTION("\x0bVADPCMCODES") tmp.Write((uint16_t) 1); tmp.Write((uint16_t) entry->book.order); tmp.Write((uint16_t) entry->book.npredictors); @@ -575,7 +601,7 @@ void AudioManager::write_aifc(AifcEntry* entry, LUS::BinaryWriter &out) { END_SECTION(); if(entry->loop.count != 0){ - START_CUSTOM_SECTION("\u000BVADPCMLOOPS") + START_CUSTOM_SECTION("\x0bVADPCMLOOPS") uint16_t one = BSWAP16(1); tmp.Write((char*) &one, 2); tmp.Write((char*) &one, 2); @@ -609,3 +635,30 @@ void AudioManager::create_aifc(int32_t index, LUS::BinaryWriter &out) { } } } + +AudioBankSample AudioManager::get_aifc(int32_t index) { + int32_t idx = -1; + for(auto &sample_bank : this->loaded_tbl.banks){ + auto offsets = PyUtils::keys(sample_bank->entries); + std::sort(offsets.begin(), offsets.end()); + + for(auto &offset : offsets){ + if(++idx == index){ + return *sample_bank->entries[offset]; + } + } + } + + throw std::runtime_error("Invalid index"); +} + +uint32_t AudioManager::get_index(AudioBankSample* entry) { + if(!this->sampleMap.contains(entry)){ + return -1; + } + return this->sampleMap[entry]; +} + +std::map AudioManager::get_banks() { + return this->banks; +} diff --git a/src/audio/AudioManager.h b/src/audio/AudioManager.h index 4c3265c..4b5d9ce 100644 --- a/src/audio/AudioManager.h +++ b/src/audio/AudioManager.h @@ -1,9 +1,9 @@ #pragma once +#include #include #include #include -#include #include "binarytools/BinaryWriter.h" #define NONE 0xFFFF @@ -27,31 +27,32 @@ struct Entry { uint32_t length; }; -struct Sound { +struct AudioBankSound { uint32_t offset; float tuning; - bool operator==(const Sound& other) const { + bool operator==(const AudioBankSound& other) const { return offset == other.offset && tuning == other.tuning; } }; -struct Inst { +struct Instrument { + bool valid; std::string name; uint32_t offset; uint8_t releaseRate; uint8_t normalRangeLo; uint8_t normalRangeHi; uint32_t envelope; - std::optional soundLo; - std::optional soundMed; - std::optional soundHi; + std::optional soundLo; + std::optional soundMed; + std::optional soundHi; }; -struct Book { - uint32_t order{}; - uint32_t npredictors{}; - std::vector table; - bool operator==(const Book& other) const { +struct AdpcmBook { + int32_t order{}; + int32_t npredictors{}; + std::vector table; + bool operator==(const AdpcmBook& other) const { return order == other.order && npredictors == other.npredictors && table == other.table; } }; @@ -62,28 +63,28 @@ struct Drum { uint8_t releaseRate; uint8_t pan; uint32_t envelope; - Sound sound; + AudioBankSound 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 { +struct AdpcmLoop { uint32_t start{}; uint32_t end{}; int32_t count{}; uint32_t pad{}; - std::optional> state; - bool operator==(const Loop& other) const { + std::optional> state; + bool operator==(const AdpcmLoop& other) const { return start == other.start && end == other.end && count == other.count && pad == other.pad && state == other.state; } }; -struct AifcEntry { +struct AudioBankSample { std::string name; std::vector data; - Book book; - Loop loop; + AdpcmBook book; + AdpcmLoop loop; std::vector tunings; }; @@ -91,15 +92,15 @@ struct SampleBank { std::string name; uint32_t offset; std::vector data; - std::unordered_map entries; + std::map entries; - AifcEntry* AddSample(uint32_t addr, size_t sampleSize, const Book& book, const Loop& loop); + AudioBankSample* AddSample(uint32_t addr, size_t sampleSize, const AdpcmBook& book, const AdpcmLoop& loop); }; struct TBLFile { std::vector banks; std::vector tbls; - std::unordered_map map; + std::map map; }; struct CTLHeader { @@ -108,22 +109,25 @@ struct CTLHeader { uint32_t shared; }; -typedef std::pair EnvelopeData; +struct AdsrEnvelope { + int16_t delay; + int16_t arg; +}; struct Envelope { std::string name; - std::vector entries; + std::vector entries; }; struct Bank { std::string name; SampleBank* sampleBank; - std::vector insts; + std::vector insts; std::vector drums; - std::vector>> allInsts; + std::vector>> allInsts; std::vector instOffsets; - std::unordered_map envelopes; - std::unordered_map samples; + std::map envelopes; + std::map samples; void print() const; }; @@ -132,22 +136,26 @@ public: static AudioManager* Instance; void initialize(std::vector& buffer); void create_aifc(int32_t index, LUS::BinaryWriter& writer); + AudioBankSample get_aifc(int32_t index); + std::map get_banks(); + uint32_t get_index(AudioBankSample* bank); private: - std::vector banks; + std::map banks; + std::map sampleMap; TBLFile loaded_tbl; static std::vector parse_seq_file(std::vector& buffer, uint32_t offset, bool isCTL); static CTLHeader parse_ctl_header(std::vector& data); - static std::optional parse_sound(std::vector data); + static std::optional parse_sound(std::vector data); static Drum parse_drum(std::vector& data, uint32_t addr); - static Inst parse_inst(std::vector& data, uint32_t addr); - static Loop parse_loop(uint32_t addr, std::vector& bankData); - static Book parse_book(uint32_t addr, std::vector& bankData); - static AifcEntry* parse_sample(std::vector& data, std::vector& bankData, SampleBank* sampleBank); - static std::vector parse_envelope(uint32_t addr, std::vector& dataBank); + static Instrument parse_inst(std::vector& data, uint32_t addr); + static AdpcmLoop parse_loop(uint32_t addr, std::vector& bankData); + static AdpcmBook parse_book(uint32_t addr, std::vector& bankData); + static AudioBankSample* parse_sample(std::vector& data, std::vector& bankData, SampleBank* sampleBank); + static std::vector parse_envelope(uint32_t addr, std::vector& dataBank); static Bank parse_ctl(CTLHeader header, std::vector data, SampleBank* bank, uint32_t index); static TBLFile parse_tbl(std::vector& data, std::vector& entries); - static void write_aifc(AifcEntry* entry, LUS::BinaryWriter& writer); + static void write_aifc(AudioBankSample* entry, LUS::BinaryWriter& writer); }; \ No newline at end of file diff --git a/src/audio/samples_table.h b/src/audio/samples_table.h new file mode 100644 index 0000000..2946d05 --- /dev/null +++ b/src/audio/samples_table.h @@ -0,0 +1,722 @@ +#pragma once + +#if defined(_WIN32) +#define ALIGN_ASSET(x) __declspec(align(x)) +#else +#define ALIGN_ASSET(x) __attribute__((aligned (x))) +#endif + +#define dgSample00 "__OTR__sound/samples/sfx_1/00_twirl" +static const ALIGN_ASSET(2) char gSample00[] = dgSample00; + +#define dgSample01 "__OTR__sound/samples/sfx_1/01_brushing" +static const ALIGN_ASSET(2) char gSample01[] = dgSample01; + +#define dgSample02 "__OTR__sound/samples/sfx_1/02_hand_touch" +static const ALIGN_ASSET(2) char gSample02[] = dgSample02; + +#define dgSample03 "__OTR__sound/samples/sfx_1/03_yoshi" +static const ALIGN_ASSET(2) char gSample03[] = dgSample03; + +#define dgSample04 "__OTR__sound/samples/sfx_1/04_plop" +static const ALIGN_ASSET(2) char gSample04[] = dgSample04; + +#define dgSample05 "__OTR__sound/samples/sfx_1/05_heavy_landing" +static const ALIGN_ASSET(2) char gSample05[] = dgSample05; + +#define dgSample06 "__OTR__sound/samples/sfx_terrain/00_step_default" +static const ALIGN_ASSET(2) char gSample06[] = dgSample06; + +#define dgSample07 "__OTR__sound/samples/sfx_terrain/01_step_grass" +static const ALIGN_ASSET(2) char gSample07[] = dgSample07; + +#define dgSample08 "__OTR__sound/samples/sfx_terrain/02_step_stone" +static const ALIGN_ASSET(2) char gSample08[] = dgSample08; + +#define dgSample09 "__OTR__sound/samples/sfx_terrain/03_step_spooky" +static const ALIGN_ASSET(2) char gSample09[] = dgSample09; + +#define dgSample0A "__OTR__sound/samples/sfx_terrain/04_step_snow" +static const ALIGN_ASSET(2) char gSample0A[] = dgSample0A; + +#define dgSample0B "__OTR__sound/samples/sfx_terrain/05_step_ice" +static const ALIGN_ASSET(2) char gSample0B[] = dgSample0B; + +#define dgSample0C "__OTR__sound/samples/sfx_terrain/06_step_metal" +static const ALIGN_ASSET(2) char gSample0C[] = dgSample0C; + +#define dgSample0D "__OTR__sound/samples/sfx_terrain/07_step_sand" +static const ALIGN_ASSET(2) char gSample0D[] = dgSample0D; + +#define dgSample0E "__OTR__sound/samples/sfx_water/00_plunge" +static const ALIGN_ASSET(2) char gSample0E[] = dgSample0E; + +#define dgSample0F "__OTR__sound/samples/sfx_water/01_splash" +static const ALIGN_ASSET(2) char gSample0F[] = dgSample0F; + +#define dgSample10 "__OTR__sound/samples/sfx_water/02_swim" +static const ALIGN_ASSET(2) char gSample10[] = dgSample10; + +#define dgSample11 "__OTR__sound/samples/sfx_4/00" +static const ALIGN_ASSET(2) char gSample11[] = dgSample11; + +#define dgSample12 "__OTR__sound/samples/sfx_4/01" +static const ALIGN_ASSET(2) char gSample12[] = dgSample12; + +#define dgSample13 "__OTR__sound/samples/sfx_4/02" +static const ALIGN_ASSET(2) char gSample13[] = dgSample13; + +#define dgSample14 "__OTR__sound/samples/sfx_4/03" +static const ALIGN_ASSET(2) char gSample14[] = dgSample14; + +#define dgSample15 "__OTR__sound/samples/sfx_4/04" +static const ALIGN_ASSET(2) char gSample15[] = dgSample15; + +#define dgSample16 "__OTR__sound/samples/sfx_4/05" +static const ALIGN_ASSET(2) char gSample16[] = dgSample16; + +#define dgSample17 "__OTR__sound/samples/sfx_4/06" +static const ALIGN_ASSET(2) char gSample17[] = dgSample17; + +#define dgSample18 "__OTR__sound/samples/sfx_4/07" +static const ALIGN_ASSET(2) char gSample18[] = dgSample18; + +#define dgSample19 "__OTR__sound/samples/sfx_4/08" +static const ALIGN_ASSET(2) char gSample19[] = dgSample19; + +#define dgSample1A "__OTR__sound/samples/sfx_4/09" +static const ALIGN_ASSET(2) char gSample1A[] = dgSample1A; + +#define dgSample1B "__OTR__sound/samples/sfx_5/00" +static const ALIGN_ASSET(2) char gSample1B[] = dgSample1B; + +#define dgSample1C "__OTR__sound/samples/sfx_5/01" +static const ALIGN_ASSET(2) char gSample1C[] = dgSample1C; + +#define dgSample1D "__OTR__sound/samples/sfx_5/02" +static const ALIGN_ASSET(2) char gSample1D[] = dgSample1D; + +#define dgSample1E "__OTR__sound/samples/sfx_5/03" +static const ALIGN_ASSET(2) char gSample1E[] = dgSample1E; + +#define dgSample1F "__OTR__sound/samples/sfx_5/04" +static const ALIGN_ASSET(2) char gSample1F[] = dgSample1F; + +#define dgSample20 "__OTR__sound/samples/sfx_5/05" +static const ALIGN_ASSET(2) char gSample20[] = dgSample20; + +#define dgSample21 "__OTR__sound/samples/sfx_5/06" +static const ALIGN_ASSET(2) char gSample21[] = dgSample21; + +#define dgSample22 "__OTR__sound/samples/sfx_5/07" +static const ALIGN_ASSET(2) char gSample22[] = dgSample22; + +#define dgSample23 "__OTR__sound/samples/sfx_5/08" +static const ALIGN_ASSET(2) char gSample23[] = dgSample23; + +#define dgSample24 "__OTR__sound/samples/sfx_5/09" +static const ALIGN_ASSET(2) char gSample24[] = dgSample24; + +#define dgSample25 "__OTR__sound/samples/sfx_5/0A" +static const ALIGN_ASSET(2) char gSample25[] = dgSample25; + +#define dgSample26 "__OTR__sound/samples/sfx_5/0B" +static const ALIGN_ASSET(2) char gSample26[] = dgSample26; + +#define dgSample27 "__OTR__sound/samples/sfx_5/0C" +static const ALIGN_ASSET(2) char gSample27[] = dgSample27; + +#define dgSample28 "__OTR__sound/samples/sfx_5/0D" +static const ALIGN_ASSET(2) char gSample28[] = dgSample28; + +#define dgSample29 "__OTR__sound/samples/sfx_5/0E" +static const ALIGN_ASSET(2) char gSample29[] = dgSample29; + +#define dgSample2A "__OTR__sound/samples/sfx_5/0F" +static const ALIGN_ASSET(2) char gSample2A[] = dgSample2A; + +#define dgSample2B "__OTR__sound/samples/sfx_5/10" +static const ALIGN_ASSET(2) char gSample2B[] = dgSample2B; + +#define dgSample2C "__OTR__sound/samples/sfx_5/11" +static const ALIGN_ASSET(2) char gSample2C[] = dgSample2C; + +#define dgSample2D "__OTR__sound/samples/sfx_5/12" +static const ALIGN_ASSET(2) char gSample2D[] = dgSample2D; + +#define dgSample2E "__OTR__sound/samples/sfx_5/13" +static const ALIGN_ASSET(2) char gSample2E[] = dgSample2E; + +#define dgSample2F "__OTR__sound/samples/sfx_5/14" +static const ALIGN_ASSET(2) char gSample2F[] = dgSample2F; + +#define dgSample30 "__OTR__sound/samples/sfx_5/15" +static const ALIGN_ASSET(2) char gSample30[] = dgSample30; + +#define dgSample31 "__OTR__sound/samples/sfx_5/16" +static const ALIGN_ASSET(2) char gSample31[] = dgSample31; + +#define dgSample32 "__OTR__sound/samples/sfx_5/17" +static const ALIGN_ASSET(2) char gSample32[] = dgSample32; + +#define dgSample33 "__OTR__sound/samples/sfx_5/18" +static const ALIGN_ASSET(2) char gSample33[] = dgSample33; + +#define dgSample34 "__OTR__sound/samples/sfx_5/19" +static const ALIGN_ASSET(2) char gSample34[] = dgSample34; + +#define dgSample35 "__OTR__sound/samples/sfx_5/1A" +static const ALIGN_ASSET(2) char gSample35[] = dgSample35; + +#define dgSample36 "__OTR__sound/samples/sfx_5/1B" +static const ALIGN_ASSET(2) char gSample36[] = dgSample36; + +#define dgSample37 "__OTR__sound/samples/sfx_5/1C" +static const ALIGN_ASSET(2) char gSample37[] = dgSample37; + +#define dgSample38 "__OTR__sound/samples/sfx_6/00" +static const ALIGN_ASSET(2) char gSample38[] = dgSample38; + +#define dgSample39 "__OTR__sound/samples/sfx_6/01" +static const ALIGN_ASSET(2) char gSample39[] = dgSample39; + +#define dgSample3A "__OTR__sound/samples/sfx_6/02" +static const ALIGN_ASSET(2) char gSample3A[] = dgSample3A; + +#define dgSample3B "__OTR__sound/samples/sfx_6/03" +static const ALIGN_ASSET(2) char gSample3B[] = dgSample3B; + +#define dgSample3C "__OTR__sound/samples/sfx_6/04" +static const ALIGN_ASSET(2) char gSample3C[] = dgSample3C; + +#define dgSample3D "__OTR__sound/samples/sfx_6/05" +static const ALIGN_ASSET(2) char gSample3D[] = dgSample3D; + +#define dgSample3E "__OTR__sound/samples/sfx_6/06" +static const ALIGN_ASSET(2) char gSample3E[] = dgSample3E; + +#define dgSample3F "__OTR__sound/samples/sfx_6/07" +static const ALIGN_ASSET(2) char gSample3F[] = dgSample3F; + +#define dgSample40 "__OTR__sound/samples/sfx_6/08" +static const ALIGN_ASSET(2) char gSample40[] = dgSample40; + +#define dgSample41 "__OTR__sound/samples/sfx_6/09" +static const ALIGN_ASSET(2) char gSample41[] = dgSample41; + +#define dgSample42 "__OTR__sound/samples/sfx_6/0A" +static const ALIGN_ASSET(2) char gSample42[] = dgSample42; + +#define dgSample43 "__OTR__sound/samples/sfx_6/0B" +static const ALIGN_ASSET(2) char gSample43[] = dgSample43; + +#define dgSample44 "__OTR__sound/samples/sfx_6/0C" +static const ALIGN_ASSET(2) char gSample44[] = dgSample44; + +#define dgSample45 "__OTR__sound/samples/sfx_6/0D" +static const ALIGN_ASSET(2) char gSample45[] = dgSample45; + +#define dgSample46 "__OTR__sound/samples/sfx_7/00" +static const ALIGN_ASSET(2) char gSample46[] = dgSample46; + +#define dgSample47 "__OTR__sound/samples/sfx_7/01" +static const ALIGN_ASSET(2) char gSample47[] = dgSample47; + +#define dgSample48 "__OTR__sound/samples/sfx_7/02" +static const ALIGN_ASSET(2) char gSample48[] = dgSample48; + +#define dgSample49 "__OTR__sound/samples/sfx_7/03" +static const ALIGN_ASSET(2) char gSample49[] = dgSample49; + +#define dgSample4A "__OTR__sound/samples/sfx_7/04" +static const ALIGN_ASSET(2) char gSample4A[] = dgSample4A; + +#define dgSample4B "__OTR__sound/samples/sfx_7/05" +static const ALIGN_ASSET(2) char gSample4B[] = dgSample4B; + +#define dgSample4C "__OTR__sound/samples/sfx_7/06" +static const ALIGN_ASSET(2) char gSample4C[] = dgSample4C; + +#define dgSample4D "__OTR__sound/samples/sfx_7/07" +static const ALIGN_ASSET(2) char gSample4D[] = dgSample4D; + +#define dgSample4E "__OTR__sound/samples/sfx_7/08" +static const ALIGN_ASSET(2) char gSample4E[] = dgSample4E; + +#define dgSample4F "__OTR__sound/samples/sfx_7/09" +static const ALIGN_ASSET(2) char gSample4F[] = dgSample4F; + +#define dgSample50 "__OTR__sound/samples/sfx_7/0A" +static const ALIGN_ASSET(2) char gSample50[] = dgSample50; + +#define dgSample51 "__OTR__sound/samples/sfx_7/0B" +static const ALIGN_ASSET(2) char gSample51[] = dgSample51; + +#define dgSample52 "__OTR__sound/samples/sfx_7/0C" +static const ALIGN_ASSET(2) char gSample52[] = dgSample52; + +#define dgSample53 "__OTR__sound/samples/sfx_7/0D_chain_chomp_bark" +static const ALIGN_ASSET(2) char gSample53[] = dgSample53; + +#define dgSample54 "__OTR__sound/samples/sfx_mario/00_mario_jump_hoo" +static const ALIGN_ASSET(2) char gSample54[] = dgSample54; + +#define dgSample55 "__OTR__sound/samples/sfx_mario/01_mario_jump_wah" +static const ALIGN_ASSET(2) char gSample55[] = dgSample55; + +#define dgSample56 "__OTR__sound/samples/sfx_mario/02_mario_yah" +static const ALIGN_ASSET(2) char gSample56[] = dgSample56; + +#define dgSample57 "__OTR__sound/samples/sfx_mario/03_mario_haha" +static const ALIGN_ASSET(2) char gSample57[] = dgSample57; + +#define dgSample58 "__OTR__sound/samples/sfx_mario/04_mario_yahoo" +static const ALIGN_ASSET(2) char gSample58[] = dgSample58; + +#define dgSample59 "__OTR__sound/samples/sfx_mario/05_mario_uh" +static const ALIGN_ASSET(2) char gSample59[] = dgSample59; + +#define dgSample5A "__OTR__sound/samples/sfx_mario/06_mario_hrmm" +static const ALIGN_ASSET(2) char gSample5A[] = dgSample5A; + +#define dgSample5B "__OTR__sound/samples/sfx_mario/07_mario_wah2" +static const ALIGN_ASSET(2) char gSample5B[] = dgSample5B; + +#define dgSample5C "__OTR__sound/samples/sfx_mario/08_mario_whoa" +static const ALIGN_ASSET(2) char gSample5C[] = dgSample5C; + +#define dgSample5D "__OTR__sound/samples/sfx_mario/09_mario_eeuh" +static const ALIGN_ASSET(2) char gSample5D[] = dgSample5D; + +#define dgSample5E "__OTR__sound/samples/sfx_mario/0A_mario_attacked" +static const ALIGN_ASSET(2) char gSample5E[] = dgSample5E; + +#define dgSample5F "__OTR__sound/samples/sfx_mario/0B_mario_ooof" +static const ALIGN_ASSET(2) char gSample5F[] = dgSample5F; + +#define dgSample60 "__OTR__sound/samples/sfx_mario/0C_mario_here_we_go" +static const ALIGN_ASSET(2) char gSample60[] = dgSample60; + +#define dgSample61 "__OTR__sound/samples/sfx_mario/0D_mario_yawning" +static const ALIGN_ASSET(2) char gSample61[] = dgSample61; + +#define dgSample62 "__OTR__sound/samples/sfx_mario/0E_mario_snoring1" +static const ALIGN_ASSET(2) char gSample62[] = dgSample62; + +#define dgSample63 "__OTR__sound/samples/sfx_mario/0F_mario_snoring2" +static const ALIGN_ASSET(2) char gSample63[] = dgSample63; + +#define dgSample64 "__OTR__sound/samples/sfx_mario/10_mario_doh" +static const ALIGN_ASSET(2) char gSample64[] = dgSample64; + +#define dgSample65 "__OTR__sound/samples/sfx_mario/11_mario_game_over" +static const ALIGN_ASSET(2) char gSample65[] = dgSample65; + +#define dgSample66 "__OTR__sound/samples/sfx_mario/12_mario_hello" +static const ALIGN_ASSET(2) char gSample66[] = dgSample66; + +#define dgSample67 "__OTR__sound/samples/sfx_mario/13_mario_press_start_to_play" +static const ALIGN_ASSET(2) char gSample67[] = dgSample67; + +#define dgSample68 "__OTR__sound/samples/sfx_mario/14_mario_twirl_bounce" +static const ALIGN_ASSET(2) char gSample68[] = dgSample68; + +#define dgSample69 "__OTR__sound/samples/sfx_mario/15_mario_snoring3" +static const ALIGN_ASSET(2) char gSample69[] = dgSample69; + +#define dgSample6A "__OTR__sound/samples/sfx_mario/16_mario_so_longa_bowser" +static const ALIGN_ASSET(2) char gSample6A[] = dgSample6A; + +#define dgSample6B "__OTR__sound/samples/sfx_mario/17_mario_ima_tired" +static const ALIGN_ASSET(2) char gSample6B[] = dgSample6B; + +#define dgSample6C "__OTR__sound/samples/sfx_mario/18_mario_waha" +static const ALIGN_ASSET(2) char gSample6C[] = dgSample6C; + +#define dgSample6D "__OTR__sound/samples/sfx_mario/19_mario_yippee" +static const ALIGN_ASSET(2) char gSample6D[] = dgSample6D; + +#define dgSample6E "__OTR__sound/samples/sfx_mario/1A_mario_lets_a_go" +static const ALIGN_ASSET(2) char gSample6E[] = dgSample6E; + +#define dgSample6F "__OTR__sound/samples/sfx_9/00" +static const ALIGN_ASSET(2) char gSample6F[] = dgSample6F; + +#define dgSample70 "__OTR__sound/samples/sfx_9/01" +static const ALIGN_ASSET(2) char gSample70[] = dgSample70; + +#define dgSample71 "__OTR__sound/samples/sfx_9/02" +static const ALIGN_ASSET(2) char gSample71[] = dgSample71; + +#define dgSample72 "__OTR__sound/samples/sfx_9/03" +static const ALIGN_ASSET(2) char gSample72[] = dgSample72; + +#define dgSample73 "__OTR__sound/samples/sfx_9/04_camera_buzz" +static const ALIGN_ASSET(2) char gSample73[] = dgSample73; + +#define dgSample74 "__OTR__sound/samples/sfx_9/05_camera_shutter" +static const ALIGN_ASSET(2) char gSample74[] = dgSample74; + +#define dgSample75 "__OTR__sound/samples/sfx_9/06" +static const ALIGN_ASSET(2) char gSample75[] = dgSample75; + +#define dgSample76 "__OTR__sound/samples/sfx_mario_peach/00_mario_waaaooow" +static const ALIGN_ASSET(2) char gSample76[] = dgSample76; + +#define dgSample77 "__OTR__sound/samples/sfx_mario_peach/01_mario_hoohoo" +static const ALIGN_ASSET(2) char gSample77[] = dgSample77; + +#define dgSample78 "__OTR__sound/samples/sfx_mario_peach/02_mario_panting" +static const ALIGN_ASSET(2) char gSample78[] = dgSample78; + +#define dgSample79 "__OTR__sound/samples/sfx_mario_peach/03_mario_dying" +static const ALIGN_ASSET(2) char gSample79[] = dgSample79; + +#define dgSample7A "__OTR__sound/samples/sfx_mario_peach/04_mario_on_fire" +static const ALIGN_ASSET(2) char gSample7A[] = dgSample7A; + +#define dgSample7B "__OTR__sound/samples/sfx_mario_peach/05_mario_uh2" +static const ALIGN_ASSET(2) char gSample7B[] = dgSample7B; + +#define dgSample7C "__OTR__sound/samples/sfx_mario_peach/06_mario_coughing" +static const ALIGN_ASSET(2) char gSample7C[] = dgSample7C; + +#define dgSample7D "__OTR__sound/samples/sfx_mario_peach/07_mario_its_a_me_mario" +static const ALIGN_ASSET(2) char gSample7D[] = dgSample7D; + +#define dgSample7E "__OTR__sound/samples/sfx_mario_peach/08_mario_punch_yah" +static const ALIGN_ASSET(2) char gSample7E[] = dgSample7E; + +#define dgSample7F "__OTR__sound/samples/sfx_mario_peach/09_mario_punch_hoo" +static const ALIGN_ASSET(2) char gSample7F[] = dgSample7F; + +#define dgSample80 "__OTR__sound/samples/sfx_mario_peach/0A_mario_mama_mia" +static const ALIGN_ASSET(2) char gSample80[] = dgSample80; + +#define dgSample81 "__OTR__sound/samples/sfx_mario_peach/0B_mario_okey_dokey" +static const ALIGN_ASSET(2) char gSample81[] = dgSample81; + +#define dgSample82 "__OTR__sound/samples/sfx_mario_peach/0C_mario_drowning" +static const ALIGN_ASSET(2) char gSample82[] = dgSample82; + +#define dgSample83 "__OTR__sound/samples/sfx_mario_peach/0D_mario_thank_you_playing_my_game" +static const ALIGN_ASSET(2) char gSample83[] = dgSample83; + +#define dgSample84 "__OTR__sound/samples/sfx_mario_peach/0E_peach_dear_mario" +static const ALIGN_ASSET(2) char gSample84[] = dgSample84; + +#define dgSample85 "__OTR__sound/samples/sfx_mario_peach/0F_peach_mario" +static const ALIGN_ASSET(2) char gSample85[] = dgSample85; + +#define dgSample86 "__OTR__sound/samples/sfx_mario_peach/10_peach_power_of_the_stars" +static const ALIGN_ASSET(2) char gSample86[] = dgSample86; + +#define dgSample87 "__OTR__sound/samples/sfx_mario_peach/11_peach_thanks_to_you" +static const ALIGN_ASSET(2) char gSample87[] = dgSample87; + +#define dgSample88 "__OTR__sound/samples/sfx_mario_peach/12_peach_thank_you_mario" +static const ALIGN_ASSET(2) char gSample88[] = dgSample88; + +#define dgSample89 "__OTR__sound/samples/sfx_mario_peach/13_peach_something_special" +static const ALIGN_ASSET(2) char gSample89[] = dgSample89; + +#define dgSample8A "__OTR__sound/samples/sfx_mario_peach/14_peach_bake_a_cake" +static const ALIGN_ASSET(2) char gSample8A[] = dgSample8A; + +#define dgSample8B "__OTR__sound/samples/sfx_mario_peach/15_peach_for_mario" +static const ALIGN_ASSET(2) char gSample8B[] = dgSample8B; + +#define dgSample8C "__OTR__sound/samples/sfx_mario_peach/16_peach_mario2" +static const ALIGN_ASSET(2) char gSample8C[] = dgSample8C; + +#define dgSample8D "__OTR__sound/samples/instruments/00" +static const ALIGN_ASSET(2) char gSample8D[] = dgSample8D; + +#define dgSample8E "__OTR__sound/samples/instruments/01_banjo_1" +static const ALIGN_ASSET(2) char gSample8E[] = dgSample8E; + +#define dgSample8F "__OTR__sound/samples/instruments/02" +static const ALIGN_ASSET(2) char gSample8F[] = dgSample8F; + +#define dgSample90 "__OTR__sound/samples/instruments/03_human_whistle" +static const ALIGN_ASSET(2) char gSample90[] = dgSample90; + +#define dgSample91 "__OTR__sound/samples/instruments/04_bright_piano" +static const ALIGN_ASSET(2) char gSample91[] = dgSample91; + +#define dgSample92 "__OTR__sound/samples/instruments/05_acoustic_bass" +static const ALIGN_ASSET(2) char gSample92[] = dgSample92; + +#define dgSample93 "__OTR__sound/samples/instruments/06_kick_drum_1" +static const ALIGN_ASSET(2) char gSample93[] = dgSample93; + +#define dgSample94 "__OTR__sound/samples/instruments/07_rimshot" +static const ALIGN_ASSET(2) char gSample94[] = dgSample94; + +#define dgSample95 "__OTR__sound/samples/instruments/08" +static const ALIGN_ASSET(2) char gSample95[] = dgSample95; + +#define dgSample96 "__OTR__sound/samples/instruments/09" +static const ALIGN_ASSET(2) char gSample96[] = dgSample96; + +#define dgSample97 "__OTR__sound/samples/instruments/0A_tambourine" +static const ALIGN_ASSET(2) char gSample97[] = dgSample97; + +#define dgSample98 "__OTR__sound/samples/instruments/0B" +static const ALIGN_ASSET(2) char gSample98[] = dgSample98; + +#define dgSample99 "__OTR__sound/samples/instruments/0C_conga_stick" +static const ALIGN_ASSET(2) char gSample99[] = dgSample99; + +#define dgSample9A "__OTR__sound/samples/instruments/0D_clave" +static const ALIGN_ASSET(2) char gSample9A[] = dgSample9A; + +#define dgSample9B "__OTR__sound/samples/instruments/0E_hihat_closed" +static const ALIGN_ASSET(2) char gSample9B[] = dgSample9B; + +#define dgSample9C "__OTR__sound/samples/instruments/0F_hihat_open" +static const ALIGN_ASSET(2) char gSample9C[] = dgSample9C; + +#define dgSample9D "__OTR__sound/samples/instruments/10_cymbal_bell" +static const ALIGN_ASSET(2) char gSample9D[] = dgSample9D; + +#define dgSample9E "__OTR__sound/samples/instruments/11_splash_cymbal" +static const ALIGN_ASSET(2) char gSample9E[] = dgSample9E; + +#define dgSample9F "__OTR__sound/samples/instruments/12_snare_drum_1" +static const ALIGN_ASSET(2) char gSample9F[] = dgSample9F; + +#define dgSampleA0 "__OTR__sound/samples/instruments/13_snare_drum_2" +static const ALIGN_ASSET(2) char gSampleA0[] = dgSampleA0; + +#define dgSampleA1 "__OTR__sound/samples/instruments/14_strings_5" +static const ALIGN_ASSET(2) char gSampleA1[] = dgSampleA1; + +#define dgSampleA2 "__OTR__sound/samples/instruments/15_strings_4" +static const ALIGN_ASSET(2) char gSampleA2[] = dgSampleA2; + +#define dgSampleA3 "__OTR__sound/samples/instruments/16_french_horns" +static const ALIGN_ASSET(2) char gSampleA3[] = dgSampleA3; + +#define dgSampleA4 "__OTR__sound/samples/instruments/17_trumpet" +static const ALIGN_ASSET(2) char gSampleA4[] = dgSampleA4; + +#define dgSampleA5 "__OTR__sound/samples/instruments/18_timpani" +static const ALIGN_ASSET(2) char gSampleA5[] = dgSampleA5; + +#define dgSampleA6 "__OTR__sound/samples/instruments/19_brass" +static const ALIGN_ASSET(2) char gSampleA6[] = dgSampleA6; + +#define dgSampleA7 "__OTR__sound/samples/instruments/1A_slap_bass" +static const ALIGN_ASSET(2) char gSampleA7[] = dgSampleA7; + +#define dgSampleA8 "__OTR__sound/samples/instruments/1B_organ_2" +static const ALIGN_ASSET(2) char gSampleA8[] = dgSampleA8; + +#define dgSampleA9 "__OTR__sound/samples/instruments/1C" +static const ALIGN_ASSET(2) char gSampleA9[] = dgSampleA9; + +#define dgSampleAA "__OTR__sound/samples/instruments/1D" +static const ALIGN_ASSET(2) char gSampleAA[] = dgSampleAA; + +#define dgSampleAB "__OTR__sound/samples/instruments/1E_closed_triangle" +static const ALIGN_ASSET(2) char gSampleAB[] = dgSampleAB; + +#define dgSampleAC "__OTR__sound/samples/instruments/1F_open_triangle" +static const ALIGN_ASSET(2) char gSampleAC[] = dgSampleAC; + +#define dgSampleAD "__OTR__sound/samples/instruments/20_cabasa" +static const ALIGN_ASSET(2) char gSampleAD[] = dgSampleAD; + +#define dgSampleAE "__OTR__sound/samples/instruments/21_sine_bass" +static const ALIGN_ASSET(2) char gSampleAE[] = dgSampleAE; + +#define dgSampleAF "__OTR__sound/samples/instruments/22_boys_choir" +static const ALIGN_ASSET(2) char gSampleAF[] = dgSampleAF; + +#define dgSampleB0 "__OTR__sound/samples/instruments/23_strings_1" +static const ALIGN_ASSET(2) char gSampleB0[] = dgSampleB0; + +#define dgSampleB1 "__OTR__sound/samples/instruments/24_strings_2" +static const ALIGN_ASSET(2) char gSampleB1[] = dgSampleB1; + +#define dgSampleB2 "__OTR__sound/samples/instruments/25_strings_3" +static const ALIGN_ASSET(2) char gSampleB2[] = dgSampleB2; + +#define dgSampleB3 "__OTR__sound/samples/instruments/26_crystal_rhodes" +static const ALIGN_ASSET(2) char gSampleB3[] = dgSampleB3; + +#define dgSampleB4 "__OTR__sound/samples/instruments/27_harpsichord" +static const ALIGN_ASSET(2) char gSampleB4[] = dgSampleB4; + +#define dgSampleB5 "__OTR__sound/samples/instruments/28_sitar_1" +static const ALIGN_ASSET(2) char gSampleB5[] = dgSampleB5; + +#define dgSampleB6 "__OTR__sound/samples/instruments/29_orchestra_hit" +static const ALIGN_ASSET(2) char gSampleB6[] = dgSampleB6; + +#define dgSampleB7 "__OTR__sound/samples/instruments/2A" +static const ALIGN_ASSET(2) char gSampleB7[] = dgSampleB7; + +#define dgSampleB8 "__OTR__sound/samples/instruments/2B" +static const ALIGN_ASSET(2) char gSampleB8[] = dgSampleB8; + +#define dgSampleB9 "__OTR__sound/samples/instruments/2C" +static const ALIGN_ASSET(2) char gSampleB9[] = dgSampleB9; + +#define dgSampleBA "__OTR__sound/samples/instruments/2D_trombone" +static const ALIGN_ASSET(2) char gSampleBA[] = dgSampleBA; + +#define dgSampleBB "__OTR__sound/samples/instruments/2E_accordion" +static const ALIGN_ASSET(2) char gSampleBB[] = dgSampleBB; + +#define dgSampleBC "__OTR__sound/samples/instruments/2F_sleigh_bells" +static const ALIGN_ASSET(2) char gSampleBC[] = dgSampleBC; + +#define dgSampleBD "__OTR__sound/samples/instruments/30_rarefaction-lahna" +static const ALIGN_ASSET(2) char gSampleBD[] = dgSampleBD; + +#define dgSampleBE "__OTR__sound/samples/instruments/31_rarefaction-convolution" +static const ALIGN_ASSET(2) char gSampleBE[] = dgSampleBE; + +#define dgSampleBF "__OTR__sound/samples/instruments/32_metal_rimshot" +static const ALIGN_ASSET(2) char gSampleBF[] = dgSampleBF; + +#define dgSampleC0 "__OTR__sound/samples/instruments/33_kick_drum_2" +static const ALIGN_ASSET(2) char gSampleC0[] = dgSampleC0; + +#define dgSampleC1 "__OTR__sound/samples/instruments/34_alto_flute" +static const ALIGN_ASSET(2) char gSampleC1[] = dgSampleC1; + +#define dgSampleC2 "__OTR__sound/samples/instruments/35_gospel_organ" +static const ALIGN_ASSET(2) char gSampleC2[] = dgSampleC2; + +#define dgSampleC3 "__OTR__sound/samples/instruments/36_sawtooth_synth" +static const ALIGN_ASSET(2) char gSampleC3[] = dgSampleC3; + +#define dgSampleC4 "__OTR__sound/samples/instruments/37_square_synth" +static const ALIGN_ASSET(2) char gSampleC4[] = dgSampleC4; + +#define dgSampleC5 "__OTR__sound/samples/instruments/38_electric_kick_drum" +static const ALIGN_ASSET(2) char gSampleC5[] = dgSampleC5; + +#define dgSampleC6 "__OTR__sound/samples/instruments/39_sitar_2" +static const ALIGN_ASSET(2) char gSampleC6[] = dgSampleC6; + +#define dgSampleC7 "__OTR__sound/samples/instruments/3A_music_box" +static const ALIGN_ASSET(2) char gSampleC7[] = dgSampleC7; + +#define dgSampleC8 "__OTR__sound/samples/instruments/3B_banjo_2" +static const ALIGN_ASSET(2) char gSampleC8[] = dgSampleC8; + +#define dgSampleC9 "__OTR__sound/samples/instruments/3C_acoustic_guitar" +static const ALIGN_ASSET(2) char gSampleC9[] = dgSampleC9; + +#define dgSampleCA "__OTR__sound/samples/instruments/3D" +static const ALIGN_ASSET(2) char gSampleCA[] = dgSampleCA; + +#define dgSampleCB "__OTR__sound/samples/instruments/3E_monk_choir" +static const ALIGN_ASSET(2) char gSampleCB[] = dgSampleCB; + +#define dgSampleCC "__OTR__sound/samples/instruments/3F" +static const ALIGN_ASSET(2) char gSampleCC[] = dgSampleCC; + +#define dgSampleCD "__OTR__sound/samples/instruments/40_bell" +static const ALIGN_ASSET(2) char gSampleCD[] = dgSampleCD; + +#define dgSampleCE "__OTR__sound/samples/instruments/41_pan_flute" +static const ALIGN_ASSET(2) char gSampleCE[] = dgSampleCE; + +#define dgSampleCF "__OTR__sound/samples/instruments/42_vibraphone" +static const ALIGN_ASSET(2) char gSampleCF[] = dgSampleCF; + +#define dgSampleD0 "__OTR__sound/samples/instruments/43_harmonica" +static const ALIGN_ASSET(2) char gSampleD0[] = dgSampleD0; + +#define dgSampleD1 "__OTR__sound/samples/instruments/44_grand_piano" +static const ALIGN_ASSET(2) char gSampleD1[] = dgSampleD1; + +#define dgSampleD2 "__OTR__sound/samples/instruments/45_french_horns_lq" +static const ALIGN_ASSET(2) char gSampleD2[] = dgSampleD2; + +#define dgSampleD3 "__OTR__sound/samples/instruments/46_pizzicato_strings_1" +static const ALIGN_ASSET(2) char gSampleD3[] = dgSampleD3; + +#define dgSampleD4 "__OTR__sound/samples/instruments/47_pizzicato_strings_2" +static const ALIGN_ASSET(2) char gSampleD4[] = dgSampleD4; + +#define dgSampleD5 "__OTR__sound/samples/instruments/48_steel_drum" +static const ALIGN_ASSET(2) char gSampleD5[] = dgSampleD5; + +#define dgSampleD6 "__OTR__sound/samples/piranha_music_box/00_music_box" +static const ALIGN_ASSET(2) char gSampleD6[] = dgSampleD6; + +#define dgSampleD7 "__OTR__sound/samples/course_start/00_la" +static const ALIGN_ASSET(2) char gSampleD7[] = dgSampleD7; + +#define dgSampleD8 "__OTR__sound/samples/bowser_organ/00_organ_1" +static const ALIGN_ASSET(2) char gSampleD8[] = dgSampleD8; + +#define dgSampleD9 "__OTR__sound/samples/bowser_organ/01_organ_1_lq" +static const ALIGN_ASSET(2) char gSampleD9[] = dgSampleD9; + +#define dgSampleDA "__OTR__sound/samples/bowser_organ/02_boys_choir" +static const ALIGN_ASSET(2) char gSampleDA[] = dgSampleDA; + +static const char* gSampleTable[] = { + gSample00, gSample01, gSample02, gSample03, + gSample04, gSample05, gSample06, gSample07, + gSample08, gSample09, gSample0A, gSample0B, + gSample0C, gSample0D, gSample0E, gSample0F, + gSample10, gSample11, gSample12, gSample13, + gSample14, gSample15, gSample16, gSample17, + gSample18, gSample19, gSample1A, gSample1B, + gSample1C, gSample1D, gSample1E, gSample1F, + gSample20, gSample21, gSample22, gSample23, + gSample24, gSample25, gSample26, gSample27, + gSample28, gSample29, gSample2A, gSample2B, + gSample2C, gSample2D, gSample2E, gSample2F, + gSample30, gSample31, gSample32, gSample33, + gSample34, gSample35, gSample36, gSample37, + gSample38, gSample39, gSample3A, gSample3B, + gSample3C, gSample3D, gSample3E, gSample3F, + gSample40, gSample41, gSample42, gSample43, + gSample44, gSample45, gSample46, gSample47, + gSample48, gSample49, gSample4A, gSample4B, + gSample4C, gSample4D, gSample4E, gSample4F, + gSample50, gSample51, gSample52, gSample53, + gSample54, gSample55, gSample56, gSample57, + gSample58, gSample59, gSample5A, gSample5B, + gSample5C, gSample5D, gSample5E, gSample5F, + gSample60, gSample61, gSample62, gSample63, + gSample64, gSample65, gSample66, gSample67, + gSample68, gSample69, gSample6A, gSample6B, + gSample6C, gSample6D, gSample6E, gSample6F, + gSample70, gSample71, gSample72, gSample73, + gSample74, gSample75, gSample76, gSample77, + gSample78, gSample79, gSample7A, gSample7B, + gSample7C, gSample7D, gSample7E, gSample7F, + gSample80, gSample81, gSample82, gSample83, + gSample84, gSample85, gSample86, gSample87, + gSample88, gSample89, gSample8A, gSample8B, + gSample8C, gSample8D, gSample8E, gSample8F, + gSample90, gSample91, gSample92, gSample93, + gSample94, gSample95, gSample96, gSample97, + gSample98, gSample99, gSample9A, gSample9B, + gSample9C, gSample9D, gSample9E, gSample9F, + gSampleA0, gSampleA1, gSampleA2, gSampleA3, + gSampleA4, gSampleA5, gSampleA6, gSampleA7, + gSampleA8, gSampleA9, gSampleAA, gSampleAB, + gSampleAC, gSampleAD, gSampleAE, gSampleAF, + gSampleB0, gSampleB1, gSampleB2, gSampleB3, + gSampleB4, gSampleB5, gSampleB6, gSampleB7, + gSampleB8, gSampleB9, gSampleBA, gSampleBB, + gSampleBC, gSampleBD, gSampleBE, gSampleBF, + gSampleC0, gSampleC1, gSampleC2, gSampleC3, + gSampleC4, gSampleC5, gSampleC6, gSampleC7, + gSampleC8, gSampleC9, gSampleCA, gSampleCB, + gSampleCC, gSampleCD, gSampleCE, gSampleCF, + gSampleD0, gSampleD1, gSampleD2, gSampleD3, + gSampleD4, gSampleD5, gSampleD6, gSampleD7, + gSampleD8, gSampleD9, gSampleDA, +}; diff --git a/src/factories/AudioFactory.cpp b/src/factories/AudioFactory.cpp index d1d2111..a6c5364 100644 --- a/src/factories/AudioFactory.cpp +++ b/src/factories/AudioFactory.cpp @@ -2,7 +2,7 @@ #include #include "audio/AudioManager.h" -#include "audio/AIFCDecode.h" +#include "audio/samples_table.h" #include "binarytools/BinaryReader.h" bool AudioFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std::vector& buffer) { @@ -22,16 +22,156 @@ bool AudioFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std: return true; } - if(!metadata[1].contains("us")){ + if(path.find("sound/banks") != std::string::npos){ + auto banks = AudioManager::Instance->get_banks(); + auto bankId = metadata["us"][0]; + auto bank = banks[bankId]; + + if(bankId == 10){ + int bp = 1; + } + + WRITE_HEADER(LUS::ResourceType::Bank, 0); + + if(path.find("sound/banks/us/20_bank") != std::string::npos){ + int bp = 0; + } + + WRITE_U32(bank.insts.size()); + for(auto &instrument : bank.insts){ + WRITE_U8(instrument.valid); + if(!instrument.valid){ + continue; + } + WRITE_U8(instrument.releaseRate); + WRITE_U8(instrument.normalRangeLo); + WRITE_U8(instrument.normalRangeHi); + + auto envelope = bank.envelopes[instrument.envelope]; + if(!envelope.entries.empty()){ + WRITE_U32(envelope.entries.size()); + for(auto &entry : envelope.entries){ + writer->Write(entry.delay); + writer->Write(entry.arg); + } + } else { + WRITE_U32(0); + } + + uint32_t soundFlags = 0; + bool hasLo = instrument.soundLo.has_value(); + bool hasMed = instrument.soundMed.has_value(); + bool hasHi = instrument.soundHi.has_value(); + if(hasLo){ + soundFlags |= (1 << 0); + } + if(hasMed){ + soundFlags |= (1 << 1); + } + if(hasHi){ + soundFlags |= (1 << 2); + } + + WRITE_U32(soundFlags); + if(hasLo){ + auto value = instrument.soundLo.value(); + uint32_t idx = AudioManager::Instance->get_index(bank.samples[value.offset]); + writer->Write(std::string(gSampleTable[idx])); + writer->Write(value.tuning); + } + if(hasMed){ + auto value = instrument.soundMed.value(); + uint32_t idx = AudioManager::Instance->get_index(bank.samples[value.offset]); + writer->Write(std::string(gSampleTable[idx])); + writer->Write(value.tuning); + } + if(hasHi){ + auto value = instrument.soundHi.value(); + uint32_t idx = AudioManager::Instance->get_index(bank.samples[value.offset]); + writer->Write(std::string(gSampleTable[idx])); + writer->Write(value.tuning); + } + + } + + WRITE_U32(bank.drums.size()); + for(auto &drum : bank.drums){ + WRITE_U8(drum.releaseRate); + WRITE_U8(drum.pan); + auto envelope = bank.envelopes[drum.envelope]; + if(!envelope.entries.empty()){ + std::cout << "Envelope entries: " << envelope.entries.size() << std::endl; + WRITE_U32(envelope.entries.size()); + for(auto &entry : envelope.entries){ + writer->Write(entry.delay); + writer->Write(entry.arg); + } + } else { + WRITE_U32(0); + } + + uint32_t idx = AudioManager::Instance->get_index(bank.samples[drum.sound.offset]); + std::string out = std::string(gSampleTable[idx]); + writer->Write(out); + writer->Write(drum.sound.tuning); + } + + return true; + } + + if(metadata.size() < 2 || !metadata[1].contains("us")){ return false; } - LUS::BinaryWriter aifcWriter; - AudioManager::Instance->create_aifc(metadata[1]["us"][1], aifcWriter); - std::vector aifcData = aifcWriter.ToVector(); - aifcWriter.Close(); + /* + * HINT: Uncomment this to write the aiff to a file + LUS::BinaryWriter aifcWriter; + AudioManager::Instance->create_aifc(metadata[1]["us"][1], aifcWriter); + std::vector aifcData = aifcWriter.ToVector(); + aifcWriter.Close(); + write_aiff(aifcData, *writer); + */ - write_aiff(aifcData, *writer); + WRITE_HEADER(LUS::ResourceType::Audio, 0); + + AudioBankSample entry = AudioManager::Instance->get_aifc(metadata[1]["us"][1]); + + if(path.find("00_mario_waaaooow") != std::string::npos){ + int bp = 0; + } + + // Write AdpcmLoop + WRITE_U32(entry.loop.start); + WRITE_U32(entry.loop.end); + WRITE_I32(entry.loop.count); + WRITE_U32(entry.loop.pad); + + if(entry.loop.state.has_value()){ + std::vector state = entry.loop.state.value(); + WRITE_U32(state.size()); + for(auto &value : state){ + writer->Write(value); + } + } else { + WRITE_U32(0); + } + + // Write AdpcmBook + WRITE_I32(entry.book.order); + WRITE_I32(entry.book.npredictors); + + WRITE_U32(entry.book.table.size()); + for(auto &value : entry.book.table){ + writer->Write(value); + } + + // Write sample + WRITE_I32(entry.data.size()); + for(auto &value : entry.data){ + WRITE_U8(value); + } + + writer->Write(entry.name); return true; -} +} \ No newline at end of file diff --git a/src/factories/RawFactory.h b/src/factories/RawFactory.h index fe6ec98..8918b8b 100644 --- a/src/factories/RawFactory.h +++ b/src/factories/RawFactory.h @@ -8,11 +8,13 @@ #define WRITE_HEADER(type, version) this->WriteHeader(writer, type, version) #define WRITE_BHEADER(type, version) this->WriteHeader(writer, type, version, true) +#define WRITE_U8(value) writer->Write((uint8_t) value) #define WRITE_U16(value) writer->Write((uint16_t) value) #define WRITE_U32(value) writer->Write((uint32_t) value) #define WRITE_U64(value) writer->Write((uint64_t) value) #define WRITE_I8(value) writer->Write((int8_t) value) #define WRITE_I16(value) writer->Write((int16_t) value) +#define WRITE_I32(value) writer->Write((int32_t) value) #define WRITE_DATA(vec) writer->Write((char*) vec.data(), data.size()) #define WRITE_ARRAY(data, size) writer->Write((char*) data, size) diff --git a/src/factories/ResourceType.h b/src/factories/ResourceType.h index 3d4013b..ee8a9f6 100644 --- a/src/factories/ResourceType.h +++ b/src/factories/ResourceType.h @@ -19,5 +19,7 @@ enum class ResourceType { Demo = 0x44454D4F, // DEMO Skybox = 0x534B5942, // SKYB Anim = 0x414E494D, // ANIM + Bank = 0x42414E4B, // BANK + Audio = 0x41554643, // AIFC }; } // namespace LUS diff --git a/tools/gen_banks.js b/tools/gen_banks.js new file mode 100644 index 0000000..6e1da48 --- /dev/null +++ b/tools/gen_banks.js @@ -0,0 +1,37 @@ +const fs = require('fs'); +const data = require('../assets.json'); + +const header = [ + '#pragma once', + '#include "align_asset_macro.h"', + '', +] +const banks = [] + +async function main(){ + const files = Object.keys(data).filter(f => f.endsWith(".bnk") && data[f]['us'] != undefined).map(m => { + return { + file: m, + offset: data[m]['us'][0] + } + }) + + files.sort((a, b) => a.offset - b.offset); + + for(const entry of files){ + const sampleId = entry.offset.toString(16).padStart(2, '0').toUpperCase(); + banks.push(`gBank${sampleId}`); + header.push(`#define dgBank${sampleId} "__OTR__${entry.file.replace('.bnk', '')}"`); + header.push(`static const ALIGN_ASSET(2) char gBank${sampleId}[] = dgBank${sampleId};\n`); + } + + header.push('static const char* gBankTable[] = {'); + for (const bank of banks) { + header.push(` ${bank},`); + } + header.push('};\n'); + + fs.writeFileSync('./banks_table.h', header.join('\n')); +} + +main(); \ No newline at end of file diff --git a/tools/gen_samples.js b/tools/gen_samples.js new file mode 100644 index 0000000..56cead5 --- /dev/null +++ b/tools/gen_samples.js @@ -0,0 +1,37 @@ +const fs = require('fs'); +const data = require('../assets.json'); + +const header = [ + '#pragma once', + '#include "align_asset_macro.h"', + '', +] +const samples = [] + +async function main(){ + const files = Object.keys(data).filter(f => f.endsWith(".aiff") && data[f][1]['us'] != undefined).map(m => { + return { + file: m, + offset: data[m][1]['us'][1] + } + }) + + files.sort((a, b) => a.offset - b.offset); + + for(const entry of files){ + const sampleId = entry.offset.toString(16).padStart(2, '0').toUpperCase(); + samples.push(`gSample${sampleId}`); + header.push(`#define dgSample${sampleId} "__OTR__${entry.file.replace('.aiff', '')}"`); + header.push(`static const ALIGN_ASSET(2) char gSample${sampleId}[] = dgSample${sampleId};\n`); + } + + header.push('static const char* gSampleTable[] = {'); + for (const sample of samples) { + header.push(` ${sample},`); + } + header.push('};\n'); + + fs.writeFileSync('./samples_table.h', header.join('\n')); +} + +main(); \ No newline at end of file From 98b6d598324fdcb499a2cc4d37a7a6fcd19e8ead Mon Sep 17 00:00:00 2001 From: KiritoDv Date: Sat, 16 Sep 2023 03:10:56 -0600 Subject: [PATCH 6/6] Added support to extract banks on each sequence --- assets.json | 71 +++++++++++++++++----------------- sequences.json | 37 ++++++++++++++++++ src/Companion.cpp | 2 +- src/audio/AudioManager.cpp | 4 +- src/factories/AudioFactory.cpp | 34 +++++++++++----- src/factories/ResourceType.h | 3 +- tools/gen_bankset.js | 23 +++++++++++ 7 files changed, 124 insertions(+), 50 deletions(-) create mode 100644 sequences.json create mode 100644 tools/gen_bankset.js diff --git a/assets.json b/assets.json index 43208d4..eb6a14e 100644 --- a/assets.json +++ b/assets.json @@ -1086,45 +1086,44 @@ "sound/sequences/sh/20_cutscene_ending.m64": [1887,{"sh":[7981824]}], "sound/sequences/sh/21_menu_file_select.m64": [786,{"sh":[7983712]}], "sound/sequences/sh/22_cutscene_lakitu.m64": [317,{"sh":[7984512]}], -"sound/sequences/us/00_sound_player.m64": [13452,{"us":[8063360]}], -"sound/sequences/us/01_cutscene_collect_star.m64": [619,{"us":[8076816]}], -"sound/sequences/us/02_menu_title_screen.m64": [8254,{"us":[8077440]}], -"sound/sequences/us/03_level_grass.m64": [5122,{"us":[8085696]}], -"sound/sequences/us/04_level_inside_castle.m64": [2494,{"us":[8090832]}], -"sound/sequences/us/05_level_water.m64": [4780,{"us":[8093328]}], -"sound/sequences/us/06_level_hot.m64": [2451,{"us":[8098112]}], -"sound/sequences/us/07_level_boss_koopa.m64": [3418,{"us":[8100576]}], -"sound/sequences/us/08_level_snow.m64": [8143,{"us":[8104000]}], -"sound/sequences/us/09_level_slide.m64": [7432,{"us":[8112144]}], -"sound/sequences/us/0A_level_spooky.m64": [5674,{"us":[8119584]}], -"sound/sequences/us/0B_event_piranha_plant.m64": [1395,{"us":[8125264]}], -"sound/sequences/us/0C_level_underground.m64": [4887,{"us":[8126672]}], -"sound/sequences/us/0D_menu_star_select.m64": [134,{"us":[8131568]}], -"sound/sequences/us/0E_event_powerup.m64": [3129,{"us":[8131712]}], -"sound/sequences/us/0F_event_metal_cap.m64": [2770,{"us":[8134848]}], -"sound/sequences/us/10_event_koopa_message.m64": [552,{"us":[8137632]}], -"sound/sequences/us/11_level_koopa_road.m64": [4741,{"us":[8138192]}], -"sound/sequences/us/12_event_high_score.m64": [271,{"us":[8142944]}], -"sound/sequences/us/13_event_merry_go_round.m64": [1657,{"us":[8143216]}], -"sound/sequences/us/14_event_race.m64": [197,{"us":[8144880]}], -"sound/sequences/us/15_cutscene_star_spawn.m64": [644,{"us":[8145088]}], -"sound/sequences/us/16_event_boss.m64": [3435,{"us":[8145744]}], -"sound/sequences/us/17_cutscene_collect_key.m64": [671,{"us":[8149184]}], -"sound/sequences/us/18_event_endless_stairs.m64": [1777,{"us":[8149856]}], -"sound/sequences/us/19_level_boss_koopa_final.m64": [3515,{"us":[8151648]}], -"sound/sequences/us/1A_cutscene_credits.m64": [14313,{"us":[8155168]}], -"sound/sequences/us/1B_event_solve_puzzle.m64": [216,{"us":[8169488]}], -"sound/sequences/us/1C_event_toad_message.m64": [208,{"us":[8169712]}], -"sound/sequences/us/1D_event_peach_message.m64": [432,{"us":[8169920]}], -"sound/sequences/us/1E_cutscene_intro.m64": [1764,{"us":[8170352]}], -"sound/sequences/us/1F_cutscene_victory.m64": [2058,{"us":[8172128]}], -"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/sequences/us/00_sound_player.m64": [13452,{"us":[8063360], "banks": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]}], +"sound/sequences/us/01_cutscene_collect_star.m64": [619,{"us":[8076816], "banks": [34]}], +"sound/sequences/us/02_menu_title_screen.m64": [8254,{"us":[8077440], "banks": [17]}], +"sound/sequences/us/03_level_grass.m64": [5122,{"us":[8085696], "banks": [34]}], +"sound/sequences/us/04_level_inside_castle.m64": [2494,{"us":[8090832], "banks": [14]}], +"sound/sequences/us/05_level_water.m64": [4780,{"us":[8093328], "banks": [19]}], +"sound/sequences/us/06_level_hot.m64": [2451,{"us":[8098112], "banks": [15]}], +"sound/sequences/us/07_level_boss_koopa.m64": [3418,{"us":[8100576], "banks": [18]}], +"sound/sequences/us/08_level_snow.m64": [8143,{"us":[8104000], "banks": [11]}], +"sound/sequences/us/09_level_slide.m64": [7432,{"us":[8112144], "banks": [13]}], +"sound/sequences/us/0A_level_spooky.m64": [5674,{"us":[8119584], "banks": [33, 16]}], +"sound/sequences/us/0B_event_piranha_plant.m64": [1395,{"us":[8125264], "banks": [20]}], +"sound/sequences/us/0C_level_underground.m64": [4887,{"us":[8126672], "banks": [21]}], +"sound/sequences/us/0D_menu_star_select.m64": [134,{"us":[8131568], "banks": [22]}], +"sound/sequences/us/0E_event_powerup.m64": [3129,{"us":[8131712], "banks": [23]}], +"sound/sequences/us/0F_event_metal_cap.m64": [2770,{"us":[8134848], "banks": [24]}], +"sound/sequences/us/10_event_koopa_message.m64": [552,{"us":[8137632], "banks": [18]}], +"sound/sequences/us/11_level_koopa_road.m64": [4741,{"us":[8138192], "banks": [25]}], +"sound/sequences/us/12_event_high_score.m64": [271,{"us":[8142944], "banks": [31]}], +"sound/sequences/us/13_event_merry_go_round.m64": [1657,{"us":[8143216], "banks": [33]}], +"sound/sequences/us/14_event_race.m64": [197,{"us":[8144880], "banks": [26]}], +"sound/sequences/us/15_cutscene_star_spawn.m64": [644,{"us":[8145088], "banks": [14]}], +"sound/sequences/us/16_event_boss.m64": [3435,{"us":[8145744], "banks": [27]}], +"sound/sequences/us/17_cutscene_collect_key.m64": [671,{"us":[8149184], "banks": [26]}], +"sound/sequences/us/18_event_endless_stairs.m64": [1777,{"us":[8149856], "banks": [28]}], +"sound/sequences/us/19_level_boss_koopa_final.m64": [3515,{"us":[8151648], "banks": [29]}], +"sound/sequences/us/1A_cutscene_credits.m64": [14313,{"us":[8155168], "banks": [37]}], +"sound/sequences/us/1B_event_solve_puzzle.m64": [216,{"us":[8169488], "banks": [20]}], +"sound/sequences/us/1C_event_toad_message.m64": [208,{"us":[8169712], "banks": [32]}], +"sound/sequences/us/1D_event_peach_message.m64": [432,{"us":[8169920], "banks": [30]}], +"sound/sequences/us/1E_cutscene_intro.m64": [1764,{"us":[8170352], "banks": [27]}], +"sound/sequences/us/1F_cutscene_victory.m64": [2058,{"us":[8172128], "banks": [26]}], +"sound/sequences/us/20_cutscene_ending.m64": [1882,{"us":[8174192], "banks": [35]}], +"sound/sequences/us/21_menu_file_select.m64": [781,{"us":[8176080], "banks": [36]}], +"sound/sequences/us/22_cutscene_lakitu.m64": [313,{"us":[8176864], "banks": [27]}], "sound/bank_sets.bset": [159, { "us": [8177185] }], "@sound_data": { "us": { - "bank_sets": [8177184, 160], "sound_ctl": [5748512, 97856], "sound_tbl": [5846368, 2216704] } diff --git a/sequences.json b/sequences.json new file mode 100644 index 0000000..13e7895 --- /dev/null +++ b/sequences.json @@ -0,0 +1,37 @@ +{ + "00_sound_player": ["00", "01_terrain", "02_water", "03", "04", "05", "06", "07", "08_mario", "09", "0A_mario_peach"], + "01_cutscene_collect_star": ["22"], + "02_menu_title_screen": ["11"], + "03_level_grass": ["22"], + "04_level_inside_castle": ["0E"], + "05_level_water": ["13"], + "06_level_hot": ["0F"], + "07_level_boss_koopa": ["12"], + "08_level_snow": ["0B"], + "09_level_slide": ["0D"], + "0A_level_spooky": ["10", "21"], + "0B_event_piranha_plant": ["14_piranha_music_box"], + "0C_level_underground": ["15"], + "0D_menu_star_select": ["16_course_start"], + "0E_event_powerup": ["17"], + "0F_event_metal_cap": ["18"], + "10_event_koopa_message": ["12"], + "11_level_koopa_road": ["19"], + "12_event_high_score": ["1F"], + "13_event_merry_go_round": ["21"], + "14_event_race": ["1A"], + "15_cutscene_star_spawn": ["0E"], + "16_event_boss": ["1B"], + "17_cutscene_collect_key": ["1A"], + "18_event_endless_stairs": ["1C_endless_stairs"], + "19_level_boss_koopa_final": ["1D_bowser_organ"], + "1A_cutscene_credits": ["25"], + "1B_event_solve_puzzle": ["14_piranha_music_box"], + "1C_event_toad_message": ["20"], + "1D_event_peach_message": ["1E"], + "1E_cutscene_intro": ["1B"], + "1F_cutscene_victory": ["1A"], + "20_cutscene_ending": ["23"], + "21_menu_file_select": ["24"], + "22_cutscene_lakitu": ["1B"] +} diff --git a/src/Companion.cpp b/src/Companion.cpp index 05a100c..df793be 100644 --- a/src/Companion.cpp +++ b/src/Companion.cpp @@ -23,8 +23,8 @@ static const std::unordered_map gFactories = { { ".aiff", new AudioFactory() }, { ".bnk", new AudioFactory() }, { ".bset", new AudioFactory() }, + { ".m64", new AudioFactory() }, { ".bin", new BlobFactory(LUS::ResourceType::Blob) }, - { ".m64", new BlobFactory(LUS::ResourceType::Blob) }, { ".sbox", new BlobFactory(LUS::ResourceType::Blob, true) }, }; diff --git a/src/audio/AudioManager.cpp b/src/audio/AudioManager.cpp index d72bda6..2316141 100644 --- a/src/audio/AudioManager.cpp +++ b/src/audio/AudioManager.cpp @@ -335,7 +335,6 @@ AdpcmLoop AudioManager::parse_loop(uint32_t addr, std::vector& bankData int32_t count = reader.ReadInt32(); uint32_t pad = reader.ReadUInt32(); - std::cout << "Loop End: " << end << '\n'; if(count != 0){ state = std::vector(); for (size_t i = 0; i < 16; ++i) { @@ -433,6 +432,7 @@ TBLFile AudioManager::parse_tbl(std::vector& data, std::vector& void AudioManager::initialize(std::vector& 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"]; @@ -661,4 +661,4 @@ uint32_t AudioManager::get_index(AudioBankSample* entry) { std::map AudioManager::get_banks() { return this->banks; -} +} \ No newline at end of file diff --git a/src/factories/AudioFactory.cpp b/src/factories/AudioFactory.cpp index a6c5364..bd91a20 100644 --- a/src/factories/AudioFactory.cpp +++ b/src/factories/AudioFactory.cpp @@ -33,10 +33,6 @@ bool AudioFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std: WRITE_HEADER(LUS::ResourceType::Bank, 0); - if(path.find("sound/banks/us/20_bank") != std::string::npos){ - int bp = 0; - } - WRITE_U32(bank.insts.size()); for(auto &instrument : bank.insts){ WRITE_U8(instrument.valid); @@ -119,7 +115,29 @@ bool AudioFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std: return true; } - if(metadata.size() < 2 || !metadata[1].contains("us")){ + if(path.find("sound/sequences") != std::string::npos){ + + if(!metadata[1].contains("us")){ + return false; + } + + WRITE_HEADER(LUS::ResourceType::Sequence, 0); + + size_t size = metadata[0]; + auto offsets = metadata[1]["us"]; + + std::vector bank = metadata[1]["banks"]; + + WRITE_U32(bank.size()); + WRITE_ARRAY(bank.data(), bank.size()); + + WRITE_U32(size); + WRITE_ARRAY(buffer.data() + (size_t)offsets[0], size); + + return true; + } + + if(path.find("sound/samples") == std::string::npos || metadata.size() < 2 || !metadata[1].contains("us")){ return false; } @@ -132,14 +150,10 @@ bool AudioFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std: write_aiff(aifcData, *writer); */ - WRITE_HEADER(LUS::ResourceType::Audio, 0); + WRITE_HEADER(LUS::ResourceType::Sample, 0); AudioBankSample entry = AudioManager::Instance->get_aifc(metadata[1]["us"][1]); - if(path.find("00_mario_waaaooow") != std::string::npos){ - int bp = 0; - } - // Write AdpcmLoop WRITE_U32(entry.loop.start); WRITE_U32(entry.loop.end); diff --git a/src/factories/ResourceType.h b/src/factories/ResourceType.h index ee8a9f6..7dfac06 100644 --- a/src/factories/ResourceType.h +++ b/src/factories/ResourceType.h @@ -20,6 +20,7 @@ enum class ResourceType { Skybox = 0x534B5942, // SKYB Anim = 0x414E494D, // ANIM Bank = 0x42414E4B, // BANK - Audio = 0x41554643, // AIFC + Sample = 0x41554643, // AIFC + Sequence = 0x53455143, // SEQC }; } // namespace LUS diff --git a/tools/gen_bankset.js b/tools/gen_bankset.js new file mode 100644 index 0000000..4b07141 --- /dev/null +++ b/tools/gen_bankset.js @@ -0,0 +1,23 @@ +const fs = require('fs'); +const data = require('../sequences.json'); + +const header = [ + '#pragma once', + '', +] +const bankset = [] + +async function main(){ + // header.push('static const uint8_t gBankIdxMap[][16] = {'); + let idx = 0; + for(const values of Object.values(data)){ + const indices = values.map(v => `${parseInt(v.split('_')[0], 16)}`); + // header.push(` // Bank: ${Object.keys(data)[idx++]}`); + header.push(`[${indices.join(', ')}],`); + } + // header.push('};\n'); + + fs.writeFileSync('./bankset_table.h', header.join('\n')); +} + +main(); \ No newline at end of file