Surround support (#642)

* Added surround option.

* Added surround effect.

* Remove printf.

* fix.

---------

Co-authored-by: Lywx <kiritodev01@gmail.com>
This commit is contained in:
tortugaveloz
2026-02-06 07:28:54 +01:00
committed by GitHub
parent c902e200ab
commit d7413601a6
11 changed files with 149 additions and 19 deletions
+1 -3
View File
@@ -287,12 +287,10 @@ enum COLOR_ID {
/**
* @brief Sound mode options
* Option 2 appears to be unused, as such its probably not
* a valid option
*/
#define SOUND_STEREO 0
#define SOUND_HEADPHONES 1
#define SOUND_UNUSED 2
#define SOUND_SURROUND 2
#define SOUND_MONO 3
#define NUM_SOUND_MODES 4
+1
View File
@@ -64,6 +64,7 @@
#define SOUND_MENU_STEREO SOUND_ARG_LOAD(0x49, 0x00, 0x80, 0x24)
#define SOUND_MENU_HEADPHONES SOUND_ARG_LOAD(0x49, 0x00, 0x80, 0x25)
#define SOUND_MENU_SURROUND SOUND_ARG_LOAD(0x49, 0x00, 0x80, 0x25)
#define SOUND_MENU_MONO SOUND_ARG_LOAD(0x49, 0x00, 0x80, 0x29)
/* Staging */
+44
View File
@@ -291,6 +291,42 @@ s8 func_800C16E8(f32 arg0, f32 arg1, u8 cameraId) {
GLOBAL_ASM("asm/non_matchings/audio/external/func_800C16E8.s")
#endif
/**
* Calculate surround effect index from Z position (depth relative to camera).
* Positive Z is behind the camera, negative Z is in front.
*
* 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)
*/
u8 get_sound_surround_effect_index(f32 z) {
f32 absZ;
s32 surroundEffectIndex;
f32 maxZ = 100.0f;
absZ = (z < 0) ? -z : z;
if (absZ > maxZ) {
absZ = maxZ;
}
// 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;
}
f32 func_800C1934(u8 bank, u8 soundId) {
f32 phi_f2;
@@ -325,6 +361,14 @@ void func_800C19D0(u8 arg0, u8 arg1, u8 arg2) {
sp3B = func_800C15D0(arg0, arg1, arg2);
sp34 = func_800C1934(arg0, arg1) * *temp_s0->unk10;
sp33 = func_800C16E8(*temp_s0->unk00[0], *temp_s0->unk08, temp_s0->cameraId);
// Set surround effect index when in surround mode
if (gAudioLibSoundMode == SOUND_MODE_SURROUND) {
struct SequenceChannel* channel = gSequencePlayers[2].channels[arg2];
if (IS_SEQUENCE_CHANNEL_VALID(channel)) {
channel->surroundEffectIndex = get_sound_surround_effect_index(*temp_s0->unk08);
}
}
break;
}
temp_s0_2 = &D_8018EF18[arg2];
+3 -1
View File
@@ -31,8 +31,9 @@
#define SEQUENCE_ARGS(priority, seqId) ((priority << 8) | seqId)
#define SOUND_MODE_STEREO 0
#define SOUND_MODE_MONO 3
#define SOUND_MODE_HEADSET 1
#define SOUND_MODE_SURROUND 2
#define SOUND_MODE_MONO 3
#define SEQ_PLAYER_LEVEL 0 // Level background music
#define SEQ_PLAYER_ENV 1 // Misc music like the puzzle jingle
@@ -204,6 +205,7 @@ void audio_reset_session_eu(OSMesg);
f32 func_800C1480(u8, u8);
s8 func_800C15D0(u8, u8, u8);
s8 func_800C16E8(f32, f32, u8);
u8 get_sound_surround_effect_index(f32 z);
f32 func_800C1934(u8, u8);
void func_800C19D0(u8, u8, u8);
struct Unk_8018EFD8* func_800C1C88(u8, Vec3f, Vec3f, f32*, u8, u32);
+3
View File
@@ -338,6 +338,7 @@ struct SequenceChannel {
/*0x5C, 0x60*/ struct M64ScriptState scriptState;
/*0x78, 0x7C*/ struct AdsrSettings adsr;
/*0x80, 0x84*/ struct NotePool notePool;
/* */ u8 surroundEffectIndex; // Surround depth: 0 = front, 0x7F = behind
}; // size = 0xC0, 0xC4 in EU, 0xD0 in SH
// Also known as a Track, according to debug strings.
@@ -460,6 +461,8 @@ struct Note {
/*0x84, 0x8C*/ struct VibratoState vibratoState;
u8 pad3[8];
/* , 0xB0, 0xB4*/ struct NoteSubEu noteSubEu;
/* */ u8 surroundEffectIndex; // Surround depth: 0 = front, 0x7F = behind
/* */ u8 notePan; // Pan position for surround effect (0=left, 128=center, 255=right)
}; // size = 0xC0, known to be 0xC8 on SH
// While this struct needs to be size 0xA0, its not clear
+9 -1
View File
@@ -20,6 +20,9 @@ void note_set_vel_pan_reverb(struct Note* note, f32 velocity, u8 pan, u8 reverbV
u16 unkMask = ~0x80;
pan &= unkMask;
// Store pan for surround effect
note->notePan = pan;
if (note->noteSubEu.stereoHeadsetEffects && gAudioLibSoundMode == SOUND_MODE_HEADSET) {
smallPanIndex = pan >> 3;
@@ -35,7 +38,7 @@ void note_set_vel_pan_reverb(struct Note* note, f32 velocity, u8 pan, u8 reverbV
volLeft = gHeadsetPanVolume[pan];
volRight = gHeadsetPanVolume[127 - pan];
} else if (sub->stereoHeadsetEffects && gAudioLibSoundMode == SOUND_MODE_STEREO) {
} else if (sub->stereoHeadsetEffects && (gAudioLibSoundMode == SOUND_MODE_STEREO || gAudioLibSoundMode == SOUND_MODE_SURROUND)) {
strongRight = false;
strongLeft = false;
sub->headsetPanRight = 0;
@@ -316,6 +319,11 @@ void process_notes(void) {
pan = playbackState->parentLayer->notePan;
reverbVol = playbackState->parentLayer->seqChannel->reverbVol;
bookOffset = playbackState->parentLayer->seqChannel->bookOffset & 0x7;
// Transfer surround effect index from channel to note
if (gAudioLibSoundMode == SOUND_MODE_SURROUND) {
note->surroundEffectIndex = playbackState->parentLayer->seqChannel->surroundEffectIndex;
}
}
frequency *= playbackState->vibratoFreqScale * playbackState->portamentoFreqScale;
+71
View File
@@ -7,6 +7,7 @@
#include "audio/load.h"
#include "audio/seqplayer.h"
#include "audio/internal.h"
#include "audio/external.h"
#include "port/Engine.h"
#include <libultra/abi.h>
@@ -617,6 +618,12 @@ Acmd* synthesis_process_note(s32 noteIndex, struct NoteSubEu* noteSubEu, struct
// synthesisState->restart = 1;
cmd = note_apply_headset_pan_effects(cmd, noteSubEu, synthesisState, inBuf * 2, flags, leftRight);
}
// Apply surround effect for notes with spatial effects enabled when in surround mode
if (noteSubEu->stereoHeadsetEffects && gAudioLibSoundMode == SOUND_MODE_SURROUND) {
cmd = note_apply_surround_effect(cmd, note, inBuf);
}
return cmd;
}
#else
@@ -789,3 +796,67 @@ Acmd* note_apply_headset_pan_effects(Acmd* acmd, struct NoteSubEu* noteSubEu, st
return acmd;
}
/**
* Apply surround sound effect using matrix encoding based on depth position.
* Uses surroundEffectIndex (0x00-0x7F) calculated from Z position:
* 0x00-0x3F: Sound in front (0 = far front, 0x3F = at camera) - less rear effect
* 0x40-0x7F: Sound behind (0x40 = at camera, 0x7F = far behind) - more rear effect
*
* This creates a rear channel effect by phase-inverting and mixing based on pan and depth.
*/
Acmd* note_apply_surround_effect(Acmd* cmd, struct Note* note, s32 bufLen) {
s16 dryGain;
s32 wetGain;
f32 depthFactor;
u8 surroundIdx = note->surroundEffectIndex;
struct NoteSubEu* sub = &note->noteSubEu;
// Calculate depth factor: how much rear channel to add
// surroundEffectIndex: 0 = front, 0x3F = at camera, 0x7F = far behind
// We want sounds behind the camera to have stronger rear channel effect
depthFactor = (f32)surroundIdx / 127.0f;
// Convert u8 pan (0=left, 64=center, 127=right) to float (0.0-1.0)
f32 panPosition = (f32)note->notePan / 127.0f;
// Calculate base gain from current volume and depth
dryGain = sub->targetVolLeft > sub->targetVolRight ? sub->targetVolLeft : sub->targetVolRight;
dryGain = (s16)(dryGain * depthFactor); // Scale by depth
dryGain = dryGain >> 2; // Scale down for subtle effect
if (dryGain > 0x1800) {
dryGain = 0x1800; // Limit surround intensity
}
// Skip if gain is too low
if (dryGain < 0x100) {
return cmd;
}
// Matrix surround encoding: steer surround based on pan
// The idea: add out-of-phase content to create width/depth
// Left-panned sounds get positive left, negative right (spreads to rear left)
// Right-panned sounds get negative left, positive right (spreads to rear right)
s16 leftGain = (s16)(dryGain * (1.0f - panPosition));
s16 rightGain = (s16)(dryGain * panPosition);
// Mix surround contribution into channels
// Left channel gets positive surround from left-panned content
aSetBuffer(cmd++, 0, 0, 0, bufLen * 2);
aMix(cmd++, 0, leftGain, DMEM_ADDR_LEFT_CH, DMEM_ADDR_LEFT_CH);
// Right channel gets phase-inverted surround contribution for matrix encoding
aMix(cmd++, 0, (s16)(rightGain ^ 0xFFFF), DMEM_ADDR_RIGHT_CH, DMEM_ADDR_RIGHT_CH);
// Apply to wet (reverb) channels for consistent spatialization
wetGain = (dryGain * sub->reverbVol) >> 7;
if (wetGain > 0) {
s16 wetLeftGain = (s16)(wetGain * (1.0f - panPosition));
s16 wetRightGain = (s16)(wetGain * panPosition);
aMix(cmd++, 0, wetLeftGain, DMEM_ADDR_WET_LEFT_CH, DMEM_ADDR_WET_LEFT_CH);
aMix(cmd++, 0, (s16)(wetRightGain ^ 0xFFFF), DMEM_ADDR_WET_RIGHT_CH, DMEM_ADDR_WET_RIGHT_CH);
}
return cmd;
}
+1
View File
@@ -114,6 +114,7 @@ Acmd* func_800B86A0(Acmd* cmd, struct NoteSubEu* note, struct NoteSynthesisState
u16 inBuf, s32 headsetPanSettings, u32 flags);
Acmd* note_apply_headset_pan_effects(Acmd* acmd, struct NoteSubEu* noteSubEu, struct NoteSynthesisState* note,
s32 bufLen, s32 flags, s32 leftRight);
Acmd* note_apply_surround_effect(Acmd* cmd, struct Note* note, s32 bufLen);
extern struct SynthesisReverb gSynthesisReverbs[4];
+2 -2
View File
@@ -384,11 +384,11 @@ char* gDebugScreenModeNames[] = {
char* gDebugSoundModeNames[] = {
"stereo",
"head phone",
"xxx",
"surround",
"monaural",
};
char* gSoundModeNames[NUM_SOUND_MODES] = { "STEREO", "HEADPHONE", "", "MONO" };
char* gSoundModeNames[NUM_SOUND_MODES] = { "STEREO", "HEADPHONE", "SURROUND", "MONO" };
char* gWinLoseText[] = {
"WINNER!",
+13 -11
View File
@@ -1,4 +1,5 @@
#include <libultraship.h>
#include <libultraship/bridge/audiobridge.h>
#include <macros.h>
#include <defines.h>
#include <common_structs.h>
@@ -152,7 +153,7 @@ const s8 unref_800F2BDC[4] = { 1, 0, 0, 0 };
// from sScreenModePlayerTable, note the 2 is not set since that's for vertical 2p screen
const s8 sScreenModeIdxFromPlayerMode[4] = { 0, 1, 3, 4 };
const union GameModePack sSoundMenuPack = { { SOUND_STEREO, SOUND_HEADPHONES, SOUND_UNUSED, SOUND_MONO } };
const union GameModePack sSoundMenuPack = { { SOUND_STEREO, SOUND_HEADPHONES, SOUND_SURROUND, SOUND_MONO } };
/**************************/
@@ -298,9 +299,6 @@ void options_menu_act(struct Controller* controller, u16 controllerIdx) {
} else {
gSoundMode = SOUND_STEREO;
}
if (gSoundMode == SOUND_UNUSED) {
gSoundMode = SOUND_MONO;
}
set_sound_mode();
switch (gSoundMode) {
case SOUND_STEREO:
@@ -309,6 +307,9 @@ void options_menu_act(struct Controller* controller, u16 controllerIdx) {
case SOUND_HEADPHONES:
play_sound2(SOUND_MENU_HEADPHONES);
return;
case SOUND_SURROUND:
play_sound2(SOUND_MENU_SURROUND);
return;
case SOUND_MONO:
play_sound2(SOUND_MENU_MONO);
return;
@@ -1146,9 +1147,6 @@ void splash_menu_act(struct Controller* controller, u16 controllerIdx) {
case DEBUG_MENU_SOUND_MODE: {
if ((btnAndStick & R_JPAD) && (gSoundMode < 3)) {
gSoundMode += 1;
if (gSoundMode == SOUND_UNUSED) {
gSoundMode = SOUND_MONO;
}
play_sound2(SOUND_MENU_CURSOR_MOVE);
set_sound_mode();
gSaveData.main.saveInfo.soundMode = gSoundMode;
@@ -1157,9 +1155,6 @@ void splash_menu_act(struct Controller* controller, u16 controllerIdx) {
}
if ((btnAndStick & L_JPAD) && (gSoundMode > 0)) {
gSoundMode -= 1;
if (gSoundMode == SOUND_UNUSED) {
gSoundMode = SOUND_HEADPHONES;
}
play_sound2(SOUND_MENU_CURSOR_MOVE);
set_sound_mode();
gSaveData.main.saveInfo.soundMode = gSoundMode;
@@ -2078,8 +2073,15 @@ void set_sound_mode(void) {
union GameModePack pack;
pack = sSoundMenuPack;
if ((gSoundMode == SOUND_STEREO) || (gSoundMode == SOUND_HEADPHONES) || (gSoundMode == SOUND_MONO)) {
if ((gSoundMode == SOUND_STEREO) || (gSoundMode == SOUND_HEADPHONES) ||
(gSoundMode == SOUND_SURROUND) || (gSoundMode == SOUND_MONO)) {
func_800C3448(pack.modes[gSoundMode] | 0xE0000000);
if (gSoundMode == SOUND_SURROUND) {
SetAudioChannels(audioMatrix51);
} else {
SetAudioChannels(audioStereo);
}
}
}