0a94ff4a5696490937a92bd3e50659544e1a90ef
[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(int ns_to)\r
57 {\r
58  memset(sRVBStart,0,ns_to*sizeof(sRVBStart[0])*2);\r
59 }\r
60 \r
61 ////////////////////////////////////////////////////////////////////////\r
62 \r
63 INLINE int rvb2ram_offs(int curr, int space, int iOff)\r
64 {\r
65  iOff += curr;\r
66  if (iOff >= 0x40000) iOff -= space;\r
67  return iOff;\r
68 }\r
69 \r
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
73 \r
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
78 \r
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
82 \r
83 ////////////////////////////////////////////////////////////////////////\r
84 \r
85 // portions based on spu2-x from PCSX2\r
86 static void MixREVERB(int ns_to)\r
87 {\r
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
92  int l = 0, r = 0, ns;\r
93 \r
94  for (ns = 0; ns < ns_to * 2; )\r
95   {\r
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
148    l = (mix_dest_a0 + mix_dest_b0) / 2;\r
149    r = (mix_dest_a1 + mix_dest_b1) / 2;\r
150 \r
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
171 static void MixREVERB_off(int ns_to)\r
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
177  int l = 0, r = 0, ns;\r
178 \r
179  for (ns = 0; ns < ns_to * 2; )\r
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
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
197   }\r
198 \r
199  rvb.iRVBLeft = l;\r
200  rvb.iRVBRight = r;\r
201  rvb.CurrAddr = curr_addr;\r
202 }\r
203 \r
204 static 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
248 \r
249 INLINE void REVERBDo(int ns_to)\r
250 {\r
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
259   if (unlikely(rvb.dirty))\r
260    prepare_offsets();\r
261 \r
262   MixREVERB(ns_to);\r
263  }\r
264  else if (rvb.VolLeft || rvb.VolRight)\r
265  {\r
266   if (unlikely(rvb.dirty))\r
267    prepare_offsets();\r
268 \r
269   MixREVERB_off(ns_to);\r
270  }\r
271  else                                                  // -> reverb off\r
272  {\r
273   // reverb runs anyway\r
274   rvb.CurrAddr += ns_to / 2;\r
275   while (rvb.CurrAddr >= 0x40000)\r
276    rvb.CurrAddr -= 0x40000 - rvb.StartAddr;\r
277  }\r
278 }\r
279 \r
280 ////////////////////////////////////////////////////////////////////////\r
281 \r
282 #endif\r
283 \r
284 /*\r
285 -----------------------------------------------------------------------------\r
286 PSX reverb hardware notes\r
287 by Neill Corlett\r
288 -----------------------------------------------------------------------------\r
289 \r
290 Yadda yadda disclaimer yadda probably not perfect yadda well it's okay anyway\r
291 yadda yadda.\r
292 \r
293 -----------------------------------------------------------------------------\r
294 \r
295 Basics\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
302 Setting the address at 1DA2 resets the current reverb work address.\r
303 \r
304 This work address ALWAYS increments every 1/22050 sec., regardless of\r
305 whether reverb is enabled (bit 7 of 1DAA set).\r
306 \r
307 And 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
313 Register names\r
314 --------------\r
315 \r
316 These are probably not their real names.\r
317 These are probably not even correct names.\r
318 We will use them anyway, because we can.\r
319 \r
320 1DC0: FB_SRC_A       (offset)\r
321 1DC2: FB_SRC_B       (offset)\r
322 1DC4: IIR_ALPHA      (coef.)\r
323 1DC6: ACC_COEF_A     (coef.)\r
324 1DC8: ACC_COEF_B     (coef.)\r
325 1DCA: ACC_COEF_C     (coef.)\r
326 1DCC: ACC_COEF_D     (coef.)\r
327 1DCE: IIR_COEF       (coef.)\r
328 1DD0: FB_ALPHA       (coef.)\r
329 1DD2: FB_X           (coef.)\r
330 1DD4: IIR_DEST_A0    (offset)\r
331 1DD6: IIR_DEST_A1    (offset)\r
332 1DD8: ACC_SRC_A0     (offset)\r
333 1DDA: ACC_SRC_A1     (offset)\r
334 1DDC: ACC_SRC_B0     (offset)\r
335 1DDE: ACC_SRC_B1     (offset)\r
336 1DE0: IIR_SRC_A0     (offset)\r
337 1DE2: IIR_SRC_A1     (offset)\r
338 1DE4: IIR_DEST_B0    (offset)\r
339 1DE6: IIR_DEST_B1    (offset)\r
340 1DE8: ACC_SRC_C0     (offset)\r
341 1DEA: ACC_SRC_C1     (offset)\r
342 1DEC: ACC_SRC_D0     (offset)\r
343 1DEE: ACC_SRC_D1     (offset)\r
344 1DF0: IIR_SRC_B1     (offset)\r
345 1DF2: IIR_SRC_B0     (offset)\r
346 1DF4: MIX_DEST_A0    (offset)\r
347 1DF6: MIX_DEST_A1    (offset)\r
348 1DF8: MIX_DEST_B0    (offset)\r
349 1DFA: MIX_DEST_B1    (offset)\r
350 1DFC: IN_COEF_L      (coef.)\r
351 1DFE: IN_COEF_R      (coef.)\r
352 \r
353 The 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
357 The offsets are (byte/8) offsets into the reverb buffer.\r
358 i.e. you multiply them by 8, you get byte offsets.\r
359 You can also think of them as (samples/4) offsets.\r
360 They appear to be signed.  They can be negative.\r
361 None of the documented presets make them negative, though.\r
362 \r
363 Yes, 1DF0 and 1DF2 appear to be backwards.  Not a typo.\r
364 \r
365 -----------------------------------------------------------------------------\r
366 \r
367 What it does\r
368 ------------\r
369 \r
370 We 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
373 and mix them into one stereo 44100hz signal.\r
374 \r
375 Lowpass/downsample that to 22050hz.  The PSX uses a proper bandlimiting\r
376 algorithm here, but I haven't figured out the hysterically exact specifics.\r
377 I use an 8-tap filter with these coefficients, which are nice but probably\r
378 not the real ones:\r
379 \r
380 0.037828187894\r
381 0.157538631280\r
382 0.321159685278\r
383 0.449322115345\r
384 0.449322115345\r
385 0.321159685278\r
386 0.157538631280\r
387 0.037828187894\r
388 \r
389 So 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
395 At 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
404 The 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
406 volume.\r
407 \r
408 -----------------------------------------------------------------------------\r
409 \r
410 Reverb steady-state\r
411 -------------------\r
412 \r
413 The reverb steady-state algorithm is fairly clever, and of course by\r
414 "clever" I mean "batshit insane".\r
415 \r
416 buffer[x] is relative to the current buffer position, not the beginning of\r
417 the buffer.  Note that all buffer offsets must wrap around so they're\r
418 contained within the reverb work area.\r
419 \r
420 Clipping is performed at the end... maybe also sooner, but definitely at\r
421 the end.\r
422 \r
423 IIR_INPUT_A0 = buffer[IIR_SRC_A0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;\r
424 IIR_INPUT_A1 = buffer[IIR_SRC_A1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;\r
425 IIR_INPUT_B0 = buffer[IIR_SRC_B0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;\r
426 IIR_INPUT_B1 = buffer[IIR_SRC_B1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;\r
427 \r
428 IIR_A0 = IIR_INPUT_A0 * IIR_ALPHA + buffer[IIR_DEST_A0] * (1.0 - IIR_ALPHA);\r
429 IIR_A1 = IIR_INPUT_A1 * IIR_ALPHA + buffer[IIR_DEST_A1] * (1.0 - IIR_ALPHA);\r
430 IIR_B0 = IIR_INPUT_B0 * IIR_ALPHA + buffer[IIR_DEST_B0] * (1.0 - IIR_ALPHA);\r
431 IIR_B1 = IIR_INPUT_B1 * IIR_ALPHA + buffer[IIR_DEST_B1] * (1.0 - IIR_ALPHA);\r
432 \r
433 buffer[IIR_DEST_A0 + 1sample] = IIR_A0;\r
434 buffer[IIR_DEST_A1 + 1sample] = IIR_A1;\r
435 buffer[IIR_DEST_B0 + 1sample] = IIR_B0;\r
436 buffer[IIR_DEST_B1 + 1sample] = IIR_B1;\r
437 \r
438 ACC0 = 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
442 ACC1 = 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
447 FB_A0 = buffer[MIX_DEST_A0 - FB_SRC_A];\r
448 FB_A1 = buffer[MIX_DEST_A1 - FB_SRC_A];\r
449 FB_B0 = buffer[MIX_DEST_B0 - FB_SRC_B];\r
450 FB_B1 = buffer[MIX_DEST_B1 - FB_SRC_B];\r
451 \r
452 buffer[MIX_DEST_A0] = ACC0 - FB_A0 * FB_ALPHA;\r
453 buffer[MIX_DEST_A1] = ACC1 - FB_A1 * FB_ALPHA;\r
454 buffer[MIX_DEST_B0] = (FB_ALPHA * ACC0) - FB_A0 * (FB_ALPHA^0x8000) - FB_B0 * FB_X;\r
455 buffer[MIX_DEST_B1] = (FB_ALPHA * ACC1) - FB_A1 * (FB_ALPHA^0x8000) - FB_B1 * FB_X;\r
456 \r
457 -----------------------------------------------------------------------------\r
458 */\r
459 \r
460 // vim:shiftwidth=1:expandtab\r