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