Added sample extraction to aiff

This commit is contained in:
KiritoDv
2024-11-21 21:40:38 -06:00
parent 1537229c69
commit a9662a158f
16 changed files with 254 additions and 17 deletions
+1 -1
View File
@@ -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; }
+11 -11
View File
@@ -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);
+1
View File
@@ -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);
+2
View File
@@ -33,4 +33,6 @@ public:
REGISTER(Code, AudioDummyExporter)
};
}
bool HasModdedDependencies() override { return true; }
};
+160
View File
@@ -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);
}
+38
View File
@@ -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);
};
@@ -51,4 +51,6 @@ public:
REGISTER(Code, AudioTableCodeExporter)
};
}
bool HasModdedDependencies() override { return true; }
};
-4
View File
@@ -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());
}
+2
View File
@@ -32,4 +32,6 @@ public:
REGISTER(Code, ADPCMBookCodeExporter)
};
}
bool HasModdedDependencies() override { return true; }
};
+2
View File
@@ -34,4 +34,6 @@ public:
REGISTER(Code, DrumCodeExporter)
};
}
bool HasModdedDependencies() override { return true; }
};
@@ -38,4 +38,6 @@ public:
REGISTER(Code, EnvelopeCodeExporter)
};
}
bool HasModdedDependencies() override { return true; }
};
@@ -37,4 +37,6 @@ public:
REGISTER(Code, InstrumentCodeExporter)
};
}
bool HasModdedDependencies() override { return true; }
};
+2
View File
@@ -33,4 +33,6 @@ public:
REGISTER(Code, ADPCMLoopCodeExporter)
};
}
bool HasModdedDependencies() override { return true; }
};
+20 -1
View File
@@ -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;
+7
View File
@@ -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; }
};
@@ -34,4 +34,6 @@ public:
REGISTER(Code, SoundFontCodeExporter)
};
}
bool HasModdedDependencies() override { return true; }
};