Merge pull request #1635 from sudonim1/reverb

SPU2-X: Reverb rewrite
This commit is contained in:
sudonim1 2016-10-24 17:17:48 +01:00 committed by GitHub
commit 820462dbc0
9 changed files with 427 additions and 761 deletions

View File

@ -216,39 +216,39 @@ void DoFullDump()
fprintf(dump, " - IN_COEF_L: %x\n", Cores[c].Revb.IN_COEF_R);
fprintf(dump, " - IN_COEF_R: %x\n", Cores[c].Revb.IN_COEF_L);
fprintf(dump, " - FB_ALPHA: %x\n", Cores[c].Revb.FB_ALPHA);
fprintf(dump, " - FB_X: %x\n", Cores[c].Revb.FB_X);
fprintf(dump, " - FB_SRC_A: %x\n", Cores[c].Revb.FB_SRC_A);
fprintf(dump, " - FB_SRC_B: %x\n", Cores[c].Revb.FB_SRC_B);
fprintf(dump, " - APF1_VOL: %x\n", Cores[c].Revb.APF1_VOL);
fprintf(dump, " - APF2_VOL: %x\n", Cores[c].Revb.APF2_VOL);
fprintf(dump, " - APF1_SIZE: %x\n", Cores[c].Revb.APF1_SIZE);
fprintf(dump, " - APF2_SIZE: %x\n", Cores[c].Revb.APF2_SIZE);
fprintf(dump, " - IIR_ALPHA: %x\n", Cores[c].Revb.IIR_ALPHA);
fprintf(dump, " - IIR_COEF: %x\n", Cores[c].Revb.IIR_COEF);
fprintf(dump, " - IIR_SRC_A0: %x\n", Cores[c].Revb.IIR_SRC_A0);
fprintf(dump, " - IIR_SRC_A1: %x\n", Cores[c].Revb.IIR_SRC_A1);
fprintf(dump, " - IIR_SRC_B1: %x\n", Cores[c].Revb.IIR_SRC_B0);
fprintf(dump, " - IIR_SRC_B0: %x\n", Cores[c].Revb.IIR_SRC_B1);
fprintf(dump, " - IIR_DEST_A0: %x\n", Cores[c].Revb.IIR_DEST_A0);
fprintf(dump, " - IIR_DEST_A1: %x\n", Cores[c].Revb.IIR_DEST_A1);
fprintf(dump, " - IIR_DEST_B0: %x\n", Cores[c].Revb.IIR_DEST_B0);
fprintf(dump, " - IIR_DEST_B1: %x\n", Cores[c].Revb.IIR_DEST_B1);
fprintf(dump, " - IIR_VOL: %x\n", Cores[c].Revb.IIR_VOL);
fprintf(dump, " - WALL_VOL: %x\n", Cores[c].Revb.WALL_VOL);
fprintf(dump, " - SAME_L_SRC: %x\n", Cores[c].Revb.SAME_L_SRC);
fprintf(dump, " - SAME_R_SRC: %x\n", Cores[c].Revb.SAME_R_SRC);
fprintf(dump, " - DIFF_L_SRC: %x\n", Cores[c].Revb.DIFF_L_SRC);
fprintf(dump, " - DIFF_R_SRC: %x\n", Cores[c].Revb.DIFF_R_SRC);
fprintf(dump, " - SAME_L_DST: %x\n", Cores[c].Revb.SAME_L_DST);
fprintf(dump, " - SAME_R_DST: %x\n", Cores[c].Revb.SAME_R_DST);
fprintf(dump, " - DIFF_L_DST: %x\n", Cores[c].Revb.DIFF_L_DST);
fprintf(dump, " - DIFF_R_DST: %x\n", Cores[c].Revb.DIFF_R_DST);
fprintf(dump, " - ACC_COEF_A: %x\n", Cores[c].Revb.ACC_COEF_A);
fprintf(dump, " - ACC_COEF_B: %x\n", Cores[c].Revb.ACC_COEF_B);
fprintf(dump, " - ACC_COEF_C: %x\n", Cores[c].Revb.ACC_COEF_C);
fprintf(dump, " - ACC_COEF_D: %x\n", Cores[c].Revb.ACC_COEF_D);
fprintf(dump, " - ACC_SRC_A0: %x\n", Cores[c].Revb.ACC_SRC_A0);
fprintf(dump, " - ACC_SRC_A1: %x\n", Cores[c].Revb.ACC_SRC_A1);
fprintf(dump, " - ACC_SRC_B0: %x\n", Cores[c].Revb.ACC_SRC_B0);
fprintf(dump, " - ACC_SRC_B1: %x\n", Cores[c].Revb.ACC_SRC_B1);
fprintf(dump, " - ACC_SRC_C0: %x\n", Cores[c].Revb.ACC_SRC_C0);
fprintf(dump, " - ACC_SRC_C1: %x\n", Cores[c].Revb.ACC_SRC_C1);
fprintf(dump, " - ACC_SRC_D0: %x\n", Cores[c].Revb.ACC_SRC_D0);
fprintf(dump, " - ACC_SRC_D1: %x\n", Cores[c].Revb.ACC_SRC_D1);
fprintf(dump, " - COMB1_VOL: %x\n", Cores[c].Revb.COMB1_VOL);
fprintf(dump, " - COMB2_VOL: %x\n", Cores[c].Revb.COMB2_VOL);
fprintf(dump, " - COMB3_VOL: %x\n", Cores[c].Revb.COMB3_VOL);
fprintf(dump, " - COMB4_VOL: %x\n", Cores[c].Revb.COMB4_VOL);
fprintf(dump, " - COMB1_L_SRC: %x\n", Cores[c].Revb.COMB1_L_SRC);
fprintf(dump, " - COMB1_R_SRC: %x\n", Cores[c].Revb.COMB1_R_SRC);
fprintf(dump, " - COMB2_L_SRC: %x\n", Cores[c].Revb.COMB2_L_SRC);
fprintf(dump, " - COMB2_R_SRC: %x\n", Cores[c].Revb.COMB2_R_SRC);
fprintf(dump, " - COMB3_L_SRC: %x\n", Cores[c].Revb.COMB3_L_SRC);
fprintf(dump, " - COMB3_R_SRC: %x\n", Cores[c].Revb.COMB3_R_SRC);
fprintf(dump, " - COMB4_L_SRC: %x\n", Cores[c].Revb.COMB4_L_SRC);
fprintf(dump, " - COMB4_R_SRC: %x\n", Cores[c].Revb.COMB4_R_SRC);
fprintf(dump, " - MIX_DEST_A0: %x\n", Cores[c].Revb.MIX_DEST_A0);
fprintf(dump, " - MIX_DEST_A1: %x\n", Cores[c].Revb.MIX_DEST_A1);
fprintf(dump, " - MIX_DEST_B0: %x\n", Cores[c].Revb.MIX_DEST_B0);
fprintf(dump, " - MIX_DEST_B1: %x\n", Cores[c].Revb.MIX_DEST_B1);
fprintf(dump, " - APF1_L_DST: %x\n", Cores[c].Revb.APF1_L_DST);
fprintf(dump, " - APF1_R_DST: %x\n", Cores[c].Revb.APF1_R_DST);
fprintf(dump, " - APF2_L_DST: %x\n", Cores[c].Revb.APF2_L_DST);
fprintf(dump, " - APF2_R_DST: %x\n", Cores[c].Revb.APF2_R_DST);
fprintf(dump, "#### END OF DUMP.\n\n");
}
fclose(dump);

View File

