Skip to content

Commit 89821ea

Browse files
authored
Surround support (#142)
* Trying adding surround. * First attempt at making surround effects. * Fix. * fix. * Add cehck of stereoHeadsetEffects. * Fix surround effects. * Add comb filter. * comb filter. * Add back printf.
1 parent 814efd7 commit 89821ea

14 files changed

Lines changed: 342 additions & 15 deletions

src/audio/effects.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,3 +542,30 @@ s32 adsr_update(struct AdsrState *adsr) {
542542
return 0;
543543
#endif
544544
}
545+
546+
/**
547+
* Compute comb filter gain based on Y position (height relative to camera).
548+
* Used for creating height-based audio effects in surround sound.
549+
*
550+
* Returns:
551+
* -32 to -1 (0xE0-0xFF): Sound is below camera (negative Y)
552+
* 0 to 127: Sound is above camera (positive Y)
553+
* Bit 0 is always set (odd number)
554+
*/
555+
s8 audio_compute_comb_filter(f32 posY) {
556+
s8 combFilterGain;
557+
558+
if (posY < 0.0f) {
559+
// Below camera
560+
if (posY < -625.0f) {
561+
combFilterGain = -32; // Very far below
562+
} else {
563+
combFilterGain = (s8)(((625.0f + posY) / 625.0f) * 31.0f) + 0xE0;
564+
}
565+
} else if (posY > 1250.0f) {
566+
combFilterGain = 127; // Very far above
567+
} else {
568+
combFilterGain = (s8)((posY / 1250.0f) * 126.0f);
569+
}
570+
return combFilterGain | 1;
571+
}

src/audio/effects.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,6 @@ f32 adsr_update(struct AdsrState *adsr);
4343
s32 adsr_update(struct AdsrState *adsr);
4444
#endif
4545

46+
s8 audio_compute_comb_filter(f32 posY);
47+
4648
#endif // AUDIO_EFFECTS_H

src/audio/external.c

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77
#include "external.h"
88
#include "playback.h"
99
#include "synthesis.h"
10+
#include "effects.h"
1011
#include "game/level_update.h"
1112
#include "game/object_list_processor.h"
1213
#include "game/camera.h"
1314
#include "seq_ids.h"
1415
#include "dialog_ids.h"
1516
#include <lib/src/osAi.h>
17+
#include <stdio.h>
1618

1719
#if defined(VERSION_EU) || defined(VERSION_SH)
1820
#define EU_FLOAT(x) x##f
@@ -1045,6 +1047,45 @@ static f32 get_sound_pan(f32 x, f32 z) {
10451047
return pan;
10461048
}
10471049

1050+
/**
1051+
* Calculate surround effect index from Z position (depth relative to camera).
1052+
* In SM64's coordinate system, positive Z is behind the camera, negative Z is in front.
1053+
* Uses AUDIO_MAX_DISTANCE for scaling, matching pan and volume distance calculations.
1054+
*
1055+
* Returns:
1056+
* 0x00 - 0x3F: Sound is in front of camera (0 = far front, 0x3F = at camera)
1057+
* 0x40 - 0x7F: Sound is behind camera (0x40 = at camera, 0x7F = far behind)
1058+
*
1059+
* Called from threads: thread4_sound, thread5_game_loop (EU only)
1060+
*/
1061+
static u8 get_sound_surround_effect_index(f32 z) {
1062+
f32 absZ;
1063+
s32 surroundEffectIndex;
1064+
f32 maxZ = 1000.f;
1065+
1066+
absZ = (z < 0 ? -z : z);
1067+
if (absZ > maxZ) {
1068+
absZ = maxZ;
1069+
}
1070+
1071+
// In SM64, positive z means behind the camera
1072+
if (z > 0.0f) {
1073+
// Behind camera - surround effect index 0x3F (at camera) to 0x7F (far behind)
1074+
surroundEffectIndex = (s32)((absZ / maxZ) * 64.0f) + 0x3F;
1075+
if (surroundEffectIndex > 0x7F) {
1076+
surroundEffectIndex = 0x7F;
1077+
}
1078+
} else {
1079+
// In front of camera - surround effect index 0x3F (at camera) to 0x00 (far front)
1080+
surroundEffectIndex = 0x3F - (s32)((absZ / maxZ) * 63.0f);
1081+
if (surroundEffectIndex < 0) {
1082+
surroundEffectIndex = 0;
1083+
}
1084+
}
1085+
1086+
return (u8)surroundEffectIndex;
1087+
}
1088+
10481089
/**
10491090
* Called from threads: thread4_sound, thread5_game_loop (EU only)
10501091
*/
@@ -1225,6 +1266,16 @@ static void update_game_sound(void) {
12251266
gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->soundScriptIO[4] = soundId;
12261267
gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->soundScriptIO[0] = 1;
12271268

1269+
// Set surround effect index based on Z depth when starting sound
1270+
if (gSoundMode == SOUND_MODE_SURROUND) {
1271+
gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->surroundEffectIndex =
1272+
get_sound_surround_effect_index(*sSoundBanks[bank][soundIndex].z);
1273+
// Set comb filter gain based on Y height for vertical positioning
1274+
gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->combFilterGain =
1275+
audio_compute_comb_filter(*sSoundBanks[bank][soundIndex].y);
1276+
gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->combFilterSize = 16;
1277+
}
1278+
12281279
switch (bank) {
12291280
case SOUND_BANK_MOVING:
12301281
if (!(sSoundBanks[bank][soundIndex].soundBits & SOUND_CONSTANT_FREQUENCY)) {
@@ -1409,6 +1460,17 @@ static void update_game_sound(void) {
14091460
// on the same line after preprocessing, and the compiler,
14101461
// somehow caring about line numbers, makes it not match (it
14111462
// computes function arguments in the wrong order).
1463+
1464+
// Update surround effect index based on Z depth during playback
1465+
if (gSoundMode == SOUND_MODE_SURROUND) {
1466+
gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->surroundEffectIndex =
1467+
get_sound_surround_effect_index(*sSoundBanks[bank][soundIndex].z);
1468+
// Update comb filter gain based on Y height for vertical positioning
1469+
gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->combFilterGain =
1470+
audio_compute_comb_filter(*sSoundBanks[bank][soundIndex].y);
1471+
gSequencePlayers[SEQ_PLAYER_SFX].channels[channelIndex]->combFilterSize = 0x28;
1472+
}
1473+
14121474
switch (bank) {
14131475
case SOUND_BANK_MOVING:
14141476
if (!(sSoundBanks[bank][soundIndex].soundBits & SOUND_CONSTANT_FREQUENCY)) {

src/audio/external.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#define SOUND_MODE_STEREO 0
1414
#define SOUND_MODE_MONO 3
1515
#define SOUND_MODE_HEADSET 1
16+
#define SOUND_MODE_SURROUND 2
1617

1718
#define SEQ_PLAYER_LEVEL 0 // Level background music
1819
#define SEQ_PLAYER_ENV 1 // Misc music like the puzzle jingle

src/audio/internal.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,9 @@ struct SequenceChannel {
446446
u8 unkSH06; // some priority
447447
#endif
448448
/*0x05, 0x06*/ u8 bankId;
449+
/* */ u8 surroundEffectIndex; // Surround depth: 0 = front, 0x7F = behind
450+
/* */ u8 combFilterSize; // Comb filter size (delay in bytes, typically 0x28)
451+
/* */ u16 combFilterGain; // Comb filter gain for surround height effect
449452
#if defined(VERSION_EU) || defined(VERSION_SH)
450453
/* , 0x07*/ u8 reverbIndex;
451454
/* , 0x08, 0x09*/ u8 bookOffset;
@@ -567,6 +570,7 @@ struct NoteSynthesisState {
567570
/* 0x04*/ u8 reverbVol;
568571
/* 0x05*/ u8 unk5;
569572
#endif
573+
/* */ u8 combFilterNeedsInit; // TRUE if comb filter state needs to be cleared
570574
/*0x04, 0x06*/ u16 samplePosFrac;
571575
/*0x08*/ s32 samplePosInt;
572576
/*0x0C*/ struct NoteSynthesisBuffers *synthesisBuffers;
@@ -641,6 +645,10 @@ struct Note {
641645
/*0x04, 0x30, 0x30*/ u8 priority;
642646
/* 0x31, 0x31*/ u8 waveId;
643647
/* 0x32, 0x32*/ u8 sampleCountIndex;
648+
/* */ u8 surroundEffectIndex; // Index for surround effect pan position
649+
/* */ u8 pan; // Pan position: 0 = left, 128 = center, 255 = right
650+
/* */ u8 combFilterSize; // Comb filter size (delay in bytes)
651+
/* */ u16 combFilterGain; // Comb filter gain for surround height effect
644652
#ifdef VERSION_SH
645653
/* 0x33*/ u8 bankId;
646654
/* 0x34*/ u8 unkSH34;
@@ -701,7 +709,11 @@ struct Note {
701709
/*0x3C*/ u16 targetVolLeft; // Q1.15, but will always be non-negative
702710
/*0x3E*/ u16 targetVolRight; // Q1.15, but will always be non-negative
703711
/*0x40*/ u8 reverbVol; // Q1.7
704-
/*0x41*/ u8 unused1; // never read, set to 0x3f
712+
/*0x41*/ u8 surroundEffectIndex; // Index for surround effect pan position
713+
/*0x42*/ u8 pan; // Pan position: 0 = left, 128 = center, 255 = right
714+
/* */ u8 combFilterSize; // Comb filter size (delay in bytes, typically 0x28)
715+
/* */ u8 combFilterNeedsInit; // TRUE if comb filter state needs to be cleared
716+
/* */ u16 combFilterGain; // Comb filter gain for surround height effect
705717
/*0x44*/ struct NoteAttributes attributes;
706718
/*0x54, 0x58*/ struct AdsrState adsr;
707719
/*0x74, 0x7C*/ struct Portamento portamento;
@@ -731,6 +743,7 @@ struct NoteSynthesisBuffers {
731743
s16 samples[0x40];
732744
#endif
733745
#endif
746+
s16 combFilterState[0x40]; // State buffer for comb filter (stores previous samples for delay)
734747
};
735748

736749
#ifdef VERSION_EU

src/audio/mixer.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
#define BUF_U8(a) (rspa.buf.as_u8 + ((a) - 0x450))
3636
#define BUF_S16(a) (rspa.buf.as_s16 + ((a) - 0x450) / sizeof(int16_t))
3737
#else
38-
#define BUF_SIZE 2512
38+
#define BUF_SIZE 2816 // extended to accommodate Surround mode
3939
#define BUF_U8(a) (rspa.buf.as_u8 + (a))
4040
#define BUF_S16(a) (rspa.buf.as_s16 + (a) / sizeof(int16_t))
4141
#endif

src/audio/playback.c

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "synthesis.h"
1010
#include "effects.h"
1111
#include "external.h"
12+
#include <stdio.h>
1213

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

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

50+
// Store pan as u8 (0=left, 128=center, 255=right) for surround effect
51+
// EU pan is 0-127, scale to 0-255
52+
note->pan = pan * 2;
53+
4954
if (note->noteSubEu.stereoHeadsetEffects && gSoundMode == SOUND_MODE_HEADSET) {
5055
#ifdef VERSION_SH
5156
smallPanIndex = pan >> 1;
@@ -114,6 +119,24 @@ void note_set_vel_pan_reverb(struct Note *note, f32 velocity, u8 pan, u8 reverbV
114119
} else if (gSoundMode == SOUND_MODE_MONO) {
115120
volLeft = 0.707f;
116121
volRight = 0.707f;
122+
} else if (sub->stereoHeadsetEffects && gSoundMode == SOUND_MODE_SURROUND) {
123+
// TEMPORARY: Surround mode behaves like stereo to test if glitch persists
124+
sub->headsetPanLeft = 0;
125+
sub->headsetPanRight = 0;
126+
sub->usesHeadsetPanEffects = FALSE;
127+
volLeft = gStereoPanVolume[pan];
128+
volRight = gStereoPanVolume[127 - pan];
129+
// Use same thresholds as stereo (0x20/0x60)
130+
if (pan < 0x20) {
131+
sub->stereoStrongLeft = TRUE;
132+
sub->stereoStrongRight = FALSE;
133+
} else if (pan > 0x60) {
134+
sub->stereoStrongRight = TRUE;
135+
sub->stereoStrongLeft = FALSE;
136+
} else {
137+
sub->stereoStrongLeft = FALSE;
138+
sub->stereoStrongRight = FALSE;
139+
}
117140
} else {
118141
volLeft = gDefaultPanVolume[pan];
119142
volRight = gDefaultPanVolume[127 - pan];
@@ -1151,6 +1174,11 @@ void note_init_for_layer(struct Note *note, struct SequenceChannelLayer *seqLaye
11511174
#endif
11521175
sub->stereoHeadsetEffects = seqLayer->seqChannel->stereoHeadsetEffects;
11531176
sub->reverbIndex = seqLayer->seqChannel->reverbIndex & 3;
1177+
note->surroundEffectIndex = seqLayer->seqChannel->surroundEffectIndex;
1178+
note->combFilterGain = seqLayer->seqChannel->combFilterGain;
1179+
note->combFilterSize = seqLayer->seqChannel->combFilterSize;
1180+
// EU pan is s32 0-127, scale to u8 0-254
1181+
note->pan = (u8)(seqLayer->seqChannel->pan * 2);
11541182
}
11551183
#else
11561184
s32 note_init_for_layer(struct Note *note, struct SequenceChannelLayer *seqLayer) {
@@ -1172,6 +1200,13 @@ s32 note_init_for_layer(struct Note *note, struct SequenceChannelLayer *seqLayer
11721200
build_synthetic_wave(note, seqLayer);
11731201
}
11741202
note_init(note);
1203+
// Copy surround index after note_init to avoid being reset by note_init_volume
1204+
note->surroundEffectIndex = seqLayer->seqChannel->surroundEffectIndex;
1205+
// Copy comb filter settings for height-based surround effect
1206+
note->combFilterGain = seqLayer->seqChannel->combFilterGain;
1207+
note->combFilterSize = seqLayer->seqChannel->combFilterSize;
1208+
// Non-EU pan is f32 0.0-1.0, scale to u8 0-255
1209+
note->pan = (u8)(seqLayer->seqChannel->pan * 255.0f);
11751210
return FALSE;
11761211
}
11771212
#endif
@@ -1414,6 +1449,9 @@ void note_init_all(void) {
14141449
note = &gNotes[i];
14151450
#if defined(VERSION_EU) || defined(VERSION_SH)
14161451
note->noteSubEu = gZeroNoteSub;
1452+
note->combFilterGain = 0;
1453+
note->combFilterSize = 0;
1454+
note->synthesisState.combFilterNeedsInit = TRUE;
14171455
#else
14181456
note->enabled = FALSE;
14191457
note->stereoStrongRight = FALSE;
@@ -1437,7 +1475,10 @@ void note_init_all(void) {
14371475
note->targetVolLeft = 0;
14381476
note->targetVolRight = 0;
14391477
note->frequency = 0.0f;
1440-
note->unused1 = 0x3f;
1478+
note->surroundEffectIndex = 0;
1479+
note->combFilterGain = 0;
1480+
note->combFilterSize = 0;
1481+
note->combFilterNeedsInit = TRUE;
14411482
#endif
14421483
note->attributes.velocity = 0.0f;
14431484
note->adsrVolScale = 0;

src/audio/seqplayer.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ void sequence_channel_init(struct SequenceChannel *seqChannel) {
3737
seqChannel->stopSomething2 = FALSE;
3838
seqChannel->hasInstrument = FALSE;
3939
seqChannel->stereoHeadsetEffects = FALSE;
40+
seqChannel->surroundEffectIndex = 0;
41+
seqChannel->combFilterGain = 0;
42+
seqChannel->combFilterSize = 0;
4043
seqChannel->transposition = 0;
4144
seqChannel->largeNotes = FALSE;
4245
#if defined(VERSION_EU) || defined(VERSION_SH)

0 commit comments

Comments
 (0)