Files
Shiip-of-Hakinian-Espanol/soh/soh/Enhancements/audio/AudioEditor.cpp
Malkierian ba693ecac4 Search Exclusion and Search Widgets (#5656)
* Implement widget search exclusion.
Exclude all external windows from the search.

* Add second column to search results for non-widget results.

* Implement extra search index.
Add various controls, audio editor, and tracker options to the extra search index.
Fix capitalization on a few options.
Add click-to-view functionality to extra search column.

* Add extraSearchWidgets for searching through non-menu widgets.
Convert Controls extraSearches to widgets. Used to display in-window, as well as provide reference to extraSearchWidgets.

* Swap Audio search to menu widget search system.

* Fix missing static on a ShipInit instance.

* Implement SearchWidgets in popout windows.
Remove remaining vestiges of custom search.

* Add SearchWidget for randomizing audio on seed gen.

* Remove comments on color picker entries in WidgetType.

* Put all search results in middle column for width management.
Change combobox alignment and label position in search results to unify them all to left and above for better organization.
Add "Search Results" text to header of results page for clarification.

* clang
2025-09-16 18:57:33 -07:00

899 lines
41 KiB
C++

#include "AudioEditor.h"
#include "sequence.h"
#include <map>
#include <set>
#include <string>
#include <sstream>
#include <libultraship/libultraship.h>
#include <functions.h>
#include "../randomizer/3drando/random.hpp"
#include "soh/OTRGlobals.h"
#include "soh/cvar_prefixes.h"
#include <utils/StringHelper.h>
#include "soh/SohGui/SohMenu.h"
#include "soh/SohGui/SohGui.hpp"
#include "AudioCollection.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h"
extern "C" {
#include "z64save.h"
extern SaveContext gSaveContext;
}
Vec3f pos = { 0.0f, 0.0f, 0.0f };
f32 freqScale = 1.0f;
s8 reverbAdd = 0;
using namespace UIWidgets;
static WidgetInfo lowHpAlarm;
static WidgetInfo naviCall;
static WidgetInfo enemyProx;
static WidgetInfo leadingMusic;
static WidgetInfo displaySeqName;
static WidgetInfo ovlDuration;
static WidgetInfo voicePitch;
static WidgetInfo randoMusicOnSceneChange;
static WidgetInfo randomAudioOnSeedGen;
static WidgetInfo lowerOctaves;
namespace SohGui {
extern std::shared_ptr<SohMenu> mSohMenu;
}
// Authentic sequence counts
// used to ensure we have enough to shuffle
#define SEQ_COUNT_BGM_WORLD 30
#define SEQ_COUNT_BGM_BATTLE 6
#define SEQ_COUNT_FANFARE 15
#define SEQ_COUNT_OCARINA 12
#define SEQ_COUNT_NOSHUFFLE 6
#define SEQ_COUNT_BGM_EVENT 17
#define SEQ_COUNT_INSTRUMENT 6
#define SEQ_COUNT_SFX 57
#define SEQ_COUNT_VOICE 108
size_t AuthenticCountBySequenceType(SeqType type) {
switch (type) {
case SEQ_NOSHUFFLE:
return SEQ_COUNT_NOSHUFFLE;
case SEQ_BGM_WORLD:
return SEQ_COUNT_BGM_WORLD;
case SEQ_BGM_EVENT:
return SEQ_COUNT_BGM_EVENT;
case SEQ_BGM_BATTLE:
return SEQ_COUNT_BGM_BATTLE;
case SEQ_OCARINA:
return SEQ_COUNT_OCARINA;
case SEQ_FANFARE:
return SEQ_COUNT_FANFARE;
case SEQ_SFX:
return SEQ_COUNT_SFX;
case SEQ_INSTRUMENT:
return SEQ_COUNT_INSTRUMENT;
case SEQ_VOICE:
return SEQ_COUNT_VOICE;
default:
return 0;
}
}
// Grabs the current BGM sequence ID and replays it
// which will lookup the proper override, or reset back to vanilla
void ReplayCurrentBGM() {
u16 curSeqId = func_800FA0B4(SEQ_PLAYER_BGM_MAIN);
// TODO: replace with Audio_StartSeq when the macro is shared
// The fade time and audio player flags will always be 0 in the case of replaying the BGM, so they are not set here
Audio_QueueSeqCmd(0x00000000 | curSeqId);
}
// Attempt to update the BGM if it matches the current sequence that is being played
// The seqKey that is passed in should be the vanilla ID, not the override ID
void UpdateCurrentBGM(u16 seqKey, SeqType seqType) {
if (seqType != SEQ_BGM_WORLD) {
return;
}
u16 curSeqId = func_800FA0B4(SEQ_PLAYER_BGM_MAIN);
if (curSeqId == seqKey) {
ReplayCurrentBGM();
}
}
void RandomizeGroup(SeqType type) {
std::vector<u16> values;
// An empty IncludedSequences set means that the AudioEditor window has never been drawn
if (AudioCollection::Instance->GetIncludedSequences().empty()) {
AudioCollection::Instance->InitializeShufflePool();
}
// use a while loop to add duplicates if we don't have enough included sequences
while (values.size() < AuthenticCountBySequenceType(type)) {
for (const auto& seqData : AudioCollection::Instance->GetIncludedSequences()) {
if (seqData->category & type && seqData->canBeUsedAsReplacement) {
values.push_back(seqData->sequenceId);
}
}
// if we didn't find any, return early without shuffling to prevent an infinite loop
if (!values.size())
return;
}
Shuffle(values);
for (const auto& [seqId, seqData] : AudioCollection::Instance->GetAllSequences()) {
const std::string cvarKey = AudioCollection::Instance->GetCvarKey(seqData.sfxKey);
const std::string cvarLockKey = AudioCollection::Instance->GetCvarLockKey(seqData.sfxKey);
// don't randomize locked entries
if ((seqData.category & type) && CVarGetInteger(cvarLockKey.c_str(), 0) == 0) {
// Only save authentic sequence CVars
if ((((seqData.category & SEQ_BGM_CUSTOM) || seqData.category == SEQ_FANFARE) &&
seqData.sequenceId >= MAX_AUTHENTIC_SEQID) ||
seqData.canBeReplaced == false) {
continue;
}
const int randomValue = values.back();
CVarSetInteger(cvarKey.c_str(), randomValue);
values.pop_back();
}
}
}
void ResetGroup(const std::map<u16, SequenceInfo>& map, SeqType type) {
for (const auto& [defaultValue, seqData] : map) {
if (seqData.category == type) {
// Only save authentic sequence CVars
if (seqData.category == SEQ_FANFARE && defaultValue >= MAX_AUTHENTIC_SEQID) {
continue;
}
const std::string cvarKey = AudioCollection::Instance->GetCvarKey(seqData.sfxKey);
const std::string cvarLockKey = AudioCollection::Instance->GetCvarLockKey(seqData.sfxKey);
if (CVarGetInteger(cvarLockKey.c_str(), 0) == 0) {
CVarClear(cvarKey.c_str());
}
}
}
}
void LockGroup(const std::map<u16, SequenceInfo>& map, SeqType type) {
for (const auto& [defaultValue, seqData] : map) {
if (seqData.category == type) {
// Only save authentic sequence CVars
if (seqData.category == SEQ_FANFARE && defaultValue >= MAX_AUTHENTIC_SEQID) {
continue;
}
const std::string cvarKey = AudioCollection::Instance->GetCvarKey(seqData.sfxKey);
const std::string cvarLockKey = AudioCollection::Instance->GetCvarLockKey(seqData.sfxKey);
CVarSetInteger(cvarLockKey.c_str(), 1);
}
}
}
void UnlockGroup(const std::map<u16, SequenceInfo>& map, SeqType type) {
for (const auto& [defaultValue, seqData] : map) {
if (seqData.category == type) {
// Only save authentic sequence CVars
if (seqData.category == SEQ_FANFARE && defaultValue >= MAX_AUTHENTIC_SEQID) {
continue;
}
const std::string cvarKey = AudioCollection::Instance->GetCvarKey(seqData.sfxKey);
const std::string cvarLockKey = AudioCollection::Instance->GetCvarLockKey(seqData.sfxKey);
CVarSetInteger(cvarLockKey.c_str(), 0);
}
}
}
void DrawPreviewButton(uint16_t sequenceId, std::string sfxKey, SeqType sequenceType) {
const std::string cvarKey = AudioCollection::Instance->GetCvarKey(sfxKey);
const std::string hiddenKey = "##" + cvarKey;
const std::string stopButton = ICON_FA_STOP + hiddenKey;
const std::string previewButton = ICON_FA_PLAY + hiddenKey;
if (CVarGetInteger(CVAR_AUDIO("Playing"), 0) == sequenceId) {
if (UIWidgets::Button(stopButton.c_str(), UIWidgets::ButtonOptions()
.Size(UIWidgets::Sizes::Inline)
.Padding(ImVec2(10.0f, 6.0f))
.Tooltip("Stop Preview")
.Color(THEME_COLOR))) {
func_800F5C2C();
CVarSetInteger(CVAR_AUDIO("Playing"), 0);
}
} else {
if (UIWidgets::Button(previewButton.c_str(), UIWidgets::ButtonOptions()
.Size(UIWidgets::Sizes::Inline)
.Padding(ImVec2(10.0f, 6.0f))
.Tooltip("Play Preview")
.Color(THEME_COLOR))) {
if (CVarGetInteger(CVAR_AUDIO("Playing"), 0) != 0) {
func_800F5C2C();
CVarSetInteger(CVAR_AUDIO("Playing"), 0);
} else {
if (sequenceType == SEQ_SFX || sequenceType == SEQ_VOICE) {
Audio_PlaySoundGeneral(sequenceId, &pos, 4, &freqScale, &freqScale, &reverbAdd);
} else if (sequenceType == SEQ_INSTRUMENT) {
Audio_OcaSetInstrument(sequenceId - INSTRUMENT_OFFSET);
Audio_OcaSetSongPlayback(9, 1);
} else {
// TODO: Cant do both here, so have to click preview button twice
PreviewSequence(sequenceId);
CVarSetInteger(CVAR_AUDIO("Playing"), sequenceId);
}
}
}
}
}
void Draw_SfxTab(const std::string& tabId, SeqType type, const std::string& tabName) {
const std::map<u16, SequenceInfo>& map = AudioCollection::Instance->GetAllSequences();
const std::string hiddenTabId = "##" + tabId;
const std::string resetAllButton = "Reset All" + hiddenTabId;
const std::string randomizeAllButton = "Randomize All" + hiddenTabId;
const std::string lockAllButton = "Lock All" + hiddenTabId;
const std::string unlockAllButton = "Unlock All" + hiddenTabId;
ImGui::SeparatorText(tabName.c_str());
if (UIWidgets::Button(resetAllButton.c_str(),
UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline).Color(THEME_COLOR))) {
auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN);
auto prevReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM);
ResetGroup(map, type);
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
auto curReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM);
if (type == SEQ_BGM_WORLD && prevReplacement != curReplacement) {
ReplayCurrentBGM();
}
}
ImGui::SameLine();
if (UIWidgets::Button(randomizeAllButton.c_str(),
UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline).Color(THEME_COLOR))) {
auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN);
auto prevReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM);
RandomizeGroup(type);
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
auto curReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM);
if (type == SEQ_BGM_WORLD && prevReplacement != curReplacement) {
ReplayCurrentBGM();
}
}
ImGui::SameLine();
if (UIWidgets::Button(lockAllButton.c_str(),
UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline).Color(THEME_COLOR))) {
auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN);
auto prevReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM);
LockGroup(map, type);
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
auto curReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM);
if (type == SEQ_BGM_WORLD && prevReplacement != curReplacement) {
ReplayCurrentBGM();
}
}
ImGui::SameLine();
if (UIWidgets::Button(unlockAllButton.c_str(),
UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline).Color(THEME_COLOR))) {
auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN);
auto prevReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM);
UnlockGroup(map, type);
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
auto curReplacement = AudioCollection::Instance->GetReplacementSequence(currentBGM);
if (type == SEQ_BGM_WORLD && prevReplacement != curReplacement) {
ReplayCurrentBGM();
}
}
auto playingFromMenu = CVarGetInteger(CVAR_AUDIO("Playing"), 0);
auto currentBGM = func_800FA0B4(SEQ_PLAYER_BGM_MAIN);
// Longest text in Audio Editor
ImVec2 columnSize = ImGui::CalcTextSize("Navi - Look/Hey/Watchout (Target Enemy)");
ImGui::BeginTable(tabId.c_str(), 3, ImGuiTableFlags_SizingFixedFit);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, columnSize.x + 30);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, columnSize.x + 30);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 160.0f);
for (const auto& [defaultValue, seqData] : map) {
if (~(seqData.category) & type) {
continue;
}
// Do not display custom sequences in the list
if ((((seqData.category & SEQ_BGM_CUSTOM) || seqData.category == SEQ_FANFARE) &&
defaultValue >= MAX_AUTHENTIC_SEQID) ||
seqData.canBeReplaced == false) {
continue;
}
const std::string initialSfxKey = seqData.sfxKey;
const std::string cvarKey = AudioCollection::Instance->GetCvarKey(seqData.sfxKey);
const std::string cvarLockKey = AudioCollection::Instance->GetCvarLockKey(seqData.sfxKey);
const std::string hiddenKey = "##" + cvarKey;
const std::string resetButton = ICON_FA_UNDO + hiddenKey;
const std::string randomizeButton = ICON_FA_RANDOM + hiddenKey;
const std::string lockedButton = ICON_FA_LOCK + hiddenKey;
const std::string unlockedButton = ICON_FA_UNLOCK + hiddenKey;
const int currentValue = CVarGetInteger(cvarKey.c_str(), defaultValue);
const bool isCurrentlyPlaying = currentValue == playingFromMenu || seqData.sequenceId == currentBGM;
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (isCurrentlyPlaying) {
ImGui::TextColored(UIWidgets::ColorValues.at(UIWidgets::Colors::Yellow), "%s %s", ICON_FA_PLAY,
seqData.label.c_str());
} else {
ImGui::Text("%s", seqData.label.c_str());
}
ImGui::TableNextColumn();
ImGui::PushItemWidth(-FLT_MIN);
const int initialValue = map.contains(currentValue) ? currentValue : defaultValue;
UIWidgets::PushStyleCombobox(THEME_COLOR);
if (ImGui::BeginCombo(hiddenKey.c_str(), map.at(initialValue).label.c_str())) {
for (const auto& [value, seqData] : map) {
// If excluded as a replacement sequence, don't show in other dropdowns except the effect's own
// dropdown.
if (~(seqData.category) & type ||
(!seqData.canBeUsedAsReplacement && initialSfxKey != seqData.sfxKey)) {
continue;
}
if (ImGui::Selectable(seqData.label.c_str())) {
CVarSetInteger(cvarKey.c_str(), value);
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
UpdateCurrentBGM(defaultValue, type);
}
if (currentValue == value) {
ImGui::SetItemDefaultFocus();
}
}
ImGui::EndCombo();
}
UIWidgets::PopStyleCombobox();
ImGui::TableNextColumn();
ImGui::PushItemWidth(-FLT_MIN);
DrawPreviewButton((type == SEQ_SFX || type == SEQ_VOICE || type == SEQ_INSTRUMENT) ? defaultValue
: currentValue,
seqData.sfxKey, type);
auto locked = CVarGetInteger(cvarLockKey.c_str(), 0) == 1;
ImGui::SameLine();
ImGui::PushItemWidth(-FLT_MIN);
if (UIWidgets::Button(resetButton.c_str(), UIWidgets::ButtonOptions()
.Size(UIWidgets::Sizes::Inline)
.Padding(ImVec2(10.0f, 6.0f))
.Tooltip("Reset to default")
.Color(THEME_COLOR))) {
CVarClear(cvarKey.c_str());
CVarClear(cvarLockKey.c_str());
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
UpdateCurrentBGM(defaultValue, seqData.category);
}
ImGui::SameLine();
ImGui::PushItemWidth(-FLT_MIN);
if (UIWidgets::Button(randomizeButton.c_str(), UIWidgets::ButtonOptions()
.Size(UIWidgets::Sizes::Inline)
.Padding(ImVec2(10.0f, 6.0f))
.Tooltip("Randomize this sound")
.Color(THEME_COLOR))) {
std::vector<SequenceInfo*> validSequences = {};
for (const auto seqInfo : AudioCollection::Instance->GetIncludedSequences()) {
if (seqInfo->category & type) {
validSequences.push_back(seqInfo);
}
}
if (validSequences.size()) {
auto it = validSequences.begin();
const auto& seqData = *std::next(it, rand() % validSequences.size());
CVarSetInteger(cvarKey.c_str(), seqData->sequenceId);
if (locked) {
CVarClear(cvarLockKey.c_str());
}
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
UpdateCurrentBGM(defaultValue, type);
}
}
ImGui::SameLine();
ImGui::PushItemWidth(-FLT_MIN);
if (UIWidgets::Button(locked ? lockedButton.c_str() : unlockedButton.c_str(),
UIWidgets::ButtonOptions()
.Size(UIWidgets::Sizes::Inline)
.Padding(ImVec2(10.0f, 6.0f))
.Tooltip(locked ? "Sound locked" : "Sound unlocked")
.Color(THEME_COLOR))) {
if (locked) {
CVarClear(cvarLockKey.c_str());
} else {
CVarSetInteger(cvarLockKey.c_str(), 1);
}
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
}
}
ImGui::EndTable();
}
extern "C" u16 AudioEditor_GetReplacementSeq(u16 seqId) {
return AudioCollection::Instance->GetReplacementSequence(seqId);
}
std::string GetSequenceTypeName(SeqType type) {
switch (type) {
case SEQ_NOSHUFFLE:
return "No Shuffle";
case SEQ_BGM_WORLD:
return "World";
case SEQ_BGM_EVENT:
return "Event";
case SEQ_BGM_BATTLE:
return "Battle";
case SEQ_OCARINA:
return "Ocarina";
case SEQ_FANFARE:
return "Fanfare";
case SEQ_BGM_ERROR:
return "Error";
case SEQ_SFX:
return "SFX";
case SEQ_VOICE:
return "Voice";
case SEQ_INSTRUMENT:
return "Instrument";
case SEQ_BGM_CUSTOM:
return "Custom";
default:
return "No Sequence Type";
}
}
ImVec4 GetSequenceTypeColor(SeqType type) {
switch (type) {
case SEQ_BGM_WORLD:
return ImVec4(0.0f, 0.2f, 0.0f, 1.0f);
case SEQ_BGM_EVENT:
return ImVec4(0.3f, 0.0f, 0.15f, 1.0f);
case SEQ_BGM_BATTLE:
return ImVec4(0.2f, 0.07f, 0.0f, 1.0f);
case SEQ_OCARINA:
return ImVec4(0.0f, 0.0f, 0.4f, 1.0f);
case SEQ_FANFARE:
return ImVec4(0.3f, 0.0f, 0.3f, 1.0f);
case SEQ_SFX:
return ImVec4(0.4f, 0.33f, 0.0f, 1.0f);
case SEQ_VOICE:
return ImVec4(0.3f, 0.42f, 0.09f, 1.0f);
case SEQ_INSTRUMENT:
return ImVec4(0.0f, 0.25f, 0.5f, 1.0f);
case SEQ_BGM_CUSTOM:
return ImVec4(0.9f, 0.0f, 0.9f, 1.0f);
default:
return ImVec4(1.0f, 0.0f, 0.0f, 1.0f);
}
}
void DrawTypeChip(SeqType type, std::string sequenceName) {
ImGui::BeginDisabled();
ImGui::PushStyleColor(ImGuiCol_Button, GetSequenceTypeColor(type));
std::string buttonLabel = GetSequenceTypeName(type) + "##" + sequenceName;
ImGui::Button(buttonLabel.c_str());
ImGui::PopStyleColor();
ImGui::EndDisabled();
}
void AudioEditorRegisterOnSceneInitHook() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](int16_t sceneNum) {
if (gSaveContext.gameMode != GAMEMODE_END_CREDITS && CVarGetInteger(CVAR_AUDIO("RandomizeAllOnNewScene"), 0)) {
AudioEditor_RandomizeAll();
}
});
}
void AudioEditorRegisterOnGenerationCompletionHook() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGenerationCompletion>([]() {
if (CVarGetInteger(CVAR_AUDIO("RandomizeAllOnRandoGen"), 0)) {
AudioEditor_RandomizeAll();
}
});
}
void AudioEditor::InitElement() {
AudioEditorRegisterOnSceneInitHook();
AudioEditorRegisterOnGenerationCompletionHook();
}
void AudioEditor::DrawElement() {
AudioCollection::Instance->InitializeShufflePool();
UIWidgets::Separator();
if (UIWidgets::Button("Randomize All Groups",
UIWidgets::ButtonOptions()
.Size(ImVec2(230.0f, 0.0f))
.Color(THEME_COLOR)
.Tooltip("Randomizes all unlocked music and sound effects across tab groups"))) {
AudioEditor_RandomizeAll();
}
ImGui::SameLine();
if (UIWidgets::Button("Reset All Groups",
UIWidgets::ButtonOptions()
.Size(ImVec2(230.0f, 0.0f))
.Color(THEME_COLOR)
.Tooltip("Resets all unlocked music and sound effects across tab groups"))) {
AudioEditor_ResetAll();
}
ImGui::SameLine();
if (UIWidgets::Button("Lock All Groups", UIWidgets::ButtonOptions()
.Size(ImVec2(230.0f, 0.0f))
.Color(THEME_COLOR)
.Tooltip("Locks all music and sound effects across tab groups"))) {
AudioEditor_LockAll();
}
ImGui::SameLine();
if (UIWidgets::Button("Unlock All Groups", UIWidgets::ButtonOptions()
.Size(ImVec2(230.0f, 0.0f))
.Color(THEME_COLOR)
.Tooltip("Unlocks all music and sound effects across tab groups"))) {
AudioEditor_UnlockAll();
}
UIWidgets::Separator();
UIWidgets::PushStyleTabs(THEME_COLOR);
if (ImGui::BeginTabBar("SfxContextTabBar", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) {
static ImVec2 cellPadding(8.0f, 8.0f);
if (ImGui::BeginTabItem("Audio Options")) {
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding);
ImGui::BeginTable("Audio Options", 1, ImGuiTableFlags_SizingStretchSame);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::BeginChild("SfxOptions", ImVec2(0, -8))) {
SohGui::mSohMenu->MenuDrawItem(lowHpAlarm, ImGui::GetContentRegionAvail().x, THEME_COLOR);
SohGui::mSohMenu->MenuDrawItem(naviCall, ImGui::GetContentRegionAvail().x, THEME_COLOR);
SohGui::mSohMenu->MenuDrawItem(enemyProx, ImGui::GetContentRegionAvail().x, THEME_COLOR);
SohGui::mSohMenu->MenuDrawItem(leadingMusic, ImGui::GetContentRegionAvail().x, THEME_COLOR);
SohGui::mSohMenu->MenuDrawItem(displaySeqName, ImGui::GetContentRegionAvail().x, THEME_COLOR);
SohGui::mSohMenu->MenuDrawItem(ovlDuration, ImGui::GetContentRegionAvail().x, THEME_COLOR);
SohGui::mSohMenu->MenuDrawItem(voicePitch, ImGui::GetContentRegionAvail().x, THEME_COLOR);
ImGui::SameLine();
ImGui::SetCursorPosY(ImGui::GetCursorPos().y + 40.f);
if (UIWidgets::Button("Reset##linkVoiceFreqMultiplier",
UIWidgets::ButtonOptions().Size(ImVec2(80, 36)).Padding(ImVec2(5.0f, 0.0f)))) {
CVarSetFloat(CVAR_AUDIO("LinkVoiceFreqMultiplier"), 1.0f);
}
SohGui::mSohMenu->MenuDrawItem(randoMusicOnSceneChange, ImGui::GetContentRegionAvail().x, THEME_COLOR);
SohGui::mSohMenu->MenuDrawItem(randomAudioOnSeedGen, ImGui::GetContentRegionAvail().x, THEME_COLOR);
SohGui::mSohMenu->MenuDrawItem(lowerOctaves, ImGui::GetContentRegionAvail().x, THEME_COLOR);
}
ImGui::EndChild();
ImGui::EndTable();
ImGui::PopStyleVar(1);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Background Music")) {
Draw_SfxTab("backgroundMusic", SEQ_BGM_WORLD, "Background Music");
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Fanfares")) {
Draw_SfxTab("fanfares", SEQ_FANFARE, "Fanfares");
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Events")) {
Draw_SfxTab("event", SEQ_BGM_EVENT, "Events");
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Battle Music")) {
Draw_SfxTab("battleMusic", SEQ_BGM_BATTLE, "Battle Music");
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Ocarina")) {
Draw_SfxTab("instrument", SEQ_INSTRUMENT, "Instruments");
Draw_SfxTab("ocarina", SEQ_OCARINA, "Ocarina");
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Sound Effects")) {
Draw_SfxTab("sfx", SEQ_SFX, "Sound Effects");
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Voices")) {
Draw_SfxTab("voice", SEQ_VOICE, "Voices");
ImGui::EndTabItem();
}
static bool excludeTabOpen = false;
if (ImGui::BeginTabItem("Audio Shuffle Pool Management")) {
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, cellPadding);
if (!excludeTabOpen) {
excludeTabOpen = true;
}
static std::map<SeqType, bool> showType{
{ SEQ_BGM_WORLD, true }, { SEQ_BGM_EVENT, true }, { SEQ_BGM_BATTLE, true },
{ SEQ_OCARINA, true }, { SEQ_FANFARE, true }, { SEQ_SFX, true },
{ SEQ_VOICE, true }, { SEQ_INSTRUMENT, true }, { SEQ_BGM_CUSTOM, true },
};
// make temporary sets because removing from the set we're iterating through crashes ImGui
std::set<SequenceInfo*> seqsToInclude = {};
std::set<SequenceInfo*> seqsToExclude = {};
static ImGuiTextFilter sequenceSearch;
UIWidgets::PushStyleInput(THEME_COLOR);
sequenceSearch.Draw("Filter (inc,-exc)", 490.0f);
UIWidgets::PopStyleInput();
ImGui::SameLine();
if (UIWidgets::Button("Exclude All",
UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline).Color(THEME_COLOR))) {
for (auto seqInfo : AudioCollection::Instance->GetIncludedSequences()) {
if (sequenceSearch.PassFilter(seqInfo->label.c_str()) && showType[seqInfo->category]) {
seqsToExclude.insert(seqInfo);
}
}
}
ImGui::SameLine();
if (UIWidgets::Button("Include All",
UIWidgets::ButtonOptions().Size(UIWidgets::Sizes::Inline).Color(THEME_COLOR))) {
for (auto seqInfo : AudioCollection::Instance->GetExcludedSequences()) {
if (sequenceSearch.PassFilter(seqInfo->label.c_str()) && showType[seqInfo->category]) {
seqsToInclude.insert(seqInfo);
}
}
}
ImGui::BeginTable("sequenceTypes", 9,
ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_Borders);
ImGui::TableNextColumn();
ImGui::PushStyleColor(ImGuiCol_Header, GetSequenceTypeColor(SEQ_BGM_WORLD));
ImGui::Selectable(GetSequenceTypeName(SEQ_BGM_WORLD).c_str(), &showType[SEQ_BGM_WORLD]);
ImGui::PopStyleColor(1);
ImGui::TableNextColumn();
ImGui::PushStyleColor(ImGuiCol_Header, GetSequenceTypeColor(SEQ_BGM_EVENT));
ImGui::Selectable(GetSequenceTypeName(SEQ_BGM_EVENT).c_str(), &showType[SEQ_BGM_EVENT]);
ImGui::PopStyleColor(1);
ImGui::TableNextColumn();
ImGui::PushStyleColor(ImGuiCol_Header, GetSequenceTypeColor(SEQ_BGM_BATTLE));
ImGui::Selectable(GetSequenceTypeName(SEQ_BGM_BATTLE).c_str(), &showType[SEQ_BGM_BATTLE]);
ImGui::PopStyleColor(1);
ImGui::TableNextColumn();
ImGui::PushStyleColor(ImGuiCol_Header, GetSequenceTypeColor(SEQ_OCARINA));
ImGui::Selectable(GetSequenceTypeName(SEQ_OCARINA).c_str(), &showType[SEQ_OCARINA]);
ImGui::PopStyleColor(1);
ImGui::TableNextColumn();
ImGui::PushStyleColor(ImGuiCol_Header, GetSequenceTypeColor(SEQ_FANFARE));
ImGui::Selectable(GetSequenceTypeName(SEQ_FANFARE).c_str(), &showType[SEQ_FANFARE]);
ImGui::PopStyleColor(1);
ImGui::TableNextColumn();
ImGui::PushStyleColor(ImGuiCol_Header, GetSequenceTypeColor(SEQ_SFX));
ImGui::Selectable(GetSequenceTypeName(SEQ_SFX).c_str(), &showType[SEQ_SFX]);
ImGui::PopStyleColor(1);
ImGui::TableNextColumn();
ImGui::PushStyleColor(ImGuiCol_Header, GetSequenceTypeColor(SEQ_VOICE));
ImGui::Selectable(GetSequenceTypeName(SEQ_VOICE).c_str(), &showType[SEQ_VOICE]);
ImGui::PopStyleColor(1);
ImGui::TableNextColumn();
ImGui::PushStyleColor(ImGuiCol_Header, GetSequenceTypeColor(SEQ_INSTRUMENT));
ImGui::Selectable(GetSequenceTypeName(SEQ_INSTRUMENT).c_str(), &showType[SEQ_INSTRUMENT]);
ImGui::PopStyleColor(1);
ImGui::TableNextColumn();
ImGui::PushStyleColor(ImGuiCol_Header, GetSequenceTypeColor(SEQ_BGM_CUSTOM));
ImGui::Selectable(GetSequenceTypeName(SEQ_BGM_CUSTOM).c_str(), &showType[SEQ_BGM_CUSTOM]);
ImGui::PopStyleColor(1);
ImGui::EndTable();
if (ImGui::BeginTable("tableAllSequences", 2, ImGuiTableFlags_BordersH | ImGuiTableFlags_BordersV)) {
ImGui::TableSetupColumn("Included", ImGuiTableColumnFlags_WidthStretch, 200.0f);
ImGui::TableSetupColumn("Excluded", ImGuiTableColumnFlags_WidthStretch, 200.0f);
ImGui::TableHeadersRow();
ImGui::TableNextRow();
// COLUMN 1 - INCLUDED SEQUENCES
ImGui::TableNextColumn();
ImGui::BeginChild("ChildIncludedSequences", ImVec2(0, -8));
for (auto seqInfo : AudioCollection::Instance->GetIncludedSequences()) {
if (sequenceSearch.PassFilter(seqInfo->label.c_str()) && showType[seqInfo->category]) {
if (UIWidgets::Button(std::string(ICON_FA_TIMES "##" + seqInfo->sfxKey).c_str(),
UIWidgets::ButtonOptions()
.Size(UIWidgets::Sizes::Inline)
.Padding(ImVec2(9.0f, 6.0f))
.Color(THEME_COLOR))) {
seqsToExclude.insert(seqInfo);
}
ImGui::SameLine();
DrawPreviewButton(seqInfo->sequenceId, seqInfo->sfxKey, seqInfo->category);
ImGui::SameLine();
DrawTypeChip(seqInfo->category, seqInfo->label);
ImGui::SameLine();
ImGui::Text("%s", seqInfo->label.c_str());
}
}
ImGui::EndChild();
// remove the sequences we added to the temp set
for (auto seqInfo : seqsToExclude) {
AudioCollection::Instance->RemoveFromShufflePool(seqInfo);
}
// COLUMN 2 - EXCLUDED SEQUENCES
ImGui::TableNextColumn();
ImGui::BeginChild("ChildExcludedSequences", ImVec2(0, -8));
for (auto seqInfo : AudioCollection::Instance->GetExcludedSequences()) {
if (sequenceSearch.PassFilter(seqInfo->label.c_str()) && showType[seqInfo->category]) {
if (UIWidgets::Button(std::string(ICON_FA_PLUS "##" + seqInfo->sfxKey).c_str(),
UIWidgets::ButtonOptions()
.Size(UIWidgets::Sizes::Inline)
.Padding(ImVec2(9.0f, 6.0f))
.Color(THEME_COLOR))) {
seqsToInclude.insert(seqInfo);
}
ImGui::SameLine();
DrawPreviewButton(seqInfo->sequenceId, seqInfo->sfxKey, seqInfo->category);
ImGui::SameLine();
DrawTypeChip(seqInfo->category, seqInfo->sfxKey);
ImGui::SameLine();
ImGui::Text("%s", seqInfo->label.c_str());
}
}
ImGui::EndChild();
// add the sequences we added to the temp set
for (auto seqInfo : seqsToInclude) {
AudioCollection::Instance->AddToShufflePool(seqInfo);
}
ImGui::EndTable();
}
ImGui::PopStyleVar(1);
ImGui::EndTabItem();
} else {
excludeTabOpen = false;
}
ImGui::EndTabBar();
}
UIWidgets::PopStyleTabs();
}
std::vector<SeqType> allTypes = {
SEQ_BGM_WORLD, SEQ_BGM_EVENT, SEQ_BGM_BATTLE, SEQ_OCARINA, SEQ_FANFARE, SEQ_INSTRUMENT, SEQ_SFX, SEQ_VOICE,
};
void AudioEditor_RandomizeAll() {
for (auto type : allTypes) {
RandomizeGroup(type);
}
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
ReplayCurrentBGM();
}
void AudioEditor_RandomizeGroup(SeqType group) {
RandomizeGroup(group);
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
ReplayCurrentBGM();
}
void AudioEditor_ResetAll() {
for (auto type : allTypes) {
ResetGroup(AudioCollection::Instance->GetAllSequences(), type);
}
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
ReplayCurrentBGM();
}
void AudioEditor_ResetGroup(SeqType group) {
ResetGroup(AudioCollection::Instance->GetAllSequences(), group);
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
ReplayCurrentBGM();
}
void AudioEditor_LockAll() {
for (auto type : allTypes) {
LockGroup(AudioCollection::Instance->GetAllSequences(), type);
}
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
}
void AudioEditor_UnlockAll() {
for (auto type : allTypes) {
UnlockGroup(AudioCollection::Instance->GetAllSequences(), type);
}
Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame();
}
void RegisterAudioWidgets() {
lowHpAlarm = { .name = "Mute Low HP Alarm", .type = WidgetType::WIDGET_CVAR_CHECKBOX };
lowHpAlarm.CVar(CVAR_AUDIO("LowHpAlarm"))
.Options(CheckboxOptions().Color(THEME_COLOR).Tooltip("Disable the low HP beeping sound."));
SohGui::mSohMenu->AddSearchWidget({ lowHpAlarm, "Enhancements", "Audio Editor", "Audio Options" });
naviCall = { .name = "Disable Navi Call Audio", .type = WidgetType::WIDGET_CVAR_CHECKBOX };
naviCall.CVar(CVAR_AUDIO("DisableNaviCallAudio"))
.Options(CheckboxOptions().Color(THEME_COLOR).Tooltip("Disables the voice audio when Navi calls you."));
SohGui::mSohMenu->AddSearchWidget({ naviCall, "Enhancements", "Audio Editor", "Audio Options" });
enemyProx = { .name = "Disable Enemy Proximity Music", .type = WidgetType::WIDGET_CVAR_CHECKBOX };
enemyProx.CVar(CVAR_AUDIO("EnemyBGMDisable"))
.Options(CheckboxOptions()
.Color(THEME_COLOR)
.Tooltip("Disables the music change when getting close to enemies. Useful for hearing "
"your custom music for each scene more often."));
leadingMusic = { .name = "Disable Leading Music in Lost Woods", .type = WidgetType::WIDGET_CVAR_CHECKBOX };
leadingMusic.CVar(CVAR_AUDIO("LostWoodsConsistentVolume"))
.Options(CheckboxOptions()
.Color(THEME_COLOR)
.Tooltip("Disables the volume shifting in the Lost Woods. Useful for hearing "
"your custom music in the Lost Woods if you don't need the navigation assitance "
"the volume changing provides. If toggling this while in the Lost Woods, reload "
"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"))
.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)."));
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)));
SohGui::mSohMenu->AddSearchWidget({ ovlDuration, "Enhancements", "Audio Editor", "Audio Options" });
voicePitch = { .name = "Link's Voice Pitch Multiplier", .type = WidgetType::WIDGET_CVAR_SLIDER_FLOAT };
voicePitch.CVar(CVAR_AUDIO("LinkVoiceFreqMultiplier"))
.Options(FloatSliderOptions()
.Color(THEME_COLOR)
.IsPercentage()
.Min(0.4f)
.Max(2.5f)
.DefaultValue(1.0f)
.Size(ImVec2(300.0f, 0.0f)));
SohGui::mSohMenu->AddSearchWidget({ voicePitch, "Enhancements", "Audio Editor", "Audio Options" });
randoMusicOnSceneChange = { .name = "Randomize All Music and Sound Effects on New Scene",
.type = WidgetType::WIDGET_CVAR_CHECKBOX };
randoMusicOnSceneChange.CVar(CVAR_AUDIO("RandomizeAllOnNewScene"))
.Options(CheckboxOptions()
.Color(THEME_COLOR)
.Tooltip("Enables randomizing all unlocked music and sound effects when you enter a new scene."));
SohGui::mSohMenu->AddSearchWidget({ randoMusicOnSceneChange, "Enhancements", "Audio Editor", "Audio Options" });
randomAudioOnSeedGen = { .name = "Randomize All Music and Sound Effects on Randomizer Generation",
.type = WidgetType::WIDGET_CVAR_CHECKBOX };
randomAudioOnSeedGen.CVar(CVAR_AUDIO("RandomizeAllOnRandoGen"))
.Options(CheckboxOptions()
.Color(THEME_COLOR)
.Tooltip("Enables randomizing all unlocked music and sound effects when you generate a new "
"randomizer. Respects locks already in place."));
SohGui::mSohMenu->AddSearchWidget({ randomAudioOnSeedGen, "Enhancements", "Audio Editor", "Audio Options" });
lowerOctaves = { .name = "Lower Octaves of Unplayable High Notes", .type = WidgetType::WIDGET_CVAR_CHECKBOX };
lowerOctaves.CVar(CVAR_AUDIO("ExperimentalOctaveDrop"))
.Options(CheckboxOptions()
.Color(THEME_COLOR)
.Tooltip("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."));
SohGui::mSohMenu->AddSearchWidget({ lowerOctaves, "Enhancements", "Audio Editor", "Audio Options" });
}
static RegisterMenuInitFunc initAudioWidgets(RegisterAudioWidgets);