diff options
Diffstat (limited to 'src/audio/AudioManager.cpp')
| -rw-r--r-- | src/audio/AudioManager.cpp | 231 |
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; + } + } } } |
