Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/audio/effects.c
Original file line number Diff line number Diff line change
Expand Up @@ -542,3 +542,30 @@ s32 adsr_update(struct AdsrState *adsr) {
return 0;
#endif
}

/**
* Compute comb filter gain based on Y position (height relative to camera).
* Used for creating height-based audio effects in surround sound.
*
* Returns:
* -32 to -1 (0xE0-0xFF): Sound is below camera (negative Y)
* 0 to 127: Sound is above camera (positive Y)
* Bit 0 is always set (odd number)
*/
s8 audio_compute_comb_filter(f32 posY) {
s8 combFilterGain;

if (posY < 0.0f) {
// Below camera
if (posY < -625.0f) {
combFilterGain = -32; // Very far below
} else {
combFilterGain = (s8)(((625.0f + posY) / 625.0f) * 31.0f) + 0xE0;
}
} else if (posY > 1250.0f) {
combFilterGain = 127; // Very far above
} else {
combFilterGain = (s8)((posY / 1250.0f) * 126.0f);
}
return combFilterGain | 1;
}
2 changes: 2 additions & 0 deletions src/audio/effects.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,6 @@ f32 adsr_update(struct AdsrState *adsr);
s32 adsr_update(struct AdsrState *adsr);
#endif

s8 audio_compute_comb_filter(f32 posY);

#endif // AUDIO_EFFECTS_H
62 changes: 62 additions & 0 deletions src/audio/external.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
#include "external.h"
#include "playback.h"
#include "synthesis.h"
#include "effects.h"
#include "game/level_update.h"
#include "game/object_list_processor.h"
#include "game/camera.h"
#include "seq_ids.h"
#include "dialog_ids.h"
#include <lib/src/osAi.h>
#include <stdio.h>

#if defined(VERSION_EU) || defined(VERSION_SH)
#define EU_FLOAT(x) x##f
Expand Down Expand Up @@ -1045,6 +1047,45 @@ static f32 get_sound_pan(f32 x, f32 z) {
return pan;
}

/**
* Calculate surround effect index from Z position (depth relative to camera).
* In SM64's coordinate system, positive Z is behind the camera, negative Z is in front.
* Uses AUDIO_MAX_DISTANCE for scaling, matching pan and volume distance calculations.
*
* Returns:
* 0x00 - 0x3F: Sound is in front of camera (0 = far front, 0x3F = at camera)
* 0x40 - 0x7F: Sound is behind camera (0x40 = at camera, 0x7F = far behind)
*
* Called from threads: thread4_sound, thread5_game_loop (EU only)
*/
static u8 get_sound_surround_effect_index(f32 z) {
f32 absZ;
s32 surroundEffectIndex;
f32 maxZ = 1000.f;

absZ = (z < 0 ? -z : z);
if (absZ > maxZ) {
absZ = maxZ;
}

// In SM64, positive z means behind the camera
if (z > 0.0f) {
// Behind camera - surround effect index 0x3F (at camera) to 0x7F (far behind)
surroundEffectIndex = (s32)((absZ / maxZ) * 64.0f) + 0x3F;
if (surroundEffectIndex > 0x7F) {
surroundEffectIndex = 0x7F;
}
} else {
// In front of camera - surround effect index 0x3F (at camera) to 0x00 (far front)
surroundEffectIndex = 0x3F - (s32)((absZ / maxZ) * 63.0f);
if (surroundEffectIndex < 0) {
surroundEffectIndex = 0;
}
}

return (u8)surroundEffectIndex;
}

