diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt index 6a0138f0..30306c22 100644 --- a/config/SOUE01/symbols.txt +++ b/config/SOUE01/symbols.txt @@ -20477,7 +20477,7 @@ fn_803706E0 = .text:0x803706E0; // type:function size:0x24 fn_80370710 = .text:0x80370710; // type:function size:0x64 beginBgmBattleRoom__12dSndBgmMgr_cFv = .text:0x80370780; // type:function size:0x54 endBgmBattleRoom__12dSndBgmMgr_cFv = .text:0x803707E0; // type:function size:0x60 -fn_80370840 = .text:0x80370840; // type:function size:0x78 +isPlayingAnyBattleMusic__12dSndBgmMgr_cFv = .text:0x80370840; // type:function size:0x78 fn_803708C0 = .text:0x803708C0; // type:function size:0x34 fn_80370900 = .text:0x80370900; // type:function size:0xA0 fn_803709A0 = .text:0x803709A0; // type:function size:0xA8 @@ -20834,7 +20834,7 @@ remove__38SndMgrDisposer<20dSndSmallEffectMgr_c>Fv = .text:0x8037D8D0; // type:f __ct__20dSndSmallEffectMgr_cFv = .text:0x8037D8E0; // type:function size:0x9C __ct__Q34nw4r3snd11SoundHandleFv = .text:0x8037D980; // type:function size:0xC fn_8037D990 = .text:0x8037D990; // type:function size:0x4 -fn_8037D9A0 = .text:0x8037D9A0; // type:function size:0xA0 +calc__20dSndSmallEffectMgr_cFv = .text:0x8037D9A0; // type:function size:0xA0 fn_8037DA40 = .text:0x8037DA40; // type:function size:0x18 fn_8037DA60 = .text:0x8037DA60; // type:function size:0x70 fn_8037DAD0 = .text:0x8037DAD0; // type:function size:0xDC @@ -20846,9 +20846,9 @@ playSound__20dSndSmallEffectMgr_cFUl = .text:0x8037DED0; // type:function size:0 playSoundWithPan__20dSndSmallEffectMgr_cFUlf = .text:0x8037E1A0; // type:function size:0x90 fn_8037E230 = .text:0x8037E230; // type:function size:0xEC playSoundInternal__20dSndSmallEffectMgr_cFUl = .text:0x8037E320; // type:function size:0x78 -fn_8037E3A0 = .text:0x8037E3A0; // type:function size:0x158 +getHoldSoundHandle__20dSndSmallEffectMgr_cFUl = .text:0x8037E3A0; // type:function size:0x158 fn_8037E500 = .text:0x8037E500; // type:function size:0x58 -playSoundWithPitch__20dSndSmallEffectMgr_cFUlf = .text:0x8037E560; // type:function size:0xBC +holdSoundWithPitch__20dSndSmallEffectMgr_cFUlf = .text:0x8037E560; // type:function size:0xBC fn_8037E620 = .text:0x8037E620; // type:function size:0xDC fn_8037E700 = .text:0x8037E700; // type:function size:0x8 fn_8037E710 = .text:0x8037E710; // type:function size:0x8 @@ -20865,7 +20865,7 @@ fn_8037EAD0 = .text:0x8037EAD0; // type:function size:0x40 fn_8037EB10 = .text:0x8037EB10; // type:function size:0x11C fn_8037EC30 = .text:0x8037EC30; // type:function size:0x8 fn_8037EC40 = .text:0x8037EC40; // type:function size:0x3C -fn_8037EC80 = .text:0x8037EC80; // type:function size:0xB0 +playSkbSound__20dSndSmallEffectMgr_cFUl = .text:0x8037EC80; // type:function size:0xB0 fn_8037ED30 = .text:0x8037ED30; // type:function size:0x68 stopSounds__20dSndSmallEffectMgr_cFUlUll = .text:0x8037EDA0; // type:function size:0xA0 __cl__12SoundStopperFRQ34nw4r3snd11SoundHandle = .text:0x8037EE40; // type:function size:0x3C @@ -20875,7 +20875,7 @@ isPlayingSound__20dSndSmallEffectMgr_cFUl = .text:0x8037EFA0; // type:function s playButtonPressSoundWhenAdvancingTextBoxes__20dSndSmallEffectMgr_cFf = .text:0x8037F020; // type:function size:0xB4 resetButtonPressSound__20dSndSmallEffectMgr_cFv = .text:0x8037F0E0; // type:function size:0xC setButtonPressSound__20dSndSmallEffectMgr_cFP14dSoundSource_c = .text:0x8037F0F0; // type:function size:0x60 -fn_8037F150 = .text:0x8037F150; // type:function size:0x6D0 +playBattleHitSound__20dSndSmallEffectMgr_cFQ220dSndSmallEffectMgr_c16BattleHitSound_eP14dSoundSource_c = .text:0x8037F150; // type:function size:0x6D0 getName__14dSoundSource_cCFv = .text:0x8037F820; // type:function size:0x8 getSourceType__14dSoundSource_cCFv = .text:0x8037F830; // type:function size:0x8 fn_8037F840 = .text:0x8037F840; // type:function size:0x74 diff --git a/include/d/snd/d_snd_bgm_mgr.h b/include/d/snd/d_snd_bgm_mgr.h index 6ce2882d..ce6d1619 100644 --- a/include/d/snd/d_snd_bgm_mgr.h +++ b/include/d/snd/d_snd_bgm_mgr.h @@ -64,6 +64,8 @@ public: /** Runs when the fight is over, no matter how you started it */ bool endBgmBattleRoom(); + bool isPlayingAnyBattleMusic(); + private: bool stopBgmSound(dSndBgmSound_c *sound, s32 fadeFrames); void checkForPrepareStoppedBgmSound(u32 stoppedSoundId); diff --git a/include/d/snd/d_snd_small_effect_mgr.h b/include/d/snd/d_snd_small_effect_mgr.h index 6504728a..a03c2b0e 100644 --- a/include/d/snd/d_snd_small_effect_mgr.h +++ b/include/d/snd/d_snd_small_effect_mgr.h @@ -15,18 +15,37 @@ SND_DISPOSER_FORWARD_DECL(dSndSmallEffectMgr_c); class dSndSmallEffectMgr_c { SND_DISPOSER_MEMBERS(dSndSmallEffectMgr_c) + static const s32 NUM_DELAYED_SOUNDS = 2; + static const s32 NUM_HOLD_SOUNDS = 3; + public: + enum BattleHitSound_e { + BATTLE_TUTTI_NORMAL = 0, + BATTLE_TUTTI_TURN = 1, + BATTLE_TUTTI_JUMP = 2, + BATTLE_TUTTI_FINISH = 3, + BATTLE_TUTTI_GUARDJUST = 5, + }; + dSndSmallEffectMgr_c(); + void calc(); + bool playSound(u32 soundId); + // used for clawshots cursor, pan depends on where on the screen + // your cursor is when it activates bool playSoundWithPan(u32 soundId, f32 pan); - void playSoundWithPitch(u32 soundId, f32 pitch); + void holdSoundWithPitch(u32 soundId, f32 pitch); + bool playSkbSound(u32 soundId); bool playButtonPressSoundWhenAdvancingTextBoxes(f32); void resetButtonPressSound(); void setButtonPressSound(dSoundSource_c *source); + void playSound(u32 soundId, nw4r::snd::SoundHandle *handle); + bool playBattleHitSound(BattleHitSound_e type, dSoundSource_c *source); + private: bool playSoundInternal(u32 soundId); void stopSounds(u32 playerIdx, u32 soundId, s32 fadeFrames); @@ -34,20 +53,23 @@ private: bool isPlayingSound(u32 playerIdx, u32 soundId); bool isPlayingSound(u32 soundId); - /* 0x10 */ nw4r::snd::SoundHandle mHandle1; + /** + * Finds a sound handle currently playing the given sound, + * or an idle sound handle, + * or stops a lower-priority sound if needed and possible. + */ + nw4r::snd::SoundHandle *getHoldSoundHandle(u32 soundId); + + /* 0x10 */ s32 field_0x10; // used for most sounds /* 0x14 */ nw4r::snd::SoundHandle mNormalSound; /* 0x18 */ nw4r::snd::SoundHandle mHandle3; - // apparently used for shield gauge sounds, but maybe not given - // that the callers appear unreachable - /* 0x1C */ nw4r::snd::SoundHandle mShieldGaugeHandles[3]; + /* 0x1C */ nw4r::snd::SoundHandle mHoldSoundHandles[NUM_HOLD_SOUNDS]; - /* 0x28 */ s32 field_0x28; - /* 0x2C */ s32 field_0x2C; - /* 0x30 */ s32 field_0x30; - /* 0x34 */ s32 field_0x34; + /* 0x28 */ u32 mDelayedSoundIds[NUM_DELAYED_SOUNDS]; + /* 0x30 */ s32 mDelayedSoundTimers[NUM_DELAYED_SOUNDS]; /* 0x38 */ u32 mTextboxAdvanceSound; - /* 0x3C */ nw4r::snd::SoundHandle mHandle4; + /* 0x3C */ nw4r::snd::SoundHandle mBattleTuttiHandle; /* 0x40 */ u16 field_0x40; /* 0x42 */ u16 field_0x42; /* 0x44 */ s32 field_0x44; diff --git a/include/d/snd/d_snd_source.h b/include/d/snd/d_snd_source.h index 9c5db0c1..a2851e76 100644 --- a/include/d/snd/d_snd_source.h +++ b/include/d/snd/d_snd_source.h @@ -10,8 +10,6 @@ #include "nw4r/snd/snd_SoundStartable.h" #include "nw4r/ut/ut_list.h" -#include - /** Manages sound relating to a particular actor. */ /** Size: probably 0x15C */ class dSoundSource_c : public dSoundSourceIf_c, public dSnd3DActor_c { @@ -28,10 +26,9 @@ public: // This is where it gets a bit wild and this class starts mixing in overrides between // new virtual functions, which causes the vtable to list these functions in exactly this // order. - virtual const char *getName() const; // 0x17C - bool isName(const char *name) const { - return !std::strcmp(getName(), name); - } + virtual const char *getName() const { + return mpName; + } // 0x17C virtual void d_s_vt_0x180(); virtual void d_s_vt_0x184(); virtual void d_s_vt_0x188(); diff --git a/include/d/snd/d_snd_source_enums.h b/include/d/snd/d_snd_source_enums.h index d1f29cc8..16acb895 100644 --- a/include/d/snd/d_snd_source_enums.h +++ b/include/d/snd/d_snd_source_enums.h @@ -33,6 +33,7 @@ enum SoundSourceType_e { SND_SOURCE_TIME_STONE = 37, SND_SOURCE_CLEF = 38, SND_SOURCE_SHUTTER = 39, + SND_SOURCE_OBJECT_40 = 40, SND_SOURCE_OBJECT_42 = 42, // 43-52: Npc (4) diff --git a/include/d/snd/d_snd_util.h b/include/d/snd/d_snd_util.h index 5040fa50..cca09ce3 100644 --- a/include/d/snd/d_snd_util.h +++ b/include/d/snd/d_snd_util.h @@ -4,6 +4,12 @@ #include "common.h" #include "egg/core/eggDisposer.h" +#include + +inline bool streq(const char *left, const char *right) { + return !std::strcmp(left, right); +} + // This setup is only inferred. d/snd uses it all over the place. // This works for dSndPlayerMgr_c, which has a vtable of its own but the Disposer at offset 0. // It also works for the factory at 0x80399c20, which calls a base class ctor, diff --git a/include/nw4r/snd/snd_SeqSoundHandle.h b/include/nw4r/snd/snd_SeqSoundHandle.h index 374a966c..426eb909 100644 --- a/include/nw4r/snd/snd_SeqSoundHandle.h +++ b/include/nw4r/snd/snd_SeqSoundHandle.h @@ -36,6 +36,12 @@ namespace nw4r { namespace snd void DetachSound(); + u32 GetTick() const { + if (IsAttachedSound()) + return mSound->GetTick(); + return 0; + } + void WriteVariable(int varNo, s16 value) { if (IsAttachedSound()) mSound->WriteVariable(varNo, value); diff --git a/src/d/lyt/meter/d_lyt_meter_shield_gauge.cpp b/src/d/lyt/meter/d_lyt_meter_shield_gauge.cpp index 7618b0d8..0b2dcdac 100644 --- a/src/d/lyt/meter/d_lyt_meter_shield_gauge.cpp +++ b/src/d/lyt/meter/d_lyt_meter_shield_gauge.cpp @@ -181,13 +181,13 @@ bool dLytMeterShieldGauge_c::execute() { if (mCurrentDurability >= mMaxDurability) { if (field_0x31E) { // Unreachable? - dSndSmallEffectMgr_c::GetInstance()->playSoundWithPitch(SE_S_GAUGE_SHIELD_UP_LV, 1.0f); + dSndSmallEffectMgr_c::GetInstance()->holdSoundWithPitch(SE_S_GAUGE_SHIELD_UP_LV, 1.0f); } dSndSmallEffectMgr_c::GetInstance()->playSound(SE_S_GAUGE_SHIELD_UP_MAX); } else { if (field_0x31E) { // Unreachable? - dSndSmallEffectMgr_c::GetInstance()->playSoundWithPitch( + dSndSmallEffectMgr_c::GetInstance()->holdSoundWithPitch( SE_S_GAUGE_SHIELD_UP_LV, mCurrentDurability / mMaxDurability ); } diff --git a/src/d/snd/d_snd_small_effect_mgr.cpp b/src/d/snd/d_snd_small_effect_mgr.cpp index ba048d90..6a4004ec 100644 --- a/src/d/snd/d_snd_small_effect_mgr.cpp +++ b/src/d/snd/d_snd_small_effect_mgr.cpp @@ -1,11 +1,13 @@ #include "d/snd/d_snd_small_effect_mgr.h" #include "common.h" +#include "d/snd/d_snd_bgm_mgr.h" #include "d/snd/d_snd_checkers.h" #include "d/snd/d_snd_control_player_mgr.h" +#include "d/snd/d_snd_mgr.h" #include "d/snd/d_snd_player_mgr.h" #include "d/snd/d_snd_source.h" -#include "d/snd/d_snd_source_if.h" +#include "d/snd/d_snd_source_enums.h" #include "d/snd/d_snd_util.h" #include "d/snd/d_snd_wzsound.h" #include "nw4r/snd/snd_SeqSoundHandle.h" @@ -14,12 +16,26 @@ SND_DISPOSER_DEFINE(dSndSmallEffectMgr_c) -dSndSmallEffectMgr_c::dSndSmallEffectMgr_c() : mTextboxAdvanceSound(-1), field_0x40(0), field_0x42(0), field_0x44(0) { - // probably arrays - field_0x28 = -1; - field_0x30 = 0; - field_0x2C = -1; - field_0x34 = 0; +dSndSmallEffectMgr_c::dSndSmallEffectMgr_c() + : field_0x10(0), mTextboxAdvanceSound(-1), field_0x40(0), field_0x42(0), field_0x44(0) { + for (int i = 0; i < NUM_DELAYED_SOUNDS; i++) { + mDelayedSoundIds[i] = -1; + mDelayedSoundTimers[i] = 0; + } +} + +void dSndSmallEffectMgr_c::calc() { + if (!dSndPlayerMgr_c::GetInstance()->checkFlag(0x4)) { + for (int i = 0; i < NUM_DELAYED_SOUNDS; i++) { + if (mDelayedSoundIds[i] != -1) { + mDelayedSoundTimers[i]--; + if (mDelayedSoundTimers[i] <= 0) { + playSound(mDelayedSoundIds[i], nullptr); + mDelayedSoundIds[i] = -1; + } + } + } + } } bool dSndSmallEffectMgr_c::playSound(u32 soundId) { @@ -132,6 +148,57 @@ bool dSndSmallEffectMgr_c::playSoundWithPan(u32 soundId, f32 pan) { return ok; } +nw4r::snd::SoundHandle *dSndSmallEffectMgr_c::getHoldSoundHandle(u32 soundId) { + // Find an existing handle holding this sound + for (int i = 0; i < NUM_HOLD_SOUNDS; i++) { + nw4r::snd::SoundHandle *h = &mHoldSoundHandles[i]; + if (h->GetId() == soundId) { + return h; + } + } + + // Find a free handle + for (int i = 0; i < NUM_HOLD_SOUNDS; i++) { + nw4r::snd::SoundHandle *h = &mHoldSoundHandles[i]; + if (!h->IsAttachedSound()) { + return h; + } + } + + // Drop a lower-priority sound + nw4r::snd::SoundHandle *least = nullptr; + nw4r::snd::SoundArchive::SoundInfo info; + dSndMgr_c::GetInstance()->getArchive()->ReadSoundInfo(soundId, &info); + s32 newPriority = info.playerPriority; + + for (int i = 0; i < NUM_HOLD_SOUNDS; i++) { + nw4r::snd::SoundHandle *h = &mHoldSoundHandles[i]; + dSndMgr_c::GetInstance()->getArchive()->ReadSoundInfo(h->GetId(), &info); + if (info.playerPriority < newPriority) { + newPriority = info.playerPriority; + least = h; + } + } + return least; +} + +bool dSndSmallEffectMgr_c::playSkbSound(u32 soundId) { + switch (soundId) { + case SE_S_SK_POINT: + case SE_S_SK_INPUT: + if (isPlayingSound(SE_S_SK_INPUT_DECIDE)) { + return false; + } + break; + case SE_S_SK_INPUT_DECIDE: + stopSounds(dSndPlayerMgr_c::PLAYER_SMALL_NORMAL, SE_S_SK_POINT, 0); + stopSounds(dSndPlayerMgr_c::PLAYER_SMALL_NORMAL, SE_S_SK_INPUT, 0); + break; + case SE_S_SK_DELETE_ERROR: stopSounds(dSndPlayerMgr_c::PLAYER_SMALL_NORMAL, SE_S_SK_INPUT, 0); break; + } + return playSound(soundId); +} + void dSndSmallEffectMgr_c::stopSounds(u32 playerIdx, u32 soundId, s32 fadeFrames) { SoundStopper stopper(soundId, fadeFrames); dSndControlPlayerMgr_c::GetInstance()->getPlayer1(playerIdx)->ForEachSound(stopper, false); @@ -191,3 +258,62 @@ void dSndSmallEffectMgr_c::setButtonPressSound(dSoundSource_c *source) { } } } + +bool dSndSmallEffectMgr_c::playBattleHitSound(BattleHitSound_e type, dSoundSource_c *source) { + // if we're not in battle, don't play any of the hit effects + if (!dSndBgmMgr_c::GetInstance()->isPlayingAnyBattleMusic()) { + return false; + } + + if (source != nullptr) { + const char *name = source->getName(); + s32 sourceType = source->getSourceType(); + if (sourceType == SND_SOURCE_OBJECT_40) { + return false; + } + + switch (type) { + case BATTLE_TUTTI_GUARDJUST: + if (sourceType >= SND_SOURCE_BULLET) { + // Do not play battle effects for countering bullets + return false; + } + break; + case BATTLE_TUTTI_FINISH: + if (streq(name, "BLasBos")) { + // Do not play finish effect for finishing Demise + return false; + } + break; + default: break; + } + } + u32 soundId = BGM_BATTLE_TUTTI; + switch (type) { + case BATTLE_TUTTI_TURN: soundId = BGM_BATTLE_TUTTI_TURN; break; + case BATTLE_TUTTI_JUMP: soundId = BGM_BATTLE_TUTTI_JUMP; break; + case BATTLE_TUTTI_FINISH: soundId = BGM_BATTLE_TUTTI_FINISH; break; + case BATTLE_TUTTI_GUARDJUST: soundId = BGM_BATTLE_TUTTI_GUARDJUST; break; + default: break; + } + + if (mBattleTuttiHandle.IsAttachedSound()) { + u32 alreadyPlayingSound = mBattleTuttiHandle.GetId(); + // BGM_BATTLE_TUTTI_ sounds are ordered by priority apparently + if (alreadyPlayingSound > soundId) { + return false; + } + if (alreadyPlayingSound == BGM_BATTLE_TUTTI) { + nw4r::snd::SeqSoundHandle handle(&mBattleTuttiHandle); + // Do not allow stopping BGM_BATTLE_TUTTI too early + if ((s32)handle.GetTick() < 12) { + return false; + } + } + mBattleTuttiHandle.Stop(5); + } + + // TODO ... + + return true; +} diff --git a/src/d/snd/d_snd_source_mgr.cpp b/src/d/snd/d_snd_source_mgr.cpp index 7e175559..9b132e52 100644 --- a/src/d/snd/d_snd_source_mgr.cpp +++ b/src/d/snd/d_snd_source_mgr.cpp @@ -3,6 +3,7 @@ #include "common.h" #include "d/snd/d_snd_source.h" #include "d/snd/d_snd_source_enums.h" +#include "d/snd/d_snd_util.h" #include "nw4r/ut/ut_list.h" #include @@ -110,7 +111,7 @@ void dSndSourceMgr_c::registerSource(dSoundSource_c *source) { break; } case SND_SOURCE_CATEGORY_OBJECT: { - if (source->isName("TBoat") && mpTBoatSource == nullptr) { + if (streq(source->getName(), "TBoat") && mpTBoatSource == nullptr) { mpTBoatSource = source; } break; diff --git a/src/d/snd/d_snd_state_mgr.cpp b/src/d/snd/d_snd_state_mgr.cpp index e62ad85b..298ad65e 100644 --- a/src/d/snd/d_snd_state_mgr.cpp +++ b/src/d/snd/d_snd_state_mgr.cpp @@ -84,10 +84,6 @@ void dSndStateMgr_c::setup(EGG::Heap *pHeap) { resetStageName(); } -inline bool streq(const char *stageName, const char *name) { - return !std::strcmp(stageName, name); -} - void dSndStateMgr_c::onStageOrLayerUpdate() { if (dSndPlayerMgr_c::GetInstance()->checkFlag(dSndPlayerMgr_c::MGR_UNK_0x80)) { dSndPlayerMgr_c::GetInstance()->stopAllSound();