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) / 3;
\r
161 r = (mix_dest_a1 + mix_dest_b1) / 3;
\r
163 l = (l * rvb.VolLeft) >> 14;
\r
164 r = (r * rvb.VolRight) >> 14;
\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 prepare_offsets(void)
\r
185 int space = 0x40000 - rvb.StartAddr;
\r
187 #define prep_offs(v) \
\r
189 while (t >= space) \
\r
192 #define prep_offs2(d, v1, v2) \
\r
193 t = rvb.v1 - rvb.v2; \
\r
194 while (t >= space) \
\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
228 INLINE void REVERBDo(void)
\r
230 if (!rvb.StartAddr) // reverb is off
\r
232 rvb.iRVBLeft = rvb.iRVBRight = 0;
\r
236 if (spuCtrl & 0x80) // -> reverb on? oki
\r
243 else // -> reverb off
\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
252 ////////////////////////////////////////////////////////////////////////
\r
257 -----------------------------------------------------------------------------
\r
258 PSX reverb hardware notes
\r
260 -----------------------------------------------------------------------------
\r
262 Yadda yadda disclaimer yadda probably not perfect yadda well it's okay anyway
\r
265 -----------------------------------------------------------------------------
\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
274 Setting the address at 1DA2 resets the current reverb work address.
\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
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
283 -----------------------------------------------------------------------------
\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
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
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
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
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
335 Yes, 1DF0 and 1DF2 appear to be backwards. Not a typo.
\r
337 -----------------------------------------------------------------------------
\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
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
361 So we have two input samples (INPUT_SAMPLE_L, INPUT_SAMPLE_R) every 22050hz.
\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
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
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
380 -----------------------------------------------------------------------------
\r
382 Reverb steady-state
\r
383 -------------------
\r
385 The reverb steady-state algorithm is fairly clever, and of course by
\r
386 "clever" I mean "batshit insane".
\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
392 Clipping is performed at the end... maybe also sooner, but definitely at
\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
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
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
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
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
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
429 -----------------------------------------------------------------------------
\r
432 // vim:shiftwidth=1:expandtab
\r