allow multiple sound drivers to be compiled
[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
b72f17a1 160 l = (mix_dest_a0 + mix_dest_b0) / 2;\r
161 r = (mix_dest_a1 + mix_dest_b1) / 2;\r
1775933a 162\r
b72f17a1 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
183static 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
1775933a 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
ef79bbde 209 }\r
1775933a 210\r
211 rvb.iRVBLeft = l;\r
212 rvb.iRVBRight = r;\r
213 rvb.CurrAddr = curr_addr;\r
ef79bbde
P
214}\r
215\r
1775933a 216static 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
ef79bbde 260\r
1775933a 261INLINE void REVERBDo(void)\r
ef79bbde 262{\r
1775933a 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
b72f17a1 271 if (unlikely(rvb.dirty))\r
1775933a 272 prepare_offsets();\r
273\r
274 MixREVERB();\r
275 }\r
b72f17a1 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
1775933a 283 else // -> reverb off\r
284 {\r
b72f17a1 285 // reverb runs anyway\r
1775933a 286 rvb.CurrAddr += NSSIZE/2;\r
287 while (rvb.CurrAddr >= 0x40000)\r
288 rvb.CurrAddr -= 0x40000 - rvb.StartAddr;\r
289 }\r
ef79bbde
P
290}\r
291\r
292////////////////////////////////////////////////////////////////////////\r
293\r
294#endif\r
295\r
296/*\r
297-----------------------------------------------------------------------------\r
298PSX reverb hardware notes\r
299by Neill Corlett\r
300-----------------------------------------------------------------------------\r
301\r
302Yadda yadda disclaimer yadda probably not perfect yadda well it's okay anyway\r
303yadda yadda.\r
304\r
305-----------------------------------------------------------------------------\r
306\r
307Basics\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
314Setting the address at 1DA2 resets the current reverb work address.\r
315\r
316This work address ALWAYS increments every 1/22050 sec., regardless of\r
317whether reverb is enabled (bit 7 of 1DAA set).\r
318\r
319And 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
325Register names\r
326--------------\r
327\r
328These are probably not their real names.\r
329These are probably not even correct names.\r
330We will use them anyway, because we can.\r
331\r
3321DC0: FB_SRC_A (offset)\r
3331DC2: FB_SRC_B (offset)\r
3341DC4: IIR_ALPHA (coef.)\r
3351DC6: ACC_COEF_A (coef.)\r
3361DC8: ACC_COEF_B (coef.)\r
3371DCA: ACC_COEF_C (coef.)\r
3381DCC: ACC_COEF_D (coef.)\r
3391DCE: IIR_COEF (coef.)\r
3401DD0: FB_ALPHA (coef.)\r
3411DD2: FB_X (coef.)\r
3421DD4: IIR_DEST_A0 (offset)\r
3431DD6: IIR_DEST_A1 (offset)\r
3441DD8: ACC_SRC_A0 (offset)\r
3451DDA: ACC_SRC_A1 (offset)\r
3461DDC: ACC_SRC_B0 (offset)\r
3471DDE: ACC_SRC_B1 (offset)\r
3481DE0: IIR_SRC_A0 (offset)\r
3491DE2: IIR_SRC_A1 (offset)\r
3501DE4: IIR_DEST_B0 (offset)\r
3511DE6: IIR_DEST_B1 (offset)\r
3521DE8: ACC_SRC_C0 (offset)\r
3531DEA: ACC_SRC_C1 (offset)\r
3541DEC: ACC_SRC_D0 (offset)\r
3551DEE: ACC_SRC_D1 (offset)\r
3561DF0: IIR_SRC_B1 (offset)\r
3571DF2: IIR_SRC_B0 (offset)\r
3581DF4: MIX_DEST_A0 (offset)\r
3591DF6: MIX_DEST_A1 (offset)\r
3601DF8: MIX_DEST_B0 (offset)\r
3611DFA: MIX_DEST_B1 (offset)\r
3621DFC: IN_COEF_L (coef.)\r
3631DFE: IN_COEF_R (coef.)\r
364\r
365The 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
369The offsets are (byte/8) offsets into the reverb buffer.\r
370i.e. you multiply them by 8, you get byte offsets.\r
371You can also think of them as (samples/4) offsets.\r
372They appear to be signed. They can be negative.\r
373None of the documented presets make them negative, though.\r
374\r
375Yes, 1DF0 and 1DF2 appear to be backwards. Not a typo.\r
376\r
377-----------------------------------------------------------------------------\r
378\r
379What it does\r
380------------\r
381\r
382We 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
385and mix them into one stereo 44100hz signal.\r
386\r
387Lowpass/downsample that to 22050hz. The PSX uses a proper bandlimiting\r
388algorithm here, but I haven't figured out the hysterically exact specifics.\r
389I use an 8-tap filter with these coefficients, which are nice but probably\r
390not the real ones:\r
391\r
3920.037828187894\r
3930.157538631280\r
3940.321159685278\r
3950.449322115345\r
3960.449322115345\r
3970.321159685278\r
3980.157538631280\r
3990.037828187894\r
400\r
401So 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
407At 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
416The 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
418volume.\r
419\r
420-----------------------------------------------------------------------------\r
421\r
422Reverb steady-state\r
423-------------------\r
424\r
425The reverb steady-state algorithm is fairly clever, and of course by\r
426"clever" I mean "batshit insane".\r
427\r
428buffer[x] is relative to the current buffer position, not the beginning of\r
429the buffer. Note that all buffer offsets must wrap around so they're\r
430contained within the reverb work area.\r
431\r
432Clipping is performed at the end... maybe also sooner, but definitely at\r
433the end.\r
434\r
435IIR_INPUT_A0 = buffer[IIR_SRC_A0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;\r
436IIR_INPUT_A1 = buffer[IIR_SRC_A1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;\r
437IIR_INPUT_B0 = buffer[IIR_SRC_B0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;\r
438IIR_INPUT_B1 = buffer[IIR_SRC_B1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;\r
439\r
440IIR_A0 = IIR_INPUT_A0 * IIR_ALPHA + buffer[IIR_DEST_A0] * (1.0 - IIR_ALPHA);\r
441IIR_A1 = IIR_INPUT_A1 * IIR_ALPHA + buffer[IIR_DEST_A1] * (1.0 - IIR_ALPHA);\r
442IIR_B0 = IIR_INPUT_B0 * IIR_ALPHA + buffer[IIR_DEST_B0] * (1.0 - IIR_ALPHA);\r
443IIR_B1 = IIR_INPUT_B1 * IIR_ALPHA + buffer[IIR_DEST_B1] * (1.0 - IIR_ALPHA);\r
444\r
445buffer[IIR_DEST_A0 + 1sample] = IIR_A0;\r
446buffer[IIR_DEST_A1 + 1sample] = IIR_A1;\r
447buffer[IIR_DEST_B0 + 1sample] = IIR_B0;\r
448buffer[IIR_DEST_B1 + 1sample] = IIR_B1;\r
449\r
450ACC0 = 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
454ACC1 = 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
459FB_A0 = buffer[MIX_DEST_A0 - FB_SRC_A];\r
460FB_A1 = buffer[MIX_DEST_A1 - FB_SRC_A];\r
461FB_B0 = buffer[MIX_DEST_B0 - FB_SRC_B];\r
462FB_B1 = buffer[MIX_DEST_B1 - FB_SRC_B];\r
463\r
464buffer[MIX_DEST_A0] = ACC0 - FB_A0 * FB_ALPHA;\r
465buffer[MIX_DEST_A1] = ACC1 - FB_A1 * FB_ALPHA;\r
466buffer[MIX_DEST_B0] = (FB_ALPHA * ACC0) - FB_A0 * (FB_ALPHA^0x8000) - FB_B0 * FB_X;\r
467buffer[MIX_DEST_B1] = (FB_ALPHA * ACC1) - FB_A1 * (FB_ALPHA^0x8000) - FB_B1 * FB_X;\r
468\r
469-----------------------------------------------------------------------------\r
470*/\r
471\r
1775933a 472// vim:shiftwidth=1:expandtab\r