| 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 | Portions (C) GraÅžvydas "notaz" Ignotas, 2010-2011\r |
| 9 | Portions (C) SPU2-X, gigaherz, Pcsx2 Development Team\r |
| 10 | \r |
| 11 | ***************************************************************************/\r |
| 12 | /***************************************************************************\r |
| 13 | * *\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 |
| 19 | * *\r |
| 20 | ***************************************************************************/\r |
| 21 | \r |
| 22 | #include "stdafx.h"\r |
| 23 | #include "spu.h"\r |
| 24 | #include <assert.h>\r |
| 25 | \r |
| 26 | #define _IN_REVERB\r |
| 27 | \r |
| 28 | // will be included from spu.c\r |
| 29 | #ifdef _IN_SPU\r |
| 30 | \r |
| 31 | ////////////////////////////////////////////////////////////////////////\r |
| 32 | // START REVERB\r |
| 33 | ////////////////////////////////////////////////////////////////////////\r |
| 34 | \r |
| 35 | INLINE void StartREVERB(int ch)\r |
| 36 | {\r |
| 37 | if(spu.s_chan[ch].bReverb && (spu.spuCtrl&0x80)) // reverb possible?\r |
| 38 | {\r |
| 39 | spu.s_chan[ch].bRVBActive=!!spu_config.iUseReverb;\r |
| 40 | }\r |
| 41 | else spu.s_chan[ch].bRVBActive=0; // else -> no reverb\r |
| 42 | }\r |
| 43 | \r |
| 44 | ////////////////////////////////////////////////////////////////////////\r |
| 45 | \r |
| 46 | INLINE int rvb_wrap(int ofs, int space)\r |
| 47 | {\r |
| 48 | #if 0\r |
| 49 | int mask = (0x3ffff - ofs) >> 31;\r |
| 50 | ofs = ofs - (space & mask);\r |
| 51 | #else\r |
| 52 | if (ofs >= 0x40000)\r |
| 53 | ofs -= space;\r |
| 54 | #endif\r |
| 55 | //assert(ofs >= 0x40000 - space);\r |
| 56 | //assert(ofs < 0x40000);\r |
| 57 | return ofs;\r |
| 58 | }\r |
| 59 | \r |
| 60 | INLINE int rvb2ram_offs(int curr, int space, int ofs)\r |
| 61 | {\r |
| 62 | ofs += curr;\r |
| 63 | return rvb_wrap(ofs, space);\r |
| 64 | }\r |
| 65 | \r |
| 66 | // get_buffer content helper: takes care about wraps\r |
| 67 | #define g_buffer(var) \\r |
| 68 | ((int)(signed short)LE16TOH(spuMem[rvb2ram_offs(curr_addr, space, var)]))\r |
| 69 | \r |
| 70 | // saturate iVal and store it as var\r |
| 71 | #define s_buffer_w(var, iVal) \\r |
| 72 | ssat32_to_16(iVal); \\r |
| 73 | spuMem[rvb2ram_offs(curr_addr, space, var)] = HTOLE16(iVal)\r |
| 74 | \r |
| 75 | ////////////////////////////////////////////////////////////////////////\r |
| 76 | \r |
| 77 | static void reverb_interpolate(sample_buf *sb, int curr_addr,\r |
| 78 | int out0[2], int out1[2])\r |
| 79 | {\r |
| 80 | int spos = (curr_addr - 3) & 3;\r |
| 81 | int dpos = curr_addr & 3;\r |
| 82 | int i;\r |
| 83 | \r |
| 84 | for (i = 0; i < 2; i++)\r |
| 85 | sb->SB_rvb[i][dpos] = sb->SB_rvb[i][4 | dpos] = out0[i];\r |
| 86 | \r |
| 87 | // mednafen uses some 20 coefs here, we just reuse gauss [0] and [128]\r |
| 88 | for (i = 0; i < 2; i++)\r |
| 89 | {\r |
| 90 | const int *s;\r |
| 91 | s = &sb->SB_rvb[i][spos];\r |
| 92 | out0[i] = (s[0] * 0x12c7 + s[1] * 0x59b3 + s[2] * 0x1307) >> 15;\r |
| 93 | out1[i] = (s[0] * 0x019c + s[1] * 0x3def + s[2] * 0x3e4c + s[3] * 0x01a8) >> 15;\r |
| 94 | }\r |
| 95 | }\r |
| 96 | \r |
| 97 | static void MixREVERB(int *SSumLR, int *RVB, int ns_to, int curr_addr,\r |
| 98 | int do_filter)\r |
| 99 | {\r |
| 100 | unsigned short *spuMem = spu.spuMem;\r |
| 101 | const REVERBInfo *rvb = spu.rvb;\r |
| 102 | sample_buf *sb = &spu.sb[MAXCHAN];\r |
| 103 | int space = 0x40000 - rvb->StartAddr;\r |
| 104 | int mlsame_m2o = rvb->mLSAME + space - 1;\r |
| 105 | int mrsame_m2o = rvb->mRSAME + space - 1;\r |
| 106 | int mldiff_m2o = rvb->mLDIFF + space - 1;\r |
| 107 | int mrdiff_m2o = rvb->mRDIFF + space - 1;\r |
| 108 | int vCOMB1 = rvb->vCOMB1 >> 1, vCOMB2 = rvb->vCOMB2 >> 1;\r |
| 109 | int vCOMB3 = rvb->vCOMB3 >> 1, vCOMB4 = rvb->vCOMB4 >> 1;\r |
| 110 | int vAPF1 = rvb->vAPF1 >> 1, vAPF2 = rvb->vAPF2 >> 1;\r |
| 111 | int vLIN = rvb->vLIN >> 1, vRIN = rvb->vRIN >> 1;\r |
| 112 | int vWALL = rvb->vWALL >> 1;\r |
| 113 | int vIIR = rvb->vIIR;\r |
| 114 | int ns;\r |
| 115 | \r |
| 116 | #if P_HAVE_PTHREAD || defined(WANT_THREAD_CODE)\r |
| 117 | sb = &spu.sb_thread[MAXCHAN];\r |
| 118 | #endif\r |
| 119 | if (mlsame_m2o >= space) mlsame_m2o -= space;\r |
| 120 | if (mrsame_m2o >= space) mrsame_m2o -= space;\r |
| 121 | if (mldiff_m2o >= space) mldiff_m2o -= space;\r |
| 122 | if (mrdiff_m2o >= space) mrdiff_m2o -= space;\r |
| 123 | \r |
| 124 | for (ns = 0; ns < ns_to * 2; )\r |
| 125 | {\r |
| 126 | int Lin = RVB[ns];\r |
| 127 | int Rin = RVB[ns+1];\r |
| 128 | int mlsame_m2 = g_buffer(mlsame_m2o) << (15-1);\r |
| 129 | int mrsame_m2 = g_buffer(mrsame_m2o) << (15-1);\r |
| 130 | int mldiff_m2 = g_buffer(mldiff_m2o) << (15-1);\r |
| 131 | int mrdiff_m2 = g_buffer(mrdiff_m2o) << (15-1);\r |
| 132 | int Lout, Rout, out0[2], out1[2];\r |
| 133 | \r |
| 134 | ssat32_to_16(Lin); Lin *= vLIN;\r |
| 135 | ssat32_to_16(Rin); Rin *= vRIN;\r |
| 136 | \r |
| 137 | // from nocash psx-spx\r |
| 138 | mlsame_m2 += ((Lin + g_buffer(rvb->dLSAME) * vWALL - mlsame_m2) >> 15) * vIIR;\r |
| 139 | mrsame_m2 += ((Rin + g_buffer(rvb->dRSAME) * vWALL - mrsame_m2) >> 15) * vIIR;\r |
| 140 | mldiff_m2 += ((Lin + g_buffer(rvb->dLDIFF) * vWALL - mldiff_m2) >> 15) * vIIR;\r |
| 141 | mrdiff_m2 += ((Rin + g_buffer(rvb->dRDIFF) * vWALL - mrdiff_m2) >> 15) * vIIR;\r |
| 142 | mlsame_m2 >>= (15-1); s_buffer_w(rvb->mLSAME, mlsame_m2);\r |
| 143 | mrsame_m2 >>= (15-1); s_buffer_w(rvb->mRSAME, mrsame_m2);\r |
| 144 | mldiff_m2 >>= (15-1); s_buffer_w(rvb->mLDIFF, mldiff_m2);\r |
| 145 | mrdiff_m2 >>= (15-1); s_buffer_w(rvb->mRDIFF, mrdiff_m2);\r |
| 146 | \r |
| 147 | Lout = vCOMB1 * g_buffer(rvb->mLCOMB1) + vCOMB2 * g_buffer(rvb->mLCOMB2)\r |
| 148 | + vCOMB3 * g_buffer(rvb->mLCOMB3) + vCOMB4 * g_buffer(rvb->mLCOMB4);\r |
| 149 | Rout = vCOMB1 * g_buffer(rvb->mRCOMB1) + vCOMB2 * g_buffer(rvb->mRCOMB2)\r |
| 150 | + vCOMB3 * g_buffer(rvb->mRCOMB3) + vCOMB4 * g_buffer(rvb->mRCOMB4);\r |
| 151 | \r |
| 152 | preload(SSumLR + ns + 64*2/4 - 4);\r |
| 153 | \r |
| 154 | Lout -= vAPF1 * g_buffer(rvb->mLAPF1_dAPF1); Lout >>= (15-1);\r |
| 155 | Rout -= vAPF1 * g_buffer(rvb->mRAPF1_dAPF1); Rout >>= (15-1);\r |
| 156 | s_buffer_w(rvb->mLAPF1, Lout);\r |
| 157 | s_buffer_w(rvb->mRAPF1, Rout);\r |
| 158 | Lout = Lout * vAPF1 + (g_buffer(rvb->mLAPF1_dAPF1) << (15-1));\r |
| 159 | Rout = Rout * vAPF1 + (g_buffer(rvb->mRAPF1_dAPF1) << (15-1));\r |
| 160 | \r |
| 161 | preload(RVB + ns + 64*2/4 - 4);\r |
| 162 | \r |
| 163 | Lout -= vAPF2 * g_buffer(rvb->mLAPF2_dAPF2); Lout >>= (15-1);\r |
| 164 | Rout -= vAPF2 * g_buffer(rvb->mRAPF2_dAPF2); Rout >>= (15-1);\r |
| 165 | s_buffer_w(rvb->mLAPF2, Lout);\r |
| 166 | s_buffer_w(rvb->mRAPF2, Rout);\r |
| 167 | Lout = Lout * vAPF2 + (g_buffer(rvb->mLAPF2_dAPF2) << (15-1));\r |
| 168 | Rout = Rout * vAPF2 + (g_buffer(rvb->mRAPF2_dAPF2) << (15-1));\r |
| 169 | \r |
| 170 | out0[0] = out1[0] = (Lout >> (15-1)) * rvb->VolLeft >> 15;\r |
| 171 | out0[1] = out1[1] = (Rout >> (15-1)) * rvb->VolRight >> 15;\r |
| 172 | if (do_filter)\r |
| 173 | reverb_interpolate(sb, curr_addr, out0, out1);\r |
| 174 | \r |
| 175 | SSumLR[ns++] += out0[0];\r |
| 176 | SSumLR[ns++] += out0[1];\r |
| 177 | SSumLR[ns++] += out1[0];\r |
| 178 | SSumLR[ns++] += out1[1];\r |
| 179 | \r |
| 180 | curr_addr++;\r |
| 181 | curr_addr = rvb_wrap(curr_addr, space);\r |
| 182 | }\r |
| 183 | }\r |
| 184 | \r |
| 185 | static void MixREVERB_off(int *SSumLR, int ns_to, int curr_addr)\r |
| 186 | {\r |
| 187 | const REVERBInfo *rvb = spu.rvb;\r |
| 188 | unsigned short *spuMem = spu.spuMem;\r |
| 189 | int space = 0x40000 - rvb->StartAddr;\r |
| 190 | int Lout, Rout, ns;\r |
| 191 | \r |
| 192 | for (ns = 0; ns < ns_to * 2; )\r |
| 193 | {\r |
| 194 | preload(SSumLR + ns + 64*2/4 - 4);\r |
| 195 | \r |
| 196 | // todo: is this missing COMB and APF1?\r |
| 197 | Lout = g_buffer(rvb->mLAPF2_dAPF2);\r |
| 198 | Rout = g_buffer(rvb->mLAPF2_dAPF2);\r |
| 199 | \r |
| 200 | Lout = (Lout * rvb->VolLeft) >> 15;\r |
| 201 | Rout = (Rout * rvb->VolRight) >> 15;\r |
| 202 | \r |
| 203 | SSumLR[ns++] += Lout;\r |
| 204 | SSumLR[ns++] += Rout;\r |
| 205 | SSumLR[ns++] += Lout;\r |
| 206 | SSumLR[ns++] += Rout;\r |
| 207 | \r |
| 208 | curr_addr++;\r |
| 209 | if (curr_addr >= 0x40000) curr_addr = rvb->StartAddr;\r |
| 210 | }\r |
| 211 | }\r |
| 212 | \r |
| 213 | static void REVERBPrep(void)\r |
| 214 | {\r |
| 215 | REVERBInfo *rvb = spu.rvb;\r |
| 216 | int space, t;\r |
| 217 | \r |
| 218 | t = regAreaGet(H_SPUReverbAddr);\r |
| 219 | if (t == 0xFFFF || t <= 0x200)\r |
| 220 | spu.rvb->StartAddr = spu.rvb->CurrAddr = 0;\r |
| 221 | else if (spu.rvb->StartAddr != (t << 2))\r |
| 222 | spu.rvb->StartAddr = spu.rvb->CurrAddr = t << 2;\r |
| 223 | \r |
| 224 | space = 0x40000 - rvb->StartAddr;\r |
| 225 | \r |
| 226 | #define prep_offs(v, r) \\r |
| 227 | t = spu.regArea[(0x1c0 + r) >> 1] * 4; \\r |
| 228 | while (t >= space) \\r |
| 229 | t -= space; \\r |
| 230 | rvb->v = t\r |
| 231 | #define prep_offs2(d, r1, r2) \\r |
| 232 | t = spu.regArea[(0x1c0 + r1) >> 1] * 4; \\r |
| 233 | t -= spu.regArea[(0x1c0 + r2) >> 1] * 4; \\r |
| 234 | while (t < 0) \\r |
| 235 | t += space; \\r |
| 236 | while (t >= space) \\r |
| 237 | t -= space; \\r |
| 238 | rvb->d = t\r |
| 239 | \r |
| 240 | prep_offs(mLSAME, 0x14);\r |
| 241 | prep_offs(mRSAME, 0x16);\r |
| 242 | prep_offs(mLCOMB1, 0x18);\r |
| 243 | prep_offs(mRCOMB1, 0x1a);\r |
| 244 | prep_offs(mLCOMB2, 0x1c);\r |
| 245 | prep_offs(mRCOMB2, 0x1e);\r |
| 246 | prep_offs(dLSAME, 0x20);\r |
| 247 | prep_offs(dRSAME, 0x22);\r |
| 248 | prep_offs(mLDIFF, 0x24);\r |
| 249 | prep_offs(mRDIFF, 0x26);\r |
| 250 | prep_offs(mLCOMB3, 0x28);\r |
| 251 | prep_offs(mRCOMB3, 0x2a);\r |
| 252 | prep_offs(mLCOMB4, 0x2c);\r |
| 253 | prep_offs(mRCOMB4, 0x2e);\r |
| 254 | prep_offs(dLDIFF, 0x30);\r |
| 255 | prep_offs(dRDIFF, 0x32);\r |
| 256 | prep_offs(mLAPF1, 0x34);\r |
| 257 | prep_offs(mRAPF1, 0x36);\r |
| 258 | prep_offs(mLAPF2, 0x38);\r |
| 259 | prep_offs(mRAPF2, 0x3a);\r |
| 260 | prep_offs2(mLAPF1_dAPF1, 0x34, 0);\r |
| 261 | prep_offs2(mRAPF1_dAPF1, 0x36, 0);\r |
| 262 | prep_offs2(mLAPF2_dAPF2, 0x38, 2);\r |
| 263 | prep_offs2(mRAPF2_dAPF2, 0x3a, 2);\r |
| 264 | \r |
| 265 | #undef prep_offs\r |
| 266 | #undef prep_offs2\r |
| 267 | rvb->dirty = 0;\r |
| 268 | }\r |
| 269 | \r |
| 270 | INLINE void REVERBDo(int *SSumLR, int *RVB, int ns_to, int curr_addr)\r |
| 271 | {\r |
| 272 | if (spu.spuCtrl & 0x80) // -> reverb on? oki\r |
| 273 | {\r |
| 274 | MixREVERB(SSumLR, RVB, ns_to, curr_addr, 0); //spu.interpolation > 1);\r |
| 275 | }\r |
| 276 | else if (spu.rvb->VolLeft || spu.rvb->VolRight)\r |
| 277 | {\r |
| 278 | MixREVERB_off(SSumLR, ns_to, curr_addr);\r |
| 279 | }\r |
| 280 | }\r |
| 281 | \r |
| 282 | ////////////////////////////////////////////////////////////////////////\r |
| 283 | \r |
| 284 | #endif\r |
| 285 | \r |
| 286 | // vim:shiftwidth=1:expandtab\r |