remove stuff we won't need
[pcsx_rearmed.git] / plugins / dfsound / reverb.c
CommitLineData
ef79bbde
P
1/***************************************************************************\r
2 reverb.c - description\r
3 -------------------\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
9 * *\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
15 * *\r
16 ***************************************************************************/\r
17\r
18#include "stdafx.h"\r
19\r
20#define _IN_REVERB\r
21\r
22// will be included from spu.c\r
23#ifdef _IN_SPU\r
24\r
25////////////////////////////////////////////////////////////////////////\r
26// globals\r
27////////////////////////////////////////////////////////////////////////\r
28\r
29// REVERB info and timing vars...\r
30\r
31int * sRVBPlay = 0;\r
32int * sRVBEnd = 0;\r
33int * sRVBStart = 0;\r
34int iReverbOff = -1; // some delay factor for reverb\r
35int iReverbRepeat = 0;\r
36int iReverbNum = 1; \r
37\r
38////////////////////////////////////////////////////////////////////////\r
39// SET REVERB\r
40////////////////////////////////////////////////////////////////////////\r
41\r
42void SetREVERB(unsigned short val)\r
43{\r
44 switch(val)\r
45 {\r
46 case 0x0000: iReverbOff=-1; break; // off\r
47 case 0x007D: iReverbOff=32; iReverbNum=2; iReverbRepeat=128; break; // ok room\r
48\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
52\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
58 }\r
59}\r
60\r
61////////////////////////////////////////////////////////////////////////\r
62// START REVERB\r
63////////////////////////////////////////////////////////////////////////\r
64\r
65INLINE void StartREVERB(int ch)\r
66{\r
67 if(s_chan[ch].bReverb && (spuCtrl&0x80)) // reverb possible?\r
68 {\r
69 if(iUseReverb==2) s_chan[ch].bRVBActive=1;\r
70 else\r
71 if(iUseReverb==1 && iReverbOff>0) // -> fake reverb used?\r
72 {\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
77 }\r
78 }\r
79 else s_chan[ch].bRVBActive=0; // else -> no reverb\r
80}\r
81\r
82////////////////////////////////////////////////////////////////////////\r
83// HELPER FOR NEILL'S REVERB: re-inits our reverb mixing buf\r
84////////////////////////////////////////////////////////////////////////\r
85\r
86INLINE void InitREVERB(void)\r
87{\r
88 if(iUseReverb==2)\r
89 {memset(sRVBStart,0,NSSIZE*2*4);}\r
90}\r
91\r
92////////////////////////////////////////////////////////////////////////\r
93// STORE REVERB\r
94////////////////////////////////////////////////////////////////////////\r
95\r
96INLINE void StoreREVERB(int ch,int ns)\r
97{\r
98 if(iUseReverb==0) return;\r
99 else\r
100 if(iUseReverb==2) // -------------------------------- // Neil's reverb\r
101 {\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
104\r
105 ns<<=1;\r
106\r
107 *(sRVBStart+ns) +=iRxl; // -> we mix all active reverb channels into an extra buffer\r
108 *(sRVBStart+ns+1)+=iRxr;\r
109 }\r
110 else // --------------------------------------------- // Pete's easy fake reverb\r
111 {\r
112 int * pN;int iRn,iRr=0;\r
113\r
114 // we use the half channel volume (/0x8000) for the first reverb effects, quarter for next and so on\r
115\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
118 \r
119 for(iRn=1;iRn<=s_chan[ch].iRVBNum;iRn++,iRr+=s_chan[ch].iRVBRepeat,iRxl/=2,iRxr/=2)\r
120 {\r
121 pN=sRVBPlay+((s_chan[ch].iRVBOffset+iRr+ns)<<1);\r
122 if(pN>=sRVBEnd) pN=sRVBStart+(pN-sRVBEnd);\r
123\r
124 (*pN)+=iRxl;\r
125 pN++;\r
126 (*pN)+=iRxr;\r
127 }\r
128 }\r
129}\r
130\r
131////////////////////////////////////////////////////////////////////////\r
132\r
133INLINE int g_buffer(int iOff) // get_buffer content helper: takes care about wraps\r
134{\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
140}\r
141\r
142////////////////////////////////////////////////////////////////////////\r
143\r
144INLINE void s_buffer(int iOff,int iVal) // set_buffer content helper: takes care about wraps and clipping\r
145{\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
152}\r
153\r
154////////////////////////////////////////////////////////////////////////\r
155\r
156INLINE void s_buffer1(int iOff,int iVal) // set_buffer (+1 sample) content helper: takes care about wraps and clipping\r
157{\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
164}\r
165\r
166////////////////////////////////////////////////////////////////////////\r
167\r
168INLINE int MixREVERBLeft(int ns)\r
169{\r
170 if(iUseReverb==0) return 0;\r
171 else\r
172 if(iUseReverb==2)\r
173 {\r
174 static int iCnt=0; // this func will be called with 44.1 khz\r
175\r
176 if(!rvb.StartAddr) // reverb is off\r
177 {\r
178 rvb.iLastRVBLeft=rvb.iLastRVBRight=rvb.iRVBLeft=rvb.iRVBRight=0;\r
179 return 0;\r
180 }\r
181\r
182 iCnt++; \r
183\r
184 if(iCnt&1) // we work on every second left value: downsample to 22 khz\r
185 {\r
186 if(spuCtrl&0x80) // -> reverb on? oki\r
187 {\r
188 int ACC0,ACC1,FB_A0,FB_A1,FB_B0,FB_B1;\r
189\r
190 const int INPUT_SAMPLE_L=*(sRVBStart+(ns<<1)); \r
191 const int INPUT_SAMPLE_R=*(sRVBStart+(ns<<1)+1); \r
192\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
197\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
202\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
207 \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
216\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
221\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
224 \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
227 \r
228 rvb.iLastRVBLeft = rvb.iRVBLeft;\r
229 rvb.iLastRVBRight = rvb.iRVBRight;\r
230\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
233\r
234 rvb.iRVBLeft = (rvb.iRVBLeft * rvb.VolLeft) / 0x4000;\r
235 rvb.iRVBRight = (rvb.iRVBRight * rvb.VolRight) / 0x4000;\r
236\r
237 rvb.CurrAddr++;\r
238 if(rvb.CurrAddr>0x3ffff) rvb.CurrAddr=rvb.StartAddr;\r
239\r
240 return rvb.iLastRVBLeft+(rvb.iRVBLeft-rvb.iLastRVBLeft)/2;\r
241 }\r
242 else // -> reverb off\r
243 {\r
244 rvb.iLastRVBLeft=rvb.iLastRVBRight=rvb.iRVBLeft=rvb.iRVBRight=0;\r
245 }\r
246\r
247 rvb.CurrAddr++;\r
248 if(rvb.CurrAddr>0x3ffff) rvb.CurrAddr=rvb.StartAddr;\r
249 }\r
250\r
251 return rvb.iLastRVBLeft;\r
252 }\r
253 else // easy fake reverb:\r
254 {\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
259 }\r
260}\r
261\r
262////////////////////////////////////////////////////////////////////////\r
263\r
264INLINE int MixREVERBRight(void)\r
265{\r
266 if(iUseReverb==0) return 0;\r
267 else\r
268 if(iUseReverb==2) // Neill's reverb:\r
269 {\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
273 }\r
274 else // easy fake reverb:\r
275 {\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
280 }\r
281}\r
282\r
283////////////////////////////////////////////////////////////////////////\r
284\r
285#endif\r
286\r
287/*\r
288-----------------------------------------------------------------------------\r
289PSX reverb hardware notes\r
290by Neill Corlett\r
291-----------------------------------------------------------------------------\r
292\r
293Yadda yadda disclaimer yadda probably not perfect yadda well it's okay anyway\r
294yadda yadda.\r
295\r
296-----------------------------------------------------------------------------\r
297\r
298Basics\r
299------\r
300\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
304\r
305Setting the address at 1DA2 resets the current reverb work address.\r
306\r
307This work address ALWAYS increments every 1/22050 sec., regardless of\r
308whether reverb is enabled (bit 7 of 1DAA set).\r
309\r
310And 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
313\r
314-----------------------------------------------------------------------------\r
315\r
316Register names\r
317--------------\r
318\r
319These are probably not their real names.\r
320These are probably not even correct names.\r
321We will use them anyway, because we can.\r
322\r
3231DC0: FB_SRC_A (offset)\r
3241DC2: FB_SRC_B (offset)\r
3251DC4: IIR_ALPHA (coef.)\r
3261DC6: ACC_COEF_A (coef.)\r
3271DC8: ACC_COEF_B (coef.)\r
3281DCA: ACC_COEF_C (coef.)\r
3291DCC: ACC_COEF_D (coef.)\r
3301DCE: IIR_COEF (coef.)\r
3311DD0: FB_ALPHA (coef.)\r
3321DD2: FB_X (coef.)\r
3331DD4: IIR_DEST_A0 (offset)\r
3341DD6: IIR_DEST_A1 (offset)\r
3351DD8: ACC_SRC_A0 (offset)\r
3361DDA: ACC_SRC_A1 (offset)\r
3371DDC: ACC_SRC_B0 (offset)\r
3381DDE: ACC_SRC_B1 (offset)\r
3391DE0: IIR_SRC_A0 (offset)\r
3401DE2: IIR_SRC_A1 (offset)\r
3411DE4: IIR_DEST_B0 (offset)\r
3421DE6: IIR_DEST_B1 (offset)\r
3431DE8: ACC_SRC_C0 (offset)\r
3441DEA: ACC_SRC_C1 (offset)\r
3451DEC: ACC_SRC_D0 (offset)\r
3461DEE: ACC_SRC_D1 (offset)\r
3471DF0: IIR_SRC_B1 (offset)\r
3481DF2: IIR_SRC_B0 (offset)\r
3491DF4: MIX_DEST_A0 (offset)\r
3501DF6: MIX_DEST_A1 (offset)\r
3511DF8: MIX_DEST_B0 (offset)\r
3521DFA: MIX_DEST_B1 (offset)\r
3531DFC: IN_COEF_L (coef.)\r
3541DFE: IN_COEF_R (coef.)\r
355\r
356The 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
359\r
360The offsets are (byte/8) offsets into the reverb buffer.\r
361i.e. you multiply them by 8, you get byte offsets.\r
362You can also think of them as (samples/4) offsets.\r
363They appear to be signed. They can be negative.\r
364None of the documented presets make them negative, though.\r
365\r
366Yes, 1DF0 and 1DF2 appear to be backwards. Not a typo.\r
367\r
368-----------------------------------------------------------------------------\r
369\r
370What it does\r
371------------\r
372\r
373We 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
376and mix them into one stereo 44100hz signal.\r
377\r
378Lowpass/downsample that to 22050hz. The PSX uses a proper bandlimiting\r
379algorithm here, but I haven't figured out the hysterically exact specifics.\r
380I use an 8-tap filter with these coefficients, which are nice but probably\r
381not the real ones:\r
382\r
3830.037828187894\r
3840.157538631280\r
3850.321159685278\r
3860.449322115345\r
3870.449322115345\r
3880.321159685278\r
3890.157538631280\r
3900.037828187894\r
391\r
392So we have two input samples (INPUT_SAMPLE_L, INPUT_SAMPLE_R) every 22050hz.\r
393\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
397\r
398At 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
406\r
407The 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
409volume.\r
410\r
411-----------------------------------------------------------------------------\r
412\r
413Reverb steady-state\r
414-------------------\r
415\r
416The reverb steady-state algorithm is fairly clever, and of course by\r
417"clever" I mean "batshit insane".\r
418\r
419buffer[x] is relative to the current buffer position, not the beginning of\r
420the buffer. Note that all buffer offsets must wrap around so they're\r
421contained within the reverb work area.\r
422\r
423Clipping is performed at the end... maybe also sooner, but definitely at\r
424the end.\r
425\r
426IIR_INPUT_A0 = buffer[IIR_SRC_A0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;\r
427IIR_INPUT_A1 = buffer[IIR_SRC_A1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;\r
428IIR_INPUT_B0 = buffer[IIR_SRC_B0] * IIR_COEF + INPUT_SAMPLE_L * IN_COEF_L;\r
429IIR_INPUT_B1 = buffer[IIR_SRC_B1] * IIR_COEF + INPUT_SAMPLE_R * IN_COEF_R;\r
430\r
431IIR_A0 = IIR_INPUT_A0 * IIR_ALPHA + buffer[IIR_DEST_A0] * (1.0 - IIR_ALPHA);\r
432IIR_A1 = IIR_INPUT_A1 * IIR_ALPHA + buffer[IIR_DEST_A1] * (1.0 - IIR_ALPHA);\r
433IIR_B0 = IIR_INPUT_B0 * IIR_ALPHA + buffer[IIR_DEST_B0] * (1.0 - IIR_ALPHA);\r
434IIR_B1 = IIR_INPUT_B1 * IIR_ALPHA + buffer[IIR_DEST_B1] * (1.0 - IIR_ALPHA);\r
435\r
436buffer[IIR_DEST_A0 + 1sample] = IIR_A0;\r
437buffer[IIR_DEST_A1 + 1sample] = IIR_A1;\r
438buffer[IIR_DEST_B0 + 1sample] = IIR_B0;\r
439buffer[IIR_DEST_B1 + 1sample] = IIR_B1;\r
440\r
441ACC0 = 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
445ACC1 = 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
449\r
450FB_A0 = buffer[MIX_DEST_A0 - FB_SRC_A];\r
451FB_A1 = buffer[MIX_DEST_A1 - FB_SRC_A];\r
452FB_B0 = buffer[MIX_DEST_B0 - FB_SRC_B];\r
453FB_B1 = buffer[MIX_DEST_B1 - FB_SRC_B];\r
454\r
455buffer[MIX_DEST_A0] = ACC0 - FB_A0 * FB_ALPHA;\r
456buffer[MIX_DEST_A1] = ACC1 - FB_A1 * FB_ALPHA;\r
457buffer[MIX_DEST_B0] = (FB_ALPHA * ACC0) - FB_A0 * (FB_ALPHA^0x8000) - FB_B0 * FB_X;\r
458buffer[MIX_DEST_B1] = (FB_ALPHA * ACC1) - FB_A1 * (FB_ALPHA^0x8000) - FB_B1 * FB_X;\r
459\r
460-----------------------------------------------------------------------------\r
461*/\r
462\r