gpu_neon: integrate Exophase's rasterizer
[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
56INLINE void InitREVERB(void)\r
57{\r
1775933a 58 memset(sRVBStart,0,NSSIZE*2*4);\r
ef79bbde
P
59}\r
60\r
61////////////////////////////////////////////////////////////////////////\r
62// STORE REVERB\r
63////////////////////////////////////////////////////////////////////////\r
64\r
1775933a 65INLINE void StoreREVERB(int ch,int ns,int l,int r)\r
ef79bbde 66{\r
1775933a 67 ns<<=1;\r
ef79bbde 68\r
1775933a 69 sRVBStart[ns] +=l; // -> we mix all active reverb channels into an extra buffer\r
70 sRVBStart[ns+1]+=r;\r
ef79bbde
P
71}\r
72\r
73////////////////////////////////////////////////////////////////////////\r
74\r
1775933a 75INLINE int rvb2ram_offs(int curr, int space, int iOff)\r
ef79bbde 76{\r
1775933a 77 iOff += curr;\r
78 if (iOff >= 0x40000) iOff -= space;\r
79 return iOff;\r
ef79bbde
P
80}\r
81\r
1775933a 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
ef79bbde 85\r
1775933a 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
ef79bbde 90\r
1775933a 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
ef79bbde
P
94\r
95////////////////////////////////////////////////////////////////////////\r
96\r
1775933a 97// portions based on spu2-x from PCSX2\r
98static void MixREVERB(void)\r
ef79bbde 99{\r
1775933a 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
ef79bbde 107 {\r
1775933a 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
ef79bbde 176 }\r
1775933a 177\r
178 rvb.iRVBLeft = l;\r
179 rvb.iRVBRight = r;\r
180 rvb.CurrAddr = curr_addr;\r
ef79bbde
P
181}\r
182\r
1775933a 183static 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
ef79bbde 227\r
1775933a 228INLINE void REVERBDo(void)\r
ef79bbde 229{\r
1775933a 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
ef79bbde
P
250}\r
251\r
252////////////////////////////////////////////////////////////////////////\r
253\r
254#endif\r
255\r
256/*\r
257-----------------------------------------------------------------------------\r
258PSX reverb hardware notes\r
259by Neill Corlett\r
260-----------------------------------------------------------------------------\r
261\r
262Yadda yadda disclaimer yadda probably not perfect yadda well it's okay anyway\r
263yadda yadda.\r
264\r
265-----------------------------------------------------------------------------\r
266\r
267Basics\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
274Setting the address at 1DA2 resets the current reverb work address.\r
275\r
276This work address ALWAYS increments every 1/22050 sec., regardless of\r
277whether reverb is enabled (bit 7 of 1DAA set).\r
278\r
279And 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
285Register names\r
286--------------\r
287\r
288These are probably not their real names.\r
289These are probably not even correct names.\r
290We will use them anyway, because we can.\r
291\r
2921DC0: FB_SRC_A (offset)\r
2931DC2: FB_SRC_B (offset)\r
2941DC4: IIR_ALPHA (coef.)\r
2951DC6: ACC_COEF_A (coef.)\r
2961DC8: ACC_COEF_B (coef.)\r
2971DCA: ACC_COEF_C (coef.)\r
2981DCC: ACC_COEF_D (coef.)\r
2991DCE: IIR_COEF (coef.)\r
3001DD0: FB_ALPHA (coef.)\r
3011DD2: FB_X (coef.)\r
3021DD4: IIR_DEST_A0 (offset)\r
3031DD6: IIR_DEST_A1 (offset)\r
3041DD8: ACC_SRC_A0 (offset)\r
3051DDA: ACC_SRC_A1 (offset)\r
3061DDC: ACC_SRC_B0 (offset)\r
3071DDE: ACC_SRC_B1 (offset)\r
3081DE0: IIR_SRC_A0 (offset)\r
3091DE2: IIR_SRC_A1 (offset)\r
3101DE4: IIR_DEST_B0 (offset)\r
3111DE6: IIR_DEST_B1 (offset)\r
3121DE8: ACC_SRC_C0 (offset)\r
3131DEA: ACC_SRC_C1 (offset)\r
3141DEC: ACC_SRC_D0 (offset)\r
3151DEE: ACC_SRC_D1 (offset)\r
3161DF0: IIR_SRC_B1 (offset)\r
3171DF2: IIR_SRC_B0 (offset)\r
3181DF4: MIX_DEST_A0 (offset)\r
3191DF6: MIX_DEST_A1 (offset)\r
3201DF8: MIX_DEST_B0 (offset)\r
3211DFA: MIX_DEST_B1 (offset)\r
3221DFC: IN_COEF_L (coef.)\r
3231DFE: IN_COEF_R (coef.)\r
324\r
325The 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
329The offsets are (byte/8) offsets into the reverb buffer.\r
330i.e. you multiply them by 8, you get byte offsets.\r
331You can also think of them as (samples/4) offsets.\r
332They appear to be signed. They can be negative.\r
333None of the documented presets make them negative, though.\r
334\r
335Yes, 1DF0 and 1DF2 appear to be backwards. Not a typo.\r
336\r
337-----------------------------------------------------------------------------\r
338\r
339What it does\r
340------------\r
341\r
342We 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
345and mix them into one stereo 44100hz signal.\r
346\r
347Lowpass/downsample that to 22050hz. The PSX uses a proper bandlimiting\r
348algorithm here, but I haven't figured out the hysterically exact specifics.\r
349I use an 8-tap filter with these coefficients, which are nice but probably\r
350not the real ones:\r
351\r
3520.037828187894\r
3530.157538631280\r
3540.321159685278\r
3550.449322115345\r
3560.449322115345\r
3570.321159685278\r
3580.157538631280\r
3590.037828187894\r
360\r
361So 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
367At 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
376The 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
378volume.\r
379\r
380-----------------------------------------------------------------------------\r
381\r
382Reverb steady-state\r
383-------------------\r
384\r
385The reverb steady-state algorithm is fairly clever, and of course by\r
386"clever" I mean "batshit insane".\r
387\r
388buffer[x] is relative to the current buffer position, not the beginning of\r
389the buffer. Note that all buffer offsets must wrap around so they're\r
390contained within the reverb work area.\r
391\r
392Clipping is performed at the end... maybe also sooner, but definitely at\r
393the end.\r
394\r
395IIR_INPUT_A0 = buffer[IIR_SRC_A0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;\r
396IIR_INPUT_A1 = buffer[IIR_SRC_A1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;\r
397IIR_INPUT_B0 = buffer[IIR_SRC_B0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;\r
398IIR_INPUT_B1 = buffer[IIR_SRC_B1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;\r
399\r
400IIR_A0 = IIR_INPUT_A0 * IIR_ALPHA + buffer[IIR_DEST_A0] * (1.0 - IIR_ALPHA);\r
401IIR_A1 = IIR_INPUT_A1 * IIR_ALPHA + buffer[IIR_DEST_A1] * (1.0 - IIR_ALPHA);\r
402IIR_B0 = IIR_INPUT_B0 * IIR_ALPHA + buffer[IIR_DEST_B0] * (1.0 - IIR_ALPHA);\r
403IIR_B1 = IIR_INPUT_B1 * IIR_ALPHA + buffer[IIR_DEST_B1] * (1.0 - IIR_ALPHA);\r
404\r
405buffer[IIR_DEST_A0 + 1sample] = IIR_A0;\r
406buffer[IIR_DEST_A1 + 1sample] = IIR_A1;\r
407buffer[IIR_DEST_B0 + 1sample] = IIR_B0;\r
408buffer[IIR_DEST_B1 + 1sample] = IIR_B1;\r
409\r
410ACC0 = 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
414ACC1 = 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
419FB_A0 = buffer[MIX_DEST_A0 - FB_SRC_A];\r
420FB_A1 = buffer[MIX_DEST_A1 - FB_SRC_A];\r
421FB_B0 = buffer[MIX_DEST_B0 - FB_SRC_B];\r
422FB_B1 = buffer[MIX_DEST_B1 - FB_SRC_B];\r
423\r
424buffer[MIX_DEST_A0] = ACC0 - FB_A0 * FB_ALPHA;\r
425buffer[MIX_DEST_A1] = ACC1 - FB_A1 * FB_ALPHA;\r
426buffer[MIX_DEST_B0] = (FB_ALPHA * ACC0) - FB_A0 * (FB_ALPHA^0x8000) - FB_B0 * FB_X;\r
427buffer[MIX_DEST_B1] = (FB_ALPHA * ACC1) - FB_A1 * (FB_ALPHA^0x8000) - FB_B1 * FB_X;\r
428\r
429-----------------------------------------------------------------------------\r
430*/\r
431\r
1775933a 432// vim:shiftwidth=1:expandtab\r