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
7 ***************************************************************************/
\r
8 /***************************************************************************
\r
10 * This program is free software; you can redistribute it and/or modify *
\r
11 * it under the terms of the GNU General Public License as published by *
\r
12 * the Free Software Foundation; either version 2 of the License, or *
\r
13 * (at your option) any later version. See also the license.txt file for *
\r
14 * additional informations. *
\r
16 ***************************************************************************/
\r
22 // will be included from spu.c
\r
25 ////////////////////////////////////////////////////////////////////////
\r
27 ////////////////////////////////////////////////////////////////////////
\r
29 // REVERB info and timing vars...
\r
33 int * sRVBStart = 0;
\r
34 int iReverbOff = -1; // some delay factor for reverb
\r
35 int iReverbRepeat = 0;
\r
36 int iReverbNum = 1;
\r
38 ////////////////////////////////////////////////////////////////////////
\r
40 ////////////////////////////////////////////////////////////////////////
\r
42 void SetREVERB(unsigned short val)
\r
46 case 0x0000: iReverbOff=-1; break; // off
\r
47 case 0x007D: iReverbOff=32; iReverbNum=2; iReverbRepeat=128; break; // ok room
\r
49 case 0x0033: iReverbOff=32; iReverbNum=2; iReverbRepeat=64; break; // studio small
\r
50 case 0x00B1: iReverbOff=48; iReverbNum=2; iReverbRepeat=96; break; // ok studio medium
\r
51 case 0x00E3: iReverbOff=64; iReverbNum=2; iReverbRepeat=128; break; // ok studio large ok
\r
53 case 0x01A5: iReverbOff=128; iReverbNum=4; iReverbRepeat=32; break; // ok hall
\r
54 case 0x033D: iReverbOff=256; iReverbNum=4; iReverbRepeat=64; break; // space echo
\r
55 case 0x0001: iReverbOff=184; iReverbNum=3; iReverbRepeat=128; break; // echo/delay
\r
56 case 0x0017: iReverbOff=128; iReverbNum=2; iReverbRepeat=128; break; // half echo
\r
57 default: iReverbOff=32; iReverbNum=1; iReverbRepeat=0; break;
\r
61 ////////////////////////////////////////////////////////////////////////
\r
63 ////////////////////////////////////////////////////////////////////////
\r
65 INLINE void StartREVERB(int ch)
\r
67 if(s_chan[ch].bReverb && (spuCtrl&0x80)) // reverb possible?
\r
69 if(iUseReverb==2) s_chan[ch].bRVBActive=1;
\r
71 if(iUseReverb==1 && iReverbOff>0) // -> fake reverb used?
\r
73 s_chan[ch].bRVBActive=1; // -> activate it
\r
74 s_chan[ch].iRVBOffset=iReverbOff*45;
\r
75 s_chan[ch].iRVBRepeat=iReverbRepeat*45;
\r
76 s_chan[ch].iRVBNum =iReverbNum;
\r
79 else s_chan[ch].bRVBActive=0; // else -> no reverb
\r
82 ////////////////////////////////////////////////////////////////////////
\r
83 // HELPER FOR NEILL'S REVERB: re-inits our reverb mixing buf
\r
84 ////////////////////////////////////////////////////////////////////////
\r
86 INLINE void InitREVERB(void)
\r
89 {memset(sRVBStart,0,NSSIZE*2*4);}
\r
92 ////////////////////////////////////////////////////////////////////////
\r
94 ////////////////////////////////////////////////////////////////////////
\r
96 INLINE void StoreREVERB(int ch,int ns)
\r
98 if(iUseReverb==0) return;
\r
100 if(iUseReverb==2) // -------------------------------- // Neil's reverb
\r
102 const int iRxl=(s_chan[ch].sval*s_chan[ch].iLeftVolume)/0x4000;
\r
103 const int iRxr=(s_chan[ch].sval*s_chan[ch].iRightVolume)/0x4000;
\r
107 *(sRVBStart+ns) +=iRxl; // -> we mix all active reverb channels into an extra buffer
\r
108 *(sRVBStart+ns+1)+=iRxr;
\r
110 else // --------------------------------------------- // Pete's easy fake reverb
\r
112 int * pN;int iRn,iRr=0;
\r
114 // we use the half channel volume (/0x8000) for the first reverb effects, quarter for next and so on
\r
116 int iRxl=(s_chan[ch].sval*s_chan[ch].iLeftVolume)/0x8000;
\r
117 int iRxr=(s_chan[ch].sval*s_chan[ch].iRightVolume)/0x8000;
\r
119 for(iRn=1;iRn<=s_chan[ch].iRVBNum;iRn++,iRr+=s_chan[ch].iRVBRepeat,iRxl/=2,iRxr/=2)
\r
121 pN=sRVBPlay+((s_chan[ch].iRVBOffset+iRr+ns)<<1);
\r
122 if(pN>=sRVBEnd) pN=sRVBStart+(pN-sRVBEnd);
\r
131 ////////////////////////////////////////////////////////////////////////
\r
133 INLINE int g_buffer(int iOff) // get_buffer content helper: takes care about wraps
\r
135 short * p=(short *)spuMem;
\r
136 iOff=(iOff*4)+rvb.CurrAddr;
\r
137 while(iOff>0x3FFFF) iOff=rvb.StartAddr+(iOff-0x40000);
\r
138 while(iOff<rvb.StartAddr) iOff=0x3ffff-(rvb.StartAddr-iOff);
\r
139 return (int)*(p+iOff);
\r
142 ////////////////////////////////////////////////////////////////////////
\r
144 INLINE void s_buffer(int iOff,int iVal) // set_buffer content helper: takes care about wraps and clipping
\r
146 short * p=(short *)spuMem;
\r
147 iOff=(iOff*4)+rvb.CurrAddr;
\r
148 while(iOff>0x3FFFF) iOff=rvb.StartAddr+(iOff-0x40000);
\r
149 while(iOff<rvb.StartAddr) iOff=0x3ffff-(rvb.StartAddr-iOff);
\r
150 if(iVal<-32768L) iVal=-32768L;if(iVal>32767L) iVal=32767L;
\r
151 *(p+iOff)=(short)iVal;
\r
154 ////////////////////////////////////////////////////////////////////////
\r
156 INLINE void s_buffer1(int iOff,int iVal) // set_buffer (+1 sample) content helper: takes care about wraps and clipping
\r
158 short * p=(short *)spuMem;
\r
159 iOff=(iOff*4)+rvb.CurrAddr+1;
\r
160 while(iOff>0x3FFFF) iOff=rvb.StartAddr+(iOff-0x40000);
\r
161 while(iOff<rvb.StartAddr) iOff=0x3ffff-(rvb.StartAddr-iOff);
\r
162 if(iVal<-32768L) iVal=-32768L;if(iVal>32767L) iVal=32767L;
\r
163 *(p+iOff)=(short)iVal;
\r
166 ////////////////////////////////////////////////////////////////////////
\r
168 INLINE int MixREVERBLeft(int ns)
\r
170 if(iUseReverb==0) return 0;
\r
174 static int iCnt=0; // this func will be called with 44.1 khz
\r
176 if(!rvb.StartAddr) // reverb is off
\r
178 rvb.iLastRVBLeft=rvb.iLastRVBRight=rvb.iRVBLeft=rvb.iRVBRight=0;
\r
184 if(iCnt&1) // we work on every second left value: downsample to 22 khz
\r
186 if(spuCtrl&0x80) // -> reverb on? oki
\r
188 int ACC0,ACC1,FB_A0,FB_A1,FB_B0,FB_B1;
\r
190 const int INPUT_SAMPLE_L=*(sRVBStart+(ns<<1));
\r
191 const int INPUT_SAMPLE_R=*(sRVBStart+(ns<<1)+1);
\r
193 const int IIR_INPUT_A0 = (g_buffer(rvb.IIR_SRC_A0) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_L * rvb.IN_COEF_L)/32768L;
\r
194 const int IIR_INPUT_A1 = (g_buffer(rvb.IIR_SRC_A1) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_R * rvb.IN_COEF_R)/32768L;
\r
195 const int IIR_INPUT_B0 = (g_buffer(rvb.IIR_SRC_B0) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_L * rvb.IN_COEF_L)/32768L;
\r
196 const int IIR_INPUT_B1 = (g_buffer(rvb.IIR_SRC_B1) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_R * rvb.IN_COEF_R)/32768L;
\r
198 const int IIR_A0 = (IIR_INPUT_A0 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_A0) * (32768L - rvb.IIR_ALPHA))/32768L;
\r
199 const int IIR_A1 = (IIR_INPUT_A1 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_A1) * (32768L - rvb.IIR_ALPHA))/32768L;
\r
200 const int IIR_B0 = (IIR_INPUT_B0 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_B0) * (32768L - rvb.IIR_ALPHA))/32768L;
\r
201 const int IIR_B1 = (IIR_INPUT_B1 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_B1) * (32768L - rvb.IIR_ALPHA))/32768L;
\r
203 s_buffer1(rvb.IIR_DEST_A0, IIR_A0);
\r
204 s_buffer1(rvb.IIR_DEST_A1, IIR_A1);
\r
205 s_buffer1(rvb.IIR_DEST_B0, IIR_B0);
\r
206 s_buffer1(rvb.IIR_DEST_B1, IIR_B1);
\r
208 ACC0 = (g_buffer(rvb.ACC_SRC_A0) * rvb.ACC_COEF_A)/32768L +
\r
209 (g_buffer(rvb.ACC_SRC_B0) * rvb.ACC_COEF_B)/32768L +
\r
210 (g_buffer(rvb.ACC_SRC_C0) * rvb.ACC_COEF_C)/32768L +
\r
211 (g_buffer(rvb.ACC_SRC_D0) * rvb.ACC_COEF_D)/32768L;
\r
212 ACC1 = (g_buffer(rvb.ACC_SRC_A1) * rvb.ACC_COEF_A)/32768L +
\r
213 (g_buffer(rvb.ACC_SRC_B1) * rvb.ACC_COEF_B)/32768L +
\r
214 (g_buffer(rvb.ACC_SRC_C1) * rvb.ACC_COEF_C)/32768L +
\r
215 (g_buffer(rvb.ACC_SRC_D1) * rvb.ACC_COEF_D)/32768L;
\r
217 FB_A0 = g_buffer(rvb.MIX_DEST_A0 - rvb.FB_SRC_A);
\r
218 FB_A1 = g_buffer(rvb.MIX_DEST_A1 - rvb.FB_SRC_A);
\r
219 FB_B0 = g_buffer(rvb.MIX_DEST_B0 - rvb.FB_SRC_B);
\r
220 FB_B1 = g_buffer(rvb.MIX_DEST_B1 - rvb.FB_SRC_B);
\r
222 s_buffer(rvb.MIX_DEST_A0, ACC0 - (FB_A0 * rvb.FB_ALPHA)/32768L);
\r
223 s_buffer(rvb.MIX_DEST_A1, ACC1 - (FB_A1 * rvb.FB_ALPHA)/32768L);
\r
225 s_buffer(rvb.MIX_DEST_B0, (rvb.FB_ALPHA * ACC0)/32768L - (FB_A0 * (int)(rvb.FB_ALPHA^0xFFFF8000))/32768L - (FB_B0 * rvb.FB_X)/32768L);
\r
226 s_buffer(rvb.MIX_DEST_B1, (rvb.FB_ALPHA * ACC1)/32768L - (FB_A1 * (int)(rvb.FB_ALPHA^0xFFFF8000))/32768L - (FB_B1 * rvb.FB_X)/32768L);
\r
228 rvb.iLastRVBLeft = rvb.iRVBLeft;
\r
229 rvb.iLastRVBRight = rvb.iRVBRight;
\r
231 rvb.iRVBLeft = (g_buffer(rvb.MIX_DEST_A0)+g_buffer(rvb.MIX_DEST_B0))/3;
\r
232 rvb.iRVBRight = (g_buffer(rvb.MIX_DEST_A1)+g_buffer(rvb.MIX_DEST_B1))/3;
\r
234 rvb.iRVBLeft = (rvb.iRVBLeft * rvb.VolLeft) / 0x4000;
\r
235 rvb.iRVBRight = (rvb.iRVBRight * rvb.VolRight) / 0x4000;
\r
238 if(rvb.CurrAddr>0x3ffff) rvb.CurrAddr=rvb.StartAddr;
\r
240 return rvb.iLastRVBLeft+(rvb.iRVBLeft-rvb.iLastRVBLeft)/2;
\r
242 else // -> reverb off
\r
244 rvb.iLastRVBLeft=rvb.iLastRVBRight=rvb.iRVBLeft=rvb.iRVBRight=0;
\r
248 if(rvb.CurrAddr>0x3ffff) rvb.CurrAddr=rvb.StartAddr;
\r
251 return rvb.iLastRVBLeft;
\r
253 else // easy fake reverb:
\r
255 const int iRV=*sRVBPlay; // -> simply take the reverb mix buf value
\r
256 *sRVBPlay++=0; // -> init it after
\r
257 if(sRVBPlay>=sRVBEnd) sRVBPlay=sRVBStart; // -> and take care about wrap arounds
\r
258 return iRV; // -> return reverb mix buf val
\r
262 ////////////////////////////////////////////////////////////////////////
\r
264 INLINE int MixREVERBRight(void)
\r
266 if(iUseReverb==0) return 0;
\r
268 if(iUseReverb==2) // Neill's reverb:
\r
270 int i=rvb.iLastRVBRight+(rvb.iRVBRight-rvb.iLastRVBRight)/2;
\r
271 rvb.iLastRVBRight=rvb.iRVBRight;
\r
272 return i; // -> just return the last right reverb val (little bit scaled by the previous right val)
\r
274 else // easy fake reverb:
\r
276 const int iRV=*sRVBPlay; // -> simply take the reverb mix buf value
\r
277 *sRVBPlay++=0; // -> init it after
\r
278 if(sRVBPlay>=sRVBEnd) sRVBPlay=sRVBStart; // -> and take care about wrap arounds
\r
279 return iRV; // -> return reverb mix buf val
\r
283 ////////////////////////////////////////////////////////////////////////
\r
288 -----------------------------------------------------------------------------
\r
289 PSX reverb hardware notes
\r
291 -----------------------------------------------------------------------------
\r
293 Yadda yadda disclaimer yadda probably not perfect yadda well it's okay anyway
\r
296 -----------------------------------------------------------------------------
\r
301 - The reverb buffer is 22khz 16-bit mono PCM.
\r
302 - It starts at the reverb address given by 1DA2, extends to
\r
303 the end of sound RAM, and wraps back to the 1DA2 address.
\r
305 Setting the address at 1DA2 resets the current reverb work address.
\r
307 This work address ALWAYS increments every 1/22050 sec., regardless of
\r
308 whether reverb is enabled (bit 7 of 1DAA set).
\r
310 And the contents of the reverb buffer ALWAYS play, scaled by the
\r
311 "reverberation depth left/right" volumes (1D84/1D86).
\r
312 (which, by the way, appear to be scaled so 3FFF=approx. 1.0, 4000=-1.0)
\r
314 -----------------------------------------------------------------------------
\r
319 These are probably not their real names.
\r
320 These are probably not even correct names.
\r
321 We will use them anyway, because we can.
\r
323 1DC0: FB_SRC_A (offset)
\r
324 1DC2: FB_SRC_B (offset)
\r
325 1DC4: IIR_ALPHA (coef.)
\r
326 1DC6: ACC_COEF_A (coef.)
\r
327 1DC8: ACC_COEF_B (coef.)
\r
328 1DCA: ACC_COEF_C (coef.)
\r
329 1DCC: ACC_COEF_D (coef.)
\r
330 1DCE: IIR_COEF (coef.)
\r
331 1DD0: FB_ALPHA (coef.)
\r
333 1DD4: IIR_DEST_A0 (offset)
\r
334 1DD6: IIR_DEST_A1 (offset)
\r
335 1DD8: ACC_SRC_A0 (offset)
\r
336 1DDA: ACC_SRC_A1 (offset)
\r
337 1DDC: ACC_SRC_B0 (offset)
\r
338 1DDE: ACC_SRC_B1 (offset)
\r
339 1DE0: IIR_SRC_A0 (offset)
\r
340 1DE2: IIR_SRC_A1 (offset)
\r
341 1DE4: IIR_DEST_B0 (offset)
\r
342 1DE6: IIR_DEST_B1 (offset)
\r
343 1DE8: ACC_SRC_C0 (offset)
\r
344 1DEA: ACC_SRC_C1 (offset)
\r
345 1DEC: ACC_SRC_D0 (offset)
\r
346 1DEE: ACC_SRC_D1 (offset)
\r
347 1DF0: IIR_SRC_B1 (offset)
\r
348 1DF2: IIR_SRC_B0 (offset)
\r
349 1DF4: MIX_DEST_A0 (offset)
\r
350 1DF6: MIX_DEST_A1 (offset)
\r
351 1DF8: MIX_DEST_B0 (offset)
\r
352 1DFA: MIX_DEST_B1 (offset)
\r
353 1DFC: IN_COEF_L (coef.)
\r
354 1DFE: IN_COEF_R (coef.)
\r
356 The coefficients are signed fractional values.
\r
357 -32768 would be -1.0
\r
358 32768 would be 1.0 (if it were possible... the highest is of course 32767)
\r
360 The offsets are (byte/8) offsets into the reverb buffer.
\r
361 i.e. you multiply them by 8, you get byte offsets.
\r
362 You can also think of them as (samples/4) offsets.
\r
363 They appear to be signed. They can be negative.
\r
364 None of the documented presets make them negative, though.
\r
366 Yes, 1DF0 and 1DF2 appear to be backwards. Not a typo.
\r
368 -----------------------------------------------------------------------------
\r
373 We take all reverb sources:
\r
374 - regular channels that have the reverb bit on
\r
375 - cd and external sources, if their reverb bits are on
\r
376 and mix them into one stereo 44100hz signal.
\r
378 Lowpass/downsample that to 22050hz. The PSX uses a proper bandlimiting
\r
379 algorithm here, but I haven't figured out the hysterically exact specifics.
\r
380 I use an 8-tap filter with these coefficients, which are nice but probably
\r
392 So we have two input samples (INPUT_SAMPLE_L, INPUT_SAMPLE_R) every 22050hz.
\r
394 * IN MY EMULATION, I divide these by 2 to make it clip less.
\r
395 (and of course the L/R output coefficients are adjusted to compensate)
\r
396 The real thing appears to not do this.
\r
398 At every 22050hz tick:
\r
399 - If the reverb bit is enabled (bit 7 of 1DAA), execute the reverb
\r
400 steady-state algorithm described below
\r
401 - AFTERWARDS, retrieve the "wet out" L and R samples from the reverb buffer
\r
402 (This part may not be exactly right and I guessed at the coefs. TODO: check later.)
\r
403 L is: 0.333 * (buffer[MIX_DEST_A0] + buffer[MIX_DEST_B0])
\r
404 R is: 0.333 * (buffer[MIX_DEST_A1] + buffer[MIX_DEST_B1])
\r
405 - Advance the current buffer position by 1 sample
\r
407 The wet out L and R are then upsampled to 44100hz and played at the
\r
408 "reverberation depth left/right" (1D84/1D86) volume, independent of the main
\r
411 -----------------------------------------------------------------------------
\r
413 Reverb steady-state
\r
414 -------------------
\r
416 The reverb steady-state algorithm is fairly clever, and of course by
\r
417 "clever" I mean "batshit insane".
\r
419 buffer[x] is relative to the current buffer position, not the beginning of
\r
420 the buffer. Note that all buffer offsets must wrap around so they're
\r
421 contained within the reverb work area.
\r
423 Clipping is performed at the end... maybe also sooner, but definitely at
\r
426 IIR_INPUT_A0 = buffer[IIR_SRC_A0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;
\r
427 IIR_INPUT_A1 = buffer[IIR_SRC_A1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;
\r
428 IIR_INPUT_B0 = buffer[IIR_SRC_B0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;
\r
429 IIR_INPUT_B1 = buffer[IIR_SRC_B1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;
\r
431 IIR_A0 = IIR_INPUT_A0 * IIR_ALPHA + buffer[IIR_DEST_A0] * (1.0 - IIR_ALPHA);
\r
432 IIR_A1 = IIR_INPUT_A1 * IIR_ALPHA + buffer[IIR_DEST_A1] * (1.0 - IIR_ALPHA);
\r
433 IIR_B0 = IIR_INPUT_B0 * IIR_ALPHA + buffer[IIR_DEST_B0] * (1.0 - IIR_ALPHA);
\r
434 IIR_B1 = IIR_INPUT_B1 * IIR_ALPHA + buffer[IIR_DEST_B1] * (1.0 - IIR_ALPHA);
\r
436 buffer[IIR_DEST_A0 + 1sample] = IIR_A0;
\r
437 buffer[IIR_DEST_A1 + 1sample] = IIR_A1;
\r
438 buffer[IIR_DEST_B0 + 1sample] = IIR_B0;
\r
439 buffer[IIR_DEST_B1 + 1sample] = IIR_B1;
\r
441 ACC0 = buffer[ACC_SRC_A0] * ACC_COEF_A +
\r
442 buffer[ACC_SRC_B0] * ACC_COEF_B +
\r
443 buffer[ACC_SRC_C0] * ACC_COEF_C +
\r
444 buffer[ACC_SRC_D0] * ACC_COEF_D;
\r
445 ACC1 = buffer[ACC_SRC_A1] * ACC_COEF_A +
\r
446 buffer[ACC_SRC_B1] * ACC_COEF_B +
\r
447 buffer[ACC_SRC_C1] * ACC_COEF_C +
\r
448 buffer[ACC_SRC_D1] * ACC_COEF_D;
\r
450 FB_A0 = buffer[MIX_DEST_A0 - FB_SRC_A];
\r
451 FB_A1 = buffer[MIX_DEST_A1 - FB_SRC_A];
\r
452 FB_B0 = buffer[MIX_DEST_B0 - FB_SRC_B];
\r
453 FB_B1 = buffer[MIX_DEST_B1 - FB_SRC_B];
\r
455 buffer[MIX_DEST_A0] = ACC0 - FB_A0 * FB_ALPHA;
\r
456 buffer[MIX_DEST_A1] = ACC1 - FB_A1 * FB_ALPHA;
\r
457 buffer[MIX_DEST_B0] = (FB_ALPHA * ACC0) - FB_A0 * (FB_ALPHA^0x8000) - FB_B0 * FB_X;
\r
458 buffer[MIX_DEST_B1] = (FB_ALPHA * ACC1) - FB_A1 * (FB_ALPHA^0x8000) - FB_B1 * FB_X;
\r
460 -----------------------------------------------------------------------------
\r