mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2025-04-28 13:17:58 +03:00
827 lines
37 KiB
C++
827 lines
37 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/UIWidgets.hpp"
|
|
#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;
|
|
|
|
// 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();
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
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 AudioEditor::InitElement() {
|
|
AudioEditorRegisterOnSceneInitHook();
|
|
}
|
|
|
|
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))) {
|
|
UIWidgets::CVarCheckbox(
|
|
"Mute Low HP Alarm", CVAR_AUDIO("LowHpAlarm"),
|
|
UIWidgets::CheckboxOptions().Color(THEME_COLOR).Tooltip("Disable the low HP beeping sound."));
|
|
UIWidgets::CVarCheckbox("Disable Navi Call Audio", CVAR_AUDIO("DisableNaviCallAudio"),
|
|
UIWidgets::CheckboxOptions()
|
|
.Color(THEME_COLOR)
|
|
.Tooltip("Disables the voice audio when Navi calls you."));
|
|
UIWidgets::CVarCheckbox(
|
|
"Disable Enemy Proximity Music", CVAR_AUDIO("EnemyBGMDisable"),
|
|
UIWidgets::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."));
|
|
UIWidgets::CVarCheckbox(
|
|
"Disable Leading Music in Lost Woods", CVAR_AUDIO("LostWoodsConsistentVolume"),
|
|
UIWidgets::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."));
|
|
UIWidgets::CVarCheckbox(
|
|
"Display Sequence Name on Overlay", CVAR_AUDIO("SeqNameOverlay"),
|
|
UIWidgets::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)."));
|
|
UIWidgets::CVarSliderInt("Overlay Duration: %d seconds", CVAR_AUDIO("SeqNameOverlayDuration"),
|
|
UIWidgets::IntSliderOptions()
|
|
.Min(1)
|
|
.Max(10)
|
|
.DefaultValue(5)
|
|
.Size(ImVec2(300.0f, 0.0f))
|
|
.Color(THEME_COLOR));
|
|
UIWidgets::CVarSliderFloat("Link's voice pitch multiplier", CVAR_AUDIO("LinkVoiceFreqMultiplier"),
|
|
UIWidgets::FloatSliderOptions()
|
|
.IsPercentage()
|
|
.Min(0.4f)
|
|
.Max(2.5f)
|
|
.DefaultValue(1.0f)
|
|
.Size(ImVec2(300.0f, 0.0f))
|
|
.Color(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);
|
|
}
|
|
UIWidgets::CVarCheckbox(
|
|
"Randomize All Music and Sound Effects on New Scene", CVAR_AUDIO("RandomizeAllOnNewScene"),
|
|
UIWidgets::CheckboxOptions()
|
|
.Color(THEME_COLOR)
|
|
.Tooltip(
|
|
"Enables randomizing all unlocked music and sound effects when you enter a new scene."));
|
|
UIWidgets::CVarCheckbox(
|
|
"Lower Octaves of Unplayable High Notes", CVAR_AUDIO("ExperimentalOctaveDrop"),
|
|
UIWidgets::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."));
|
|
}
|
|
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();
|
|
}
|