mirror of
https://github.com/HarbourMasters/SpaghettiKart
synced 2026-07-09 23:02:11 -04:00
Surround support (#642)
* Added surround option. * Added surround effect. * Remove printf. * fix. --------- Co-authored-by: Lywx <kiritodev01@gmail.com>
This commit is contained in:
+1
-3
@@ -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
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
+1
-1
Submodule libultraship updated: 41eeeb247e...f40cfd33b8
@@ -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];
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = ¬e->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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user