1
0
Fork 0
mirror of https://github.com/zeldaret/oot.git synced 2024-11-25 09:45:02 +00:00

Improve River_Sound Docs (#1100)

* river_sound docs

* Small touch-up

* PR Suggestions

* Improve comments on river line calculations

* More PR Feedback

* after analyzing the filter data for MM, lowPassFilter is reverse to what I thought it was
This commit is contained in:
engineer124 2022-01-15 09:13:04 +11:00 committed by GitHub
parent 9fec455805
commit 9450272503
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 255 additions and 178 deletions

View file

@ -1899,7 +1899,7 @@ void AudioHeap_InitMainPools(s32 sizeForAudioInitPool);
void* AudioHeap_AllocCached(s32 tableType, s32 size, s32 cache, s32 id); void* AudioHeap_AllocCached(s32 tableType, s32 size, s32 cache, s32 id);
void* AudioHeap_SearchCaches(s32 tableType, s32 arg1, s32 id); void* AudioHeap_SearchCaches(s32 tableType, s32 arg1, s32 id);
void* AudioHeap_SearchRegularCaches(s32 tableType, s32 cache, s32 id); void* AudioHeap_SearchRegularCaches(s32 tableType, s32 cache, s32 id);
void AudioHeap_LoadFilter(s16* filter, s32 filter1, s32 filter2); void AudioHeap_LoadFilter(s16* filter, s32 lowPassCutoff, s32 highPassCutoff);
s32 AudioHeap_ResetStep(void); s32 AudioHeap_ResetStep(void);
void AudioHeap_Init(void); void AudioHeap_Init(void);
void* AudioHeap_SearchPermanentCache(s32 tableType, s32 id); void* AudioHeap_SearchPermanentCache(s32 tableType, s32 id);
@ -2032,8 +2032,8 @@ void Audio_PlaySoundWaterfall(Vec3f* pos, f32 freqScale);
void func_800F47BC(void); void func_800F47BC(void);
void func_800F47FC(void); void func_800F47FC(void);
void func_800F483C(u8 targetVol, u8 volFadeTimer); void func_800F483C(u8 targetVol, u8 volFadeTimer);
void func_800F4870(u8); void Audio_SetGanonsTowerBgmVolumeLevel(u8 ganonsTowerLevel);
void func_800F4A54(u8); void Audio_LowerMainBgmVolume(u8 volume);
void Audio_PlaySoundIncreasinglyTransposed(Vec3f* pos, s16 sfxId, u8* semitones); void Audio_PlaySoundIncreasinglyTransposed(Vec3f* pos, s16 sfxId, u8* semitones);
void Audio_ResetIncreasingTranspose(void); void Audio_ResetIncreasingTranspose(void);
void Audio_PlaySoundTransposed(Vec3f* pos, u16 sfxId, s8 semitone); void Audio_PlaySoundTransposed(Vec3f* pos, u16 sfxId, s8 semitone);

View file

@ -1313,8 +1313,8 @@ void AudioSeq_SequenceChannelProcessScript(SequenceChannel* channel) {
command = parameters[0]; command = parameters[0];
if (channel->filter != NULL) { if (channel->filter != NULL) {
lowBits = (command >> 4) & 0xF; lowBits = (command >> 4) & 0xF; // LowPassCutoff
command &= 0xF; command &= 0xF; // HighPassCutoff
AudioHeap_LoadFilter(channel->filter, lowBits, command); AudioHeap_LoadFilter(channel->filter, lowBits, command);
} }
break; break;

View file

@ -111,8 +111,8 @@ u8 gMorphaTransposeTable[16] = { 0, 0, 0, 1, 1, 2, 4, 6, 8, 8, 8, 8, 8, 8, 8, 8
u8 sPrevChargeLevel = 0; u8 sPrevChargeLevel = 0;
f32 D_801305E4[4] = { 1.0f, 1.12246f, 1.33484f, 1.33484f }; // 2**({0, 2, 5, 5}/12) f32 D_801305E4[4] = { 1.0f, 1.12246f, 1.33484f, 1.33484f }; // 2**({0, 2, 5, 5}/12)
f32 D_801305F4 = 1.0f; f32 D_801305F4 = 1.0f;
u8 D_801305F8[8] = { 127, 80, 75, 73, 70, 68, 65, 60 }; u8 sGanonsTowerLevelsVol[8] = { 127, 80, 75, 73, 70, 68, 65, 60 };
u8 D_80130600 = 0; u8 sEnterGanonsTowerTimer = 0;
s8 D_80130604 = 2; s8 D_80130604 = 2;
s8 D_80130608 = 0; s8 D_80130608 = 0;
s8 sAudioCutsceneFlag = 0; s8 sAudioCutsceneFlag = 0;
@ -1187,11 +1187,11 @@ struct {
s8 str[5]; s8 str[5];
u16 num; u16 num;
} sAudioScrPrtBuf[SCROLL_PRINT_BUF_SIZE]; } sAudioScrPrtBuf[SCROLL_PRINT_BUF_SIZE];
u8 D_8016B8B0; u8 sRiverSoundMainBgmVol;
u8 D_8016B8B1; u8 sRiverSoundMainBgmCurrentVol;
u8 D_8016B8B2; u8 sRiverSoundMainBgmLower;
u8 D_8016B8B3; u8 sRiverSoundMainBgmRestore;
u8 sAudioGanonDistVol; u8 sGanonsTowerVol;
SfxPlayerState sSfxChannelState[0x10]; SfxPlayerState sSfxChannelState[0x10];
char sBinToStrBuf[0x20]; char sBinToStrBuf[0x20];
@ -1243,7 +1243,7 @@ void PadMgr_RequestPadData(PadMgr* padmgr, Input* inputs, s32 mode);
void Audio_StepFreqLerp(FreqLerp* lerp); void Audio_StepFreqLerp(FreqLerp* lerp);
void func_800F56A8(void); void func_800F56A8(void);
void Audio_PlayNatureAmbienceSequence(u8 natureAmbienceId); void Audio_PlayNatureAmbienceSequence(u8 natureAmbienceId);
s32 Audio_SetGanonDistVol(u8 targetVol); s32 Audio_SetGanonsTowerBgmVolume(u8 targetVol);
void func_800EC960(u8 custom) { void func_800EC960(u8 custom) {
if (!custom) { if (!custom) {
@ -2852,7 +2852,7 @@ void AudioDebug_Draw(GfxPrint* printer) {
GfxPrint_Printf(printer, "ENEMY DIST %f VOL %3d", sAudioEnemyDist, sAudioEnemyVol); GfxPrint_Printf(printer, "ENEMY DIST %f VOL %3d", sAudioEnemyDist, sAudioEnemyVol);
GfxPrint_SetPos(printer, 3, 11); GfxPrint_SetPos(printer, 3, 11);
GfxPrint_Printf(printer, "GANON DIST VOL %3d", sAudioGanonDistVol); GfxPrint_Printf(printer, "GANON DIST VOL %3d", sGanonsTowerVol);
GfxPrint_SetPos(printer, 3, 12); GfxPrint_SetPos(printer, 3, 12);
GfxPrint_Printf(printer, "DEMO FLAG %d", sAudioCutsceneFlag); GfxPrint_Printf(printer, "DEMO FLAG %d", sAudioCutsceneFlag);
@ -3490,7 +3490,7 @@ void AudioDebug_ProcessInput(void) {
D_8013340C = sAudioScrPrtWork[10]; D_8013340C = sAudioScrPrtWork[10];
} }
void func_800F4A70(void); void Audio_UpdateRiverSoundVolumes(void);
void func_800F5CF8(void); void func_800F5CF8(void);
void func_800F3054(void) { void func_800F3054(void) {
@ -3500,7 +3500,7 @@ void func_800F3054(void) {
func_800EE6F4(); func_800EE6F4();
Audio_StepFreqLerp(&sRiverFreqScaleLerp); Audio_StepFreqLerp(&sRiverFreqScaleLerp);
Audio_StepFreqLerp(&sWaterfallFreqScaleLerp); Audio_StepFreqLerp(&sWaterfallFreqScaleLerp);
func_800F4A70(); Audio_UpdateRiverSoundVolumes();
func_800F56A8(); func_800F56A8();
func_800F5CF8(); func_800F5CF8();
if (gAudioSpecId == 7) { if (gAudioSpecId == 7) {
@ -4064,88 +4064,116 @@ void func_800F483C(u8 targetVol, u8 volFadeTimer) {
Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 0, targetVol, volFadeTimer); Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 0, targetVol, volFadeTimer);
} }
void func_800F4870(u8 arg0) { /**
s8 pan; * Incrementally increase volume of NA_BGM_GANON_TOWER for each new room during the climb of Ganon's Tower
u8 i; */
void Audio_SetGanonsTowerBgmVolumeLevel(u8 ganonsTowerLevel) {
u8 channelIdx;
s8 pan = 0;
pan = 0; // Ganondorf's Lair
if (arg0 == 0) { if (ganonsTowerLevel == 0) {
pan = 0x7F; pan = 0x7F;
} }
for (i = 0; i < 16; i++) { for (channelIdx = 0; channelIdx < 16; channelIdx++) {
// CHAN_UPD_PAN_UNSIGNED // CHAN_UPD_PAN_UNSIGNED
Audio_QueueCmdS8( Audio_QueueCmdS8(_SHIFTL(0x7, 24, 8) | _SHIFTL(SEQ_PLAYER_BGM_MAIN, 16, 8) | _SHIFTL(channelIdx, 8, 8) |
_SHIFTL(0x7, 24, 8) | _SHIFTL(SEQ_PLAYER_BGM_MAIN, 16, 8) | _SHIFTL(i, 8, 8) | _SHIFTL(0, 0, 8), pan); _SHIFTL(0, 0, 8),
pan);
} }
if (arg0 == 7) { // Lowest room in Ganon's Tower (Entrance Room)
D_80130600 = 2; if (ganonsTowerLevel == 7) {
// Adds a delay to setting the volume in the first room
sEnterGanonsTowerTimer = 2;
} else { } else {
Audio_SetGanonDistVol(D_801305F8[arg0 & 7]); Audio_SetGanonsTowerBgmVolume(sGanonsTowerLevelsVol[ganonsTowerLevel % ARRAY_COUNTU(sGanonsTowerLevelsVol)]);
} }
} }
// (name derived from debug strings, should probably update. used in ganon/ganon_boss scenes) /**
s32 Audio_SetGanonDistVol(u8 targetVol) { * If a new volume is requested for ganon's tower, update the volume and
u8 phi_v0; * calculate a new low-pass filter cutoff and reverb based on the new volume
u16 phi_v0_2; */
u8 i; s32 Audio_SetGanonsTowerBgmVolume(u8 targetVol) {
u8 lowPassFilterCutoff;
u16 reverb;
u8 channelIdx;
if (sAudioGanonDistVol != targetVol) { if (sGanonsTowerVol != targetVol) {
// Sets the volume
Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 0, targetVol, 2); Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 0, targetVol, 2);
if (targetVol < 0x40) {
phi_v0 = 0x10;
} else {
phi_v0 = (((targetVol - 0x40) >> 2) + 1) << 4;
}
Audio_SeqCmd8(SEQ_PLAYER_BGM_MAIN, 4, 15, phi_v0); // Sets the filter cutoff of the form (lowPassFilterCutoff << 4) | (highPassFilter & 0xF). highPassFilter is
for (i = 0; i < 0x10; i++) { // always set to 0
if (gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].channels[i] != &gAudioContext.sequenceChannelNone) { if (targetVol < 0x40) {
if ((u8)gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].channels[i]->soundScriptIO[5] != 0xFF) { // Only the first room
// this looks like some kind of macro? lowPassFilterCutoff = 1 << 4;
phi_v0_2 = } else {
((u16)gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].channels[i]->soundScriptIO[5] - targetVol) + // Higher volume leads to a higher cut-off frequency in the low-pass filtering
lowPassFilterCutoff = (((targetVol - 0x40) >> 2) + 1) << 4;
}
// Set lowPassFilterCutoff to io port 4 from channel 15
Audio_SeqCmd8(SEQ_PLAYER_BGM_MAIN, 4, 15, lowPassFilterCutoff);
// Sets the reverb
for (channelIdx = 0; channelIdx < 16; channelIdx++) {
if (gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].channels[channelIdx] !=
&gAudioContext.sequenceChannelNone) {
// soundScriptIO[5] is set to 0x40 in channels 0, 1, and 4
if ((u8)gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].channels[channelIdx]->soundScriptIO[5] != 0xFF) {
// Higher volume leads to lower reverb
reverb =
((u16)gAudioContext.seqPlayers[SEQ_PLAYER_BGM_MAIN].channels[channelIdx]->soundScriptIO[5] -
targetVol) +
0x7F; 0x7F;
if (phi_v0_2 >= 0x80) { if (reverb > 0x7F) {
phi_v0_2 = 0x7F; reverb = 0x7F;
} }
// CHAN_UPD_REVERB // CHAN_UPD_REVERB
Audio_QueueCmdS8(_SHIFTL(0x5, 24, 8) | _SHIFTL(SEQ_PLAYER_BGM_MAIN, 16, 8) | _SHIFTL(i, 8, 8) | Audio_QueueCmdS8(_SHIFTL(0x5, 24, 8) | _SHIFTL(SEQ_PLAYER_BGM_MAIN, 16, 8) |
_SHIFTL(0, 0, 8), _SHIFTL(channelIdx, 8, 8) | _SHIFTL(0, 0, 8),
(u8)phi_v0_2); (u8)reverb);
} }
} }
} }
sAudioGanonDistVol = targetVol; sGanonsTowerVol = targetVol;
} }
return -1; return -1;
} }
void func_800F4A54(u8 arg0) { /**
D_8016B8B0 = arg0; * Responsible for lowering market bgm in Child Market Entrance and Child Market Back Alley
D_8016B8B2 = 1; * Only lowers volume for 1 frame, so must be called every frame to maintain lower volume
*/
void Audio_LowerMainBgmVolume(u8 volume) {
sRiverSoundMainBgmVol = volume;
sRiverSoundMainBgmLower = true;
} }
void func_800F4A70(void) { void Audio_UpdateRiverSoundVolumes(void) {
if (D_8016B8B2 == 1) { // Updates Main Bgm Volume (RiverSound of type RS_LOWER_MAIN_BGM_VOLUME)
if (D_8016B8B1 != D_8016B8B0) { if (sRiverSoundMainBgmLower == true) {
Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 0, D_8016B8B0, 0xA); if (sRiverSoundMainBgmCurrentVol != sRiverSoundMainBgmVol) {
D_8016B8B1 = D_8016B8B0; // lowers the volume for 1 frame
D_8016B8B3 = 1; Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 0, sRiverSoundMainBgmVol, 0xA);
sRiverSoundMainBgmCurrentVol = sRiverSoundMainBgmVol;
sRiverSoundMainBgmRestore = true;
} }
D_8016B8B2 = 0; sRiverSoundMainBgmLower = false;
} else if (D_8016B8B3 == 1 && D_80130608 == 0) { } else if (sRiverSoundMainBgmRestore == true && D_80130608 == 0) {
// restores the volume every frame
Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 0, 0x7F, 0xA); Audio_SetVolScale(SEQ_PLAYER_BGM_MAIN, 0, 0x7F, 0xA);
D_8016B8B1 = 0x7F; sRiverSoundMainBgmCurrentVol = 0x7F;
D_8016B8B3 = 0; sRiverSoundMainBgmRestore = false;
} }
if (D_80130600 != 0) { // Update Ganon's Tower Volume (RiverSound of type RS_GANON_TOWER_7)
D_80130600--; if (sEnterGanonsTowerTimer != 0) {
if (D_80130600 == 0) { sEnterGanonsTowerTimer--;
Audio_SetGanonDistVol(D_801305F8[7]); if (sEnterGanonsTowerTimer == 0) {
Audio_SetGanonsTowerBgmVolume(sGanonsTowerLevelsVol[7]);
} }
} }
} }
@ -4915,11 +4943,11 @@ void func_800F6C34(void) {
sRiverFreqScaleLerp.value = 1.0f; sRiverFreqScaleLerp.value = 1.0f;
sWaterfallFreqScaleLerp.value = 1.0f; sWaterfallFreqScaleLerp.value = 1.0f;
D_8016B7D8 = 1.0f; D_8016B7D8 = 1.0f;
D_8016B8B0 = 0x7F; sRiverSoundMainBgmVol = 0x7F;
D_8016B8B1 = 0x7F; sRiverSoundMainBgmCurrentVol = 0x7F;
D_8016B8B2 = 0; sRiverSoundMainBgmLower = false;
D_8016B8B3 = 0; sRiverSoundMainBgmRestore = false;
sAudioGanonDistVol = 0xFF; sGanonsTowerVol = 0xFF;
D_8016B9D8 = 0; D_8016B9D8 = 0;
sSpecReverb = sSpecReverbs[gAudioSpecId]; sSpecReverb = sSpecReverbs[gAudioSpecId];
D_80130608 = 0; D_80130608 = 0;

View file

@ -1,7 +1,7 @@
/* /*
* File: z_en_river_sound.c * File: z_en_river_sound.c
* Overlay: ovl_En_River_Sound * Overlay: ovl_En_River_Sound
* Description: Ambient Sound Effects * Description: Controls various sounds. Includes sound effects and bgms
*/ */
#include "z_en_river_sound.h" #include "z_en_river_sound.h"
@ -28,17 +28,18 @@ const ActorInit En_River_Sound_InitVars = {
void EnRiverSound_Init(Actor* thisx, GlobalContext* globalCtx) { void EnRiverSound_Init(Actor* thisx, GlobalContext* globalCtx) {
EnRiverSound* this = (EnRiverSound*)thisx; EnRiverSound* this = (EnRiverSound*)thisx;
this->playSound = 0; this->playSound = false;
this->pathIndex = (this->actor.params >> 8) & 0xFF; this->pathIndex = (this->actor.params >> 8) & 0xFF;
this->actor.params = this->actor.params & 0xFF; this->actor.params = this->actor.params & 0xFF;
if (this->actor.params >= RS_MAX) { // used for ganon and ganon_boss scenes if (this->actor.params >= RS_GANON_TOWER_0) {
func_800F4870(this->actor.params - RS_MAX); // Incrementally increase volume of NA_BGM_GANON_TOWER for each new room during the climb of Ganon's Tower
Audio_SetGanonsTowerBgmVolumeLevel(this->actor.params - RS_GANON_TOWER_0);
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
} else if (this->actor.params == RS_UNK_F7) { } else if (this->actor.params == RS_NATURE_AMBIENCE) {
Audio_PlayNatureAmbienceSequence(NATURE_ID_KOKIRI_REGION); Audio_PlayNatureAmbienceSequence(NATURE_ID_KOKIRI_REGION);
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
} else if (this->actor.params == RS_SARIAS_SONG) { } else if (this->actor.params == RS_LOST_WOODS_SARIAS_SONG) {
if (!CHECK_QUEST_ITEM(QUEST_SONG_LULLABY) || CHECK_QUEST_ITEM(QUEST_SONG_SARIA)) { if (!CHECK_QUEST_ITEM(QUEST_SONG_LULLABY) || CHECK_QUEST_ITEM(QUEST_SONG_SARIA)) {
Actor_Kill(&this->actor); Actor_Kill(&this->actor);
} }
@ -48,37 +49,57 @@ void EnRiverSound_Init(Actor* thisx, GlobalContext* globalCtx) {
void EnRiverSound_Destroy(Actor* thisx, GlobalContext* globalCtx) { void EnRiverSound_Destroy(Actor* thisx, GlobalContext* globalCtx) {
EnRiverSound* this = (EnRiverSound*)thisx; EnRiverSound* this = (EnRiverSound*)thisx;
if (this->actor.params == RS_SARIAS_SONG) { if (this->actor.params == RS_LOST_WOODS_SARIAS_SONG) {
Audio_ClearSariaBgmAtPos(&this->actor.projectedPos); Audio_ClearSariaBgmAtPos(&this->actor.projectedPos);
} else if (this->actor.params == RS_UNK_13) { } else if (this->actor.params == RS_GORON_CITY_SARIAS_SONG) {
Audio_ClearSariaBgm2(); Audio_ClearSariaBgm2();
} }
} }
s32 func_80AE6A54(Vec3f* arg0, Vec3f* arg1, Vec3f* arg2, Vec3f* arg3) { /**
Vec3f vec[3]; * Determines the closest point to hearPos that is contained on the line connecting points A & B
* If the closest point on the line will not be on the line segment connecting points A or B, return false.
* Otherwise, calculate the point between A & B, assign it to `newSoundPos`, and return true
*/
s32 EnRiverSound_FindClosestPointOnLineSegment(Vec3f* pointA, Vec3f* pointB, Vec3f* hearPos, Vec3f* newSoundPos) {
Vec3f lineSeg[3];
f32 temp; f32 temp;
vec[0].x = arg0->x - arg2->x; // Line Segment from the hearPos to the first point in the path
vec[0].y = arg0->y - arg2->y; lineSeg[0].x = pointA->x - hearPos->x;
vec[0].z = arg0->z - arg2->z; lineSeg[0].y = pointA->y - hearPos->y;
lineSeg[0].z = pointA->z - hearPos->z;
vec[1].x = arg1->x - arg2->x; // Line Segment from the hearPos to the second point in the path
vec[1].y = arg1->y - arg2->y; lineSeg[1].x = pointB->x - hearPos->x;
vec[1].z = arg1->z - arg2->z; lineSeg[1].y = pointB->y - hearPos->y;
lineSeg[1].z = pointB->z - hearPos->z;
vec[2].x = vec[1].x - vec[0].x; // Line Segment from the first point to the second point in the path
vec[2].y = vec[1].y - vec[0].y; lineSeg[2].x = lineSeg[1].x - lineSeg[0].x;
vec[2].z = vec[1].z - vec[0].z; lineSeg[2].y = lineSeg[1].y - lineSeg[0].y;
lineSeg[2].z = lineSeg[1].z - lineSeg[0].z;
temp = DOTXYZ(vec[2], vec[0]); temp = DOTXYZ(lineSeg[2], lineSeg[0]);
if ((DOTXYZ(vec[2], vec[1]) * temp) < 0.0f) { /**
temp = -temp / (SQ(vec[2].x) + SQ(vec[2].y) + SQ(vec[2].z)); * | |
* | |
* | |
* A ----------------- B
* | |
* | |
* | |
* This condition uses dot products to check to see that `hearPos` is contained within the above region
* i.e. The closest point on line AB must be between A & B
*/
if ((DOTXYZ(lineSeg[2], lineSeg[1]) * temp) < 0.0f) {
temp = -temp / SQXYZ(lineSeg[2]);
arg3->x = (vec[2].x * temp) + arg0->x; // Closest point to hearPos contained on line segment A-B
arg3->y = (vec[2].y * temp) + arg0->y; newSoundPos->x = (lineSeg[2].x * temp) + pointA->x;
arg3->z = (vec[2].z * temp) + arg0->z; newSoundPos->y = (lineSeg[2].y * temp) + pointA->y;
newSoundPos->z = (lineSeg[2].z * temp) + pointA->z;
return true; return true;
} }
@ -93,70 +114,78 @@ s32 func_80AE6A54(Vec3f* arg0, Vec3f* arg1, Vec3f* arg2, Vec3f* arg3) {
*/ */
s32 EnRiverSound_GetSoundPos(Vec3s* points, s32 numPoints, Vec3f* hearPos, Vec3f* soundPos) { s32 EnRiverSound_GetSoundPos(Vec3s* points, s32 numPoints, Vec3f* hearPos, Vec3f* soundPos) {
s32 i; s32 i;
s32 pointIdx; s32 closestPointIdx;
s32 sp78[2] = { 0, 0 }; s32 useAdjacentPoints[2] = {
Vec3f pointLoc; false, // use previous point
Vec3f sp60; false, // use next point
Vec3f sp54; };
Vec3f vec; Vec3f closestPointPos;
f32 pointDist = 10000.0f; Vec3f nextLineSegClosestPos;
Vec3s* point; Vec3f prevLineSegClosestPos;
Vec3f point;
f32 closestPointDist = 10000.0f;
Vec3s* closestPoint;
for (i = 0; i < numPoints; i++) { for (i = 0; i < numPoints; i++) {
f32 dist; f32 dist;
vec.x = points[i].x; point.x = points[i].x;
vec.y = points[i].y; point.y = points[i].y;
vec.z = points[i].z; point.z = points[i].z;
dist = Math_Vec3f_DistXYZ(hearPos, &vec); dist = Math_Vec3f_DistXYZ(hearPos, &point);
if (dist < pointDist) { if (dist < closestPointDist) {
pointDist = dist; closestPointDist = dist;
pointIdx = i; closestPointIdx = i;
} }
} }
if (pointDist >= 10000.0f) { if (closestPointDist >= 10000.0f) {
return false; return false;
} }
point = &points[pointIdx]; closestPoint = &points[closestPointIdx];
pointLoc.x = point->x; closestPointPos.x = closestPoint->x;
pointLoc.y = point->y; closestPointPos.y = closestPoint->y;
pointLoc.z = point->z; closestPointPos.z = closestPoint->z;
if (pointIdx != 0) { // point on path before closest point
vec.x = point[-1].x; if (closestPointIdx != 0) {
vec.y = point[-1].y; point.x = closestPoint[-1].x;
vec.z = point[-1].z; point.y = closestPoint[-1].y;
sp78[0] = func_80AE6A54(&vec, &pointLoc, hearPos, &sp54); point.z = closestPoint[-1].z;
useAdjacentPoints[0] =
EnRiverSound_FindClosestPointOnLineSegment(&point, &closestPointPos, hearPos, &prevLineSegClosestPos);
} }
if (pointIdx + 1 != numPoints) { // point on path after closest point
vec.x = point[1].x; if (closestPointIdx + 1 != numPoints) {
vec.y = point[1].y; point.x = closestPoint[1].x;
vec.z = point[1].z; point.y = closestPoint[1].y;
sp78[1] = func_80AE6A54(&pointLoc, &vec, hearPos, &sp60); point.z = closestPoint[1].z;
useAdjacentPoints[1] =
EnRiverSound_FindClosestPointOnLineSegment(&closestPointPos, &point, hearPos, &nextLineSegClosestPos);
} }
if (sp78[0] && sp78[1]) { if (useAdjacentPoints[0] && useAdjacentPoints[1]) {
if (!func_80AE6A54(&sp54, &sp60, hearPos, soundPos)) { if (!EnRiverSound_FindClosestPointOnLineSegment(&prevLineSegClosestPos, &nextLineSegClosestPos, hearPos,
soundPos->x = (sp54.x + sp60.x) * 0.5f; soundPos)) {
soundPos->y = (sp54.y + sp60.y) * 0.5f; soundPos->x = (prevLineSegClosestPos.x + nextLineSegClosestPos.x) * 0.5f;
soundPos->z = (sp54.z + sp60.z) * 0.5f; soundPos->y = (prevLineSegClosestPos.y + nextLineSegClosestPos.y) * 0.5f;
soundPos->z = (prevLineSegClosestPos.z + nextLineSegClosestPos.z) * 0.5f;
} }
} else if (sp78[0]) { } else if (useAdjacentPoints[0]) {
soundPos->x = sp54.x; soundPos->x = prevLineSegClosestPos.x;
soundPos->y = sp54.y; soundPos->y = prevLineSegClosestPos.y;
soundPos->z = sp54.z; soundPos->z = prevLineSegClosestPos.z;
} else if (sp78[1]) { } else if (useAdjacentPoints[1]) {
soundPos->x = sp60.x; soundPos->x = nextLineSegClosestPos.x;
soundPos->y = sp60.y; soundPos->y = nextLineSegClosestPos.y;
soundPos->z = sp60.z; soundPos->z = nextLineSegClosestPos.z;
} else { } else {
soundPos->x = pointLoc.x; soundPos->x = closestPointPos.x;
soundPos->y = pointLoc.y; soundPos->y = closestPointPos.y;
soundPos->z = pointLoc.z; soundPos->z = closestPointPos.z;
} }
return true; return true;
@ -167,35 +196,37 @@ void EnRiverSound_Update(Actor* thisx, GlobalContext* globalCtx) {
Vec3f* pos; Vec3f* pos;
Player* player = GET_PLAYER(globalCtx); Player* player = GET_PLAYER(globalCtx);
EnRiverSound* this = (EnRiverSound*)thisx; EnRiverSound* this = (EnRiverSound*)thisx;
s32 sp34; s32 bgId;
if ((thisx->params == RS_UNK_0) || (thisx->params == RS_UNK_4) || (thisx->params == RS_UNK_5)) { if ((thisx->params == RS_RIVER_DEFAULT_LOW_FREQ) || (thisx->params == RS_RIVER_DEFAULT_MEDIUM_FREQ) ||
(thisx->params == RS_RIVER_DEFAULT_HIGH_FREQ)) {
path = &globalCtx->setupPathList[this->pathIndex]; path = &globalCtx->setupPathList[this->pathIndex];
pos = &thisx->world.pos; pos = &thisx->world.pos;
if (EnRiverSound_GetSoundPos(SEGMENTED_TO_VIRTUAL(path->points), path->count, &player->actor.world.pos, pos)) { if (EnRiverSound_GetSoundPos(SEGMENTED_TO_VIRTUAL(path->points), path->count, &player->actor.world.pos, pos)) {
if (BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &thisx->floorPoly, &sp34, thisx, pos) != if (BgCheck_EntityRaycastFloor4(&globalCtx->colCtx, &thisx->floorPoly, &bgId, thisx, pos) !=
BGCHECK_Y_MIN) { BGCHECK_Y_MIN) {
// Get the sound volume pitch based on the speed of the river current under the actor // Get the river sfx frequency based on the speed of the river current under the actor
this->soundPitchIndex = SurfaceType_GetConveyorSpeed(&globalCtx->colCtx, thisx->floorPoly, sp34); this->soundFreqIndex = SurfaceType_GetConveyorSpeed(&globalCtx->colCtx, thisx->floorPoly, bgId);
} else { } else {
this->soundPitchIndex = 0; this->soundFreqIndex = 0;
} }
if (this->soundPitchIndex == 0) { if (this->soundFreqIndex == 0) {
if (thisx->params == RS_UNK_4) { if (thisx->params == RS_RIVER_DEFAULT_MEDIUM_FREQ) {
this->soundPitchIndex = 0; this->soundFreqIndex = 0;
} else if (thisx->params == RS_UNK_0) { } else if (thisx->params == RS_RIVER_DEFAULT_LOW_FREQ) {
this->soundPitchIndex = 1; this->soundFreqIndex = 1;
} else { } else {
this->soundPitchIndex = 2; // RS_RIVER_DEFAULT_HIGH_FREQ
this->soundFreqIndex = 2;
} }
} else { } else {
this->soundPitchIndex--; this->soundFreqIndex--;
this->soundPitchIndex = CLAMP_MAX(this->soundPitchIndex, 2); this->soundFreqIndex = CLAMP_MAX(this->soundFreqIndex, 2);
} }
} }
} else if ((thisx->params == RS_UNK_13) || (thisx->params == RS_UNK_19)) { } else if ((thisx->params == RS_GORON_CITY_SARIAS_SONG) || (thisx->params == RS_GREAT_FAIRY)) {
func_8002DBD0(&player->actor, &thisx->home.pos, &thisx->world.pos); func_8002DBD0(&player->actor, &thisx->home.pos, &thisx->world.pos);
} else if (globalCtx->sceneNum == SCENE_DDAN_BOSS && Flags_GetClear(globalCtx, thisx->room)) { } else if (globalCtx->sceneNum == SCENE_DDAN_BOSS && Flags_GetClear(globalCtx, thisx->room)) {
Actor_Kill(thisx); Actor_Kill(thisx);
@ -227,26 +258,37 @@ void EnRiverSound_Draw(Actor* thisx, GlobalContext* globalCtx) {
NA_SE_EV_TORCH - SFX_FLAG, NA_SE_EV_TORCH - SFX_FLAG,
NA_SE_EV_COW_CRY_LV - SFX_FLAG, NA_SE_EV_COW_CRY_LV - SFX_FLAG,
}; };
static f32 soundPitch[] = { 0.7f, 1.0f, 1.4f }; static f32 soundFreq[] = { 0.7f, 1.0f, 1.4f };
EnRiverSound* this = (EnRiverSound*)thisx; EnRiverSound* this = (EnRiverSound*)thisx;
if (!(this->playSound)) { if (!(this->playSound)) {
this->playSound = true; this->playSound = true;
} else if ((this->actor.params == RS_UNK_0) || (this->actor.params == RS_UNK_4) || } else if ((this->actor.params == RS_RIVER_DEFAULT_LOW_FREQ) ||
(this->actor.params == RS_UNK_5)) { (this->actor.params == RS_RIVER_DEFAULT_MEDIUM_FREQ) ||
Audio_PlaySoundRiver(&this->actor.projectedPos, soundPitch[this->soundPitchIndex]); (this->actor.params == RS_RIVER_DEFAULT_HIGH_FREQ)) {
} else if (this->actor.params == RS_UNK_11) { Audio_PlaySoundRiver(&this->actor.projectedPos, soundFreq[this->soundFreqIndex]);
func_800F4A54(90); } else if (this->actor.params == RS_LOWER_MAIN_BGM_VOLUME) {
} else if (this->actor.params == RS_SARIAS_SONG) { // Responsible for lowering market bgm in Child Market Entrance and Child Market Back Alley
// Lower volume from default 127 to a volume of 90
Audio_LowerMainBgmVolume(90);
} else if (this->actor.params == RS_LOST_WOODS_SARIAS_SONG) {
// Play Sarias Song at the next correct Lost Woods path to Sacred Forest Meadow
// Volume depends on distance to source
func_800F4E30(&this->actor.projectedPos, this->actor.xzDistToPlayer); func_800F4E30(&this->actor.projectedPos, this->actor.xzDistToPlayer);
} else if (this->actor.params == RS_UNK_13) { } else if (this->actor.params == RS_GORON_CITY_SARIAS_SONG) {
// Play Sarias Song in Goron City at the entrance to lost woods
// Volume depends on distance to source
Audio_PlaySariaBgm(&this->actor.home.pos, NA_BGM_SARIA_THEME, 1000); Audio_PlaySariaBgm(&this->actor.home.pos, NA_BGM_SARIA_THEME, 1000);
} else if (this->actor.params == RS_UNK_19) { } else if (this->actor.params == RS_GREAT_FAIRY) {
// Play the Great Fairy Song inside the fairy fountain
// Volume depends on distance to source
Audio_PlaySariaBgm(&this->actor.home.pos, NA_BGM_GREAT_FAIRY, 800); Audio_PlaySariaBgm(&this->actor.home.pos, NA_BGM_GREAT_FAIRY, 800);
} else if ((this->actor.params == RS_SANDSTORM) || (this->actor.params == RS_CHAMBER_OF_SAGES_1) || } else if ((this->actor.params == RS_SANDSTORM) || (this->actor.params == RS_CHAMBER_OF_SAGES_1) ||
(this->actor.params == RS_CHAMBER_OF_SAGES_2) || (this->actor.params == RS_RUMBLING)) { (this->actor.params == RS_CHAMBER_OF_SAGES_2) || (this->actor.params == RS_RUMBLING)) {
// Play sfx in the fixed center of the screen
func_800788CC(soundEffects[this->actor.params]); func_800788CC(soundEffects[this->actor.params]);
} else { } else {
// Play sfx at the location of riverSounds projected position
Audio_PlayActorSound2(&this->actor, soundEffects[this->actor.params]); Audio_PlayActorSound2(&this->actor, soundEffects[this->actor.params]);
} }
} }

View file

@ -9,35 +9,42 @@ struct EnRiverSound;
typedef struct EnRiverSound { typedef struct EnRiverSound {
/* 0x0000 */ Actor actor; /* 0x0000 */ Actor actor;
/* 0x014C */ u8 playSound; /* 0x014C */ u8 playSound;
/* 0x014D */ u8 soundPitchIndex; /* 0x014D */ u8 soundFreqIndex;
/* 0x014E */ s16 pathIndex; /* 0x014E */ s16 pathIndex;
} EnRiverSound; // size = 0x0150 } EnRiverSound; // size = 0x0150
typedef enum { typedef enum {
/* 0x00 */ RS_UNK_0, /* 0x00 */ RS_RIVER_DEFAULT_LOW_FREQ,
/* 0x01 */ RS_SMALL_WATERFALL, /* 0x01 */ RS_SMALL_WATERFALL,
/* 0x02 */ RS_LAVA_BUBBLES_1, /* 0x02 */ RS_LAVA_BUBBLES_1,
/* 0x03 */ RS_LARGE_WATERFALL, /* 0x03 */ RS_LARGE_WATERFALL,
/* 0x04 */ RS_UNK_4, /* 0x04 */ RS_RIVER_DEFAULT_MEDIUM_FREQ,
/* 0x05 */ RS_UNK_5, /* 0x05 */ RS_RIVER_DEFAULT_HIGH_FREQ,
/* 0x06 */ RS_LAVA_BUBBLES_2, /* 0x06 */ RS_LAVA_BUBBLES_2,
/* 0x07 */ RS_LAVA_BUBBLES_3, /* 0x07 */ RS_LAVA_BUBBLES_3,
/* 0x08 */ RS_DRIPPING_WATER, /* 0x08 */ RS_DRIPPING_WATER,
/* 0x09 */ RS_FOUNTAIN_WATER, /* 0x09 */ RS_FOUNTAIN_WATER,
/* 0x0A */ RS_MARKET_CROWD, /* 0x0A */ RS_MARKET_CROWD,
/* 0x0B */ RS_UNK_11, /* 0x0B */ RS_LOWER_MAIN_BGM_VOLUME,
/* 0x0C */ RS_SARIAS_SONG, /* 0x0C */ RS_LOST_WOODS_SARIAS_SONG,
/* 0x0D */ RS_UNK_13, /* 0x0D */ RS_GORON_CITY_SARIAS_SONG,
/* 0x0E */ RS_SANDSTORM, /* 0x0E */ RS_SANDSTORM,
/* 0x0F */ RS_LAKESIDE_LAB_TANK, /* 0x0F */ RS_LAKESIDE_LAB_TANK,
/* 0x10 */ RS_CHAMBER_OF_SAGES_1, /* 0x10 */ RS_CHAMBER_OF_SAGES_1,
/* 0x11 */ RS_CHAMBER_OF_SAGES_2, /* 0x11 */ RS_CHAMBER_OF_SAGES_2,
/* 0x12 */ RS_RUMBLING, /* 0x12 */ RS_RUMBLING,
/* 0x13 */ RS_UNK_19, /* 0x13 */ RS_GREAT_FAIRY,
/* 0x14 */ RS_TORCH_CRACKLING, /* 0x14 */ RS_TORCH_CRACKLING,
/* 0x15 */ RS_COW_MOOING, /* 0x15 */ RS_COW_MOOING,
/* 0xF7 */ RS_UNK_F7 = 0xF7, /* 0xF7 */ RS_NATURE_AMBIENCE = 0xF7,
/* 0xF8 */ RS_MAX /* 0xF8 */ RS_GANON_TOWER_0, // Ganondorf's Lair
/* 0xF9 */ RS_GANON_TOWER_1, // Top of Ganon's Tower
/* 0xFA */ RS_GANON_TOWER_2,
/* 0xFB */ RS_GANON_TOWER_3,
/* 0xFC */ RS_GANON_TOWER_4,
/* 0xFD */ RS_GANON_TOWER_5,
/* 0xFE */ RS_GANON_TOWER_6,
/* 0xFF */ RS_GANON_TOWER_7 // Bottom of Ganon's Tower
} RiverSoundType; } RiverSoundType;
#endif #endif