1 /***************************************************************************
2                           reverb.c  -  description
3                              -------------------
4     begin                : Wed May 15 2002
5     copyright            : (C) 2002 by Pete Bernert
6     email                : BlackDove@addcom.de
7  ***************************************************************************/
8 /***************************************************************************
9  *                                                                         *
10  *   This program is free software; you can redistribute it and/or modify  *
11  *   it under the terms of the GNU General Public License as published by  *
12  *   the Free Software Foundation; either version 2 of the License, or     *
13  *   (at your option) any later version. See also the license.txt file for *
14  *   additional informations.                                              *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "stdafx.h"
19 #include "registers.h"
20 
21 #define _IN_REVERB
22 
23 // will be included from spu.c
24 #ifdef _IN_SPU
25 
26 ////////////////////////////////////////////////////////////////////////
27 // globals
28 ////////////////////////////////////////////////////////////////////////
29 
30 // REVERB info and timing vars...
31 
32 int *          sRVBPlay      = 0;
33 int *          sRVBEnd       = 0;
34 int *          sRVBStart     = 0;
35 int            iReverbOff    = -1;                          // some delay factor for reverb
36 int            iReverbRepeat = 0;
37 int            iReverbNum    = 1;
38 
39 ////////////////////////////////////////////////////////////////////////
40 // SET REVERB
41 ////////////////////////////////////////////////////////////////////////
42 
SetREVERB(unsigned short val)43 void SetREVERB(unsigned short val)
44 {
45  switch(val)
46   {
47    case 0x0000: iReverbOff=-1;  break;                                         // off
48    case 0x007D: iReverbOff=32;  iReverbNum=2; iReverbRepeat=128;  break;       // ok room
49 
50    case 0x0033: iReverbOff=32;  iReverbNum=2; iReverbRepeat=64;   break;       // studio small
51    case 0x00B1: iReverbOff=48;  iReverbNum=2; iReverbRepeat=96;   break;       // ok studio medium
52    case 0x00E3: iReverbOff=64;  iReverbNum=2; iReverbRepeat=128;  break;       // ok studio large ok
53 
54    case 0x01A5: iReverbOff=128; iReverbNum=4; iReverbRepeat=32;   break;       // ok hall
55    case 0x033D: iReverbOff=256; iReverbNum=4; iReverbRepeat=64;   break;       // space echo
56    case 0x0001: iReverbOff=184; iReverbNum=3; iReverbRepeat=128;  break;       // echo/delay
57    case 0x0017: iReverbOff=128; iReverbNum=2; iReverbRepeat=128;  break;       // half echo
58    default:     iReverbOff=32;  iReverbNum=1; iReverbRepeat=0;    break;
59   }
60 }
61 
62 ////////////////////////////////////////////////////////////////////////
63 // START REVERB
64 ////////////////////////////////////////////////////////////////////////
65 
StartREVERB(int ch)66 static INLINE void StartREVERB(int ch)
67 {
68  if(s_chan[ch].bReverb && (spuCtrl&0x80))              // reverb possible?
69   {
70    if(iUseReverb==2) s_chan[ch].bRVBActive=1;
71    else
72    if(iUseReverb==1 && iReverbOff>0)                   // -> fake reverb used?
73     {
74      s_chan[ch].bRVBActive=1;                            // -> activate it
75      s_chan[ch].iRVBOffset=iReverbOff*45;
76      s_chan[ch].iRVBRepeat=iReverbRepeat*45;
77      s_chan[ch].iRVBNum   =iReverbNum;
78     }
79   }
80  else s_chan[ch].bRVBActive=0;                         // else -> no reverb
81 }
82 
83 ////////////////////////////////////////////////////////////////////////
84 // HELPER FOR NEILL'S REVERB: re-inits our reverb mixing buf
85 ////////////////////////////////////////////////////////////////////////
86 
InitREVERB(void)87 static INLINE void InitREVERB(void)
88 {
89  if(iUseReverb==2)
90   {memset(sRVBStart,0,NSSIZE*2*4);}
91 }
92 
93 ////////////////////////////////////////////////////////////////////////
94 // STORE REVERB
95 ////////////////////////////////////////////////////////////////////////
96 
StoreREVERB_CD(int left,int right,int ns)97 static INLINE void StoreREVERB_CD(int left, int right,int ns)
98 {
99  if(iUseReverb==0) return;
100  else
101  if(iUseReverb==2) // -------------------------------- // Neil's reverb
102   {
103    const int iRxl=left;
104    const int iRxr=right;
105 
106    ns<<=1;
107 
108    // -> we mix all active reverb channels into an extra buffer
109 	 *(sRVBStart+ns)   += CLAMP16( *(sRVBStart+ns+0) + ( iRxl ) );
110    *(sRVBStart+ns+1) += CLAMP16( *(sRVBStart+ns+1) + ( iRxr ) );
111   }
112 }
113 
114 
StoreREVERB(int ch,int ns)115 static INLINE void StoreREVERB(int ch,int ns)
116 {
117  if(iUseReverb==0) return;
118  else
119  if(iUseReverb==2) // -------------------------------- // Neil's reverb
120   {
121    const int iRxl=(s_chan[ch].sval*s_chan[ch].iLeftVolume)/0x4000;
122    const int iRxr=(s_chan[ch].sval*s_chan[ch].iRightVolume)/0x4000;
123 
124    ns<<=1;
125 
126    *(sRVBStart+ns)  +=iRxl;                            // -> we mix all active reverb channels into an extra buffer
127    *(sRVBStart+ns+1)+=iRxr;
128   }
129  else // --------------------------------------------- // Pete's easy fake reverb
130   {
131    int * pN;int iRn,iRr=0;
132 
133    // we use the half channel volume (/0x8000) for the first reverb effects, quarter for next and so on
134 
135    int iRxl=(s_chan[ch].sval*s_chan[ch].iLeftVolume)/0x8000;
136    int iRxr=(s_chan[ch].sval*s_chan[ch].iRightVolume)/0x8000;
137 
138    for(iRn=1;iRn<=s_chan[ch].iRVBNum;iRn++,iRr+=s_chan[ch].iRVBRepeat,iRxl/=2,iRxr/=2)
139     {
140      pN=sRVBPlay+((s_chan[ch].iRVBOffset+iRr+ns)<<1);
141      if(pN>=sRVBEnd) pN=sRVBStart+(pN-sRVBEnd);
142 
143      (*pN)+=iRxl;
144      pN++;
145      (*pN)+=iRxr;
146     }
147   }
148 }
149 
150 ////////////////////////////////////////////////////////////////////////
151 
g_buffer(int iOff)152 static INLINE int g_buffer(int iOff)                          // get_buffer content helper: takes care about wraps
153 {
154  short * p=(short *)spuMem;
155  iOff=(iOff*4)+rvb.CurrAddr;
156  while(iOff>0x3FFFF)       iOff=rvb.StartAddr+(iOff-0x40000);
157  while(iOff<rvb.StartAddr) iOff=0x3ffff-(rvb.StartAddr-iOff);
158  return (int)*(p+iOff);
159 }
160 
161 ////////////////////////////////////////////////////////////////////////
162 
s_buffer(int iOff,int iVal)163 static INLINE void s_buffer(int iOff,int iVal)                // set_buffer content helper: takes care about wraps and clipping
164 {
165  short * p=(short *)spuMem;
166  iOff=(iOff*4)+rvb.CurrAddr;
167  while(iOff>0x3FFFF) iOff=rvb.StartAddr+(iOff-0x40000);
168  while(iOff<rvb.StartAddr) iOff=0x3ffff-(rvb.StartAddr-iOff);
169  if(iVal<-32768L) iVal=-32768L;if(iVal>32767L) iVal=32767L;
170  *(p+iOff)=(short)iVal;
171 }
172 
173 ////////////////////////////////////////////////////////////////////////
174 
s_buffer1(int iOff,int iVal)175 static INLINE void s_buffer1(int iOff,int iVal)                // set_buffer (+1 sample) content helper: takes care about wraps and clipping
176 {
177  short * p=(short *)spuMem;
178  iOff=(iOff*4)+rvb.CurrAddr+1;
179  while(iOff>0x3FFFF) iOff=rvb.StartAddr+(iOff-0x40000);
180  while(iOff<rvb.StartAddr) iOff=0x3ffff-(rvb.StartAddr-iOff);
181  if(iVal<-32768L) iVal=-32768L;if(iVal>32767L) iVal=32767L;
182  *(p+iOff)=(short)iVal;
183 }
184 
185 ////////////////////////////////////////////////////////////////////////
186 
MixREVERBLeft(int ns)187 static INLINE int MixREVERBLeft(int ns)
188 {
189  if(iUseReverb==0) return 0;
190  else
191  if(iUseReverb==2)
192   {
193    static int iCnt=0;                                  // this func will be called with 44.1 khz
194 
195    if(!rvb.StartAddr)                                  // reverb is off
196     {
197      rvb.iLastRVBLeft=rvb.iLastRVBRight=rvb.iRVBLeft=rvb.iRVBRight=0;
198      return 0;
199     }
200 
201    iCnt++;
202 
203    if(iCnt&1)                                          // we work on every second left value: downsample to 22 khz
204     {
205      if(spuCtrl&0x80)                                  // -> reverb on? oki
206       {
207        int ACC0,ACC1,FB_A0,FB_A1,FB_B0,FB_B1;
208 
209        const int INPUT_SAMPLE_L=*(sRVBStart+(ns<<1));
210        const int INPUT_SAMPLE_R=*(sRVBStart+(ns<<1)+1);
211 
212        const int IIR_INPUT_A0 = (g_buffer(rvb.IIR_SRC_A0) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_L * rvb.IN_COEF_L)/32768L;
213        const int IIR_INPUT_A1 = (g_buffer(rvb.IIR_SRC_A1) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_R * rvb.IN_COEF_R)/32768L;
214        const int IIR_INPUT_B0 = (g_buffer(rvb.IIR_SRC_B0) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_L * rvb.IN_COEF_L)/32768L;
215        const int IIR_INPUT_B1 = (g_buffer(rvb.IIR_SRC_B1) * rvb.IIR_COEF)/32768L + (INPUT_SAMPLE_R * rvb.IN_COEF_R)/32768L;
216 
217        const int IIR_A0 = (IIR_INPUT_A0 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_A0) * (32768L - rvb.IIR_ALPHA))/32768L;
218        const int IIR_A1 = (IIR_INPUT_A1 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_A1) * (32768L - rvb.IIR_ALPHA))/32768L;
219        const int IIR_B0 = (IIR_INPUT_B0 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_B0) * (32768L - rvb.IIR_ALPHA))/32768L;
220        const int IIR_B1 = (IIR_INPUT_B1 * rvb.IIR_ALPHA)/32768L + (g_buffer(rvb.IIR_DEST_B1) * (32768L - rvb.IIR_ALPHA))/32768L;
221 
222        s_buffer1(rvb.IIR_DEST_A0, IIR_A0);
223        s_buffer1(rvb.IIR_DEST_A1, IIR_A1);
224        s_buffer1(rvb.IIR_DEST_B0, IIR_B0);
225        s_buffer1(rvb.IIR_DEST_B1, IIR_B1);
226 
227        ACC0 = (g_buffer(rvb.ACC_SRC_A0) * rvb.ACC_COEF_A)/32768L +
228               (g_buffer(rvb.ACC_SRC_B0) * rvb.ACC_COEF_B)/32768L +
229               (g_buffer(rvb.ACC_SRC_C0) * rvb.ACC_COEF_C)/32768L +
230               (g_buffer(rvb.ACC_SRC_D0) * rvb.ACC_COEF_D)/32768L;
231        ACC1 = (g_buffer(rvb.ACC_SRC_A1) * rvb.ACC_COEF_A)/32768L +
232               (g_buffer(rvb.ACC_SRC_B1) * rvb.ACC_COEF_B)/32768L +
233               (g_buffer(rvb.ACC_SRC_C1) * rvb.ACC_COEF_C)/32768L +
234               (g_buffer(rvb.ACC_SRC_D1) * rvb.ACC_COEF_D)/32768L;
235 
236        FB_A0 = g_buffer(rvb.MIX_DEST_A0 - rvb.FB_SRC_A);
237        FB_A1 = g_buffer(rvb.MIX_DEST_A1 - rvb.FB_SRC_A);
238        FB_B0 = g_buffer(rvb.MIX_DEST_B0 - rvb.FB_SRC_B);
239        FB_B1 = g_buffer(rvb.MIX_DEST_B1 - rvb.FB_SRC_B);
240 
241        s_buffer(rvb.MIX_DEST_A0, ACC0 - (FB_A0 * rvb.FB_ALPHA)/32768L);
242        s_buffer(rvb.MIX_DEST_A1, ACC1 - (FB_A1 * rvb.FB_ALPHA)/32768L);
243 
244        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);
245        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);
246 
247        rvb.iLastRVBLeft  = rvb.iRVBLeft;
248        rvb.iLastRVBRight = rvb.iRVBRight;
249 
250        rvb.iRVBLeft  = (g_buffer(rvb.MIX_DEST_A0)+g_buffer(rvb.MIX_DEST_B0))/3;
251        rvb.iRVBRight = (g_buffer(rvb.MIX_DEST_A1)+g_buffer(rvb.MIX_DEST_B1))/3;
252 
253        rvb.iRVBLeft  = (rvb.iRVBLeft  * rvb.VolLeft)  / 0x4000;
254        rvb.iRVBRight = (rvb.iRVBRight * rvb.VolRight) / 0x4000;
255 
256        rvb.CurrAddr++;
257        if(rvb.CurrAddr>0x3ffff) rvb.CurrAddr=rvb.StartAddr;
258 
259        return rvb.iLastRVBLeft+(rvb.iRVBLeft-rvb.iLastRVBLeft)/2;
260       }
261      else                                              // -> reverb off
262       {
263 			 // Vib Ribbon - grab current reverb sample (cdda data)
264 			 // - mono data
265 
266 			 rvb.iRVBLeft = (short) spuMem[ rvb.CurrAddr ];
267 			 rvb.iRVBRight = rvb.iRVBLeft;
268 			 rvb.iLastRVBLeft  = (rvb.iRVBLeft  * rvb.VolLeft)  / 0x4000;
269 			 rvb.iLastRVBRight = (rvb.iRVBRight * rvb.VolRight) / 0x4000;
270 
271 			 //rvb.iLastRVBLeft=rvb.iLastRVBRight=rvb.iRVBLeft=rvb.iRVBRight=0;
272       }
273 
274 
275 		 Check_IRQ( rvb.CurrAddr*2, 0 );
276 
277      rvb.CurrAddr++;
278      if(rvb.CurrAddr>0x3ffff) rvb.CurrAddr=rvb.StartAddr;
279     }
280 
281    return rvb.iLastRVBLeft;
282   }
283  else                                                  // easy fake reverb:
284   {
285    const int iRV=*sRVBPlay;                            // -> simply take the reverb mix buf value
286    *sRVBPlay++=0;                                      // -> init it after
287    if(sRVBPlay>=sRVBEnd) sRVBPlay=sRVBStart;           // -> and take care about wrap arounds
288    return iRV;                                         // -> return reverb mix buf val
289   }
290 }
291 
292 ////////////////////////////////////////////////////////////////////////
293 
MixREVERBRight(void)294 static INLINE int MixREVERBRight(void)
295 {
296  if(iUseReverb==0) return 0;
297  else
298  if(iUseReverb==2)                                     // Neill's reverb:
299   {
300 	 // Vib Ribbon - reverb always on (!), reverb write flag
301    if(spuCtrl & CTRL_REVERB)                         // -> reverb on? oki
302 	 {
303 		 int i=rvb.iLastRVBRight+(rvb.iRVBRight-rvb.iLastRVBRight)/2;
304 		 rvb.iLastRVBRight=rvb.iRVBRight;
305 		 return i;                                           // -> just return the last right reverb val (little bit scaled by the previous right val)
306 	 } else {
307 		 // Vib Ribbon - return reverb buffer (cdda data)
308 		 return CLAMP16(rvb.iLastRVBRight);
309 	 }
310   }
311  else                                                  // easy fake reverb:
312   {
313    const int iRV=*sRVBPlay;                            // -> simply take the reverb mix buf value
314    *sRVBPlay++=0;                                      // -> init it after
315    if(sRVBPlay>=sRVBEnd) sRVBPlay=sRVBStart;           // -> and take care about wrap arounds
316    return iRV;                                         // -> return reverb mix buf val
317   }
318 }
319 
320 ////////////////////////////////////////////////////////////////////////
321 
322 #endif
323 
324 /*
325 -----------------------------------------------------------------------------
326 PSX reverb hardware notes
327 by Neill Corlett
328 -----------------------------------------------------------------------------
329 
330 Yadda yadda disclaimer yadda probably not perfect yadda well it's okay anyway
331 yadda yadda.
332 
333 -----------------------------------------------------------------------------
334 
335 Basics
336 ------
337 
338 - The reverb buffer is 22khz 16-bit mono PCM.
339 - It starts at the reverb address given by 1DA2, extends to
340   the end of sound RAM, and wraps back to the 1DA2 address.
341 
342 Setting the address at 1DA2 resets the current reverb work address.
343 
344 This work address ALWAYS increments every 1/22050 sec., regardless of
345 whether reverb is enabled (bit 7 of 1DAA set).
346 
347 And the contents of the reverb buffer ALWAYS play, scaled by the
348 "reverberation depth left/right" volumes (1D84/1D86).
349 (which, by the way, appear to be scaled so 3FFF=approx. 1.0, 4000=-1.0)
350 
351 -----------------------------------------------------------------------------
352 
353 Register names
354 --------------
355 
356 These are probably not their real names.
357 These are probably not even correct names.
358 We will use them anyway, because we can.
359 
360 1DC0: FB_SRC_A       (offset)
361 1DC2: FB_SRC_B       (offset)
362 1DC4: IIR_ALPHA      (coef.)
363 1DC6: ACC_COEF_A     (coef.)
364 1DC8: ACC_COEF_B     (coef.)
365 1DCA: ACC_COEF_C     (coef.)
366 1DCC: ACC_COEF_D     (coef.)
367 1DCE: IIR_COEF       (coef.)
368 1DD0: FB_ALPHA       (coef.)
369 1DD2: FB_X           (coef.)
370 1DD4: IIR_DEST_A0    (offset)
371 1DD6: IIR_DEST_A1    (offset)
372 1DD8: ACC_SRC_A0     (offset)
373 1DDA: ACC_SRC_A1     (offset)
374 1DDC: ACC_SRC_B0     (offset)
375 1DDE: ACC_SRC_B1     (offset)
376 1DE0: IIR_SRC_A0     (offset)
377 1DE2: IIR_SRC_A1     (offset)
378 1DE4: IIR_DEST_B0    (offset)
379 1DE6: IIR_DEST_B1    (offset)
380 1DE8: ACC_SRC_C0     (offset)
381 1DEA: ACC_SRC_C1     (offset)
382 1DEC: ACC_SRC_D0     (offset)
383 1DEE: ACC_SRC_D1     (offset)
384 1DF0: IIR_SRC_B1     (offset)
385 1DF2: IIR_SRC_B0     (offset)
386 1DF4: MIX_DEST_A0    (offset)
387 1DF6: MIX_DEST_A1    (offset)
388 1DF8: MIX_DEST_B0    (offset)
389 1DFA: MIX_DEST_B1    (offset)
390 1DFC: IN_COEF_L      (coef.)
391 1DFE: IN_COEF_R      (coef.)
392 
393 The coefficients are signed fractional values.
394 -32768 would be -1.0
395  32768 would be  1.0 (if it were possible... the highest is of course 32767)
396 
397 The offsets are (byte/8) offsets into the reverb buffer.
398 i.e. you multiply them by 8, you get byte offsets.
399 You can also think of them as (samples/4) offsets.
400 They appear to be signed.  They can be negative.
401 None of the documented presets make them negative, though.
402 
403 Yes, 1DF0 and 1DF2 appear to be backwards.  Not a typo.
404 
405 -----------------------------------------------------------------------------
406 
407 What it does
408 ------------
409 
410 We take all reverb sources:
411 - regular channels that have the reverb bit on
412 - cd and external sources, if their reverb bits are on
413 and mix them into one stereo 44100hz signal.
414 
415 Lowpass/downsample that to 22050hz.  The PSX uses a proper bandlimiting
416 algorithm here, but I haven't figured out the hysterically exact specifics.
417 I use an 8-tap filter with these coefficients, which are nice but probably
418 not the real ones:
419 
420 0.037828187894
421 0.157538631280
422 0.321159685278
423 0.449322115345
424 0.449322115345
425 0.321159685278
426 0.157538631280
427 0.037828187894
428 
429 So we have two input samples (INPUT_SAMPLE_L, INPUT_SAMPLE_R) every 22050hz.
430 
431 * IN MY EMULATION, I divide these by 2 to make it clip less.
432   (and of course the L/R output coefficients are adjusted to compensate)
433   The real thing appears to not do this.
434 
435 At every 22050hz tick:
436 - If the reverb bit is enabled (bit 7 of 1DAA), execute the reverb
437   steady-state algorithm described below
438 - AFTERWARDS, retrieve the "wet out" L and R samples from the reverb buffer
439   (This part may not be exactly right and I guessed at the coefs. TODO: check later.)
440   L is: 0.333 * (buffer[MIX_DEST_A0] + buffer[MIX_DEST_B0])
441   R is: 0.333 * (buffer[MIX_DEST_A1] + buffer[MIX_DEST_B1])
442 - Advance the current buffer position by 1 sample
443 
444 The wet out L and R are then upsampled to 44100hz and played at the
445 "reverberation depth left/right" (1D84/1D86) volume, independent of the main
446 volume.
447 
448 -----------------------------------------------------------------------------
449 
450 Reverb steady-state
451 -------------------
452 
453 The reverb steady-state algorithm is fairly clever, and of course by
454 "clever" I mean "batshit insane".
455 
456 buffer[x] is relative to the current buffer position, not the beginning of
457 the buffer.  Note that all buffer offsets must wrap around so they're
458 contained within the reverb work area.
459 
460 Clipping is performed at the end... maybe also sooner, but definitely at
461 the end.
462 
463 IIR_INPUT_A0 = buffer[IIR_SRC_A0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;
464 IIR_INPUT_A1 = buffer[IIR_SRC_A1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;
465 IIR_INPUT_B0 = buffer[IIR_SRC_B0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;
466 IIR_INPUT_B1 = buffer[IIR_SRC_B1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;
467 
468 IIR_A0 = IIR_INPUT_A0 * IIR_ALPHA + buffer[IIR_DEST_A0] * (1.0 - IIR_ALPHA);
469 IIR_A1 = IIR_INPUT_A1 * IIR_ALPHA + buffer[IIR_DEST_A1] * (1.0 - IIR_ALPHA);
470 IIR_B0 = IIR_INPUT_B0 * IIR_ALPHA + buffer[IIR_DEST_B0] * (1.0 - IIR_ALPHA);
471 IIR_B1 = IIR_INPUT_B1 * IIR_ALPHA + buffer[IIR_DEST_B1] * (1.0 - IIR_ALPHA);
472 
473 buffer[IIR_DEST_A0 + 1sample] = IIR_A0;
474 buffer[IIR_DEST_A1 + 1sample] = IIR_A1;
475 buffer[IIR_DEST_B0 + 1sample] = IIR_B0;
476 buffer[IIR_DEST_B1 + 1sample] = IIR_B1;
477 
478 ACC0 = buffer[ACC_SRC_A0] * ACC_COEF_A +
479        buffer[ACC_SRC_B0] * ACC_COEF_B +
480        buffer[ACC_SRC_C0] * ACC_COEF_C +
481        buffer[ACC_SRC_D0] * ACC_COEF_D;
482 ACC1 = buffer[ACC_SRC_A1] * ACC_COEF_A +
483        buffer[ACC_SRC_B1] * ACC_COEF_B +
484        buffer[ACC_SRC_C1] * ACC_COEF_C +
485        buffer[ACC_SRC_D1] * ACC_COEF_D;
486 
487 FB_A0 = buffer[MIX_DEST_A0 - FB_SRC_A];
488 FB_A1 = buffer[MIX_DEST_A1 - FB_SRC_A];
489 FB_B0 = buffer[MIX_DEST_B0 - FB_SRC_B];
490 FB_B1 = buffer[MIX_DEST_B1 - FB_SRC_B];
491 
492 buffer[MIX_DEST_A0] = ACC0 - FB_A0 * FB_ALPHA;
493 buffer[MIX_DEST_A1] = ACC1 - FB_A1 * FB_ALPHA;
494 buffer[MIX_DEST_B0] = (FB_ALPHA * ACC0) - FB_A0 * (FB_ALPHA^0x8000) - FB_B0 * FB_X;
495 buffer[MIX_DEST_B1] = (FB_ALPHA * ACC1) - FB_A1 * (FB_ALPHA^0x8000) - FB_B1 * FB_X;
496 
497 -----------------------------------------------------------------------------
498 */
499 
500