@ -675,6 +675,7 @@ StereoOut32 V_Core::Mix(const VoiceMixSet &inVoices, const StereoOut32 &Input, c
// ToDo:
// Bad EndA causes memory corruption. Bad for us, unknown on PS2!
// According to no$psx, effects always run but don't always write back, so the FxEnable check may be wrong
if (!FxEnable || EffectsEndA >= 0x100000)
return TD;
@ -886,194 +887,3 @@ __forceinline
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// //
/*
-----------------------------------------------------------------------------
PSX reverb hardware notes
by Neill Corlett
-----------------------------------------------------------------------------
Yadda yadda disclaimer yadda probably not perfect yadda well it's okay anyway
yadda yadda.
-----------------------------------------------------------------------------
Basics
------
- The reverb buffer is 22khz 16-bit mono PCM.
- It starts at the reverb address given by 1DA2, extends to
the end of sound RAM, and wraps back to the 1DA2 address.
Setting the address at 1DA2 resets the current reverb work address.
This work address ALWAYS increments every 1/22050 sec., regardless of
whether reverb is enabled (bit 7 of 1DAA set).
And the contents of the reverb buffer ALWAYS play, scaled by the
"reverberation depth left/right" volumes (1D84/1D86).
(which, by the way, appear to be scaled so 3FFF=approx. 1.0, 4000=-1.0)
-----------------------------------------------------------------------------
Register names
--------------
These are probably not their real names.
These are probably not even correct names.
We will use them anyway, because we can.
1DC0: FB_SRC_A (offset)
1DC2: FB_SRC_B (offset)
1DC4: IIR_ALPHA (coef.)
1DC6: ACC_COEF_A (coef.)
1DC8: ACC_COEF_B (coef.)
1DCA: ACC_COEF_C (coef.)
1DCC: ACC_COEF_D (coef.)
1DCE: IIR_COEF (coef.)
1DD0: FB_ALPHA (coef.)
1DD2: FB_X (coef.)
1DD4: IIR_DEST_A0 (offset)
1DD6: IIR_DEST_A1 (offset)
1DD8: ACC_SRC_A0 (offset)
1DDA: ACC_SRC_A1 (offset)
1DDC: ACC_SRC_B0 (offset)
1DDE: ACC_SRC_B1 (offset)
1DE0: IIR_SRC_A0 (offset)
1DE2: IIR_SRC_A1 (offset)
1DE4: IIR_DEST_B0 (offset)
1DE6: IIR_DEST_B1 (offset)
1DE8: ACC_SRC_C0 (offset)
1DEA: ACC_SRC_C1 (offset)
1DEC: ACC_SRC_D0 (offset)
1DEE: ACC_SRC_D1 (offset)
1DF0: IIR_SRC_B1 (offset)
1DF2: IIR_SRC_B0 (offset)
1DF4: MIX_DEST_A0 (offset)
1DF6: MIX_DEST_A1 (offset)
1DF8: MIX_DEST_B0 (offset)
1DFA: MIX_DEST_B1 (offset)
1DFC: IN_COEF_L (coef.)
1DFE: IN_COEF_R (coef.)
The coefficients are signed fractional values.
-32768 would be -1.0
32768 would be 1.0 (if it were possible... the highest is of course 32767)
The offsets are (byte/8) offsets into the reverb buffer.
i.e. you multiply them by 8, you get byte offsets.
You can also think of them as (samples/4) offsets.
They appear to be signed. They can be negative.
None of the documented presets make them negative, though.
Yes, 1DF0 and 1DF2 appear to be backwards. Not a typo.
-----------------------------------------------------------------------------
What it does
------------
We take all reverb sources:
- regular channels that have the reverb bit on
- cd and external sources, if their reverb bits are on
and mix them into one stereo 44100hz signal.
Lowpass/downsample that to 22050hz. The PSX uses a proper bandlimiting
algorithm here, but I haven't figured out the hysterically exact specifics.
I use an 8-tap filter with these coefficients, which are nice but probably
not the real ones:
0.037828187894
0.157538631280
0.321159685278
0.449322115345
0.449322115345
0.321159685278
0.157538631280
0.037828187894
So we have two input samples (INPUT_SAMPLE_L, INPUT_SAMPLE_R) every 22050hz.
* IN MY EMULATION, I divide these by 2 to make it clip less.
(and of course the L/R output coefficients are adjusted to compensate)
The real thing appears to not do this.
At every 22050hz tick:
- If the reverb bit is enabled (bit 7 of 1DAA), execute the reverb
steady-state algorithm described below
- AFTERWARDS, retrieve the "wet out" L and R samples from the reverb buffer
(This part may not be exactly right and I guessed at the coefs. TODO: check later.)
L is: 0.333 * (buffer[MIX_DEST_A0] + buffer[MIX_DEST_B0])
R is: 0.333 * (buffer[MIX_DEST_A1] + buffer[MIX_DEST_B1])
- Advance the current buffer position by 1 sample
The wet out L and R are then upsampled to 44100hz and played at the
"reverberation depth left/right" (1D84/1D86) volume, independent of the main
volume.
-----------------------------------------------------------------------------
Reverb steady-state
-------------------
The reverb steady-state algorithm is fairly clever, and of course by
"clever" I mean "batshit insane".
buffer[x] is relative to the current buffer position, not the beginning of
the buffer. Note that all buffer offsets must wrap around so they're
contained within the reverb work area.
Clipping is performed at the end... maybe also sooner, but definitely at
the end.
IIR_INPUT_A0 = buffer[IIR_SRC_A0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;
IIR_INPUT_A1 = buffer[IIR_SRC_A1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;
IIR_INPUT_B0 = buffer[IIR_SRC_B0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;
IIR_INPUT_B1 = buffer[IIR_SRC_B1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;
IIR_A0 = IIR_INPUT_A0 * IIR_ALPHA + buffer[IIR_DEST_A0] * (1.0 - IIR_ALPHA);
IIR_A1 = IIR_INPUT_A1 * IIR_ALPHA + buffer[IIR_DEST_A1] * (1.0 - IIR_ALPHA);
IIR_B0 = IIR_INPUT_B0 * IIR_ALPHA + buffer[IIR_DEST_B0] * (1.0 - IIR_ALPHA);
IIR_B1 = IIR_INPUT_B1 * IIR_ALPHA + buffer[IIR_DEST_B1] * (1.0 - IIR_ALPHA);
buffer[IIR_DEST_A0 + 1sample] = IIR_A0;
buffer[IIR_DEST_A1 + 1sample] = IIR_A1;
buffer[IIR_DEST_B0 + 1sample] = IIR_B0;
buffer[IIR_DEST_B1 + 1sample] = IIR_B1;
ACC0 = buffer[ACC_SRC_A0] * ACC_COEF_A +
buffer[ACC_SRC_B0] * ACC_COEF_B +
buffer[ACC_SRC_C0] * ACC_COEF_C +
buffer[ACC_SRC_D0] * ACC_COEF_D;
ACC1 = buffer[ACC_SRC_A1] * ACC_COEF_A +
buffer[ACC_SRC_B1] * ACC_COEF_B +
buffer[ACC_SRC_C1] * ACC_COEF_C +
buffer[ACC_SRC_D1] * ACC_COEF_D;
FB_A0 = buffer[MIX_DEST_A0 - FB_SRC_A];
FB_A1 = buffer[MIX_DEST_A1 - FB_SRC_A];
FB_B0 = buffer[MIX_DEST_B0 - FB_SRC_B];
FB_B1 = buffer[MIX_DEST_B1 - FB_SRC_B];
buffer[MIX_DEST_A0] = ACC0 - FB_A0 * FB_ALPHA;
buffer[MIX_DEST_A1] = ACC1 - FB_A1 * FB_ALPHA;
buffer[MIX_DEST_B0] = (FB_ALPHA * ACC0) - FB_A0 * (FB_ALPHA^0x8000) - FB_B0 * FB_X;
buffer[MIX_DEST_B1] = (FB_ALPHA * ACC1) - FB_A1 * (FB_ALPHA^0x8000) - FB_B1 * FB_X;
Air notes:
The above is effectivly the same as:
buffer[MIX_DEST_B0] = (ACC0 * FB_ALPHA) + (FB_A0 * (1.0-FB_ALPHA)) - FB_B0 * FB_X;
buffer[MIX_DEST_B1] = (ACC1 * FB_ALPHA) + (FB_A1 * (1.0-FB_ALPHA)) - FB_B1 * FB_X;
Which reduces to:
buffer[MIX_DEST_B0] = FB_A0 + ((ACC0-FB_A0) * FB_ALPHA) - FB_B0 * FB_X;
buffer[MIX_DEST_B1] = FB_A1 + ((ACC1-FB_A1) * FB_ALPHA) - FB_B1 * FB_X;
-----------------------------------------------------------------------------
*/

View File

@ -93,29 +93,29 @@ void SPU2writeLog(const char *action, u32 rmem, u16 value)
case REG_P_MVOLXR:
RegLog(2, "MVOLXR", rmem, core, value);
break;
case R_IIR_ALPHA:
RegLog(2, "IIR_ALPHA", rmem, core, value);
case R_IIR_VOL:
RegLog(2, "IIR_VOL", rmem, core, value);
break;
case R_ACC_COEF_A:
RegLog(2, "ACC_COEF_A", rmem, core, value);
case R_COMB1_VOL:
RegLog(2, "COMB1_VOL", rmem, core, value);
break;
case R_ACC_COEF_B:
RegLog(2, "ACC_COEF_B", rmem, core, value);
case R_COMB2_VOL:
RegLog(2, "COMB2_VOL", rmem, core, value);
break;
case R_ACC_COEF_C:
RegLog(2, "ACC_COEF_C", rmem, core, value);
case R_COMB3_VOL:
RegLog(2, "COMB3_VOL", rmem, core, value);
break;
case R_ACC_COEF_D:
RegLog(2, "ACC_COEF_D", rmem, core, value);
case R_COMB4_VOL:
RegLog(2, "COMB4_VOL", rmem, core, value);
break;
case R_IIR_COEF:
RegLog(2, "IIR_COEF", rmem, core, value);
case R_WALL_VOL:
RegLog(2, "WALL_VOL", rmem, core, value);
break;
case R_FB_ALPHA:
RegLog(2, "FB_ALPHA", rmem, core, value);
case R_APF1_VOL:
RegLog(2, "APF1_VOL", rmem, core, value);
break;
case R_FB_X:
RegLog(2, "FB_X", rmem, core, value);
case R_APF2_VOL:
RegLog(2, "APF2_VOL", rmem, core, value);
break;
case R_IN_COEF_L:
RegLog(2, "IN_COEF_L", rmem, core, value);
@ -265,28 +265,28 @@ void SPU2writeLog(const char *action, u32 rmem, u16 value)
RegLog(2, t "L", mem, core, value); \
break;
LOG_REVB_REG(FB_SRC_A, "FB_SRC_A")
LOG_REVB_REG(FB_SRC_B, "FB_SRC_B")
LOG_REVB_REG(IIR_SRC_A0, "IIR_SRC_A0")
LOG_REVB_REG(IIR_SRC_A1, "IIR_SRC_A1")
LOG_REVB_REG(IIR_SRC_B1, "IIR_SRC_B1")
LOG_REVB_REG(IIR_SRC_B0, "IIR_SRC_B0")
LOG_REVB_REG(IIR_DEST_A0, "IIR_DEST_A0")
LOG_REVB_REG(IIR_DEST_A1, "IIR_DEST_A1")
LOG_REVB_REG(IIR_DEST_B0, "IIR_DEST_B0")
LOG_REVB_REG(IIR_DEST_B1, "IIR_DEST_B1")
LOG_REVB_REG(ACC_SRC_A0, "ACC_SRC_A0")
LOG_REVB_REG(ACC_SRC_A1, "ACC_SRC_A1")
LOG_REVB_REG(ACC_SRC_B0, "ACC_SRC_B0")
LOG_REVB_REG(ACC_SRC_B1, "ACC_SRC_B1")
LOG_REVB_REG(ACC_SRC_C0, "ACC_SRC_C0")
LOG_REVB_REG(ACC_SRC_C1, "ACC_SRC_C1")
LOG_REVB_REG(ACC_SRC_D0, "ACC_SRC_D0")
LOG_REVB_REG(ACC_SRC_D1, "ACC_SRC_D1")
LOG_REVB_REG(MIX_DEST_A0, "MIX_DEST_A0")
LOG_REVB_REG(MIX_DEST_A1, "MIX_DEST_A1")
LOG_REVB_REG(MIX_DEST_B0, "MIX_DEST_B0")
LOG_REVB_REG(MIX_DEST_B1, "MIX_DEST_B1")
LOG_REVB_REG(APF1_SIZE, "APF1_SIZE")
LOG_REVB_REG(APF2_SIZE, "APF2_SIZE")
LOG_REVB_REG(SAME_L_SRC, "SAME_L_SRC")
LOG_REVB_REG(SAME_R_SRC, "SAME_R_SRC")
LOG_REVB_REG(DIFF_L_SRC, "DIFF_L_SRC")
LOG_REVB_REG(DIFF_R_SRC, "DIFF_R_SRC")
LOG_REVB_REG(SAME_L_DST, "SAME_L_DST")
LOG_REVB_REG(SAME_R_DST, "SAME_R_DST")
LOG_REVB_REG(DIFF_L_DST, "DIFF_L_DST")
LOG_REVB_REG(DIFF_R_DST, "DIFF_R_DST")
LOG_REVB_REG(COMB1_L_SRC, "COMB1_L_SRC")
LOG_REVB_REG(COMB1_R_SRC, "COMB1_R_SRC")
LOG_REVB_REG(COMB2_L_SRC, "COMB2_L_SRC")
LOG_REVB_REG(COMB2_R_SRC, "COMB2_R_SRC")
LOG_REVB_REG(COMB3_L_SRC, "COMB3_L_SRC")
LOG_REVB_REG(COMB3_R_SRC, "COMB3_R_SRC")
LOG_REVB_REG(COMB4_L_SRC, "COMB4_L_SRC")
LOG_REVB_REG(COMB4_R_SRC, "COMB4_R_SRC")
LOG_REVB_REG(APF1_L_DST, "APF1_L_DST")
LOG_REVB_REG(APF1_R_DST, "APF1_R_DST")
LOG_REVB_REG(APF2_L_DST, "APF2_L_DST")
LOG_REVB_REG(APF2_R_DST, "APF2_R_DST")
default:
RegLog(2, "UNKNOWN", rmem, core, value);

View File

@ -99,28 +99,28 @@ u16 const *const regtable_original[0x401] =
PCORE(0, ExtEffectsStartA) + 1,
PCORE(0, ExtEffectsStartA),
PREVB_REG(0, FB_SRC_A),
PREVB_REG(0, FB_SRC_B),
PREVB_REG(0, IIR_DEST_A0),
PREVB_REG(0, IIR_DEST_A1),
PREVB_REG(0, ACC_SRC_A0),
PREVB_REG(0, ACC_SRC_A1),
PREVB_REG(0, ACC_SRC_B0),
PREVB_REG(0, ACC_SRC_B1),
PREVB_REG(0, IIR_SRC_A0),
PREVB_REG(0, IIR_SRC_A1),
PREVB_REG(0, IIR_DEST_B0),
PREVB_REG(0, IIR_DEST_B1),
PREVB_REG(0, ACC_SRC_C0),
PREVB_REG(0, ACC_SRC_C1),
PREVB_REG(0, ACC_SRC_D0),
PREVB_REG(0, ACC_SRC_D1),
PREVB_REG(0, IIR_SRC_B0),
PREVB_REG(0, IIR_SRC_B1),
PREVB_REG(0, MIX_DEST_A0),
PREVB_REG(0, MIX_DEST_A1),
PREVB_REG(0, MIX_DEST_B0),
PREVB_REG(0, MIX_DEST_B1),
PREVB_REG(0, APF1_SIZE),
PREVB_REG(0, APF2_SIZE),
PREVB_REG(0, SAME_L_DST),
PREVB_REG(0, SAME_R_DST),
PREVB_REG(0, COMB1_L_SRC),
PREVB_REG(0, COMB1_R_SRC),
PREVB_REG(0, COMB2_L_SRC),
PREVB_REG(0, COMB2_R_SRC),
PREVB_REG(0, SAME_L_SRC),
PREVB_REG(0, SAME_R_SRC),
PREVB_REG(0, DIFF_L_DST),
PREVB_REG(0, DIFF_R_DST),
PREVB_REG(0, COMB3_L_SRC),
PREVB_REG(0, COMB3_R_SRC),
PREVB_REG(0, COMB4_L_SRC),
PREVB_REG(0, COMB4_R_SRC),
PREVB_REG(0, DIFF_L_SRC),
PREVB_REG(0, DIFF_R_SRC),
PREVB_REG(0, APF1_L_DST),
PREVB_REG(0, APF1_R_DST),
PREVB_REG(0, APF2_L_DST),
PREVB_REG(0, APF2_R_DST),
PCORE(0, ExtEffectsEndA) + 1,
PCORE(0, ExtEffectsEndA),
@ -202,28 +202,28 @@ u16 const *const regtable_original[0x401] =
PCORE(1, ExtEffectsStartA) + 1,
PCORE(1, ExtEffectsStartA),
PREVB_REG(1, FB_SRC_A),
PREVB_REG(1, FB_SRC_B),
PREVB_REG(1, IIR_DEST_A0),
PREVB_REG(1, IIR_DEST_A1),
PREVB_REG(1, ACC_SRC_A0),
PREVB_REG(1, ACC_SRC_A1),
PREVB_REG(1, ACC_SRC_B0),
PREVB_REG(1, ACC_SRC_B1),
PREVB_REG(1, IIR_SRC_A0),
PREVB_REG(1, IIR_SRC_A1),
PREVB_REG(1, IIR_DEST_B0),
PREVB_REG(1, IIR_DEST_B1),
PREVB_REG(1, ACC_SRC_C0),
PREVB_REG(1, ACC_SRC_C1),
PREVB_REG(1, ACC_SRC_D0),
PREVB_REG(1, ACC_SRC_D1),
PREVB_REG(1, IIR_SRC_B0),
PREVB_REG(1, IIR_SRC_B1),
PREVB_REG(1, MIX_DEST_A0),
PREVB_REG(1, MIX_DEST_A1),
PREVB_REG(1, MIX_DEST_B0),
PREVB_REG(1, MIX_DEST_B1),
PREVB_REG(1, APF1_SIZE),
PREVB_REG(1, APF2_SIZE),
PREVB_REG(1, SAME_L_DST),
PREVB_REG(1, SAME_R_DST),
PREVB_REG(1, COMB1_L_SRC),
PREVB_REG(1, COMB1_R_SRC),
PREVB_REG(1, COMB2_L_SRC),
PREVB_REG(1, COMB2_R_SRC),
PREVB_REG(1, SAME_L_SRC),
PREVB_REG(1, SAME_R_SRC),
PREVB_REG(1, DIFF_L_DST),
PREVB_REG(1, DIFF_R_DST),
PREVB_REG(1, COMB3_L_SRC),
PREVB_REG(1, COMB3_R_SRC),
PREVB_REG(1, COMB4_L_SRC),
PREVB_REG(1, COMB4_R_SRC),
PREVB_REG(1, DIFF_L_SRC),
PREVB_REG(1, DIFF_R_SRC),
PREVB_REG(1, APF1_L_DST),
PREVB_REG(1, APF1_R_DST),
PREVB_REG(1, APF2_L_DST),
PREVB_REG(1, APF2_R_DST),
PCORE(1, ExtEffectsEndA) + 1,
PCORE(1, ExtEffectsEndA),
@ -248,14 +248,14 @@ u16 const *const regtable_original[0x401] =
PCORE(0, InpVol.Right) + 1,
PCORE(0, MasterVol.Left.Value) + 1,
PCORE(0, MasterVol.Right.Value) + 1,
PCORE(0, Revb.IIR_ALPHA),
PCORE(0, Revb.ACC_COEF_A),
PCORE(0, Revb.ACC_COEF_B),
PCORE(0, Revb.ACC_COEF_C),
PCORE(0, Revb.ACC_COEF_D),
PCORE(0, Revb.IIR_COEF),
PCORE(0, Revb.FB_ALPHA),
PCORE(0, Revb.FB_X),
PCORE(0, Revb.IIR_VOL),
PCORE(0, Revb.COMB1_VOL),
PCORE(0, Revb.COMB2_VOL),
PCORE(0, Revb.COMB3_VOL),
PCORE(0, Revb.COMB4_VOL),
PCORE(0, Revb.WALL_VOL),
PCORE(0, Revb.APF1_VOL),
PCORE(0, Revb.APF2_VOL),
PCORE(0, Revb.IN_COEF_L),
PCORE(0, Revb.IN_COEF_R),
@ -269,14 +269,14 @@ u16 const *const regtable_original[0x401] =
PCORE(1, InpVol.Right) + 1,
PCORE(1, MasterVol.Left.Value) + 1,
PCORE(1, MasterVol.Right.Value) + 1,
PCORE(1, Revb.IIR_ALPHA),
PCORE(1, Revb.ACC_COEF_A),
PCORE(1, Revb.ACC_COEF_B),
PCORE(1, Revb.ACC_COEF_C),
PCORE(1, Revb.ACC_COEF_D),
PCORE(1, Revb.IIR_COEF),
PCORE(1, Revb.FB_ALPHA),
PCORE(1, Revb.FB_X),
PCORE(1, Revb.IIR_VOL),
PCORE(1, Revb.COMB1_VOL),
PCORE(1, Revb.COMB2_VOL),
PCORE(1, Revb.COMB3_VOL),
PCORE(1, Revb.COMB4_VOL),
PCORE(1, Revb.WALL_VOL),
PCORE(1, Revb.APF1_VOL),
PCORE(1, Revb.APF2_VOL),
PCORE(1, Revb.IN_COEF_L),
PCORE(1, Revb.IN_COEF_R),

View File

@ -16,13 +16,6 @@
*/
#include "Global.h"
#include "Lowpass.h"
// Low pass filters: Change these to 32 for a speedup (benchmarks needed to see if
// the speed gain is worth the quality drop)
//static LowPassFilter64 lowpass_left( 11000, SampleRate );
//static LowPassFilter64 lowpass_right( 11000, SampleRate );
__forceinline s32 V_Core::RevbGetIndexer(s32 offset)
{
@ -56,74 +49,31 @@ void V_Core::Reverb_AdvanceBuffer()
StereoOut32 V_Core::DoReverb(const StereoOut32 &Input)
{
#if 0
static const s32 downcoeffs[8] =
{
1283, 5344, 10895, 15243,
15243, 10895, 5344, 1283
};
#else
// 2/3 of the above
static const s32 downcoeffs[8] =
{
855, 3562, 7263, 10163,
10163, 7263, 3562, 855};
#endif
downbuf[dbpos] = Input;
dbpos = (dbpos + 1) & 7;
// Reverb processing occurs at 24khz, so we skip processing every other sample,
// and use the previous calculation for this core instead.
if ((Cycles & 1) == 0) {
// Important: Factor silence into the upsampler here, otherwise the reverb engine
// develops a nasty feedback loop.
upbuf[ubpos] = StereoOut32::Empty;
} else {
if (EffectsBufferSize <= 0) {
ubpos = (ubpos + 1) & 7;
return StereoOut32::Empty;
}
// Advance the current reverb buffer pointer, and cache the read/write addresses we'll be
// needing for this session of reverb.
bool R = Cycles & 1;
const u32 src_a0 = RevbGetIndexer(RevBuffers.IIR_SRC_A0);
const u32 src_a1 = RevbGetIndexer(RevBuffers.IIR_SRC_A1);
const u32 src_b0 = RevbGetIndexer(RevBuffers.IIR_SRC_B0);
const u32 src_b1 = RevbGetIndexer(RevBuffers.IIR_SRC_B1);
// Calculate the read/write addresses we'll be needing for this session of reverb.
const u32 dest_a0 = RevbGetIndexer(RevBuffers.IIR_DEST_A0);
const u32 dest_a1 = RevbGetIndexer(RevBuffers.IIR_DEST_A1);
const u32 dest_b0 = RevbGetIndexer(RevBuffers.IIR_DEST_B0);
const u32 dest_b1 = RevbGetIndexer(RevBuffers.IIR_DEST_B1);
const u32 same_src = RevbGetIndexer(R ? RevBuffers.SAME_R_SRC : RevBuffers.SAME_L_SRC);
const u32 same_dst = RevbGetIndexer(R ? RevBuffers.SAME_R_DST : RevBuffers.SAME_L_DST);
const u32 same_prv = RevbGetIndexer(R ? RevBuffers.SAME_R_PRV : RevBuffers.SAME_L_PRV);
const u32 dest2_a0 = RevbGetIndexer(RevBuffers.IIR_DEST_A0 + 1);
const u32 dest2_a1 = RevbGetIndexer(RevBuffers.IIR_DEST_A1 + 1);
const u32 dest2_b0 = RevbGetIndexer(RevBuffers.IIR_DEST_B0 + 1);
const u32 dest2_b1 = RevbGetIndexer(RevBuffers.IIR_DEST_B1 + 1);
const u32 diff_src = RevbGetIndexer(R ? RevBuffers.DIFF_L_SRC : RevBuffers.DIFF_R_SRC);
const u32 diff_dst = RevbGetIndexer(R ? RevBuffers.DIFF_R_DST : RevBuffers.DIFF_L_DST);
const u32 diff_prv = RevbGetIndexer(R ? RevBuffers.DIFF_R_PRV : RevBuffers.DIFF_L_PRV);
const u32 acc_src_a0 = RevbGetIndexer(RevBuffers.ACC_SRC_A0);
const u32 acc_src_b0 = RevbGetIndexer(RevBuffers.ACC_SRC_B0);
const u32 acc_src_c0 = RevbGetIndexer(RevBuffers.ACC_SRC_C0);
const u32 acc_src_d0 = RevbGetIndexer(RevBuffers.ACC_SRC_D0);
const u32 comb1_src = RevbGetIndexer(R ? RevBuffers.COMB1_R_SRC : RevBuffers.COMB1_L_SRC);
const u32 comb2_src = RevbGetIndexer(R ? RevBuffers.COMB2_R_SRC : RevBuffers.COMB2_L_SRC);
const u32 comb3_src = RevbGetIndexer(R ? RevBuffers.COMB3_R_SRC : RevBuffers.COMB3_L_SRC);
const u32 comb4_src = RevbGetIndexer(R ? RevBuffers.COMB4_R_SRC : RevBuffers.COMB4_L_SRC);
const u32 acc_src_a1 = RevbGetIndexer(RevBuffers.ACC_SRC_A1);
const u32 acc_src_b1 = RevbGetIndexer(RevBuffers.ACC_SRC_B1);
const u32 acc_src_c1 = RevbGetIndexer(RevBuffers.ACC_SRC_C1);
const u32 acc_src_d1 = RevbGetIndexer(RevBuffers.ACC_SRC_D1);
const u32 fb_src_a0 = RevbGetIndexer(RevBuffers.FB_SRC_A0);
const u32 fb_src_a1 = RevbGetIndexer(RevBuffers.FB_SRC_A1);
const u32 fb_src_b0 = RevbGetIndexer(RevBuffers.FB_SRC_B0);
const u32 fb_src_b1 = RevbGetIndexer(RevBuffers.FB_SRC_B1);
const u32 mix_dest_a0 = RevbGetIndexer(RevBuffers.MIX_DEST_A0);
const u32 mix_dest_a1 = RevbGetIndexer(RevBuffers.MIX_DEST_A1);
const u32 mix_dest_b0 = RevbGetIndexer(RevBuffers.MIX_DEST_B0);
const u32 mix_dest_b1 = RevbGetIndexer(RevBuffers.MIX_DEST_B1);
const u32 apf1_src = RevbGetIndexer(R ? RevBuffers.APF1_R_SRC : RevBuffers.APF1_L_SRC);
const u32 apf1_dst = RevbGetIndexer(R ? RevBuffers.APF1_R_DST : RevBuffers.APF1_L_DST);
const u32 apf2_src = RevbGetIndexer(R ? RevBuffers.APF2_R_SRC : RevBuffers.APF2_L_SRC);
const u32 apf2_dst = RevbGetIndexer(R ? RevBuffers.APF2_R_DST : RevBuffers.APF2_L_DST);
// -----------------------------------------
// Optimized IRQ Testing !
@ -136,135 +86,48 @@ StereoOut32 V_Core::DoReverb(const StereoOut32 &Input)
for (int i = 0; i < 2; i++) {
if (Cores[i].IRQEnable && ((Cores[i].IRQA >= EffectsStartA) && (Cores[i].IRQA <= EffectsEndA))) {
if ((Cores[i].IRQA == src_a0) || (Cores[i].IRQA == src_a1) ||
(Cores[i].IRQA == src_b0) || (Cores[i].IRQA == src_b1) ||
if ((Cores[i].IRQA == same_src) || (Cores[i].IRQA == diff_src) ||
(Cores[i].IRQA == same_dst) || (Cores[i].IRQA == diff_dst) ||
(Cores[i].IRQA == same_prv) || (Cores[i].IRQA == diff_prv) ||
(Cores[i].IRQA == dest_a0) || (Cores[i].IRQA == dest_a1) ||
(Cores[i].IRQA == dest_b0) || (Cores[i].IRQA == dest_b1) ||
(Cores[i].IRQA == comb1_src) || (Cores[i].IRQA == comb2_src) ||
(Cores[i].IRQA == comb3_src) || (Cores[i].IRQA == comb4_src) ||
(Cores[i].IRQA == dest2_a0) || (Cores[i].IRQA == dest2_a1) ||
(Cores[i].IRQA == dest2_b0) || (Cores[i].IRQA == dest2_b1) ||
(Cores[i].IRQA == acc_src_a0) || (Cores[i].IRQA == acc_src_a1) ||
(Cores[i].IRQA == acc_src_b0) || (Cores[i].IRQA == acc_src_b1) ||
(Cores[i].IRQA == acc_src_c0) || (Cores[i].IRQA == acc_src_c1) ||
(Cores[i].IRQA == acc_src_d0) || (Cores[i].IRQA == acc_src_d1) ||
(Cores[i].IRQA == fb_src_a0) || (Cores[i].IRQA == fb_src_a1) ||
(Cores[i].IRQA == fb_src_b0) || (Cores[i].IRQA == fb_src_b1) ||
(Cores[i].IRQA == mix_dest_a0) || (Cores[i].IRQA == mix_dest_a1) ||
(Cores[i].IRQA == mix_dest_b0) || (Cores[i].IRQA == mix_dest_b1)) {
(Cores[i].IRQA == apf1_dst) || (Cores[i].IRQA == apf1_src) ||
(Cores[i].IRQA == apf2_dst) || (Cores[i].IRQA == apf2_src)) {
//printf("Core %d IRQ Called (Reverb). IRQA = %x\n",i,addr);
SetIrqCall(i);
}
}
}
// -----------------------------------------
// Begin Reverb Processing !
// -----------------------------------------
// Reverb algorithm pretty much directly ripped from http://drhell.web.fc2.com/ps1/
// minus the 35 step FIR which just seems to break things.
StereoOut32 INPUT_SAMPLE;
s32 in, same, diff, apf1, apf2, out;
for (int x = 0; x < 8; ++x) {
INPUT_SAMPLE.Left += (downbuf[(dbpos + x) & 7].Left * downcoeffs[x]);
INPUT_SAMPLE.Right += (downbuf[(dbpos + x) & 7].Right * downcoeffs[x]);
#define MUL(x, y) ((x) * (y) >> 15)
in = MUL(R ? Revb.IN_COEF_R : Revb.IN_COEF_L, R ? Input.Right : Input.Left);
same = MUL(Revb.IIR_VOL, in + MUL(Revb.WALL_VOL, _spu2mem[same_src]) - _spu2mem[same_prv]) + _spu2mem[same_prv];
diff = MUL(Revb.IIR_VOL, in + MUL(Revb.WALL_VOL, _spu2mem[diff_src]) - _spu2mem[diff_prv]) + _spu2mem[diff_prv];
out = MUL(Revb.COMB1_VOL, _spu2mem[comb1_src]) + MUL(Revb.COMB2_VOL, _spu2mem[comb2_src]) + MUL(Revb.COMB3_VOL, _spu2mem[comb3_src]) + MUL(Revb.COMB4_VOL, _spu2mem[comb4_src]);
apf1 = out - MUL(Revb.APF1_VOL, _spu2mem[apf1_src]);
out = _spu2mem[apf1_src] + MUL(Revb.APF1_VOL, apf1);
apf2 = out - MUL(Revb.APF2_VOL, _spu2mem[apf2_src]);
out = _spu2mem[apf2_src] + MUL(Revb.APF2_VOL, apf2);
// According to no$psx the effects always run but don't always write back, see check in V_Core::Mix
if (FxEnable) {
_spu2mem[same_dst] = clamp_mix(same);
_spu2mem[diff_dst] = clamp_mix(diff);
_spu2mem[apf1_dst] = clamp_mix(apf1);
_spu2mem[apf2_dst] = clamp_mix(apf2);
}
INPUT_SAMPLE.Left >>= 16;
INPUT_SAMPLE.Right >>= 16;
(R ? LastEffect.Right : LastEffect.Left) = -clamp_mix(out);
s32 input_L = INPUT_SAMPLE.Left * Revb.IN_COEF_L;
s32 input_R = INPUT_SAMPLE.Right * Revb.IN_COEF_R;
const s32 IIR_INPUT_A0 = clamp_mix((((s32)_spu2mem[src_a0] * Revb.IIR_COEF) + input_L) >> 15);
const s32 IIR_INPUT_A1 = clamp_mix((((s32)_spu2mem[src_a1] * Revb.IIR_COEF) + input_L) >> 15);
const s32 IIR_INPUT_B0 = clamp_mix((((s32)_spu2mem[src_b0] * Revb.IIR_COEF) + input_R) >> 15);
const s32 IIR_INPUT_B1 = clamp_mix((((s32)_spu2mem[src_b1] * Revb.IIR_COEF) + input_R) >> 15);
const s32 src_dest_a0 = _spu2mem[dest_a0];
const s32 src_dest_a1 = _spu2mem[dest_a1];
const s32 src_dest_b0 = _spu2mem[dest_b0];
const s32 src_dest_b1 = _spu2mem[dest_b1];
// This section differs from Neill's doc as it uses single-mul interpolation instead
// of 0x8000-val inversion. (same result, faster)
const s32 IIR_A0 = src_dest_a0 + (((IIR_INPUT_A0 - src_dest_a0) * Revb.IIR_ALPHA) >> 15);
const s32 IIR_A1 = src_dest_a1 + (((IIR_INPUT_A1 - src_dest_a1) * Revb.IIR_ALPHA) >> 15);
const s32 IIR_B0 = src_dest_b0 + (((IIR_INPUT_B0 - src_dest_b0) * Revb.IIR_ALPHA) >> 15);
const s32 IIR_B1 = src_dest_b1 + (((IIR_INPUT_B1 - src_dest_b1) * Revb.IIR_ALPHA) >> 15);
_spu2mem[dest2_a0] = clamp_mix(IIR_A0);
_spu2mem[dest2_a1] = clamp_mix(IIR_A1);
_spu2mem[dest2_b0] = clamp_mix(IIR_B0);
_spu2mem[dest2_b1] = clamp_mix(IIR_B1);
const s32 ACC0 = clamp_mix(
((_spu2mem[acc_src_a0] * Revb.ACC_COEF_A) >> 15) +
((_spu2mem[acc_src_b0] * Revb.ACC_COEF_B) >> 15) +
((_spu2mem[acc_src_c0] * Revb.ACC_COEF_C) >> 15) +
((_spu2mem[acc_src_d0] * Revb.ACC_COEF_D) >> 15));
const s32 ACC1 = clamp_mix(
((_spu2mem[acc_src_a1] * Revb.ACC_COEF_A) >> 15) +
((_spu2mem[acc_src_b1] * Revb.ACC_COEF_B) >> 15) +
((_spu2mem[acc_src_c1] * Revb.ACC_COEF_C) >> 15) +
((_spu2mem[acc_src_d1] * Revb.ACC_COEF_D) >> 15));
// The following code differs from Neill's doc as it uses the more natural single-mul
// interpolative, instead of the funky ^0x8000 stuff. (better result, faster)
const s32 FB_A0 = _spu2mem[fb_src_a0];
const s32 FB_A1 = _spu2mem[fb_src_a1];
const s32 FB_B0 = _spu2mem[fb_src_b0];
const s32 FB_B1 = _spu2mem[fb_src_b1];
const s32 mix_a0 = clamp_mix(ACC0 - ((FB_A0 * Revb.FB_ALPHA) >> 15));
const s32 mix_a1 = clamp_mix(ACC1 - ((FB_A1 * Revb.FB_ALPHA) >> 15));
const s32 mix_b0 = clamp_mix(FB_A0 + (((ACC0 - FB_A0) * Revb.FB_ALPHA - FB_B0 * Revb.FB_X) >> 15));
const s32 mix_b1 = clamp_mix(FB_A1 + (((ACC1 - FB_A1) * Revb.FB_ALPHA - FB_B1 * Revb.FB_X) >> 15));
_spu2mem[mix_dest_a0] = mix_a0;
_spu2mem[mix_dest_a1] = mix_a1;
_spu2mem[mix_dest_b0] = mix_b0;
_spu2mem[mix_dest_b1] = mix_b1;
upbuf[ubpos] = clamp_mix(StereoOut32(
mix_a0 + mix_b0, // left
mix_a1 + mix_b1 // right
));
}
StereoOut32 retval;
//for( int x=0; x<8; ++x )
//{
// retval.Left += (upbuf[(ubpos+x)&7].Left*downcoeffs[x]);
// retval.Right += (upbuf[(ubpos+x)&7].Right*downcoeffs[x]);
//}
if ((Cycles & 1) == 0) {
retval.Left = (upbuf[(ubpos + 5) & 7].Left + upbuf[(ubpos + 7) & 7].Left) >> 1;
retval.Right = (upbuf[(ubpos + 5) & 7].Right + upbuf[(ubpos + 7) & 7].Right) >> 1;
} else {
retval.Left = upbuf[(ubpos + 6) & 7].Left;
retval.Right = upbuf[(ubpos + 6) & 7].Right;
}
// Notes:
// the first -1 is to adjust for the null padding in every other upbuf sample (which
// halves the overall volume).
// The second +1 divides by two, which is part of Neill's suggestion to divide by 3.
//
// According Neill the final result should be divided by 3, but currently the output
// is way too quiet for that to fly. In fact no division at all might be better.
// In any case the problem always seems to be that the reverb isn't resonating enough
// (indicating short buffers or bad coefficient math?), not that it isn't loud enough.
//retval.Left >>= (16-1 + 1);
//retval.Right >>= (16-1 + 1);
ubpos = (ubpos + 1) & 7;
return retval;
return LastEffect;
}

View File

@ -18,6 +18,7 @@
#pragma once
#include "Mixer.h"
#include "SndOut.h"
// --------------------------------------------------------------------------------------
// SPU2 Memory Indexers
@ -229,73 +230,78 @@ struct V_Reverb
s16 IN_COEF_L;
s16 IN_COEF_R;
u32 FB_SRC_A;
u32 FB_SRC_B;
u32 APF1_SIZE;
u32 APF2_SIZE;
s16 FB_ALPHA;
s16 FB_X;
s16 APF1_VOL;
s16 APF2_VOL;
u32 IIR_SRC_A0;
u32 IIR_SRC_A1;
u32 IIR_SRC_B1;
u32 IIR_SRC_B0;
u32 IIR_DEST_A0;
u32 IIR_DEST_A1;
u32 IIR_DEST_B0;
u32 IIR_DEST_B1;
u32 SAME_L_SRC;
u32 SAME_R_SRC;
u32 DIFF_L_SRC;
u32 DIFF_R_SRC;
u32 SAME_L_DST;
u32 SAME_R_DST;
u32 DIFF_L_DST;
u32 DIFF_R_DST;
s16 IIR_ALPHA;
s16 IIR_COEF;
s16 IIR_VOL;
s16 WALL_VOL;
u32 ACC_SRC_A0;
u32 ACC_SRC_A1;
u32 ACC_SRC_B0;
u32 ACC_SRC_B1;
u32 ACC_SRC_C0;
u32 ACC_SRC_C1;
u32 ACC_SRC_D0;
u32 ACC_SRC_D1;
u32 COMB1_L_SRC;
u32 COMB1_R_SRC;
u32 COMB2_L_SRC;
u32 COMB2_R_SRC;
u32 COMB3_L_SRC;
u32 COMB3_R_SRC;
u32 COMB4_L_SRC;
u32 COMB4_R_SRC;
s16 ACC_COEF_A;
s16 ACC_COEF_B;
s16 ACC_COEF_C;
s16 ACC_COEF_D;
s16 COMB1_VOL;
s16 COMB2_VOL;
s16 COMB3_VOL;
s16 COMB4_VOL;
u32 MIX_DEST_A0;
u32 MIX_DEST_A1;
u32 MIX_DEST_B0;
u32 MIX_DEST_B1;
u32 APF1_L_DST;
u32 APF1_R_DST;
u32 APF2_L_DST;
u32 APF2_R_DST;
};
struct V_ReverbBuffers
{
s32 FB_SRC_A0;
s32 FB_SRC_B0;
s32 FB_SRC_A1;
s32 FB_SRC_B1;
s32 SAME_L_SRC;
s32 SAME_R_SRC;
s32 DIFF_R_SRC;
s32 DIFF_L_SRC;
s32 SAME_L_DST;
s32 SAME_R_DST;
s32 DIFF_L_DST;
s32 DIFF_R_DST;
s32 IIR_SRC_A0;
s32 IIR_SRC_A1;
s32 IIR_SRC_B0;
s32 IIR_SRC_B1;
s32 IIR_DEST_A0;
s32 IIR_DEST_A1;
s32 IIR_DEST_B0;
s32 IIR_DEST_B1;
s32 COMB1_L_SRC;
s32 COMB1_R_SRC;
s32 COMB2_L_SRC;
s32 COMB2_R_SRC;
s32 COMB3_L_SRC;
s32 COMB3_R_SRC;
s32 COMB4_L_SRC;
s32 COMB4_R_SRC;
s32 ACC_SRC_A0;
s32 ACC_SRC_A1;
s32 ACC_SRC_B0;
s32 ACC_SRC_B1;
s32 ACC_SRC_C0;
s32 ACC_SRC_C1;
s32 ACC_SRC_D0;
s32 ACC_SRC_D1;
s32 APF1_L_DST;
s32 APF1_R_DST;
s32 APF2_L_DST;
s32 APF2_R_DST;
s32 MIX_DEST_A0;
s32 MIX_DEST_A1;
s32 MIX_DEST_B0;
s32 MIX_DEST_B1;
s32 SAME_L_PRV;
s32 SAME_R_PRV;
s32 DIFF_L_PRV;
s32 DIFF_R_PRV;
s32 APF1_L_SRC;
s32 APF1_R_SRC;
s32 APF2_L_SRC;
s32 APF2_R_SRC;
bool NeedsUpdated;
};
@ -419,9 +425,7 @@ struct V_Core
V_CoreRegs Regs; // Registers
// Last samples to pass through the effects processor.
// Used because the effects processor works at 24khz and just pulls
// from this for the odd Ts.
// Preserves the channel processed last cycle
StereoOut32 LastEffect;
u8 CoreEnabled;
@ -444,10 +448,6 @@ struct V_Core
u16 psxSoundDataTransferControl;
u16 psxSPUSTAT;
StereoOut32 downbuf[8];
StereoOut32 upbuf[8];
int dbpos, ubpos;
// HACK -- This is a temp buffer which is (or isn't?) used to circumvent some memory
// corruption that originates elsewhere in the plugin. >_< The actual ADMA buffer
// is an area mapped to SPU2 main memory.
@ -471,8 +471,6 @@ struct V_Core
void AnalyzeReverbPreset();
s32 EffectsBufferIndexer(s32 offset) const;
void UpdateFeedbackBuffersA();
void UpdateFeedbackBuffersB();
void WriteRegPS1(u32 mem, u16 value);
u16 ReadRegPS1(u32 mem);

View File

@ -62,28 +62,28 @@
// .. repeated for each voice ..
#define REG_A_ESA 0x02E0 //Address: Top address of working area for effects processing
#define R_FB_SRC_A 0x02E4 // Feedback Source A
#define R_FB_SRC_B 0x02E8 // Feedback Source B
#define R_IIR_DEST_A0 0x02EC
#define R_IIR_DEST_A1 0x02F0
#define R_ACC_SRC_A0 0x02F4
#define R_ACC_SRC_A1 0x02F8
#define R_ACC_SRC_B0 0x02FC
#define R_ACC_SRC_B1 0x0300
#define R_IIR_SRC_A0 0x0304
#define R_IIR_SRC_A1 0x0308
#define R_IIR_DEST_B0 0x030C
#define R_IIR_DEST_B1 0x0310
#define R_ACC_SRC_C0 0x0314
#define R_ACC_SRC_C1 0x0318
#define R_ACC_SRC_D0 0x031C
#define R_ACC_SRC_D1 0x0320
#define R_IIR_SRC_B0 0x0324 // Some sources have R_IIR_SRC_B0 and R_IIR_SRC_B1 swapped ><
#define R_IIR_SRC_B1 0x0328 // Assume a typo in the docs and B0 is actually at 324, B1 at 328 in the HW.
#define R_MIX_DEST_A0 0x032C
#define R_MIX_DEST_A1 0x0330
#define R_MIX_DEST_B0 0x0334
#define R_MIX_DEST_B1 0x0338
#define R_APF1_SIZE 0x02E4 // Feedback Source A
#define R_APF2_SIZE 0x02E8 // Feedback Source B
#define R_SAME_L_DST 0x02EC
#define R_SAME_R_DST 0x02F0
#define R_COMB1_L_SRC 0x02F4
#define R_COMB1_R_SRC 0x02F8
#define R_COMB2_L_SRC 0x02FC
#define R_COMB2_R_SRC 0x0300
#define R_SAME_L_SRC 0x0304
#define R_SAME_R_SRC 0x0308
#define R_DIFF_L_DST 0x030C
#define R_DIFF_R_DST 0x0310
#define R_COMB3_L_SRC 0x0314
#define R_COMB3_R_SRC 0x0318
#define R_COMB4_L_SRC 0x031C
#define R_COMB4_R_SRC 0x0320
#define R_DIFF_L_SRC 0x0324 // Some sources have R_DIFF_R_SRC and R_DIFF_L_SRC swapped ><
#define R_DIFF_R_SRC 0x0328
#define R_APF1_L_DST 0x032C
#define R_APF1_R_DST 0x0330
#define R_APF2_L_DST 0x0334
#define R_APF2_R_DST 0x0338
#define REG_A_EEA 0x033C // Address: End address of working area for effects processing (upper part of address only!)
#define REG_S_ENDX 0x0340 // End Point passed flag
@ -110,14 +110,14 @@
#define REG_P_MVOLXL 0x0770 // Current Master Volume Left
#define REG_P_MVOLXR 0x0772 // Current Master Volume Right
#define R_IIR_ALPHA 0x0774 //IIR alpha (% used)
#define R_ACC_COEF_A 0x0776
#define R_ACC_COEF_B 0x0778
#define R_ACC_COEF_C 0x077A
#define R_ACC_COEF_D 0x077C
#define R_IIR_COEF 0x077E
#define R_FB_ALPHA 0x0780 //feedback alpha (% used)
#define R_FB_X 0x0782 //feedback
#define R_IIR_VOL 0x0774
#define R_COMB1_VOL 0x0776
#define R_COMB2_VOL 0x0778
#define R_COMB3_VOL 0x077A
#define R_COMB4_VOL 0x077C
#define R_WALL_VOL 0x077E
#define R_APF1_VOL 0x0780
#define R_APF2_VOL 0x0782
#define R_IN_COEF_L 0x0784
#define R_IN_COEF_R 0x0786

View File

@ -25,7 +25,7 @@ static const u32 SAVE_ID = 0x1227521;
// versioning for saves.
// Increment this when changes to the savestate system are made.
static const u32 SAVE_VERSION = 0x000d;
static const u32 SAVE_VERSION = 0x000e;
static void wipe_the_cache()
{

View File

@ -195,29 +195,29 @@ void V_Core::AnalyzeReverbPreset()
ConLog("----------------------------------------------------------\n");
ConLog(" IN_COEF_L, IN_COEF_R 0x%08x, 0x%08x\n", Revb.IN_COEF_L, Revb.IN_COEF_R);
ConLog(" FB_SRC_A, FB_SRC_B 0x%08x, 0x%08x\n", Revb.FB_SRC_A, Revb.FB_SRC_B);
ConLog(" FB_ALPHA, FB_X 0x%08x, 0x%08x\n", Revb.FB_ALPHA, Revb.FB_X);
ConLog(" APF1_SIZE, APF2_SIZE 0x%08x, 0x%08x\n", Revb.APF1_SIZE, Revb.APF2_SIZE);
ConLog(" APF1_VOL, APF2_VOL 0x%08x, 0x%08x\n", Revb.APF1_VOL, Revb.APF2_VOL);
ConLog(" ACC_COEF_A 0x%08x\n", Revb.ACC_COEF_A);
ConLog(" ACC_COEF_B 0x%08x\n", Revb.ACC_COEF_B);
ConLog(" ACC_COEF_C 0x%08x\n", Revb.ACC_COEF_C);
ConLog(" ACC_COEF_D 0x%08x\n", Revb.ACC_COEF_D);
ConLog(" COMB1_VOL 0x%08x\n", Revb.COMB1_VOL);
ConLog(" COMB2_VOL 0x%08x\n", Revb.COMB2_VOL);
ConLog(" COMB3_VOL 0x%08x\n", Revb.COMB3_VOL);
ConLog(" COMB4_VOL 0x%08x\n", Revb.COMB4_VOL);
ConLog(" ACC_SRC_A0, ACC_SRC_A1 0x%08x, 0x%08x\n", Revb.ACC_SRC_A0, Revb.ACC_SRC_A1);
ConLog(" ACC_SRC_B0, ACC_SRC_B1 0x%08x, 0x%08x\n", Revb.ACC_SRC_B0, Revb.ACC_SRC_B1);
ConLog(" ACC_SRC_C0, ACC_SRC_C1 0x%08x, 0x%08x\n", Revb.ACC_SRC_C0, Revb.ACC_SRC_C1);
ConLog(" ACC_SRC_D0, ACC_SRC_D1 0x%08x, 0x%08x\n", Revb.ACC_SRC_D0, Revb.ACC_SRC_D1);
ConLog(" COMB1_L_SRC, COMB1_R_SRC 0x%08x, 0x%08x\n", Revb.COMB1_L_SRC, Revb.COMB1_R_SRC);
ConLog(" COMB2_L_SRC, COMB2_R_SRC 0x%08x, 0x%08x\n", Revb.COMB2_L_SRC, Revb.COMB2_R_SRC);
ConLog(" COMB3_L_SRC, COMB3_R_SRC 0x%08x, 0x%08x\n", Revb.COMB3_L_SRC, Revb.COMB3_R_SRC);
ConLog(" COMB4_L_SRC, COMB4_R_SRC 0x%08x, 0x%08x\n", Revb.COMB4_L_SRC, Revb.COMB4_R_SRC);
ConLog(" IIR_SRC_A0, IIR_SRC_A1 0x%08x, 0x%08x\n", Revb.IIR_SRC_A0, Revb.IIR_SRC_A1);
ConLog(" IIR_SRC_B0, IIR_SRC_B1 0x%08x, 0x%08x\n", Revb.IIR_SRC_B0, Revb.IIR_SRC_B1);
ConLog(" IIR_DEST_A0, IIR_DEST_A1 0x%08x, 0x%08x\n", Revb.IIR_DEST_A0, Revb.IIR_DEST_A1);
ConLog(" IIR_DEST_B0, IIR_DEST_B1 0x%08x, 0x%08x\n", Revb.IIR_DEST_B0, Revb.IIR_DEST_B1);
ConLog(" IIR_ALPHA, IIR_COEF 0x%08x, 0x%08x\n", Revb.IIR_ALPHA, Revb.IIR_COEF);
ConLog(" SAME_L_SRC, SAME_R_SRC 0x%08x, 0x%08x\n", Revb.SAME_L_SRC, Revb.SAME_R_SRC);
ConLog(" DIFF_L_SRC, DIFF_R_SRC 0x%08x, 0x%08x\n", Revb.DIFF_L_SRC, Revb.DIFF_R_SRC);
ConLog(" SAME_L_DST, SAME_R_DST 0x%08x, 0x%08x\n", Revb.SAME_L_DST, Revb.SAME_R_DST);
ConLog(" DIFF_L_DST, DIFF_R_DST 0x%08x, 0x%08x\n", Revb.DIFF_L_DST, Revb.DIFF_R_DST);
ConLog(" IIR_VOL, WALL_VOL 0x%08x, 0x%08x\n", Revb.IIR_VOL, Revb.WALL_VOL);
ConLog(" MIX_DEST_A0 0x%08x\n", Revb.MIX_DEST_A0);
ConLog(" MIX_DEST_A1 0x%08x\n", Revb.MIX_DEST_A1);
ConLog(" MIX_DEST_B0 0x%08x\n", Revb.MIX_DEST_B0);
ConLog(" MIX_DEST_B1 0x%08x\n", Revb.MIX_DEST_B1);
ConLog(" APF1_L_DST 0x%08x\n", Revb.APF1_L_DST);
ConLog(" APF1_R_DST 0x%08x\n", Revb.APF1_R_DST);
ConLog(" APF2_L_DST 0x%08x\n", Revb.APF2_L_DST);
ConLog(" APF2_R_DST 0x%08x\n", Revb.APF2_R_DST);
ConLog(" EffectsBufferSize 0x%x\n", EffectsBufferSize);
ConLog("----------------------------------------------------------\n");
@ -242,18 +242,6 @@ s32 V_Core::EffectsBufferIndexer(s32 offset) const
return pos;
}
void V_Core::UpdateFeedbackBuffersA()
{
RevBuffers.FB_SRC_A0 = EffectsBufferIndexer(Revb.MIX_DEST_A0 - Revb.FB_SRC_A);
RevBuffers.FB_SRC_A1 = EffectsBufferIndexer(Revb.MIX_DEST_A1 - Revb.FB_SRC_A);
}
void V_Core::UpdateFeedbackBuffersB()
{
RevBuffers.FB_SRC_B0 = EffectsBufferIndexer(Revb.MIX_DEST_B0 - Revb.FB_SRC_B);
RevBuffers.FB_SRC_B1 = EffectsBufferIndexer(Revb.MIX_DEST_B1 - Revb.FB_SRC_B);
}
void V_Core::UpdateEffectsBufferSize()
{
const s32 newbufsize = EffectsEndA - EffectsStartA + 1;
@ -281,32 +269,39 @@ void V_Core::UpdateEffectsBufferSize()
AnalyzeReverbPreset();
// Rebuild buffer indexers.
RevBuffers.ACC_SRC_A0 = EffectsBufferIndexer(Revb.ACC_SRC_A0);
RevBuffers.ACC_SRC_A1 = EffectsBufferIndexer(Revb.ACC_SRC_A1);
RevBuffers.ACC_SRC_B0 = EffectsBufferIndexer(Revb.ACC_SRC_B0);
RevBuffers.ACC_SRC_B1 = EffectsBufferIndexer(Revb.ACC_SRC_B1);
RevBuffers.ACC_SRC_C0 = EffectsBufferIndexer(Revb.ACC_SRC_C0);
RevBuffers.ACC_SRC_C1 = EffectsBufferIndexer(Revb.ACC_SRC_C1);
RevBuffers.ACC_SRC_D0 = EffectsBufferIndexer(Revb.ACC_SRC_D0);
RevBuffers.ACC_SRC_D1 = EffectsBufferIndexer(Revb.ACC_SRC_D1);
RevBuffers.COMB1_L_SRC = EffectsBufferIndexer(Revb.COMB1_L_SRC);
RevBuffers.COMB1_R_SRC = EffectsBufferIndexer(Revb.COMB1_R_SRC);
RevBuffers.COMB2_L_SRC = EffectsBufferIndexer(Revb.COMB2_L_SRC);
RevBuffers.COMB2_R_SRC = EffectsBufferIndexer(Revb.COMB2_R_SRC);
RevBuffers.COMB3_L_SRC = EffectsBufferIndexer(Revb.COMB3_L_SRC);
RevBuffers.COMB3_R_SRC = EffectsBufferIndexer(Revb.COMB3_R_SRC);
RevBuffers.COMB4_L_SRC = EffectsBufferIndexer(Revb.COMB4_L_SRC);
RevBuffers.COMB4_R_SRC = EffectsBufferIndexer(Revb.COMB4_R_SRC);
UpdateFeedbackBuffersA();
UpdateFeedbackBuffersB();
RevBuffers.SAME_L_DST = EffectsBufferIndexer(Revb.SAME_L_DST);
RevBuffers.SAME_R_DST = EffectsBufferIndexer(Revb.SAME_R_DST);
RevBuffers.DIFF_L_DST = EffectsBufferIndexer(Revb.DIFF_L_DST);
RevBuffers.DIFF_R_DST = EffectsBufferIndexer(Revb.DIFF_R_DST);
RevBuffers.IIR_DEST_A0 = EffectsBufferIndexer(Revb.IIR_DEST_A0);
RevBuffers.IIR_DEST_A1 = EffectsBufferIndexer(Revb.IIR_DEST_A1);
RevBuffers.IIR_DEST_B0 = EffectsBufferIndexer(Revb.IIR_DEST_B0);
RevBuffers.IIR_DEST_B1 = EffectsBufferIndexer(Revb.IIR_DEST_B1);
RevBuffers.SAME_L_SRC = EffectsBufferIndexer(Revb.SAME_L_SRC);
RevBuffers.SAME_R_SRC = EffectsBufferIndexer(Revb.SAME_R_SRC);
RevBuffers.DIFF_L_SRC = EffectsBufferIndexer(Revb.DIFF_L_SRC);
RevBuffers.DIFF_R_SRC = EffectsBufferIndexer(Revb.DIFF_R_SRC);
RevBuffers.IIR_SRC_A0 = EffectsBufferIndexer(Revb.IIR_SRC_A0);
RevBuffers.IIR_SRC_A1 = EffectsBufferIndexer(Revb.IIR_SRC_A1);
RevBuffers.IIR_SRC_B0 = EffectsBufferIndexer(Revb.IIR_SRC_B0);
RevBuffers.IIR_SRC_B1 = EffectsBufferIndexer(Revb.IIR_SRC_B1);
RevBuffers.APF1_L_DST = EffectsBufferIndexer(Revb.APF1_L_DST);
RevBuffers.APF1_R_DST = EffectsBufferIndexer(Revb.APF1_R_DST);
RevBuffers.APF2_L_DST = EffectsBufferIndexer(Revb.APF2_L_DST);
RevBuffers.APF2_R_DST = EffectsBufferIndexer(Revb.APF2_R_DST);
RevBuffers.MIX_DEST_A0 = EffectsBufferIndexer(Revb.MIX_DEST_A0);
RevBuffers.MIX_DEST_A1 = EffectsBufferIndexer(Revb.MIX_DEST_A1);
RevBuffers.MIX_DEST_B0 = EffectsBufferIndexer(Revb.MIX_DEST_B0);
RevBuffers.MIX_DEST_B1 = EffectsBufferIndexer(Revb.MIX_DEST_B1);
RevBuffers.SAME_L_PRV = EffectsBufferIndexer(Revb.SAME_L_DST - 1);
RevBuffers.SAME_R_PRV = EffectsBufferIndexer(Revb.SAME_R_DST - 1);
RevBuffers.DIFF_L_PRV = EffectsBufferIndexer(Revb.DIFF_L_DST - 1);
RevBuffers.DIFF_R_PRV = EffectsBufferIndexer(Revb.DIFF_R_DST - 1);
RevBuffers.APF1_L_SRC = EffectsBufferIndexer(Revb.APF1_L_DST - Revb.APF1_SIZE);
RevBuffers.APF1_R_SRC = EffectsBufferIndexer(Revb.APF1_R_DST - Revb.APF1_SIZE);
RevBuffers.APF2_L_SRC = EffectsBufferIndexer(Revb.APF2_L_DST - Revb.APF2_SIZE);
RevBuffers.APF2_R_SRC = EffectsBufferIndexer(Revb.APF2_R_DST - Revb.APF2_SIZE);
}
void V_Voice::QueueStart()
@ -719,94 +714,94 @@ void V_Core::WriteRegPS1(u32 mem, u16 value)
break;
case 0x1DC0:
Revb.FB_SRC_A = value * 4;
Revb.APF1_SIZE = value * 4;
break;
case 0x1DC2:
Revb.FB_SRC_B = value * 4;
Revb.APF2_SIZE = value * 4;
break;
case 0x1DC4:
Revb.IIR_ALPHA = value;
Revb.IIR_VOL = value;
break;
case 0x1DC6:
Revb.ACC_COEF_A = value;
Revb.COMB1_VOL = value;
break;
case 0x1DC8:
Revb.ACC_COEF_B = value;
Revb.COMB2_VOL = value;
break;
case 0x1DCA:
Revb.ACC_COEF_C = value;
Revb.COMB3_VOL = value;
break;
case 0x1DCC:
Revb.ACC_COEF_D = value;
Revb.COMB4_VOL = value;
break;
case 0x1DCE:
Revb.IIR_COEF = value;
Revb.WALL_VOL = value;
break;
case 0x1DD0:
Revb.FB_ALPHA = value;
Revb.APF1_VOL = value;
break;
case 0x1DD2:
Revb.FB_X = value;
Revb.APF2_VOL = value;
break;
case 0x1DD4:
Revb.IIR_DEST_A0 = value * 4;
Revb.SAME_L_DST = value * 4;
break;
case 0x1DD6:
Revb.IIR_DEST_A1 = value * 4;
Revb.SAME_R_DST = value * 4;
break;
case 0x1DD8:
Revb.ACC_SRC_A0 = value * 4;
Revb.COMB1_L_SRC = value * 4;
break;
case 0x1DDA:
Revb.ACC_SRC_A1 = value * 4;
Revb.COMB1_R_SRC = value * 4;
break;
case 0x1DDC:
Revb.ACC_SRC_B0 = value * 4;
Revb.COMB2_L_SRC = value * 4;
break;
case 0x1DDE:
Revb.ACC_SRC_B1 = value * 4;
Revb.COMB2_R_SRC = value * 4;
break;
case 0x1DE0:
Revb.IIR_SRC_A0 = value * 4;
Revb.SAME_L_SRC = value * 4;
break;
case 0x1DE2:
Revb.IIR_SRC_A1 = value * 4;
Revb.SAME_R_SRC = value * 4;
break;
case 0x1DE4:
Revb.IIR_DEST_B0 = value * 4;
Revb.DIFF_L_DST = value * 4;
break;
case 0x1DE6:
Revb.IIR_DEST_B1 = value * 4;
Revb.DIFF_R_DST = value * 4;
break;
case 0x1DE8:
Revb.ACC_SRC_C0 = value * 4;
Revb.COMB3_L_SRC = value * 4;
break;
case 0x1DEA:
Revb.ACC_SRC_C1 = value * 4;
Revb.COMB3_R_SRC = value * 4;
break;
case 0x1DEC:
Revb.ACC_SRC_D0 = value * 4;
Revb.COMB4_L_SRC = value * 4;
break;
case 0x1DEE:
Revb.ACC_SRC_D1 = value * 4;
Revb.COMB4_R_SRC = value * 4;
break;
case 0x1DF0:
Revb.IIR_SRC_B0 = value * 4;
break; // IIR_SRC_B0 and IIR_SRC_B1 supposedly swapped on SPU2
Revb.DIFF_L_SRC = value * 4;
break; // DIFF_R_SRC and DIFF_L_SRC supposedly swapped on SPU2
case 0x1DF2:
Revb.IIR_SRC_B1 = value * 4;
Revb.DIFF_R_SRC = value * 4;
break; // but I don't believe it! (games in psxmode sound better unswapped)
case 0x1DF4:
Revb.MIX_DEST_A0 = value * 4;
Revb.APF1_L_DST = value * 4;
break;
case 0x1DF6:
Revb.MIX_DEST_A1 = value * 4;
Revb.APF1_R_DST = value * 4;
break;
case 0x1DF8:
Revb.MIX_DEST_B0 = value * 4;
Revb.APF2_L_DST = value * 4;
break;
case 0x1DFA:
Revb.MIX_DEST_B1 = value * 4;
Revb.APF2_R_DST = value * 4;
break;
case 0x1DFC:
Revb.IN_COEF_L = value;
@ -1550,28 +1545,28 @@ static RegWriteHandler *const tbl_reg_writes[0x401] =
CoreParamsPair(0, REG_A_ESA),
ReverbPair(0, R_FB_SRC_A), // 0x02E4 // Feedback Source A
ReverbPair(0, R_FB_SRC_B), // 0x02E8 // Feedback Source B
ReverbPair(0, R_IIR_DEST_A0), // 0x02EC
ReverbPair(0, R_IIR_DEST_A1), // 0x02F0
ReverbPair(0, R_ACC_SRC_A0), // 0x02F4
ReverbPair(0, R_ACC_SRC_A1), // 0x02F8
ReverbPair(0, R_ACC_SRC_B0), // 0x02FC
ReverbPair(0, R_ACC_SRC_B1), // 0x0300
ReverbPair(0, R_IIR_SRC_A0), // 0x0304
ReverbPair(0, R_IIR_SRC_A1), // 0x0308
ReverbPair(0, R_IIR_DEST_B0), // 0x030C
ReverbPair(0, R_IIR_DEST_B1), // 0x0310
ReverbPair(0, R_ACC_SRC_C0), // 0x0314
ReverbPair(0, R_ACC_SRC_C1), // 0x0318
ReverbPair(0, R_ACC_SRC_D0), // 0x031C
ReverbPair(0, R_ACC_SRC_D1), // 0x0320
ReverbPair(0, R_IIR_SRC_B0), // 0x0324
ReverbPair(0, R_IIR_SRC_B1), // 0x0328
ReverbPair(0, R_MIX_DEST_A0), // 0x032C
ReverbPair(0, R_MIX_DEST_A1), // 0x0330
ReverbPair(0, R_MIX_DEST_B0), // 0x0334
ReverbPair(0, R_MIX_DEST_B1), // 0x0338
ReverbPair(0, R_APF1_SIZE), // 0x02E4 // Feedback Source A
ReverbPair(0, R_APF2_SIZE), // 0x02E8 // Feedback Source B
ReverbPair(0, R_SAME_L_DST), // 0x02EC
ReverbPair(0, R_SAME_R_DST), // 0x02F0
ReverbPair(0, R_COMB1_L_SRC), // 0x02F4
ReverbPair(0, R_COMB1_R_SRC), // 0x02F8
ReverbPair(0, R_COMB2_L_SRC), // 0x02FC
ReverbPair(0, R_COMB2_R_SRC), // 0x0300
ReverbPair(0, R_SAME_L_SRC), // 0x0304
ReverbPair(0, R_SAME_R_SRC), // 0x0308
ReverbPair(0, R_DIFF_L_DST), // 0x030C
ReverbPair(0, R_DIFF_R_DST), // 0x0310
ReverbPair(0, R_COMB3_L_SRC), // 0x0314
ReverbPair(0, R_COMB3_R_SRC), // 0x0318
ReverbPair(0, R_COMB4_L_SRC), // 0x031C
ReverbPair(0, R_COMB4_R_SRC), // 0x0320
ReverbPair(0, R_DIFF_L_SRC), // 0x0324
ReverbPair(0, R_DIFF_R_SRC), // 0x0328
ReverbPair(0, R_APF1_L_DST), // 0x032C
ReverbPair(0, R_APF1_R_DST), // 0x0330
ReverbPair(0, R_APF2_L_DST), // 0x0334
ReverbPair(0, R_APF2_R_DST), // 0x0338
RegWrite_Core<0, REG_A_EEA>, RegWrite_Null,
@ -1640,28 +1635,28 @@ static RegWriteHandler *const tbl_reg_writes[0x401] =
CoreParamsPair(1, REG_A_ESA),
ReverbPair(1, R_FB_SRC_A), // 0x02E4 // Feedback Source A
ReverbPair(1, R_FB_SRC_B), // 0x02E8 // Feedback Source B
ReverbPair(1, R_IIR_DEST_A0), // 0x02EC
ReverbPair(1, R_IIR_DEST_A1), // 0x02F0
ReverbPair(1, R_ACC_SRC_A0), // 0x02F4
ReverbPair(1, R_ACC_SRC_A1), // 0x02F8
ReverbPair(1, R_ACC_SRC_B0), // 0x02FC
ReverbPair(1, R_ACC_SRC_B1), // 0x0300
ReverbPair(1, R_IIR_SRC_A0), // 0x0304
ReverbPair(1, R_IIR_SRC_A1), // 0x0308
ReverbPair(1, R_IIR_DEST_B0), // 0x030C
ReverbPair(1, R_IIR_DEST_B1), // 0x0310
ReverbPair(1, R_ACC_SRC_C0), // 0x0314
ReverbPair(1, R_ACC_SRC_C1), // 0x0318
ReverbPair(1, R_ACC_SRC_D0), // 0x031C
ReverbPair(1, R_ACC_SRC_D1), // 0x0320
ReverbPair(1, R_IIR_SRC_B0), // 0x0324
ReverbPair(1, R_IIR_SRC_B1), // 0x0328
ReverbPair(1, R_MIX_DEST_A0), // 0x032C
ReverbPair(1, R_MIX_DEST_A1), // 0x0330
ReverbPair(1, R_MIX_DEST_B0), // 0x0334
ReverbPair(1, R_MIX_DEST_B1), // 0x0338
ReverbPair(1, R_APF1_SIZE), // 0x02E4 // Feedback Source A
ReverbPair(1, R_APF2_SIZE), // 0x02E8 // Feedback Source B
ReverbPair(1, R_SAME_L_DST), // 0x02EC
ReverbPair(1, R_SAME_R_DST), // 0x02F0
ReverbPair(1, R_COMB1_L_SRC), // 0x02F4
ReverbPair(1, R_COMB1_R_SRC), // 0x02F8
ReverbPair(1, R_COMB2_L_SRC), // 0x02FC
ReverbPair(1, R_COMB2_R_SRC), // 0x0300
ReverbPair(1, R_SAME_L_SRC), // 0x0304
ReverbPair(1, R_SAME_R_SRC), // 0x0308
ReverbPair(1, R_DIFF_L_DST), // 0x030C
ReverbPair(1, R_DIFF_R_DST), // 0x0310
ReverbPair(1, R_COMB3_L_SRC), // 0x0314
ReverbPair(1, R_COMB3_R_SRC), // 0x0318
ReverbPair(1, R_COMB4_L_SRC), // 0x031C
ReverbPair(1, R_COMB4_R_SRC), // 0x0320
ReverbPair(1, R_DIFF_R_SRC), // 0x0324
ReverbPair(1, R_DIFF_L_SRC), // 0x0328
ReverbPair(1, R_APF1_L_DST), // 0x032C
ReverbPair(1, R_APF1_R_DST), // 0x0330
ReverbPair(1, R_APF2_L_DST), // 0x0334
ReverbPair(1, R_APF2_R_DST), // 0x0338
RegWrite_Core<1, REG_A_EEA>, RegWrite_Null,
@ -1686,14 +1681,14 @@ static RegWriteHandler *const tbl_reg_writes[0x401] =
RegWrite_CoreExt<0, REG_P_MVOLXL>, // 0x0770 // Current Master Volume Left
RegWrite_CoreExt<0, REG_P_MVOLXR>, // 0x0772 // Current Master Volume Right
RegWrite_CoreExt<0, R_IIR_ALPHA>, // 0x0774 //IIR alpha (% used)
RegWrite_CoreExt<0, R_ACC_COEF_A>, // 0x0776
RegWrite_CoreExt<0, R_ACC_COEF_B>, // 0x0778
RegWrite_CoreExt<0, R_ACC_COEF_C>, // 0x077A
RegWrite_CoreExt<0, R_ACC_COEF_D>, // 0x077C
RegWrite_CoreExt<0, R_IIR_COEF>, // 0x077E
RegWrite_CoreExt<0, R_FB_ALPHA>, // 0x0780 //feedback alpha (% used)
RegWrite_CoreExt<0, R_FB_X>, // 0x0782 //feedback
RegWrite_CoreExt<0, R_IIR_VOL>, // 0x0774 //IIR alpha (% used)
RegWrite_CoreExt<0, R_COMB1_VOL>, // 0x0776
RegWrite_CoreExt<0, R_COMB2_VOL>, // 0x0778
RegWrite_CoreExt<0, R_COMB3_VOL>, // 0x077A
RegWrite_CoreExt<0, R_COMB4_VOL>, // 0x077C
RegWrite_CoreExt<0, R_WALL_VOL>, // 0x077E
RegWrite_CoreExt<0, R_APF1_VOL>, // 0x0780 //feedback alpha (% used)
RegWrite_CoreExt<0, R_APF2_VOL>, // 0x0782 //feedback
RegWrite_CoreExt<0, R_IN_COEF_L>, // 0x0784
RegWrite_CoreExt<0, R_IN_COEF_R>, // 0x0786
@ -1710,14 +1705,14 @@ static RegWriteHandler *const tbl_reg_writes[0x401] =
RegWrite_CoreExt<1, REG_P_MVOLXL>, // 0x0770 // Current Master Volume Left
RegWrite_CoreExt<1, REG_P_MVOLXR>, // 0x0772 // Current Master Volume Right
RegWrite_CoreExt<1, R_IIR_ALPHA>, // 0x0774 //IIR alpha (% used)
RegWrite_CoreExt<1, R_ACC_COEF_A>, // 0x0776
RegWrite_CoreExt<1, R_ACC_COEF_B>, // 0x0778
RegWrite_CoreExt<1, R_ACC_COEF_C>, // 0x077A
RegWrite_CoreExt<1, R_ACC_COEF_D>, // 0x077C
RegWrite_CoreExt<1, R_IIR_COEF>, // 0x077E
RegWrite_CoreExt<1, R_FB_ALPHA>, // 0x0780 //feedback alpha (% used)
RegWrite_CoreExt<1, R_FB_X>, // 0x0782 //feedback
RegWrite_CoreExt<1, R_IIR_VOL>, // 0x0774 //IIR alpha (% used)
RegWrite_CoreExt<1, R_COMB1_VOL>, // 0x0776
RegWrite_CoreExt<1, R_COMB2_VOL>, // 0x0778
RegWrite_CoreExt<1, R_COMB3_VOL>, // 0x077A
RegWrite_CoreExt<1, R_COMB4_VOL>, // 0x077C
RegWrite_CoreExt<1, R_WALL_VOL>, // 0x077E
RegWrite_CoreExt<1, R_APF1_VOL>, // 0x0780 //feedback alpha (% used)
RegWrite_CoreExt<1, R_APF2_VOL>, // 0x0782 //feedback
RegWrite_CoreExt<1, R_IN_COEF_L>, // 0x0784
RegWrite_CoreExt<1, R_IN_COEF_R>, // 0x0786