From 1d485d7695699dbfe28407a62c312b6c3732d4a2 Mon Sep 17 00:00:00 2001 From: KiritoDv Date: Thu, 28 Nov 2024 16:18:45 -0600 Subject: [PATCH] Fixed aifc extraction on naudio v0 --- .../naudio/v0/AudioHeaderFactory.cpp | 31 ++- src/factories/naudio/v0/AudioHeaderFactory.h | 7 +- src/factories/naudio/v0/AudioManager.cpp | 180 +++--------------- src/factories/naudio/v0/AudioManager.h | 18 +- src/factories/naudio/v1/AudioConverter.cpp | 88 ++++++++- src/factories/naudio/v1/AudioConverter.h | 4 +- src/factories/naudio/v1/SampleFactory.cpp | 2 +- 7 files changed, 156 insertions(+), 174 deletions(-) diff --git a/src/factories/naudio/v0/AudioHeaderFactory.cpp b/src/factories/naudio/v0/AudioHeaderFactory.cpp index dc4d547c..1cd3d7cb 100644 --- a/src/factories/naudio/v0/AudioHeaderFactory.cpp +++ b/src/factories/naudio/v0/AudioHeaderFactory.cpp @@ -1,7 +1,36 @@ #include "AudioHeaderFactory.h" #include -#include "AudioManager.h" +#include "Companion.h" +#include "AIFCDecode.h" +#include "spdlog/spdlog.h" +#include + +ExportResult AudioAIFCExporter::Export(std::ostream& write, std::shared_ptr data, std::string& entryName, YAML::Node& node, std::string* replacement) { + + auto samples = AudioManager::Instance->get_samples(); + + int temp = 0; + for(auto& sample : samples){ + std::string dpath = Companion::Instance->GetOutputPath() + "/" + (*replacement); + if(!exists(fs::path(dpath).parent_path())){ + create_directories(fs::path(dpath).parent_path()); + } + std::ofstream file(dpath + "_bank_" + std::to_string(++temp) + ".aiff", std::ios::binary); + + LUS::BinaryWriter aifc = LUS::BinaryWriter(); + AudioConverter::SampleV0ToAIFC(sample, aifc); + + LUS::BinaryWriter aiff = LUS::BinaryWriter(); + write_aiff(aifc.ToVector(), aiff); + aifc.Close(); + aiff.Finish(file); + file.close(); + SPDLOG_INFO("Exported {}", dpath + "_bank_" + std::to_string(temp) + ".aiff"); + } + + return std::nullopt; +} std::optional> AudioHeaderFactory::parse(std::vector& buffer, YAML::Node& data) { AudioManager::Instance->initialize(buffer, data); diff --git a/src/factories/naudio/v0/AudioHeaderFactory.h b/src/factories/naudio/v0/AudioHeaderFactory.h index 1c5c0b19..0d09d73b 100644 --- a/src/factories/naudio/v0/AudioHeaderFactory.h +++ b/src/factories/naudio/v0/AudioHeaderFactory.h @@ -2,6 +2,11 @@ #include +class AudioAIFCExporter : public BaseExporter { +public: + ExportResult Export(std::ostream& write, std::shared_ptr data, std::string& entryName, YAML::Node& node, std::string* replacement); +}; + class AudioDummyExporter : public BaseExporter { public: ExportResult Export(std::ostream& write, std::shared_ptr data, std::string& entryName, YAML::Node& node, std::string* replacement) override { @@ -17,7 +22,7 @@ class AudioHeaderFactory : public BaseFactory { } std::unordered_map> GetExporters() override { return { - REGISTER(Modding, AudioDummyExporter) + REGISTER(Modding, AudioAIFCExporter) REGISTER(Header, AudioDummyExporter) REGISTER(Binary, AudioDummyExporter) REGISTER(Code, AudioDummyExporter) diff --git a/src/factories/naudio/v0/AudioManager.cpp b/src/factories/naudio/v0/AudioManager.cpp index c168a39a..27523087 100644 --- a/src/factories/naudio/v0/AudioManager.cpp +++ b/src/factories/naudio/v0/AudioManager.cpp @@ -128,9 +128,11 @@ Bank AudioManager::parse_ctl(CTLHeader header, std::vector data, Sample for (size_t i = 0; i < numDrums; ++i) { uint32_t drumOffset; memcpy(&drumOffset, rawData + drumBaseAddr + i * 4, 4); - drumOffset = BSWAP32(drumOffset); - assert(drumOffset != 0); - drumOffsets.push_back(drumOffset); + if(drumOffset == 0){ + continue; + } + + drumOffsets.push_back(BSWAP32(drumOffset)); } } else { assert(drumBaseAddr == 0); @@ -382,7 +384,14 @@ AudioBankSample* AudioManager::parse_sample(std::vector& data, std::vec uint32_t loop = reader.ReadUInt32(); uint32_t book = reader.ReadUInt32(); uint32_t sampleSize = reader.ReadUInt32(); - assert(zero == 0); + + SPDLOG_INFO("Zero: 0x{:X}", zero); + SPDLOG_INFO("Addr: 0x{:X}", addr); + SPDLOG_INFO("Loop: 0x{:X}", loop); + SPDLOG_INFO("Book: 0x{:X}", book); + SPDLOG_INFO("Sample Size: {}", sampleSize); + + // assert(zero == 0); assert(loop != 0); assert(book != 0); @@ -479,154 +488,6 @@ void AudioManager::initialize(std::vector& buffer, YAML::Node& data) { } } -void serialize_f80(double num, LUS::BinaryWriter &writer) { - // Convert the input double to an 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((uint32_t) BSWAP32(section)); \ - LUS::BinaryWriter tmp = LUS::BinaryWriter(); \ - tmp.SetEndianness(Torch::Endianness::Big); \ - -#define START_CUSTOM_SECTION(section) \ - { \ - LUS::BinaryWriter tmp = LUS::BinaryWriter(); \ - tmp.SetEndianness(Torch::Endianness::Big); \ - out.Write((uint32_t) 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((uint32_t) BSWAP32((uint32_t) size)); \ - out.Write(odata.data(), odata.size()); \ - if(size % 2){ \ - out.WriteByte(0); \ - } \ - } \ - -void AudioManager::write_aifc(AudioBankSample* 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((uint32_t) 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((uint32_t) 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("\x0bVADPCM ~4-1", false); - - END_SECTION(); - - START_SECTION(AIFC::MagicValues::INST) - tmp.Write(std::string(20, '\0'), false); - END_SECTION(); - - START_CUSTOM_SECTION("\x0bVADPCMCODES") - 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("\x0bVADPCMLOOPS") - uint16_t one = BSWAP16(1); - tmp.Write(reinterpret_cast(&one), 2); - tmp.Write(reinterpret_cast(&one), 2); - tmp.Write(entry->loop.start); - tmp.Write(entry->loop.end); - tmp.Write(entry->loop.count); - for(size_t i = 0; i < 16; i++){ - int16_t loop = BSWAP16(entry->loop.state.value()[i]); - tmp.Write(reinterpret_cast(&loop), 2); - } - END_SECTION(); - } - - len += 4; - out.Seek(4, LUS::SeekOffsetType::Start); - out.Write((uint32_t) BSWAP32(len)); - -} - void AudioManager::bind_sample(YAML::Node& node, const std::string& path){ auto id = GetSafeNode(node, "id"); sample_table[id] = path; @@ -639,6 +500,7 @@ std::string& AudioManager::get_sample(uint32_t id) { return sample_table[id]; } +/* void AudioManager::create_aifc(int32_t index, LUS::BinaryWriter &out) { int32_t idx = -1; for(auto &sample_bank : this->loaded_tbl.banks){ @@ -653,6 +515,7 @@ void AudioManager::create_aifc(int32_t index, LUS::BinaryWriter &out) { } } } +*/ AudioBankSample AudioManager::get_aifc(int32_t index) { int32_t idx = 0; @@ -680,4 +543,17 @@ uint32_t AudioManager::get_index(AudioBankSample* entry) { std::map AudioManager::get_banks() { return this->banks; +} + +std::vector AudioManager::get_samples() { + std::vector samples; + for(auto &bank : this->loaded_tbl.banks){ + for(auto &entry : bank->entries){ + // Avoid duplicates + if(std::find(samples.begin(), samples.end(), entry.second) == samples.end()){ + samples.push_back(entry.second); + } + } + } + return samples; } \ No newline at end of file diff --git a/src/factories/naudio/v0/AudioManager.h b/src/factories/naudio/v0/AudioManager.h index b5a9dcc3..0d072bc6 100644 --- a/src/factories/naudio/v0/AudioManager.h +++ b/src/factories/naudio/v0/AudioManager.h @@ -12,19 +12,6 @@ #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; @@ -139,12 +126,11 @@ class AudioManager { static AudioManager* Instance; void initialize(std::vector& buffer, YAML::Node& data); void bind_sample(YAML::Node& node, const std::string& path); - void create_aifc(int32_t index, LUS::BinaryWriter& writer); std::string& get_sample(uint32_t id); AudioBankSample get_aifc(int32_t index); std::map get_banks(); + std::vector get_samples(); uint32_t get_index(AudioBankSample* bank); - private: std::map banks; std::map sampleMap; @@ -161,6 +147,4 @@ class AudioManager { 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(AudioBankSample* entry, LUS::BinaryWriter& writer); }; \ No newline at end of file diff --git a/src/factories/naudio/v1/AudioConverter.cpp b/src/factories/naudio/v1/AudioConverter.cpp index f4764224..62d14708 100644 --- a/src/factories/naudio/v1/AudioConverter.cpp +++ b/src/factories/naudio/v1/AudioConverter.cpp @@ -8,6 +8,7 @@ #include #include #include +#include "hj/pyutils.h" void AIFCWriter::End(std::string chunk, LUS::BinaryWriter& writer) { auto buffer = writer.ToVector(); @@ -79,7 +80,92 @@ void SerializeF80(double num, LUS::BinaryWriter &writer) { writer.Write(low); } -void AudioConverter::SampleToAIFC(NSampleData* sample, LUS::BinaryWriter &out) { +void AudioConverter::SampleV0ToAIFC(AudioBankSample* sample, LUS::BinaryWriter &out) { + auto aifc = AIFCWriter(); + auto data = sample->data; + + uint32_t num_frames = data.size() * 16 / 9; + uint32_t sample_rate = -1; + + if(sample->tunings.size() == 1){ + sample_rate = 32000 * sample->tunings[0]; + } else { + float tmin = PyUtils::min(sample->tunings); + float tmax = PyUtils::max(sample->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); + } + } + + int16_t num_channels = 1; + int16_t sample_size = 16; + + // COMM Chunk + auto comm = aifc.Start(); + comm.Write(num_channels); + comm.Write(num_frames); + comm.Write(sample_size); + SerializeF80(sample_rate, comm); + comm.Write(AIFCMagicValues::VAPC); + comm.Write((char*) "\x0bVADPCM ~4-1", 12); + aifc.End("COMM", comm); + + // INST Chunk + auto inst = aifc.Start(); + for(size_t i = 0; i < 5; i++){ + inst.Write((int32_t) 0); + } + aifc.End("INST", inst); + + // VADPCMCODES Chunk + auto vcodes = aifc.Start(); + vcodes.Write((char*) "stoc\x0bVADPCMCODES", 16); + vcodes.Write((int16_t) 1); + vcodes.Write((int16_t) sample->book.order); + vcodes.Write((int16_t) sample->book.npredictors); + + for(auto page : sample->book.table){ + vcodes.Write(page); + } + aifc.End("APPL", vcodes); + + // SSND Chunk + auto ssnd = aifc.Start(); + ssnd.Write((uint64_t) 0); + ssnd.Write((char*) data.data(), data.size()); + aifc.End("SSND", ssnd); + + // VADPCMLOOPS + if(sample->loop.count != 0){ + auto vloops = aifc.Start(); + vloops.Write((char*) "stoc\x0bVADPCMLOOPS", 16); + vloops.Write((uint16_t) 1); + vloops.Write((uint16_t) 1); + vloops.Write(sample->loop.start); + vloops.Write(sample->loop.end); + vloops.Write(sample->loop.count); + + if(sample->loop.state.has_value()){ + for(auto state : sample->loop.state.value()){ + vcodes.Write(state); + } + } + aifc.End("APPL", vloops); + } + + aifc.Close(out); +} + +void AudioConverter::SampleV1ToAIFC(NSampleData* sample, LUS::BinaryWriter &out) { auto loop = std::static_pointer_cast(Companion::Instance->GetParseDataByAddr(sample->loop)->data.value()); auto book = std::static_pointer_cast(Companion::Instance->GetParseDataByAddr(sample->book)->data.value()); auto entry = AudioContext::tableData[AudioTableType::SAMPLE_TABLE]->entries[sample->sampleBankId]; diff --git a/src/factories/naudio/v1/AudioConverter.h b/src/factories/naudio/v1/AudioConverter.h index ef87a0da..1840ab0c 100644 --- a/src/factories/naudio/v1/AudioConverter.h +++ b/src/factories/naudio/v1/AudioConverter.h @@ -2,6 +2,7 @@ #include #include +#include enum AIFCMagicValues { FORM = (uint32_t) 0x464f524d, @@ -15,7 +16,8 @@ enum AIFCMagicValues { class AudioConverter { public: - static void SampleToAIFC(NSampleData* tSample, LUS::BinaryWriter &out); + static void SampleV0ToAIFC(AudioBankSample* entry, LUS::BinaryWriter &out); + static void SampleV1ToAIFC(NSampleData* tSample, LUS::BinaryWriter &out); }; struct AIFCChunk { diff --git a/src/factories/naudio/v1/SampleFactory.cpp b/src/factories/naudio/v1/SampleFactory.cpp index b35467ea..47e91b7b 100644 --- a/src/factories/naudio/v1/SampleFactory.cpp +++ b/src/factories/naudio/v1/SampleFactory.cpp @@ -48,7 +48,7 @@ ExportResult NSampleModdingExporter::Export(std::ostream &write, std::shared_ptr *replacement += ".aiff"; auto aifc = LUS::BinaryWriter(); - AudioConverter::SampleToAIFC(data.get(), aifc); + AudioConverter::SampleV1ToAIFC(data.get(), aifc); auto cnv = aifc.ToVector(); if(!cnv.empty()){