gpu_neon/psx_gpu: workaround for library build
[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) / 3;\r
161    r = (mix_dest_a1 + mix_dest_b1) / 3;\r
162 \r
163    l = (l * rvb.VolLeft)  >> 14;\r
164    r = (r * rvb.VolRight) >> 14;\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 prepare_offsets(void)\r
184 {\r
185  int space = 0x40000 - rvb.StartAddr;\r
186  int t;\r
187  #define prep_offs(v) \\r
188    t = rvb.v; \\r
189    while (t >= space) \\r
190      t -= space; \\r
191    rvb.n##v = t\r
192  #define prep_offs2(d, v1, v2) \\r
193    t = rvb.v1 - rvb.v2; \\r
194    while (t >= space) \\r
195      t -= space; \\r
196    rvb.n##d = t\r
197 \r
198  prep_offs(IIR_SRC_A0);\r
199  prep_offs(IIR_SRC_A1);\r
200  prep_offs(IIR_SRC_B0);\r
201  prep_offs(IIR_SRC_B1);\r
202  prep_offs(IIR_DEST_A0);\r
203  prep_offs(IIR_DEST_A1);\r
204  prep_offs(IIR_DEST_B0);\r
205  prep_offs(IIR_DEST_B1);\r
206  prep_offs(ACC_SRC_A0);\r
207  prep_offs(ACC_SRC_A1);\r
208  prep_offs(ACC_SRC_B0);\r
209  prep_offs(ACC_SRC_B1);\r
210  prep_offs(ACC_SRC_C0);\r
211  prep_offs(ACC_SRC_C1);\r
212  prep_offs(ACC_SRC_D0);\r
213  prep_offs(ACC_SRC_D1);\r
214  prep_offs(MIX_DEST_A0);\r
215  prep_offs(MIX_DEST_A1);\r
216  prep_offs(MIX_DEST_B0);\r
217  prep_offs(MIX_DEST_B1);\r
218  prep_offs2(FB_SRC_A0, MIX_DEST_A0, FB_SRC_A);\r
219  prep_offs2(FB_SRC_A1, MIX_DEST_A1, FB_SRC_A);\r
220  prep_offs2(FB_SRC_B0, MIX_DEST_B0, FB_SRC_B);\r
221  prep_offs2(FB_SRC_B1, MIX_DEST_B1, FB_SRC_B);\r
222 \r
223 #undef prep_offs\r
224 #undef prep_offs2\r
225  rvb.dirty = 0;\r
226 }\r
227 \r
228 INLINE void REVERBDo(void)\r
229 {\r
230  if (!rvb.StartAddr)                                   // reverb is off\r
231  {\r
232   rvb.iRVBLeft = rvb.iRVBRight = 0;\r
233   return;\r
234  }\r
235 \r
236  if (spuCtrl & 0x80)                                   // -> reverb on? oki\r
237  {\r
238   if (rvb.dirty)\r
239    prepare_offsets();\r
240 \r
241   MixREVERB();\r
242  }\r
243  else                                                  // -> reverb off\r
244  {\r
245   // supposedly runs anyway?\r
246   rvb.CurrAddr += NSSIZE/2;\r
247   while (rvb.CurrAddr >= 0x40000)\r
248    rvb.CurrAddr -= 0x40000 - rvb.StartAddr;\r
249  }\r
250 }\r
251 \r
252 ////////////////////////////////////////////////////////////////////////\r
253 \r
254 #endif\r
255 \r
256 /*\r
257 -----------------------------------------------------------------------------\r
258 PSX reverb hardware notes\r
259 by Neill Corlett\r
260 -----------------------------------------------------------------------------\r
261 \r
262 Yadda yadda disclaimer yadda probably not perfect yadda well it's okay anyway\r
263 yadda yadda.\r
264 \r
265 -----------------------------------------------------------------------------\r
266 \r
267 Basics\r
268 ------\r
269 \r
270 - The reverb buffer is 22khz 16-bit mono PCM.\r
271 - It starts at the reverb address given by 1DA2, extends to\r
272   the end of sound RAM, and wraps back to the 1DA2 address.\r
273 \r
274 Setting the address at 1DA2 resets the current reverb work address.\r
275 \r
276 This work address ALWAYS increments every 1/22050 sec., regardless of\r
277 whether reverb is enabled (bit 7 of 1DAA set).\r
278 \r
279 And the contents of the reverb buffer ALWAYS play, scaled by the\r
280 "reverberation depth left/right" volumes (1D84/1D86).\r
281 (which, by the way, appear to be scaled so 3FFF=approx. 1.0, 4000=-1.0)\r
282 \r
283 -----------------------------------------------------------------------------\r
284 \r
285 Register names\r
286 --------------\r
287 \r
288 These are probably not their real names.\r
289 These are probably not even correct names.\r
290 We will use them anyway, because we can.\r
291 \r
292 1DC0: FB_SRC_A       (offset)\r
293 1DC2: FB_SRC_B       (offset)\r
294 1DC4: IIR_ALPHA      (coef.)\r
295 1DC6: ACC_COEF_A     (coef.)\r
296 1DC8: ACC_COEF_B     (coef.)\r
297 1DCA: ACC_COEF_C     (coef.)\r
298 1DCC: ACC_COEF_D     (coef.)\r
299 1DCE: IIR_COEF       (coef.)\r
300 1DD0: FB_ALPHA       (coef.)\r
301 1DD2: FB_X           (coef.)\r
302 1DD4: IIR_DEST_A0    (offset)\r
303 1DD6: IIR_DEST_A1    (offset)\r
304 1DD8: ACC_SRC_A0     (offset)\r
305 1DDA: ACC_SRC_A1     (offset)\r
306 1DDC: ACC_SRC_B0     (offset)\r
307 1DDE: ACC_SRC_B1     (offset)\r
308 1DE0: IIR_SRC_A0     (offset)\r
309 1DE2: IIR_SRC_A1     (offset)\r
310 1DE4: IIR_DEST_B0    (offset)\r
311 1DE6: IIR_DEST_B1    (offset)\r
312 1DE8: ACC_SRC_C0     (offset)\r
313 1DEA: ACC_SRC_C1     (offset)\r
314 1DEC: ACC_SRC_D0     (offset)\r
315 1DEE: ACC_SRC_D1     (offset)\r
316 1DF0: IIR_SRC_B1     (offset)\r
317 1DF2: IIR_SRC_B0     (offset)\r
318 1DF4: MIX_DEST_A0    (offset)\r
319 1DF6: MIX_DEST_A1    (offset)\r
320 1DF8: MIX_DEST_B0    (offset)\r
321 1DFA: MIX_DEST_B1    (offset)\r
322 1DFC: IN_COEF_L      (coef.)\r
323 1DFE: IN_COEF_R      (coef.)\r
324 \r
325 The coefficients are signed fractional values.\r
326 -32768 would be -1.0\r
327  32768 would be  1.0 (if it were possible... the highest is of course 32767)\r
328 \r
329 The offsets are (byte/8) offsets into the reverb buffer.\r
330 i.e. you multiply them by 8, you get byte offsets.\r
331 You can also think of them as (samples/4) offsets.\r
332 They appear to be signed.  They can be negative.\r
333 None of the documented presets make them negative, though.\r
334 \r
335 Yes, 1DF0 and 1DF2 appear to be backwards.  Not a typo.\r
336 \r
337 -----------------------------------------------------------------------------\r
338 \r
339 What it does\r
340 ------------\r
341 \r
342 We take all reverb sources:\r
343 - regular channels that have the reverb bit on\r
344 - cd and external sources, if their reverb bits are on\r
345 and mix them into one stereo 44100hz signal.\r
346 \r
347 Lowpass/downsample that to 22050hz.  The PSX uses a proper bandlimiting\r
348 algorithm here, but I haven't figured out the hysterically exact specifics.\r
349 I use an 8-tap filter with these coefficients, which are nice but probably\r
350 not the real ones:\r
351 \r
352 0.037828187894\r
353 0.157538631280\r
354 0.321159685278\r
355 0.449322115345\r
356 0.449322115345\r
357 0.321159685278\r
358 0.157538631280\r
359 0.037828187894\r
360 \r
361 So we have two input samples (INPUT_SAMPLE_L, INPUT_SAMPLE_R) every 22050hz.\r
362 \r
363 * IN MY EMULATION, I divide these by 2 to make it clip less.\r
364   (and of course the L/R output coefficients are adjusted to compensate)\r
365   The real thing appears to not do this.\r
366 \r
367 At every 22050hz tick:\r
368 - If the reverb bit is enabled (bit 7 of 1DAA), execute the reverb\r
369   steady-state algorithm described below\r
370 - AFTERWARDS, retrieve the "wet out" L and R samples from the reverb buffer\r
371   (This part may not be exactly right and I guessed at the coefs. TODO: check later.)\r
372   L is: 0.333 * (buffer[MIX_DEST_A0] + buffer[MIX_DEST_B0])\r
373   R is: 0.333 * (buffer[MIX_DEST_A1] + buffer[MIX_DEST_B1])\r
374 - Advance the current buffer position by 1 sample\r
375 \r
376 The wet out L and R are then upsampled to 44100hz and played at the\r
377 "reverberation depth left/right" (1D84/1D86) volume, independent of the main\r
378 volume.\r
379 \r
380 -----------------------------------------------------------------------------\r
381 \r
382 Reverb steady-state\r
383 -------------------\r
384 \r
385 The reverb steady-state algorithm is fairly clever, and of course by\r
386 "clever" I mean "batshit insane".\r
387 \r
388 buffer[x] is relative to the current buffer position, not the beginning of\r
389 the buffer.  Note that all buffer offsets must wrap around so they're\r
390 contained within the reverb work area.\r
391 \r
392 Clipping is performed at the end... maybe also sooner, but definitely at\r
393 the end.\r
394 \r
395 IIR_INPUT_A0 = buffer[IIR_SRC_A0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;\r
396 IIR_INPUT_A1 = buffer[IIR_SRC_A1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;\r
397 IIR_INPUT_B0 = buffer[IIR_SRC_B0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;\r
398 IIR_INPUT_B1 = buffer[IIR_SRC_B1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;\r
399 \r
400 IIR_A0 = IIR_INPUT_A0 * IIR_ALPHA + buffer[IIR_DEST_A0] * (1.0 - IIR_ALPHA);\r
401 IIR_A1 = IIR_INPUT_A1 * IIR_ALPHA + buffer[IIR_DEST_A1] * (1.0 - IIR_ALPHA);\r
402 IIR_B0 = IIR_INPUT_B0 * IIR_ALPHA + buffer[IIR_DEST_B0] * (1.0 - IIR_ALPHA);\r
403 IIR_B1 = IIR_INPUT_B1 * IIR_ALPHA + buffer[IIR_DEST_B1] * (1.0 - IIR_ALPHA);\r
404 \r
405 buffer[IIR_DEST_A0 + 1sample] = IIR_A0;\r
406 buffer[IIR_DEST_A1 + 1sample] = IIR_A1;\r
407 buffer[IIR_DEST_B0 + 1sample] = IIR_B0;\r
408 buffer[IIR_DEST_B1 + 1sample] = IIR_B1;\r
409 \r
410 ACC0 = buffer[ACC_SRC_A0] * ACC_COEF_A +\r
411        buffer[ACC_SRC_B0] * ACC_COEF_B +\r
412        buffer[ACC_SRC_C0] * ACC_COEF_C +\r
413        buffer[ACC_SRC_D0] * ACC_COEF_D;\r
414 ACC1 = buffer[ACC_SRC_A1] * ACC_COEF_A +\r
415        buffer[ACC_SRC_B1] * ACC_COEF_B +\r
416        buffer[ACC_SRC_C1] * ACC_COEF_C +\r
417        buffer[ACC_SRC_D1] * ACC_COEF_D;\r
418 \r
419 FB_A0 = buffer[MIX_DEST_A0 - FB_SRC_A];\r
420 FB_A1 = buffer[MIX_DEST_A1 - FB_SRC_A];\r
421 FB_B0 = buffer[MIX_DEST_B0 - FB_SRC_B];\r
422 FB_B1 = buffer[MIX_DEST_B1 - FB_SRC_B];\r
423 \r
424 buffer[MIX_DEST_A0] = ACC0 - FB_A0 * FB_ALPHA;\r
425 buffer[MIX_DEST_A1] = ACC1 - FB_A1 * FB_ALPHA;\r
426 buffer[MIX_DEST_B0] = (FB_ALPHA * ACC0) - FB_A0 * (FB_ALPHA^0x8000) - FB_B0 * FB_X;\r
427 buffer[MIX_DEST_B1] = (FB_ALPHA * ACC1) - FB_A1 * (FB_ALPHA^0x8000) - FB_B1 * FB_X;\r
428 \r
429 -----------------------------------------------------------------------------\r
430 */\r
431 \r
432 // vim:shiftwidth=1:expandtab\r