Skip to content

Commit

Permalink
Added sample extraction to aiff
Browse files Browse the repository at this point in the history
  • Loading branch information
KiritoDv committed Nov 22, 2024
1 parent 1537229 commit a9662a1
Show file tree
Hide file tree
Showing 16 changed files with 254 additions and 17 deletions.
2 changes: 1 addition & 1 deletion src/Companion.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ class Companion {
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; }
Expand Down
22 changes: 11 additions & 11 deletions src/factories/naudio/v0/AIFCDecode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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];
}
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/factories/naudio/v1/AudioContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 2 additions & 0 deletions src/factories/naudio/v1/AudioContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,6 @@ class AudioContextFactory : public BaseFactory {
REGISTER(Code, AudioDummyExporter)
};
}

bool HasModdedDependencies() override { return true; }
};
160 changes: 160 additions & 0 deletions src/factories/naudio/v1/AudioConverter.cpp
Original file line number Diff line number Diff line change
@@ -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 changes: 38 additions & 0 deletions src/factories/naudio/v1/AudioConverter.h
Original file line number Diff line number Diff line change
@@ -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);
};
2 changes: 2 additions & 0 deletions src/factories/naudio/v1/AudioTableFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,6 @@ class AudioTableFactory : public BaseFactory {
REGISTER(Code, AudioTableCodeExporter)
};
}

bool HasModdedDependencies() override { return true; }
};
4 changes: 0 additions & 4 deletions src/factories/naudio/v1/BookFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down
2 changes: 2 additions & 0 deletions src/factories/naudio/v1/BookFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,6 @@ class ADPCMBookFactory : public BaseFactory {
REGISTER(Code, ADPCMBookCodeExporter)
};
}

bool HasModdedDependencies() override { return true; }
};
2 changes: 2 additions & 0 deletions src/factories/naudio/v1/DrumFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ class DrumFactory : public BaseFactory {
REGISTER(Code, DrumCodeExporter)
};
}

bool HasModdedDependencies() override { return true; }
};
2 changes: 2 additions & 0 deletions src/factories/naudio/v1/EnvelopeFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,6 @@ class EnvelopeFactory : public BaseFactory {
REGISTER(Code, EnvelopeCodeExporter)
};
}

bool HasModdedDependencies() override { return true; }
};
2 changes: 2 additions & 0 deletions src/factories/naudio/v1/InstrumentFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,6 @@ class InstrumentFactory : public BaseFactory {
REGISTER(Code, InstrumentCodeExporter)
};
}

bool HasModdedDependencies() override { return true; }
};
2 changes: 2 additions & 0 deletions src/factories/naudio/v1/LoopFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,6 @@ class ADPCMLoopFactory : public BaseFactory {
REGISTER(Code, ADPCMLoopCodeExporter)
};
}

bool HasModdedDependencies() override { return true; }
};
21 changes: 20 additions & 1 deletion src/factories/naudio/v1/SampleFactory.cpp
Original file line number Diff line number Diff line change
@@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
Loading

0 comments on commit a9662a1

Please sign in to comment.