diff options
| author | KiritoDv <kiritodev01@gmail.com> | 2024-11-21 21:40:38 -0600 |
|---|---|---|
| committer | KiritoDv <kiritodev01@gmail.com> | 2024-11-21 21:40:38 -0600 |
| commit | a9662a158ff7764ecbefd1b88280d3854f3c4fea (patch) | |
| tree | 0390161cd5d7cf7e34a28fa3a150533253465fb4 /src | |
| parent | 1537229c694c0a91ec37fb7389bdcf3e3968cd71 (diff) | |
Added sample extraction to aiff
Diffstat (limited to 'src')
| -rw-r--r-- | src/Companion.h | 2 | ||||
| -rw-r--r-- | src/factories/naudio/v0/AIFCDecode.cpp | 22 | ||||
| -rw-r--r-- | src/factories/naudio/v1/AudioContext.cpp | 1 | ||||
| -rw-r--r-- | src/factories/naudio/v1/AudioContext.h | 2 | ||||
| -rw-r--r-- | src/factories/naudio/v1/AudioConverter.cpp | 160 | ||||
| -rw-r--r-- | src/factories/naudio/v1/AudioConverter.h | 38 | ||||
| -rw-r--r-- | src/factories/naudio/v1/AudioTableFactory.h | 2 | ||||
| -rw-r--r-- | src/factories/naudio/v1/BookFactory.cpp | 4 | ||||
| -rw-r--r-- | src/factories/naudio/v1/BookFactory.h | 2 | ||||
| -rw-r--r-- | src/factories/naudio/v1/DrumFactory.h | 2 | ||||
| -rw-r--r-- | src/factories/naudio/v1/EnvelopeFactory.h | 2 | ||||
| -rw-r--r-- | src/factories/naudio/v1/InstrumentFactory.h | 2 | ||||
| -rw-r--r-- | src/factories/naudio/v1/LoopFactory.h | 2 | ||||
| -rw-r--r-- | src/factories/naudio/v1/SampleFactory.cpp | 21 | ||||
| -rw-r--r-- | src/factories/naudio/v1/SampleFactory.h | 7 | ||||
| -rw-r--r-- | src/factories/naudio/v1/SoundFontFactory.h | 2 |
16 files changed, 254 insertions, 17 deletions
diff --git a/src/Companion.h b/src/Companion.h index f169786..eb51783 100644 --- a/src/Companion.h +++ b/src/Companion.h @@ -127,7 +127,7 @@ public: bool IsDebug() const { return this->gConfig.debug; } N64::Cartridge* GetCartridge() const { return this->gCartridge.get(); } - std::vector<uint8_t> GetRomData() { return this->gRomData; } + std::vector<uint8_t>& GetRomData() { return this->gRomData; } std::string GetOutputPath() { return this->gConfig.outputPath; } GBIVersion GetGBIVersion() const { return this->gConfig.gbi.version; } diff --git a/src/factories/naudio/v0/AIFCDecode.cpp b/src/factories/naudio/v0/AIFCDecode.cpp index e627ad0..b1c9329 100644 --- a/src/factories/naudio/v0/AIFCDecode.cpp +++ b/src/factories/naudio/v0/AIFCDecode.cpp @@ -176,11 +176,11 @@ s32 readaifccodebook(LUS::BinaryReader& fhandle, s32 ****table, s16 *order, s16 BSWAP16(*order); checked_fread(npredictors, sizeof(s16), 1, fhandle); BSWAP16(*npredictors); - *table = static_cast<s32 ***>(malloc(*npredictors * sizeof(s32 **))); + *table = new s32**[*npredictors]; for (s32 i = 0; i < *npredictors; i++) { - (*table)[i] = static_cast<s32 **>(malloc(8 * sizeof(s32 *))); + (*table)[i] = new s32*[8]; for (s32 j = 0; j < 8; j++) { - (*table)[i][j] = static_cast<s32 *>(malloc((*order + 8) * sizeof(s32))); + (*table)[i][j] = new s32[*order + 8]; } } @@ -401,7 +401,7 @@ void write_aiff(std::vector<char> data, LUS::BinaryWriter& writer) { ALADPCMloop *aloops = nullptr; s16 npredictors = -1; s32 ***coefTable = nullptr; - s32 state[16]; + s32 state[16] = {0}; s32 soundPointer = -1; s32 currPos = 0; s32 nSamples = 0; @@ -525,12 +525,12 @@ void write_aiff(std::vector<char> data, LUS::BinaryWriter& writer) { 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]; + u8 input[9] = {0}; + u8 encoded[9] = {0}; + s32 lastState[16] = {0}; + s32 decoded[16] = {0}; + s16 guess[16] = {0}; + s16 origGuess[16] = {0}; memcpy(lastState, state, sizeof(lastState)); checked_fread(input, 9, 1, reader); @@ -550,7 +550,7 @@ void write_aiff(std::vector<char> data, LUS::BinaryWriter& writer) { 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) { + if (0 && memcmp(input, encoded, 9) != 0) { s32 scale = 1 << (input[0] >> 4); do { permute(guess, decoded, scale); diff --git a/src/factories/naudio/v1/AudioContext.cpp b/src/factories/naudio/v1/AudioContext.cpp index 76741c9..1c27598 100644 --- a/src/factories/naudio/v1/AudioContext.cpp +++ b/src/factories/naudio/v1/AudioContext.cpp @@ -60,6 +60,7 @@ TunedSample AudioContext::LoadTunedSample(LUS::BinaryReader& reader, uint32_t pa node["type"] = "NAUDIO:V1:SAMPLE"; node["parent"] = parent; node["offset"] = parent + sampleAddr; + node["tuning"] = tuning; node["sampleBankId"] = sampleBankId; Companion::Instance->AddAsset(node); diff --git a/src/factories/naudio/v1/AudioContext.h b/src/factories/naudio/v1/AudioContext.h index f1ab111..84e5337 100644 --- a/src/factories/naudio/v1/AudioContext.h +++ b/src/factories/naudio/v1/AudioContext.h @@ -33,4 +33,6 @@ public: REGISTER(Code, AudioDummyExporter) }; } + + bool HasModdedDependencies() override { return true; } }; diff --git a/src/factories/naudio/v1/AudioConverter.cpp b/src/factories/naudio/v1/AudioConverter.cpp new file mode 100644 index 0000000..2c541dd --- /dev/null +++ b/src/factories/naudio/v1/AudioConverter.cpp @@ -0,0 +1,160 @@ +#include "AudioConverter.h" + +#include <factories/naudio/v1/BookFactory.h> +#include <factories/naudio/v1/LoopFactory.h> +#include <factories/naudio/v1/AudioContext.h> +#include <factories/naudio/v1/SampleFactory.h> +#include <factories/BaseFactory.h> +#include <Companion.h> +#include <cassert> + +void AIFCWriter::End(std::string chunk, LUS::BinaryWriter& writer) { + auto buffer = writer.ToVector(); + this->Chunks.push_back({ + chunk, buffer + }); + + this->totalSize += ALIGN(buffer.size(), 2) + 8; +} + +void AIFCWriter::Close(LUS::BinaryWriter& out){ + out.SetEndianness(Torch::Endianness::Big); + + out.Write(AIFCMagicValues::FORM); + out.Write((uint32_t) (this->totalSize + 4)); + out.Write(AIFCMagicValues::AIFC); + + for(auto& chunk : this->Chunks){ + out.Write((char*) chunk.id.data(), chunk.id.size()); + out.Write((uint32_t) chunk.data.size()); + out.Write((char*) chunk.data.data(), chunk.data.size()); + + if(chunk.data.size() % 2 == 1) { + out.Write((uint8_t) 0); + } + } +} + +// Function to serialize double to 80-bit extended-precision +void SerializeF80(double num, LUS::BinaryWriter &writer) { + // Convert the input double to a uint64_t representation + std::uint64_t f64; + std::memcpy(&f64, &num, sizeof(double)); + + // Extract the sign bit + std::uint64_t f64_sign_bit = f64 & (1ULL << 63); + + // Handle the special case: zero + if (num == 0.0) { + if (f64_sign_bit) { + writer.Write(static_cast<uint16_t>(0x8000)); // Sign bit set + } else { + writer.Write(static_cast<uint16_t>(0x0000)); // No sign bit + } + writer.Write(static_cast<uint64_t>(0x0000000000000000)); // Zero mantissa + return; + } + + // Extract the exponent and mantissa + std::uint64_t exponent = (f64 >> 52) & 0x7FF; // Exponent bits + assert(exponent != 0); // Ensure not denormal + assert(exponent != 0x7FF); // Ensure not infinity/NaN + + exponent -= 1023; // Adjust bias for 64-bit + + std::uint64_t f64_mantissa_bits = f64 & ((1ULL << 52) - 1); // Mantissa bits + + // Construct the 80-bit extended-precision fields + std::uint64_t f80_sign_bit = f64_sign_bit << (80 - 64); // Shift sign + std::uint64_t f80_exponent = (exponent + 0x3FFF) & 0x7FFF; // Adjust bias + std::uint64_t f80_mantissa_bits = (1ULL << 63) | (f64_mantissa_bits << (63 - 52)); // Add implicit bit + + // Combine components into the 80-bit representation + uint16_t high = static_cast<uint16_t>(f80_sign_bit >> 48) | static_cast<uint16_t>(f80_exponent); + uint64_t low = f80_mantissa_bits; + + // Write the result in big-endian order + writer.Write(high); + writer.Write(low); +} + +void AudioConverter::SampleToAIFC(NSampleData* sample, LUS::BinaryWriter &out) { + auto loop = std::static_pointer_cast<ADPCMLoopData>(Companion::Instance->GetParseDataByAddr(sample->loop)->data.value()); + auto book = std::static_pointer_cast<ADPCMBookData>(Companion::Instance->GetParseDataByAddr(sample->book)->data.value()); + auto entry = AudioContext::tableData[AudioTableType::SAMPLE_TABLE]->entries[sample->sampleBankId]; + auto buffer = AudioContext::data[AudioTableType::SAMPLE_TABLE]; + auto sampleData = buffer.data() + entry.addr + sample->sampleAddr; + auto aifc = AIFCWriter(); + std::vector<uint8_t> data(sampleData, sampleData + sample->size); + + uint32_t num_frames = data.size() * 16 / 9; + uint32_t sample_rate = 0; + + if (sample->tuning <= 0.5f) { + sample_rate = 16000; + } else if (sample->tuning <= 1.0f) { + sample_rate = 32000; + } else if (sample->tuning <= 1.5f) { + sample_rate = 48000; + } else if (sample->tuning <= 2.5f) { + sample_rate = 80000; + } else { + sample_rate = 16000 * sample->tuning; + } + + 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) book->order); + vcodes.Write((int16_t) book->numPredictors); + + for(auto page : book->book){ + 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(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(loop->start); + vloops.Write(loop->end); + vloops.Write(loop->count); + + for(size_t i = 0; i < 16; i++){ + vloops.Write(loop->predictorState[i]); + } + aifc.End("APPL", vloops); + } + + aifc.Close(out); +}
\ No newline at end of file diff --git a/src/factories/naudio/v1/AudioConverter.h b/src/factories/naudio/v1/AudioConverter.h new file mode 100644 index 0000000..ef87a0d --- /dev/null +++ b/src/factories/naudio/v1/AudioConverter.h @@ -0,0 +1,38 @@ +#pragma once + +#include <factories/BaseFactory.h> +#include <factories/naudio/v1/SampleFactory.h> + +enum AIFCMagicValues { + FORM = (uint32_t) 0x464f524d, + AIFC = (uint32_t) 0x41494643, + VAPC = (uint32_t) 0x56415043, + AAPL = (uint32_t) 0x4150504c +}; + +#define NONE 0xFFFF +#define ALIGN(val, al) (size_t) ((val + (al - 1)) & -al) + +class AudioConverter { +public: + static void SampleToAIFC(NSampleData* tSample, LUS::BinaryWriter &out); +}; + +struct AIFCChunk { + std::string id; + std::vector<char> data; +}; + +class AIFCWriter { +public: + std::vector<AIFCChunk> Chunks; + size_t totalSize = 0; + + LUS::BinaryWriter Start(){ + auto writer = LUS::BinaryWriter(); + writer.SetEndianness(Torch::Endianness::Big); + return writer; + } + void End(std::string chunk, LUS::BinaryWriter& writer); + void Close(LUS::BinaryWriter& out); +};
\ No newline at end of file diff --git a/src/factories/naudio/v1/AudioTableFactory.h b/src/factories/naudio/v1/AudioTableFactory.h index 1479b87..5d3042d 100644 --- a/src/factories/naudio/v1/AudioTableFactory.h +++ b/src/factories/naudio/v1/AudioTableFactory.h @@ -51,4 +51,6 @@ public: REGISTER(Code, AudioTableCodeExporter) }; } + + bool HasModdedDependencies() override { return true; } }; diff --git a/src/factories/naudio/v1/BookFactory.cpp b/src/factories/naudio/v1/BookFactory.cpp index 96d1e53..a501bf5 100644 --- a/src/factories/naudio/v1/BookFactory.cpp +++ b/src/factories/naudio/v1/BookFactory.cpp @@ -46,10 +46,6 @@ std::optional<std::shared_ptr<IParsedData>> ADPCMBookFactory::parse(std::vector< book->numPredictors = reader.ReadInt32(); size_t length = 8 * book->order * book->numPredictors; - if(length > 0x40){ - return std::nullopt; - } - for(size_t i = 0; i < length; i++){ book->book.push_back(reader.ReadInt16()); } diff --git a/src/factories/naudio/v1/BookFactory.h b/src/factories/naudio/v1/BookFactory.h index a761f22..1ee83ea 100644 --- a/src/factories/naudio/v1/BookFactory.h +++ b/src/factories/naudio/v1/BookFactory.h @@ -32,4 +32,6 @@ public: REGISTER(Code, ADPCMBookCodeExporter) }; } + + bool HasModdedDependencies() override { return true; } }; diff --git a/src/factories/naudio/v1/DrumFactory.h b/src/factories/naudio/v1/DrumFactory.h index e417e9a..34c2afd 100644 --- a/src/factories/naudio/v1/DrumFactory.h +++ b/src/factories/naudio/v1/DrumFactory.h @@ -34,4 +34,6 @@ public: REGISTER(Code, DrumCodeExporter) }; } + + bool HasModdedDependencies() override { return true; } }; diff --git a/src/factories/naudio/v1/EnvelopeFactory.h b/src/factories/naudio/v1/EnvelopeFactory.h index a75d031..53e1b42 100644 --- a/src/factories/naudio/v1/EnvelopeFactory.h +++ b/src/factories/naudio/v1/EnvelopeFactory.h @@ -38,4 +38,6 @@ public: REGISTER(Code, EnvelopeCodeExporter) }; } + + bool HasModdedDependencies() override { return true; } }; diff --git a/src/factories/naudio/v1/InstrumentFactory.h b/src/factories/naudio/v1/InstrumentFactory.h index 9d6ccf0..9af9295 100644 --- a/src/factories/naudio/v1/InstrumentFactory.h +++ b/src/factories/naudio/v1/InstrumentFactory.h @@ -37,4 +37,6 @@ public: REGISTER(Code, InstrumentCodeExporter) }; } + + bool HasModdedDependencies() override { return true; } }; diff --git a/src/factories/naudio/v1/LoopFactory.h b/src/factories/naudio/v1/LoopFactory.h index e281946..72f2f7d 100644 --- a/src/factories/naudio/v1/LoopFactory.h +++ b/src/factories/naudio/v1/LoopFactory.h @@ -33,4 +33,6 @@ public: REGISTER(Code, ADPCMLoopCodeExporter) }; } + + bool HasModdedDependencies() override { return true; } }; diff --git a/src/factories/naudio/v1/SampleFactory.cpp b/src/factories/naudio/v1/SampleFactory.cpp index dec7ada..aa00782 100644 --- a/src/factories/naudio/v1/SampleFactory.cpp +++ b/src/factories/naudio/v1/SampleFactory.cpp @@ -1,6 +1,7 @@ #include "SampleFactory.h" -#include "utils/Decompressor.h" +#include "AudioConverter.h" #include "Companion.h" +#include <factories/naudio/v0/AIFCDecode.h> ExportResult NSampleHeaderExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement) { const auto symbol = GetSafeNode(node, "symbol", entryName); @@ -41,9 +42,26 @@ ExportResult NSampleBinaryExporter::Export(std::ostream &write, std::shared_ptr< return std::nullopt; } +ExportResult NSampleModdingExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement ) { + auto aiff = LUS::BinaryWriter(); + auto data = std::static_pointer_cast<NSampleData>(raw); + *replacement += ".aiff"; + + auto aifc = LUS::BinaryWriter(); + AudioConverter::SampleToAIFC(data.get(), aifc); + auto cnv = aifc.ToVector(); + + if(!cnv.empty()){ + write_aiff(cnv, aiff); + aiff.Finish(write); + } + return std::nullopt; +} + std::optional<std::shared_ptr<IParsedData>> NSampleFactory::parse(std::vector<uint8_t>& buffer, YAML::Node& node) { auto offset = GetSafeNode<uint32_t>(node, "offset"); auto parent = GetSafeNode<uint32_t>(node, "parent"); + auto tuning = GetSafeNode<float>(node, "tuning", 1.0f); auto sampleBankId = GetSafeNode<uint32_t>(node, "sampleBankId"); auto entry = AudioContext::tables[AudioTableType::FONT_TABLE].at(parent); @@ -74,6 +92,7 @@ std::optional<std::shared_ptr<IParsedData>> NSampleFactory::parse(std::vector<ui Companion::Instance->AddAsset(book); sample->sampleAddr = addr; + sample->tuning = tuning; sample->sampleBankId = sampleBankId; return sample; diff --git a/src/factories/naudio/v1/SampleFactory.h b/src/factories/naudio/v1/SampleFactory.h index d4ed44f..c95cb7a 100644 --- a/src/factories/naudio/v1/SampleFactory.h +++ b/src/factories/naudio/v1/SampleFactory.h @@ -14,6 +14,7 @@ public: uint32_t loop; uint32_t book; uint32_t sampleBankId; + float tuning; }; class NSampleHeaderExporter : public BaseExporter { @@ -28,6 +29,10 @@ class NSampleCodeExporter : public BaseExporter { ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override; }; +class NSampleModdingExporter : public BaseExporter { + ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override; +}; + class NSampleFactory : public BaseFactory { public: std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override; @@ -36,6 +41,8 @@ public: REGISTER(Header, NSampleHeaderExporter) REGISTER(Binary, NSampleBinaryExporter) REGISTER(Code, NSampleCodeExporter) + REGISTER(Modding, NSampleModdingExporter) }; } + bool SupportModdedAssets() override { return true; } }; diff --git a/src/factories/naudio/v1/SoundFontFactory.h b/src/factories/naudio/v1/SoundFontFactory.h index 8bb73e4..256f2ce 100644 --- a/src/factories/naudio/v1/SoundFontFactory.h +++ b/src/factories/naudio/v1/SoundFontFactory.h @@ -34,4 +34,6 @@ public: REGISTER(Code, SoundFontCodeExporter) }; } + + bool HasModdedDependencies() override { return true; } }; |
