[WIP] Audio Extractor

This commit is contained in:
KiritoDv
2023-08-30 21:26:38 -06:00
parent f5cbd0056d
commit 3bb2a8a13c
8 changed files with 534 additions and 12 deletions
+8 -2
View File
@@ -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]}],
+141
View File
@@ -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,
};
+9 -5
View File
@@ -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<std::string, RawFactory*> 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();
}
+333
View File
@@ -0,0 +1,333 @@
#include "AudioFactory.h"
#include <vector>
#include <iostream>
#include <filesystem>
#include <sstream>
#include "Companion.h"
#include "utils/MIODecoder.h"
#include "binarytools/BinaryReader.h"
namespace fs = std::filesystem;
#define NONE 0xFFFF
std::unordered_map<std::string, uint32_t> nameTable;
struct ALSeqData {
uint8_t *offset;
uint32_t len;
};
struct Entry {
uint32_t offset;
uint32_t length;
};
struct SampleBank {
std::string name;
uint32_t offset;
std::vector<uint8_t> data;
};
struct TBLFile {
std::vector<SampleBank> banks;
std::vector<std::string> tbls;
std::unordered_map<std::string, uint32_t> map;
};
struct CTLHeader {
uint32_t instruments;
uint32_t numDrums;
uint32_t shared;
};
struct Sound {
int32_t offset;
float tuning;
};
struct Bank {
std::string name;
SampleBank sampleBank;
};
struct Inst {
std::string name;
uint32_t offset;
uint8_t releaseRate;
uint8_t normalRangeLo;
uint8_t normalRangeHi;
int32_t envelope;
std::optional<Sound> soundLo;
std::optional<Sound> soundMed;
std::optional<Sound> soundHi;
};
struct Drum {
std::string name;
uint32_t offset;
uint8_t releaseRate;
uint8_t pan;
int32_t envelope;
Sound sound;
};
template<typename T>
std::vector<T> slice(std::vector<T> const &v, uint32_t m, uint32_t n) {
auto first = v.cbegin() + m;
auto last = v.cbegin() + n;
std::vector<T> vec(first, last);
return vec;
}
std::vector<uint32_t> range(uint32_t start, uint32_t end) {
std::vector<uint32_t> result;
for (uint32_t i = start; i < end; ++i) {
result.push_back(i);
}
return result;
}
template <typename T1, typename T2, typename T3>
std::vector<std::tuple<size_t, T2, T3>> zip(const std::vector<T1>& container1, const std::vector<T2>& container2, const std::vector<T3>& container3) {
std::vector<std::tuple<size_t, T2, T3>> result;
size_t minSize = std::min({container1.size(), container2.size(), container3.size()});
for (size_t i = 0; i < minSize; ++i) {
result.emplace_back(container1[i], container2[i], container3[i]);
}
return result;
}
std::vector<Entry> parseSeqFile(std::vector<uint8_t>& buffer, int32_t offset){
std::vector<Entry> entries;
LUS::BinaryReader reader((char*) buffer.data(), buffer.size());
reader.SetEndianness(LUS::Endianness::Big);
reader.Seek(offset, LUS::SeekOffsetType::Start);
uint16_t magic = reader.ReadUInt16();
uint16_t num_entries = reader.ReadUInt16();
for (int i = 0; i < num_entries; ++i) {
reader.Seek((offset + 4) + (i * 8), LUS::SeekOffsetType::Start);
entries.push_back({reader.ReadUInt32(), reader.ReadUInt32()});
}
reader.Close();
return entries;
}
CTLHeader parseCTLHeader(std::vector<uint8_t>& data){
LUS::BinaryReader reader((char*) data.data(), data.size());
reader.SetEndianness(LUS::Endianness::Big);
CTLHeader header = { reader.ReadUInt32(), reader.ReadUInt32(), reader.ReadUInt32() };
reader.Close();
return header;
}
std::string gen_name(const std::string& prefix){
if(!nameTable.contains(prefix)){
nameTable[prefix] = 0;
}
nameTable[prefix] += 1;
return prefix + "-" + std::to_string(nameTable[prefix]);
}
std::optional<Sound> parseSound(std::vector<uint8_t> data) {
LUS::BinaryReader reader((char*) data.data(), data.size());
reader.SetEndianness(LUS::Endianness::Big);
int32_t addr = reader.ReadInt32();
float tuning = reader.ReadFloat();
if(addr == 0){
assert(tuning == 0.0f);
return std::nullopt;
}
Sound sound = { addr, tuning };
reader.Close();
return sound;
}
Drum parseDrum(std::vector<uint8_t>& data, uint32_t addr) {
LUS::BinaryReader reader((char*) data.data(), data.size());
reader.SetEndianness(LUS::Endianness::Big);
std::string name = gen_name("drum");
uint8_t releaseRate = reader.ReadInt8();
uint8_t pan = reader.ReadInt8();
reader.Seek(12, LUS::SeekOffsetType::Start);
Sound sound = parseSound(slice(data, 4, 12)).value();
int32_t envOffset = reader.ReadInt32();
assert(envOffset != 0);
Drum drum = { name, addr, releaseRate, pan, envOffset, sound };
return drum;
}
Inst parseInst(std::vector<uint8_t>& data, uint32_t addr) {
std::string name = gen_name("inst");
char* rawData = (char*) data.data();
uint8_t normalRangeLo = data[1];
uint8_t normalRangeHi = data[2];
uint8_t releaseRate = data[3];
int32_t envAddr;
memcpy(&envAddr, rawData + 4, 4);
envAddr = BSWAP32(envAddr);
auto soundLo = parseSound(slice(data, 8, 16));
auto soundMed = parseSound(slice(data, 16, 24));
auto soundHi = parseSound(slice(data, 24, 32));
if (soundLo == std::nullopt) {
assert(normalRangeLo == 0);
}
if (soundHi == std::nullopt) {
assert(normalRangeHi == 127);
}
Inst inst = { name, addr, releaseRate, normalRangeLo, normalRangeHi, envAddr, soundLo, soundMed, soundHi };
return inst;
}
void parseCTL(CTLHeader header, std::vector<uint8_t>& data, SampleBank bank, uint32_t index) {
nameTable.clear();
std::ostringstream ss;
ss << std::hex << std::setw(2) << std::setfill('0') << index;
std::string name = ss.str();
uint32_t numInstruments = header.instruments;
uint32_t numDrums = header.numDrums;
char* rawData = (char*) data.data();
uint32_t drumBaseAddr;
memcpy(&drumBaseAddr, rawData, 4);
drumBaseAddr = BSWAP32(drumBaseAddr);
std::vector<uint32_t> drumOffsets;
if(numDrums != 0){
for (size_t i = 0; i < numDrums; ++i) {
uint32_t drumOffset;
memcpy(&drumOffset, rawData + drumBaseAddr + (i * 4), 4);
drumOffsets.push_back(BSWAP32(drumOffset));
}
}
uint32_t instrumentBaseAddr = 4;
std::vector<uint32_t> instrumentOffsets;
std::vector<uint32_t> instrumentList;
for (size_t i = 0; i < numInstruments; ++i) {
uint32_t instOffset;
memcpy(&instOffset, rawData + instrumentBaseAddr + (i * 4), 4);
instOffset = BSWAP32(instOffset);
if(instOffset == 0){
instrumentList.push_back(NONE);
} else {
instrumentOffsets.push_back(instOffset);
instrumentList.push_back(instOffset);
}
}
std::sort(instrumentOffsets.begin(), instrumentOffsets.end());
std::vector<Inst> insts;
for(auto &offset : instrumentOffsets){
auto rInst = slice(data, offset, offset + 32);
Inst inst = parseInst(rInst, offset);
insts.push_back(inst);
}
std::vector<Drum> drums;
for(auto &offset : drumOffsets){
auto rDrum = slice(data, offset, offset + 16);
Drum drum = parseDrum(rDrum, offset);
drums.push_back(drum);
}
}
TBLFile parseTBL(std::vector<uint8_t>& data, std::vector<Entry>& entries) {
TBLFile tbl;
std::unordered_map<uint32_t, std::string> cache;
for(auto &entry : entries){
if(!cache.contains(entry.offset)){
std::string name = gen_name("sample_bank");
tbl.banks.push_back({name, entry.offset, slice(data, entry.offset, entry.offset + entry.length)});
tbl.map[name] = tbl.banks.size() - 1;
cache[entry.offset] = name;
}
tbl.tbls.push_back(cache[entry.offset]);
}
cache.clear();
return tbl;
}
bool AudioFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std::vector<uint8_t>& buffer) {
auto metadata = data["offsets"];
std::string path = data["path"];
if(path.find("@sound") != std::string::npos){
auto offsets = metadata["us"];
std::vector<Entry> tbl = parseSeqFile(buffer, offsets["sound_tbl"][0]);
std::vector<Entry> ctl = parseSeqFile(buffer, offsets["sound_ctl"][0]);
std::cout << "Table entries: " << tbl.size() << std::endl;
std::cout << "Control entries: " << ctl.size() << std::endl;
std::vector<uint8_t> tblData = slice(buffer, offsets["sound_tbl"][0], (size_t) offsets["sound_tbl"][0] + (size_t) offsets["sound_tbl"][1]);
std::vector<uint8_t> ctlData = slice(buffer, offsets["sound_ctl"][0], (size_t) offsets["sound_ctl"][0] + (size_t) offsets["sound_ctl"][1]);
TBLFile tblFile = parseTBL(tblData, tbl);
std::cout << "TBLs: " << tblFile.tbls.size() << std::endl;
std::cout << "Banks: " << tblFile.banks.size() << std::endl;
std::cout << "Map: " << tblFile.map.size() << std::endl;
auto zipped = zip(range(0, ctl.size()), ctl, tblFile.tbls);
for (const auto& item : zipped) {
auto [index, ctrl, sample_bank_name] = item;
auto sample_bank = tblFile.map[sample_bank_name];
auto entry = slice(ctlData, ctrl.offset, ctrl.offset + ctrl.length);
auto headerRaw = slice(entry, 0, 16);
auto header = parseCTLHeader(headerRaw);
std::cout << "Instruments: " << header.instruments << std::endl;
std::cout << "Drums: " << header.numDrums << std::endl;
}
exit(0);
}
if(path.find("bank_sets") != std::string::npos){
WRITE_HEADER(LUS::ResourceType::Blob, 0);
char* raw = (char*) (buffer.data() + metadata[1]["us"][0]);
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<Entry> entries = parseSeqFile(buffer, metadata[1]["us"][0]);
// for(auto & entry : entries){
// std::cout << "Offset: " << entrie.offset << std::endl;
// std::cout << "Len: " << entrie.length << std::endl;
// }
}
return false;
}
+9
View File
@@ -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<uint8_t>& buffer) override;
};
+1 -5
View File
@@ -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"];
+1
View File
@@ -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)
+32
View File
@@ -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'));