psxcounters: don't do many spu updates
[pcsx_rearmed.git] / plugins / dfsound / reverb.c
CommitLineData
ef79bbde
P
1/***************************************************************************\r
2 reverb.c - description\r
3 -------------------\r
4 begin : Wed May 15 2002\r
5 copyright : (C) 2002 by Pete Bernert\r
6 email : BlackDove@addcom.de\r
1775933a 7\r
8 Portions (C) GraÅžvydas "notaz" Ignotas, 2010-2011\r
9 Portions (C) SPU2-X, gigaherz, Pcsx2 Development Team\r
10\r
ef79bbde
P
11 ***************************************************************************/\r
12/***************************************************************************\r
13 * *\r
14 * This program is free software; you can redistribute it and/or modify *\r
15 * it under the terms of the GNU General Public License as published by *\r
16 * the Free Software Foundation; either version 2 of the License, or *\r
17 * (at your option) any later version. See also the license.txt file for *\r
18 * additional informations. *\r
19 * *\r
20 ***************************************************************************/\r
21\r
22#include "stdafx.h"\r
23\r
24#define _IN_REVERB\r
25\r
26// will be included from spu.c\r
27#ifdef _IN_SPU\r
28\r
29////////////////////////////////////////////////////////////////////////\r
30// globals\r
31////////////////////////////////////////////////////////////////////////\r
32\r
33// REVERB info and timing vars...\r
34\r
35int * sRVBPlay = 0;\r
36int * sRVBEnd = 0;\r
37int * sRVBStart = 0;\r
ef79bbde
P
38\r
39////////////////////////////////////////////////////////////////////////\r
40// START REVERB\r
41////////////////////////////////////////////////////////////////////////\r
42\r
43INLINE void StartREVERB(int ch)\r
44{\r
45 if(s_chan[ch].bReverb && (spuCtrl&0x80)) // reverb possible?\r
46 {\r
1775933a 47 s_chan[ch].bRVBActive=!!iUseReverb;\r
ef79bbde
P
48 }\r
49 else s_chan[ch].bRVBActive=0; // else -> no reverb\r
50}\r
51\r
52////////////////////////////////////////////////////////////////////////\r
53// HELPER FOR NEILL'S REVERB: re-inits our reverb mixing buf\r
54////////////////////////////////////////////////////////////////////////\r
55\r
650adfd2 56INLINE void InitREVERB(int ns_to)\r
ef79bbde 57{\r
650adfd2 58 memset(sRVBStart,0,ns_to*sizeof(sRVBStart[0])*2);\r
ef79bbde
P
59}\r
60\r
61////////////////////////////////////////////////////////////////////////\r
62\r
1775933a 63INLINE int rvb2ram_offs(int curr, int space, int iOff)\r
ef79bbde 64{\r
1775933a 65 iOff += curr;\r
66 if (iOff >= 0x40000) iOff -= space;\r
67 return iOff;\r
ef79bbde
P
68}\r
69\r
1775933a 70// get_buffer content helper: takes care about wraps\r
71#define g_buffer(var) \\r
72 ((int)(signed short)spuMem[rvb2ram_offs(curr_addr, space, rvb.n##var)])\r
ef79bbde 73\r
1775933a 74// saturate iVal and store it as var\r
75#define s_buffer(var, iVal) \\r
76 ssat32_to_16(iVal); \\r
77 spuMem[rvb2ram_offs(curr_addr, space, rvb.n##var)] = iVal\r
ef79bbde 78\r
1775933a 79#define s_buffer1(var, iVal) \\r
80 ssat32_to_16(iVal); \\r
81 spuMem[rvb2ram_offs(curr_addr, space, rvb.n##var + 1)] = iVal\r
ef79bbde
P
82\r
83////////////////////////////////////////////////////////////////////////\r
84\r
1775933a 85// portions based on spu2-x from PCSX2\r
650adfd2 86static void MixREVERB(int ns_to)\r
ef79bbde 87{\r
1775933a 88 int l_old = rvb.iRVBLeft;\r
89 int r_old = rvb.iRVBRight;\r
90 int curr_addr = rvb.CurrAddr;\r
91 int space = 0x40000 - rvb.StartAddr;\r
650adfd2 92 int l = 0, r = 0, ns;\r
1775933a 93\r
650adfd2 94 for (ns = 0; ns < ns_to * 2; )\r
ef79bbde 95 {\r
1775933a 96 int IIR_ALPHA = rvb.IIR_ALPHA;\r
97 int ACC0, ACC1, FB_A0, FB_A1, FB_B0, FB_B1;\r
98 int mix_dest_a0, mix_dest_a1, mix_dest_b0, mix_dest_b1;\r
99\r
100 int input_L = sRVBStart[ns] * rvb.IN_COEF_L;\r
101 int input_R = sRVBStart[ns+1] * rvb.IN_COEF_R;\r
102\r
103 int IIR_INPUT_A0 = ((g_buffer(IIR_SRC_A0) * rvb.IIR_COEF) + input_L) >> 15;\r
104 int IIR_INPUT_A1 = ((g_buffer(IIR_SRC_A1) * rvb.IIR_COEF) + input_R) >> 15;\r
105 int IIR_INPUT_B0 = ((g_buffer(IIR_SRC_B0) * rvb.IIR_COEF) + input_L) >> 15;\r
106 int IIR_INPUT_B1 = ((g_buffer(IIR_SRC_B1) * rvb.IIR_COEF) + input_R) >> 15;\r
107\r
108 int iir_dest_a0 = g_buffer(IIR_DEST_A0);\r
109 int iir_dest_a1 = g_buffer(IIR_DEST_A1);\r
110 int iir_dest_b0 = g_buffer(IIR_DEST_B0);\r
111 int iir_dest_b1 = g_buffer(IIR_DEST_B1);\r
112\r
113 int IIR_A0 = iir_dest_a0 + ((IIR_INPUT_A0 - iir_dest_a0) * IIR_ALPHA >> 15);\r
114 int IIR_A1 = iir_dest_a1 + ((IIR_INPUT_A1 - iir_dest_a1) * IIR_ALPHA >> 15);\r
115 int IIR_B0 = iir_dest_b0 + ((IIR_INPUT_B0 - iir_dest_b0) * IIR_ALPHA >> 15);\r
116 int IIR_B1 = iir_dest_b1 + ((IIR_INPUT_B1 - iir_dest_b1) * IIR_ALPHA >> 15);\r
117\r
118 s_buffer1(IIR_DEST_A0, IIR_A0);\r
119 s_buffer1(IIR_DEST_A1, IIR_A1);\r
120 s_buffer1(IIR_DEST_B0, IIR_B0);\r
121 s_buffer1(IIR_DEST_B1, IIR_B1);\r
122\r
123 ACC0 = (g_buffer(ACC_SRC_A0) * rvb.ACC_COEF_A +\r
124 g_buffer(ACC_SRC_B0) * rvb.ACC_COEF_B +\r
125 g_buffer(ACC_SRC_C0) * rvb.ACC_COEF_C +\r
126 g_buffer(ACC_SRC_D0) * rvb.ACC_COEF_D) >> 15;\r
127 ACC1 = (g_buffer(ACC_SRC_A1) * rvb.ACC_COEF_A +\r
128 g_buffer(ACC_SRC_B1) * rvb.ACC_COEF_B +\r
129 g_buffer(ACC_SRC_C1) * rvb.ACC_COEF_C +\r
130 g_buffer(ACC_SRC_D1) * rvb.ACC_COEF_D) >> 15;\r
131\r
132 FB_A0 = g_buffer(FB_SRC_A0);\r
133 FB_A1 = g_buffer(FB_SRC_A1);\r
134 FB_B0 = g_buffer(FB_SRC_B0);\r
135 FB_B1 = g_buffer(FB_SRC_B1);\r
136\r
137 mix_dest_a0 = ACC0 - ((FB_A0 * rvb.FB_ALPHA) >> 15);\r
138 mix_dest_a1 = ACC1 - ((FB_A1 * rvb.FB_ALPHA) >> 15);\r
139\r
140 mix_dest_b0 = FB_A0 + (((ACC0 - FB_A0) * rvb.FB_ALPHA - FB_B0 * rvb.FB_X) >> 15);\r
141 mix_dest_b1 = FB_A1 + (((ACC1 - FB_A1) * rvb.FB_ALPHA - FB_B1 * rvb.FB_X) >> 15);\r
142\r
143 s_buffer(MIX_DEST_A0, mix_dest_a0);\r
144 s_buffer(MIX_DEST_A1, mix_dest_a1);\r
145 s_buffer(MIX_DEST_B0, mix_dest_b0);\r
146 s_buffer(MIX_DEST_B1, mix_dest_b1);\r
147\r
b72f17a1 148 l = (mix_dest_a0 + mix_dest_b0) / 2;\r
149 r = (mix_dest_a1 + mix_dest_b1) / 2;\r
1775933a 150\r
b72f17a1 151 l = (l * rvb.VolLeft) >> 15; // 15?\r
152 r = (r * rvb.VolRight) >> 15;\r
153\r
154 SSumLR[ns++] += (l + l_old) / 2;\r
155 SSumLR[ns++] += (r + r_old) / 2;\r
156 SSumLR[ns++] += l;\r
157 SSumLR[ns++] += r;\r
158\r
159 l_old = l;\r
160 r_old = r;\r
161\r
162 curr_addr++;\r
163 if (curr_addr >= 0x40000) curr_addr = rvb.StartAddr;\r
164 }\r
165\r
166 rvb.iRVBLeft = l;\r
167 rvb.iRVBRight = r;\r
168 rvb.CurrAddr = curr_addr;\r
169}\r
170\r
650adfd2 171static void MixREVERB_off(int ns_to)\r
b72f17a1 172{\r
173 int l_old = rvb.iRVBLeft;\r
174 int r_old = rvb.iRVBRight;\r
175 int curr_addr = rvb.CurrAddr;\r
176 int space = 0x40000 - rvb.StartAddr;\r
650adfd2 177 int l = 0, r = 0, ns;\r
b72f17a1 178\r
650adfd2 179 for (ns = 0; ns < ns_to * 2; )\r
b72f17a1 180 {\r
181 l = (g_buffer(MIX_DEST_A0) + g_buffer(MIX_DEST_B0)) / 2;\r
182 r = (g_buffer(MIX_DEST_A1) + g_buffer(MIX_DEST_B1)) / 2;\r
183\r
184 l = (l * rvb.VolLeft) >> 15;\r
185 r = (r * rvb.VolRight) >> 15;\r
1775933a 186\r
187 SSumLR[ns++] += (l + l_old) / 2;\r
188 SSumLR[ns++] += (r + r_old) / 2;\r
189 SSumLR[ns++] += l;\r
190 SSumLR[ns++] += r;\r
191\r
192 l_old = l;\r
193 r_old = r;\r
194\r
195 curr_addr++;\r
196 if (curr_addr >= 0x40000) curr_addr = rvb.StartAddr;\r
ef79bbde 197 }\r
1775933a 198\r
199 rvb.iRVBLeft = l;\r
200 rvb.iRVBRight = r;\r
201 rvb.CurrAddr = curr_addr;\r
ef79bbde
P
202}\r
203\r
1775933a 204static void prepare_offsets(void)\r
205{\r
206 int space = 0x40000 - rvb.StartAddr;\r
207 int t;\r
208 #define prep_offs(v) \\r
209 t = rvb.v; \\r
210 while (t >= space) \\r
211 t -= space; \\r
212 rvb.n##v = t\r
213 #define prep_offs2(d, v1, v2) \\r
214 t = rvb.v1 - rvb.v2; \\r
215 while (t >= space) \\r
216 t -= space; \\r
217 rvb.n##d = t\r
218\r
219 prep_offs(IIR_SRC_A0);\r
220 prep_offs(IIR_SRC_A1);\r
221 prep_offs(IIR_SRC_B0);\r
222 prep_offs(IIR_SRC_B1);\r
223 prep_offs(IIR_DEST_A0);\r
224 prep_offs(IIR_DEST_A1);\r
225 prep_offs(IIR_DEST_B0);\r
226 prep_offs(IIR_DEST_B1);\r
227 prep_offs(ACC_SRC_A0);\r
228 prep_offs(ACC_SRC_A1);\r
229 prep_offs(ACC_SRC_B0);\r
230 prep_offs(ACC_SRC_B1);\r
231 prep_offs(ACC_SRC_C0);\r
232 prep_offs(ACC_SRC_C1);\r
233 prep_offs(ACC_SRC_D0);\r
234 prep_offs(ACC_SRC_D1);\r
235 prep_offs(MIX_DEST_A0);\r
236 prep_offs(MIX_DEST_A1);\r
237 prep_offs(MIX_DEST_B0);\r
238 prep_offs(MIX_DEST_B1);\r
239 prep_offs2(FB_SRC_A0, MIX_DEST_A0, FB_SRC_A);\r
240 prep_offs2(FB_SRC_A1, MIX_DEST_A1, FB_SRC_A);\r
241 prep_offs2(FB_SRC_B0, MIX_DEST_B0, FB_SRC_B);\r
242 prep_offs2(FB_SRC_B1, MIX_DEST_B1, FB_SRC_B);\r
243\r
244#undef prep_offs\r
245#undef prep_offs2\r
246 rvb.dirty = 0;\r
247}\r
ef79bbde 248\r
650adfd2 249INLINE void REVERBDo(int ns_to)\r
ef79bbde 250{\r
1775933a 251 if (!rvb.StartAddr) // reverb is off\r
252 {\r
253 rvb.iRVBLeft = rvb.iRVBRight = 0;\r
254 return;\r
255 }\r
256\r
257 if (spuCtrl & 0x80) // -> reverb on? oki\r
258 {\r
b72f17a1 259 if (unlikely(rvb.dirty))\r
1775933a 260 prepare_offsets();\r
261\r
650adfd2 262 MixREVERB(ns_to);\r
1775933a 263 }\r
b72f17a1 264 else if (rvb.VolLeft || rvb.VolRight)\r
265 {\r
266 if (unlikely(rvb.dirty))\r
267 prepare_offsets();\r
268\r
650adfd2 269 MixREVERB_off(ns_to);\r
b72f17a1 270 }\r
1775933a 271 else // -> reverb off\r
272 {\r
b72f17a1 273 // reverb runs anyway\r
650adfd2 274 rvb.CurrAddr += ns_to / 2;\r
1775933a 275 while (rvb.CurrAddr >= 0x40000)\r
276 rvb.CurrAddr -= 0x40000 - rvb.StartAddr;\r
277 }\r
ef79bbde
P
278}\r
279\r
280////////////////////////////////////////////////////////////////////////\r
281\r
282#endif\r
283\r
284/*\r
285-----------------------------------------------------------------------------\r
286PSX reverb hardware notes\r
287by Neill Corlett\r
288-----------------------------------------------------------------------------\r
289\r
290Yadda yadda disclaimer yadda probably not perfect yadda well it's okay anyway\r
291yadda yadda.\r
292\r
293-----------------------------------------------------------------------------\r
294\r
295Basics\r
296------\r
297\r
298- The reverb buffer is 22khz 16-bit mono PCM.\r
299- It starts at the reverb address given by 1DA2, extends to\r
300 the end of sound RAM, and wraps back to the 1DA2 address.\r
301\r
302Setting the address at 1DA2 resets the current reverb work address.\r
303\r
304This work address ALWAYS increments every 1/22050 sec., regardless of\r
305whether reverb is enabled (bit 7 of 1DAA set).\r
306\r
307And the contents of the reverb buffer ALWAYS play, scaled by the\r
308"reverberation depth left/right" volumes (1D84/1D86).\r
309(which, by the way, appear to be scaled so 3FFF=approx. 1.0, 4000=-1.0)\r
310\r
311-----------------------------------------------------------------------------\r
312\r
313Register names\r
314--------------\r
315\r
316These are probably not their real names.\r
317These are probably not even correct names.\r
318We will use them anyway, because we can.\r
319\r
3201DC0: FB_SRC_A (offset)\r
3211DC2: FB_SRC_B (offset)\r
3221DC4: IIR_ALPHA (coef.)\r
3231DC6: ACC_COEF_A (coef.)\r
3241DC8: ACC_COEF_B (coef.)\r
3251DCA: ACC_COEF_C (coef.)\r
3261DCC: ACC_COEF_D (coef.)\r
3271DCE: IIR_COEF (coef.)\r
3281DD0: FB_ALPHA (coef.)\r
3291DD2: FB_X (coef.)\r
3301DD4: IIR_DEST_A0 (offset)\r
3311DD6: IIR_DEST_A1 (offset)\r
3321DD8: ACC_SRC_A0 (offset)\r
3331DDA: ACC_SRC_A1 (offset)\r
3341DDC: ACC_SRC_B0 (offset)\r
3351DDE: ACC_SRC_B1 (offset)\r
3361DE0: IIR_SRC_A0 (offset)\r
3371DE2: IIR_SRC_A1 (offset)\r
3381DE4: IIR_DEST_B0 (offset)\r
3391DE6: IIR_DEST_B1 (offset)\r
3401DE8: ACC_SRC_C0 (offset)\r
3411DEA: ACC_SRC_C1 (offset)\r
3421DEC: ACC_SRC_D0 (offset)\r
3431DEE: ACC_SRC_D1 (offset)\r
3441DF0: IIR_SRC_B1 (offset)\r
3451DF2: IIR_SRC_B0 (offset)\r
3461DF4: MIX_DEST_A0 (offset)\r
3471DF6: MIX_DEST_A1 (offset)\r
3481DF8: MIX_DEST_B0 (offset)\r
3491DFA: MIX_DEST_B1 (offset)\r
3501DFC: IN_COEF_L (coef.)\r
3511DFE: IN_COEF_R (coef.)\r
352\r
353The coefficients are signed fractional values.\r
354-32768 would be -1.0\r
355 32768 would be 1.0 (if it were possible... the highest is of course 32767)\r
356\r
357The offsets are (byte/8) offsets into the reverb buffer.\r
358i.e. you multiply them by 8, you get byte offsets.\r
359You can also think of them as (samples/4) offsets.\r
360They appear to be signed. They can be negative.\r
361None of the documented presets make them negative, though.\r
362\r
363Yes, 1DF0 and 1DF2 appear to be backwards. Not a typo.\r
364\r
365-----------------------------------------------------------------------------\r
366\r
367What it does\r
368------------\r
369\r
370We take all reverb sources:\r
371- regular channels that have the reverb bit on\r
372- cd and external sources, if their reverb bits are on\r
373and mix them into one stereo 44100hz signal.\r
374\r
375Lowpass/downsample that to 22050hz. The PSX uses a proper bandlimiting\r
376algorithm here, but I haven't figured out the hysterically exact specifics.\r
377I use an 8-tap filter with these coefficients, which are nice but probably\r
378not the real ones:\r
379\r
3800.037828187894\r
3810.157538631280\r
3820.321159685278\r
3830.449322115345\r
3840.449322115345\r
3850.321159685278\r
3860.157538631280\r
3870.037828187894\r
388\r
389So we have two input samples (INPUT_SAMPLE_L, INPUT_SAMPLE_R) every 22050hz.\r
390\r
391* IN MY EMULATION, I divide these by 2 to make it clip less.\r
392 (and of course the L/R output coefficients are adjusted to compensate)\r
393 The real thing appears to not do this.\r
394\r
395At every 22050hz tick:\r
396- If the reverb bit is enabled (bit 7 of 1DAA), execute the reverb\r
397 steady-state algorithm described below\r
398- AFTERWARDS, retrieve the "wet out" L and R samples from the reverb buffer\r
399 (This part may not be exactly right and I guessed at the coefs. TODO: check later.)\r
400 L is: 0.333 * (buffer[MIX_DEST_A0] + buffer[MIX_DEST_B0])\r
401 R is: 0.333 * (buffer[MIX_DEST_A1] + buffer[MIX_DEST_B1])\r
402- Advance the current buffer position by 1 sample\r
403\r
404The wet out L and R are then upsampled to 44100hz and played at the\r
405"reverberation depth left/right" (1D84/1D86) volume, independent of the main\r
406volume.\r
407\r
408-----------------------------------------------------------------------------\r
409\r
410Reverb steady-state\r
411-------------------\r
412\r
413The reverb steady-state algorithm is fairly clever, and of course by\r
414"clever" I mean "batshit insane".\r
415\r
416buffer[x] is relative to the current buffer position, not the beginning of\r
417the buffer. Note that all buffer offsets must wrap around so they're\r
418contained within the reverb work area.\r
419\r
420Clipping is performed at the end... maybe also sooner, but definitely at\r
421the end.\r
422\r
423IIR_INPUT_A0 = buffer[IIR_SRC_A0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;\r
424IIR_INPUT_A1 = buffer[IIR_SRC_A1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;\r
425IIR_INPUT_B0 = buffer[IIR_SRC_B0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;\r
426IIR_INPUT_B1 = buffer[IIR_SRC_B1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;\r
427\r
428IIR_A0 = IIR_INPUT_A0 * IIR_ALPHA + buffer[IIR_DEST_A0] * (1.0 - IIR_ALPHA);\r
429IIR_A1 = IIR_INPUT_A1 * IIR_ALPHA + buffer[IIR_DEST_A1] * (1.0 - IIR_ALPHA);\r
430IIR_B0 = IIR_INPUT_B0 * IIR_ALPHA + buffer[IIR_DEST_B0] * (1.0 - IIR_ALPHA);\r
431IIR_B1 = IIR_INPUT_B1 * IIR_ALPHA + buffer[IIR_DEST_B1] * (1.0 - IIR_ALPHA);\r
432\r
433buffer[IIR_DEST_A0 + 1sample] = IIR_A0;\r
434buffer[IIR_DEST_A1 + 1sample] = IIR_A1;\r
435buffer[IIR_DEST_B0 + 1sample] = IIR_B0;\r
436buffer[IIR_DEST_B1 + 1sample] = IIR_B1;\r
437\r
438ACC0 = buffer[ACC_SRC_A0] * ACC_COEF_A +\r
439 buffer[ACC_SRC_B0] * ACC_COEF_B +\r
440 buffer[ACC_SRC_C0] * ACC_COEF_C +\r
441 buffer[ACC_SRC_D0] * ACC_COEF_D;\r
442ACC1 = buffer[ACC_SRC_A1] * ACC_COEF_A +\r
443 buffer[ACC_SRC_B1] * ACC_COEF_B +\r
444 buffer[ACC_SRC_C1] * ACC_COEF_C +\r
445 buffer[ACC_SRC_D1] * ACC_COEF_D;\r
446\r
447FB_A0 = buffer[MIX_DEST_A0 - FB_SRC_A];\r
448FB_A1 = buffer[MIX_DEST_A1 - FB_SRC_A];\r
449FB_B0 = buffer[MIX_DEST_B0 - FB_SRC_B];\r
450FB_B1 = buffer[MIX_DEST_B1 - FB_SRC_B];\r
451\r
452buffer[MIX_DEST_A0] = ACC0 - FB_A0 * FB_ALPHA;\r
453buffer[MIX_DEST_A1] = ACC1 - FB_A1 * FB_ALPHA;\r
454buffer[MIX_DEST_B0] = (FB_ALPHA * ACC0) - FB_A0 * (FB_ALPHA^0x8000) - FB_B0 * FB_X;\r
455buffer[MIX_DEST_B1] = (FB_ALPHA * ACC1) - FB_A1 * (FB_ALPHA^0x8000) - FB_B1 * FB_X;\r
456\r
457-----------------------------------------------------------------------------\r
458*/\r
459\r
1775933a 460// vim:shiftwidth=1:expandtab\r