diff --git a/src/sound.h b/src/sound.h index b9659fa..461fe36 100644 --- a/src/sound.h +++ b/src/sound.h @@ -49,6 +49,66 @@ namespace Sound { { 0, -0x0005, +0x0011, -0x0023, +0x0046, -0x0017, -0x0044, +0x015B, -0x0347, +0x080E, -0x1249, +0x3C07, +0x53E0, -0x16FA, +0x0AFA, -0x0548, +0x027B, -0x00EB, +0x001A, +0x002B, -0x0023, +0x0010, -0x0008, +0x0002, 0, 0, 0, 0, 0, 0 }, }; + // https://problemkaputt.de/psx-spx.htm#spureverbformula + struct ReverbPreset { + uint16 size; + int16 dAPF1, dAPF2, vIIR, vCOMB1, vCOMB2, vCOMB3, vCOMB4, vWALL; + int16 vAPF1, vAPF2, mLSAME, mRSAME, mLCOMB1, mRCOMB1, mLCOMB2, mRCOMB2; + int16 dLSAME, dRSAME, mLDIFF, mRDIFF, mLCOMB3, mRCOMB3, mLCOMB4, mRCOMB4; + int16 dLDIFF, dRDIFF, mLAPF1, mRAPF1, mLAPF2, mRAPF2, vLIN, vRIN; + }; + + static const uint16 SPU_REVERB[8][sizeof(ReverbPreset) / 2] = { + // Room + { 0x26C0 / 2, + 0x007D, 0x005B, 0x6D80, 0x54B8, 0xBED0, 0x0000, 0x0000, 0xBA80, + 0x5800, 0x5300, 0x04D6, 0x0333, 0x03F0, 0x0227, 0x0374, 0x01EF, + 0x0334, 0x01B5, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x01B4, 0x0136, 0x00B8, 0x005C, 0x8000, 0x8000 }, + // Studio Small + { 0x1F40 / 2, + 0x0033, 0x0025, 0x70F0, 0x4FA8, 0xBCE0, 0x4410, 0xC0F0, 0x9C00, + 0x5280, 0x4EC0, 0x03E4, 0x031B, 0x03A4, 0x02AF, 0x0372, 0x0266, + 0x031C, 0x025D, 0x025C, 0x018E, 0x022F, 0x0135, 0x01D2, 0x00B7, + 0x018F, 0x00B5, 0x00B4, 0x0080, 0x004C, 0x0026, 0x8000, 0x8000 }, + // Studio Medium + { 0x4840 / 2, + 0x00B1, 0x007F, 0x70F0, 0x4FA8, 0xBCE0, 0x4510, 0xBEF0, 0xB4C0, + 0x5280, 0x4EC0, 0x0904, 0x076B, 0x0824, 0x065F, 0x07A2, 0x0616, + 0x076C, 0x05ED, 0x05EC, 0x042E, 0x050F, 0x0305, 0x0462, 0x02B7, + 0x042F, 0x0265, 0x0264, 0x01B2, 0x0100, 0x0080, 0x8000, 0x8000 }, + // Studio Large + { 0x6FE0 / 2, + 0x00E3, 0x00A9, 0x6F60, 0x4FA8, 0xBCE0, 0x4510, 0xBEF0, 0xA680, + 0x5680, 0x52C0, 0x0DFB, 0x0B58, 0x0D09, 0x0A3C, 0x0BD9, 0x0973, + 0x0B59, 0x08DA, 0x08D9, 0x05E9, 0x07EC, 0x04B0, 0x06EF, 0x03D2, + 0x05EA, 0x031D, 0x031C, 0x0238, 0x0154, 0x00AA, 0x8000, 0x8000 }, + // Hall + { 0xADE0 / 2, + 0x01A5, 0x0139, 0x6000, 0x5000, 0x4C00, 0xB800, 0xBC00, 0xC000, + 0x6000, 0x5C00, 0x15BA, 0x11BB, 0x14C2, 0x10BD, 0x11BC, 0x0DC1, + 0x11C0, 0x0DC3, 0x0DC0, 0x09C1, 0x0BC4, 0x07C1, 0x0A00, 0x06CD, + 0x09C2, 0x05C1, 0x05C0, 0x041A, 0x0274, 0x013A, 0x8000, 0x8000 }, + // Half Echo + { 0x3C00 / 2, + 0x0017, 0x0013, 0x70F0, 0x4FA8, 0xBCE0, 0x4510, 0xBEF0, 0x8500, + 0x5F80, 0x54C0, 0x0371, 0x02AF, 0x02E5, 0x01DF, 0x02B0, 0x01D7, + 0x0358, 0x026A, 0x01D6, 0x011E, 0x012D, 0x00B1, 0x011F, 0x0059, + 0x01A0, 0x00E3, 0x0058, 0x0040, 0x0028, 0x0014, 0x8000, 0x8000 }, + // Space Echo + { 0xF6C0 / 2, + 0x033D, 0x0231, 0x7E00, 0x5000, 0xB400, 0xB000, 0x4C00, 0xB000, + 0x6000, 0x5400, 0x1ED6, 0x1A31, 0x1D14, 0x183B, 0x1BC2, 0x16B2, + 0x1A32, 0x15EF, 0x15EE, 0x1055, 0x1334, 0x0F2D, 0x11F6, 0x0C5D, + 0x1056, 0x0AE1, 0x0AE0, 0x07A2, 0x0464, 0x0232, 0x8000, 0x8000 }, + // Reverb off + { 0x0010 / 2, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000 } + }; + struct Frame { int16 L, R; }; @@ -65,34 +125,10 @@ namespace Sound { } stats; namespace Filter { - #define MAX_FDN 16 - #define MAX_DELAY 954 #define DSP_SCALE_BIT 8 #define DSP_SCALE (1 << DSP_SCALE_BIT) - static const int16 FDN[MAX_FDN] = { 281, 331, 373, 419, 461, 503, 547, 593, 641, 683, 727, 769, 811, 853, 907, 953 }; - - struct Delay { - int index; - int16 out[MAX_DELAY]; - - void process(int16 &x, int16 delay) { - index = (index + 1) % delay; - int16 y = out[index]; - out[index] = x; - x = y; - } - }; - - struct Absorption { - int16 out; - - void process(int16 &x, int32 gain, int32 damping) { - x = out = (out * damping + ((x * gain * (DSP_SCALE - damping)) >> DSP_SCALE_BIT)) >> DSP_SCALE_BIT; - } - }; - struct LowPass { float buffer[2][4]; @@ -117,88 +153,80 @@ namespace Sound { }; struct Reverberation { - Delay df[MAX_FDN]; - Absorption af[MAX_FDN]; + int32 pos; + int16 buffer[0x8000]; - int32 output[MAX_FDN]; - int16 buffer[MAX_FDN]; - int16 panCoeff[MAX_FDN][2]; - int32 absCoeff[MAX_FDN][2]; // absorption gain & damping + ReverbPreset preset; Reverberation() { - for (int i = 0; i < MAX_FDN; i++) { - panCoeff[i][0] = (i % 2) ? -1 : 1; - } - - for (int i = 0; i < MAX_FDN; i += 2) { - if ((i / 2) % 2) - panCoeff[i][1] = panCoeff[i + 1][1] = -1; - else - panCoeff[i][1] = panCoeff[i + 1][1] = 1; - } - + memcpy(&preset, SPU_REVERB + 0, sizeof(preset)); clear(); } void clear() { - memset(output, 0, sizeof(output)); - memset(df, 0, sizeof(df)); - memset(af, 0, sizeof(af)); - + // TODO setRoomSize(vec3(1.0f)); } void setRoomSize(const vec3 &size) { - float S = (size.x * size.z + size.x * size.y + size.z * size.y) * 2; - float V = size.x * size.y * size.z; - float f = 0.1f; // absorption factor - float rt60 = 0.161f * (V / (S * f)); - float k = -10.0f / (44100.0f * rt60); - - for (int i = 0; i < MAX_FDN; i++) { - float v = powf(10.0f, FDN[i] * k); - absCoeff[i][0] = int32(v * DSP_SCALE); - absCoeff[i][1] = int32((1.0f - (2.0f / (1.0f + powf(v, 1.0f - 1.0f / 0.15f)))) * DSP_SCALE); - } + // TODO }; + int16& value(int index) { + index += pos; + if (index >= preset.size) index -= preset.size; + if (index < 0) index += preset.size; + return buffer[index]; + } + + #define R(x) int32(preset.##x) + #define V(x) value(R(x)) + #define D(a,b) value(preset.##a - preset.##b) + void process(FrameHI *frames, int count) { PROFILE_CPU_TIMING(stats.reverb); for (int i = 0; i < count; i++) { FrameHI &frame = frames[i]; - int32 in = (frame.L + frame.R) / 2; - int32 out = 0; - int32 L = 0; - int32 R = 0; - // apply delay & absorption filters - for (int j = 0; j < MAX_FDN; j++) { - int16 k = clamp(in + output[j], -0x7FFF, 0x7FFF); - df[j].process(k, FDN[j]); - af[j].process(k, absCoeff[j][0], absCoeff[j][1]); - buffer[j] = k; - out += k; + int32 Lin = frame.L; + int32 Rin = frame.R; + + // Same Side Reflection (left-to-left and right-to-right) + V(mLSAME) = ((Lin + (V(dLSAME) * R(vWALL) >> 15) - V(mLSAME - 1)) * R(vIIR) >> 15) + V(mLSAME - 1); + V(mRSAME) = ((Rin + (V(dRSAME) * R(vWALL) >> 15) - V(mRSAME - 1)) * R(vIIR) >> 15) + V(mRSAME - 1); + + // Different Side Reflection (left-to-right and right-to-left) + V(mLDIFF) = ((Lin + (V(dRDIFF) * R(vWALL) >> 15) - V(mLDIFF - 1)) * R(vIIR) >> 15) + V(mLDIFF - 1); + V(mRDIFF) = ((Rin + (V(dLDIFF) * R(vWALL) >> 15) - V(mRDIFF - 1)) * R(vIIR) >> 15) + V(mRDIFF - 1); + + // Early Echo (Comb Filter, with input from buffer) + int32 Lout = (R(vCOMB1) * V(mLCOMB1) + R(vCOMB2) * V(mLCOMB2) + R(vCOMB3) * V(mLCOMB3) + R(vCOMB4) * V(mLCOMB4)) >> 15; + int32 Rout = (R(vCOMB1) * V(mRCOMB1) + R(vCOMB2) * V(mRCOMB2) + R(vCOMB3) * V(mRCOMB3) + R(vCOMB4) * V(mRCOMB4)) >> 15; + + // Late Reverb APF1 (All Pass Filter 1, with input from COMB) + Lout = Lout - (R(vAPF1) * D(mLAPF1, dAPF1) >> 15); V(mLAPF1) = Lout; Lout = (Lout * R(vAPF1) >> 15) + D(mLAPF1, dAPF1); + Rout = Rout - (R(vAPF1) * D(mRAPF1, dAPF1) >> 15); V(mRAPF1) = Rout; Rout = (Rout * R(vAPF1) >> 15) + D(mRAPF1, dAPF1); + + // Late Reverb APF2 (All Pass Filter 2, with input from APF1) + Lout = Lout - (R(vAPF2) * D(mLAPF2, dAPF2) >> 15); V(mLAPF2) = Lout; Lout = (Lout * R(vAPF2) >> 15) + D(mLAPF2, dAPF2); + Rout = Rout - (R(vAPF2) * D(mRAPF2, dAPF2) >> 15); V(mRAPF2) = Rout; Rout = (Rout * R(vAPF2) >> 15) + D(mRAPF2, dAPF2); + + // Output to Mixer (Output volume multiplied with input from APF2) + frame.L = Lout; + frame.R = Rout; + + pos++; + if (pos >= preset.size) { + pos -= preset.size; } - out = out * 2 / MAX_FDN; - - // apply pan - int16 buf = buffer[MAX_FDN - 1]; - for (int j = 0; j < MAX_FDN; j++) { - output[j] = max(0, out - buf); - buf = buffer[j]; - L += buf ^ panCoeff[j][0]; - R += buf ^ panCoeff[j][1]; - } - - frame.L += L / MAX_FDN; - frame.R += R / MAX_FDN; } } - }; - #undef MAX_FDN - #undef MAX_DELAY + #undef R + #undef V + #undef D + }; }; struct Decoder {