exclude audio from shuffle (#2411)

* Rename "SFX Editor" to "Audio Editor"
* Move some functionality out into a new class `AudioCollection`
* Add a tab to exclude sfx/sequences from shuffle pool 

---------

Co-authored-by: briaguya <briaguya@alice>
Co-authored-by: briaguya <briaguya>
Co-authored-by: David Chavez <david@dcvz.io>
This commit is contained in:
briaguya 2023-02-16 09:49:45 -05:00 committed by GitHub
parent 27f961ac4f
commit 1e6ec1bdda
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 960 additions and 574 deletions

View File

@ -176,10 +176,11 @@ set(Header_Files__soh__Enhancements__cosmetics
) )
source_group("Header Files\\soh\\Enhancements\\cosmetics" FILES ${Header_Files__soh__Enhancements__cosmetics}) source_group("Header Files\\soh\\Enhancements\\cosmetics" FILES ${Header_Files__soh__Enhancements__cosmetics})
set(Header_Files__soh__Enhancements__sfx_editor set(Header_Files__soh__Enhancements__audio
"soh/Enhancements/sfx-editor/SfxEditor.h" "soh/Enhancements/audio/AudioCollection.h"
"soh/Enhancements/audio/AudioEditor.h"
) )
source_group("Header Files\\soh\\Enhancements\\sfx-editor" FILES ${Header_Files__soh__Enhancements__sfx_editor}) source_group("Header Files\\soh\\Enhancements\\audio" FILES ${Header_Files__soh__Enhancements__audio})
set(Header_Files__soh__Enhancements__debugger set(Header_Files__soh__Enhancements__debugger
"soh/Enhancements/debugger/dlViewer.h" "soh/Enhancements/debugger/dlViewer.h"
@ -512,10 +513,11 @@ set(Source_Files__soh__Enhancements__cosmetics
) )
source_group("Source Files\\soh\\Enhancements\\cosmetics" FILES ${Source_Files__soh__Enhancements__cosmetics}) source_group("Source Files\\soh\\Enhancements\\cosmetics" FILES ${Source_Files__soh__Enhancements__cosmetics})
set(Source_Files__soh__Enhancements__sfx_editor set(Source_Files__soh__Enhancements__audio
"soh/Enhancements/sfx-editor/SfxEditor.cpp" "soh/Enhancements/audio/AudioCollection.cpp"
"soh/Enhancements/audio/AudioEditor.cpp"
) )
source_group("Source Files\\soh\\Enhancements\\sfx-editor" FILES ${Source_Files__soh__Enhancements__sfx_editor}) source_group("Source Files\\soh\\Enhancements\\audio" FILES ${Source_Files__soh__Enhancements__audio})
set(Source_Files__soh__Enhancements__debugger set(Source_Files__soh__Enhancements__debugger
"soh/Enhancements/debugger/dlViewer.cpp" "soh/Enhancements/debugger/dlViewer.cpp"
@ -1841,7 +1843,7 @@ set(ALL_FILES
${Header_Files__soh__Enhancements} ${Header_Files__soh__Enhancements}
${Header_Files__soh__Enhancements__controls} ${Header_Files__soh__Enhancements__controls}
${Header_Files__soh__Enhancements__cosmetics} ${Header_Files__soh__Enhancements__cosmetics}
${Header_Files__soh__Enhancements__sfx_editor} ${Header_Files__soh__Enhancements__audio}
${Header_Files__soh__Enhancements__debugger} ${Header_Files__soh__Enhancements__debugger}
${Header_Files__soh__Enhancements__randomizer} ${Header_Files__soh__Enhancements__randomizer}
${Header_Files__soh__Enhancements__randomizer__3drando} ${Header_Files__soh__Enhancements__randomizer__3drando}
@ -1854,7 +1856,7 @@ set(ALL_FILES
${Source_Files__soh__Enhancements} ${Source_Files__soh__Enhancements}
${Source_Files__soh__Enhancements__controls} ${Source_Files__soh__Enhancements__controls}
${Source_Files__soh__Enhancements__cosmetics} ${Source_Files__soh__Enhancements__cosmetics}
${Source_Files__soh__Enhancements__sfx_editor} ${Source_Files__soh__Enhancements__audio}
${Source_Files__soh__Enhancements__debugger} ${Source_Files__soh__Enhancements__debugger}
${Source_Files__soh__Enhancements__randomizer} ${Source_Files__soh__Enhancements__randomizer}
${Source_Files__soh__Enhancements__randomizer__3drando} ${Source_Files__soh__Enhancements__randomizer__3drando}

View File

@ -2092,6 +2092,7 @@ void func_800F595C(u16);
void func_800F59E8(u16); void func_800F59E8(u16);
s32 func_800F5A58(u8); s32 func_800F5A58(u8);
void func_800F5ACC(u16 seqId); void func_800F5ACC(u16 seqId);
void PreviewSequence(u16 seqId);
void func_800F5B58(void); void func_800F5B58(void);
void func_800F5BF0(u8 natureAmbienceId); void func_800F5BF0(u8 natureAmbienceId);
void Audio_PlayFanfare(u16); void Audio_PlayFanfare(u16);
@ -2134,6 +2135,7 @@ void Audio_StopSfxByPosAndBank(u8, Vec3f*);
void Audio_StopSfxByPos(Vec3f*); void Audio_StopSfxByPos(Vec3f*);
void func_800F9280(u8 playerIdx, u8 seqId, u8 arg2, u16 fadeTimer); void func_800F9280(u8 playerIdx, u8 seqId, u8 arg2, u16 fadeTimer);
void Audio_QueueSeqCmd(u32 bgmID); void Audio_QueueSeqCmd(u32 bgmID);
void Audio_QueuePreviewSeqCmd(u16 seqId);
void Audio_StopSfxByPosAndId(Vec3f* pos, u16 sfxId); void Audio_StopSfxByPosAndId(Vec3f* pos, u16 sfxId);
void Audio_StopSfxByTokenAndId(u8, u16); void Audio_StopSfxByTokenAndId(u8, u16);
void Audio_StopSfxById(u32 sfxId); void Audio_StopSfxById(u32 sfxId);

View File

@ -0,0 +1,274 @@
#include "AudioCollection.h"
#include "sequence.h"
#include "sfx.h"
#include <vector>
#include <Utils/StringHelper.h>
#include <libultraship/bridge.h>
#include <libultraship/classes.h>
#include <locale>
#include <filesystem>
#define SEQUENCE_MAP_ENTRY(sequenceId, label, sfxKey, category) \
{ sequenceId, { sequenceId, label, sfxKey, category } }
AudioCollection::AudioCollection() {
// (originalSequenceId, label, sfxKey, category),
sequenceMap = {
SEQUENCE_MAP_ENTRY(NA_BGM_FIELD_LOGIC, "Hyrule Field", "NA_BGM_FIELD_LOGIC", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_DUNGEON, "Dodongo's Cavern", "NA_BGM_DUNGEON", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_KAKARIKO_ADULT, "Kakariko Village (Adult)", "NA_BGM_KAKARIKO_ADULT", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_ENEMY, "Battle", "NA_BGM_ENEMY", SEQ_BGM_BATTLE),
SEQUENCE_MAP_ENTRY(NA_BGM_BOSS, "Boss Battle", "NA_BGM_BOSS", SEQ_BGM_BATTLE),
SEQUENCE_MAP_ENTRY(NA_BGM_INSIDE_DEKU_TREE, "Inside the Deku Tree", "NA_BGM_INSIDE_DEKU_TREE", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_MARKET, "Market", "NA_BGM_MARKET", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_TITLE, "Title Theme", "NA_BGM_TITLE", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_LINK_HOUSE, "House", "NA_BGM_LINK_HOUSE", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_GAME_OVER, "Game Over", "NA_BGM_GAME_OVER", SEQ_FANFARE),
SEQUENCE_MAP_ENTRY(NA_BGM_BOSS_CLEAR, "Boss Clear", "NA_BGM_BOSS_CLEAR", SEQ_FANFARE),
SEQUENCE_MAP_ENTRY(NA_BGM_ITEM_GET, "Obtain Item", "NA_BGM_ITEM_GET", SEQ_FANFARE),
SEQUENCE_MAP_ENTRY(NA_BGM_OPENING_GANON, "Enter Ganondorf", "NA_BGM_OPENING_GANON", SEQ_FANFARE),
SEQUENCE_MAP_ENTRY(NA_BGM_HEART_GET, "Obtain Heart Container", "NA_BGM_HEART_GET", SEQ_FANFARE),
SEQUENCE_MAP_ENTRY(NA_BGM_OCA_LIGHT, "Prelude of Light", "NA_BGM_OCA_LIGHT", SEQ_OCARINA),
SEQUENCE_MAP_ENTRY(NA_BGM_JABU_JABU, "Inside Jabu-Jabu's Belly", "NA_BGM_JABU_JABU", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_KAKARIKO_KID, "Kakariko Village (Child)", "NA_BGM_KAKARIKO_KID", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_GREAT_FAIRY, "Great Fairy's Fountain", "NA_BGM_GREAT_FAIRY", SEQ_BGM_EVENT),
SEQUENCE_MAP_ENTRY(NA_BGM_ZELDA_THEME, "Zelda's Theme", "NA_BGM_ZELDA_THEME", SEQ_BGM_EVENT),
SEQUENCE_MAP_ENTRY(NA_BGM_FIRE_TEMPLE, "Fire Temple", "NA_BGM_FIRE_TEMPLE", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_OPEN_TRE_BOX, "Open Treasure Chest", "NA_BGM_OPEN_TRE_BOX", SEQ_FANFARE),
SEQUENCE_MAP_ENTRY(NA_BGM_FOREST_TEMPLE, "Forest Temple", "NA_BGM_FOREST_TEMPLE", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_COURTYARD, "Hyrule Castle Courtyard", "NA_BGM_COURTYARD", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_GANON_TOWER, "Ganondorf's Theme", "NA_BGM_GANON_TOWER", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_LONLON, "Lon Lon Ranch", "NA_BGM_LONLON", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_GORON_CITY, "Goron City", "NA_BGM_GORON_CITY", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_FIELD_MORNING, "Hyrule Field Morning Theme", "NA_BGM_FIELD_MORNING", SEQ_NOSHUFFLE),
SEQUENCE_MAP_ENTRY(NA_BGM_SPIRITUAL_STONE, "Spiritual Stone Get", "NA_BGM_SPIRITUAL_STONE", SEQ_FANFARE),
SEQUENCE_MAP_ENTRY(NA_BGM_OCA_BOLERO, "Bolero of Fire", "NA_BGM_OCA_BOLERO", SEQ_OCARINA),
SEQUENCE_MAP_ENTRY(NA_BGM_OCA_MINUET, "Minuet of Forest", "NA_BGM_OCA_MINUET", SEQ_OCARINA),
SEQUENCE_MAP_ENTRY(NA_BGM_OCA_SERENADE, "Serenade of Water", "NA_BGM_OCA_SERENADE", SEQ_OCARINA),
SEQUENCE_MAP_ENTRY(NA_BGM_OCA_REQUIEM, "Requiem of Spirit", "NA_BGM_OCA_REQUIEM", SEQ_OCARINA),
SEQUENCE_MAP_ENTRY(NA_BGM_OCA_NOCTURNE, "Nocturne of Shadow", "NA_BGM_OCA_NOCTURNE", SEQ_OCARINA),
SEQUENCE_MAP_ENTRY(NA_BGM_MINI_BOSS, "Mini-Boss Battle", "NA_BGM_MINI_BOSS", SEQ_BGM_BATTLE),
SEQUENCE_MAP_ENTRY(NA_BGM_SMALL_ITEM_GET, "Obtain Small Item", "NA_BGM_SMALL_ITEM_GET", SEQ_FANFARE),
SEQUENCE_MAP_ENTRY(NA_BGM_TEMPLE_OF_TIME, "Temple of Time", "NA_BGM_TEMPLE_OF_TIME", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_EVENT_CLEAR, "Escape from Lon Lon Ranch", "NA_BGM_EVENT_CLEAR", SEQ_FANFARE),
SEQUENCE_MAP_ENTRY(NA_BGM_KOKIRI, "Kokiri Forest", "NA_BGM_KOKIRI", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_OCA_FAIRY_GET, "Obtain Fairy Ocarina", "NA_BGM_OCA_FAIRY_GET", SEQ_FANFARE),
SEQUENCE_MAP_ENTRY(NA_BGM_SARIA_THEME, "Lost Woods", "NA_BGM_SARIA_THEME", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_SPIRIT_TEMPLE, "Spirit Temple", "NA_BGM_SPIRIT_TEMPLE", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_HORSE, "Horse Race", "NA_BGM_HORSE", SEQ_BGM_EVENT),
SEQUENCE_MAP_ENTRY(NA_BGM_HORSE_GOAL, "Horse Race Goal", "NA_BGM_HORSE_GOAL", SEQ_FANFARE),
SEQUENCE_MAP_ENTRY(NA_BGM_INGO, "Ingo's Theme", "NA_BGM_INGO", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_MEDALLION_GET, "Obtain Medallion", "NA_BGM_MEDALLION_GET", SEQ_FANFARE),
SEQUENCE_MAP_ENTRY(NA_BGM_OCA_SARIA, "Ocarina Saria's Song", "NA_BGM_OCA_SARIA", SEQ_OCARINA),
SEQUENCE_MAP_ENTRY(NA_BGM_OCA_EPONA, "Ocarina Epona's Song", "NA_BGM_OCA_EPONA", SEQ_OCARINA),
SEQUENCE_MAP_ENTRY(NA_BGM_OCA_ZELDA, "Ocarina Zelda's Lullaby", "NA_BGM_OCA_ZELDA", SEQ_OCARINA),
SEQUENCE_MAP_ENTRY(NA_BGM_OCA_SUNS, "Ocarina Sun's Song", "NA_BGM_OCA_SUNS", SEQ_OCARINA),
SEQUENCE_MAP_ENTRY(NA_BGM_OCA_TIME, "Ocarina Song of Time", "NA_BGM_OCA_TIME", SEQ_OCARINA),
SEQUENCE_MAP_ENTRY(NA_BGM_OCA_STORM, "Ocarina Song of Storms", "NA_BGM_OCA_STORM", SEQ_OCARINA),
SEQUENCE_MAP_ENTRY(NA_BGM_NAVI_OPENING, "Fairy Flying", "NA_BGM_NAVI_OPENING", SEQ_BGM_EVENT),
SEQUENCE_MAP_ENTRY(NA_BGM_DEKU_TREE_CS, "Deku Tree", "NA_BGM_DEKU_TREE_CS", SEQ_BGM_EVENT),
SEQUENCE_MAP_ENTRY(NA_BGM_WINDMILL, "Windmill Hut", "NA_BGM_WINDMILL", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_HYRULE_CS, "Legend of Hyrule", "NA_BGM_HYRULE_CS", SEQ_BGM_EVENT),
SEQUENCE_MAP_ENTRY(NA_BGM_MINI_GAME, "Shooting Gallery", "NA_BGM_MINI_GAME", SEQ_BGM_EVENT),
SEQUENCE_MAP_ENTRY(NA_BGM_SHEIK, "Sheik's Theme", "NA_BGM_SHEIK", SEQ_BGM_EVENT),
SEQUENCE_MAP_ENTRY(NA_BGM_ZORA_DOMAIN, "Zora's Domain", "NA_BGM_ZORA_DOMAIN", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_APPEAR, "Enter Zelda", "NA_BGM_APPEAR", SEQ_FANFARE),
SEQUENCE_MAP_ENTRY(NA_BGM_ADULT_LINK, "Goodbye to Zelda", "NA_BGM_ADULT_LINK", SEQ_BGM_EVENT),
SEQUENCE_MAP_ENTRY(NA_BGM_MASTER_SWORD, "Master Sword", "NA_BGM_MASTER_SWORD", SEQ_FANFARE),
SEQUENCE_MAP_ENTRY(NA_BGM_INTRO_GANON, "Ganon Intro", "NA_BGM_INTRO_GANON", SEQ_BGM_EVENT),
SEQUENCE_MAP_ENTRY(NA_BGM_SHOP, "Shop", "NA_BGM_SHOP", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_CHAMBER_OF_SAGES, "Chamber of the Sages", "NA_BGM_CHAMBER_OF_SAGES", SEQ_BGM_EVENT),
SEQUENCE_MAP_ENTRY(NA_BGM_FILE_SELECT, "File Select", "NA_BGM_FILE_SELECT", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_ICE_CAVERN, "Ice Cavern", "NA_BGM_ICE_CAVERN", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_DOOR_OF_TIME, "Open Door of Temple of Time", "NA_BGM_DOOR_OF_TIME", SEQ_BGM_EVENT),
SEQUENCE_MAP_ENTRY(NA_BGM_OWL, "Kaepora Gaebora's Theme", "NA_BGM_OWL", SEQ_BGM_EVENT),
SEQUENCE_MAP_ENTRY(NA_BGM_SHADOW_TEMPLE, "Shadow Temple", "NA_BGM_SHADOW_TEMPLE", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_WATER_TEMPLE, "Water Temple", "NA_BGM_WATER_TEMPLE", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_BRIDGE_TO_GANONS, "Ganon's Castle Bridge", "NA_BGM_BRIDGE_TO_GANONS", SEQ_BGM_EVENT),
SEQUENCE_MAP_ENTRY(NA_BGM_OCARINA_OF_TIME, "Ocarina of Time", "NA_BGM_OCARINA_OF_TIME", SEQ_FANFARE),
SEQUENCE_MAP_ENTRY(NA_BGM_GERUDO_VALLEY, "Gerudo Valley", "NA_BGM_GERUDO_VALLEY", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_POTION_SHOP, "Potion Shop", "NA_BGM_POTION_SHOP", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_KOTAKE_KOUME, "Kotake & Koume's Theme", "NA_BGM_KOTAKE_KOUME", SEQ_BGM_EVENT),
SEQUENCE_MAP_ENTRY(NA_BGM_ESCAPE, "Escape from Ganon's Castle", "NA_BGM_ESCAPE", SEQ_BGM_EVENT),
SEQUENCE_MAP_ENTRY(NA_BGM_UNDERGROUND, "Ganon's Castle Under Ground", "NA_BGM_UNDERGROUND", SEQ_BGM_WORLD),
SEQUENCE_MAP_ENTRY(NA_BGM_GANONDORF_BOSS, "Ganondorf Battle", "NA_BGM_GANONDORF_BOSS", SEQ_BGM_BATTLE),
SEQUENCE_MAP_ENTRY(NA_BGM_GANON_BOSS, "Ganon Battle", "NA_BGM_GANON_BOSS", SEQ_BGM_BATTLE),
SEQUENCE_MAP_ENTRY(NA_BGM_END_DEMO, "Seal of Six Sages", "NA_BGM_END_DEMO", SEQ_NOSHUFFLE),
SEQUENCE_MAP_ENTRY(NA_BGM_STAFF_1, "End Credits I", "NA_BGM_STAFF_1", SEQ_NOSHUFFLE),
SEQUENCE_MAP_ENTRY(NA_BGM_STAFF_2, "End Credits II", "NA_BGM_STAFF_2", SEQ_NOSHUFFLE),
SEQUENCE_MAP_ENTRY(NA_BGM_STAFF_3, "End Credits III", "NA_BGM_STAFF_3", SEQ_NOSHUFFLE),
SEQUENCE_MAP_ENTRY(NA_BGM_STAFF_4, "End Credits IV", "NA_BGM_STAFF_4", SEQ_NOSHUFFLE),
SEQUENCE_MAP_ENTRY(NA_BGM_FIRE_BOSS, "King Dodongo & Volvagia Boss Battle", "NA_BGM_FIRE_BOSS", SEQ_BGM_BATTLE),
SEQUENCE_MAP_ENTRY(NA_BGM_TIMED_MINI_GAME, "Mini-Game", "NA_BGM_TIMED_MINI_GAME", SEQ_BGM_EVENT),
SEQUENCE_MAP_ENTRY(INSTRUMENT_OFFSET + 1, "Ocarina", "OCARINA_INSTRUMENT_DEFAULT", SEQ_INSTRUMENT),
SEQUENCE_MAP_ENTRY(INSTRUMENT_OFFSET + 2, "Malon", "OCARINA_INSTRUMENT_MALON", SEQ_INSTRUMENT),
SEQUENCE_MAP_ENTRY(INSTRUMENT_OFFSET + 3, "Whistle", "OCARINA_INSTRUMENT_WHISTLE", SEQ_INSTRUMENT),
SEQUENCE_MAP_ENTRY(INSTRUMENT_OFFSET + 4, "Harp", "OCARINA_INSTRUMENT_HARP", SEQ_INSTRUMENT),
SEQUENCE_MAP_ENTRY(INSTRUMENT_OFFSET + 5, "Organ", "OCARINA_INSTRUMENT_GRIND_ORGAN", SEQ_INSTRUMENT),
SEQUENCE_MAP_ENTRY(INSTRUMENT_OFFSET + 6, "Flute", "OCARINA_INSTRUMENT_FLUTE", SEQ_INSTRUMENT),
SEQUENCE_MAP_ENTRY(NA_SE_EV_SMALL_DOG_BARK, "Bark", "NA_SE_EV_SMALL_DOG_BARK", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_AWA_BOUND, "Bomb Bounce", "NA_SE_EN_AWA_BOUND", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_SHADEST_TAIKO_LOW, "Bongo Bongo Low", "NA_SE_EN_SHADEST_TAIKO_LOW", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_NUTS_FAINT, "Business Scrub", "NA_SE_EN_NUTS_FAINT", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_SY_CARROT_RECOVER, "Carrot Refill", "NA_SE_SY_CARROT_RECOVER", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EV_CHICKEN_CRY_N, "Cluck", "NA_SE_EV_CHICKEN_CRY_N", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EV_BRIDGE_OPEN_STOP, "Drawbridge Set", "NA_SE_EV_BRIDGE_OPEN_STOP", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_KAICHO_CRY, "Guay", "NA_SE_EN_KAICHO_CRY", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_SY_HITPOINT_ALARM, "Low HP Beep", "NA_SE_SY_HITPOINT_ALARM", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_SY_HP_RECOVER, "HP Recover", "NA_SE_SY_HP_RECOVER", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EV_HORSE_RUN, "Horse Trot", "NA_SE_EV_HORSE_RUN", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_PL_WALK_HEAVYBOOTS, "Iron Boots", "NA_SE_PL_WALK_HEAVYBOOTS", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EV_COW_CRY, "Moo", "NA_SE_EV_COW_CRY", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_VO_KZ_MOVE, "Mweep!", "NA_SE_VO_KZ_MOVE", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_VO_NA_HELLO_2, "Navi Hey!", "NA_SE_VO_NA_HELLO_2", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_VO_SK_LAUGH, "Navi Listen!", "NA_SE_VO_SK_LAUGH", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EV_POT_BROKEN, "Pot Shattering", "NA_SE_EV_POT_BROKEN", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EV_FROG_CRY_0, "Ribbit", "NA_SE_EV_FROG_CRY_0", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EV_FIVE_COUNT_LUPY, "Rupee (Silver)", "NA_SE_EV_FIVE_COUNT_LUPY", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EV_FOOT_SWITCH, "Switch", "NA_SE_EV_FOOT_SWITCH", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_IT_WALL_HIT_SOFT, "Sword Bonk", "NA_SE_IT_WALL_HIT_SOFT", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_SY_METRONOME, "Tambourine", "NA_SE_SY_METRONOME", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_VO_Z1_SURPRISE, "Zelda Gasp (Adult)", "NA_SE_VO_Z1_SURPRISE", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_AMOS_VOICE, "Armos", "NA_SE_EN_AMOS_VOICE", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_VO_LI_FALL_L_KID, "Child Scream", "NA_SE_VO_LI_FALL_L_KID", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_VO_FR_LAUGH_0, "Great Fairy", "NA_SE_VO_FR_LAUGH_0", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_REDEAD_AIM, "Redead Scream", "NA_SE_EN_REDEAD_AIM", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_VO_RT_LAUGH_0, "Ruto Giggle", "NA_SE_VO_RT_LAUGH_0", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_STALKID_ATTACK, "Stalchild Attack", "NA_SE_EN_STALKID_ATTACK", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EV_CHICKEN_CRY_M, "Cockadoodiedoo", "NA_SE_EV_CHICKEN_CRY_M", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_SY_KINSTA_MARK_APPEAR, "Gold Skulltula Token", "NA_SE_SY_KINSTA_MARK_APPEAR", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_REDEAD_CRY, "Redead Moan", "NA_SE_EN_REDEAD_CRY", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_VO_TA_SLEEP, "Talon Snore", "NA_SE_VO_TA_SLEEP", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EV_LIGHTNING, "Thunder", "NA_SE_EV_LIGHTNING", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_OC_ABYSS, "Cartoon Fall", "NA_SE_OC_ABYSS", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_FLAME_LAUGH, "Flare Dancer Laugh", "NA_SE_EN_FLAME_LAUGH", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_AWA_BREAK, "Shabom Pop", "NA_SE_EN_AWA_BREAK", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_SHADEST_TAIKO_HIGH, "Bongo Bongo High", "NA_SE_EN_SHADEST_TAIKO_HIGH", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EV_BOTTLE_CAP_OPEN, "Bottle Cork", "NA_SE_EV_BOTTLE_CAP_OPEN", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_IT_BOW_FLICK, "Bow Twang", "NA_SE_IT_BOW_FLICK", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_BUBLE_LAUGH, "Bubble Laugh", "NA_SE_EN_BUBLE_LAUGH", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_VO_LI_BREATH_DRINK_KID, "Child Pant", "NA_SE_VO_LI_BREATH_DRINK_KID", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_DEKU_JR_MOUTH, "Deku Baba", "NA_SE_EN_DEKU_JR_MOUTH", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EV_DOG_CRY_EVENING, "Dusk Howl", "NA_SE_EV_DOG_CRY_EVENING", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_FLAME_DAMAGE, "Flare Dancer Startled", "NA_SE_EN_FLAME_DAMAGE", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_GANON_AT_RETURN, "Ganondorf Teh!", "NA_SE_EN_GANON_AT_RETURN", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_GOMA_JR_CRY, "Gohma Larva Croak", "NA_SE_EN_GOMA_JR_CRY", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_GOLON_WAKE_UP, "Goron Wake", "NA_SE_EN_GOLON_WAKE_UP", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_SY_START_SHOT, "Gunshot", "NA_SE_SY_START_SHOT", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_IT_HAMMER_HIT, "Hammer Bonk", "NA_SE_IT_HAMMER_HIT", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_IRONNACK_SWING_AXE, "Iron Knuckle", "NA_SE_EN_IRONNACK_SWING_AXE", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_FANTOM_ST_LAUGH, "Phantom Ganon Laugh", "NA_SE_EN_FANTOM_ST_LAUGH", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EV_PLANT_BROKEN, "Plant Explode", "NA_SE_EV_PLANT_BROKEN", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_SY_GET_RUPY, "Rupee", "NA_SE_SY_GET_RUPY", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_VO_RT_CRASH, "Ruto Crash", "NA_SE_VO_RT_CRASH", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_VO_RT_LIFT, "Ruto Lift", "NA_SE_VO_RT_LIFT", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_VO_RT_THROW, "Ruto Thrown", "NA_SE_VO_RT_THROW", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_NUTS_UP, "Scrub Emerge", "NA_SE_EN_NUTS_UP", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_AWA_BOUND, "Shabom Bounce", "NA_SE_EN_AWA_BOUND", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_SHELL_MOUTH, "Shellblade", "NA_SE_EN_SHELL_MOUTH", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_STALTU_DAMAGE, "Skulltula Damage", "NA_SE_EN_STALTU_DAMAGE", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EN_NUTS_THROW, "Spit Nut", "NA_SE_EN_NUTS_THROW", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_VO_TA_CRY_0, "Talon Hmm", "NA_SE_VO_TA_CRY_0", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_VO_TA_SURPRISE, "Talon Surprised", "NA_SE_VO_TA_SURPRISE", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_SY_LOCK_ON, "Target Enemy", "NA_SE_SY_LOCK_ON", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_SY_LOCK_ON_HUMAN, "Target Neutral", "NA_SE_SY_LOCK_ON_HUMAN", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_SY_FSEL_CURSOR, "File Select Cursor", "NA_SE_SY_FSEL_CURSOR", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_SY_FSEL_DECIDE_L, "File Select Choose", "NA_SE_SY_FSEL_DECIDE_L", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_SY_FSEL_CLOSE, "File Select Back", "NA_SE_SY_FSEL_CLOSE", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_IT_BOMB_EXPLOSION, "Bomb Explosion", "NA_SE_IT_BOMB_EXPLOSION", SEQ_SFX),
SEQUENCE_MAP_ENTRY(NA_SE_EV_CHICKEN_CRY_A, "Chicken Cry", "NA_SE_EV_CHICKEN_CRY_A", SEQ_SFX),
};
}
void AudioCollection::AddToCollection(char* otrPath, uint16_t seqNum) {
std::string fileName = std::filesystem::path(otrPath).filename().string();
std::vector<std::string> 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 (size_t i = 0; i < typeString.length(); i++) {
typeString[i] = std::tolower(typeString[i], loc);
}
if (typeString == "fanfare") {
type = SEQ_FANFARE;
}
SequenceInfo info = {seqNum,
sequenceName,
StringHelper::Replace(StringHelper::Replace(sequenceName, " ", "_"), "~", "-"),
type};
sequenceMap.emplace(seqNum, info);
}
uint16_t AudioCollection::GetReplacementSequence(uint16_t seqId) {
// if Hyrule Field Morning is about to play, but Hyrule Field is swapped, get the replacement sequence
// for Hyrule Field instead. Otherwise, leave it alone, so that without any sfx editor modifications we will
// play the normal track as usual.
if (seqId == NA_BGM_FIELD_MORNING) {
if (CVarGetInteger("gSfxEditor_NA_BGM_FIELD_LOGIC", NA_BGM_FIELD_LOGIC) != NA_BGM_FIELD_LOGIC) {
seqId = NA_BGM_FIELD_LOGIC;
}
}
if (sequenceMap.find(seqId) == sequenceMap.end()) {
return seqId;
}
const auto& sequenceInfo = sequenceMap.at(seqId);
const std::string cvarKey = "gSfxEditor_" + sequenceInfo.sfxKey;
int replacementSeq = CVarGetInteger(cvarKey.c_str(), seqId);
if (!sequenceMap.contains(replacementSeq)) {
replacementSeq = seqId;
}
return static_cast<uint16_t>(replacementSeq);
}
void AudioCollection::RemoveFromShufflePool(SequenceInfo* seqInfo) {
const std::string cvarKey = "gExcludeSfx_" + seqInfo->sfxKey;
excludedSequences.insert(seqInfo);
includedSequences.erase(seqInfo);
CVarSetInteger(cvarKey.c_str(), 1);
SohImGui::RequestCvarSaveOnNextTick();
}
void AudioCollection::AddToShufflePool(SequenceInfo* seqInfo) {
const std::string cvarKey = "gExcludeSfx_" + seqInfo->sfxKey;
includedSequences.insert(seqInfo);
excludedSequences.erase(seqInfo);
CVarClear(cvarKey.c_str());
SohImGui::RequestCvarSaveOnNextTick();
}
void AudioCollection::InitializeShufflePool() {
if (shufflePoolInitialized) return;
for (auto& [seqId, seqInfo] : sequenceMap) {
const std::string cvarKey = "gExcludeSfx_" + seqInfo.sfxKey;
if (CVarGetInteger(cvarKey.c_str(), 0)) {
excludedSequences.insert(&seqInfo);
} else {
if (seqInfo.category != SEQ_NOSHUFFLE) {
includedSequences.insert(&seqInfo);
}
}
}
shufflePoolInitialized = true;
};
extern "C" void AudioCollection_AddToCollection(char *otrPath, uint16_t seqNum) {
AudioCollection::Instance->AddToCollection(otrPath, seqNum);
}
const char* AudioCollection::GetSequenceName(uint16_t seqId) {
auto seqIt = sequenceMap.find(seqId);
if (seqIt != sequenceMap.end()) {
return seqIt->second.label.c_str();
}
return nullptr;
}
extern "C" const char* AudioCollection_GetSequenceName(uint16_t seqId) {
return AudioCollection::Instance->GetSequenceName(seqId);
}

View File

@ -0,0 +1,65 @@
#ifdef __cplusplus
#include <map>
#include <string>
#include <set>
enum SeqType {
SEQ_NOSHUFFLE = 0,
SEQ_BGM_WORLD = 1 << 0,
SEQ_BGM_EVENT = 1 << 1,
SEQ_BGM_BATTLE = 1 << 2,
SEQ_OCARINA = 1 << 3,
SEQ_FANFARE = 1 << 4,
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,
};
#define INSTRUMENT_OFFSET 0x81
struct SequenceInfo {
uint16_t sequenceId;
std::string label;
std::string sfxKey;
SeqType category;
};
class AudioCollection {
private:
// All Loaded Audio
std::map<uint16_t, SequenceInfo> sequenceMap;
// Sequences/SFX to include in/exclude from shuffle pool
struct compareSequenceLabel {
bool operator() (SequenceInfo* a, SequenceInfo* b) const {
return a->label < b->label;
};
};
std::set<SequenceInfo*, compareSequenceLabel> includedSequences;
std::set<SequenceInfo*, compareSequenceLabel> excludedSequences;
bool shufflePoolInitialized = false;
public:
static AudioCollection* Instance;
AudioCollection();
std::map<uint16_t, SequenceInfo> GetAllSequences() const {
return sequenceMap;
}
std::set<SequenceInfo*, compareSequenceLabel> GetIncludedSequences() const {
return includedSequences;
};
std::set<SequenceInfo*, compareSequenceLabel> GetExcludedSequences() const {
return excludedSequences;
};
void AddToShufflePool(SequenceInfo*);
void RemoveFromShufflePool(SequenceInfo*);
void AddToCollection(char* otrPath, uint16_t seqNum);
uint16_t GetReplacementSequence(uint16_t seqId);
void InitializeShufflePool();
const char* GetSequenceName(uint16_t seqId);
};
#else
void AudioCollection_AddToCollection(char *otrPath, uint16_t seqNum);
const char* AudioCollection_GetSequenceName(uint16_t seqId);
#endif

View File

@ -0,0 +1,562 @@
#include "AudioEditor.h"
#include "sequence.h"
#include <map>
#include <set>
#include <string>
#include <sstream>
#include <libultraship/bridge.h>
#include <ImGuiImpl.h>
#include <functions.h>
#include "../randomizer/3drando/random.hpp"
#include "../../OTRGlobals.h"
#include <Utils/StringHelper.h>
#include "../../UIWidgets.hpp"
#include "AudioCollection.h"
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 71
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;
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;
// 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) {
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 = "gSfxEditor_" + seqData.sfxKey;
if (seqData.category & type) {
// Only save authentic sequence CVars
if (((seqData.category & SEQ_BGM_CUSTOM) || seqData.category == SEQ_FANFARE) && seqData.sequenceId >= MAX_AUTHENTIC_SEQID) {
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 = "gSfxEditor_" + seqData.sfxKey;
CVarSetInteger(cvarKey.c_str(), defaultValue);
}
}
}
void DrawPreviewButton(uint16_t sequenceId, std::string sfxKey, SeqType sequenceType) {
const std::string cvarKey = "gSfxEditor_" + sfxKey;
const std::string hiddenKey = "##" + cvarKey;
const std::string stopButton = " Stop " + hiddenKey;
const std::string previewButton = "Preview" + hiddenKey;
if (CVarGetInteger("gSfxEditor_playing", 0) == sequenceId) {
if (ImGui::Button(stopButton.c_str())) {
func_800F5C2C();
CVarSetInteger("gSfxEditor_playing", 0);
}
} else {
if (ImGui::Button(previewButton.c_str())) {
if (CVarGetInteger("gSfxEditor_playing", 0) != 0) {
func_800F5C2C();
CVarSetInteger("gSfxEditor_playing", 0);
} else {
if (sequenceType == SEQ_SFX) {
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("gSfxEditor_playing", sequenceId);
}
}
}
}
}
void Draw_SfxTab(const std::string& tabId, SeqType type) {
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;
if (ImGui::Button(resetAllButton.c_str())) {
ResetGroup(map, type);
SohImGui::RequestCvarSaveOnNextTick();
if (type == SEQ_BGM_WORLD) {
ReplayCurrentBGM();
}
}
ImGui::SameLine();
if (ImGui::Button(randomizeAllButton.c_str())) {
RandomizeGroup(type);
SohImGui::RequestCvarSaveOnNextTick();
if (type == SEQ_BGM_WORLD) {
ReplayCurrentBGM();
}
}
ImGui::BeginTable(tabId.c_str(), 3, ImGuiTableFlags_SizingFixedFit);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 190.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) {
continue;
}
const std::string cvarKey = "gSfxEditor_" + seqData.sfxKey;
const std::string hiddenKey = "##" + cvarKey;
const std::string resetButton = "Reset" + hiddenKey;
const std::string randomizeButton = "Randomize" + 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;
if (ImGui::BeginCombo(hiddenKey.c_str(), map.at(initialValue).label.c_str())) {
for (const auto& [value, seqData] : map) {
if (~(seqData.category) & type) {
continue;
}
if (ImGui::Selectable(seqData.label.c_str())) {
CVarSetInteger(cvarKey.c_str(), value);
SohImGui::RequestCvarSaveOnNextTick();
UpdateCurrentBGM(defaultValue, type);
}
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
ImGui::PushItemWidth(-FLT_MIN);
DrawPreviewButton((type == SEQ_SFX || type == SEQ_INSTRUMENT) ? defaultValue : currentValue, seqData.sfxKey, type);
ImGui::SameLine();
ImGui::PushItemWidth(-FLT_MIN);
if (ImGui::Button(resetButton.c_str())) {
CVarSetInteger(cvarKey.c_str(), defaultValue);
SohImGui::RequestCvarSaveOnNextTick();
UpdateCurrentBGM(defaultValue, seqData.category);
}
ImGui::SameLine();
ImGui::PushItemWidth(-FLT_MIN);
if (ImGui::Button(randomizeButton.c_str())) {
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);
SohImGui::RequestCvarSaveOnNextTick();
UpdateCurrentBGM(seqData->sequenceId, type);
}
}
}
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_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_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) {
ImGui::BeginDisabled();
ImGui::PushStyleColor(ImGuiCol_Button, GetSequenceTypeColor(type));
ImGui::SmallButton(GetSequenceTypeName(type).c_str());
ImGui::PopStyleColor();
ImGui::EndDisabled();
}
void DrawSfxEditor(bool& open) {
if (!open) {
CVarSetInteger("gSfxEditor", 0);
return;
}
AudioCollection::Instance->InitializeShufflePool();
ImGui::SetNextWindowSize(ImVec2(820, 630), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("Audio Editor", &open)) {
ImGui::End();
return;
}
if (ImGui::BeginTabBar("SfxContextTabBar", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) {
if (ImGui::BeginTabItem("Background Music")) {
Draw_SfxTab("backgroundMusic", SEQ_BGM_WORLD);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Fanfares")) {
Draw_SfxTab("fanfares", SEQ_FANFARE);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Events")) {
Draw_SfxTab("event", SEQ_BGM_EVENT);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Battle Music")) {
Draw_SfxTab("battleMusic", SEQ_BGM_BATTLE);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Ocarina")) {
Draw_SfxTab("instrument", SEQ_INSTRUMENT);
Draw_SfxTab("ocarina", SEQ_OCARINA);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Sound Effects")) {
Draw_SfxTab("sfx", 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::EnhancementCheckbox("Display Sequence Name on Overlay", "gSeqNameOverlay");
UIWidgets::InsertHelpHoverText(
"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)."
);
ImGui::SameLine();
UIWidgets::EnhancementSliderInt("Overlay Duration: %d seconds", "##SeqNameOverlayDuration",
"gSeqNameOverlayDuration", 1, 10, "", 5, true);
ImGui::NewLine();
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();
}
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_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;
sequenceSearch.Draw("Filter (inc,-exc)", 490.0f);
ImGui::SameLine();
if (ImGui::Button("Exclude All")) {
for (auto seqInfo : AudioCollection::Instance->GetIncludedSequences()) {
if (sequenceSearch.PassFilter(seqInfo->label.c_str()) && showType[seqInfo->category]) {
seqsToExclude.insert(seqInfo);
}
}
}
ImGui::SameLine();
if (ImGui::Button("Include All")) {
for (auto seqInfo : AudioCollection::Instance->GetExcludedSequences()) {
if (sequenceSearch.PassFilter(seqInfo->label.c_str()) && showType[seqInfo->category]) {
seqsToInclude.insert(seqInfo);
}
}
}
ImGui::BeginTable("sequenceTypes", 8, 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_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 (ImGui::ArrowButton(seqInfo->sfxKey.c_str(), ImGuiDir_Right)) {
seqsToExclude.insert(seqInfo);
}
ImGui::SameLine();
DrawPreviewButton(seqInfo->sequenceId, seqInfo->sfxKey, seqInfo->category);
ImGui::SameLine();
DrawTypeChip(seqInfo->category);
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 (ImGui::ArrowButton(seqInfo->sfxKey.c_str(), ImGuiDir_Left)) {
seqsToInclude.insert(seqInfo);
}
ImGui::SameLine();
DrawPreviewButton(seqInfo->sequenceId, seqInfo->sfxKey, seqInfo->category);
ImGui::SameLine();
DrawTypeChip(seqInfo->category);
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();
}
ImGui::End();
}
void InitAudioEditor() {
//Draw the bar in the menu.
SohImGui::AddWindow("Enhancements", "SFX Editor", DrawSfxEditor);
}
std::vector<SeqType> allTypes = { SEQ_BGM_WORLD, SEQ_BGM_EVENT, SEQ_BGM_BATTLE, SEQ_OCARINA, SEQ_FANFARE, SEQ_INSTRUMENT, SEQ_SFX };
void AudioEditor_RandomizeAll() {
for (auto type : allTypes) {
RandomizeGroup(type);
}
SohImGui::RequestCvarSaveOnNextTick();
ReplayCurrentBGM();
}
void AudioEditor_ResetAll() {
for (auto type : allTypes) {
ResetGroup(AudioCollection::Instance->GetAllSequences(), type);
}
SohImGui::RequestCvarSaveOnNextTick();
ReplayCurrentBGM();
}

View File

@ -0,0 +1,6 @@
#pragma once
#include "stdint.h"
void InitAudioEditor();
void AudioEditor_RandomizeAll();
void AudioEditor_ResetAll();

View File

@ -10,7 +10,7 @@
#include <soh/Enhancements/item-tables/ItemTableManager.h> #include <soh/Enhancements/item-tables/ItemTableManager.h>
#include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/Enhancements/cosmetics/CosmeticsEditor.h" #include "soh/Enhancements/cosmetics/CosmeticsEditor.h"
#include "soh/Enhancements/sfx-editor/SfxEditor.h" #include "soh/Enhancements/audio/AudioEditor.h"
#define Path _Path #define Path _Path
#define PATH_HACK #define PATH_HACK
@ -1210,9 +1210,9 @@ static bool SfxHandler(std::shared_ptr<Ship::Console> Console, const std::vector
} }
if (args[1].compare("reset") == 0) { if (args[1].compare("reset") == 0) {
SfxEditor_ResetAll(); AudioEditor_ResetAll();
} else if (args[1].compare("randomize") == 0) { } else if (args[1].compare("randomize") == 0) {
SfxEditor_RandomizeAll(); AudioEditor_RandomizeAll();
} else { } else {
SohImGui::GetConsole()->SendErrorMessage("[SOH] Invalid argument passed, must be 'reset' or 'randomize'"); SohImGui::GetConsole()->SendErrorMessage("[SOH] Invalid argument passed, must be 'reset' or 'randomize'");
return CMD_FAILED; return CMD_FAILED;

View File

@ -1,524 +0,0 @@
#include "SfxEditor.h"
#include "sequence.h"
#include <string>
#include <map>
#include <libultraship/bridge.h>
#include <ImGuiImpl.h>
#include <functions.h>
#include "../randomizer/3drando/random.hpp"
#include "../../OTRGlobals.h"
#include <Utils/StringHelper.h>
#include "../../UIWidgets.hpp"
Vec3f pos = { 0.0f, 0.0f, 0.0f };
f32 freqScale = 1.0f;
s8 reverbAdd = 0;
// {originalSequenceId, {label, sfxKey, category},
std::map<u16, std::tuple<std::string, std::string, SeqType>> 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}},
{NA_BGM_ENEMY, {"Battle", "NA_BGM_ENEMY", SEQ_BGM_BATTLE}},
{NA_BGM_BOSS, {"Boss Battle", "NA_BGM_BOSS", SEQ_BGM_BATTLE}},
{NA_BGM_INSIDE_DEKU_TREE, {"Inside the Deku Tree", "NA_BGM_INSIDE_DEKU_TREE", SEQ_BGM_WORLD}},
{NA_BGM_MARKET, {"Market", "NA_BGM_MARKET", SEQ_BGM_WORLD}},
{NA_BGM_TITLE, {"Title Theme", "NA_BGM_TITLE", SEQ_BGM_WORLD}},
{NA_BGM_LINK_HOUSE, {"House", "NA_BGM_LINK_HOUSE", SEQ_BGM_WORLD}},
{NA_BGM_GAME_OVER, {"Game Over", "NA_BGM_GAME_OVER", SEQ_FANFARE}},
{NA_BGM_BOSS_CLEAR, {"Boss Clear", "NA_BGM_BOSS_CLEAR", SEQ_FANFARE}},
{NA_BGM_ITEM_GET, {"Obtain Item", "NA_BGM_ITEM_GET", SEQ_FANFARE}},
{NA_BGM_OPENING_GANON, {"Enter Ganondorf", "NA_BGM_OPENING_GANON", SEQ_FANFARE}},
{NA_BGM_HEART_GET, {"Obtain Heart Container", "NA_BGM_HEART_GET", SEQ_FANFARE}},
{NA_BGM_OCA_LIGHT, {"Prelude of Light", "NA_BGM_OCA_LIGHT", SEQ_OCARINA}},
{NA_BGM_JABU_JABU, {"Inside Jabu-Jabu's Belly", "NA_BGM_JABU_JABU", SEQ_BGM_WORLD}},
{NA_BGM_KAKARIKO_KID, {"Kakariko Village (Child)", "NA_BGM_KAKARIKO_KID", SEQ_BGM_WORLD}},
{NA_BGM_GREAT_FAIRY, {"Great Fairy's Fountain", "NA_BGM_GREAT_FAIRY", SEQ_BGM_EVENT}},
{NA_BGM_ZELDA_THEME, {"Zelda's Theme", "NA_BGM_ZELDA_THEME", SEQ_BGM_EVENT}},
{NA_BGM_FIRE_TEMPLE, {"Fire Temple", "NA_BGM_FIRE_TEMPLE", SEQ_BGM_WORLD}},
{NA_BGM_OPEN_TRE_BOX, {"Open Treasure Chest", "NA_BGM_OPEN_TRE_BOX", SEQ_FANFARE}},
{NA_BGM_FOREST_TEMPLE, {"Forest Temple", "NA_BGM_FOREST_TEMPLE", SEQ_BGM_WORLD}},
{NA_BGM_COURTYARD, {"Hyrule Castle Courtyard", "NA_BGM_COURTYARD", SEQ_BGM_WORLD}},
{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_NOSHUFFLE}},
{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}},
{NA_BGM_OCA_SERENADE, {"Serenade of Water", "NA_BGM_OCA_SERENADE", SEQ_OCARINA}},
{NA_BGM_OCA_REQUIEM, {"Requiem of Spirit", "NA_BGM_OCA_REQUIEM", SEQ_OCARINA}},
{NA_BGM_OCA_NOCTURNE, {"Nocturne of Shadow", "NA_BGM_OCA_NOCTURNE", SEQ_OCARINA}},
{NA_BGM_MINI_BOSS, {"Mini-Boss Battle", "NA_BGM_MINI_BOSS", SEQ_BGM_BATTLE}},
{NA_BGM_SMALL_ITEM_GET, {"Obtain Small Item", "NA_BGM_SMALL_ITEM_GET", SEQ_FANFARE}},
{NA_BGM_TEMPLE_OF_TIME, {"Temple of Time", "NA_BGM_TEMPLE_OF_TIME", SEQ_BGM_WORLD}},
{NA_BGM_EVENT_CLEAR, {"Escape from Lon Lon Ranch", "NA_BGM_EVENT_CLEAR", SEQ_FANFARE}},
{NA_BGM_KOKIRI, {"Kokiri Forest", "NA_BGM_KOKIRI", SEQ_BGM_WORLD}},
{NA_BGM_OCA_FAIRY_GET, {"Obtain Fairy Ocarina", "NA_BGM_OCA_FAIRY_GET", SEQ_FANFARE}},
{NA_BGM_SARIA_THEME, {"Lost Woods", "NA_BGM_SARIA_THEME", SEQ_BGM_WORLD}},
{NA_BGM_SPIRIT_TEMPLE, {"Spirit Temple", "NA_BGM_SPIRIT_TEMPLE", SEQ_BGM_WORLD}},
{NA_BGM_HORSE, {"Horse Race", "NA_BGM_HORSE", SEQ_BGM_EVENT}},
{NA_BGM_HORSE_GOAL, {"Horse Race Goal", "NA_BGM_HORSE_GOAL", SEQ_FANFARE}},
{NA_BGM_INGO, {"Ingo's Theme", "NA_BGM_INGO", SEQ_BGM_WORLD}},
{NA_BGM_MEDALLION_GET, {"Obtain Medallion", "NA_BGM_MEDALLION_GET", SEQ_FANFARE}},
{NA_BGM_OCA_SARIA, {"Ocarina Saria's Song", "NA_BGM_OCA_SARIA", SEQ_OCARINA}},
{NA_BGM_OCA_EPONA, {"Ocarina Epona's Song", "NA_BGM_OCA_EPONA", SEQ_OCARINA}},
{NA_BGM_OCA_ZELDA, {"Ocarina Zelda's Lullaby", "NA_BGM_OCA_ZELDA", SEQ_OCARINA}},
{NA_BGM_OCA_SUNS, {"Ocarina Sun's Song", "NA_BGM_OCA_SUNS", SEQ_OCARINA}},
{NA_BGM_OCA_TIME, {"Ocarina Song of Time", "NA_BGM_OCA_TIME", SEQ_OCARINA}},
{NA_BGM_OCA_STORM, {"Ocarina Song of Storms", "NA_BGM_OCA_STORM", SEQ_OCARINA}},
{NA_BGM_NAVI_OPENING, {"Fairy Flying", "NA_BGM_NAVI_OPENING", SEQ_BGM_EVENT}},
{NA_BGM_DEKU_TREE_CS, {"Deku Tree", "NA_BGM_DEKU_TREE_CS", SEQ_BGM_EVENT}},
{NA_BGM_WINDMILL, {"Windmill Hut", "NA_BGM_WINDMILL", SEQ_BGM_WORLD}},
{NA_BGM_HYRULE_CS, {"Legend of Hyrule", "NA_BGM_HYRULE_CS", SEQ_BGM_EVENT}},
{NA_BGM_MINI_GAME, {"Shooting Gallery", "NA_BGM_MINI_GAME", SEQ_BGM_EVENT}},
{NA_BGM_SHEIK, {"Sheik's Theme", "NA_BGM_SHEIK", SEQ_BGM_EVENT}},
{NA_BGM_ZORA_DOMAIN, {"Zora's Domain", "NA_BGM_ZORA_DOMAIN", SEQ_BGM_WORLD}},
{NA_BGM_APPEAR, {"Enter Zelda", "NA_BGM_APPEAR", SEQ_FANFARE}},
{NA_BGM_ADULT_LINK, {"Goodbye to Zelda", "NA_BGM_ADULT_LINK", SEQ_BGM_EVENT}},
{NA_BGM_MASTER_SWORD, {"Master Sword", "NA_BGM_MASTER_SWORD", SEQ_FANFARE}},
{NA_BGM_INTRO_GANON, {"Ganon Intro", "NA_BGM_INTRO_GANON", SEQ_BGM_EVENT}},
{NA_BGM_SHOP, {"Shop", "NA_BGM_SHOP", SEQ_BGM_WORLD}},
{NA_BGM_CHAMBER_OF_SAGES, {"Chamber of the Sages", "NA_BGM_CHAMBER_OF_SAGES", SEQ_BGM_EVENT}},
{NA_BGM_FILE_SELECT, {"File Select", "NA_BGM_FILE_SELECT", SEQ_BGM_WORLD}},
{NA_BGM_ICE_CAVERN, {"Ice Cavern", "NA_BGM_ICE_CAVERN", SEQ_BGM_WORLD}},
{NA_BGM_DOOR_OF_TIME, {"Open Door of Temple of Time", "NA_BGM_DOOR_OF_TIME", SEQ_BGM_EVENT}},
{NA_BGM_OWL, {"Kaepora Gaebora's Theme", "NA_BGM_OWL", SEQ_BGM_EVENT}},
{NA_BGM_SHADOW_TEMPLE, {"Shadow Temple", "NA_BGM_SHADOW_TEMPLE", SEQ_BGM_WORLD}},
{NA_BGM_WATER_TEMPLE, {"Water Temple", "NA_BGM_WATER_TEMPLE", SEQ_BGM_WORLD}},
{NA_BGM_BRIDGE_TO_GANONS, {"Ganon's Castle Bridge", "NA_BGM_BRIDGE_TO_GANONS", SEQ_BGM_EVENT}},
{NA_BGM_OCARINA_OF_TIME, {"Ocarina of Time", "NA_BGM_OCARINA_OF_TIME", SEQ_FANFARE}},
{NA_BGM_GERUDO_VALLEY, {"Gerudo Valley", "NA_BGM_GERUDO_VALLEY", SEQ_BGM_WORLD}},
{NA_BGM_POTION_SHOP, {"Potion Shop", "NA_BGM_POTION_SHOP", SEQ_BGM_WORLD}},
{NA_BGM_KOTAKE_KOUME, {"Kotake & Koume's Theme", "NA_BGM_KOTAKE_KOUME", SEQ_BGM_EVENT}},
{NA_BGM_ESCAPE, {"Escape from Ganon's Castle", "NA_BGM_ESCAPE", SEQ_BGM_EVENT}},
{NA_BGM_UNDERGROUND, {"Ganon's Castle Under Ground", "NA_BGM_UNDERGROUND", SEQ_BGM_WORLD}},
{NA_BGM_GANONDORF_BOSS, {"Ganondorf Battle", "NA_BGM_GANONDORF_BOSS", SEQ_BGM_BATTLE}},
{NA_BGM_GANON_BOSS, {"Ganon Battle", "NA_BGM_GANON_BOSS", SEQ_BGM_BATTLE}},
{NA_BGM_END_DEMO, {"Seal of Six Sages", "NA_BGM_END_DEMO", SEQ_NOSHUFFLE}},
{NA_BGM_STAFF_1, {"End Credits I", "NA_BGM_STAFF_1", SEQ_NOSHUFFLE}},
{NA_BGM_STAFF_2, {"End Credits II", "NA_BGM_STAFF_2", SEQ_NOSHUFFLE}},
{NA_BGM_STAFF_3, {"End Credits III", "NA_BGM_STAFF_3", SEQ_NOSHUFFLE}},
{NA_BGM_STAFF_4, {"End Credits IV", "NA_BGM_STAFF_4", SEQ_NOSHUFFLE}},
{NA_BGM_FIRE_BOSS, {"King Dodongo & Volvagia Boss Battle", "NA_BGM_FIRE_BOSS", SEQ_BGM_BATTLE}},
{NA_BGM_TIMED_MINI_GAME, {"Mini-Game", "NA_BGM_TIMED_MINI_GAME", SEQ_BGM_EVENT}},
{INSTRUMENT_OFFSET + 1, {"Ocarina", "OCARINA_INSTRUMENT_DEFAULT", SEQ_INSTRUMENT}},
{INSTRUMENT_OFFSET + 2, {"Malon", "OCARINA_INSTRUMENT_MALON", SEQ_INSTRUMENT}},
{INSTRUMENT_OFFSET + 3, {"Whistle", "OCARINA_INSTRUMENT_WHISTLE", SEQ_INSTRUMENT}},
{INSTRUMENT_OFFSET + 4, {"Harp", "OCARINA_INSTRUMENT_HARP", SEQ_INSTRUMENT}},
{INSTRUMENT_OFFSET + 5, {"Organ", "OCARINA_INSTRUMENT_GRIND_ORGAN", SEQ_INSTRUMENT}},
{INSTRUMENT_OFFSET + 6, {"Flute", "OCARINA_INSTRUMENT_FLUTE", SEQ_INSTRUMENT}},
{NA_SE_EV_SMALL_DOG_BARK, {"Bark", "NA_SE_EV_SMALL_DOG_BARK", SEQ_SFX}},
{NA_SE_EN_AWA_BOUND, {"Bomb Bounce", "NA_SE_EN_AWA_BOUND", SEQ_SFX}},
{NA_SE_EN_SHADEST_TAIKO_LOW, {"Bongo Bongo Low", "NA_SE_EN_SHADEST_TAIKO_LOW", SEQ_SFX}},
{NA_SE_EN_NUTS_FAINT, {"Business Scrub", "NA_SE_EN_NUTS_FAINT", SEQ_SFX}},
{NA_SE_SY_CARROT_RECOVER, {"Carrot Refill", "NA_SE_SY_CARROT_RECOVER", SEQ_SFX}},
{NA_SE_EV_CHICKEN_CRY_N, {"Cluck", "NA_SE_EV_CHICKEN_CRY_N", SEQ_SFX}},
{NA_SE_EV_BRIDGE_OPEN_STOP, {"Drawbridge Set", "NA_SE_EV_BRIDGE_OPEN_STOP", SEQ_SFX}},
{NA_SE_EN_KAICHO_CRY, {"Guay", "NA_SE_EN_KAICHO_CRY", SEQ_SFX}},
{NA_SE_SY_HITPOINT_ALARM, {"Low HP Beep", "NA_SE_SY_HITPOINT_ALARM", SEQ_SFX}},
{NA_SE_SY_HP_RECOVER, {"HP Recover", "NA_SE_SY_HP_RECOVER", SEQ_SFX}},
{NA_SE_EV_HORSE_RUN, {"Horse Trot", "NA_SE_EV_HORSE_RUN", SEQ_SFX}},
{NA_SE_PL_WALK_HEAVYBOOTS, {"Iron Boots", "NA_SE_PL_WALK_HEAVYBOOTS", SEQ_SFX}},
{NA_SE_EV_COW_CRY, {"Moo", "NA_SE_EV_COW_CRY", SEQ_SFX}},
{NA_SE_VO_KZ_MOVE, {"Mweep!", "NA_SE_VO_KZ_MOVE", SEQ_SFX}},
{NA_SE_VO_NA_HELLO_2, {"Navi Hey!", "NA_SE_VO_NA_HELLO_2", SEQ_SFX}},
{NA_SE_VO_SK_LAUGH, {"Navi Listen!", "NA_SE_VO_SK_LAUGH", SEQ_SFX}},
{NA_SE_EV_POT_BROKEN, {"Pot Shattering", "NA_SE_EV_POT_BROKEN", SEQ_SFX}},
{NA_SE_EV_FROG_CRY_0, {"Ribbit", "NA_SE_EV_FROG_CRY_0", SEQ_SFX}},
{NA_SE_EV_FIVE_COUNT_LUPY, {"Rupee (Silver)", "NA_SE_EV_FIVE_COUNT_LUPY", SEQ_SFX}},
{NA_SE_EV_FOOT_SWITCH, {"Switch", "NA_SE_EV_FOOT_SWITCH", SEQ_SFX}},
{NA_SE_IT_WALL_HIT_SOFT, {"Sword Bonk", "NA_SE_IT_WALL_HIT_SOFT", SEQ_SFX}},
{NA_SE_SY_METRONOME, {"Tambourine", "NA_SE_SY_METRONOME", SEQ_SFX}},
{NA_SE_VO_Z1_SURPRISE, {"Zelda Gasp (Adult)", "NA_SE_VO_Z1_SURPRISE", SEQ_SFX}},
{NA_SE_EN_AMOS_VOICE, {"Armos", "NA_SE_EN_AMOS_VOICE", SEQ_SFX}},
{NA_SE_VO_LI_FALL_L_KID, {"Child Scream", "NA_SE_VO_LI_FALL_L_KID", SEQ_SFX}},
{NA_SE_VO_FR_LAUGH_0, {"Great Fairy", "NA_SE_VO_FR_LAUGH_0", SEQ_SFX}},
{NA_SE_EN_REDEAD_AIM, {"Redead Scream", "NA_SE_EN_REDEAD_AIM", SEQ_SFX}},
{NA_SE_VO_RT_LAUGH_0, {"Ruto Giggle", "NA_SE_VO_RT_LAUGH_0", SEQ_SFX}},
{NA_SE_EN_STALKID_ATTACK, {"Stalchild Attack", "NA_SE_EN_STALKID_ATTACK", SEQ_SFX}},
{NA_SE_EV_CHICKEN_CRY_M, {"Cockadoodiedoo", "NA_SE_EV_CHICKEN_CRY_M", SEQ_SFX}},
{NA_SE_SY_KINSTA_MARK_APPEAR, {"Gold Skulltula Token", "NA_SE_SY_KINSTA_MARK_APPEAR", SEQ_SFX}},
{NA_SE_EN_REDEAD_CRY, {"Redead Moan", "NA_SE_EN_REDEAD_CRY", SEQ_SFX}},
{NA_SE_VO_TA_SLEEP, {"Talon Snore", "NA_SE_VO_TA_SLEEP", SEQ_SFX}},
{NA_SE_EV_LIGHTNING, {"Thunder", "NA_SE_EV_LIGHTNING", SEQ_SFX}},
{NA_SE_OC_ABYSS, {"Cartoon Fall", "NA_SE_OC_ABYSS", SEQ_SFX}},
{NA_SE_EN_FLAME_LAUGH, {"Flare Dancer Laugh", "NA_SE_EN_FLAME_LAUGH", SEQ_SFX}},
{NA_SE_EN_AWA_BREAK, {"Shabom Pop", "NA_SE_EN_AWA_BREAK", SEQ_SFX}},
{NA_SE_EN_SHADEST_TAIKO_HIGH, {"Bongo Bongo High", "NA_SE_EN_SHADEST_TAIKO_HIGH", SEQ_SFX}},
{NA_SE_EV_BOTTLE_CAP_OPEN, {"Bottle Cork", "NA_SE_EV_BOTTLE_CAP_OPEN", SEQ_SFX}},
{NA_SE_IT_BOW_FLICK, {"Bow Twang", "NA_SE_IT_BOW_FLICK", SEQ_SFX}},
{NA_SE_EN_BUBLE_LAUGH, {"Bubble Laugh", "NA_SE_EN_BUBLE_LAUGH", SEQ_SFX}},
{NA_SE_VO_LI_BREATH_DRINK_KID, {"Child Pant", "NA_SE_VO_LI_BREATH_DRINK_KID", SEQ_SFX}},
{NA_SE_EN_DEKU_JR_MOUTH, {"Deku Baba", "NA_SE_EN_DEKU_JR_MOUTH", SEQ_SFX}},
{NA_SE_EV_DOG_CRY_EVENING, {"Dusk Howl", "NA_SE_EV_DOG_CRY_EVENING", SEQ_SFX}},
{NA_SE_EN_FLAME_DAMAGE, {"Flare Dancer Startled", "NA_SE_EN_FLAME_DAMAGE", SEQ_SFX}},
{NA_SE_EN_GANON_AT_RETURN, {"Ganondorf Teh!", "NA_SE_EN_GANON_AT_RETURN", SEQ_SFX}},
{NA_SE_EN_GOMA_JR_CRY, {"Gohma Larva Croak", "NA_SE_EN_GOMA_JR_CRY", SEQ_SFX}},
{NA_SE_EN_GOLON_WAKE_UP, {"Goron Wake", "NA_SE_EN_GOLON_WAKE_UP", SEQ_SFX}},
{NA_SE_SY_START_SHOT, {"Gunshot", "NA_SE_SY_START_SHOT", SEQ_SFX}},
{NA_SE_IT_HAMMER_HIT, {"Hammer Bonk", "NA_SE_IT_HAMMER_HIT", SEQ_SFX}},
{NA_SE_EN_IRONNACK_SWING_AXE, {"Iron Knuckle", "NA_SE_EN_IRONNACK_SWING_AXE", SEQ_SFX}},
{NA_SE_EN_FANTOM_ST_LAUGH, {"Phantom Ganon Laugh", "NA_SE_EN_FANTOM_ST_LAUGH", SEQ_SFX}},
{NA_SE_EV_PLANT_BROKEN, {"Plant Explode", "NA_SE_EV_PLANT_BROKEN", SEQ_SFX}},
{NA_SE_SY_GET_RUPY, {"Rupee", "NA_SE_SY_GET_RUPY", SEQ_SFX}},
{NA_SE_VO_RT_CRASH, {"Ruto Crash", "NA_SE_VO_RT_CRASH", SEQ_SFX}},
{NA_SE_VO_RT_LIFT, {"Ruto Lift", "NA_SE_VO_RT_LIFT", SEQ_SFX}},
{NA_SE_VO_RT_THROW, {"Ruto Thrown", "NA_SE_VO_RT_THROW", SEQ_SFX}},
{NA_SE_EN_NUTS_UP, {"Scrub Emerge", "NA_SE_EN_NUTS_UP", SEQ_SFX}},
{NA_SE_EN_AWA_BOUND, {"Shabom Bounce", "NA_SE_EN_AWA_BOUND", SEQ_SFX}},
{NA_SE_EN_SHELL_MOUTH, {"Shellblade", "NA_SE_EN_SHELL_MOUTH", SEQ_SFX}},
{NA_SE_EN_STALTU_DAMAGE, {"Skulltula Damage", "NA_SE_EN_STALTU_DAMAGE", SEQ_SFX}},
{NA_SE_EN_NUTS_THROW, {"Spit Nut", "NA_SE_EN_NUTS_THROW", SEQ_SFX}},
{NA_SE_VO_TA_CRY_0, {"Talon Hmm", "NA_SE_VO_TA_CRY_0", SEQ_SFX}},
{NA_SE_VO_TA_SURPRISE, {"Talon Surprised", "NA_SE_VO_TA_SURPRISE", SEQ_SFX}},
{NA_SE_SY_LOCK_ON, {"Target Enemy", "NA_SE_SY_LOCK_ON", SEQ_SFX}},
{NA_SE_SY_LOCK_ON_HUMAN, {"Target Neutral", "NA_SE_SY_LOCK_ON_HUMAN", SEQ_SFX}},
{NA_SE_SY_FSEL_CURSOR, {"File Select Cursor", "NA_SE_SY_FSEL_CURSOR", SEQ_SFX}},
{NA_SE_SY_FSEL_DECIDE_L, {"File Select Choose", "NA_SE_SY_FSEL_DECIDE_L", SEQ_SFX}},
{NA_SE_SY_FSEL_CLOSE, {"File Select Back", "NA_SE_SY_FSEL_CLOSE", SEQ_SFX}},
{NA_SE_IT_BOMB_EXPLOSION, {"Bomb Explosion", "NA_SE_IT_BOMB_EXPLOSION", SEQ_SFX}},
{NA_SE_EV_CHICKEN_CRY_A, {"Chicken Cry", "NA_SE_EV_CHICKEN_CRY_A", SEQ_SFX}},
};
// 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(const std::map<u16, std::tuple<std::string, std::string, SeqType>>& map, SeqType type) {
std::vector<u16> values;
for (const auto& [value, seqData] : map) {
if (std::get<2>(seqData) & type) {
values.push_back(value);
}
}
Shuffle(values);
for (const auto& [defaultValue, seqData] : map) {
const auto& [name, sfxKey, seqType] = seqData;
const std::string cvarKey = "gSfxEditor_" + sfxKey;
if (seqType & type) {
// Only save authentic sequence CVars
if (((seqType & SEQ_BGM_CUSTOM) || seqType == SEQ_FANFARE) && defaultValue >= MAX_AUTHENTIC_SEQID) {
continue;
}
const int randomValue = values.back();
CVarSetInteger(cvarKey.c_str(), randomValue);
values.pop_back();
}
}
}
void ResetGroup(const std::map<u16, std::tuple<std::string, std::string, SeqType>>& map, SeqType type) {
for (const auto& [defaultValue, seqData] : map) {
const auto& [name, sfxKey, seqType] = seqData;
if (seqType == type) {
// Only save authentic sequence CVars
if (seqType == SEQ_FANFARE && defaultValue >= MAX_AUTHENTIC_SEQID) {
continue;
}
const std::string cvarKey = "gSfxEditor_" + sfxKey;
CVarSetInteger(cvarKey.c_str(), defaultValue);
}
}
}
void Draw_SfxTab(const std::string& tabId, const std::map<u16, std::tuple<std::string, std::string, SeqType>>& map, SeqType type) {
const std::string hiddenTabId = "##" + tabId;
const std::string resetAllButton = "Reset All" + hiddenTabId;
const std::string randomizeAllButton = "Randomize All" + hiddenTabId;
if (ImGui::Button(resetAllButton.c_str())) {
ResetGroup(map, type);
SohImGui::RequestCvarSaveOnNextTick();
if (type == SEQ_BGM_WORLD) {
ReplayCurrentBGM();
}
}
ImGui::SameLine();
if (ImGui::Button(randomizeAllButton.c_str())) {
RandomizeGroup(map, type);
SohImGui::RequestCvarSaveOnNextTick();
if (type == SEQ_BGM_WORLD) {
ReplayCurrentBGM();
}
}
ImGui::BeginTable(tabId.c_str(), 3, ImGuiTableFlags_SizingFixedFit);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 190.0f);
for (const auto& [defaultValue, seqData] : map) {
const auto& [name, sfxKey, seqType] = seqData;
if (~(seqType) & type) {
continue;
}
// Do not display custom sequences in the list
if (((seqType & SEQ_BGM_CUSTOM) || seqType == SEQ_FANFARE) && defaultValue >= MAX_AUTHENTIC_SEQID) {
continue;
}
const std::string cvarKey = "gSfxEditor_" + sfxKey;
const std::string hiddenKey = "##" + cvarKey;
const std::string stopButton = " Stop " + hiddenKey;
const std::string previewButton = "Preview" + hiddenKey;
const std::string resetButton = "Reset" + hiddenKey;
const std::string randomizeButton = "Randomize" + hiddenKey;
const int currentValue = CVarGetInteger(cvarKey.c_str(), defaultValue);
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text(name.c_str());
ImGui::TableNextColumn();
ImGui::PushItemWidth(-FLT_MIN);
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) {
continue;
}
if (ImGui::Selectable(std::get<0>(seqData).c_str())) {
CVarSetInteger(cvarKey.c_str(), value);
SohImGui::RequestCvarSaveOnNextTick();
UpdateCurrentBGM(defaultValue, type);
}
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
ImGui::PushItemWidth(-FLT_MIN);
if (CVarGetInteger("gSfxEditor_playing", 0) == currentValue) {
if (ImGui::Button(stopButton.c_str())) {
func_800F5C2C();
CVarSetInteger("gSfxEditor_playing", 0);
}
} else {
if (ImGui::Button(previewButton.c_str())) {
if (CVarGetInteger("gSfxEditor_playing", 0) != 0) {
func_800F5C2C();
CVarSetInteger("gSfxEditor_playing", 0);
} else {
if (type == SEQ_SFX) {
Audio_PlaySoundGeneral(defaultValue, &pos, 4, &freqScale, &freqScale, &reverbAdd);
} else if (type == SEQ_INSTRUMENT) {
Audio_OcaSetInstrument(defaultValue - INSTRUMENT_OFFSET);
Audio_OcaSetSongPlayback(9, 1);
} else {
// TODO: Cant do both here, so have to click preview button twice
func_800F5ACC(defaultValue);
CVarSetInteger("gSfxEditor_playing", currentValue);
}
}
}
}
ImGui::SameLine();
ImGui::PushItemWidth(-FLT_MIN);
if (ImGui::Button(resetButton.c_str())) {
CVarSetInteger(cvarKey.c_str(), defaultValue);
SohImGui::RequestCvarSaveOnNextTick();
UpdateCurrentBGM(defaultValue, seqType);
}
ImGui::SameLine();
ImGui::PushItemWidth(-FLT_MIN);
if (ImGui::Button(randomizeButton.c_str())) {
auto it = map.begin();
while (true) {
const auto& [value, seqData] = *std::next(it, rand() % map.size());
const auto& [name, sfxKey, seqType] = seqData;
if (seqType & type) {
CVarSetInteger(cvarKey.c_str(), value);
SohImGui::RequestCvarSaveOnNextTick();
UpdateCurrentBGM(defaultValue, type);
break;
}
}
}
}
ImGui::EndTable();
}
extern "C" u16 SfxEditor_GetReplacementSeq(u16 seqId) {
// if Hyrule Field Morning is about to play, but Hyrule Field is swapped, get the replacement sequence
// for Hyrule Field instead. Otherwise, leave it alone, so that without any sfx editor modifications we will
// play the normal track as usual.
if (seqId == NA_BGM_FIELD_MORNING) {
if (CVarGetInteger("gSfxEditor_NA_BGM_FIELD_LOGIC", NA_BGM_FIELD_LOGIC) != NA_BGM_FIELD_LOGIC) {
seqId = NA_BGM_FIELD_LOGIC;
}
}
if (sfxEditorSequenceMap.find(seqId) == sfxEditorSequenceMap.end()) {
return seqId;
}
const auto& [name, sfxKey, seqType] = sfxEditorSequenceMap.at(seqId);
const std::string cvarKey = "gSfxEditor_" + sfxKey;
int replacementSeq = CVarGetInteger(cvarKey.c_str(), seqId);
if (!sfxEditorSequenceMap.contains(replacementSeq)) {
replacementSeq = seqId;
}
return static_cast<u16>(replacementSeq);
}
extern "C" u16 SfxEditor_GetReverseReplacementSeq(u16 seqId) {
for (const auto& [id, nameAndsfxKey] : sfxEditorSequenceMap) {
const auto& [name, sfxKey, seqType] = sfxEditorSequenceMap.at(id);
const std::string cvarKey = "gSfxEditor_" + sfxKey;
if (CVarGetInteger(cvarKey.c_str(), id) == seqId){
return static_cast<u16>(id);
}
}
return static_cast<u16>(seqId);
}
extern "C" const char* SfxEditor_GetSequenceName(u16 seqId) {
if (sfxEditorSequenceMap.contains(seqId)) {
const char *name = std::get<0>(sfxEditorSequenceMap.at(seqId)).c_str();
return name;
}
return NULL;
}
void DrawSfxEditor(bool& open) {
if (!open) {
CVarSetInteger("gSfxEditor", 0);
return;
}
ImGui::SetNextWindowSize(ImVec2(900, 630), ImGuiCond_FirstUseEver);
if (!ImGui::Begin("SFX Editor", &open)) {
ImGui::End();
return;
}
if (ImGui::BeginTabBar("SfxContextTabBar", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) {
if (ImGui::BeginTabItem("Background Music")) {
Draw_SfxTab("backgroundMusic", sfxEditorSequenceMap, SEQ_BGM_WORLD);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Fanfares")) {
Draw_SfxTab("fanfares", sfxEditorSequenceMap, SEQ_FANFARE);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Events")) {
Draw_SfxTab("event", sfxEditorSequenceMap, SEQ_BGM_EVENT);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Battle Music")) {
Draw_SfxTab("battleMusic", sfxEditorSequenceMap, SEQ_BGM_BATTLE);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Ocarina")) {
Draw_SfxTab("instrument", sfxEditorSequenceMap, SEQ_INSTRUMENT);
Draw_SfxTab("ocarina", sfxEditorSequenceMap, SEQ_OCARINA);
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Sound Effects")) {
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::EnhancementCheckbox("Display Sequence Name on Overlay", "gSeqNameOverlay");
UIWidgets::InsertHelpHoverText(
"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)."
);
ImGui::SameLine();
UIWidgets::EnhancementSliderInt("Overlay Duration: %d seconds", "##SeqNameOverlayDuration",
"gSeqNameOverlayDuration", 1, 10, "", 5, true);
ImGui::NewLine();
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();
}
ImGui::End();
}
void InitSfxEditor() {
//Draw the bar in the menu.
SohImGui::AddWindow("Enhancements", "SFX Editor", DrawSfxEditor);
}
std::vector<SeqType> allTypes = { SEQ_BGM_WORLD, SEQ_BGM_EVENT, SEQ_BGM_BATTLE, SEQ_OCARINA, SEQ_FANFARE, SEQ_INSTRUMENT, SEQ_SFX };
void SfxEditor_RandomizeAll() {
for (auto type : allTypes) {
RandomizeGroup(sfxEditorSequenceMap, type);
}
SohImGui::RequestCvarSaveOnNextTick();
ReplayCurrentBGM();
}
void SfxEditor_ResetAll() {
for (auto type : allTypes) {
ResetGroup(sfxEditorSequenceMap, type);
}
SohImGui::RequestCvarSaveOnNextTick();
ReplayCurrentBGM();
}
extern "C" void SfxEditor_AddSequence(char *otrPath, uint16_t seqNum) {
std::vector<std::string> splitName = StringHelper::Split(std::string(otrPath), "/");
std::string fileName = splitName[splitName.size() - 1];
std::vector<std::string> 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);
}

View File

@ -1,26 +0,0 @@
#pragma once
#include "stdint.h"
void InitSfxEditor();
void SfxEditor_RandomizeAll();
void SfxEditor_ResetAll();
#ifndef __cplusplus
const char* SfxEditor_GetSequenceName(u16 seqId);
void SfxEditor_AddSequence(char *otrPath, uint16_t seqNum);
#endif
#define INSTRUMENT_OFFSET 0x81
enum SeqType {
SEQ_NOSHUFFLE = 0,
SEQ_BGM_WORLD = 1 << 0,
SEQ_BGM_EVENT = 1 << 1,
SEQ_BGM_BATTLE = 1 << 2,
SEQ_OCARINA = 1 << 3,
SEQ_FANFARE = 1 << 4,
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,
};

View File

@ -835,7 +835,7 @@ namespace GameMenuBar {
SohImGui::RequestCvarSaveOnNextTick(); SohImGui::RequestCvarSaveOnNextTick();
SohImGui::EnableWindow("Cosmetics Editor", CVarGetInteger("gCosmeticsEditorEnabled", 0)); SohImGui::EnableWindow("Cosmetics Editor", CVarGetInteger("gCosmeticsEditorEnabled", 0));
} }
if (ImGui::Button(GetWindowButtonText("SFX Editor", CVarGetInteger("gSfxEditor", 0)).c_str(), ImVec2(-1.0f, 0.0f))) if (ImGui::Button(GetWindowButtonText("Audio Editor", CVarGetInteger("gSfxEditor", 0)).c_str(), ImVec2(-1.0f, 0.0f)))
{ {
bool currentValue = CVarGetInteger("gSfxEditor", 0); bool currentValue = CVarGetInteger("gSfxEditor", 0);
CVarSetInteger("gSfxEditor", !currentValue); CVarSetInteger("gSfxEditor", !currentValue);

View File

@ -29,7 +29,8 @@
#include <AudioPlayer.h> #include <AudioPlayer.h>
#include "Enhancements/controls/GameControlEditor.h" #include "Enhancements/controls/GameControlEditor.h"
#include "Enhancements/cosmetics/CosmeticsEditor.h" #include "Enhancements/cosmetics/CosmeticsEditor.h"
#include "Enhancements/sfx-editor/SfxEditor.h" #include "Enhancements/audio/AudioCollection.h"
#include "Enhancements/audio/AudioEditor.h"
#include "Enhancements/debugconsole.h" #include "Enhancements/debugconsole.h"
#include "Enhancements/debugger/debugger.h" #include "Enhancements/debugger/debugger.h"
#include "Enhancements/randomizer/randomizer.h" #include "Enhancements/randomizer/randomizer.h"
@ -109,6 +110,7 @@ SaveManager* SaveManager::Instance;
CustomMessageManager* CustomMessageManager::Instance; CustomMessageManager* CustomMessageManager::Instance;
ItemTableManager* ItemTableManager::Instance; ItemTableManager* ItemTableManager::Instance;
GameInteractor* GameInteractor::Instance; GameInteractor* GameInteractor::Instance;
AudioCollection* AudioCollection::Instance;
extern "C" char** cameraStrings; extern "C" char** cameraStrings;
std::vector<std::shared_ptr<std::string>> cameraStdStrings; std::vector<std::shared_ptr<std::string>> cameraStdStrings;
@ -568,13 +570,14 @@ extern "C" void InitOTR() {
CustomMessageManager::Instance = new CustomMessageManager(); CustomMessageManager::Instance = new CustomMessageManager();
ItemTableManager::Instance = new ItemTableManager(); ItemTableManager::Instance = new ItemTableManager();
GameInteractor::Instance = new GameInteractor(); GameInteractor::Instance = new GameInteractor();
AudioCollection::Instance = new AudioCollection();
clearMtx = (uintptr_t)&gMtxClear; clearMtx = (uintptr_t)&gMtxClear;
OTRMessage_Init(); OTRMessage_Init();
OTRAudio_Init(); OTRAudio_Init();
InitCosmeticsEditor(); InitCosmeticsEditor();
GameControlEditor::Init(); GameControlEditor::Init();
InitSfxEditor(); InitAudioEditor();
DebugConsole_Init(); DebugConsole_Init();
Debug_Init(); Debug_Init();
Rando_Init(); Rando_Init();

View File

@ -5,7 +5,7 @@
#include <libultraship/libultra.h> #include <libultraship/libultra.h>
#include "global.h" #include "global.h"
#include "soh/OTRGlobals.h" #include "soh/OTRGlobals.h"
#include "soh/Enhancements/sfx-editor/SfxEditor.h" #include "soh/Enhancements/audio/AudioCollection.h"
#define MK_ASYNC_MSG(retData, tableType, id, status) (((retData) << 24) | ((tableType) << 16) | ((id) << 8) | (status)) #define MK_ASYNC_MSG(retData, tableType, id, status) (((retData) << 24) | ((tableType) << 16) | ((id) << 8) | (status))
#define ASYNC_TBLTYPE(v) ((u8)(v >> 16)) #define ASYNC_TBLTYPE(v) ((u8)(v >> 16))
@ -487,7 +487,7 @@ u8* AudioLoad_GetFontsForSequence(s32 seqId, u32* outNumFonts) {
if (seqId == NA_BGM_DISABLED) if (seqId == NA_BGM_DISABLED)
return NULL; return NULL;
u16 newSeqId = SfxEditor_GetReplacementSeq(seqId); u16 newSeqId = AudioEditor_GetReplacementSeq(seqId);
if (newSeqId > MAX_SEQUENCES || !sequenceMap[newSeqId]) { if (newSeqId > MAX_SEQUENCES || !sequenceMap[newSeqId]) {
return NULL; return NULL;
} }
@ -613,7 +613,7 @@ s32 AudioLoad_SyncInitSeqPlayerInternal(s32 playerIdx, s32 seqId, s32 arg2) {
AudioSeq_SkipForwardSequence(seqPlayer); AudioSeq_SkipForwardSequence(seqPlayer);
//! @bug missing return (but the return value is not used so it's not UB) //! @bug missing return (but the return value is not used so it's not UB)
if (CVarGetInteger("gSeqNameOverlay", 0) && playerIdx == SEQ_PLAYER_BGM_MAIN) { if (CVarGetInteger("gSeqNameOverlay", 0) && playerIdx == SEQ_PLAYER_BGM_MAIN) {
const char* sequenceName = SfxEditor_GetSequenceName(seqId); const char* sequenceName = AudioCollection_GetSequenceName(seqId);
if (sequenceName != NULL) { if (sequenceName != NULL) {
Overlay_DisplayText_Seconds(CVarGetInteger("gSeqNameOverlayDuration", 5), sequenceName); Overlay_DisplayText_Seconds(CVarGetInteger("gSeqNameOverlayDuration", 5), sequenceName);
} }
@ -1341,7 +1341,7 @@ void AudioLoad_Init(void* heap, size_t heapSize) {
for (size_t i = startingSeqNum; i < startingSeqNum + customSeqListSize; i++) { for (size_t i = startingSeqNum; i < startingSeqNum + customSeqListSize; i++) {
int j = i - startingSeqNum; int j = i - startingSeqNum;
SfxEditor_AddSequence(customSeqList[j], i); AudioCollection_AddToCollection(customSeqList[j], i);
SequenceData sDat = ResourceMgr_LoadSeqByName(customSeqList[j]); SequenceData sDat = ResourceMgr_LoadSeqByName(customSeqList[j]);
sDat.seqNumber = i; sDat.seqNumber = i;
@ -1545,7 +1545,7 @@ s32 AudioLoad_SlowLoadSeq(s32 seqId, u8* ramAddr, s8* isDone) {
size_t size; size_t size;
seqId = AudioLoad_GetRealTableIndex(SEQUENCE_TABLE, seqId); seqId = AudioLoad_GetRealTableIndex(SEQUENCE_TABLE, seqId);
u16 newSeqId = SfxEditor_GetReplacementSeq(seqId); u16 newSeqId = AudioEditor_GetReplacementSeq(seqId);
if (seqId != newSeqId) { if (seqId != newSeqId) {
gAudioContext.seqToPlay[SEQ_PLAYER_BGM_MAIN] = newSeqId; gAudioContext.seqToPlay[SEQ_PLAYER_BGM_MAIN] = newSeqId;
gAudioContext.seqReplaced[SEQ_PLAYER_BGM_MAIN] = 1; gAudioContext.seqReplaced[SEQ_PLAYER_BGM_MAIN] = 1;

View File

@ -1067,7 +1067,7 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) {
seqPlayer->seqId = gAudioContext.seqToPlay[seqPlayer->playerIdx]; seqPlayer->seqId = gAudioContext.seqToPlay[seqPlayer->playerIdx];
gAudioContext.seqReplaced[seqPlayer->playerIdx] = 0; gAudioContext.seqReplaced[seqPlayer->playerIdx] = 0;
} }
u16 seqId = SfxEditor_GetReplacementSeq(seqPlayer->seqId); u16 seqId = AudioEditor_GetReplacementSeq(seqPlayer->seqId);
SequenceData sDat = ResourceMgr_LoadSeqByName(sequenceMap[seqId]); SequenceData sDat = ResourceMgr_LoadSeqByName(sequenceMap[seqId]);
command = sDat.fonts[sDat.numFonts - result - 1]; command = sDat.fonts[sDat.numFonts - result - 1];
} }
@ -1184,7 +1184,7 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) {
seqPlayer->seqId = gAudioContext.seqToPlay[seqPlayer->playerIdx]; seqPlayer->seqId = gAudioContext.seqToPlay[seqPlayer->playerIdx];
gAudioContext.seqReplaced[seqPlayer->playerIdx] = 0; gAudioContext.seqReplaced[seqPlayer->playerIdx] = 0;
} }
u16 seqId = SfxEditor_GetReplacementSeq(seqPlayer->seqId); u16 seqId = AudioEditor_GetReplacementSeq(seqPlayer->seqId);
SequenceData sDat = ResourceMgr_LoadSeqByName(sequenceMap[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 // The game apparantely would sometimes do negative array lookups, the result of which would get rejected by AudioHeap_SearchCaches, never

View File

@ -1700,7 +1700,7 @@ void Audio_OcaSetInstrument(u8 arg0) {
} }
u16 sfxEditorId = arg0 + 0x81; u16 sfxEditorId = arg0 + 0x81;
u16 newArg0 = SfxEditor_GetReplacementSeq(sfxEditorId); u16 newArg0 = AudioEditor_GetReplacementSeq(sfxEditorId);
if (newArg0 != sfxEditorId) { if (newArg0 != sfxEditorId) {
gAudioContext.seqReplaced[SEQ_PLAYER_SFX] = 1; gAudioContext.seqReplaced[SEQ_PLAYER_SFX] = 1;
arg0 = newArg0 - 0x81; arg0 = newArg0 - 0x81;
@ -4601,6 +4601,22 @@ void func_800F5ACC(u16 seqId) {
} }
} }
// based on func_800F5ACC
void PreviewSequence(u16 seqId) {
u16 curSeqId = func_800FA0B4(SEQ_PLAYER_BGM_MAIN);
if ((curSeqId & 0xFF) != NA_BGM_GANON_TOWER && (curSeqId & 0xFF) != NA_BGM_ESCAPE && curSeqId != seqId) {
Audio_SetSequenceMode(SEQ_MODE_IGNORE);
if (curSeqId != NA_BGM_DISABLED) {
sPrevMainBgmSeqId = curSeqId;
} else {
osSyncPrintf("Middle Boss BGM Start not stack \n");
}
Audio_QueuePreviewSeqCmd(seqId);
}
}
/** /**
* Restores the previous sequence to the main bgm player before func_800F5ACC was called * Restores the previous sequence to the main bgm player before func_800F5ACC was called
*/ */

View File

@ -221,7 +221,7 @@ void Audio_ProcessSoundRequest(void)
if (req->sfxId == 0) { if (req->sfxId == 0) {
return; return;
} }
u16 newSfxId = SfxEditor_GetReplacementSeq(req->sfxId); u16 newSfxId = AudioEditor_GetReplacementSeq(req->sfxId);
if (req->sfxId != newSfxId) { if (req->sfxId != newSfxId) {
gAudioContext.seqReplaced[SEQ_PLAYER_SFX] = 1; gAudioContext.seqReplaced[SEQ_PLAYER_SFX] = 1;
req->sfxId = newSfxId; req->sfxId = newSfxId;

View File

@ -373,7 +373,7 @@ void Audio_QueueSeqCmd(u32 cmd)
if (op == 0 || op == 2 || op == 12) { if (op == 0 || op == 2 || op == 12) {
u8 seqId = cmd & 0xFF; u8 seqId = cmd & 0xFF;
u8 playerIdx = GET_PLAYER_IDX(cmd); u8 playerIdx = GET_PLAYER_IDX(cmd);
u16 newSeqId = SfxEditor_GetReplacementSeq(seqId); u16 newSeqId = AudioEditor_GetReplacementSeq(seqId);
gAudioContext.seqReplaced[playerIdx] = (seqId != newSeqId); gAudioContext.seqReplaced[playerIdx] = (seqId != newSeqId);
gAudioContext.seqToPlay[playerIdx] = newSeqId; gAudioContext.seqToPlay[playerIdx] = newSeqId;
cmd |= (seqId & 0xFF); cmd |= (seqId & 0xFF);
@ -382,6 +382,12 @@ void Audio_QueueSeqCmd(u32 cmd)
sAudioSeqCmds[sSeqCmdWrPos++] = cmd; sAudioSeqCmds[sSeqCmdWrPos++] = cmd;
} }
void Audio_QueuePreviewSeqCmd(u16 seqId) {
gAudioContext.seqReplaced[0] = 1;
gAudioContext.seqToPlay[0] = seqId;
sAudioSeqCmds[sSeqCmdWrPos++] = 1;
}
void Audio_ProcessSeqCmds(void) { void Audio_ProcessSeqCmds(void) {
while (sSeqCmdWrPos != sSeqCmdRdPos) { while (sSeqCmdWrPos != sSeqCmdRdPos) {
Audio_ProcessSeqCmd(sAudioSeqCmds[sSeqCmdRdPos++]); Audio_ProcessSeqCmd(sAudioSeqCmds[sSeqCmdRdPos++]);