From 8c8c761726a6c58cebbb5e7e2a1a5171f26c989b Mon Sep 17 00:00:00 2001 From: Christopher Leggett Date: Thu, 8 Dec 2022 23:07:45 -0500 Subject: [PATCH] Custom Sequences (#2066) * Allows OTRExporter to parse pairs of .seq and .meta files * Gets added sequences available to SfxEditor and playing in game. * Some cleanup of the names appearing in the SfxEditor. * Moves sequence swap lower in the audio command stack. * Increases temp cache memory available on title/file-select screen. Certain sequences wouldn't play on the file select and title screen because they were too large to be cached. * Introduces workaround for 255 sequence limit. * Bug fixes and cleanup. * Fixes bug where fanfares would sometimes disable the sequence player. * Fixes bug causing certain areas to discard caches when loading enemy music. * Fixes potential config-related crash by replacing invalid characters. * Allows custom bgm to play in all BGM categories. * Properly randomizes the custom tracks. * Moves custom sequences to a patch OTR. * If custom music was not loaded, fall back to default values. * Prevents OOB crash on Synthwave array and adds octave drop feature. Added octave drop to experimental features, which drops the octave of a note that is too high for the audio engine to actually play. Without this, some custom sequences have notes which cap at a specific value and sound terrible. At least with this they will still harmonize with the other notes. Experimental tab added to the SfxEditor to house the checkbox for the octave drop feature. * Adds more pool memory for a few tracks that couldn't fit. * Some cleanup on the generated music archive process. * Fixes missed memory boost from earlier. * Adds ability to remove enemy proximity music. * Applies correct cache policy to fanfares to prevent unloading sequences. * Removes case-sensitiveness of the sequence type. * Fixes not reverting to sequence after miniboss. * Fixes transition to/from miniboss (again) and ocarina bug. To be clear, fixes the more rampant portable ocarina bug present in my earlier builds, not the authentic one. * Finally properly fixes transitions between sequences For miniboss fights and SfxEditor previews. * Removes unneeded boolean expression. * Adds randomize button to individual SFX Editor entries. * Fixes lost woods music overwriting goron city music. * Plays swapped Hyrule Field music when transitioning to daytime. * Fixes swapping Gerudo Valley music when transitioning from daytime. * Updates custom sequence OTRPath to match SequenceOTRizer. * Reverts changes to OTRExporter in favor of external tool * Fixes formatting issues. * Attempts to fix formatting issue in git diff. * Should actually fix formatting issues. * Should fix mac/linux exclusive build error. * Fixes segfault on macos. * sort custom seqs * Fixes audioseq crash when under 255 seqs * Removes magic numbers. * Removes commented out code. * fixes formatting in SfxEditor.h Co-authored-by: briaguya <70942617+briaguya-ai@users.noreply.github.com> * Cleans up the one hardcoded QueueSeqCmd call. * Fixes unneeded erroneous memory boost applied earlier. * Applies additional formatting/cleanliness suggestions from review * Fixes small logic bug Co-authored-by: RaelCappra Co-authored-by: briaguya <70942617+briaguya-ai@users.noreply.github.com> --- soh/include/z64audio.h | 10 +- soh/soh/Enhancements/sfx-editor/SfxEditor.cpp | 127 +++++++++++++++--- soh/soh/Enhancements/sfx-editor/SfxEditor.h | 6 + soh/soh/OTRGlobals.cpp | 4 + soh/soh/OTRGlobals.h | 2 + soh/src/code/audio_heap.c | 2 +- soh/src/code/audio_init_params.c | 36 ++--- soh/src/code/audio_load.c | 62 ++++++++- soh/src/code/audio_playback.c | 15 ++- soh/src/code/audio_seqplayer.c | 16 ++- soh/src/code/code_800EC960.c | 7 +- soh/src/code/code_800F7260.c | 6 +- soh/src/code/code_800F9280.c | 16 +-- .../actors/ovl_player_actor/z_player.c | 2 +- 14 files changed, 242 insertions(+), 69 deletions(-) diff --git a/soh/include/z64audio.h b/soh/include/z64audio.h index f1e44a7e8..6b4613fff 100644 --- a/soh/include/z64audio.h +++ b/soh/include/z64audio.h @@ -22,6 +22,8 @@ #define CALC_RESAMPLE_FREQ(sampleRate) ((float)sampleRate / (s32)gAudioContext.audioBufferParameters.frequency) +#define MAX_SEQUENCES 0x400 + extern char* fontMap[256]; typedef enum { @@ -259,7 +261,7 @@ typedef struct { /* 0x001 */ u8 state; /* 0x002 */ u8 noteAllocPolicy; /* 0x003 */ u8 muteBehavior; - /* 0x004 */ u8 seqId; + /* 0x004 */ u16 seqId; /* 0x005 */ u8 defaultFont; /* 0x006 */ u8 unk_06[1]; /* 0x007 */ s8 playerIdx; @@ -913,7 +915,7 @@ typedef struct { /* 0x342C */ AudioPoolSplit3 temporaryCommonPoolSplit; /* 0x3438 */ u8 sampleFontLoadStatus[0x30]; /* 0x3468 */ u8 fontLoadStatus[0x30]; - /* 0x3498 */ u8 seqLoadStatus[0x80]; + /* 0x3498 */ u8 seqLoadStatus[MAX_SEQUENCES]; /* 0x3518 */ volatile u8 resetStatus; /* 0x3519 */ u8 audioResetSpecIdToLoad; /* 0x351C */ s32 audioResetFadeOutFramesLeft; @@ -941,6 +943,8 @@ typedef struct { /* 0x5C3C */ OSMesg audioResetMesgs[1]; /* 0x5C40 */ OSMesg cmdProcMsgs[4]; /* 0x5C50 */ AudioCmd cmdBuf[0x100]; + u16 seqToPlay[4]; + u8 seqReplaced[4]; } AudioContext; // size = 0x6450 typedef struct { @@ -1105,7 +1109,7 @@ typedef enum { typedef struct { char* seqData; int32_t seqDataSize; - uint8_t seqNumber; + uint16_t seqNumber; uint8_t medium; uint8_t cachePolicy; int32_t numFonts; diff --git a/soh/soh/Enhancements/sfx-editor/SfxEditor.cpp b/soh/soh/Enhancements/sfx-editor/SfxEditor.cpp index 71d6ee36b..3efce43e6 100644 --- a/soh/soh/Enhancements/sfx-editor/SfxEditor.cpp +++ b/soh/soh/Enhancements/sfx-editor/SfxEditor.cpp @@ -6,13 +6,16 @@ #include #include #include "../randomizer/3drando/random.hpp" +#include "../../OTRGlobals.h" +#include +#include "../../UIWidgets.hpp" Vec3f pos = { 0.0f, 0.0f, 0.0f }; f32 freqScale = 1.0f; s8 reverbAdd = 0; // {originalSequenceId, {label, sfxKey, category}, -const std::map> sequenceMap = { +std::map> sfxEditorSequenceMap = { {NA_BGM_FIELD_LOGIC, {"Hyrule Field", "NA_BGM_FIELD_LOGIC", SEQ_BGM_WORLD}}, {NA_BGM_DUNGEON, {"Dodongo's Cavern", "NA_BGM_DUNGEON", SEQ_BGM_WORLD}}, {NA_BGM_KAKARIKO_ADULT, {"Kakariko Village (Adult)", "NA_BGM_KAKARIKO_ADULT", SEQ_BGM_WORLD}}, @@ -39,7 +42,6 @@ const std::map> sequenceMap = {NA_BGM_GANON_TOWER, {"Ganondorf's Theme", "NA_BGM_GANON_TOWER", SEQ_BGM_WORLD}}, {NA_BGM_LONLON, {"Lon Lon Ranch", "NA_BGM_LONLON", SEQ_BGM_WORLD}}, {NA_BGM_GORON_CITY, {"Goron City", "NA_BGM_GORON_CITY", SEQ_BGM_WORLD}}, - {NA_BGM_FIELD_MORNING, {"Hyrule Field Morning Theme", "NA_BGM_FIELD_MORNING", SEQ_BGM_WORLD}}, {NA_BGM_SPIRITUAL_STONE, {"Spiritual Stone Get", "NA_BGM_SPIRITUAL_STONE", SEQ_FANFARE}}, {NA_BGM_OCA_BOLERO, {"Bolero of Fire", "NA_BGM_OCA_BOLERO", SEQ_OCARINA}}, {NA_BGM_OCA_MINUET, {"Minuet of Forest", "NA_BGM_OCA_MINUET", SEQ_OCARINA}}, @@ -196,7 +198,7 @@ void Draw_SfxTab(const std::string& tabId, const std::map values; for (const auto& [value, seqData] : map) { - if (std::get<2>(seqData) == type) { + if (std::get<2>(seqData) & type) { values.push_back(value); } } @@ -204,7 +206,10 @@ void Draw_SfxTab(const std::string& tabId, const std::map MAX_AUTHENTIC_SEQID) { + continue; + } const int randomValue = values.back(); CVar_SetS32(cvarKey.c_str(), randomValue); values.pop_back(); @@ -216,11 +221,14 @@ void Draw_SfxTab(const std::string& tabId, const std::map= MAX_AUTHENTIC_SEQID) { continue; } @@ -229,6 +237,7 @@ void Draw_SfxTab(const std::string& tabId, const std::map(map.at(currentValue)).c_str())) { + const int initialValue = map.contains(currentValue) ? currentValue : defaultValue; + if (ImGui::BeginCombo(hiddenKey.c_str(), std::get<0>(map.at(initialValue)).c_str())) { for (const auto& [value, seqData] : map) { const auto& [name, sfxKey, seqType] = seqData; - if (seqType != type) { + if (~(seqType) & type) { continue; } @@ -283,25 +293,53 @@ void Draw_SfxTab(const std::string& tabId, const std::map(replacementSeq); } extern "C" u16 SfxEditor_GetReverseReplacementSeq(u16 seqId) { - for (const auto& [id, nameAndsfxKey] : sequenceMap) { - const auto& [name, sfxKey, seqType] = sequenceMap.at(id); + for (const auto& [id, nameAndsfxKey] : sfxEditorSequenceMap) { + const auto& [name, sfxKey, seqType] = sfxEditorSequenceMap.at(id); const std::string cvarKey = "gSfxEditor_" + sfxKey; if (CVar_GetS32(cvarKey.c_str(), id) == seqId){ return static_cast(id); @@ -324,28 +362,56 @@ void DrawSfxEditor(bool& open) { if (ImGui::BeginTabBar("SfxContextTabBar", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) { if (ImGui::BeginTabItem("Background Music")) { - Draw_SfxTab("backgroundMusic", sequenceMap, SEQ_BGM_WORLD); + Draw_SfxTab("backgroundMusic", sfxEditorSequenceMap, SEQ_BGM_WORLD); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Fanfares")) { - Draw_SfxTab("fanfares", sequenceMap, SEQ_FANFARE); + Draw_SfxTab("fanfares", sfxEditorSequenceMap, SEQ_FANFARE); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Events")) { - Draw_SfxTab("event", sequenceMap, SEQ_BGM_EVENT); + Draw_SfxTab("event", sfxEditorSequenceMap, SEQ_BGM_EVENT); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Battle Music")) { - Draw_SfxTab("battleMusic", sequenceMap, SEQ_BGM_BATTLE); + Draw_SfxTab("battleMusic", sfxEditorSequenceMap, SEQ_BGM_BATTLE); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Ocarina")) { - Draw_SfxTab("instrument", sequenceMap, SEQ_INSTRUMENT); - Draw_SfxTab("ocarina", sequenceMap, SEQ_OCARINA); + Draw_SfxTab("instrument", sfxEditorSequenceMap, SEQ_INSTRUMENT); + Draw_SfxTab("ocarina", sfxEditorSequenceMap, SEQ_OCARINA); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Sound Effects")) { - Draw_SfxTab("sfx", sequenceMap, SEQ_SFX); + Draw_SfxTab("sfx", sfxEditorSequenceMap, SEQ_SFX); + ImGui::EndTabItem(); + } + + static ImVec2 cellPadding(8.0f, 8.0f); + if (ImGui::BeginTabItem("Options")) { + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding); + ImGui::BeginTable("Options", 1, ImGuiTableFlags_SizingStretchSame); + ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (ImGui::BeginChild("SfxOptions", ImVec2(0, -8))) { + ImGui::PushItemWidth(-FLT_MIN); + UIWidgets::EnhancementCheckbox("Disable Enemy Proximity Music", "gEnemyBGMDisable"); + UIWidgets::InsertHelpHoverText( + "Disables the music change when getting close to enemies. Useful for hearing " + "your custom music for each scene more often."); + UIWidgets::PaddedSeparator(); + UIWidgets::PaddedText("The following options are experimental and may cause music\nto sound odd or have other undesireable effects."); + UIWidgets::EnhancementCheckbox("Lower Octaves of Unplayable High Notes", "gExperimentalOctaveDrop"); + UIWidgets::InsertHelpHoverText("Some custom sequences may have notes that are too high for the game's audio " + "engine to play. Enabling this checkbox will cause these notes to drop a " + "couple of octaves so they can still harmonize with the other notes of the " + "sequence."); + ImGui::PopItemWidth(); + } + ImGui::EndChild(); + ImGui::EndTable(); + ImGui::PopStyleVar(1); ImGui::EndTabItem(); } ImGui::EndTabBar(); @@ -357,3 +423,22 @@ void InitSfxEditor() { //Draw the bar in the menu. SohImGui::AddWindow("Enhancements", "SFX Editor", DrawSfxEditor); } + +extern "C" void SfxEditor_AddSequence(char *otrPath, uint16_t seqNum) { + std::vector splitName = StringHelper::Split(otrPath, "/"); + std::string fileName = splitName[splitName.size() - 1]; + std::vector splitFileName = StringHelper::Split(fileName, "_"); + std::string sequenceName = splitFileName[0]; + SeqType type = SEQ_BGM_CUSTOM; + std::string typeString = splitFileName[splitFileName.size() - 1]; + std::locale loc; + for (int i = 0; i < typeString.length(); i++) { + typeString[i] = std::tolower(typeString[i], loc); + } + if (typeString == "fanfare") { + type = SEQ_FANFARE; + } + auto tuple = std::make_tuple( + sequenceName, StringHelper::Replace(StringHelper::Replace(sequenceName, " ", "_"), "~", "-"), type); + sfxEditorSequenceMap.emplace(seqNum, tuple); +} diff --git a/soh/soh/Enhancements/sfx-editor/SfxEditor.h b/soh/soh/Enhancements/sfx-editor/SfxEditor.h index a905b3e4e..a9a856515 100644 --- a/soh/soh/Enhancements/sfx-editor/SfxEditor.h +++ b/soh/soh/Enhancements/sfx-editor/SfxEditor.h @@ -1,8 +1,13 @@ #pragma once +#include "stdint.h" void InitSfxEditor(); +#ifndef __cplusplus +void SfxEditor_AddSequence(char *otrPath, uint16_t seqNum); +#endif #define INSTRUMENT_OFFSET 0x81 +#define MAX_AUTHENTIC_SEQID 110 enum SeqType { SEQ_NOSHUFFLE = 0, @@ -14,4 +19,5 @@ enum SeqType { SEQ_BGM_ERROR = 1 << 5, SEQ_SFX = 1 << 6, SEQ_INSTRUMENT = 1 << 7, + SEQ_BGM_CUSTOM = SEQ_BGM_WORLD | SEQ_BGM_EVENT | SEQ_BGM_BATTLE, }; \ No newline at end of file diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 5b407a9b7..9d7e8b690 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -177,6 +177,10 @@ bool OTRGlobals::HasOriginal() { return hasOriginal; } +std::shared_ptr> OTRGlobals::ListFiles(std::string path) { + return context->GetResourceManager()->ListFiles(path); +} + struct ExtensionEntry { std::string path; std::string ext; diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index 126f7c396..09435f5f6 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -10,6 +10,7 @@ #include #include "Enhancements/savestates.h" #include "Enhancements/randomizer/randomizer.h" +#include const std::string customMessageTableID = "BaseGameOverrides"; @@ -27,6 +28,7 @@ public: bool HasMasterQuest(); bool HasOriginal(); + std::shared_ptr> ListFiles(std::string path); private: void CheckSaveFile(size_t sramSize) const; diff --git a/soh/src/code/audio_heap.c b/soh/src/code/audio_heap.c index 189d73133..0e7027510 100644 --- a/soh/src/code/audio_heap.c +++ b/soh/src/code/audio_heap.c @@ -53,7 +53,7 @@ void AudioHeap_ResetLoadStatus(void) { } } - for (i = 0; i < 0x80; i++) { + for (i = 0; i < MAX_SEQUENCES; i++) { if (gAudioContext.seqLoadStatus[i] != 5) { gAudioContext.seqLoadStatus[i] = 0; } diff --git a/soh/src/code/audio_init_params.c b/soh/src/code/audio_init_params.c index f5bae8fd8..12653c6dc 100644 --- a/soh/src/code/audio_init_params.c +++ b/soh/src/code/audio_init_params.c @@ -68,22 +68,22 @@ ReverbSettings D_80133420[][3] = { }; AudioSpec gAudioSpecs[18] = { - { 44100, 1, 24, 4, 0, 0, 2, D_80133420[0], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x4000, 0x2880, 0, 0, 0 }, - { 44100, 1, 24, 4, 0, 0, 2, D_80133420[1], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, - { 44100, 1, 24, 4, 0, 0, 2, D_80133420[2], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, - { 44100, 1, 23, 4, 0, 0, 2, D_80133420[4], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, - { 44100, 1, 23, 4, 0, 0, 2, D_80133420[5], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, - { 44100, 1, 24, 4, 0, 0, 2, D_80133420[6], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, - { 44100, 1, 24, 4, 0, 0, 2, D_80133420[7], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, - { 44100, 1, 23, 4, 0, 0, 2, D_80133420[8], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, - { 44100, 1, 24, 4, 0, 0, 2, D_80133420[9], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, - { 44100, 1, 23, 4, 0, 0, 2, D_80133420[8], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, - { 44100, 1, 28, 3, 0, 0, 2, D_80133420[10], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x2800, 0x2880, 0, 0, 0 }, - { 44100, 1, 28, 3, 0, 0, 1, D_80133420[11], 0x300, 0x200, 0x7FFF, 0, 0x4800, 0, 0x4000, 0, 0, 0, 0 }, - { 44100, 1, 28, 3, 0, 0, 1, D_80133420[11], 0x300, 0x200, 0x7FFF, 0, 0, 0, 0x4000, 0x4800, 0, 0, 0 }, - { 44100, 1, 22, 4, 0, 0, 2, D_80133420[0], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, - { 44100, 1, 22, 4, 0, 0, 2, D_80133420[8], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, - { 44100, 1, 16, 4, 0, 0, 2, D_80133420[0], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, - { 22050, 1, 24, 4, 0, 0, 2, D_80133420[0], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3800, 0x2880, 0, 0, 0 }, - { 44100, 1, 24, 4, 0, 0, 2, D_80133420[2], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x3600, 0x2600, 0, 0, 0 }, + { 44100, 1, 24, 4, 0, 0, 2, D_80133420[0], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 }, + { 44100, 1, 24, 4, 0, 0, 2, D_80133420[1], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 }, + { 44100, 1, 24, 4, 0, 0, 2, D_80133420[2], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 }, + { 44100, 1, 23, 4, 0, 0, 2, D_80133420[4], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 }, + { 44100, 1, 23, 4, 0, 0, 2, D_80133420[5], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 }, + { 44100, 1, 24, 4, 0, 0, 2, D_80133420[6], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 }, + { 44100, 1, 24, 4, 0, 0, 2, D_80133420[7], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 }, + { 44100, 1, 23, 4, 0, 0, 2, D_80133420[8], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 }, + { 44100, 1, 24, 4, 0, 0, 2, D_80133420[9], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 }, + { 44100, 1, 23, 4, 0, 0, 2, D_80133420[8], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 }, + { 44100, 1, 28, 3, 0, 0, 2, D_80133420[10], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 }, + { 44100, 1, 28, 3, 0, 0, 1, D_80133420[11], 0x300, 0x200, 0x7FFF, 0, 0x4800, 0, 0x5000, 0, 0, 0, 0 }, + { 44100, 1, 28, 3, 0, 0, 1, D_80133420[11], 0x300, 0x200, 0x7FFF, 0, 0, 0, 0x5000, 0x4800, 0, 0, 0 }, + { 44100, 1, 22, 4, 0, 0, 2, D_80133420[0], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 }, + { 44100, 1, 22, 4, 0, 0, 2, D_80133420[8], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 }, + { 44100, 1, 16, 4, 0, 0, 2, D_80133420[0], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 }, + { 22050, 1, 24, 4, 0, 0, 2, D_80133420[0], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2880, 0, 0, 0 }, + { 44100, 1, 24, 4, 0, 0, 2, D_80133420[2], 0x300, 0x200, 0x7FFF, 0x7F0, 0xE00, 0, 0x5000, 0x2600, 0, 0, 0 }, }; diff --git a/soh/src/code/audio_load.c b/soh/src/code/audio_load.c index eff3e5f87..0dfb56691 100644 --- a/soh/src/code/audio_load.c +++ b/soh/src/code/audio_load.c @@ -5,6 +5,7 @@ #include "ultra64.h" #include "global.h" #include "soh/OTRGlobals.h" +#include "soh/Enhancements/sfx-editor/SfxEditor.h" #define MK_ASYNC_MSG(retData, tableType, id, status) (((retData) << 24) | ((tableType) << 16) | ((id) << 8) | (status)) #define ASYNC_TBLTYPE(v) ((u8)(v >> 16)) @@ -76,7 +77,7 @@ void* sUnusedHandler = NULL; s32 gAudioContextInitalized = false; -char* sequenceMap[256]; +char* sequenceMap[MAX_SEQUENCES]; char* fontMap[256]; uintptr_t fontStart; @@ -481,10 +482,14 @@ void AudioLoad_AsyncLoadFont(s32 fontId, s32 arg1, s32 retData, OSMesgQueue* ret u8* AudioLoad_GetFontsForSequence(s32 seqId, u32* outNumFonts) { s32 index; - if (seqId == 255) - return NULL; + if (seqId == NA_BGM_DISABLED) + return NULL; - SequenceData sDat = ResourceMgr_LoadSeqByName(sequenceMap[seqId]); + u16 newSeqId = SfxEditor_GetReplacementSeq(seqId); + if (!sequenceMap[newSeqId]){ + return NULL; + } + SequenceData sDat = ResourceMgr_LoadSeqByName(sequenceMap[newSeqId]); if (sDat.numFonts == 0) return NULL; @@ -568,9 +573,10 @@ s32 AudioLoad_SyncInitSeqPlayerInternal(s32 playerIdx, s32 seqId, s32 arg2) { AudioSeq_SequencePlayerDisable(seqPlayer); fontId = 0xFF; - //index = ((u16*)gAudioContext.sequenceFontTable)[seqId]; - //numFonts = gAudioContext.sequenceFontTable[index++]; + if (gAudioContext.seqReplaced[playerIdx]) { + seqId = gAudioContext.seqToPlay[playerIdx]; + } SequenceData seqData2 = ResourceMgr_LoadSeqByName(sequenceMap[seqId]); for (int i = 0; i < seqData2.numFonts; i++) @@ -984,7 +990,6 @@ void* AudioLoad_AsyncLoadInner(s32 tableType, s32 id, s32 nChunks, s32 retData, u32 temp_v0; u32 realId; - realId = AudioLoad_GetRealTableIndex(tableType, id); switch (tableType) { case SEQUENCE_TABLE: if (gAudioContext.seqLoadStatus[realId] == 1) { @@ -1211,6 +1216,12 @@ void AudioLoad_InitSwapFont(void) { #undef BASE_ROM_OFFSET +int strcmp_sort( const void *str1, const void *str2 ) { + char *const *pp1 = str1; + char *const *pp2 = str2; + return strcmp(*pp1, *pp2); +} + void AudioLoad_Init(void* heap, size_t heapSize) { char pad[0x48]; s32 numFonts; @@ -1223,6 +1234,8 @@ void AudioLoad_Init(void* heap, size_t heapSize) { gAudioContext.resetTimer = 0; memset(&gAudioContext, 0, sizeof(gAudioContext)); + memset(gAudioContext.seqToPlay, 0, 8); + memset(gAudioContext.seqReplaced, 0, 8); switch (osTvType) { case OS_TV_PAL: @@ -1307,6 +1320,25 @@ void AudioLoad_Init(void* heap, size_t heapSize) { free(seqList); + int customSeqListSize = 0; + int startingSeqNum = MAX_AUTHENTIC_SEQID; // 109 is the highest vanilla sequence + char** customSeqList = ResourceMgr_ListFiles("custom/music/*", &customSeqListSize); + qsort(customSeqList, customSeqListSize, sizeof(char*), strcmp_sort); + + for (size_t i = startingSeqNum; i < startingSeqNum + customSeqListSize; i++) { + int j = i - startingSeqNum; + SfxEditor_AddSequence(customSeqList[j], i); + SequenceData sDat = ResourceMgr_LoadSeqByName(customSeqList[j]); + sDat.seqNumber = i; + + char* str = malloc(strlen(customSeqList[j]) + 1); + strcpy(str, customSeqList[j]); + + sequenceMap[sDat.seqNumber] = str; + } + + free(customSeqList); + int fntListSize = 0; char** fntList = ResourceMgr_ListFiles("audio/fonts*", &fntListSize); @@ -1499,6 +1531,22 @@ s32 AudioLoad_SlowLoadSeq(s32 seqId, u8* ramAddr, s8* isDone) { size_t size; seqId = AudioLoad_GetRealTableIndex(SEQUENCE_TABLE, seqId); + u16 newSeqId = SfxEditor_GetReplacementSeq(seqId); + if (seqId != newSeqId) { + gAudioContext.seqToPlay[SEQ_PLAYER_BGM_MAIN] = newSeqId; + gAudioContext.seqReplaced[SEQ_PLAYER_BGM_MAIN] = 1; + // This sequence command starts playing a sequence specified by seqId on the main BGM seq player. + // The sequence command is a bitpacked u32 where different bits of the number indicated different parameters. + // What those parameters are is dependent on the first 8 bits which represent an operation. + // First two digits (bits 31-24) - Sequence Command Operation (0x0 = play sequence immediately) + // Next two digits (bits 23-16) - Index of the SeqPlayer to operate on. (0, which is the main BGM player.) + // Next two digits (bits 15-8) - Fade Timer (0 in this case, we don't want any fade-in or out here.) + // Last two digits (bits 7-0) - the sequence ID to play. Not actually sure why it is cast to u16 instead of u8, + // copied this from authentic game code and adapted it. I think it might be so that you can choose to encode the + // fade timer into the seqId if you want to for some reason. + Audio_QueueSeqCmd(0x00000000 | ((u8)SEQ_PLAYER_BGM_MAIN << 24) | ((u8)(0) << 16) | (u16)seqId); + return 0; + } seqTable = AudioLoad_GetLoadTable(SEQUENCE_TABLE); slowLoad = &gAudioContext.slowLoads[gAudioContext.slowLoadPos]; if (slowLoad->status == LOAD_STATUS_DONE) { diff --git a/soh/src/code/audio_playback.c b/soh/src/code/audio_playback.c index 5b19796c8..a10034a14 100644 --- a/soh/src/code/audio_playback.c +++ b/soh/src/code/audio_playback.c @@ -120,7 +120,11 @@ void Audio_NoteSetResamplingRate(NoteSubEu* noteSubEu, f32 resamplingRateInput) } else { noteSubEu->bitField1.hasTwoParts = true; if (3.99996f < resamplingRateInput) { - resamplingRate = 1.99998f; + if (CVar_GetS32("gExperimentalOctaveDrop", 0)) { + resamplingRate = resamplingRateInput * 0.25; + } else { + resamplingRate = 1.99998f; + } } else { resamplingRate = resamplingRateInput * 0.5f; } @@ -347,7 +351,7 @@ Instrument* Audio_GetInstrumentInner(s32 fontId, s32 instId) { } Drum* Audio_GetDrum(s32 fontId, s32 drumId) { - Drum* drum; + Drum* drum = NULL; if (fontId == 0xFF) { return NULL; @@ -360,7 +364,9 @@ Drum* Audio_GetDrum(s32 fontId, s32 drumId) { SoundFont* sf = ResourceMgr_LoadAudioSoundFont(fontMap[fontId]); - drum = sf->drums[drumId]; + if (drumId < sf->numDrums) { + drum = sf->drums[drumId]; + } if (drum == NULL) { gAudioContext.audioErrorFlags = ((fontId << 8) + drumId) + 0x5000000; @@ -538,6 +544,9 @@ s32 Audio_BuildSyntheticWave(Note* note, SequenceLayer* layer, s32 waveId) { if (waveId < 128) { waveId = 128; } + if (waveId > 136) { + waveId = 136; + } freqScale = layer->freqScale; if (layer->portamento.mode != 0 && 0.0f < layer->portamento.extent) { diff --git a/soh/src/code/audio_seqplayer.c b/soh/src/code/audio_seqplayer.c index dd9c6e1cc..80a97b97e 100644 --- a/soh/src/code/audio_seqplayer.c +++ b/soh/src/code/audio_seqplayer.c @@ -3,7 +3,7 @@ #include "ultra64.h" #include "global.h" -extern char* sequenceMap[256]; +extern char* sequenceMap[MAX_SEQUENCES]; #define PORTAMENTO_IS_SPECIAL(x) ((x).mode & 0x80) #define PORTAMENTO_MODE(x) ((x).mode & ~0x80) @@ -1063,7 +1063,12 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) { if (seqPlayer->defaultFont != 0xFF) { - SequenceData sDat = ResourceMgr_LoadSeqByName(sequenceMap[seqPlayer->seqId]); + if (gAudioContext.seqReplaced[seqPlayer->playerIdx]) { + seqPlayer->seqId = gAudioContext.seqToPlay[seqPlayer->playerIdx]; + gAudioContext.seqReplaced[seqPlayer->playerIdx] = 0; + } + u16 seqId = SfxEditor_GetReplacementSeq(seqPlayer->seqId); + SequenceData sDat = ResourceMgr_LoadSeqByName(sequenceMap[seqId]); command = sDat.fonts[sDat.numFonts - result - 1]; } @@ -1175,7 +1180,12 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) { if (seqPlayer->defaultFont != 0xFF) { - SequenceData sDat = ResourceMgr_LoadSeqByName(sequenceMap[seqPlayer->seqId]); + if (gAudioContext.seqReplaced[seqPlayer->playerIdx]) { + seqPlayer->seqId = gAudioContext.seqToPlay[seqPlayer->playerIdx]; + gAudioContext.seqReplaced[seqPlayer->playerIdx] = 0; + } + u16 seqId = SfxEditor_GetReplacementSeq(seqPlayer->seqId); + SequenceData sDat = ResourceMgr_LoadSeqByName(sequenceMap[seqId]); // The game apparantely would sometimes do negative array lookups, the result of which would get rejected by AudioHeap_SearchCaches, never // changing the actual fontid. diff --git a/soh/src/code/code_800EC960.c b/soh/src/code/code_800EC960.c index 46cc63683..b65fc6492 100644 --- a/soh/src/code/code_800EC960.c +++ b/soh/src/code/code_800EC960.c @@ -134,7 +134,7 @@ u8 sAudioExtraFilter2 = 0; Vec3f* sSariaBgmPtr = NULL; f32 D_80130650 = 2000.0f; u8 sSeqModeInput = 0; -u8 sSeqFlags[0x6E] = { +u8 sSeqFlags[0x6F] = { 0x2, // NA_BGM_GENERAL_SFX 0x1, // NA_BGM_NATURE_BACKGROUND 0, // NA_BGM_FIELD_LOGIC @@ -245,6 +245,7 @@ u8 sSeqFlags[0x6E] = { 0, // NA_BGM_FIRE_BOSS 0x8, // NA_BGM_TIMED_MINI_GAME 0, // NA_BGM_VARIOUS_SFX + 1, // NA_BGM_CUSTOM_SEQ }; s8 sSpecReverbs[20] = { 0, 0, 0, 0, 0, 0, 0, 40, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -1701,6 +1702,7 @@ void Audio_OcaSetInstrument(u8 arg0) { u16 sfxEditorId = arg0 + 0x81; u16 newArg0 = SfxEditor_GetReplacementSeq(sfxEditorId); if (newArg0 != sfxEditorId) { + gAudioContext.seqReplaced[SEQ_PLAYER_SFX] = 1; arg0 = newArg0 - 0x81; } @@ -4586,7 +4588,6 @@ s32 func_800F5A58(u8 arg0) { */ void func_800F5ACC(u16 seqId) { u16 curSeqId = func_800FA0B4(SEQ_PLAYER_BGM_MAIN); - curSeqId = SfxEditor_GetReverseReplacementSeq(curSeqId); if ((curSeqId & 0xFF) != NA_BGM_GANON_TOWER && (curSeqId & 0xFF) != NA_BGM_ESCAPE && curSeqId != seqId) { Audio_SetSequenceMode(SEQ_MODE_IGNORE); @@ -4648,7 +4649,7 @@ void Audio_PlayFanfare(u16 seqId) sp26 = func_800FA0B4(SEQ_PLAYER_FANFARE); sp1C = func_800E5E84(sp26 & 0xFF, &sp20); - sp18 = func_800E5E84(seqId & 0xFF, &sp20); + sp18 = func_800E5E84(seqId, &sp20); if (!sp1C || !sp18) { // disable BGM, we're about to null deref! D_8016B9F4 = 1; diff --git a/soh/src/code/code_800F7260.c b/soh/src/code/code_800F7260.c index 816550618..1aaa8f360 100644 --- a/soh/src/code/code_800F7260.c +++ b/soh/src/code/code_800F7260.c @@ -221,7 +221,11 @@ void Audio_ProcessSoundRequest(void) if (req->sfxId == 0) { return; } - req->sfxId = SfxEditor_GetReplacementSeq(req->sfxId); + u16 newSfxId = SfxEditor_GetReplacementSeq(req->sfxId); + if (req->sfxId != newSfxId) { + gAudioContext.seqReplaced[SEQ_PLAYER_SFX] = 1; + req->sfxId = newSfxId; + } bankId = SFX_BANK(req->sfxId); if ((1 << bankId) & D_801333F0) { AudioDebug_ScrPrt((const s8*)D_80133340, req->sfxId); diff --git a/soh/src/code/code_800F9280.c b/soh/src/code/code_800F9280.c index 086752a1c..49ca751f1 100644 --- a/soh/src/code/code_800F9280.c +++ b/soh/src/code/code_800F9280.c @@ -106,7 +106,7 @@ void Audio_ProcessSeqCmd(u32 cmd) { u8 op; u8 subOp; u8 playerIdx; - u8 seqId; + u16 seqId; u8 seqArgs; u8 found; u8 port; @@ -369,14 +369,14 @@ extern f32 D_80130F28; void Audio_QueueSeqCmd(u32 cmd) { u8 op = cmd >> 28; - if (op == 0 || op == 2 || op == 12){ - u16 oldSeqId = cmd & 0xFFFF; - u16 newSeqId = SfxEditor_GetReplacementSeq(oldSeqId); - if (newSeqId != oldSeqId) { - cmd &= ~0xFFFF; - cmd |= newSeqId; + if (op == 0 || op == 2 || op == 12) { + u8 seqId = cmd & 0xFF; + u8 playerIdx = (cmd >> 24) & 0xFF; + u16 newSeqId = SfxEditor_GetReplacementSeq(seqId); + gAudioContext.seqReplaced[playerIdx] = (seqId != newSeqId); + gAudioContext.seqToPlay[playerIdx] = newSeqId; + cmd |= (seqId & 0xFF); } - } sAudioSeqCmds[sSeqCmdWrPos++] = cmd; } diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 5bbb42c7d..d6dc9a2a8 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -10271,7 +10271,7 @@ void Player_UpdateCamAndSeqModes(PlayState* play, Player* this) { seqMode = SEQ_MODE_STILL; } - if (play->actorCtx.targetCtx.bgmEnemy != NULL) { + if (play->actorCtx.targetCtx.bgmEnemy != NULL && !CVar_GetS32("gEnemyBGMDisable", 0)) { seqMode = SEQ_MODE_ENEMY; Audio_SetBgmEnemyVolume(sqrtf(play->actorCtx.targetCtx.bgmEnemy->xyzDistToPlayerSq)); }