This commit is contained in:
Ziemas 2025-12-13 18:05:51 -06:00 committed by GitHub
commit ea51820837
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 68 additions and 61 deletions

View File

@ -247,7 +247,6 @@ set(pcsx2SPU2Sources
SPU2/ADSR.cpp
SPU2/Debug.cpp
SPU2/Dma.cpp
SPU2/Mixer.cpp
SPU2/spu2.cpp
SPU2/ReadInput.cpp
SPU2/RegTable.cpp
@ -258,6 +257,7 @@ set(pcsx2SPU2Sources
)
set(pcsx2SPU2SourcesUnshared
SPU2/Mixer.cpp
SPU2/ReverbResample.cpp
)

View File

@ -9,6 +9,28 @@
#include "common/Assertions.h"
// LOOP/END sets the ENDX bit and sets NAX to LSA, and the voice is muted if LOOP is not set
// LOOP seems to only have any effect on the block with LOOP/END set, where it prevents muting the voice
// (the documented requirement that every block in a loop has the LOOP bit set is nonsense according to tests)
// LOOP/START sets LSA to NAX unless LSA was written manually since sound generation started
// (see LoopMode, the method by which this is achieved on the real SPU2 is unknown)
#define XAFLAG_LOOP_END (1ul << 0)
#define XAFLAG_LOOP (1ul << 1)
#define XAFLAG_LOOP_START (1ul << 2)
#if MULTI_ISA_COMPILE_ONCE
// decoded pcm data, used to cache the decoded data so that it needn't be decoded
// multiple times. Cache chunks are decoded when the mixer requests the blocks, and
// invalided when DMA transfers and memory writes are performed.
PcmCacheEntry pcm_cache_data[pcm_BlockCount];
int g_counter_cache_hits = 0;
int g_counter_cache_misses = 0;
int g_counter_cache_ignores = 0;
#endif
MULTI_ISA_UNSHARED_START
static const s32 tbl_XA_Factor[16][2] =
{
{0, 0},
@ -71,24 +93,6 @@ static void __forceinline IncrementNextA(V_Core& thiscore, uint voiceidx)
vc.NextA &= 0xFFFFF;
}
// decoded pcm data, used to cache the decoded data so that it needn't be decoded
// multiple times. Cache chunks are decoded when the mixer requests the blocks, and
// invalided when DMA transfers and memory writes are performed.
PcmCacheEntry pcm_cache_data[pcm_BlockCount];
int g_counter_cache_hits = 0;
int g_counter_cache_misses = 0;
int g_counter_cache_ignores = 0;
// LOOP/END sets the ENDX bit and sets NAX to LSA, and the voice is muted if LOOP is not set
// LOOP seems to only have any effect on the block with LOOP/END set, where it prevents muting the voice
// (the documented requirement that every block in a loop has the LOOP bit set is nonsense according to tests)
// LOOP/START sets LSA to NAX unless LSA was written manually since sound generation started
// (see LoopMode, the method by which this is achieved on the real SPU2 is unknown)
#define XAFLAG_LOOP_END (1ul << 0)
#define XAFLAG_LOOP (1ul << 1)
#define XAFLAG_LOOP_START (1ul << 2)
static __forceinline s32 GetNextDataBuffered(V_Core& thiscore, uint voiceidx)
{
V_Voice& vc(thiscore.Voices[voiceidx]);
@ -434,8 +438,6 @@ static __forceinline StereoOut32 MixVoice(uint coreidx, uint voiceidx)
return voiceOut;
}
const VoiceMixSet VoiceMixSet::Empty((StereoOut32()), (StereoOut32())); // Don't use SteroOut32::Empty because C++ doesn't make any dep/order checks on global initializers.
static __forceinline void MixCoreVoices(VoiceMixSet& dest, const uint coreidx)
{
V_Core& thiscore(Cores[coreidx]);
@ -453,40 +455,42 @@ static __forceinline void MixCoreVoices(VoiceMixSet& dest, const uint coreidx)
}
}
StereoOut32 V_Core::Mix(const VoiceMixSet& inVoices, const StereoOut32& Input, const StereoOut32& Ext)
static __forceinline StereoOut32 MixCore(const uint coreidx, const VoiceMixSet& inVoices, const StereoOut32& Input, const StereoOut32& Ext)
{
MasterVol.Update();
UpdateNoise(*this);
V_Core& thiscore(Cores[coreidx]);
thiscore.MasterVol.Update();
UpdateNoise(thiscore);
// Saturate final result to standard 16 bit range.
const VoiceMixSet Voices(clamp_mix(inVoices.Dry), clamp_mix(inVoices.Wet));
// Write Mixed results To Output Area
spu2M_WriteFast(((0 == Index) ? 0x1000 : 0x1800) + OutPos, Voices.Dry.Left);
spu2M_WriteFast(((0 == Index) ? 0x1200 : 0x1A00) + OutPos, Voices.Dry.Right);
spu2M_WriteFast(((0 == Index) ? 0x1400 : 0x1C00) + OutPos, Voices.Wet.Left);
spu2M_WriteFast(((0 == Index) ? 0x1600 : 0x1E00) + OutPos, Voices.Wet.Right);
spu2M_WriteFast(((0 == thiscore.Index) ? 0x1000 : 0x1800) + OutPos, Voices.Dry.Left);
spu2M_WriteFast(((0 == thiscore.Index) ? 0x1200 : 0x1A00) + OutPos, Voices.Dry.Right);
spu2M_WriteFast(((0 == thiscore.Index) ? 0x1400 : 0x1C00) + OutPos, Voices.Wet.Left);
spu2M_WriteFast(((0 == thiscore.Index) ? 0x1600 : 0x1E00) + OutPos, Voices.Wet.Right);
// Write mixed results to logfile (if enabled)
#ifdef PCSX2_DEVBUILD
WaveDump::WriteCore(Index, CoreSrc_DryVoiceMix, Voices.Dry);
WaveDump::WriteCore(Index, CoreSrc_WetVoiceMix, Voices.Wet);
WaveDump::WriteCore(thiscore.Index, CoreSrc_DryVoiceMix, Voices.Dry);
WaveDump::WriteCore(thiscore.Index, CoreSrc_WetVoiceMix, Voices.Wet);
#endif
// Mix in the Input data
StereoOut32 TD(
Input.Left & DryGate.InpL,
Input.Right & DryGate.InpR);
Input.Left & thiscore.DryGate.InpL,
Input.Right & thiscore.DryGate.InpR);
// Mix in the Voice data
TD.Left += Voices.Dry.Left & DryGate.SndL;
TD.Right += Voices.Dry.Right & DryGate.SndR;
TD.Left += Voices.Dry.Left & thiscore.DryGate.SndL;
TD.Right += Voices.Dry.Right & thiscore.DryGate.SndR;
// Mix in the External (nothing/core0) data
TD.Left += Ext.Left & DryGate.ExtL;
TD.Right += Ext.Right & DryGate.ExtR;
TD.Left += Ext.Left & thiscore.DryGate.ExtL;
TD.Right += Ext.Right & thiscore.DryGate.ExtR;
// ----------------------------------------------------------------------------
// Reverberation Effects Processing
@ -510,27 +514,27 @@ StereoOut32 V_Core::Mix(const VoiceMixSet& inVoices, const StereoOut32& Input, c
// Mix Input, Voice, and External data:
TW.Left = Input.Left & WetGate.InpL;
TW.Right = Input.Right & WetGate.InpR;
TW.Left = Input.Left & thiscore.WetGate.InpL;
TW.Right = Input.Right & thiscore.WetGate.InpR;
TW.Left += Voices.Wet.Left & WetGate.SndL;
TW.Right += Voices.Wet.Right & WetGate.SndR;
TW.Left += Ext.Left & WetGate.ExtL;
TW.Right += Ext.Right & WetGate.ExtR;
TW.Left += Voices.Wet.Left & thiscore.WetGate.SndL;
TW.Right += Voices.Wet.Right & thiscore.WetGate.SndR;
TW.Left += Ext.Left & thiscore.WetGate.ExtL;
TW.Right += Ext.Right & thiscore.WetGate.ExtR;
#ifdef PCSX2_DEVBUILD
WaveDump::WriteCore(Index, CoreSrc_PreReverb, TW);
WaveDump::WriteCore(thiscore.Index, CoreSrc_PreReverb, TW);
#endif
StereoOut32 RV = DoReverb(TW);
StereoOut32 RV = thiscore.DoReverb(TW);
#ifdef PCSX2_DEVBUILD
WaveDump::WriteCore(Index, CoreSrc_PostReverb, RV);
WaveDump::WriteCore(thiscore.Index, CoreSrc_PostReverb, RV);
#endif
// Mix Dry + Wet
// (master volume is applied later to the result of both outputs added together).
return TD + ApplyVolume(RV, FxVol);
return TD + ApplyVolume(RV, thiscore.FxVol);
}
static StereoOut32 DCFilter(StereoOut32 input) {
@ -546,7 +550,7 @@ static StereoOut32 DCFilter(StereoOut32 input) {
return output;
}
__forceinline void spu2Mix()
void spu2Mix()
{
// Note: Playmode 4 is SPDIF, which overrides other inputs.
StereoOut32 InputData[2] =
@ -566,11 +570,12 @@ __forceinline void spu2Mix()
#endif
// Todo: Replace me with memzero initializer!
VoiceMixSet VoiceData[2] = {VoiceMixSet::Empty, VoiceMixSet::Empty}; // mixed voice data for each core.
VoiceMixSet VoiceData[2] = {{StereoOut32(), StereoOut32()}, {StereoOut32(), StereoOut32()}}; // mixed voice data for each core.
MixCoreVoices(VoiceData[0], 0);
MixCoreVoices(VoiceData[1], 1);
StereoOut32 Ext(Cores[0].Mix(VoiceData[0], InputData[0], StereoOut32::Empty));
StereoOut32 Ext(MixCore(0, VoiceData[0], InputData[0], StereoOut32::Empty));
if ((PlayMode & 4) || (Cores[0].Mute != 0))
Ext = StereoOut32::Empty;
@ -588,7 +593,7 @@ __forceinline void spu2Mix()
#endif
Ext = ApplyVolume(Ext, Cores[1].ExtVol);
StereoOut32 Out(Cores[1].Mix(VoiceData[1], InputData[1], Ext));
StereoOut32 Out(MixCore(1, VoiceData[1], InputData[1], Ext));
if (PlayMode & 8)
{
@ -645,3 +650,5 @@ __forceinline void spu2Mix()
}
}
}
MULTI_ISA_UNSHARED_END

