From dd2628f737d12f32accc030c44d1572ecd97d12c Mon Sep 17 00:00:00 2001 From: briaguya <70942617+briaguya0@users.noreply.github.com> Date: Sat, 4 Oct 2025 21:57:01 -0400 Subject: [PATCH] sequence notifs (updated #5265) (#5824) * Adds ability for Notifiations to not make a noise This is probably the only feature that will use it, the noise makes sense for most things we want to use notifications for, but it playing on every scene transition was a bit distracting. * Adds a hook for OnSeqPlayerInit * Uses new hook and displays notification instead of overlay text * Changes names to prevent collisions Will be registering other types of hooks that will need different ShipInitFuncs in this same file later. * Change Icon * Change CVarName and remove now-unused duration slider * Update ConfigMigrator for CVar changes. * clang-format * fix * bring back duration control * config v4 * fix v4 migration --------- Co-authored-by: Christopher Leggett Co-authored-by: briaguya <70942617+briaguya-ai@users.noreply.github.com> --- soh/soh/Enhancements/audio/AudioEditor.cpp | 15 +++---- soh/soh/Enhancements/audio/AudioHooks.cpp | 43 +++++++++++++++++++ .../GameInteractor_HookTable.h | 3 ++ .../game-interactor/GameInteractor_Hooks.cpp | 5 +++ .../game-interactor/GameInteractor_Hooks.h | 3 ++ soh/soh/Notification/Notification.cpp | 6 ++- soh/soh/Notification/Notification.h | 1 + soh/soh/OTRGlobals.cpp | 1 + soh/soh/config/ConfigMigrators.h | 5 +++ soh/soh/config/ConfigUpdaters.cpp | 11 +++++ soh/soh/config/ConfigUpdaters.h | 6 +++ soh/src/code/audio_load.c | 15 +------ 12 files changed, 91 insertions(+), 23 deletions(-) create mode 100644 soh/soh/Enhancements/audio/AudioHooks.cpp diff --git a/soh/soh/Enhancements/audio/AudioEditor.cpp b/soh/soh/Enhancements/audio/AudioEditor.cpp index 18b5cec1a..c64ed7b42 100644 --- a/soh/soh/Enhancements/audio/AudioEditor.cpp +++ b/soh/soh/Enhancements/audio/AudioEditor.cpp @@ -842,18 +842,17 @@ void RegisterAudioWidgets() { "the area for the effect to kick in.")); SohGui::mSohMenu->AddSearchWidget({ leadingMusic, "Enhancements", "Audio Editor", "Audio Options" }); - displaySeqName = { .name = "Display Sequence Name on Overlay", .type = WidgetType::WIDGET_CVAR_CHECKBOX }; - displaySeqName.CVar(CVAR_AUDIO("SeqNameOverlay")) + displaySeqName = { .name = "Display Sequence Name in Notifications", .type = WidgetType::WIDGET_CVAR_CHECKBOX }; + displaySeqName.CVar(CVAR_AUDIO("SeqNameNotification")) .Options(CheckboxOptions() .Color(THEME_COLOR) - .Tooltip("Displays the name of the current sequence in the corner of the screen whenever a new " - "sequence " - "is loaded to the main sequence player (does not apply to fanfares or enemy BGM).")); + .Tooltip("Emits a notification with the current song name whenever it changes. " + "(does not apply to fanfares or enemy BGM).")); SohGui::mSohMenu->AddSearchWidget({ displaySeqName, "Enhancements", "Audio Editor", "Audio Options" }); - ovlDuration = { .name = "Overlay Duration: %d seconds", .type = WidgetType::WIDGET_CVAR_SLIDER_INT }; - ovlDuration.CVar(CVAR_AUDIO("SeqNameOverlayDuration")) - .Options(IntSliderOptions().Color(THEME_COLOR).Min(1).Max(10).DefaultValue(5).Size(ImVec2(300.0f, 0.0f))); + ovlDuration = { .name = "Sequence Notification Duration: %d seconds", .type = WidgetType::WIDGET_CVAR_SLIDER_INT }; + ovlDuration.CVar(CVAR_AUDIO("SeqNameNotificationDuration")) + .Options(IntSliderOptions().Color(THEME_COLOR).Min(1).Max(20).DefaultValue(10).Size(ImVec2(300.0f, 0.0f))); SohGui::mSohMenu->AddSearchWidget({ ovlDuration, "Enhancements", "Audio Editor", "Audio Options" }); voicePitch = { .name = "Link's Voice Pitch Multiplier", .type = WidgetType::WIDGET_CVAR_SLIDER_FLOAT }; diff --git a/soh/soh/Enhancements/audio/AudioHooks.cpp b/soh/soh/Enhancements/audio/AudioHooks.cpp new file mode 100644 index 000000000..d2d6aa279 --- /dev/null +++ b/soh/soh/Enhancements/audio/AudioHooks.cpp @@ -0,0 +1,43 @@ +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" +#include "AudioCollection.h" +#include +#include + +extern "C" { +#include "variables.h" + +extern PlayState* gPlayState; +} + +#define CVAR_SEQOVERLAY_NAME CVAR_AUDIO("SeqNameNotification") +#define CVAR_SEQOVERLAY_DEFAULT 0 +#define CVAR_SEQOVERLAY_VALUE CVarGetInteger(CVAR_SEQOVERLAY_NAME, CVAR_SEQOVERLAY_DEFAULT) + +void NotifySequenceName(int32_t playerIdx, int32_t seqId) { + // Keep track of the previous sequence/scene so we don't repeat notifications + static uint16_t previousSeqId = UINT16_MAX; + static int16_t previousSceneNum = INT16_MAX; + if (playerIdx == SEQ_PLAYER_BGM_MAIN && + (seqId != previousSeqId || (gPlayState != NULL && gPlayState->sceneNum != previousSceneNum))) { + + previousSeqId = seqId; + if (gPlayState != NULL) { + previousSceneNum = gPlayState->sceneNum; + } + const char* sequenceName = AudioCollection::Instance->GetSequenceName(seqId); + if (sequenceName != NULL) { + Notification::Emit({ + .message = ICON_FA_MUSIC " " + std::string(sequenceName), + .remainingTime = static_cast(CVarGetInteger(CVAR_AUDIO("SeqNameNotificationDuration"), 10)), + .mute = true, + }); + } + } +} + +void RegisterAudioNotificationHooks() { + COND_HOOK(OnSeqPlayerInit, CVAR_SEQOVERLAY_VALUE, NotifySequenceName); +} + +static RegisterShipInitFunc notifInitFunc(RegisterAudioNotificationHooks, { CVAR_SEQOVERLAY_NAME }); \ No newline at end of file diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h index b5b1cfbfe..0f694660c 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h @@ -74,3 +74,6 @@ DEFINE_HOOK(OnGenerationCompletion, ()); DEFINE_HOOK(OnSetGameLanguage, ()); DEFINE_HOOK(OnAssetAltChange, ()); DEFINE_HOOK(OnKaleidoUpdate, ()); + +// Audio +DEFINE_HOOK(OnSeqPlayerInit, (int32_t playerIdx, int32_t seqId)); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index f374aa2d7..7d5a243d2 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -327,3 +327,8 @@ void GameInteractor_RegisterOnAssetAltChange(void (*fn)(void)) { void GameInteractor_ExecuteOnKaleidoUpdate() { GameInteractor::Instance->ExecuteHooks(); } + +// Mark: Audio +void GameInteractor_ExecuteOnSeqPlayerInit(int32_t playerIdx, int32_t seqId) { + GameInteractor::Instance->ExecuteHooks(playerIdx, seqId); +} diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index e22d12dbd..4c3a600e7 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -86,6 +86,9 @@ void GameInteractor_RegisterOnAssetAltChange(void (*fn)(void)); // Mark: - Pause Menu void GameInteractor_ExecuteOnKaleidoUpdate(); +// Mark: - Audio +void GameInteractor_ExecuteOnSeqPlayerInit(int32_t playerIdx, int32_t seqId); + #ifdef __cplusplus } #endif diff --git a/soh/soh/Notification/Notification.cpp b/soh/soh/Notification/Notification.cpp index 14873b068..c70841960 100644 --- a/soh/soh/Notification/Notification.cpp +++ b/soh/soh/Notification/Notification.cpp @@ -131,8 +131,10 @@ void Emit(Options notification) { notification.remainingTime = CVarGetFloat(CVAR_SETTING("Notifications.Duration"), 10.0f); } notifications.push_back(notification); - Audio_PlaySoundGeneral(NA_SE_SY_METRONOME, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, - &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + if (!notification.mute) { + Audio_PlaySoundGeneral(NA_SE_SY_METRONOME, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, + &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + } } } // namespace Notification diff --git a/soh/soh/Notification/Notification.h b/soh/soh/Notification/Notification.h index 7eb4bb2d7..bf1d4ec04 100644 --- a/soh/soh/Notification/Notification.h +++ b/soh/soh/Notification/Notification.h @@ -17,6 +17,7 @@ struct Options { std::string suffix = ""; ImVec4 suffixColor = ImVec4(1.0f, 0.5f, 0.5f, 1.0f); float remainingTime = 0.0f; // Seconds + bool mute = false; // whether notification should make a noise }; class Window final : public Ship::GuiWindow { diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 963a5b00d..a0ffdc1d1 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -1237,6 +1237,7 @@ extern "C" void InitOTR() { conf->RegisterVersionUpdater(std::make_shared()); conf->RegisterVersionUpdater(std::make_shared()); conf->RegisterVersionUpdater(std::make_shared()); + conf->RegisterVersionUpdater(std::make_shared()); conf->RunVersionUpdates(); SohGui::SetupGuiElements(); diff --git a/soh/soh/config/ConfigMigrators.h b/soh/soh/config/ConfigMigrators.h index ee7e91792..54a0dc236 100644 --- a/soh/soh/config/ConfigMigrators.h +++ b/soh/soh/config/ConfigMigrators.h @@ -1520,4 +1520,9 @@ std::vector version3Migrations = { { MigrationAction::Remove, "gPreset0" }, { MigrationAction::Remove, "gPreset1" }, }; + +std::vector version4Migrations = { + { MigrationAction::Rename, "gAudioEditor.SeqNameOverlay", "gAudioEditor.SeqNameNotification" }, + { MigrationAction::Rename, "gAudioEditor.SeqNameOverlayDuration", "gAudioEditor.SeqNameNotificationDuration" }, +}; } // namespace SOH diff --git a/soh/soh/config/ConfigUpdaters.cpp b/soh/soh/config/ConfigUpdaters.cpp index ff451d98c..a7c50a845 100644 --- a/soh/soh/config/ConfigUpdaters.cpp +++ b/soh/soh/config/ConfigUpdaters.cpp @@ -9,6 +9,8 @@ ConfigVersion2Updater::ConfigVersion2Updater() : ConfigVersionUpdater(2) { } ConfigVersion3Updater::ConfigVersion3Updater() : ConfigVersionUpdater(3) { } +ConfigVersion4Updater::ConfigVersion4Updater() : ConfigVersionUpdater(4) { +} void ConfigVersion1Updater::Update(Ship::Config* conf) { if (conf->GetInt("Window.Width", 640) == 640) { @@ -111,4 +113,13 @@ void ConfigVersion3Updater::Update(Ship::Config* conf) { CVarClear(migration.from.c_str()); } } + +void ConfigVersion4Updater::Update(Ship::Config* conf) { + for (Migration migration : version4Migrations) { + if (migration.action == MigrationAction::Rename) { + CVarCopy(migration.from.c_str(), migration.to.value().c_str()); + } + CVarClear(migration.from.c_str()); + } +} } // namespace SOH diff --git a/soh/soh/config/ConfigUpdaters.h b/soh/soh/config/ConfigUpdaters.h index 7912be7de..948188c19 100644 --- a/soh/soh/config/ConfigUpdaters.h +++ b/soh/soh/config/ConfigUpdaters.h @@ -18,4 +18,10 @@ class ConfigVersion3Updater final : public Ship::ConfigVersionUpdater { ConfigVersion3Updater(); void Update(Ship::Config* conf); }; + +class ConfigVersion4Updater final : public Ship::ConfigVersionUpdater { + public: + ConfigVersion4Updater(); + void Update(Ship::Config* conf); +}; } // namespace SOH diff --git a/soh/src/code/audio_load.c b/soh/src/code/audio_load.c index 52efa4016..f5d24756d 100644 --- a/soh/src/code/audio_load.c +++ b/soh/src/code/audio_load.c @@ -8,6 +8,7 @@ #include "soh/Enhancements/audio/AudioCollection.h" #include "soh/Enhancements/audio/AudioEditor.h" #include "soh/ResourceManagerHelpers.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include #ifdef _MSC_VER #define strdup _strdup @@ -630,19 +631,7 @@ s32 AudioLoad_SyncInitSeqPlayerInternal(s32 playerIdx, s32 seqId, s32 arg2) { AudioSeq_SkipForwardSequence(seqPlayer); //! @bug missing return (but the return value is not used so it's not UB) - // Keep track of the previous sequence/scene so we don't repeat notifications - static uint16_t previousSeqId = UINT16_MAX; - static int16_t previousSceneNum = INT16_MAX; - if (CVarGetInteger(CVAR_AUDIO("SeqNameOverlay"), 0) && playerIdx == SEQ_PLAYER_BGM_MAIN && - (seqId != previousSeqId || (gPlayState != NULL && gPlayState->sceneNum != previousSceneNum))) { - - previousSeqId = seqId; - if (gPlayState != NULL) { - previousSceneNum = gPlayState->sceneNum; - } - - AudioCollection_EmitSongNameNotification(seqId); - } + GameInteractor_ExecuteOnSeqPlayerInit(playerIdx, seqId); } u8* AudioLoad_SyncLoadSeq(s32 seqId) {