diff --git a/OTRExporter/.gitignore b/OTRExporter/.gitignore index 005fe6b8c..2080ab480 100644 --- a/OTRExporter/.gitignore +++ b/OTRExporter/.gitignore @@ -341,6 +341,7 @@ build/ ZAPDUtils/build/ ZAPD/BuildInfo.h baserom/ +baserom_ntsc/ *.vtx.inc *.otr *.swp diff --git a/OTRExporter/OTRExporter/AudioExporter.cpp b/OTRExporter/OTRExporter/AudioExporter.cpp new file mode 100644 index 000000000..7fb065831 --- /dev/null +++ b/OTRExporter/OTRExporter/AudioExporter.cpp @@ -0,0 +1,174 @@ +#include "AudioExporter.h" +#include "Main.h" +#include +#include +#include +#include +#include "DisplayListExporter.h" + +void OTRExporter_Audio::WriteSampleEntryReference(SampleEntry* entry, std::map samples, BinaryWriter* writer) +{ + writer->Write((uint8_t)(entry != nullptr ? 1 : 0)); + + uint32_t addr = 0; + + for (auto pair : samples) + { + if (pair.second == entry) + { + addr = pair.first; + break; + } + } + + writer->Write(addr); +} + +void OTRExporter_Audio::WriteSampleEntry(SampleEntry* entry, BinaryWriter* writer) +{ + WriteHeader(nullptr, "", writer, Ship::ResourceType::AudioSample); + + writer->Write(entry->codec); + writer->Write(entry->medium); + writer->Write(entry->unk_bit26); + writer->Write(entry->unk_bit25); + + writer->Write((uint32_t)entry->data.size()); + writer->Write((char*)entry->data.data(), entry->data.size()); + + writer->Write((uint32_t)(entry->loop.start)); + writer->Write((uint32_t)(entry->loop.end)); + writer->Write((uint32_t)(entry->loop.count)); + writer->Write((uint32_t)entry->loop.states.size()); + + for (int i = 0; i < entry->loop.states.size(); i++) + writer->Write((entry->loop.states[i])); + + writer->Write((uint32_t)(entry->book.order)); + writer->Write((uint32_t)(entry->book.npredictors)); + writer->Write((uint32_t)entry->book.books.size()); + + for (int i = 0; i < entry->book.books.size(); i++) + writer->Write((entry->book.books[i])); +} + +void OTRExporter_Audio::WriteSoundFontEntry(SoundFontEntry* entry, std::map samples, BinaryWriter* writer) +{ + writer->Write((uint8_t)(entry != nullptr ? 1 : 0)); + + if (entry != nullptr) + { + WriteSampleEntryReference(entry->sampleEntry, samples, writer); + writer->Write(entry->tuning); + } +} + +void OTRExporter_Audio::WriteEnvData(std::vector envelopes, BinaryWriter* writer) +{ + writer->Write((uint32_t)envelopes.size()); + + for (auto env : envelopes) + { + writer->Write(env->delay); + writer->Write(env->arg); + } +} + +void OTRExporter_Audio::Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) +{ + ZAudio* audio = (ZAudio*)res; + + WriteHeader(res, outPath, writer, Ship::ResourceType::Audio); + + // Write Samples as individual files + for (auto pair : audio->samples) + { + MemoryStream* sampleStream = new MemoryStream(); + BinaryWriter sampleWriter = BinaryWriter(sampleStream); + + WriteSampleEntry(pair.second, &sampleWriter); + + std::string fName = OTRExporter_DisplayList::GetPathToRes(res, StringHelper::Sprintf("samples/sample_%08X", pair.first)); + AddFile(fName, sampleStream->ToVector()); + } + + // Write the samplebank table + //writer->Write((uint32_t)audio->sampleBankTable.size()); + //for (size_t i = 0; i < audio->sampleBankTable.size(); i++) + //{ + //} + + // Write the soundfont table + //writer->Write((uint32_t)audio->soundFontTable.size()); + + for (size_t i = 0; i < audio->soundFontTable.size(); i++) + { + MemoryStream* fntStream = new MemoryStream(); + BinaryWriter fntWriter = BinaryWriter(fntStream); + + WriteHeader(nullptr, "", &fntWriter, Ship::ResourceType::AudioSoundFont); + + fntWriter.Write(audio->soundFontTable[i].medium); + fntWriter.Write(audio->soundFontTable[i].cachePolicy); + fntWriter.Write(audio->soundFontTable[i].data1); + fntWriter.Write(audio->soundFontTable[i].data2); + fntWriter.Write(audio->soundFontTable[i].data3); + + fntWriter.Write((uint32_t)audio->soundFontTable[i].drums.size()); + fntWriter.Write((uint32_t)audio->soundFontTable[i].instruments.size()); + fntWriter.Write((uint32_t)audio->soundFontTable[i].soundEffects.size()); + + for (int k = 0; k < audio->soundFontTable[i].drums.size(); k++) + { + fntWriter.Write(audio->soundFontTable[i].drums[k].releaseRate); + fntWriter.Write(audio->soundFontTable[i].drums[k].pan); + fntWriter.Write(audio->soundFontTable[i].drums[k].loaded); + + WriteEnvData(audio->soundFontTable[i].drums[k].env, &fntWriter); + + WriteSampleEntryReference(audio->soundFontTable[i].drums[k].sample, audio->samples, &fntWriter); + fntWriter.Write(audio->soundFontTable[i].drums[k].tuning); + } + + for (int k = 0; k < audio->soundFontTable[i].instruments.size(); k++) + { + fntWriter.Write((uint8_t)audio->soundFontTable[i].instruments[k].isValidInstrument); + + fntWriter.Write(audio->soundFontTable[i].instruments[k].loaded); + fntWriter.Write(audio->soundFontTable[i].instruments[k].normalRangeLo); + fntWriter.Write(audio->soundFontTable[i].instruments[k].normalRangeHi); + fntWriter.Write(audio->soundFontTable[i].instruments[k].releaseRate); + + WriteEnvData(audio->soundFontTable[i].instruments[k].env, &fntWriter); + + WriteSoundFontEntry(audio->soundFontTable[i].instruments[k].lowNotesSound, audio->samples, &fntWriter); + WriteSoundFontEntry(audio->soundFontTable[i].instruments[k].normalNotesSound, audio->samples, &fntWriter); + WriteSoundFontEntry(audio->soundFontTable[i].instruments[k].highNotesSound, audio->samples, &fntWriter); + } + + for (int k = 0; k < audio->soundFontTable[i].soundEffects.size(); k++) + { + WriteSoundFontEntry(audio->soundFontTable[i].soundEffects[k], audio->samples, &fntWriter); + } + + std::string fName = OTRExporter_DisplayList::GetPathToRes(res, StringHelper::Sprintf("fonts/font_%02X", i)); + AddFile(fName, fntStream->ToVector()); + } + + // Write Sequences + for (int i = 0; i < audio->sequences.size(); i++) + { + auto seq = audio->sequences[i]; + + MemoryStream* seqStream = new MemoryStream(); + BinaryWriter seqWriter = BinaryWriter(seqStream); + + seqWriter.Write((uint8_t)audio->sequenceTable[i].medium); + seqWriter.Write((uint8_t)audio->sequenceTable[i].cachePolicy); + + seqWriter.Write(seq.data(), seq.size()); + + std::string fName = OTRExporter_DisplayList::GetPathToRes(res, StringHelper::Sprintf("sequences/seq_%02X", i)); + AddFile(fName, seqStream->ToVector()); + } +} diff --git a/OTRExporter/OTRExporter/AudioExporter.h b/OTRExporter/OTRExporter/AudioExporter.h new file mode 100644 index 000000000..bd91da67f --- /dev/null +++ b/OTRExporter/OTRExporter/AudioExporter.h @@ -0,0 +1,16 @@ +#pragma once + +#include "ZResource.h" +#include "ZAudio.h" +#include "Exporter.h" +#include + +class OTRExporter_Audio : public OTRExporter +{ +public: + void WriteSampleEntry(SampleEntry* entry, BinaryWriter* writer); + void WriteSampleEntryReference(SampleEntry* entry, std::map samples, BinaryWriter* writer); + void WriteSoundFontEntry(SoundFontEntry* entry, std::map samples, BinaryWriter* writer); + void WriteEnvData(std::vector envelopes, BinaryWriter* writer); + virtual void Save(ZResource* res, const fs::path& outPath, BinaryWriter* writer) override; +}; \ No newline at end of file diff --git a/OTRExporter/OTRExporter/DisplayListExporter.cpp b/OTRExporter/OTRExporter/DisplayListExporter.cpp index d03a06333..ed4694397 100644 --- a/OTRExporter/OTRExporter/DisplayListExporter.cpp +++ b/OTRExporter/OTRExporter/DisplayListExporter.cpp @@ -362,18 +362,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina BinaryWriter dlWriter = BinaryWriter(dlStream); Save(dList->otherDLists[i], outPath, &dlWriter); - -#ifdef _DEBUG - //if (otrArchive->HasFile(fName)) - //otrArchive->RemoveFile(fName); -#endif - - if (Globals::Instance->fileMode != ZFileMode::ExtractDirectory) - File::WriteAllBytes("Extract/" + fName, dlStream->ToVector()); - else - files[fName] = dlStream->ToVector(); - - //otrArchive->AddFile(fName, (uintptr_t)dlStream->ToVector().data(), dlWriter.GetBaseAddress()); + AddFile(fName, dlStream->ToVector()); } } else @@ -460,10 +449,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina Save(dList->otherDLists[i], outPath, &dlWriter); - if (Globals::Instance->fileMode != ZFileMode::ExtractDirectory) - File::WriteAllBytes("Extract/" + fName, dlStream->ToVector()); - else - files[fName] = dlStream->ToVector(); + AddFile(fName, dlStream->ToVector()); } } else @@ -827,10 +813,7 @@ void OTRExporter_DisplayList::Save(ZResource* res, const fs::path& outPath, Bina } } - if (Globals::Instance->fileMode != ZFileMode::ExtractDirectory) - File::WriteAllBytes("Extract/" + fName, vtxStream->ToVector()); - else - files[fName] = vtxStream->ToVector(); + AddFile(fName, vtxStream->ToVector()); auto end = std::chrono::steady_clock::now(); size_t diff = std::chrono::duration_cast(end - start).count(); diff --git a/OTRExporter/OTRExporter/Main.cpp b/OTRExporter/OTRExporter/Main.cpp index 2ed9cb427..e0a5254c5 100644 --- a/OTRExporter/OTRExporter/Main.cpp +++ b/OTRExporter/OTRExporter/Main.cpp @@ -15,6 +15,7 @@ #include "TextExporter.h" #include "BlobExporter.h" #include "MtxExporter.h" +#include "AudioExporter.h" #include #include #include @@ -26,6 +27,7 @@ std::shared_ptr otrArchive; BinaryWriter* fileWriter; std::chrono::steady_clock::time_point fileStart, resStart; std::map> files; +std::mutex fileMutex; void InitVersionInfo(); @@ -45,7 +47,7 @@ static void ExporterParseFileMode(const std::string& buildMode, ZFileMode& fileM if (File::Exists(otrFileName)) otrArchive = std::shared_ptr(new Ship::Archive(otrFileName, true)); else - otrArchive = Ship::Archive::CreateArchive(otrFileName, 65536 / 2); + otrArchive = Ship::Archive::CreateArchive(otrFileName, 40000); auto lst = Directory::ListFiles("Extract"); @@ -62,7 +64,7 @@ static void ExporterProgramEnd() if (Globals::Instance->fileMode == ZFileMode::ExtractDirectory) { printf("Generating OTR Archive...\n"); - otrArchive = Ship::Archive::CreateArchive(otrFileName, 65536 / 2); + otrArchive = Ship::Archive::CreateArchive(otrFileName, 40000); for (auto item : files) { @@ -79,9 +81,9 @@ static void ExporterProgramEnd() otrArchive->AddFile(StringHelper::Split(item, "Extract/")[1], (uintptr_t)fileData.data(), fileData.size()); } - otrArchive->AddFile("Audiobank", (uintptr_t)Globals::Instance->GetBaseromFile("Audiobank").data(), Globals::Instance->GetBaseromFile("Audiobank").size()); - otrArchive->AddFile("Audioseq", (uintptr_t)Globals::Instance->GetBaseromFile("Audioseq").data(), Globals::Instance->GetBaseromFile("Audioseq").size()); - otrArchive->AddFile("Audiotable", (uintptr_t)Globals::Instance->GetBaseromFile("Audiotable").data(), Globals::Instance->GetBaseromFile("Audiotable").size()); + //otrArchive->AddFile("Audiobank", (uintptr_t)Globals::Instance->GetBaseromFile("Audiobank").data(), Globals::Instance->GetBaseromFile("Audiobank").size()); + //otrArchive->AddFile("Audioseq", (uintptr_t)Globals::Instance->GetBaseromFile("Audioseq").data(), Globals::Instance->GetBaseromFile("Audioseq").size()); + //otrArchive->AddFile("Audiotable", (uintptr_t)Globals::Instance->GetBaseromFile("Audiotable").data(), Globals::Instance->GetBaseromFile("Audiotable").size()); } } @@ -158,7 +160,10 @@ static void ExporterResourceEnd(ZResource* res, BinaryWriter& writer) fName = StringHelper::Sprintf("%s/%s", oName.c_str(), rName.c_str()); if (Globals::Instance->fileMode == ZFileMode::ExtractDirectory) + { + std::unique_lock Lock(fileMutex); files[fName] = strem->ToVector(); + } else File::WriteAllBytes("Extract/" + fName, strem->ToVector()); } @@ -178,6 +183,17 @@ static void ExporterXMLEnd() { } +void AddFile(std::string fName, std::vector data) +{ + if (Globals::Instance->fileMode != ZFileMode::ExtractDirectory) + File::WriteAllBytes("Extract/" + fName, data); + else + { + std::unique_lock Lock(fileMutex); + files[fName] = data; + } +} + static void ImportExporters() { // In this example we set up a new exporter called "EXAMPLE". @@ -211,6 +227,7 @@ static void ImportExporters() exporterSet->exporters[ZResourceType::Text] = new OTRExporter_Text(); exporterSet->exporters[ZResourceType::Blob] = new OTRExporter_Blob(); exporterSet->exporters[ZResourceType::Mtx] = new OTRExporter_MtxExporter(); + exporterSet->exporters[ZResourceType::Audio] = new OTRExporter_Audio(); Globals::AddExporter("OTR", exporterSet); diff --git a/OTRExporter/OTRExporter/Main.h b/OTRExporter/OTRExporter/Main.h index af4ada763..df98bfe15 100644 --- a/OTRExporter/OTRExporter/Main.h +++ b/OTRExporter/OTRExporter/Main.h @@ -3,4 +3,6 @@ #include extern std::shared_ptr otrArchive; -extern std::map> files; \ No newline at end of file +extern std::map> files; + +void AddFile(std::string fName, std::vector data); \ No newline at end of file diff --git a/OTRExporter/OTRExporter/OTRExporter.vcxproj b/OTRExporter/OTRExporter/OTRExporter.vcxproj index 4ed7d72e1..9ec691d79 100644 --- a/OTRExporter/OTRExporter/OTRExporter.vcxproj +++ b/OTRExporter/OTRExporter/OTRExporter.vcxproj @@ -20,6 +20,7 @@ + @@ -44,6 +45,7 @@ + diff --git a/OTRExporter/OTRExporter/OTRExporter.vcxproj.filters b/OTRExporter/OTRExporter/OTRExporter.vcxproj.filters index 8ecc347dc..a8cf56737 100644 --- a/OTRExporter/OTRExporter/OTRExporter.vcxproj.filters +++ b/OTRExporter/OTRExporter/OTRExporter.vcxproj.filters @@ -81,6 +81,9 @@ Header Files + + Header Files + @@ -140,5 +143,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/OTRExporter/OTRExporter/RoomExporter.cpp b/OTRExporter/OTRExporter/RoomExporter.cpp index 9a5704bbc..fe022a691 100644 --- a/OTRExporter/OTRExporter/RoomExporter.cpp +++ b/OTRExporter/OTRExporter/RoomExporter.cpp @@ -453,13 +453,7 @@ void OTRExporter_Room::Save(ZResource* res, const fs::path& outPath, BinaryWrite OTRExporter_Cutscene cs; cs.Save(cmdSetCutscenes->cutscenes[0], "", &csWriter); - if (Globals::Instance->fileMode != ZFileMode::ExtractDirectory) - File::WriteAllBytes("Extract/" + fName, csStream->ToVector()); - else - files[fName] = csStream->ToVector(); - - //std::string fName = OTRExporter_DisplayList::GetPathToRes(res, vtxDecl->varName); - //otrArchive->AddFile(fName, (uintptr_t)csStream->ToVector().data(), csWriter.GetBaseAddress()); + AddFile(fName, csStream->ToVector()); } break; case RoomCommand::SetPathways: @@ -480,14 +474,7 @@ void OTRExporter_Room::Save(ZResource* res, const fs::path& outPath, BinaryWrite OTRExporter_Path pathExp; pathExp.Save(&cmdSetPathways->pathwayList, outPath, &pathWriter); - if (Globals::Instance->fileMode != ZFileMode::ExtractDirectory) - File::WriteAllBytes("Extract/" + path, pathStream->ToVector()); - else - files[path] = pathStream->ToVector(); - - //otrArchive->AddFile(path, (uintptr_t)pathStream->ToVector().data(), pathWriter.GetBaseAddress()); - - int bp = 0; + AddFile(path, pathStream->ToVector()); } } break; diff --git a/ZAPDTR/ZAPD/Main.cpp b/ZAPDTR/ZAPD/Main.cpp index 65a65fea6..a9f70eed5 100644 --- a/ZAPDTR/ZAPD/Main.cpp +++ b/ZAPDTR/ZAPD/Main.cpp @@ -39,7 +39,7 @@ extern "C" void Audio_SetGameVolume(int player_id, float volume) extern "C" int ResourceMgr_OTRSigCheck(char* imgData) { - + return 0; } void DebugConsole_SaveCVars() diff --git a/ZAPDTR/ZAPD/ZAPD.vcxproj b/ZAPDTR/ZAPD/ZAPD.vcxproj index 91ff515a2..940d3b2ed 100644 --- a/ZAPDTR/ZAPD/ZAPD.vcxproj +++ b/ZAPDTR/ZAPD/ZAPD.vcxproj @@ -195,6 +195,8 @@ + + @@ -288,6 +290,7 @@ + diff --git a/ZAPDTR/ZAPD/ZAPD.vcxproj.filters b/ZAPDTR/ZAPD/ZAPD.vcxproj.filters index c122b7e56..82fa8acb2 100644 --- a/ZAPDTR/ZAPD/ZAPD.vcxproj.filters +++ b/ZAPDTR/ZAPD/ZAPD.vcxproj.filters @@ -297,6 +297,12 @@ Source Files + + Source Files\Z64 + + + Source Files\Z64 + @@ -569,6 +575,9 @@ Header Files\Libraries + + Header Files\Z64 + diff --git a/ZAPDTR/ZAPD/ZAudio.cpp b/ZAPDTR/ZAPD/ZAudio.cpp new file mode 100644 index 000000000..1c24c824a --- /dev/null +++ b/ZAPDTR/ZAPD/ZAudio.cpp @@ -0,0 +1,325 @@ +#include "ZAudio.h" + +#include "Globals.h" +#include "Utils/BitConverter.h" +#include "Utils/File.h" +#include "Utils/Path.h" +#include "Utils/StringHelper.h" +#include "ZFile.h" + +REGISTER_ZFILENODE(Audio, ZAudio); + +ZAudio::ZAudio(ZFile* nParent) : ZResource(nParent) +{ + //RegisterRequiredAttribute("CodeOffset"); + //RegisterOptionalAttribute("LangOffset", "0"); +} + +void ZAudio::DecodeADPCMSample(SampleEntry* sample) +{ + int16_t buffer[1024 * 128]; + + int16_t* out = &buffer[0]; +} + +std::vector ZAudio::ParseEnvelopeData(std::vector audioBank, std::vector audioTable, int envelopeOffset, int baseOffset) +{ + std::vector result; + + //bool process = true; + + //for (int i = 0; i < 4; i++) + while (true) + { + AdsrEnvelope* env = new AdsrEnvelope(); + + env->delay = BitConverter::ToInt16BE(audioBank, envelopeOffset + 0); + env->arg = BitConverter::ToInt16BE(audioBank, envelopeOffset + 2); + + envelopeOffset += 4; + + result.push_back(env); + + if (env->delay < 0) + break; + } + + return result; +} + +SoundFontEntry* ZAudio::ParseSoundFontEntry(std::vector audioBank, + std::vector audioTable, + AudioTableEntry audioSampleBankEntry, + int soundFontOffset, + int baseOffset) +{ + SoundFontEntry* soundFont = new SoundFontEntry(); + soundFont->sampleEntry = ParseSampleEntry( + audioBank, audioTable, audioSampleBankEntry, + BitConverter::ToInt32BE(audioBank, soundFontOffset + 0) + baseOffset, baseOffset); + soundFont->tuning = BitConverter::ToFloatBE(audioBank, soundFontOffset + 4); + + return soundFont; +} + +SampleEntry* ZAudio::ParseSampleEntry(std::vector audioBank, + std::vector audioTable, + AudioTableEntry audioSampleBankEntry, + int sampleOffset, + int baseOffset) +{ + int sampleDataOffset = BitConverter::ToInt32BE(audioBank, sampleOffset + 4) + audioSampleBankEntry.ptr; + + if (samples.find(sampleOffset) == samples.end()) + { + SampleEntry* sample = new SampleEntry(); + + int sampleSize = BitConverter::ToInt32BE(audioBank, sampleOffset + 0) & 0x00FFFFFF; + int loopOffset = BitConverter::ToInt32BE(audioBank, sampleOffset + 8) + baseOffset; + int bookOffset = BitConverter::ToInt32BE(audioBank, sampleOffset + 12) + baseOffset; + + char* sampleData = (char*)malloc(sampleSize); + memcpy(sampleData, audioTable.data() + sampleDataOffset, sampleSize); + sample->data = std::vector(sampleSize); + memcpy(sample->data.data(), sampleData, sampleSize); + + uint32_t origField = (BitConverter::ToUInt32BE(audioBank, sampleOffset + 0)); + sample->codec = (origField >> 28) & 0x0F; + sample->medium = (origField >> 24) & 0x03; + sample->unk_bit26 = (origField >> 22) & 0x01; + sample->unk_bit25 = (origField >> 21) & 0x01; + + sample->loop.start = BitConverter::ToInt32BE(audioBank, loopOffset + 0); + sample->loop.end = BitConverter::ToInt32BE(audioBank, loopOffset + 4); + sample->loop.count = BitConverter::ToInt32BE(audioBank, loopOffset + 8); + + if (sample->loop.count != 0xFFFFFFFF) + { + for (int i = 0; i < sample->loop.count; i++) + { + int16_t state = BitConverter::ToInt16BE(sample->data, loopOffset + 16 + (i * 2)); + sample->loop.states.push_back(state); + } + } + + sample->book.order = BitConverter::ToInt32BE(audioBank, bookOffset + 0); + sample->book.npredictors = BitConverter::ToInt32BE(audioBank, bookOffset + 4); + + for (int i = 0; i < sample->book.npredictors * sample->book.order * 8; i++) + { + sample->book.books.push_back( + BitConverter::ToInt16BE(audioBank, bookOffset + 8 + (i * 2))); + } + + samples[sampleOffset] = sample; + + return sample; + } + else + { + return samples[sampleOffset]; + } +} + +std::vector ZAudio::ParseAudioTable(std::vector codeData, int baseOffset) +{ + std::vector entries; + + int numEntries = BitConverter::ToInt16BE(codeData, baseOffset + 0); + int romAddr = BitConverter::ToInt16BE(codeData, baseOffset + 4); + + int currentOffset = baseOffset + 16; + + for (int i = 0; i < numEntries; i++) + { + AudioTableEntry entry; + + entry.ptr = BitConverter::ToInt32BE(codeData, currentOffset + 0); + entry.size = BitConverter::ToInt32BE(codeData, currentOffset + 4); + entry.medium = codeData[currentOffset + 8]; + entry.cachePolicy = codeData[currentOffset + 9]; + entry.data1 = BitConverter::ToInt16BE(codeData, currentOffset + 10); + entry.data2 = BitConverter::ToInt16BE(codeData, currentOffset + 12); + entry.data3 = BitConverter::ToInt16BE(codeData, currentOffset + 14); + + entries.push_back(entry); + + currentOffset += 16; + } + + return entries; +} + +void ZAudio::ParseSoundFont(std::vector codeData, std::vector audioTable, + std::vector audioSampleBank, + AudioTableEntry& entry) +{ + int ptr = entry.ptr; + int size = entry.size; + int sampleBankId1 = (entry.data1 >> 8) & 0xFF; + int sampleBankId2 = (entry.data1) & 0xFF; + int numInstruments = (entry.data2 >> 8) & 0xFF; + int numDrums = entry.data2 & 0xFF; + int numSfx = entry.data3; + + int currentOffset = BitConverter::ToInt32BE(codeData, ptr) + ptr; + for (int i = 0; i < numDrums; i++) + { + DrumEntry drum; + + int samplePtr = BitConverter::ToInt32BE(codeData, currentOffset); + + if (samplePtr != 0) + { + samplePtr += ptr; + + drum.sample = ParseSampleEntry(codeData, audioTable, audioSampleBank[sampleBankId1], + BitConverter::ToInt32BE(codeData, samplePtr + 4) + ptr, ptr); + + drum.releaseRate = codeData[samplePtr + 0]; + drum.pan = codeData[samplePtr + 1]; + drum.loaded = codeData[samplePtr + 2]; + drum.tuning = BitConverter::ToFloatBE(codeData, samplePtr + 8); + + //int sampleDefOffset = BitConverter::ToInt32BE(codeData, samplePtr + 4); + drum.env = ParseEnvelopeData(codeData, audioTable, BitConverter::ToInt32BE(codeData, samplePtr + 12) + ptr, ptr); + } + + entry.drums.push_back(drum); + + currentOffset += 4; + } + + currentOffset = BitConverter::ToInt32BE(codeData, ptr + 4) + ptr; + for (int i = 0; i < numSfx; i++) + { + SoundFontEntry* sfx; + sfx = ParseSoundFontEntry(codeData, audioTable, audioSampleBank[sampleBankId1], + currentOffset, ptr); + entry.soundEffects.push_back(sfx); + + currentOffset += 8; + } + + for (int i = 0; i < numInstruments; i++) + { + InstrumentEntry instrument; + + currentOffset = BitConverter::ToInt32BE(codeData, ptr + 8 + (i * 4)); + + instrument.isValidInstrument = currentOffset != 0; + + if (currentOffset != 0) + { + currentOffset += ptr; + + instrument.loaded = codeData[currentOffset + 0]; + instrument.normalRangeLo = codeData[currentOffset + 1]; + instrument.normalRangeHi = codeData[currentOffset + 2]; + instrument.releaseRate = codeData[currentOffset + 3]; + instrument.env = ParseEnvelopeData(codeData, audioTable, BitConverter::ToInt32BE(codeData, currentOffset + 4) + ptr, ptr); + + if (BitConverter::ToInt32BE(codeData, currentOffset + 8) != 0) + instrument.lowNotesSound = ParseSoundFontEntry( + codeData, audioTable, audioSampleBank[sampleBankId1], currentOffset + 8, ptr); + + if (BitConverter::ToInt32BE(codeData, currentOffset + 16) != 0) + instrument.normalNotesSound = ParseSoundFontEntry( + codeData, audioTable, audioSampleBank[sampleBankId1], currentOffset + 16, ptr); + + if (BitConverter::ToInt32BE(codeData, currentOffset + 24) != 0 && + instrument.normalRangeHi != 0x7F) + instrument.highNotesSound = ParseSoundFontEntry( + codeData, audioTable, audioSampleBank[sampleBankId1], currentOffset + 24, ptr); + } + + entry.instruments.push_back(instrument); + } +} + +void ZAudio::ParseRawData() +{ + ZResource::ParseRawData(); + + std::vector codeData; + std::vector audioTableData; + std::vector audioBankData; + std::vector audioSeqData; + + if (Globals::Instance->fileMode == ZFileMode::ExtractDirectory) + codeData = Globals::Instance->GetBaseromFile("code"); + else + codeData = Globals::Instance->GetBaseromFile(Globals::Instance->baseRomPath.string() + "code"); + + if (Globals::Instance->fileMode == ZFileMode::ExtractDirectory) + audioTableData = Globals::Instance->GetBaseromFile("Audiotable"); + else + audioTableData = Globals::Instance->GetBaseromFile(Globals::Instance->baseRomPath.string() + "Audiotable"); + + if (Globals::Instance->fileMode == ZFileMode::ExtractDirectory) + audioBankData = Globals::Instance->GetBaseromFile("Audiobank"); + else + audioBankData = Globals::Instance->GetBaseromFile(Globals::Instance->baseRomPath.string() + "Audiobank"); + + if (Globals::Instance->fileMode == ZFileMode::ExtractDirectory) + audioSeqData = Globals::Instance->GetBaseromFile("Audioseq"); + else + audioSeqData = Globals::Instance->GetBaseromFile(Globals::Instance->baseRomPath.string() + + "Audioseq"); + + // TABLE PARSING + //int gSoundFontTableOffset = 0x138270; // OTRTODO: Make this an XML Param + //int gSequenceTableOffset = 0x1386A0; // OTRTODO: Make this an XML Param + //int gSampleBankTableOffset = 0x138D90; // OTRTODO: Make this an XML Param + int gSoundFontTableOffset = 0x138290; // OTRTODO: Make this an XML Param + int gSequenceTableOffset = 0x1386C0; // OTRTODO: Make this an XML Param + int gSampleBankTableOffset = 0x138DB0; // OTRTODO: Make this an XML Param + soundFontTable = ParseAudioTable(codeData, gSoundFontTableOffset); + sequenceTable = ParseAudioTable(codeData, gSequenceTableOffset); + sampleBankTable = ParseAudioTable(codeData, gSampleBankTableOffset); + // int gSequenceFontTableOffset = 0x1384E0; // OTRTODO: Make this an XML Param + + + // SAMPLE/FONT PARSING + for (int i = 0; i < soundFontTable.size(); i++) + { + ParseSoundFont(audioBankData, audioTableData, sampleBankTable, soundFontTable[i]); + } + + + // SOUNDBANK PARSING + /*for (int i = 0; i < sampleBankTable.size(); i++) + { + + }*/ + + // SEQUENCE PARSING + for (int i = 0; i < sequenceTable.size(); i++) + { + int seqDestIdx = i; + + if (sequenceTable[i].size == 0) + seqDestIdx = sequenceTable[i].ptr; + + std::vector seqVec = std::vector(sequenceTable[seqDestIdx].size); + memcpy(seqVec.data(), audioSeqData.data() + sequenceTable[seqDestIdx].ptr, + sequenceTable[seqDestIdx].size); + + sequences.push_back(seqVec); + } +} + +std::string ZAudio::GetSourceTypeName() const +{ + return "u8"; +} + +size_t ZAudio::GetRawDataSize() const +{ + return 1; +} + +ZResourceType ZAudio::GetResourceType() const +{ + return ZResourceType::Audio; +} diff --git a/ZAPDTR/ZAPD/ZAudio.h b/ZAPDTR/ZAPD/ZAudio.h new file mode 100644 index 000000000..a7c88410f --- /dev/null +++ b/ZAPDTR/ZAPD/ZAudio.h @@ -0,0 +1,122 @@ +#pragma once + +#include "ZResource.h" +#include "tinyxml2.h" + +struct AdsrEnvelope +{ + int16_t delay; + int16_t arg; +}; + +struct AdpcmBook +{ + /* 0x00 */ int32_t order; + /* 0x04 */ int32_t npredictors; + /* 0x08 */ std::vector books; // size 8 * order * npredictors. 8-byte aligned +}; + +struct AdpcmLoop +{ + /* 0x00 */ uint32_t start; + /* 0x04 */ uint32_t end; + /* 0x08 */ uint32_t count; + ///* 0x10 */ int16_t state[16]; // only exists if count != 0. 8-byte aligned + /* 0x10 */ std::vector states; +}; + +struct SampleEntry +{ + uint8_t codec; + uint8_t medium; + uint8_t unk_bit26; + uint8_t unk_bit25; + + std::vector data; + AdpcmLoop loop; + AdpcmBook book; +}; + +struct SoundFontEntry +{ + SampleEntry* sampleEntry = nullptr; + float tuning; +}; + +struct DrumEntry +{ + uint8_t releaseRate; + uint8_t pan; + uint8_t loaded; + uint32_t offset; + float tuning; + std::vector env; + //AdsrEnvelope* env = nullptr; + SampleEntry* sample = nullptr; +}; + +struct InstrumentEntry +{ + bool isValidInstrument; + uint8_t loaded; + uint8_t normalRangeLo; + uint8_t normalRangeHi; + uint8_t releaseRate; + std::vector env; + //AdsrEnvelope* env = nullptr; + SoundFontEntry* lowNotesSound = nullptr; + SoundFontEntry* normalNotesSound = nullptr; + SoundFontEntry* highNotesSound = nullptr; +}; + +struct AudioTableEntry +{ + uint32_t ptr; + uint32_t size; + uint8_t medium; + uint8_t cachePolicy; + uint16_t data1; + uint16_t data2; + uint16_t data3; + + std::vector drums; + std::vector soundEffects; + std::vector instruments; +}; + +class ZAudio : public ZResource +{ +public: + std::vector soundFontTable; + std::vector sequenceTable; + std::vector sampleBankTable; + std::vector> sequences; + std::map samples; + + ZAudio(ZFile* nParent); + + void DecodeADPCMSample(SampleEntry* sample); + std::vector ParseEnvelopeData(std::vector audioBank, std::vector audioTable, + int envelopeOffset, int baseOffset); + + SoundFontEntry* ParseSoundFontEntry(std::vector audioBank, + std::vector audioTable, + AudioTableEntry audioSampleBankEntry, + int soundFontOffset, + int baseOffset); + + SampleEntry* ParseSampleEntry(std::vector audioBank, std::vector audioTable, + AudioTableEntry audioSampleBankEntry, + int sampleOffset, int baseOffset); + + std::vector ParseAudioTable(std::vector codeData, int baseOffset); + void ParseSoundFont(std::vector codeData, std::vector audioTable, + std::vector audioSampleBank, AudioTableEntry& entry); + + void ParseRawData() override; + + std::string GetSourceTypeName() const override; + ZResourceType GetResourceType() const override; + + size_t GetRawDataSize() const override; +}; diff --git a/ZAPDTR/ZAPD/ZAudioDecode.cpp b/ZAPDTR/ZAPD/ZAudioDecode.cpp new file mode 100644 index 000000000..b8b066474 --- /dev/null +++ b/ZAPDTR/ZAPD/ZAudioDecode.cpp @@ -0,0 +1,669 @@ +/** + * Bruteforcing decoder for converting ADPCM-encoded AIFC into AIFF, in a way + * that roundtrips with vadpcm_enc. + */ +#include +#include +#include +#include +#include +#include +//#include + +typedef signed char s8; +typedef short s16; +typedef int s32; +typedef long long s64; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef float f32; +typedef double f64; + +#ifdef _MSC_VER +#define __builtin_bswap16 _byteswap_ushort +#define __builtin_bswap32 _byteswap_ulong +#endif + +#define bswap16(x) __builtin_bswap16(x) +#define bswap32(x) __builtin_bswap32(x) +#define BSWAP16(x) x = __builtin_bswap16(x) +#define BSWAP32(x) x = __builtin_bswap32(x) +#define BSWAP16_MANY(x, n) \ + for (s32 _i = 0; _i < n; _i++) \ + BSWAP16((x)[_i]) + +#define NORETURN __attribute__((noreturn)) +#define UNUSED __attribute__((unused)) + +typedef struct +{ + u32 ckID; + u32 ckSize; +} ChunkHeader; + +typedef struct +{ + u32 ckID; + u32 ckSize; + u32 formType; +} Chunk; + +typedef struct +{ + s16 numChannels; + u16 numFramesH; + u16 numFramesL; + s16 sampleSize; + s16 sampleRate[5]; // 80-bit float + u16 compressionTypeH; + u16 compressionTypeL; +} CommonChunk; + +typedef struct +{ + s16 MarkerID; + u16 positionH; + u16 positionL; +} Marker; + +typedef struct +{ + s16 playMode; + s16 beginLoop; + s16 endLoop; +} Loop; + +typedef struct +{ + s8 baseNote; + s8 detune; + s8 lowNote; + s8 highNote; + s8 lowVelocity; + s8 highVelocity; + s16 gain; + Loop sustainLoop; + Loop releaseLoop; +} InstrumentChunk; + +typedef struct +{ + s32 offset; + s32 blockSize; +} SoundDataChunk; + +typedef struct +{ + s16 version; + s16 order; + s16 nEntries; +} CodeChunk; + +typedef struct +{ + u32 start; + u32 end; + u32 count; + s16 state[16]; +} ALADPCMloop; + +static char usage[] = "input.aifc output.aiff"; +static const char *progname, *infilename; +static int framesize = 9; + +void fail_parse(const char* fmt, ...) +{ + char* formatted = NULL; + va_list ap; + va_start(ap, fmt); + int size = vsnprintf(NULL, 0, fmt, ap); + va_end(ap); + if (size >= 0) + { + size++; + formatted = (char*)malloc(size); + if (formatted != NULL) + { + va_start(ap, fmt); + size = vsnprintf(formatted, size, fmt, ap); + va_end(ap); + if (size < 0) + { + free(formatted); + formatted = NULL; + } + } + } + + if (formatted != NULL) + { + fprintf(stderr, "%s: %s [%s]\n", progname, formatted, infilename); + free(formatted); + } + exit(1); +} + + +s32 myrand() +{ + static u64 state = 1619236481962341ULL; + state *= 3123692312237ULL; + state += 1; + return state >> 33; +} + +s16 qsample(f32 x, s32 scale) +{ + if (x > 0.0f) + { + return (s16)((x / scale) + 0.4999999); + } + else + { + return (s16)((x / scale) - 0.4999999); + } +} + +void clamp_to_s16(f32* in, s32* out) +{ + f32 llevel = -0x8000; + f32 ulevel = 0x7fff; + + for (s32 i = 0; i < 16; i++) + { + if (in[i] > ulevel) + in[i] = ulevel; + if (in[i] < llevel) + in[i] = llevel; + + if (in[i] > 0.0f) + { + out[i] = (s32)(in[i] + 0.5); + } + else + { + out[i] = (s32)(in[i] - 0.5); + } + } +} + +s16 clamp_bits(s32 x, s32 bits) +{ + s32 lim = 1 << (bits - 1); + if (x < -lim) + return -lim; + if (x > lim - 1) + return lim - 1; + return x; +} + +s32 readaifccodebook(FILE* fhandle, s32**** table, s16* order, s16* npredictors) +{ + BSWAP16(*order); + BSWAP16(*npredictors); + *table = (s32***)malloc(*npredictors * sizeof(s32**)); + for (s32 i = 0; i < *npredictors; i++) + { + (*table)[i] = (s32**)malloc(8 * sizeof(s32*)); + for (s32 j = 0; j < 8; j++) + { + (*table)[i][j] = (s32*)malloc((*order + 8) * sizeof(s32)); + } + } + + for (s32 i = 0; i < *npredictors; i++) + { + s32** table_entry = (*table)[i]; + for (s32 j = 0; j < *order; j++) + { + for (s32 k = 0; k < 8; k++) + { + s16 ts = 0; + BSWAP16(ts); + table_entry[k][j] = ts; + } + } + + for (s32 k = 1; k < 8; k++) + { + table_entry[k][*order] = table_entry[k - 1][*order - 1]; + } + + table_entry[0][*order] = 1 << 11; + + for (s32 k = 1; k < 8; k++) + { + s32 j = 0; + for (; j < k; j++) + { + table_entry[j][k + *order] = 0; + } + + for (; j < 8; j++) + { + table_entry[j][k + *order] = table_entry[j - k][*order]; + } + } + } + return 0; +} + +ALADPCMloop* readlooppoints(FILE* ifile, s16* nloops) +{ + BSWAP16(*nloops); + ALADPCMloop* al = (ALADPCMloop*)malloc(*nloops * sizeof(ALADPCMloop)); + for (s32 i = 0; i < *nloops; i++) + { + BSWAP32(al[i].start); + BSWAP32(al[i].end); + BSWAP32(al[i].count); + BSWAP16_MANY(al[i].state, 16); + } + return al; +} + +s32 inner_product(s32 length, s32* v1, s32* v2) +{ + s32 out = 0; + for (s32 i = 0; i < length; i++) + { + out += v1[i] * v2[i]; + } + + // Compute "out / 2^11", rounded down. + s32 dout = out / (1 << 11); + s32 fiout = dout * (1 << 11); + return dout - (out - fiout < 0); +} + +void my_decodeframe(u8* frame, s32* decompressed, s32* state, s32 order, s32*** coefTable) +{ + s32 ix[16]; + + u8 header = frame[0]; + s32 scale = 1 << (header >> 4); + s32 optimalp = header & 0xf; + + if (framesize == 5) + { + for (s32 i = 0; i < 16; i += 4) + { + u8 c = frame[1 + i / 4]; + ix[i] = c >> 6; + ix[i + 1] = (c >> 4) & 0x3; + ix[i + 2] = (c >> 2) & 0x3; + ix[i + 3] = c & 0x3; + } + } + else + { + for (s32 i = 0; i < 16; i += 2) + { + u8 c = frame[1 + i / 2]; + ix[i] = c >> 4; + ix[i + 1] = c & 0xf; + } + } + + for (s32 i = 0; i < 16; i++) + { + if (framesize == 5) + { + if (ix[i] >= 2) + ix[i] -= 4; + } + else + { + if (ix[i] >= 8) + ix[i] -= 16; + } + decompressed[i] = ix[i]; + ix[i] *= scale; + } + + for (s32 j = 0; j < 2; j++) + { + s32 in_vec[16]; + if (j == 0) + { + for (s32 i = 0; i < order; i++) + { + in_vec[i] = state[16 - order + i]; + } + } + else + { + for (s32 i = 0; i < order; i++) + { + in_vec[i] = state[8 - order + i]; + } + } + + for (s32 i = 0; i < 8; i++) + { + s32 ind = j * 8 + i; + in_vec[order + i] = ix[ind]; + state[ind] = inner_product(order + i, coefTable[optimalp][i], in_vec) + ix[ind]; + } + } +} + +void get_bounds(s32* in, s32* decompressed, s32 scale, s32* minVals, s32* maxVals) +{ + s32 minv, maxv; + if (framesize == 9) + { + minv = -8; + maxv = 7; + } + else + { + minv = -2; + maxv = 1; + } + for (s32 i = 0; i < 16; i++) + { + s32 lo = in[i] - scale / 2; + s32 hi = in[i] + scale / 2; + lo -= scale; + hi += scale; + if (decompressed[i] == minv) + lo -= scale; + else if (decompressed[i] == maxv) + hi += scale; + minVals[i] = lo; + maxVals[i] = hi; + } +} + +void write_header(FILE* ofile, const char* id, s32 size) +{ + fwrite(id, 4, 1, ofile); + BSWAP32(size); + fwrite(&size, sizeof(s32), 1, ofile); +} + +char* OldMain(char* infilename) +{ + s16 order = -1; + s16 nloops = 0; + ALADPCMloop* aloops = NULL; + s16 npredictors = -1; + s32*** coefTable = NULL; + s32 state[16]; + s32 decompressed[16]; + s32 soundPointer = -1; + s32 currPos = 0; + s32 nSamples = 0; + Chunk FormChunk = Chunk(); + ChunkHeader Header = ChunkHeader(); + CommonChunk CommChunk = CommonChunk(); + InstrumentChunk InstChunk; + SoundDataChunk SndDChunk = SoundDataChunk(); + FILE* ifile = NULL; + FILE* ofile = NULL; + + if ((ifile = fopen(infilename, "rb")) == NULL) + { + fail_parse("AIFF-C file could not be opened"); + exit(1); + } + + memset(&InstChunk, 0, sizeof(InstChunk)); + + BSWAP32(FormChunk.ckID); + BSWAP32(FormChunk.formType); + if ((FormChunk.ckID != 0x464f524d) || (FormChunk.formType != 0x41494643)) + { // FORM, AIFC + fail_parse("not an AIFF-C file"); + } + + for (;;) + { + s32 num = fread(&Header, sizeof(Header), 1, ifile); + u32 ts = 0; + + if (num <= 0) + break; + + BSWAP32(Header.ckID); + BSWAP32(Header.ckSize); + + Header.ckSize++; + Header.ckSize &= ~1; + s32 offset = ftell(ifile); + + switch (Header.ckID) + { + case 0x434f4d4d: // COMM + { + BSWAP16(CommChunk.numChannels); + BSWAP16(CommChunk.numFramesH); + BSWAP16(CommChunk.numFramesL); + BSWAP16(CommChunk.sampleSize); + BSWAP16(CommChunk.compressionTypeH); + BSWAP16(CommChunk.compressionTypeL); + s32 cType = (CommChunk.compressionTypeH << 16) + CommChunk.compressionTypeL; + if (cType == 0x56415043 || cType == 0x41445039) + { // VAPC or ADP9 + framesize = 9; + } + else if (cType == 0x41445035) + { // ADP5 + framesize = 5; + } + else if (cType == 0x4850434d) + { // HPCM + framesize = 16; + } + else + { + char comprType[5] = { + CommChunk.compressionTypeH >> 8, CommChunk.compressionTypeH & 0xFF, + CommChunk.compressionTypeL >> 8, CommChunk.compressionTypeL & 0xFF, 0}; + fail_parse("file is of the wrong compression type [got %s (%08x)]", &comprType, + cType); + } + if (CommChunk.numChannels != 1) + { + fail_parse("file contains %d channels, only 1 channel supported", + CommChunk.numChannels); + } + if (CommChunk.sampleSize != 16) + { + fail_parse("file contains %d bit samples, only 16 bit samples supported", + CommChunk.sampleSize); + } + + nSamples = (CommChunk.numFramesH << 16) + CommChunk.numFramesL; + + // Allow broken input lengths + if (nSamples % 16) + { + nSamples -= (nSamples % 16); + } + + if (nSamples % 16 != 0) + { + fail_parse("number of chunks must be a multiple of 16, found %d with remainder %d", + nSamples, nSamples % 16); + } + } + break; + + case 0x53534e44: // SSND + BSWAP32(SndDChunk.offset); + BSWAP32(SndDChunk.blockSize); + assert(SndDChunk.offset == 0); + assert(SndDChunk.blockSize == 0); + soundPointer = ftell(ifile); + break; + + case 0x4150504c: // APPL + BSWAP32(ts); + if (ts == 0x73746f63) + { // stoc + u8 len = 0; + if (len == 11) + { + char ChunkName[12]; + s16 version; + ChunkName[11] = '\0'; + if (strcmp("VADPCMCODES", ChunkName) == 0) + { + BSWAP16(version); + if (version != 1) + { + fail_parse("Unknown codebook chunk version"); + } + readaifccodebook(ifile, &coefTable, &order, &npredictors); + } + else if (strcmp("VADPCMLOOPS", ChunkName) == 0) + { + BSWAP16(version); + if (version != 1) + { + fail_parse("Unknown loop chunk version"); + } + aloops = readlooppoints(ifile, &nloops); + if (nloops != 1) + { + fail_parse("Only a single loop supported"); + } + } + } + } + break; + } + + fseek(ifile, offset + Header.ckSize, SEEK_SET); + } + + if (coefTable == NULL) + { + fail_parse("Codebook missing from bitstream"); + } + + for (s32 i = 0; i < order; i++) + { + state[15 - i] = 0; + } + + u32 outputBytes = nSamples * sizeof(s16); + u8* outputBuf = (u8*)malloc(outputBytes); + + fseek(ifile, soundPointer, SEEK_SET); + s32 fails = 0; + while (currPos < nSamples) + { + u8 input[9]; + u8 encoded[9]; + s32 lastState[16]; + s32 decoded[16]; + s16 guess[16]; + s16 origGuess[16]; + + memcpy(lastState, state, sizeof(state)); + + // Decode for real + my_decodeframe(input, decompressed, state, order, coefTable); + memcpy(decoded, state, sizeof(state)); + + // Create a guess from that, by clamping to 16 bits + for (s32 i = 0; i < 16; i++) + { + origGuess[i] = clamp_bits(state[i], 16); + } + + memcpy(state, decoded, sizeof(state)); + memcpy(outputBuf + currPos * 2, decoded, sizeof(decoded)); + currPos += 16; + } + if (fails) + { + fprintf(stderr, "%s %d\n", infilename, fails); + } + + // Write an incomplete file header. We'll fill in the size later. + fwrite("FORM\0\0\0\0AIFF", 12, 1, ofile); + + // Subtract 4 from the COMM size to skip the compression field. + write_header(ofile, "COMM", sizeof(CommonChunk) - 4); + CommChunk.numFramesH = nSamples >> 16; + CommChunk.numFramesL = nSamples & 0xffff; + BSWAP16(CommChunk.numChannels); + BSWAP16(CommChunk.numFramesH); + BSWAP16(CommChunk.numFramesL); + BSWAP16(CommChunk.sampleSize); + fwrite(&CommChunk, sizeof(CommonChunk) - 4, 1, ofile); + + if (nloops > 0) + { + s32 startPos = aloops[0].start, endPos = aloops[0].end; + const char* markerNames[2] = {"start", "end"}; + Marker markers[2] = {{1, startPos >> 16, startPos & 0xffff}, + {2, endPos >> 16, endPos & 0xffff}}; + write_header(ofile, "MARK", 2 + 2 * sizeof(Marker) + 1 + 5 + 1 + 3); + s16 numMarkers = bswap16(2); + fwrite(&numMarkers, sizeof(s16), 1, ofile); + for (s32 i = 0; i < 2; i++) + { + u8 len = (u8)strlen(markerNames[i]); + BSWAP16(markers[i].MarkerID); + BSWAP16(markers[i].positionH); + BSWAP16(markers[i].positionL); + fwrite(&markers[i], sizeof(Marker), 1, ofile); + fwrite(&len, 1, 1, ofile); + fwrite(markerNames[i], len, 1, ofile); + } + + write_header(ofile, "INST", sizeof(InstrumentChunk)); + InstChunk.sustainLoop.playMode = bswap16(1); + InstChunk.sustainLoop.beginLoop = bswap16(1); + InstChunk.sustainLoop.endLoop = bswap16(2); + InstChunk.releaseLoop.playMode = 0; + InstChunk.releaseLoop.beginLoop = 0; + InstChunk.releaseLoop.endLoop = 0; + fwrite(&InstChunk, sizeof(InstrumentChunk), 1, ofile); + } + + // Save the coefficient table for use when encoding. Ideally this wouldn't + // be needed and "tabledesign -s 1" would generate the right table, but in + // practice it's difficult to adjust samples to make that happen. + write_header(ofile, "APPL", 4 + 12 + sizeof(CodeChunk) + npredictors * order * 8 * 2); + fwrite("stoc", 4, 1, ofile); + CodeChunk cChunk; + cChunk.version = bswap16(1); + cChunk.order = bswap16(order); + cChunk.nEntries = bswap16(npredictors); + fwrite("\x0bVADPCMCODES", 12, 1, ofile); + fwrite(&cChunk, sizeof(CodeChunk), 1, ofile); + for (s32 i = 0; i < npredictors; i++) + { + for (s32 j = 0; j < order; j++) + { + for (s32 k = 0; k < 8; k++) + { + s16 ts = bswap16(coefTable[i][k][j]); + fwrite(&ts, sizeof(s16), 1, ofile); + } + } + } + + write_header(ofile, "SSND", outputBytes + 8); + SndDChunk.offset = 0; + SndDChunk.blockSize = 0; + fwrite(&SndDChunk, sizeof(SoundDataChunk), 1, ofile); + fwrite(outputBuf, outputBytes, 1, ofile); + + // Fix the size in the header + s32 fileSize = bswap32(ftell(ofile) - 8); + fseek(ofile, 4, SEEK_SET); + fwrite(&fileSize, 4, 1, ofile); + + fclose(ifile); + fclose(ofile); + return 0; +} \ No newline at end of file diff --git a/ZAPDTR/ZAPD/ZResource.h b/ZAPDTR/ZAPD/ZResource.h index 82709aec0..8b1055a4b 100644 --- a/ZAPDTR/ZAPD/ZResource.h +++ b/ZAPDTR/ZAPD/ZResource.h @@ -50,7 +50,8 @@ enum class ZResourceType TextureAnimationParams, Vector, Vertex, - Text + Text, + Audio }; class ResourceAttribute diff --git a/ZAPDTR/ZAPD/ZRom.cpp b/ZAPDTR/ZAPD/ZRom.cpp index 2a1d3c8a1..a311714dd 100644 --- a/ZAPDTR/ZAPD/ZRom.cpp +++ b/ZAPDTR/ZAPD/ZRom.cpp @@ -191,6 +191,8 @@ ZRom::ZRom(std::string romPath) } else files[lines[i]] = outData; + + //File::WriteAllBytes(StringHelper::Sprintf("baserom/%s", lines[i]), files[lines[i]]); } int bp = 0; diff --git a/libultraship/libultraship/Archive.cpp b/libultraship/libultraship/Archive.cpp index e4646c305..8bc4948b2 100644 --- a/libultraship/libultraship/Archive.cpp +++ b/libultraship/libultraship/Archive.cpp @@ -142,7 +142,7 @@ namespace Ship { return FileToLoad; } - bool Archive::AddFile(const std::string& path, uintptr_t fileData, DWORD dwFileSize) { + bool Archive::AddFile(const std::string& oPath, uintptr_t fileData, DWORD dwFileSize) { HANDLE hFile; #ifdef _WIN32 SYSTEMTIME sysTime; @@ -154,6 +154,11 @@ namespace Ship { time_t stupidHack; time(&stupidHack); #endif + + std::string path = oPath; + + StringHelper::ReplaceOriginal(path, "\\", "/"); + if (!SFileCreateFile(mainMPQ, path.c_str(), stupidHack, dwFileSize, 0, MPQ_FILE_COMPRESS, &hFile)) { SPDLOG_ERROR("({}) Failed to create file of {} bytes {} in archive {}", GetLastError(), dwFileSize, path.c_str(), MainPath.c_str()); return false; diff --git a/libultraship/libultraship/Array.h b/libultraship/libultraship/Array.h index 6bccb2d7d..a84d8ec17 100644 --- a/libultraship/libultraship/Array.h +++ b/libultraship/libultraship/Array.h @@ -67,6 +67,7 @@ namespace Ship TextureAnimationParams, Vector, Vertex, + Audio }; class ArrayV0 : public ResourceFile diff --git a/libultraship/libultraship/Audio.cpp b/libultraship/libultraship/Audio.cpp new file mode 100644 index 000000000..801fac554 --- /dev/null +++ b/libultraship/libultraship/Audio.cpp @@ -0,0 +1,168 @@ +#include "Audio.h" + +namespace Ship +{ + void AudioSampleV1::ParseFileBinary(BinaryReader* reader, Resource* res) + { + AudioSample* entry = (AudioSample*)res; + + ResourceFile::ParseFileBinary(reader, res); + + entry->codec = reader->ReadByte(); + entry->medium = reader->ReadByte(); + entry->unk_bit26 = reader->ReadByte(); + entry->unk_bit25 = reader->ReadByte(); + + int dataSize = reader->ReadInt32(); + + for (size_t i = 0; i < dataSize; i++) + entry->data.push_back(reader->ReadUByte()); + + entry->loop.start = reader->ReadUInt32(); + entry->loop.end = reader->ReadUInt32(); + entry->loop.count = reader->ReadUInt32(); + + int loopStateCnt = reader->ReadUInt32(); + + for (size_t i = 0; i < loopStateCnt; i++) + entry->loop.states.push_back(reader->ReadInt16()); + + entry->book.order = reader->ReadInt32(); + entry->book.npredictors = reader->ReadInt32(); + + int bookSize = reader->ReadInt32(); + + for (size_t i = 0; i < bookSize; i++) + entry->book.books.push_back(reader->ReadInt16()); + } + + void AudioSoundFontV1::ParseFileBinary(BinaryReader* reader, Resource* res) + { + AudioSoundFont* soundFont = (AudioSoundFont*)res; + + ResourceFile::ParseFileBinary(reader, res); + + soundFont->medium = reader->ReadByte(); + soundFont->cachePolicy = reader->ReadByte(); + soundFont->data1 = reader->ReadInt16(); + soundFont->data2 = reader->ReadInt16(); + soundFont->data3 = reader->ReadInt16(); + + int drumCnt = reader->ReadInt32(); + int instrumentCnt = reader->ReadInt32(); + int sfxCnt = reader->ReadInt32(); + + for (int i = 0; i < drumCnt; i++) + { + DrumEntry drum; + drum.releaseRate = reader->ReadUByte(); + drum.pan = reader->ReadUByte(); + drum.loaded = reader->ReadUByte(); + + drum.env = ReadEnvelopeData(reader); + + bool hasSample = reader->ReadByte(); + drum.offset = reader->ReadInt32(); + drum.tuning = reader->ReadSingle(); + + soundFont->drums.push_back(drum); + } + + for (int i = 0; i < instrumentCnt; i++) + { + InstrumentEntry entry; + + entry.isValidEntry = reader->ReadByte(); + entry.loaded = reader->ReadByte(); + entry.normalRangeLo = reader->ReadByte(); + entry.normalRangeHi = reader->ReadByte(); + entry.releaseRate = reader->ReadByte(); + + entry.env = ReadEnvelopeData(reader); + + { + bool hasSFEntry = reader->ReadByte(); + + if (hasSFEntry) + { + entry.lowNotesSound = new SoundFontEntry(); + bool hasSampleRef = reader->ReadByte(); + entry.lowNotesSound->sampleOffset = reader->ReadInt32(); + entry.lowNotesSound->tuning = reader->ReadSingle(); + } + } + + { + bool hasSFEntry = reader->ReadByte(); + + if (hasSFEntry) + { + entry.normalNotesSound = new SoundFontEntry(); + bool hasSampleRef = reader->ReadByte(); + entry.normalNotesSound->sampleOffset = reader->ReadInt32(); + entry.normalNotesSound->tuning = reader->ReadSingle(); + } + } + + { + bool hasSFEntry = reader->ReadByte(); + + if (hasSFEntry) + { + entry.highNotesSound = new SoundFontEntry(); + bool hasSampleRef = reader->ReadByte(); + entry.highNotesSound->sampleOffset = reader->ReadInt32(); + entry.highNotesSound->tuning = reader->ReadSingle(); + } + } + + soundFont->instruments.push_back(entry); + } + + for (int i = 0; i < sfxCnt; i++) + { + SoundFontEntry* entry = new SoundFontEntry(); + + bool hasSFEntry = reader->ReadByte(); + + if (hasSFEntry) + { + bool hasSampleRef = reader->ReadByte(); + entry->sampleOffset = reader->ReadInt32(); + entry->tuning = reader->ReadSingle(); + } + + soundFont->soundEffects.push_back(entry); + } + } + + std::vector AudioSoundFontV1::ReadEnvelopeData(BinaryReader* reader) + { + std::vector envelopes; + + int envelopeCnt = reader->ReadInt32(); + + for (int i = 0; i < envelopeCnt; i++) + { + AdsrEnvelope* env = new AdsrEnvelope(); + env->delay = reader->ReadInt16(); + env->arg = reader->ReadInt16(); + + envelopes.push_back(env); + } + + return envelopes; + } + + void AudioV1::ParseFileBinary(BinaryReader* reader, Resource* res) + { + Audio* audio = (Audio*)res; + + ResourceFile::ParseFileBinary(reader, res); + + //int sampleCnt = reader->ReadInt32(); + + //for (size_t i = 0; i < sampleCnt; i++) + //audio->samples.push_back(ReadSampleEntry(reader)); + } +} \ No newline at end of file diff --git a/libultraship/libultraship/Audio.h b/libultraship/libultraship/Audio.h new file mode 100644 index 000000000..e66846bc0 --- /dev/null +++ b/libultraship/libultraship/Audio.h @@ -0,0 +1,120 @@ +#pragma once + +#include "Resource.h" +#include +#include + +namespace Ship +{ + struct AdsrEnvelope + { + int16_t delay; + int16_t arg; + }; + + struct AdpcmBook + { + /* 0x00 */ int32_t order; + /* 0x04 */ int32_t npredictors; + /* 0x08 */ std::vector books; // size 8 * order * npredictors. 8-byte aligned + }; + + struct AdpcmLoop + { + /* 0x00 */ uint32_t start; + /* 0x04 */ uint32_t end; + /* 0x08 */ uint32_t count; + ///* 0x10 */ int16_t state[16]; // only exists if count != 0. 8-byte aligned + /* 0x10 */ std::vector states; + }; + + struct SoundFontEntry + { + //SampleEntry* sampleEntry = nullptr; + uint32_t sampleOffset; + float tuning; + }; + + struct DrumEntry + { + uint8_t releaseRate; + uint8_t pan; + uint8_t loaded; + uint32_t offset; + float tuning; + std::vector env; + //SampleEntry* sample = nullptr; + }; + + struct InstrumentEntry + { + bool isValidEntry; + uint8_t loaded; + uint8_t normalRangeLo; + uint8_t normalRangeHi; + uint8_t releaseRate; + std::vector env; + SoundFontEntry* lowNotesSound = nullptr; + SoundFontEntry* normalNotesSound = nullptr; + SoundFontEntry* highNotesSound = nullptr; + }; + + class AudioSoundFontV1 : public ResourceFile + { + public: + void ParseFileBinary(BinaryReader* reader, Resource* res) override; + static std::vector ReadEnvelopeData(BinaryReader* reader); + }; + + class AudioSampleV1 : public ResourceFile + { + public: + void ParseFileBinary(BinaryReader* reader, Resource* res) override; + }; + + class AudioV1 : public ResourceFile + { + public: + void ParseFileBinary(BinaryReader* reader, Resource* res) override; + }; + + struct AudioSoundFont : public Resource + { + public: + uint32_t ptr; + uint32_t size; + uint8_t medium; + uint8_t cachePolicy; + uint16_t data1; + uint16_t data2; + uint16_t data3; + + std::vector drums; + std::vector soundEffects; + std::vector instruments; + }; + + class AudioSample : public Resource + { + public: + uint8_t codec; + uint8_t medium; + uint8_t unk_bit26; + uint8_t unk_bit25; + + std::vector data; + AdpcmLoop loop; + AdpcmBook book; + }; + + class Audio : public Resource + { + public: + //std::vector soundFontTable; + //std::vector sequenceTable; + //std::vector sampleBankTable; + //std::vector sequences; + //std::vector samples; + + }; +} \ No newline at end of file diff --git a/libultraship/libultraship/Cutscene.cpp b/libultraship/libultraship/Cutscene.cpp index 4d804167b..420d491f1 100644 --- a/libultraship/libultraship/Cutscene.cpp +++ b/libultraship/libultraship/Cutscene.cpp @@ -16,6 +16,4 @@ void Ship::CutsceneV0::ParseFileBinary(BinaryReader* reader, Resource* res) cs->commands.push_back(data); } - - //int bp = 0; } diff --git a/libultraship/libultraship/Factories/AudioFactory.cpp b/libultraship/libultraship/Factories/AudioFactory.cpp new file mode 100644 index 000000000..e4639feae --- /dev/null +++ b/libultraship/libultraship/Factories/AudioFactory.cpp @@ -0,0 +1,69 @@ +#include "AudioFactory.h" + +namespace Ship +{ + Audio* AudioFactory::ReadAudio(BinaryReader* reader) + { + Audio* audio = new Audio(); + Version version = (Version)reader->ReadUInt32(); + + switch (version) + { + case Version::Roy: + { + AudioV1 audioFac = AudioV1(); + audioFac.ParseFileBinary(reader, audio); + } + break; + default: + // VERSION NOT SUPPORTED + break; + } + + return audio; + } + + AudioSample* AudioSampleFactory::ReadAudioSample(BinaryReader* reader) + { + AudioSample* audioSample = new AudioSample(); + Version version = (Version)reader->ReadUInt32(); + + switch (version) + { + case Version::Deckard: // OTRTODO: Remove this line after we merge in that refactor + case Version::Roy: + { + AudioSampleV1 audioSampleFac = AudioSampleV1(); + audioSampleFac.ParseFileBinary(reader, audioSample); + } + break; + default: + // VERSION NOT SUPPORTED + break; + } + + return audioSample; + } + + AudioSoundFont* AudioSoundFontFactory::ReadAudioSoundFont(BinaryReader* reader) + { + AudioSoundFont* audioSF = new AudioSoundFont(); + Version version = (Version)reader->ReadUInt32(); + + switch (version) + { + case Version::Deckard: // OTRTODO: Remove this line after we merge in that refactor + case Version::Roy: + { + AudioSoundFontV1 audioSFFac = AudioSoundFontV1(); + audioSFFac.ParseFileBinary(reader, audioSF); + } + break; + default: + // VERSION NOT SUPPORTED + break; + } + + return audioSF; + } +}; \ No newline at end of file diff --git a/libultraship/libultraship/Factories/AudioFactory.h b/libultraship/libultraship/Factories/AudioFactory.h new file mode 100644 index 000000000..40c0c689b --- /dev/null +++ b/libultraship/libultraship/Factories/AudioFactory.h @@ -0,0 +1,23 @@ +#include "../Audio.h" +#include "Utils/BinaryReader.h" + +namespace Ship +{ + class AudioFactory + { + public: + static Audio* ReadAudio(BinaryReader* reader); + }; + + class AudioSampleFactory + { + public: + static AudioSample* ReadAudioSample(BinaryReader* reader); + }; + + class AudioSoundFontFactory + { + public: + static AudioSoundFont* ReadAudioSoundFont(BinaryReader* reader); + }; +} \ No newline at end of file diff --git a/libultraship/libultraship/Factories/ResourceLoader.cpp b/libultraship/libultraship/Factories/ResourceLoader.cpp index 7faec20f7..099d71931 100644 --- a/libultraship/libultraship/Factories/ResourceLoader.cpp +++ b/libultraship/libultraship/Factories/ResourceLoader.cpp @@ -15,6 +15,7 @@ #include "TextureFactory.h" #include "BlobFactory.h" #include "MtxFactory.h" +#include "AudioFactory.h" #include namespace Ship @@ -84,6 +85,15 @@ namespace Ship case ResourceType::Matrix: result = MtxFactory::ReadMtx(reader.get()); break; + case ResourceType::Audio: + result = AudioFactory::ReadAudio(reader.get()); + break; + case ResourceType::AudioSample: + result = AudioSampleFactory::ReadAudioSample(reader.get()); + break; + case ResourceType::AudioSoundFont: + result = AudioSoundFontFactory::ReadAudioSoundFont(reader.get()); + break; default: // RESOURCE TYPE NOT SUPPORTED break; diff --git a/libultraship/libultraship/Resource.h b/libultraship/libultraship/Resource.h index ae8dc6ce5..01949675e 100644 --- a/libultraship/libultraship/Resource.h +++ b/libultraship/libultraship/Resource.h @@ -30,6 +30,9 @@ namespace Ship Array = 0x4F415252, // OARR Text = 0x4F545854, // OTXT Blob = 0x4F424C42, // OBLB + Audio = 'OAUD', + AudioSample = 'OSMP', + AudioSoundFont = 'OSFT', }; enum class DataType diff --git a/libultraship/libultraship/StormLibDUS32.lib b/libultraship/libultraship/StormLibDUS32.lib index 4619586a9..35259b775 100644 Binary files a/libultraship/libultraship/StormLibDUS32.lib and b/libultraship/libultraship/StormLibDUS32.lib differ diff --git a/libultraship/libultraship/StormLibRUS32.lib b/libultraship/libultraship/StormLibRUS32.lib index 23af59fe5..aab5f12a0 100644 Binary files a/libultraship/libultraship/StormLibRUS32.lib and b/libultraship/libultraship/StormLibRUS32.lib differ diff --git a/libultraship/libultraship/StormLibRUS64.lib b/libultraship/libultraship/StormLibRUS64.lib index 266f5ee2f..6803e9462 100644 Binary files a/libultraship/libultraship/StormLibRUS64.lib and b/libultraship/libultraship/StormLibRUS64.lib differ diff --git a/libultraship/libultraship/libultraship.vcxproj b/libultraship/libultraship/libultraship.vcxproj index b3448e7b0..5439e7466 100644 --- a/libultraship/libultraship/libultraship.vcxproj +++ b/libultraship/libultraship/libultraship.vcxproj @@ -253,9 +253,11 @@ + + @@ -340,10 +342,12 @@ + + diff --git a/libultraship/libultraship/libultraship.vcxproj.filters b/libultraship/libultraship/libultraship.vcxproj.filters index 8ac4f0afb..074b32bbe 100644 --- a/libultraship/libultraship/libultraship.vcxproj.filters +++ b/libultraship/libultraship/libultraship.vcxproj.filters @@ -345,6 +345,12 @@ Source Files\CustomImpl\Overlay + + Source Files\Resources\Files + + + Source Files\Resources\Factories + @@ -638,5 +644,11 @@ Source Files\CustomImpl\Overlay + + Header Files\Resources\Files + + + Header Files\Resources\Factories + \ No newline at end of file diff --git a/soh/assets/xml/GC_NMQ_D/audio/Audio.xml b/soh/assets/xml/GC_NMQ_D/audio/Audio.xml new file mode 100644 index 000000000..af810ab0c --- /dev/null +++ b/soh/assets/xml/GC_NMQ_D/audio/Audio.xml @@ -0,0 +1,5 @@ + + + + diff --git a/soh/assets/xml/GC_NMQ_PAL_F/audio/Audio.xml b/soh/assets/xml/GC_NMQ_PAL_F/audio/Audio.xml new file mode 100644 index 000000000..af810ab0c --- /dev/null +++ b/soh/assets/xml/GC_NMQ_PAL_F/audio/Audio.xml @@ -0,0 +1,5 @@ + + + + diff --git a/soh/include/z64.h b/soh/include/z64.h index 7720144b4..05a32a1f9 100644 --- a/soh/include/z64.h +++ b/soh/include/z64.h @@ -30,7 +30,8 @@ #include "ichain.h" #include "regs.h" -#define AUDIO_HEAP_SIZE 0x38000 +//#define AUDIO_HEAP_SIZE 0x38000 +#define AUDIO_HEAP_SIZE 0x3800000 // The original audio heap size was too small. The heap would exceed capacity and corrupt everything in its path. #define SYSTEM_HEAP_SIZE (1024 * 1024 * 4) #ifdef __cplusplus diff --git a/soh/include/z64audio.h b/soh/include/z64audio.h index 78f262dbf..09840def7 100644 --- a/soh/include/z64audio.h +++ b/soh/include/z64audio.h @@ -120,7 +120,8 @@ typedef struct { /* 0x08 */ s16 book[1]; // size 8 * order * npredictors. 8-byte aligned } AdpcmBook; // size >= 0x8 -typedef struct { +typedef struct +{ union { struct { /* 0x00 */ u32 codec : 4; diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 9401d514a..e5c7b7e58 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -36,6 +36,7 @@ #include #include +#include OTRGlobals* OTRGlobals::Instance; @@ -57,8 +58,15 @@ extern "C" void AudioMgr_CreateNextAudioBuffer(s16* samples, u32 num_samples); extern "C" void AudioPlayer_Play(const uint8_t* buf, uint32_t len); extern "C" int AudioPlayer_Buffered(void); extern "C" int AudioPlayer_GetDesiredBuffered(void); +extern "C" void ResourceMgr_CacheDirectory(const char* resName); // C->C++ Bridge +extern "C" void OTRAudio_Init() +{ + // Precache all our samples, sequences, etc... + ResourceMgr_CacheDirectory("audio"); +} + extern "C" void InitOTR() { OTRGlobals::Instance = new OTRGlobals(); auto t = OTRGlobals::Instance->context->GetResourceManager()->LoadFile("version"); @@ -72,6 +80,7 @@ extern "C" void InitOTR() { clearMtx = (uintptr_t)&gMtxClear; OTRMessage_Init(); + OTRAudio_Init(); DebugConsole_Init(); Debug_Init(); } @@ -530,12 +539,235 @@ extern "C" CollisionHeader* ResourceMgr_LoadColByName(const char* path) return (CollisionHeader*)colHeader; } -extern "C" Vtx * ResourceMgr_LoadVtxByName(const char* path) +extern "C" Vtx* ResourceMgr_LoadVtxByName(const char* path) { auto res = std::static_pointer_cast(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(path)); return (Vtx*)res->vertices.data(); } +extern "C" char* ResourceMgr_LoadSeqByID(int seqID) +{ + std::string fmtStr = "audio/sequences/seq_%02X"; + return OTRGlobals::Instance->context->GetResourceManager()->LoadFile(StringHelper::Sprintf(fmtStr.c_str(), seqID)).get()->buffer.get(); +} + +extern "C" int ResourceMgr_GetSeqSizeByID(int seqID) +{ + return OTRGlobals::Instance->context->GetResourceManager() + ->LoadFile(StringHelper::Sprintf("audio/sequences/seq_%02X", seqID)) + .get() + ->dwBufferSize; +} + +std::map cachedCustomSFs; + +extern "C" SoundFontSample* ResourceMgr_LoadAudioSample(int romOffset) +{ + auto str = StringHelper::Sprintf("audio/samples/sample_%08X", romOffset); + + if (cachedCustomSFs.find(str) != cachedCustomSFs.end()) + return cachedCustomSFs[str]; + + // Check if our file is actually a wav... + auto sampleRaw = OTRGlobals::Instance->context->GetResourceManager()->LoadFile(str); + uint32_t* strem = (uint32_t*)sampleRaw->buffer.get(); + uint8_t* strem2 = (uint8_t*)strem; + + if (strem2[0] == 'R' && strem2[1] == 'I' && strem2[2] == 'F' && strem2[3] == 'F') + { + SoundFontSample* sampleC = (SoundFontSample*)malloc(sizeof(SoundFontSample)); + + *strem++; // RIFF + *strem++; // size + *strem++; // WAVE + + *strem++; // fmt + int fmtChunkSize = *strem++; + // OTRTODO: Make sure wav format is what the audio driver wants! + + strem = (uint32_t*)&strem2[0x0C + fmtChunkSize + 8 + 4]; + sampleC->size = *strem++; + sampleC->sampleAddr = (uint8_t*)strem; + sampleC->codec = CODEC_S16; + + // OTRTODO: Grab loop data from wav + sampleC->loop = (AdpcmLoop*)malloc(sizeof(AdpcmLoop)); + sampleC->loop->start = 0; + sampleC->loop->end = sampleC->size / 2; + sampleC->loop->count = 0; + + cachedCustomSFs[str] = sampleC; + return sampleC; + } + + auto sample = std::static_pointer_cast(OTRGlobals::Instance->context->GetResourceManager()->LoadResource(str)); + + if (sample == nullptr) + return NULL; + + if (sample->cachedGameAsset != nullptr) + { + SoundFontSample* sampleC = (SoundFontSample*)sample->cachedGameAsset; + + return (SoundFontSample*)sample->cachedGameAsset; + } + else + { + SoundFontSample* sampleC = (SoundFontSample*)malloc(sizeof(SoundFontSample)); + + sampleC->sampleAddr = sample->data.data(); + + sampleC->size = sample->data.size(); + sampleC->codec = sample->codec; + sampleC->medium = sample->medium; + sampleC->unk_bit26 = sample->unk_bit26; + sampleC->unk_bit25 = sample->unk_bit25; + + sampleC->book = (AdpcmBook*)malloc(sizeof(AdpcmBook) + (sample->book.books.size() * sizeof(int16_t))); + sampleC->book->npredictors = sample->book.npredictors; + sampleC->book->order = sample->book.order; + + for (int i = 0; i < sample->book.books.size(); i++) + sampleC->book->book[i] = sample->book.books[i]; + + sampleC->loop = (AdpcmLoop*)malloc(sizeof(AdpcmLoop)); + sampleC->loop->start = sample->loop.start; + sampleC->loop->end = sample->loop.end; + sampleC->loop->count = sample->loop.count; + + for (int i = 0; i < sample->loop.states.size(); i++) + sampleC->loop->state[i] = sample->loop.states[i]; + + sample->cachedGameAsset = sampleC; + return sampleC; + } +} + +extern "C" SoundFont* ResourceMgr_LoadAudioSoundFont(int fontIndex) { + auto soundFont = + std::static_pointer_cast(OTRGlobals::Instance->context->GetResourceManager()->LoadResource( + StringHelper::Sprintf("audio/fonts/font_%02X", fontIndex))); + + if (soundFont == nullptr) + return NULL; + + if (soundFont->cachedGameAsset != nullptr) + { + return (SoundFont*)soundFont->cachedGameAsset; + } + else + { + SoundFont* soundFontC = (SoundFont*)malloc(sizeof(SoundFont)); + + soundFontC->numDrums = soundFont->drums.size(); + soundFontC->numInstruments = soundFont->instruments.size(); + soundFontC->numSfx = soundFont->soundEffects.size(); + soundFontC->sampleBankId1 = soundFont->data1 >> 8; + soundFontC->sampleBankId2 = soundFont->data1 & 0xFF; + + soundFontC->drums = (Drum**)malloc(sizeof(Drum*) * soundFont->drums.size()); + + for (int i = 0; i < soundFont->drums.size(); i++) + { + Drum* drum = (Drum*)malloc(sizeof(Drum)); + + drum->releaseRate = soundFont->drums[i].releaseRate; + drum->pan = soundFont->drums[i].pan; + drum->loaded = 0; + + if (soundFont->drums[i].env.size() == 0) + drum->envelope = NULL; + else + { + drum->envelope = (AdsrEnvelope*)malloc(sizeof(AdsrEnvelope) * soundFont->drums[i].env.size()); + + for (int k = 0; k < soundFont->drums[i].env.size(); k++) + { + drum->envelope[k].delay = BOMSWAP16(soundFont->drums[i].env[k]->delay); + drum->envelope[k].arg = BOMSWAP16(soundFont->drums[i].env[k]->arg); + } + } + + drum->sound.sample = ResourceMgr_LoadAudioSample(soundFont->drums[i].offset); + drum->sound.tuning = soundFont->drums[i].tuning; + + soundFontC->drums[i] = drum; + } + + soundFontC->instruments = (Instrument**)malloc(sizeof(Instrument*) * soundFont->instruments.size()); + + for (int i = 0; i < soundFont->instruments.size(); i++) { + + if (soundFont->instruments[i].isValidEntry) + { + Instrument* inst = (Instrument*)malloc(sizeof(Instrument)); + + inst->loaded = 0; + inst->releaseRate = soundFont->instruments[i].releaseRate; + inst->normalRangeLo = soundFont->instruments[i].normalRangeLo; + inst->normalRangeHi = soundFont->instruments[i].normalRangeHi; + + if (soundFont->instruments[i].env.size() == 0) + inst->envelope = NULL; + else + { + inst->envelope = (AdsrEnvelope*)malloc(sizeof(AdsrEnvelope) * soundFont->instruments[i].env.size()); + + for (int k = 0; k < soundFont->instruments[i].env.size(); k++) + { + inst->envelope[k].delay = BOMSWAP16(soundFont->instruments[i].env[k]->delay); + inst->envelope[k].arg = BOMSWAP16(soundFont->instruments[i].env[k]->arg); + } + } + if (soundFont->instruments[i].lowNotesSound != nullptr) + { + inst->lowNotesSound.sample = + ResourceMgr_LoadAudioSample(soundFont->instruments[i].lowNotesSound->sampleOffset); + inst->lowNotesSound.tuning = soundFont->instruments[i].lowNotesSound->tuning; + } else { + inst->lowNotesSound.sample = NULL; + inst->lowNotesSound.tuning = 0; + } + + if (soundFont->instruments[i].normalNotesSound != nullptr) { + inst->normalNotesSound.sample = + ResourceMgr_LoadAudioSample(soundFont->instruments[i].normalNotesSound->sampleOffset); + inst->normalNotesSound.tuning = soundFont->instruments[i].normalNotesSound->tuning; + + } else { + inst->normalNotesSound.sample = NULL; + inst->normalNotesSound.tuning = 0; + } + + if (soundFont->instruments[i].highNotesSound != nullptr) { + inst->highNotesSound.sample = + ResourceMgr_LoadAudioSample(soundFont->instruments[i].highNotesSound->sampleOffset); + inst->highNotesSound.tuning = soundFont->instruments[i].highNotesSound->tuning; + } else { + inst->highNotesSound.sample = NULL; + inst->highNotesSound.tuning = 0; + } + + soundFontC->instruments[i] = inst; + } else + { + soundFontC->instruments[i] = nullptr; + } + } + + soundFontC->soundEffects = (SoundFontSound*)malloc(sizeof(SoundFontSound) * soundFont->soundEffects.size()); + + for (int i = 0; i < soundFont->soundEffects.size(); i++) + { + soundFontC->soundEffects[i].sample = ResourceMgr_LoadAudioSample(soundFont->soundEffects[i]->sampleOffset); + soundFontC->soundEffects[i].tuning = soundFont->soundEffects[i]->tuning; + } + + soundFont->cachedGameAsset = soundFontC; + return soundFontC; + } +} + extern "C" int ResourceMgr_OTRSigCheck(char* imgData) { uintptr_t i = (uintptr_t)(imgData); diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index 0a318de73..d21d94ca5 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -24,8 +24,8 @@ private: #endif #ifndef __cplusplus -void InitOTR(); -void Graph_ProcessFrame(void (*run_one_game_iter)(void)); +void OTRAudio_Init(); +void InitAudio(); void Graph_StartFrame(); void Graph_ProcessGfxCommands(Gfx* commands); void OTRLogString(const char* src); diff --git a/soh/soh/stubs.c b/soh/soh/stubs.c index 98f3b4823..26f4d0ae6 100644 --- a/soh/soh/stubs.c +++ b/soh/soh/stubs.c @@ -445,7 +445,9 @@ void osViExtendVStart(u32 arg0) } - +#if 0 +AudioTable gSoundFontTable = { 0 }; +#else AudioTable gSoundFontTable = { 0x0026, 0x0000, 0x00000000, @@ -795,7 +797,11 @@ AudioTable gSoundFontTable = { 0x0026, }, } }; +#endif +#if 0 +AudioTable gSequenceTable = { 0 }; +#else AudioTable gSequenceTable = { 0x006E, 0x0000, 0x00000000, @@ -1585,7 +1591,7 @@ AudioTable gSequenceTable = { 0x006E, 0x0000, }, { - 0x00000028, + 0x00000028, 0x00000000, 0x02, 0x02, @@ -1791,9 +1797,12 @@ AudioTable gSequenceTable = { 0x006E, 0x0000, 0x0000, }, - } - }; + } }; +#endif +#if 0 +AudioTable gSampleBankTable = { 0 }; +#else AudioTable gSampleBankTable = { 0x0007, 0x0000, 0x00000000, @@ -1864,7 +1873,9 @@ AudioTable gSampleBankTable = { 0x0007, }, } }; +#endif +// OTRTODO: Implement This in OTR File u8 gSequenceFontTable[0x1C0] = { 0xDC, 0x00, diff --git a/soh/src/code/audio_heap.c b/soh/src/code/audio_heap.c index 765fd8b09..84e91d439 100644 --- a/soh/src/code/audio_heap.c +++ b/soh/src/code/audio_heap.c @@ -10,6 +10,8 @@ void AudioHeap_DiscardSampleCaches(void); void AudioHeap_DiscardSampleBank(s32 sampleBankId); void AudioHeap_DiscardSampleBanks(void); +extern bool gUseLegacySD; + f32 func_800DDE20(f32 arg0) { return 256.0f * gAudioContext.audioBufferParameters.unkUpdatesPerFrameScaled / arg0; } @@ -1201,7 +1203,11 @@ void AudioHeap_DiscardSampleCacheEntry(SampleCacheEntry* entry) { } } -void AudioHeap_UnapplySampleCache(SampleCacheEntry* entry, SoundFontSample* sample) { +void AudioHeap_UnapplySampleCache(SampleCacheEntry* entry, SoundFontSample* sample) +{ + if (!gUseLegacySD) + return; + if (sample != NULL) { if (sample->sampleAddr == entry->allocatedAddr) { sample->sampleAddr = entry->sampleAddr; diff --git a/soh/src/code/audio_load.c b/soh/src/code/audio_load.c index 7af9037a6..ecb6eab2d 100644 --- a/soh/src/code/audio_load.c +++ b/soh/src/code/audio_load.c @@ -4,6 +4,7 @@ #include "ultra64.h" #include "global.h" +#include "soh/OTRGlobals.h" #define MK_ASYNC_MSG(retData, tableType, id, status) (((retData) << 24) | ((tableType) << 16) | ((id) << 8) | (status)) #define ASYNC_TBLTYPE(v) ((u8)(v >> 16)) @@ -37,7 +38,7 @@ void AudioLoad_ProcessAsyncLoads(s32 resetStatus); void AudioLoad_ProcessAsyncLoadUnkMedium(AudioAsyncLoad* asyncLoad, s32 resetStatus); void AudioLoad_ProcessAsyncLoad(AudioAsyncLoad* asyncLoad, s32 resetStatus); void AudioLoad_RelocateFontAndPreloadSamples(s32 fontId, SoundFontData* mem, RelocInfo* relocInfo, s32 temporary); -void AudioLoad_RelocateSample(SoundFontSound* sound, SoundFontData* mem, RelocInfo* relocInfo); +void AudioLoad_RelocateSample(SoundFontSound* sound, SoundFontData* mem, RelocInfo* relocInfo, int fontId); void AudioLoad_DiscardFont(s32 fontId); u32 AudioLoad_TrySyncLoadSampleBank(u32 sampleBankId, u32* outMedium, s32 noLoad); void* AudioLoad_SyncLoad(u32 tableType, u32 tableId, s32* didAllocate); @@ -74,6 +75,11 @@ void* sUnusedHandler = NULL; s32 gAudioContextInitalized = false; +uintptr_t fontStart; +uint32_t fontOffsets[8192]; + +bool gUseLegacySD = false; + void AudioLoad_DecreaseSampleDmaTtls(void) { u32 i; @@ -278,8 +284,11 @@ void AudioLoad_InitSampleDmaBuffers(s32 arg0) { } s32 AudioLoad_IsFontLoadComplete(s32 fontId) { + return true; + if (fontId == 0xFF) { return true; + } else if (gAudioContext.fontLoadStatus[fontId] >= 2) { return true; } else if (gAudioContext.fontLoadStatus[AudioLoad_GetRealTableIndex(FONT_TABLE, fontId)] >= 2) { @@ -352,6 +361,9 @@ void AudioLoad_InitTable(AudioTable* table, uintptr_t romAddr, u16 unkMediumPara for (i = 0; i < table->numEntries; i++) { if ((table->entries[i].size != 0) && (table->entries[i].medium == MEDIUM_CART)) { + if (romAddr == fontStart) + fontOffsets[i] = table->entries[i].romAddr; + table->entries[i].romAddr += romAddr; } } @@ -400,7 +412,8 @@ void AudioLoad_SyncLoadSeqParts(s32 seqId, s32 arg1) { s32 AudioLoad_SyncLoadSample(SoundFontSample* sample, s32 fontId) { void* sampleAddr; - if (sample->unk_bit25 == 1) { + if (sample->unk_bit25 == 1) + { if (sample->medium != MEDIUM_RAM) { sampleAddr = AudioHeap_AllocSampleCache(sample->size, fontId, (void*)sample->sampleAddr, sample->medium, CACHE_PERSISTENT); @@ -544,7 +557,7 @@ s32 AudioLoad_SyncInitSeqPlayerInternal(s32 playerIdx, s32 seqId, s32 arg2) { s32 numFonts; s32 fontId; - if (seqId >= gAudioContext.numSequences) { + if (gUseLegacySD && seqId >= gAudioContext.numSequences) { return 0; } @@ -556,18 +569,27 @@ s32 AudioLoad_SyncInitSeqPlayerInternal(s32 playerIdx, s32 seqId, s32 arg2) { while (numFonts > 0) { fontId = gAudioContext.sequenceFontTable[index++]; - AudioLoad_SyncLoadFont(fontId); + + //if (gUseLegacySD) + AudioLoad_SyncLoadFont(fontId); + numFonts--; } seqData = AudioLoad_SyncLoadSeq(seqId); + if (seqData == NULL) { return 0; } AudioSeq_ResetSequencePlayer(seqPlayer); seqPlayer->seqId = seqId; - seqPlayer->defaultFont = AudioLoad_GetRealTableIndex(FONT_TABLE, fontId); + + if (gUseLegacySD) + seqPlayer->defaultFont = AudioLoad_GetRealTableIndex(FONT_TABLE, fontId); + else + seqPlayer->defaultFont = fontId; + seqPlayer->seqData = seqData; seqPlayer->enabled = 1; seqPlayer->scriptState.pc = seqData; @@ -633,12 +655,22 @@ SoundFontData* AudioLoad_SyncLoadFont(u32 fontId) { s32 didAllocate; RelocInfo relocInfo; s32 realFontId = AudioLoad_GetRealTableIndex(FONT_TABLE, fontId); + //s32 realFontId = fontId; if (gAudioContext.fontLoadStatus[realFontId] == 1) { return NULL; } - sampleBankId1 = gAudioContext.soundFonts[realFontId].sampleBankId1; - sampleBankId2 = gAudioContext.soundFonts[realFontId].sampleBankId2; + + if (!gUseLegacySD) { + SoundFont* sf = ResourceMgr_LoadAudioSoundFont(fontId); + + sampleBankId1 = sf->sampleBankId1; + sampleBankId2 = sf->sampleBankId2; + } else { + + sampleBankId1 = gAudioContext.soundFonts[realFontId].sampleBankId1; + sampleBankId2 = gAudioContext.soundFonts[realFontId].sampleBankId2; + } relocInfo.sampleBankId1 = sampleBankId1; relocInfo.sampleBankId2 = sampleBankId2; @@ -681,13 +713,39 @@ void* AudioLoad_SyncLoad(u32 tableType, u32 id, s32* didAllocate) { if (ret != NULL) { *didAllocate = false; status = 2; - } else { - table = AudioLoad_GetLoadTable(tableType); - size = table->entries[realId].size; - size = ALIGN16(size); - medium = table->entries[id].medium; - cachePolicy = table->entries[id].cachePolicy; - romAddr = table->entries[realId].romAddr; + } else + { + char* seqData = 0; + SoundFont* fnt; + + if (!gUseLegacySD && tableType == SEQUENCE_TABLE) + { + seqData = ResourceMgr_LoadSeqByID(id); + size = ResourceMgr_GetSeqSizeByID(id); + size -= 2; + medium = seqData[0]; + cachePolicy = seqData[1]; + romAddr = 0; + seqData += 2; + } + else if (!gUseLegacySD && tableType == FONT_TABLE) + { + fnt = ResourceMgr_LoadAudioSoundFont(id); + size = sizeof(SoundFont); + medium = 2; + cachePolicy = 0; + romAddr = 0; + } + else + { + table = AudioLoad_GetLoadTable(tableType); + size = table->entries[realId].size; + size = ALIGN16(size); + medium = table->entries[id].medium; + cachePolicy = table->entries[id].cachePolicy; + romAddr = table->entries[realId].romAddr; + } + switch (cachePolicy) { case 0: ret = AudioHeap_AllocPermanent(tableType, realId, size); @@ -720,7 +778,14 @@ void* AudioLoad_SyncLoad(u32 tableType, u32 id, s32* didAllocate) { if (medium == MEDIUM_UNK) { AudioLoad_SyncDmaUnkMedium(romAddr, ret, size, (s16)table->unkMediumParam); } else { - AudioLoad_SyncDma(romAddr, ret, size, medium); + if (!gUseLegacySD && tableType == SEQUENCE_TABLE && seqData != NULL) { + AudioLoad_SyncDma(seqData, ret, size, medium); + } else if (!gUseLegacySD && tableType == FONT_TABLE) { + AudioLoad_SyncDma(fnt, ret, size, medium); + } + else { + AudioLoad_SyncDma(romAddr, ret, size, medium); + } } status = cachePolicy == 0 ? 5 : 2; @@ -743,9 +808,16 @@ void* AudioLoad_SyncLoad(u32 tableType, u32 id, s32* didAllocate) { return ret; } -u32 AudioLoad_GetRealTableIndex(s32 tableType, u32 id) { +u32 AudioLoad_GetRealTableIndex(s32 tableType, u32 id) +{ + if ((tableType == SEQUENCE_TABLE || tableType == FONT_TABLE) && !gUseLegacySD) { + return id; + } + AudioTable* table = AudioLoad_GetLoadTable(tableType); + // If the size is 0, then this entry actually redirects to another entry. + // The rom address is actually an index into the same table where the "real" data is. if (table->entries[id].size == 0) { id = table->entries[id].romAddr; } @@ -796,29 +868,63 @@ void AudioLoad_RelocateFont(s32 fontId, SoundFontData* mem, RelocInfo* relocInfo Drum* drum; SoundFontSound* sfx; s32 i; - s32 numDrums = gAudioContext.soundFonts[fontId].numDrums; - s32 numInstruments = gAudioContext.soundFonts[fontId].numInstruments; - s32 numSfx = gAudioContext.soundFonts[fontId].numSfx; + SoundFont* sf = ResourceMgr_LoadAudioSoundFont(fontId); + + s32 numDrums = 0; + s32 numInstruments = 0; + s32 numSfx = 0; + + if (gUseLegacySD) { + numDrums = gAudioContext.soundFonts[fontId].numDrums; + numInstruments = gAudioContext.soundFonts[fontId].numInstruments; + numSfx = gAudioContext.soundFonts[fontId].numSfx; + } else { + numDrums = sf->numDrums; + numInstruments = sf->numInstruments; + numSfx = sf->numSfx; + } + void** ptrs = (void**)mem; + #define BASE_OFFSET(x) (void*)((u32)(x) + (u32)(mem)) reloc2 = ptrs[0]; if (1) {} - if ((reloc2 != 0) && (numDrums != 0)) { + if ((reloc2 != 0 || !gUseLegacySD) && (numDrums != 0)) + { ptrs[0] = BASE_OFFSET(reloc2); - for (i = 0; i < numDrums; i++) { - reloc = ((Drum**)ptrs[0])[i]; + for (i = 0; i < numDrums; i++) + { + if (gUseLegacySD) + reloc = ((Drum**)ptrs[0])[i]; - if (reloc != 0) { - reloc = BASE_OFFSET(reloc); + if (reloc != 0 || !gUseLegacySD) + { + if (gUseLegacySD) + { + reloc = BASE_OFFSET(reloc); + ((Drum**)ptrs[0])[i] = drum = reloc; + } - ((Drum**)ptrs[0])[i] = drum = reloc; - if (!drum->loaded) { - AudioLoad_RelocateSample(&drum->sound, mem, relocInfo); - reloc = drum->envelope; - drum->envelope = BASE_OFFSET(reloc); - drum->loaded = 1; + if (!gUseLegacySD) + drum = sf->drums[i]; + + if (!drum->loaded) + { + if (!gUseLegacySD) + { + AudioLoad_RelocateSample(&sf->drums[i]->sound, mem, relocInfo, fontOffsets[fontId]); + //reloc = drum->envelope; + drum->loaded = 1; + } + else + { + AudioLoad_RelocateSample(&drum->sound, mem, relocInfo, fontOffsets[fontId]); + reloc = drum->envelope; + drum->envelope = BASE_OFFSET(reloc); + drum->loaded = 1; + } } } } @@ -826,39 +932,56 @@ void AudioLoad_RelocateFont(s32 fontId, SoundFontData* mem, RelocInfo* relocInfo reloc2 = ptrs[1]; if (1) {} - if ((reloc2 != 0) && (numSfx != 0)) { + if ((reloc2 != 0 || !gUseLegacySD) && (numSfx != 0)) { ptrs[1] = BASE_OFFSET(reloc2); for (i = 0; i < numSfx; i++) { reloc = (SoundFontSound*)ptrs[1] + i; - if (reloc != 0) { - sfx = reloc; - if (sfx->sample != NULL) { - AudioLoad_RelocateSample(sfx, mem, relocInfo); + if (reloc != 0 || !gUseLegacySD) + { + if (!gUseLegacySD) { + AudioLoad_RelocateSample(&sf->soundEffects[i].sample, mem, relocInfo, fontOffsets[fontId]); + } else { + sfx = reloc; + if (sfx->sample != NULL) { + AudioLoad_RelocateSample(sfx, mem, relocInfo, fontOffsets[fontId]); + } } } } } - + if (numInstruments > 0x7E) { numInstruments = 0x7E; } - for (i = 2; i <= 2 + numInstruments - 1; i++) { - if (ptrs[i] != NULL) { + int startI = gUseLegacySD ? 2 : 0; + int startEC = gUseLegacySD ? 2 + numInstruments - 1 : numInstruments - 1; + //for (i = 2; i <= 2 + numInstruments - 1; i++) { + for (i = startI; i <= startEC; i++) { + if (!gUseLegacySD || ptrs[i] != NULL) + { ptrs[i] = BASE_OFFSET(ptrs[i]); - inst = ptrs[i]; - if (!inst->loaded) { - if (inst->normalRangeLo != 0) { - AudioLoad_RelocateSample(&inst->lowNotesSound, mem, relocInfo); + if (gUseLegacySD) + inst = ptrs[i]; + else + inst = sf->instruments[i]; + + if (inst != NULL && !inst->loaded) { + if (inst->normalRangeLo != 0) + { + AudioLoad_RelocateSample(&inst->lowNotesSound, mem, relocInfo, fontOffsets[fontId]); } - AudioLoad_RelocateSample(&inst->normalNotesSound, mem, relocInfo); + AudioLoad_RelocateSample(&inst->normalNotesSound, mem, relocInfo, fontOffsets[fontId]); if (inst->normalRangeHi != 0x7F) { - AudioLoad_RelocateSample(&inst->highNotesSound, mem, relocInfo); + AudioLoad_RelocateSample(&inst->highNotesSound, mem, relocInfo, fontOffsets[fontId]); } reloc = inst->envelope; - inst->envelope = BASE_OFFSET(reloc); + + if (gUseLegacySD) + inst->envelope = BASE_OFFSET(reloc); + inst->loaded = 1; } } @@ -866,9 +989,11 @@ void AudioLoad_RelocateFont(s32 fontId, SoundFontData* mem, RelocInfo* relocInfo #undef BASE_OFFSET - gAudioContext.soundFonts[fontId].drums = ptrs[0]; - gAudioContext.soundFonts[fontId].soundEffects = ptrs[1]; - gAudioContext.soundFonts[fontId].instruments = (Instrument**)(ptrs + 2); + if (gUseLegacySD) { + gAudioContext.soundFonts[fontId].drums = ptrs[0]; + gAudioContext.soundFonts[fontId].soundEffects = ptrs[1]; + gAudioContext.soundFonts[fontId].instruments = (Instrument**)(ptrs + 2); + } } void AudioLoad_SyncDma(u32 devAddr, u8* addr, size_t size, s32 medium) { @@ -1270,17 +1395,21 @@ void AudioLoad_Init(void* heap, u32 heapSize) { uintptr_t bankStart = ResourceMgr_LoadFileRaw(_AudiobankSegmentRomStart); uintptr_t tableStart = ResourceMgr_LoadFileRaw(_AudiotableSegmentRomStart); + fontStart = bankStart; + AudioLoad_InitTable(gAudioContext.sequenceTable, seqStart, 0); AudioLoad_InitTable(gAudioContext.soundFontTable, bankStart, 0); AudioLoad_InitTable(gAudioContext.sampleBankTable, tableStart, 0); numFonts = gAudioContext.soundFontTable->numEntries; gAudioContext.soundFonts = AudioHeap_Alloc(&gAudioContext.audioInitPool, numFonts * sizeof(SoundFont)); - for (i = 0; i < numFonts; i++) { - AudioLoad_InitSoundFontMeta(i); - } + if (gUseLegacySD) { + for (i = 0; i < numFonts; i++) { + AudioLoad_InitSoundFontMeta(i); + } - AudioLoad_InitSwapFont(); + AudioLoad_InitSwapFont(); + } if (temp_v0_3 = AudioHeap_Alloc(&gAudioContext.audioInitPool, D_8014A6C4.permanentPoolSize), temp_v0_3 == NULL) { // cast away const from D_8014A6C4 @@ -1454,28 +1583,40 @@ s32 AudioLoad_SlowLoadSeq(s32 seqId, u8* ramAddr, s8* isDone) { AudioTable* seqTable; size_t size; - if (seqId >= gAudioContext.numSequences) { + if (gUseLegacySD && seqId >= gAudioContext.numSequences) { *isDone = 0; return -1; } seqId = AudioLoad_GetRealTableIndex(SEQUENCE_TABLE, seqId); + seqTable = AudioLoad_GetLoadTable(SEQUENCE_TABLE); + slowLoad = &gAudioContext.slowLoads[gAudioContext.slowLoadPos]; if (slowLoad->status == LOAD_STATUS_DONE) { slowLoad->status = LOAD_STATUS_WAITING; } + slowLoad->sample.sampleAddr = NULL; slowLoad->isDone = isDone; - size = seqTable->entries[seqId].size; - size = ALIGN16(size); + + if (!gUseLegacySD) { + char* seqData = ResourceMgr_LoadSeqByID(seqId); + size = ResourceMgr_GetSeqSizeByID(seqId) - 2; + slowLoad->curDevAddr = seqData + 2; + slowLoad->medium = seqData[0]; + } else { + size = seqTable->entries[seqId].size; + size = ALIGN16(size); + slowLoad->curDevAddr = seqTable->entries[seqId].romAddr; + slowLoad->medium = seqTable->entries[seqId].medium; + } + slowLoad->curRamAddr = ramAddr; slowLoad->status = LOAD_STATUS_START; slowLoad->bytesRemaining = size; slowLoad->ramAddr = ramAddr; - slowLoad->curDevAddr = seqTable->entries[seqId].romAddr; - slowLoad->medium = seqTable->entries[seqId].medium; slowLoad->seqOrFontId = seqId; if (slowLoad->medium == MEDIUM_UNK) { @@ -1687,7 +1828,7 @@ void AudioLoad_AsyncDmaUnkMedium(u32 devAddr, void* ramAddr, size_t size, s16 ar #define RELOC(v, base) (reloc = (void*)((u32)(v) + (u32)(base))) -void AudioLoad_RelocateSample(SoundFontSound* sound, SoundFontData* mem, RelocInfo* relocInfo) { +void AudioLoad_RelocateSample(SoundFontSound* sound, SoundFontData* mem, RelocInfo* relocInfo, int fontId) { // OTRTODO: This is hack to detect whether or not the sample has been relocated. size_t maxSoundBankSize = 0x3EB2A0; // sample bank 0 is largest size at 0x3EB2A0 if ((uintptr_t)mem <= maxSoundBankSize) { @@ -1699,32 +1840,58 @@ void AudioLoad_RelocateSample(SoundFontSound* sound, SoundFontData* mem, RelocIn void* reloc; // OTRTODO: Seems precarious to assume the RAM is never <= 0x3EB2A0, but it largely works. - if ((uintptr_t)sound->sample < maxSoundBankSize) { - sample = sound->sample = RELOC(sound->sample, mem); - if (sample->size != 0 && sample->unk_bit25 != 1) { - sample->loop = RELOC(sample->loop, mem); - sample->book = RELOC(sample->book, mem); + if ((uintptr_t)sound->sample < maxSoundBankSize || !gUseLegacySD) + { + if (!gUseLegacySD) { + SoundFontSample* sample2 = sound; - // Resolve the sample medium 2-bit bitfield into a real value based on relocInfo. - switch (sample->medium) { - case 0: - sample->sampleAddr = RELOC(sample->sampleAddr, relocInfo->baseAddr1); - sample->medium = relocInfo->medium1; - break; - case 1: - sample->sampleAddr = RELOC(sample->sampleAddr, relocInfo->baseAddr2); - sample->medium = relocInfo->medium2; - break; - case 2: - case 3: - // Invalid? This leaves sample->medium as MEDIUM_CART and MEDIUM_DISK_DRIVE - // respectively, and the sampleAddr unrelocated. - break; + if (sample2->unk_bit25 != 1) + { + //if (sample2->size == 0x5CC8) + //{ + // switch (sample2->medium) { + // case 0: + // sample2->medium = relocInfo->medium1; + // break; + // case 1: + // sample2->medium = relocInfo->medium2; + // break; + // } + + // sample2->unk_bit25 = 1; + // if (sample2->unk_bit26 && (sample2->medium != MEDIUM_RAM)) { + // // gAudioContext.usedSamples[gAudioContext.numUsedSamples++] = sample2; + // } + //} } + } else { + sample = sound->sample = RELOC(sound->sample, mem); - sample->unk_bit25 = 1; - if (sample->unk_bit26 && (sample->medium != MEDIUM_RAM)) { - gAudioContext.usedSamples[gAudioContext.numUsedSamples++] = sample; + if (sample->size != 0 && sample->unk_bit25 != 1) { + sample->loop = RELOC(sample->loop, mem); + sample->book = RELOC(sample->book, mem); + + // Resolve the sample medium 2-bit bitfield into a real value based on relocInfo. + switch (sample->medium) { + case 0: + sample->sampleAddr = RELOC(sample->sampleAddr, relocInfo->baseAddr1); + sample->medium = relocInfo->medium1; + break; + case 1: + sample->sampleAddr = RELOC(sample->sampleAddr, relocInfo->baseAddr2); + sample->medium = relocInfo->medium2; + break; + case 2: + case 3: + // Invalid? This leaves sample->medium as MEDIUM_CART and MEDIUM_DISK_DRIVE + // respectively, and the sampleAddr unrelocated. + break; + } + + sample->unk_bit25 = 1; + if (sample->unk_bit26 && (sample->medium != MEDIUM_RAM)) { + gAudioContext.usedSamples[gAudioContext.numUsedSamples++] = sample; + } } } } @@ -1977,9 +2144,17 @@ void AudioLoad_PreloadSamplesForFont(s32 fontId, s32 async, RelocInfo* relocInfo gAudioContext.numUsedSamples = 0; - numDrums = gAudioContext.soundFonts[fontId].numDrums; - numInstruments = gAudioContext.soundFonts[fontId].numInstruments; - numSfx = gAudioContext.soundFonts[fontId].numSfx; + if (!gUseLegacySD) { + SoundFont* sf = ResourceMgr_LoadAudioSoundFont(fontId); + + numDrums = sf->numDrums; + numInstruments = sf->numInstruments; + numSfx = sf->numSfx; + } else { + numDrums = gAudioContext.soundFonts[fontId].numDrums; + numInstruments = gAudioContext.soundFonts[fontId].numInstruments; + numSfx = gAudioContext.soundFonts[fontId].numSfx; + } for (i = 0; i < numInstruments; i++) { instrument = Audio_GetInstrumentInner(fontId, i); @@ -2024,6 +2199,7 @@ void AudioLoad_PreloadSamplesForFont(s32 fontId, s32 async, RelocInfo* relocInfo } sample = gAudioContext.usedSamples[i]; + if (sample->medium == MEDIUM_RAM) { continue; } @@ -2100,10 +2276,19 @@ void AudioLoad_LoadPermanentSamples(void) { for (i = 0; i < gAudioContext.permanentPool.count; i++) { RelocInfo relocInfo; - if (gAudioContext.permanentCache[i].tableType == FONT_TABLE) { + if (gAudioContext.permanentCache[i].tableType == FONT_TABLE) + { fontId = AudioLoad_GetRealTableIndex(FONT_TABLE, gAudioContext.permanentCache[i].id); - relocInfo.sampleBankId1 = gAudioContext.soundFonts[fontId].sampleBankId1; - relocInfo.sampleBankId2 = gAudioContext.soundFonts[fontId].sampleBankId2; + //fontId = gAudioContext.permanentCache[i].id; + + if (!gUseLegacySD) { + SoundFont* sf = ResourceMgr_LoadAudioSoundFont(fontId); + relocInfo.sampleBankId1 = sf->sampleBankId1; + relocInfo.sampleBankId2 = sf->sampleBankId2; + } else { + relocInfo.sampleBankId1 = gAudioContext.soundFonts[fontId].sampleBankId1; + relocInfo.sampleBankId2 = gAudioContext.soundFonts[fontId].sampleBankId2; + } if (relocInfo.sampleBankId1 != 0xFF) { relocInfo.sampleBankId1 = AudioLoad_GetRealTableIndex(SAMPLE_TABLE, relocInfo.sampleBankId1); diff --git a/soh/src/code/audio_playback.c b/soh/src/code/audio_playback.c index c116b4ddb..5d43118b8 100644 --- a/soh/src/code/audio_playback.c +++ b/soh/src/code/audio_playback.c @@ -1,6 +1,8 @@ #include "global.h" #include "Cvar.h" +extern bool gUseLegacySD; + void Audio_InitNoteSub(Note* note, NoteSubEu* sub, NoteSubAttributes* attrs) { f32 volRight, volLeft; s32 smallPanIndex; @@ -307,7 +309,7 @@ SoundFontSound* Audio_InstrumentGetSound(Instrument* instrument, s32 semitone) { Instrument* Audio_GetInstrumentInner(s32 fontId, s32 instId) { Instrument* inst; - + if (fontId == 0xFF) { return NULL; } @@ -317,16 +319,31 @@ Instrument* Audio_GetInstrumentInner(s32 fontId, s32 instId) { return NULL; } - if (instId >= gAudioContext.soundFonts[fontId].numInstruments) { - gAudioContext.audioErrorFlags = ((fontId << 8) + instId) + 0x3000000; - return NULL; + int instCnt = 0; + + if (gUseLegacySD) { + instCnt = gAudioContext.soundFonts[fontId].numInstruments; + inst = gAudioContext.soundFonts[fontId].instruments[instId]; + + if (instId >= gAudioContext.soundFonts[fontId].numInstruments) + if (instId >= instCnt) { + gAudioContext.audioErrorFlags = ((fontId << 8) + instId) + 0x3000000; + return NULL; + } + } else { + SoundFont* sf = ResourceMgr_LoadAudioSoundFont(fontId); + + if (instId >= sf->numInstruments) + return NULL; + + inst = sf->instruments[instId]; } - inst = gAudioContext.soundFonts[fontId].instruments[instId]; if (inst == NULL) { gAudioContext.audioErrorFlags = ((fontId << 8) + instId) + 0x1000000; return inst; } + return inst; } @@ -343,12 +360,17 @@ Drum* Audio_GetDrum(s32 fontId, s32 drumId) { return NULL; } - if (drumId >= gAudioContext.soundFonts[fontId].numDrums) { - gAudioContext.audioErrorFlags = ((fontId << 8) + drumId) + 0x4000000; - return NULL; - } + if (gUseLegacySD) { + if (drumId >= gAudioContext.soundFonts[fontId].numDrums) { + gAudioContext.audioErrorFlags = ((fontId << 8) + drumId) + 0x4000000; + return NULL; + } - drum = gAudioContext.soundFonts[fontId].drums[drumId]; + drum = gAudioContext.soundFonts[fontId].drums[drumId]; + } else { + SoundFont* sf = ResourceMgr_LoadAudioSoundFont(fontId); + drum = sf->drums[drumId]; + } if (drum == NULL) { gAudioContext.audioErrorFlags = ((fontId << 8) + drumId) + 0x5000000; @@ -369,12 +391,17 @@ SoundFontSound* Audio_GetSfx(s32 fontId, s32 sfxId) { return NULL; } - if (sfxId >= gAudioContext.soundFonts[fontId].numSfx) { - gAudioContext.audioErrorFlags = ((fontId << 8) + sfxId) + 0x4000000; - return NULL; - } + if (gUseLegacySD) { + if (sfxId >= gAudioContext.soundFonts[fontId].numSfx) { + gAudioContext.audioErrorFlags = ((fontId << 8) + sfxId) + 0x4000000; + return NULL; + } - sfx = &gAudioContext.soundFonts[fontId].soundEffects[sfxId]; + sfx = &gAudioContext.soundFonts[fontId].soundEffects[sfxId]; + } else { + SoundFont* sf = ResourceMgr_LoadAudioSoundFont(fontId); + sfx = &sf->soundEffects[sfxId]; + } if (sfx == NULL) { gAudioContext.audioErrorFlags = ((fontId << 8) + sfxId) + 0x5000000; diff --git a/soh/src/code/audio_seqplayer.c b/soh/src/code/audio_seqplayer.c index 3dd7b811e..52d14a9a1 100644 --- a/soh/src/code/audio_seqplayer.c +++ b/soh/src/code/audio_seqplayer.c @@ -938,8 +938,16 @@ u8 AudioSeq_GetInstrument(SequenceChannel* channel, u8 instId, Instrument** inst *instOut = NULL; return 0; } - adsr->envelope = inst->envelope; - adsr->releaseRate = inst->releaseRate; + + if (inst->envelope != NULL) + { + adsr->envelope = inst->envelope; + adsr->releaseRate = (inst->releaseRate); + } + else { + adsr->envelope = gDefaultEnvelope; + } + *instOut = inst; instId += 2; return instId; diff --git a/soh/src/code/audio_synthesis.c b/soh/src/code/audio_synthesis.c index 037bd2ec3..b567dd0ef 100644 --- a/soh/src/code/audio_synthesis.c +++ b/soh/src/code/audio_synthesis.c @@ -749,8 +749,10 @@ Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSubEu, NoteSynthesisS cmd = AudioSynth_LoadWaveSamples(cmd, noteSubEu, synthState, nSamplesToLoad); noteSamplesDmemAddrBeforeResampling = DMEM_UNCOMPRESSED_NOTE + (synthState->samplePosInt * 2); synthState->samplePosInt += nSamplesToLoad; - } else { + } else + { audioFontSample = noteSubEu->sound.soundFontSound->sample; + loopInfo = audioFontSample->loop; loopEndPos = loopInfo->end; sampleAddr = audioFontSample->sampleAddr; @@ -822,6 +824,7 @@ Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSubEu, NoteSynthesisS noteFinished = true; } } + switch (audioFontSample->codec) { case CODEC_ADPCM: @@ -848,6 +851,9 @@ Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSubEu, NoteSynthesisS goto skip; case CODEC_S16: AudioSynth_ClearBuffer(cmd++, DMEM_UNCOMPRESSED_NOTE, (samplesLenAdjusted * 2) + 0x20); + AudioSynth_LoadBuffer(cmd++, DMEM_UNCOMPRESSED_NOTE, ALIGN16(nSamplesToLoad * 2), + audioFontSample->sampleAddr + (synthState->samplePosInt * 2)); + flags = A_CONTINUE; skipBytes = 0; nSamplesProcessed = samplesLenAdjusted; @@ -860,7 +866,8 @@ Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSubEu, NoteSynthesisS if (nFramesToDecode != 0) { frameIndex = (synthState->samplePosInt + skipInitialSamples - nFirstFrameSamplesToIgnore) / 16; sampleDataOffset = frameIndex * frameSize; - if (audioFontSample->medium == MEDIUM_RAM) { + if (audioFontSample->medium == MEDIUM_RAM) + { sampleData = (u8*)(sampleDataStart + sampleDataOffset + sampleAddr); } else if (audioFontSample->medium == MEDIUM_UNK) { return cmd; @@ -1024,7 +1031,8 @@ Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSubEu, NoteSynthesisS unk7 = noteSubEu->unk_07; unkE = noteSubEu->unk_0E; buf = &synthState->synthesisBuffers->panSamplesBuffer[0x18]; - if (unk7 != 0 && noteSubEu->unk_0E != 0) { + if (unk7 != 0 && noteSubEu->unk_0E != 0) + { AudioSynth_DMemMove(cmd++, DMEM_TEMP, DMEM_SCRATCH2, aiBufLen * 2); thing = DMEM_SCRATCH2 - unk7; if (synthState->unk_1A != 0) { diff --git a/soh/src/code/code_800E4FE0.c b/soh/src/code/code_800E4FE0.c index c46ae7a07..d88e731f4 100644 --- a/soh/src/code/code_800E4FE0.c +++ b/soh/src/code/code_800E4FE0.c @@ -213,11 +213,7 @@ AudioTask* func_800E5000(void) { task = &gAudioContext.currTask->task.t; task->type = M_AUDTASK; task->flags = 0; - //task->ucode_boot = D_801120C0; task->ucode_boot_size = 0x1000; - //task->ucode_data_size = ((rspAspMainDataEnd - rspAspMainDataStart) * sizeof(u64)) - 1; - //task->ucode = D_801120C0; - //task->ucode_data = rspAspMainDataStart; task->ucode_size = 0x1000; task->dram_stack = NULL; task->dram_stack_size = 0;