View File

@ -63,11 +63,12 @@ struct StereoOut32
}
};
extern void (*spu2Mix)();
extern s16* GetMemPtr(u32 addr);
extern s16 spu2M_Read(u32 addr);
extern void spu2M_Write(u32 addr, s16 value);
extern void spu2M_Write(u32 addr, u16 value);
extern void spu2Mix();
MULTI_ISA_DEF(void spu2Mix();)
extern void spu2Output(StereoOut32 out);
static __forceinline s16 SignExtend16(u16 v)
@ -404,7 +405,6 @@ struct V_CoreGates
struct VoiceMixSet
{
static const VoiceMixSet Empty;
StereoOut32 Dry, Wet;
VoiceMixSet() {}
@ -491,11 +491,6 @@ struct V_Core
u16 psxSoundDataTransferControl;
u16 psxSPUSTAT;
// HACK -- This is a temp buffer which is (or isn't?) used to circumvent some memory
// corruption that originates elsewhere. >_< The actual ADMA buffer
// is an area mapped to SPU2 main memory.
//s16 ADMATempBuffer[0x1000];
// ----------------------------------------------------------------------------------
// V_Core Methods
// ----------------------------------------------------------------------------------
@ -519,7 +514,6 @@ struct V_Core
// Mixer Section
// --------------------------------------------------------------------------------------
StereoOut32 Mix(const VoiceMixSet& inVoices, const StereoOut32& Input, const StereoOut32& Ext);
StereoOut32 DoReverb(StereoOut32 Input);
s32 RevbGetIndexer(s32 offset);
@ -643,3 +637,6 @@ struct PcmCacheEntry
};
extern PcmCacheEntry pcm_cache_data[pcm_BlockCount];
extern int g_counter_cache_hits;
extern int g_counter_cache_misses;
extern int g_counter_cache_ignores;