/**
* Called from threads: thread4_sound, thread5_game_loop (EU only)
*/
Expand Down Expand Up @@ -1225,6 +1266,16 @@ static void update_game_sound(void) {
gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->soundScriptIO[4] = soundId;
gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->soundScriptIO[0] = 1;

// Set surround effect index based on Z depth when starting sound
if (gSoundMode == SOUND_MODE_SURROUND) {
gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->surroundEffectIndex =
get_sound_surround_effect_index(*sSoundBanks[bank][soundIndex].z);
// Set comb filter gain based on Y height for vertical positioning
gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->combFilterGain =
audio_compute_comb_filter(*sSoundBanks[bank][soundIndex].y);
gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->combFilterSize = 16;
}

switch (bank) {
case SOUND_BANK_MOVING:
if (!(sSoundBanks[bank][soundIndex].soundBits & SOUND_CONSTANT_FREQUENCY)) {
Expand Down Expand Up @@ -1409,6 +1460,17 @@ static void update_game_sound(void) {
// on the same line after preprocessing, and the compiler,
// somehow caring about line numbers, makes it not match (it
// computes function arguments in the wrong order).

// Update surround effect index based on Z depth during playback
if (gSoundMode == SOUND_MODE_SURROUND) {
gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->surroundEffectIndex =
get_sound_surround_effect_index(*sSoundBanks[bank][soundIndex].z);
// Update comb filter gain based on Y height for vertical positioning
gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->combFilterGain =
audio_compute_comb_filter(*sSoundBanks[bank][soundIndex].y);
gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->combFilterSize = 0x28;
}

switch (bank) {
case SOUND_BANK_MOVING:
if (!(sSoundBanks[bank][soundIndex].soundBits & SOUND_CONSTANT_FREQUENCY)) {
Expand Down
1 change: 1 addition & 0 deletions src/audio/external.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#define SOUND_MODE_STEREO 0
#define SOUND_MODE_MONO 3
#define SOUND_MODE_HEADSET 1
#define SOUND_MODE_SURROUND 2

#define SEQ_PLAYER_LEVEL 0 // Level background music
#define SEQ_PLAYER_ENV 1 // Misc music like the puzzle jingle
Expand Down
15 changes: 14 additions & 1 deletion src/audio/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,9 @@ struct SequenceChannel {
u8 unkSH06; // some priority
#endif
/*0x05, 0x06*/ u8 bankId;
/* */ u8 surroundEffectIndex; // Surround depth: 0 = front, 0x7F = behind
/* */ u8 combFilterSize; // Comb filter size (delay in bytes, typically 0x28)
/* */ u16 combFilterGain; // Comb filter gain for surround height effect
#if defined(VERSION_EU) || defined(VERSION_SH)
/* , 0x07*/ u8 reverbIndex;
/* , 0x08, 0x09*/ u8 bookOffset;
Expand Down Expand Up @@ -567,6 +570,7 @@ struct NoteSynthesisState {
/* 0x04*/ u8 reverbVol;
/* 0x05*/ u8 unk5;
#endif
/* */ u8 combFilterNeedsInit; // TRUE if comb filter state needs to be cleared
/*0x04, 0x06*/ u16 samplePosFrac;
/*0x08*/ s32 samplePosInt;
/*0x0C*/ struct NoteSynthesisBuffers *synthesisBuffers;
Expand Down Expand Up @@ -641,6 +645,10 @@ struct Note {
/*0x04, 0x30, 0x30*/ u8 priority;
/* 0x31, 0x31*/ u8 waveId;
/* 0x32, 0x32*/ u8 sampleCountIndex;
/* */ u8 surroundEffectIndex; // Index for surround effect pan position
/* */ u8 pan; // Pan position: 0 = left, 128 = center, 255 = right
/* */ u8 combFilterSize; // Comb filter size (delay in bytes)
/* */ u16 combFilterGain; // Comb filter gain for surround height effect
#ifdef VERSION_SH
/* 0x33*/ u8 bankId;
/* 0x34*/ u8 unkSH34;
Expand Down Expand Up @@ -701,7 +709,11 @@ struct Note {
/*0x3C*/ u16 targetVolLeft; // Q1.15, but will always be non-negative
/*0x3E*/ u16 targetVolRight; // Q1.15, but will always be non-negative
/*0x40*/ u8 reverbVol; // Q1.7
/*0x41*/ u8 unused1; // never read, set to 0x3f
/*0x41*/ u8 surroundEffectIndex; // Index for surround effect pan position
/*0x42*/ u8 pan; // Pan position: 0 = left, 128 = center, 255 = right
/* */ u8 combFilterSize; // Comb filter size (delay in bytes, typically 0x28)
/* */ u8 combFilterNeedsInit; // TRUE if comb filter state needs to be cleared
/* */ u16 combFilterGain; // Comb filter gain for surround height effect
/*0x44*/ struct NoteAttributes attributes;
/*0x54, 0x58*/ struct AdsrState adsr;
/*0x74, 0x7C*/ struct Portamento portamento;
Expand Down Expand Up @@ -731,6 +743,7 @@ struct NoteSynthesisBuffers {
s16 samples[0x40];
#endif
#endif
s16 combFilterState[0x40]; // State buffer for comb filter (stores previous samples for delay)
};

#ifdef VERSION_EU
Expand Down
2 changes: 1 addition & 1 deletion src/audio/mixer.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
#define BUF_U8(a) (rspa.buf.as_u8 + ((a) - 0x450))
#define BUF_S16(a) (rspa.buf.as_s16 + ((a) - 0x450) / sizeof(int16_t))
#else
#define BUF_SIZE 2512
#define BUF_SIZE 2816 // extended to accommodate Surround mode
#define BUF_U8(a) (rspa.buf.as_u8 + (a))
#define BUF_S16(a) (rspa.buf.as_s16 + (a) / sizeof(int16_t))
#endif
Expand Down
43 changes: 42 additions & 1 deletion src/audio/playback.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "synthesis.h"
#include "effects.h"
#include "external.h"
#include <stdio.h>

void note_set_resampling_rate(struct Note *note, f32 resamplingRateInput);

Expand Down Expand Up @@ -46,6 +47,10 @@ void note_set_vel_pan_reverb(struct Note *note, f32 velocity, u8 pan, u8 reverbV
pan &= unkMask;
#endif

// Store pan as u8 (0=left, 128=center, 255=right) for surround effect
// EU pan is 0-127, scale to 0-255
note->pan = pan * 2;

if (note->noteSubEu.stereoHeadsetEffects && gSoundMode == SOUND_MODE_HEADSET) {
#ifdef VERSION_SH
smallPanIndex = pan >> 1;
Expand Down Expand Up @@ -114,6 +119,24 @@ void note_set_vel_pan_reverb(struct Note *note, f32 velocity, u8 pan, u8 reverbV
} else if (gSoundMode == SOUND_MODE_MONO) {
volLeft = 0.707f;
volRight = 0.707f;
} else if (sub->stereoHeadsetEffects && gSoundMode == SOUND_MODE_SURROUND) {
// TEMPORARY: Surround mode behaves like stereo to test if glitch persists
sub->headsetPanLeft = 0;
sub->headsetPanRight = 0;
sub->usesHeadsetPanEffects = FALSE;
volLeft = gStereoPanVolume[pan];
volRight = gStereoPanVolume[127 - pan];
// Use same thresholds as stereo (0x20/0x60)
if (pan < 0x20) {
sub->stereoStrongLeft = TRUE;
sub->stereoStrongRight = FALSE;
} else if (pan > 0x60) {
sub->stereoStrongRight = TRUE;
sub->stereoStrongLeft = FALSE;
} else {
sub->stereoStrongLeft = FALSE;
sub->stereoStrongRight = FALSE;
}
} else {
volLeft = gDefaultPanVolume[pan];
volRight = gDefaultPanVolume[127 - pan];
Expand Down Expand Up @@ -1151,6 +1174,11 @@ void note_init_for_layer(struct Note *note, struct SequenceChannelLayer *seqLaye
#endif
sub->stereoHeadsetEffects = seqLayer->seqChannel->stereoHeadsetEffects;
sub->reverbIndex = seqLayer->seqChannel->reverbIndex & 3;
note->surroundEffectIndex = seqLayer->seqChannel->surroundEffectIndex;
note->combFilterGain = seqLayer->seqChannel->combFilterGain;
note->combFilterSize = seqLayer->seqChannel->combFilterSize;
// EU pan is s32 0-127, scale to u8 0-254
note->pan = (u8)(seqLayer->seqChannel->pan * 2);
}
#else
s32 note_init_for_layer(struct Note *note, struct SequenceChannelLayer *seqLayer) {
Expand All @@ -1172,6 +1200,13 @@ s32 note_init_for_layer(struct Note *note, struct SequenceChannelLayer *seqLayer
build_synthetic_wave(note, seqLayer);
}
note_init(note);
// Copy surround index after note_init to avoid being reset by note_init_volume
note->surroundEffectIndex = seqLayer->seqChannel->surroundEffectIndex;
// Copy comb filter settings for height-based surround effect
note->combFilterGain = seqLayer->seqChannel->combFilterGain;
note->combFilterSize = seqLayer->seqChannel->combFilterSize;
// Non-EU pan is f32 0.0-1.0, scale to u8 0-255
note->pan = (u8)(seqLayer->seqChannel->pan * 255.0f);
return FALSE;
}
#endif
Expand Down Expand Up @@ -1414,6 +1449,9 @@ void note_init_all(void) {
note = &gNotes[i];
#if defined(VERSION_EU) || defined(VERSION_SH)
note->noteSubEu = gZeroNoteSub;
note->combFilterGain = 0;
note->combFilterSize = 0;
note->synthesisState.combFilterNeedsInit = TRUE;
#else
note->enabled = FALSE;
note->stereoStrongRight = FALSE;
Expand All @@ -1437,7 +1475,10 @@ void note_init_all(void) {
note->targetVolLeft = 0;
note->targetVolRight = 0;
note->frequency = 0.0f;
note->unused1 = 0x3f;
note->surroundEffectIndex = 0;
note->combFilterGain = 0;
note->combFilterSize = 0;
note->combFilterNeedsInit = TRUE;
#endif
note->attributes.velocity = 0.0f;
note->adsrVolScale = 0;
Expand Down
3 changes: 3 additions & 0 deletions src/audio/seqplayer.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ void sequence_channel_init(struct SequenceChannel *seqChannel) {
seqChannel->stopSomething2 = FALSE;
seqChannel->hasInstrument = FALSE;
seqChannel->stereoHeadsetEffects = FALSE;
seqChannel->surroundEffectIndex = 0;
seqChannel->combFilterGain = 0;
seqChannel->combFilterSize = 0;
seqChannel->transposition = 0;
seqChannel->largeNotes = FALSE;
#if defined(VERSION_EU) || defined(VERSION_SH)
Expand Down
Loading