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