View File

@ -219,6 +219,10 @@ bool SPU2::IsAudioCaptureActive()
void SPU2::InternalReset(bool psxmode)
{
spu2Mix = MULTI_ISA_SELECT(spu2Mix);
ReverbDownsample = MULTI_ISA_SELECT(ReverbDownsample);
ReverbUpsample = MULTI_ISA_SELECT(ReverbUpsample);
s_current_chunk_pos = 0;
s_psxmode = psxmode;
if (!s_psxmode)

View File

@ -38,6 +38,8 @@ static bool has_to_call_irq_dma[2] = { false, false };
StereoOut32 (*ReverbUpsample)(V_Core& core);
s32 (*ReverbDownsample)(V_Core& core, bool right);
// Function pointer for multi-isa mixer
void (*spu2Mix)();
static bool psxmode = false;
@ -103,9 +105,6 @@ void V_Core::Init(int index)
if (SPU2::MsgToConsole())
SPU2::ConLog("* SPU2: Init SPU2 core %d \n", index);
ReverbDownsample = MULTI_ISA_SELECT(ReverbDownsample);
ReverbUpsample = MULTI_ISA_SELECT(ReverbUpsample);
//memset(this, 0, sizeof(V_Core));
// Explicitly initializing variables instead.
Mute = false;