1 /***************************************************************************
\r
2 reverb.c - description
\r
4 begin : Wed May 15 2002
\r
5 copyright : (C) 2002 by Pete Bernert
\r
6 email : BlackDove@addcom.de
\r
8 Portions (C) GraÅžvydas "notaz" Ignotas, 2010-2011
\r
9 Portions (C) SPU2-X, gigaherz, Pcsx2 Development Team
\r
11 ***************************************************************************/
\r
12 /***************************************************************************
\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
20 ***************************************************************************/
\r
26 // will be included from spu.c
\r
29 ////////////////////////////////////////////////////////////////////////
\r
31 ////////////////////////////////////////////////////////////////////////
\r
33 // REVERB info and timing vars...
\r
37 int * sRVBStart = 0;
\r
39 ////////////////////////////////////////////////////////////////////////
\r
41 ////////////////////////////////////////////////////////////////////////
\r
43 INLINE void StartREVERB(int ch)
\r
45 if(s_chan[ch].bReverb && (spuCtrl&0x80)) // reverb possible?
\r
47 s_chan[ch].bRVBActive=!!iUseReverb;
\r
49 else s_chan[ch].bRVBActive=0; // else -> no reverb
\r
52 ////////////////////////////////////////////////////////////////////////
\r
53 // HELPER FOR NEILL'S REVERB: re-inits our reverb mixing buf
\r
54 ////////////////////////////////////////////////////////////////////////
\r
56 INLINE void InitREVERB(void)
\r
58 memset(sRVBStart,0,NSSIZE*2*4);
\r
61 ////////////////////////////////////////////////////////////////////////
\r
63 ////////////////////////////////////////////////////////////////////////
\r
65 INLINE void StoreREVERB(int ch,int ns,int l,int r)
\r
69 sRVBStart[ns] +=l; // -> we mix all active reverb channels into an extra buffer
\r
73 ////////////////////////////////////////////////////////////////////////
\r
75 INLINE int rvb2ram_offs(int curr, int space, int iOff)
\r
78 if (iOff >= 0x40000) iOff -= space;
\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
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
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
95 ////////////////////////////////////////////////////////////////////////
\r
97 // portions based on spu2-x from PCSX2
\r
98 static void MixREVERB(void)
\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
106 for (ns = 0; ns < NSSIZE*2; )
\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
112 int input_L = sRVBStart[ns] * rvb.IN_COEF_L;
\r
113 int input_R = sRVBStart[ns+1] * rvb.IN_COEF_R;
\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
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
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
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
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
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
149 mix_dest_a0 = ACC0 - ((FB_A0 * rvb.FB_ALPHA) >> 15);
\r
150 mix_dest_a1 = ACC1 - ((FB_A1 * rvb.FB_ALPHA) >> 15);
\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
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
160 l = (mix_dest_a0 + mix_dest_b0) / 2;
\r
161 r = (mix_dest_a1 + mix_dest_b1) / 2;
\r
163 l = (l * rvb.VolLeft) >> 15; // 15?
\r
164 r = (r * rvb.VolRight) >> 15;
\r
166 SSumLR[ns++] += (l + l_old) / 2;
\r
167 SSumLR[ns++] += (r + r_old) / 2;
\r
175 if (curr_addr >= 0x40000) curr_addr = rvb.StartAddr;
\r
180 rvb.CurrAddr = curr_addr;
\r
183 static void MixREVERB_off(void)
\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
191 for (ns = 0; ns < NSSIZE*2; )
\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
196 l = (l * rvb.VolLeft) >> 15;
\r
197 r = (r * rvb.VolRight) >> 15;
\r
199 SSumLR[ns++] += (l + l_old) / 2;
\r
200 SSumLR[ns++] += (r + r_old) / 2;
\r
208 if (curr_addr >= 0x40000) curr_addr = rvb.StartAddr;
\r
213 rvb.CurrAddr = curr_addr;
\r
216 static void prepare_offsets(void)
\r
218 int space = 0x40000 - rvb.StartAddr;
\r
220 #define prep_offs(v) \
\r
222 while (t >= space) \
\r
225 #define prep_offs2(d, v1, v2) \
\r
226 t = rvb.v1 - rvb.v2; \
\r
227 while (t >= space) \
\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
261 INLINE void REVERBDo(void)
\r
263 if (!rvb.StartAddr) // reverb is off
\r
265 rvb.iRVBLeft = rvb.iRVBRight = 0;
\r
269 if (spuCtrl & 0x80) // -> reverb on? oki
\r
271 if (unlikely(rvb.dirty))
\r
276 else if (rvb.VolLeft || rvb.VolRight)
\r
278 if (unlikely(rvb.dirty))
\r
283 else // -> reverb off
\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
292 ////////////////////////////////////////////////////////////////////////
\r
297 -----------------------------------------------------------------------------
\r
298 PSX reverb hardware notes
\r
300 -----------------------------------------------------------------------------
\r
302 Yadda yadda disclaimer yadda probably not perfect yadda well it's okay anyway
\r
305 -----------------------------------------------------------------------------
\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
314 Setting the address at 1DA2 resets the current reverb work address.
\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
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
323 -----------------------------------------------------------------------------
\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
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
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
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
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
375 Yes, 1DF0 and 1DF2 appear to be backwards. Not a typo.
\r
377 -----------------------------------------------------------------------------
\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
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
401 So we have two input samples (INPUT_SAMPLE_L, INPUT_SAMPLE_R) every 22050hz.
\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
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
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
420 -----------------------------------------------------------------------------
\r
422 Reverb steady-state
\r
423 -------------------
\r
425 The reverb steady-state algorithm is fairly clever, and of course by
\r
426 "clever" I mean "batshit insane".
\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
432 Clipping is performed at the end... maybe also sooner, but definitely at
\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
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
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
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
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
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
469 -----------------------------------------------------------------------------
\r
472 // vim:shiftwidth=1:expandtab
\r