summaryrefslogtreecommitdiff
path: root/src/audio/AudioManager.cpp
diff options
context:
space:
mode:
authorKiritoDv <kiritodev01@gmail.com>2023-09-03 20:11:35 -0600
committerKiritoDv <kiritodev01@gmail.com>2023-09-03 20:11:35 -0600
commit063224c69d564d6e10dd4febf25caa9f59fccfda (patch)
treec1edbd90c120d7b846490159540f68adfddfbb5a /src/audio/AudioManager.cpp
parent113472302904ac84d4ddf70df32ca245ad156bf2 (diff)
Fixed AIFC Extraction
Diffstat (limited to 'src/audio/AudioManager.cpp')
-rw-r--r--src/audio/AudioManager.cpp231
1 files changed, 202 insertions, 29 deletions
diff --git a/src/audio/AudioManager.cpp b/src/audio/AudioManager.cpp
index 1f3e382..f241a9b 100644
--- a/src/audio/AudioManager.cpp
+++ b/src/audio/AudioManager.cpp
@@ -8,35 +8,43 @@
#include "hj/pyutils.h"
#include "binarytools/BinaryReader.h"
#include "hj/zip.h"
+#include "AIFCWriter.h"
#include <nlohmann/json.hpp>
std::unordered_map<std::string, uint32_t> name_table;
AudioManager* AudioManager::Instance;
+std::vector<uint32_t> PyUtils::range(uint32_t start, uint32_t end) {
+ std::vector<uint32_t> result;
+ for (uint32_t i = start; i < end; ++i) {
+ result.push_back(i);
+ }
+ return result;
+}
+
std::string gen_name(const std::string& prefix){
if(!name_table.contains(prefix)){
name_table[prefix] = 0;
}
- name_table[prefix] += 1;
- return prefix + "-" + std::to_string(name_table[prefix]);
+ return prefix + std::to_string(name_table[prefix]++);
}
-AifcEntry SampleBank::AddSample(uint32_t addr, size_t sampleSize, const Book& book, const Loop& loop){
+AifcEntry* SampleBank::AddSample(uint32_t addr, size_t sampleSize, const Book& book, const Loop& loop){
assert(sampleSize % 2 == 0);
if(sampleSize % 9 != 0){
assert(sampleSize % 9 == 1);
sampleSize -= 1;
}
- AifcEntry entry;
+ AifcEntry* entry;
if(this->entries.contains(addr)){
entry = this->entries[addr];
- assert(entry.book == book);
- assert(entry.loop == loop);
- assert(entry.data.size() == sampleSize);
+ assert(entry->book == book);
+ assert(entry->loop == loop);
+ assert(entry->data.size() == sampleSize);
} else {
- entry = { gen_name("aifc"), PyUtils::slice(this->data, addr, addr + sampleSize), book, loop };
+ entry = new AifcEntry{ gen_name("aifc"), PyUtils::slice(this->data, addr, addr + sampleSize), book, loop };
this->entries[addr] = entry;
}
@@ -51,8 +59,8 @@ void Bank::print() const {
std::cout << "Envelopes: " << std::to_string(envelopes.size()) << std::endl;
std::cout << "All Instruments: " << std::to_string(allInsts.size()) << std::endl;
std::cout << "Inst Offsets: " << std::to_string(instOffsets.size()) << std::endl;
- std::cout << "Sample Bank: " << sampleBank.name << std::endl;
- std::cout << "Sample Bank Offset: " << std::to_string(sampleBank.offset) << std::endl;
+ std::cout << "Sample Bank: " << sampleBank->name << std::endl;
+ std::cout << "Sample Bank Offset: " << std::to_string(sampleBank->offset) << std::endl;
}
std::vector<Entry> AudioManager::parse_seq_file(std::vector<uint8_t>& buffer, uint32_t offset, bool isCTL){
@@ -198,7 +206,7 @@ Book AudioManager::parse_book(uint32_t addr, std::vector<uint8_t>& bankData){
return book;
}
-AifcEntry AudioManager::parse_sample(std::vector<uint8_t>& data, std::vector<uint8_t>& bankData, SampleBank& sampleBank){
+AifcEntry* AudioManager::parse_sample(std::vector<uint8_t>& data, std::vector<uint8_t>& bankData, SampleBank* sampleBank){
LUS::BinaryReader reader((char*) data.data(), data.size());
reader.SetEndianness(LUS::Endianness::Big);
@@ -215,7 +223,7 @@ AifcEntry AudioManager::parse_sample(std::vector<uint8_t>& data, std::vector<uin
Book bookData = parse_book(book, bankData);
reader.Close();
- return sampleBank.AddSample(addr, sampleSize, bookData, loopData);
+ return sampleBank->AddSample(addr, sampleSize, bookData, loopData);
}
@@ -239,7 +247,7 @@ std::vector<EnvelopeData> AudioManager::parse_envelope(uint32_t addr, std::vecto
return entries;
}
-Bank AudioManager::parse_ctl(CTLHeader header, std::vector<uint8_t> data, SampleBank bank, uint32_t index) {
+Bank AudioManager::parse_ctl(CTLHeader header, std::vector<uint8_t> data, SampleBank* bank, uint32_t index) {
name_table.clear();
std::ostringstream ss;
ss << std::hex << std::setw(2) << std::setfill('0') << index;
@@ -333,13 +341,13 @@ Bank AudioManager::parse_ctl(CTLHeader header, std::vector<uint8_t> data, Sample
allInsts.emplace_back(drums);
}
- std::unordered_map<uint32_t, AifcEntry> samples;
+ std::unordered_map<uint32_t, AifcEntry*> samples;
std::sort(sampleOffsets.begin(), sampleOffsets.end());
for(auto &offset : sampleOffsets){
auto rSample = PyUtils::slice(data, offset, offset + 20);
- AifcEntry sample = parse_sample(rSample, data, bank);
+ AifcEntry* sample = parse_sample(rSample, data, bank);
for(auto &tuning : tunings){
- sample.tunings.push_back(tuning.second);
+ sample->tunings.push_back(tuning.second);
}
samples[offset] = sample;
}
@@ -392,11 +400,11 @@ TBLFile AudioManager::parse_tbl(std::vector<uint8_t>& data, std::vector<Entry>&
for(auto &entry : entries){
if(!cache.contains(entry.offset)){
std::string name = gen_name("sample_bank");
- SampleBank sampleBank = {
- name, entry.offset, PyUtils::slice(data, entry.offset, entry.offset + entry.length)
+ auto* sampleBank = new SampleBank{
+ name, entry.offset, PyUtils::slice(data, entry.offset, entry.offset + entry.length)
};
tbl.banks.push_back(sampleBank);
- tbl.map[name] = tbl.banks.size() - 1;
+ tbl.map[name] = sampleBank;
cache[entry.offset] = name;
}
tbl.tbls.push_back(cache[entry.offset]);
@@ -419,24 +427,189 @@ void AudioManager::initialize(std::vector<uint8_t>& buffer) {
std::vector<uint8_t> tbl_data = PyUtils::slice(buffer, sound_tbl[0], (size_t) sound_tbl[0] + (size_t) sound_tbl[1]);
std::vector<uint8_t> ctl_data = PyUtils::slice(buffer, sound_ctl[0], (size_t) sound_ctl[0] + (size_t) sound_ctl[1]);
- TBLFile tbl_file = parse_tbl(tbl_data, tbl);
+ this->loaded_tbl = parse_tbl(tbl_data, tbl);
- std::cout << "TBLs: " << tbl_file.tbls.size() << std::endl;
- std::cout << "Banks: " << tbl_file.banks.size() << std::endl;
- std::cout << "Map: " << tbl_file.map.size() << std::endl;
+ std::cout << "TBLs: " << this->loaded_tbl.tbls.size() << std::endl;
+ std::cout << "Banks: " << this->loaded_tbl.banks.size() << std::endl;
+ std::cout << "Map: " << this->loaded_tbl.map.size() << std::endl;
- auto zipped = zip(PyUtils::range(0, ctl.size()), ctl, tbl_file.tbls);
+ auto zipped = zip(PyUtils::range(0, ctl.size()), ctl, this->loaded_tbl.tbls);
- std::cout << "================================" << std::endl;
+// std::cout << "================================" << std::endl;
for (const auto& item : zipped) {
auto [index, ctrl, sample_bank_name] = item;
- auto sample_bank = tbl_file.map[sample_bank_name];
+ auto sample_bank = this->loaded_tbl.map[sample_bank_name];
auto entry = PyUtils::slice(ctl_data, ctrl.offset, ctrl.offset + ctrl.length);
auto headerRaw = PyUtils::slice(entry, 0, 16);
auto header = parse_ctl_header(headerRaw);
- auto bank = parse_ctl(header, PyUtils::slice(entry, 16), tbl_file.banks[sample_bank], index);
+ auto bank = parse_ctl(header, PyUtils::slice(entry, 16), sample_bank, index);
banks.push_back(bank);
- bank.print();
- std::cout << "================================" << std::endl;
+// bank.print();
+// std::cout << "================================" << std::endl;
+ }
+
+
+}
+
+void serialize_f80(double num, LUS::BinaryWriter &writer) {
+ // Convert the input double to a 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(BSWAP32(section)); \
+ LUS::BinaryWriter tmp = LUS::BinaryWriter(); \
+ tmp.SetEndianness(LUS::Endianness::Big); \
+
+#define START_CUSTOM_SECTION(section) \
+ { \
+ LUS::BinaryWriter tmp = LUS::BinaryWriter(); \
+ tmp.SetEndianness(LUS::Endianness::Big); \
+ out.Write(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(BSWAP32((uint32_t) size)); \
+ out.Write(odata.data(), odata.size()); \
+ if(size % 2){ \
+ out.WriteByte(0); \
+ } \
+ } \
+
+void AudioManager::write_aifc(AifcEntry* 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(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(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("\u000BVADPCM ~4-1", false);
+
+ END_SECTION();
+
+ START_SECTION(AIFC::MagicValues::INST)
+ tmp.Write(std::string(20, '\0'), false);
+ END_SECTION();
+
+ START_CUSTOM_SECTION("\u000BVADPCMCODES")
+ 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("\u000BVADPCMLOOPS")
+ uint16_t one = BSWAP16(1);
+ tmp.Write((char*) &one, 2);
+ tmp.Write((char*) &one, 2);
+ tmp.Write((uint32_t) entry->loop.start);
+ tmp.Write((uint32_t) entry->loop.end);
+ tmp.Write((int32_t) entry->loop.count);
+ for(size_t i = 0; i < 16; i++){
+ int16_t loop = BSWAP16(entry->loop.state.value()[i]);
+ tmp.Write((char*) &loop, 2);
+ }
+ END_SECTION();
+ }
+
+ len += 4;
+ out.Seek(4, LUS::SeekOffsetType::Start);
+ out.Write((uint32_t) BSWAP32(len));
+
+}
+
+void AudioManager::create_aifc(int32_t index, LUS::BinaryWriter &out) {
+ int32_t idx = -1;
+ for(auto &sample_bank : this->loaded_tbl.banks){
+ auto offsets = PyUtils::keys(sample_bank->entries);
+ std::sort(offsets.begin(), offsets.end());
+
+ for(auto &offset : offsets){
+ if(++idx == index){
+ write_aifc(sample_bank->entries[offset], out);
+ return;
+ }
+ }
}
}