diff options
| author | KiritoDv <kiritodev01@gmail.com> | 2023-09-03 20:11:35 -0600 |
|---|---|---|
| committer | KiritoDv <kiritodev01@gmail.com> | 2023-09-03 20:11:35 -0600 |
| commit | 063224c69d564d6e10dd4febf25caa9f59fccfda (patch) | |
| tree | c1edbd90c120d7b846490159540f68adfddfbb5a /src | |
| parent | 113472302904ac84d4ddf70df32ca245ad156bf2 (diff) | |
Fixed AIFC Extraction
Diffstat (limited to 'src')
| -rw-r--r-- | src/Companion.cpp | 6 | ||||
| -rw-r--r-- | src/audio/AIFCWriter.cpp | 48 | ||||
| -rw-r--r-- | src/audio/AIFCWriter.h | 36 | ||||
| -rw-r--r-- | src/audio/AudioManager.cpp | 231 | ||||
| -rw-r--r-- | src/audio/AudioManager.h | 23 | ||||
| -rw-r--r-- | src/factories/AudioFactory.cpp | 9 |
6 files changed, 309 insertions, 44 deletions
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<char> AIFCWriter::pack(const std::string& str) { + std::vector<char> result; + result.insert(result.end(), str.begin(), str.end()); + return result; +} + +std::vector<char> AIFCWriter::pstring(const std::vector<char>& data) { + std::vector<char> 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<char>& 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<char>& 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 <iostream> +#include <vector> +#include <string> +#include <cstdint> +#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<char> pack(const std::string& str); + static std::vector<char> pstring(const std::vector<char>& data); + void add_section(AIFC::MagicValues, const std::vector<char>& data); + void add_custom_section(const std::string& section, const std::vector<char>& data); + void finish(); + +private: + LUS::BinaryWriter& out; + std::vector<std::pair<AIFC::MagicValues, std::vector<char>>> 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 <nlohmann/json.hpp> std::unordered_map<std::string, uint32_t> name_table; AudioManager* AudioManager::Instance; +std::vector<uint32_t> PyUtils::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; +} + 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<Entry> AudioManager::parse_seq_file(std::vector<uint8_t>& buffer, uint32_t offset, bool isCTL){ @@ -198,7 +206,7 @@ Book AudioManager::parse_book(uint32_t addr, std::vector<uint8_t>& bankData){ return book; } -AifcEntry AudioManager::parse_sample(std::vector<uint8_t>& data, std::vector<uint8_t>& bankData, SampleBank& sampleBank){ +AifcEntry* AudioManager::parse_sample(std::vector<uint8_t>& data, std::vector<uint8_t>& bankData, SampleBank* sampleBank){ LUS::BinaryReader reader((char*) data.data(), data.size()); reader.SetEndianness(LUS::Endianness::Big); @@ -215,7 +223,7 @@ AifcEntry AudioManager::parse_sample(std::vector<uint8_t>& data, std::vector<uin Book bookData = parse_book(book, bankData); reader.Close(); - return sampleBank.AddSample(addr, sampleSize, bookData, loopData); + return sampleBank->AddSample(addr, sampleSize, bookData, loopData); } @@ -239,7 +247,7 @@ std::vector<EnvelopeData> AudioManager::parse_envelope(uint32_t addr, std::vecto return entries; } -Bank AudioManager::parse_ctl(CTLHeader header, std::vector<uint8_t> data, SampleBank bank, uint32_t index) { +Bank AudioManager::parse_ctl(CTLHeader header, std::vector<uint8_t> data, SampleBank* bank, uint32_t index) { name_table.clear(); std::ostringstream ss; ss << std::hex << std::setw(2) << std::setfill('0') << index; @@ -333,13 +341,13 @@ Bank AudioManager::parse_ctl(CTLHeader header, std::vector<uint8_t> data, Sample allInsts.emplace_back(drums); } - std::unordered_map<uint32_t, AifcEntry> samples; + std::unordered_map<uint32_t, AifcEntry*> samples; std::sort(sampleOffsets.begin(), sampleOffsets.end()); for(auto &offset : sampleOffsets){ auto rSample = PyUtils::slice(data, offset, offset + 20); - AifcEntry sample = parse_sample(rSample, data, bank); + 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<uint8_t>& data, std::vector<Entry>& 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<uint8_t>& buffer) { std::vector<uint8_t> tbl_data = PyUtils::slice(buffer, sound_tbl[0], (size_t) sound_tbl[0] + (size_t) sound_tbl[1]); std::vector<uint8_t> ctl_data = PyUtils::slice(buffer, sound_ctl[0], (size_t) sound_ctl[0] + (size_t) sound_ctl[1]); - TBLFile tbl_file = parse_tbl(tbl_data, tbl); + 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 <string> #include <iostream> #include <unordered_map> +#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<uint8_t> data; - std::unordered_map<uint32_t, AifcEntry> entries; + std::unordered_map<uint32_t, AifcEntry*> 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<SampleBank> banks; + std::vector<SampleBank*> banks; std::vector<std::string> tbls; - std::unordered_map<std::string, uint32_t> map; + std::unordered_map<std::string, SampleBank*> map; }; struct CTLHeader { @@ -103,13 +104,13 @@ struct Envelope { struct Bank { std::string name; - SampleBank sampleBank; + SampleBank* sampleBank; std::vector<Inst> insts; std::vector<Drum> drums; std::vector<std::variant<Inst, std::vector<Drum>>> allInsts; std::vector<uint32_t> instOffsets; std::unordered_map<uint32_t, Envelope> envelopes; - std::unordered_map<uint32_t, AifcEntry> samples; + std::unordered_map<uint32_t, AifcEntry*> samples; void print() const; }; @@ -117,8 +118,12 @@ class AudioManager { public: static AudioManager* Instance; void initialize(std::vector<uint8_t>& buffer); + void create_aifc(int32_t index, LUS::BinaryWriter& writer); private: + std::vector<Bank> banks; + TBLFile loaded_tbl; + static std::vector<Entry> parse_seq_file(std::vector<uint8_t>& buffer, uint32_t offset, bool isCTL); static CTLHeader parse_ctl_header(std::vector<uint8_t>& data); static std::optional<Sound> parse_sound(std::vector<uint8_t> data); @@ -126,10 +131,10 @@ private: static Inst parse_inst(std::vector<uint8_t>& data, uint32_t addr); static Loop parse_loop(uint32_t addr, std::vector<uint8_t>& bankData); static Book parse_book(uint32_t addr, std::vector<uint8_t>& bankData); - static AifcEntry parse_sample(std::vector<uint8_t>& data, std::vector<uint8_t>& bankData, SampleBank& sampleBank); + static AifcEntry* parse_sample(std::vector<uint8_t>& data, std::vector<uint8_t>& bankData, SampleBank* sampleBank); static std::vector<EnvelopeData> parse_envelope(uint32_t addr, std::vector<uint8_t>& dataBank); - static Bank parse_ctl(CTLHeader header, std::vector<uint8_t> data, SampleBank bank, uint32_t index); + static Bank parse_ctl(CTLHeader header, std::vector<uint8_t> data, SampleBank* bank, uint32_t index); static TBLFile parse_tbl(std::vector<uint8_t>& data, std::vector<Entry>& entries); - std::vector<Bank> banks; + 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 <vector> +#include "audio/AudioManager.h" #include "binarytools/BinaryReader.h" bool AudioFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std::vector<uint8_t>& 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; } |
