From 69cdf95057b3b5b10adcbde208d472e4e660c075 Mon Sep 17 00:00:00 2001 From: robojumper Date: Sun, 29 Jun 2025 22:48:44 +0200 Subject: [PATCH] d_snd_bgm_harp_data OK --- config/SOUE01/splits.txt | 7 +- config/SOUE01/symbols.txt | 34 +++--- configure.py | 3 +- include/d/snd/d_snd_bgm_harp_data.h | 132 +++++++++++++++++++++ include/nw4r/snd/snd_SeqSoundHandle.h | 4 + src/d/snd/d_snd_bgm_harp_data.cpp | 165 ++++++++++++++++++++++++++ 6 files changed, 325 insertions(+), 20 deletions(-) create mode 100644 include/d/snd/d_snd_bgm_harp_data.h create mode 100644 src/d/snd/d_snd_bgm_harp_data.cpp diff --git a/config/SOUE01/splits.txt b/config/SOUE01/splits.txt index 1399580f..24cca8d9 100644 --- a/config/SOUE01/splits.txt +++ b/config/SOUE01/splits.txt @@ -2698,11 +2698,14 @@ d/snd/d_snd_bgm_sound.cpp: .text start:0x80379D20 end:0x8037BA68 align:16 .sbss start:0x80575D88 end:0x80575D90 -d/snd/d_snd_bgm_data_mgr.cpp: - .text start:0x8037BA70 end:0x8037C518 align:16 +d/snd/d_snd_bgm_battle_data_mgr.cpp: + .text start:0x8037BA70 end:0x8037BEA8 align:16 .data start:0x80549148 end:0x80549158 .sdata start:0x805742F0 end:0x805742F8 +d/snd/d_snd_bgm_harp_data.cpp: + .text start:0x8037BEB0 end:0x8037C518 align:16 + d/snd/d_snd_bgm_mml_parser_base.cpp: .text start:0x8037C520 end:0x8037D014 align:16 .data start:0x80549158 end:0x80549168 diff --git a/config/SOUE01/symbols.txt b/config/SOUE01/symbols.txt index b20c48a9..2c03b0b7 100644 --- a/config/SOUE01/symbols.txt +++ b/config/SOUE01/symbols.txt @@ -20781,23 +20781,23 @@ fn_8037BD80 = .text:0x8037BD80; // type:function size:0x4 fn_8037BD90 = .text:0x8037BD90; // type:function size:0x4C fn_8037BDE0 = .text:0x8037BDE0; // type:function size:0x5C fn_8037BE40 = .text:0x8037BE40; // type:function size:0x68 -fn_8037BEB0 = .text:0x8037BEB0; // type:function size:0x7C -fn_8037BF30 = .text:0x8037BF30; // type:function size:0x10 -fn_8037BF40 = .text:0x8037BF40; // type:function size:0x64 -fn_8037BFB0 = .text:0x8037BFB0; // type:function size:0x48 -fn_8037C000 = .text:0x8037C000; // type:function size:0x30 -fn_8037C030 = .text:0x8037C030; // type:function size:0x14 -fn_8037C050 = .text:0x8037C050; // type:function size:0x70 -fn_8037C0C0 = .text:0x8037C0C0; // type:function size:0x64 -fn_8037C130 = .text:0x8037C130; // type:function size:0x124 -fn_8037C260 = .text:0x8037C260; // type:function size:0x14 -fn_8037C280 = .text:0x8037C280; // type:function size:0x34 -fn_8037C2C0 = .text:0x8037C2C0; // type:function size:0x34 -fn_8037C300 = .text:0x8037C300; // type:function size:0x68 -fn_8037C370 = .text:0x8037C370; // type:function size:0x34 -fn_8037C3B0 = .text:0x8037C3B0; // type:function size:0x58 -fn_8037C410 = .text:0x8037C410; // type:function size:0x60 -setHarpPitchSeqVars = .text:0x8037C470; // type:function size:0xA8 +__ct__27dSndBgmDataHarpVarSetBase_cFl = .text:0x8037BEB0; // type:function size:0x7C +__ct__20dSndBgmDataHarpVar_cFv = .text:0x8037BF30; // type:function size:0x10 scope:weak +__dt__27dSndBgmDataHarpVarSetBase_cFv = .text:0x8037BF40; // type:function size:0x64 +resetVars__27dSndBgmDataHarpVarSetBase_cFv = .text:0x8037BFB0; // type:function size:0x48 +addVar__27dSndBgmDataHarpVarSetBase_cFUc = .text:0x8037C000; // type:function size:0x30 +__ct__21dSndBgmHarpDataBase_cFv = .text:0x8037C030; // type:function size:0x14 +resetVars__21dSndBgmHarpDataBase_cFv = .text:0x8037C050; // type:function size:0x70 +getIdxForPosition__21dSndBgmHarpDataBase_cFl = .text:0x8037C0C0; // type:function size:0x64 +addVar__21dSndBgmHarpDataBase_cFlll = .text:0x8037C130; // type:function size:0x124 +setField_0x08__21dSndBgmHarpDataBase_cFl = .text:0x8037C260; // type:function size:0x14 +getUsableVarSet__21dSndBgmHarpDataBase_cFl = .text:0x8037C280; // type:function size:0x34 +getVarSet__21dSndBgmHarpDataBase_cFl = .text:0x8037C2C0; // type:function size:0x34 +__ct__17dSndBgmHarpData_cFv = .text:0x8037C300; // type:function size:0x68 +__ct__23dSndBgmDataHarpVarSet_cFv = .text:0x8037C370; // type:function size:0x34 scope:weak +__dt__23dSndBgmDataHarpVarSet_cFv = .text:0x8037C3B0; // type:function size:0x58 scope:weak +__dt__17dSndBgmHarpData_cFv = .text:0x8037C410; // type:function size:0x60 +writeSeqVars__17dSndBgmHarpData_cFl = .text:0x8037C470; // type:function size:0xA8 __ct__18dSndBgmMmlParser_cFPQ34nw4r3snd18SoundArchivePlayerPQ34nw4r3snd12SoundArchive = .text:0x8037C520; // type:function size:0x20 initTrack__18dSndBgmMmlParser_cFlUl = .text:0x8037C540; // type:function size:0x34 loadAndParse__18dSndBgmMmlParser_cFUlUlb = .text:0x8037C580; // type:function size:0x98 diff --git a/configure.py b/configure.py index 36d34462..03daa257 100644 --- a/configure.py +++ b/configure.py @@ -757,7 +757,8 @@ config.libs = [ Object(NonMatching, "d/snd/d_snd_bgm_sound_callbacks.cpp"), Object(NonMatching, "d/snd/d_snd_bgm_sound_battle_callbacks.cpp"), Object(NonMatching, "d/snd/d_snd_bgm_sound.cpp"), - Object(NonMatching, "d/snd/d_snd_bgm_data_mgr.cpp"), + Object(NonMatching, "d/snd/d_snd_bgm_battle_data_mgr.cpp"), + Object(Matching, "d/snd/d_snd_bgm_harp_data.cpp"), Object(Matching, "d/snd/d_snd_bgm_mml_parser_base.cpp"), Object(NonMatching, "d/snd/d_snd_bgm_mml_parsers.cpp"), Object(NonMatching, "d/snd/d_snd_small_effect_mgr.cpp"), diff --git a/include/d/snd/d_snd_bgm_harp_data.h b/include/d/snd/d_snd_bgm_harp_data.h new file mode 100644 index 00000000..b2f9cdd9 --- /dev/null +++ b/include/d/snd/d_snd_bgm_harp_data.h @@ -0,0 +1,132 @@ +#ifndef D_SND_BGM_HARP_DATA_H +#define D_SND_BGM_HARP_DATA_H + +#include "common.h" + +/** + * This file deals with the pitch of the Goddess' Harp when Link + * is freely strumming. In this case the pitch of the strings + * is adjusted to match the key of background music through sequence variables. + * When Link strums the harp, 12 strings can be heard, but it's actually only four + * notes of the same chord spanning three octaves. + * + * I haven't yet investigated all the data, but a typical chord Link might play + * is Cmaj^7, consisting of C-E-G-B. Link actually plays: + * G3-C4-E4-G4-B4-C5-E5-G5-B5-C6-E6-G6 + * B3 is missing, possibly to prevent dissonance at the ends of the scale. + * (Sealed Grounds, before Demise fight, no actual BGM) + */ + +/** + * Contains a single Seq sound var controlling the pitch of a subset of harp strings + * Size: 0x02 + */ +struct dSndBgmDataHarpVar_c { + dSndBgmDataHarpVar_c() : field_0x00(0), field_0x01(0) {} + + void reset() { + field_0x00 = 0; + field_0x01 = 0; + } + + void onFlag() { + field_0x00 |= 1; + } + + /* 0x00 */ u8 field_0x00; // flags + /* 0x01 */ s8 field_0x01; // var +}; + +/** + * Contains Seq sound vars for all harp strings, represents a "key" in the bgm music + * Size: 0x0C + */ +class dSndBgmDataHarpVarSetBase_c { +public: + dSndBgmDataHarpVarSetBase_c(s32 count); + ~dSndBgmDataHarpVarSetBase_c(); + + void resetVars(); + void addVar(u8 value); + + s32 getPosition() const { + return mPosition; + } + + void setPosition(s32 position) { + mPosition = position; + } + + s32 getCount() const { + return mCount; + } + + s32 getMax() const { + return mMax; + } + + dSndBgmDataHarpVar_c *getVar(s32 idx) { + if (idx >= getCount()) { + return nullptr; + } + return &mpVars[idx]; + } + + dSndBgmDataHarpVar_c *getUnusedVar() { + if (mCount >= mMax) { + return nullptr; + } + return &mpVars[mCount]; + } + +private: + /* 0x00 */ dSndBgmDataHarpVar_c *mpVars; + /* 0x04 */ s32 mPosition; + /* 0x08 */ s16 mMax; + /* 0x0A */ s16 mCount; +}; + +class dSndBgmDataHarpVarSet_c : public dSndBgmDataHarpVarSetBase_c { +public: + dSndBgmDataHarpVarSet_c() : dSndBgmDataHarpVarSetBase_c(4) {} + +private: +}; + +/** + * Contains parsed seq data for harp strings for a single bgm sound + */ +class dSndBgmHarpDataBase_c { +public: + dSndBgmHarpDataBase_c(); + void resetVars(); + s32 getIdxForPosition(s32 position); + + dSndBgmDataHarpVarSetBase_c *getUsableVarSet(s32 idx); + void addVar(s32 position, s32 value, s32 unk); + void setField_0x08(s32 value); + +protected: + dSndBgmDataHarpVarSetBase_c *getVarSet(s32 idx); + /* 0x00 */ dSndBgmDataHarpVarSetBase_c *mpVarSets; + /* 0x04 */ s16 mMax; + /* 0x06 */ s16 mCount; + /* 0x08 */ s32 field_0x08; +}; + +class dSndBgmHarpData_c : public dSndBgmHarpDataBase_c { +public: + dSndBgmHarpData_c(); + ~dSndBgmHarpData_c(); + + /** + * Writes the Seq sound variables that control the pitch + * of the individual harp strings. + */ + void writeSeqVars(s32 idx); + +private: + static const u32 NUM_SETS = 300; +}; + +#endif diff --git a/include/nw4r/snd/snd_SeqSoundHandle.h b/include/nw4r/snd/snd_SeqSoundHandle.h index 426eb909..764bbec3 100644 --- a/include/nw4r/snd/snd_SeqSoundHandle.h +++ b/include/nw4r/snd/snd_SeqSoundHandle.h @@ -52,6 +52,10 @@ namespace nw4r { namespace snd mSound->ReadVariable(varNo, value); } + static bool WriteGlobalVariable(int varNo, s16 value) { + return detail::SeqSound::WriteGlobalVariable(varNo, value); + } + void SetTrackMute(u32 trackFlags, SeqMute mute) { if (IsAttachedSound()) mSound->SetTrackMute(trackFlags, mute); diff --git a/src/d/snd/d_snd_bgm_harp_data.cpp b/src/d/snd/d_snd_bgm_harp_data.cpp new file mode 100644 index 00000000..1479eee5 --- /dev/null +++ b/src/d/snd/d_snd_bgm_harp_data.cpp @@ -0,0 +1,165 @@ +#include "d/snd/d_snd_bgm_harp_data.h" + +#include "common.h" +#include "nw4r/snd/snd_SeqSoundHandle.h" + +dSndBgmDataHarpVarSetBase_c::dSndBgmDataHarpVarSetBase_c(s32 count) { + mPosition = -1; + mMax = count; + mCount = 0; + mpVars = new dSndBgmDataHarpVar_c[count]; + resetVars(); +} + +dSndBgmDataHarpVarSetBase_c::~dSndBgmDataHarpVarSetBase_c() { + delete[] mpVars; +} + +void dSndBgmDataHarpVarSetBase_c::resetVars() { + mCount = 0; + mPosition = -1; + for (int i = 0; i < mMax; i++) { + mpVars[i].reset(); + } +} + +void dSndBgmDataHarpVarSetBase_c::addVar(u8 value) { + if (mCount < mMax) { + mpVars[mCount].field_0x01 = value; + mCount++; + } +} + +dSndBgmHarpDataBase_c::dSndBgmHarpDataBase_c() : mMax(0), mCount(0), field_0x08(0) {} + +void dSndBgmHarpDataBase_c::resetVars() { + mCount = 0; + field_0x08 = 0; + for (int i = 0; i < mMax; i++) { + mpVarSets[i].resetVars(); + } +} + +s32 dSndBgmHarpDataBase_c::getIdxForPosition(s32 position) { + if (position < 0) { + return 0; + } + if (mpVarSets[0].getPosition() > position) { + return 0; + } + + for (int i = mCount - 1; i > 0; i--) { + if (mpVarSets[i].getPosition() <= position) { + return i; + } + } + + return 0; +} + +void dSndBgmHarpDataBase_c::addVar(s32 position, s32 value, s32 unk) { + if (position < 0) { + return; + } + + if (value <= 0) { + return; + } + + if (field_0x08 > 0) { + return; + } + + if (mCount > mMax) { + return; + } + + // Look at the set that might not be full yet + dSndBgmDataHarpVarSetBase_c *set = getVarSet(mCount); + if (set->getPosition() >= 0 && set->getPosition() < position) { + // If it's not full but it doesn't match the position, + // advance either way + set++; + mCount++; + } + + // Set position for new set + if (set->getPosition() < 0) { + // Make sure set positions are strictly monotonically increasing (1), + // do not record e.g. a fifth value for the same set + if (mCount > 0 && set[-1].getPosition() >= position) { + return; + } + set->setPosition(position); + } + + dSndBgmDataHarpVar_c *var = set->getUnusedVar(); + if (var != nullptr) { + if (unk == 0x7F) { + var->onFlag(); + } + set->addVar(value); + + if (set->getCount() >= set->getMax()) { + // If the set is full, continue with the next set. + // Condition (1) above will make sure that the next + // set gets a strictly higher position, so there are + // no sets with the same position. + mCount++; + } + } +} + +void dSndBgmHarpDataBase_c::setField_0x08(s32 value) { + if (value < 0) { + value = 0; + } + field_0x08 = value; +} + +dSndBgmDataHarpVarSetBase_c *dSndBgmHarpDataBase_c::getUsableVarSet(s32 idx) { + if (idx < 0) { + return nullptr; + } + + if (idx >= mCount) { + return nullptr; + } + + return &mpVarSets[idx]; +} + +dSndBgmDataHarpVarSetBase_c *dSndBgmHarpDataBase_c::getVarSet(s32 idx) { + if (idx < 0) { + return nullptr; + } + + if (idx >= mMax) { + return nullptr; + } + + return &mpVarSets[idx]; +} + +dSndBgmHarpData_c::dSndBgmHarpData_c() { + mpVarSets = new dSndBgmDataHarpVarSet_c[NUM_SETS]; + mMax = NUM_SETS; + resetVars(); +} + +dSndBgmHarpData_c::~dSndBgmHarpData_c() { + delete[] mpVarSets; +} + +void dSndBgmHarpData_c::writeSeqVars(s32 idx) { + if (idx < 0) { + return; + } + if (idx >= mCount) { + return; + } + + for (int i = 0; i < mpVarSets[idx].getCount(); i++) { + nw4r::snd::SeqSoundHandle::WriteGlobalVariable(10 + i, mpVarSets[idx].getVar(i)->field_0x01); + } +}