mirror of
https://github.com/izzy2lost/Torch.git
synced 2026-07-06 00:18:35 -07:00
Added sample extraction to aiff
This commit is contained in:
+1
-1
@@ -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; }
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -33,4 +33,6 @@ public:
|
||||
REGISTER(Code, AudioDummyExporter)
|
||||
};
|
||||
}
|
||||
|
||||
bool HasModdedDependencies() override { return true; }
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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; }
|
||||
};
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -32,4 +32,6 @@ public:
|
||||
REGISTER(Code, ADPCMBookCodeExporter)
|
||||
};
|
||||
}
|
||||
|
||||
bool HasModdedDependencies() override { return true; }
|
||||
};
|
||||
|
||||
@@ -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; }
|
||||
};
|
||||
|
||||
@@ -33,4 +33,6 @@ public:
|
||||
REGISTER(Code, ADPCMLoopCodeExporter)
|
||||
};
|
||||
}
|
||||
|
||||
bool HasModdedDependencies() override { return true; }
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user