CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/HW/SasReverb.cpp
Views: 1401
// Copyright (c) 2015- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include <cstdint>18#include <cstring>1920#include "Common/Math/math_util.h"21#include "Core/Config.h"22#include "Core/HW/SasReverb.h"23#include "Core/Util/AudioFormat.h"2425// This is under the assumption that the reverb used in Sas is the same as the PSX SPU reverb.2627// Source: http://problemkaputt.de/psx-spx.htm#spureverbformula2829struct SasReverbData {30const char *name;31int32_t size;3233int16_t dAPF1;34int16_t dAPF2;35int16_t vIIR;36int16_t vCOMB1;37int16_t vCOMB2;38int16_t vCOMB3;39int16_t vCOMB4;40int16_t vWALL;4142int16_t vAPF1;43int16_t vAPF2;44int16_t mLSAME;45int16_t mRSAME;46int16_t mLCOMB1;47int16_t mRCOMB1;48int16_t mLCOMB2;49int16_t mRCOMB2;5051int16_t dLSAME;52int16_t dRSAME;53int16_t mLDIFF;54int16_t mRDIFF;55int16_t mLCOMB3;56int16_t mRCOMB3;57int16_t mLCOMB4;58int16_t mRCOMB4;5960int16_t dLDIFF;61int16_t dRDIFF;62int16_t mLAPF1;63int16_t mRAPF1;64int16_t mLAPF2;65int16_t mRAPF2;6667// These aren't used for anything else than 1.0 in any of the presets so let's drop them.68// int16_t vLIN;69// int16_t vRIN;70};7172static const SasReverbData presets[10] = {73{74"Room",750x26C0,760x007D,0x005B,0x6D80,0x54B8,(int16_t)0xBED0,0x0000,0x0000,(int16_t)0xBA80,770x5800,0x5300,0x04D6,0x0333,0x03F0,0x0227,0x0374,0x01EF,780x0334,0x01B5,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,790x0000,0x0000,0x01B4,0x0136,0x00B8,0x005C, //(int16_t)0x8000,(int16_t)0x8000,80},81{82"Studio Small",830x1F40,840x0033,0x0025,0x70F0,0x4FA8,(int16_t)0xBCE0,0x4410,(int16_t)0xC0F0,(int16_t)0x9C00,850x5280,0x4EC0,0x03E4,0x031B,0x03A4,0x02AF,0x0372,0x0266,860x031C,0x025D,0x025C,0x018E,0x022F,0x0135,0x01D2,0x00B7,870x018F,0x00B5,0x00B4,0x0080,0x004C,0x0026, //(int16_t)0x8000,(int16_t)0x8000,88},8990{91"Studio Medium",920x4840,930x00B1,0x007F,0x70F0,0x4FA8,(int16_t)0xBCE0,0x4510,(int16_t)0xBEF0,(int16_t)0xB4C0,940x5280,0x4EC0,0x0904,0x076B,0x0824,0x065F,0x07A2,0x0616,950x076C,0x05ED,0x05EC,0x042E,0x050F,0x0305,0x0462,0x02B7,960x042F,0x0265,0x0264,0x01B2,0x0100,0x0080, //(int16_t)0x8000,(int16_t)0x8000,97},9899// Studio Large(size = 6FE0h bytes)100{101"Studio Large",1020x6FE0,1030x00E3,0x00A9,0x6F60,0x4FA8,(int16_t)0xBCE0,0x4510,(int16_t)0xBEF0,(int16_t)0xA680,1040x5680,0x52C0,0x0DFB,0x0B58,0x0D09,0x0A3C,0x0BD9,0x0973,1050x0B59,0x08DA,0x08D9,0x05E9,0x07EC,0x04B0,0x06EF,0x03D2,1060x05EA,0x031D,0x031C,0x0238,0x0154,0x00AA, //(int16_t)0x8000,(int16_t)0x8000,107},108109{110"Hall",1110xADE0,1120x01A5,0x0139,0x6000,0x5000,0x4C00,(int16_t)0xB800,(int16_t)0xBC00,(int16_t)0xC000,1130x6000,0x5C00,0x15BA,0x11BB,0x14C2,0x10BD,0x11BC,0x0DC1,1140x11C0,0x0DC3,0x0DC0,0x09C1,0x0BC4,0x07C1,0x0A00,0x06CD,1150x09C2,0x05C1,0x05C0,0x041A,0x0274,0x013A, //(int16_t)0x8000,(int16_t)0x8000,116},117118{119"Space Echo",1200xF6C0,1210x033D,0x0231,0x7E00,0x5000,(int16_t)0xB400,(int16_t)0xB000,0x4C00,(int16_t)0xB000,1220x6000,0x5400,0x1ED6,0x1A31,0x1D14,0x183B,0x1BC2,0x16B2,1230x1A32,0x15EF,0x15EE,0x1055,0x1334,0x0F2D,0x11F6,0x0C5D,1240x1056,0x0AE1,0x0AE0,0x07A2,0x0464,0x0232, //(int16_t)0x8000,(int16_t)0x8000,125},126127{128"Echo (almost infinite)",1290x18040,1300x0001,0x0001,0x7FFF,0x7FFF,0x0000,0x0000,0x0000,(int16_t)0xC080,1310x0000,0x0000,0x1FFF,0x0FFF,0x1005,0x0005,0x0000,0x0000,1320x1005,0x0005,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,1330x0000,0x0000,0x1004,0x1002,0x0004,0x0002, //(int16_t)0x8000,(int16_t)0x8000,134},135136{137"Delay (one - shot echo)",1380x18040,1390x0001,0x0001,0x7FFF,0x7FFF,0x0000,0x0000,0x0000,0x0000,1400x0000,0x0000,0x1FFF,0x0FFF,0x1005,0x0005,0x0000,0x0000,1410x1005,0x0005,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,1420x0000,0x0000,0x1004,0x1002,0x0004,0x0002, //(int16_t)0x8000,(int16_t)0x8000,143},144145{146"Half Echo",1470x3C00,1480x0017,0x0013,0x70F0,0x4FA8,(int16_t)0xBCE0,0x4510,(int16_t)0xBEF0,(int16_t)0x8500,1490x5F80,0x54C0,0x0371,0x02AF,0x02E5,0x01DF,0x02B0,0x01D7,1500x0358,0x026A,0x01D6,0x011E,0x012D,0x00B1,0x011F,0x0059,1510x01A0,0x00E3,0x0058,0x0040,0x0028,0x0014, //(int16_t)0x8000,(int16_t)0x8000,152},153};154155SasReverb::SasReverb() : preset_(-1), pos_(0) {156workspace_ = new int16_t[BUFSIZE];157}158159SasReverb::~SasReverb() {160delete[] workspace_;161}162163const char *SasReverb::GetPresetName(int preset) {164if (preset == -1) {165return "Off";166}167return presets[preset].name;168}169170void SasReverb::SetPreset(int preset) {171if (preset < (int)ARRAY_SIZE(presets))172preset_ = preset;173if (preset_ != -1) {174pos_ = BUFSIZE - presets[preset_].size;175memset(workspace_, 0, sizeof(int16_t) * BUFSIZE);176} else {177pos_ = 0;178}179}180181// Wraps around the upper part of a buffer.182template<int bufsize>183class BufferWrapper {184public:185BufferWrapper(int16_t *buffer, int position, int usedSize) : buf_(buffer), pos_(position), end_(bufsize), base_(bufsize - usedSize), size_(usedSize) {}186int16_t &operator [](int index) {187int addr = pos_ + index;188if (addr >= end_) { addr -= size_; }189if (addr < base_) { addr += size_; }190return buf_[addr];191}192193int GetPosition() { return pos_; }194void Next() {195pos_++;196if (pos_ >= end_) {197pos_ -= size_;198}199}200201private:202int16_t *buf_;203int pos_;204int end_;205int base_;206int size_;207};208209void SasReverb::ProcessReverb(int16_t *output, const int16_t *input, size_t inputSize, uint16_t volLeft, uint16_t volRight) {210// This means replicate the input signal in the processed buffer.211// Can also be used to verify that the error is in here...212if (preset_ == -1) {213// Strangely, OFF is not filled with zeroes every other. Seems special cased.214for (size_t i = 0; i < inputSize; ++i) {215output[i * 4 + 0] = clamp_s16((int)input[i * 2 + 0] * volLeft >> 15);216output[i * 4 + 1] = clamp_s16((int)input[i * 2 + 1] * volRight >> 15);217output[i * 4 + 2] = clamp_s16((int)input[i * 2 + 0] * volLeft >> 15);218output[i * 4 + 3] = clamp_s16((int)input[i * 2 + 1] * volRight >> 15);219}220return;221}222223const uint8_t reverbVolume = Clamp(g_Config.iReverbVolume, 0, 25);224// Standard volume is 10, which pairs with a normal shift of 15.225const uint8_t finalShift = 25 - reverbVolume;226if (reverbVolume == 0) {227// Force to zero output, which is not the same as "Off."228memset(output, 0, inputSize * 4);229return;230}231232const SasReverbData &d = presets[preset_];233234// We put this on the stack instead of in the object to let the compiler optimize better (avoid mem r/w).235BufferWrapper<BUFSIZE> b(workspace_, pos_, d.size);236237// This runs at 22khz.238// Very unoptimized, straight from the description. Can probably be reformulated into something way more efficient.239// Or we could actually template the whole thing with the parameters as template arguments, as the presets are fixed.240for (size_t i = 0; i < inputSize; i++) {241// Dividing by two here is an incorrect hack. Some multiplication factor is needed to prevent the reverb from getting too loud, though.242int16_t LeftInput = input[i * 2] >> 1;243int16_t RightInput = input[i * 2 + 1] >> 1;244245int16_t Lin = LeftInput; // (d.vLIN * LeftInput) >> 15;246int16_t Rin = RightInput; // (d.vRIN * RightInput) >> 15;247248// ____Same Side Reflection(left - to - left and right - to - right)___________________249b[d.mLSAME] = clamp_s16(Lin + (b[d.dLSAME] * d.vWALL >> 15) - (b[d.mLSAME - 1]*d.vIIR >> 15) + b[d.mLSAME - 1]); // L - to - L250b[d.mRSAME] = clamp_s16(Rin + (b[d.dRSAME] * d.vWALL >> 15) - (b[d.mRSAME - 1]*d.vIIR >> 15) + b[d.mRSAME - 1]); // R - to - R251// ___Different Side Reflection(left - to - right and right - to - left)_______________252b[d.mLDIFF] = clamp_s16(Lin + (b[d.dRDIFF] * d.vWALL >> 15) - (b[d.mLDIFF - 1]*d.vIIR >> 15) + b[d.mLDIFF - 1]); // R - to - L253b[d.mRDIFF] = clamp_s16(Rin + (b[d.dLDIFF] * d.vWALL >> 15) - (b[d.mRDIFF - 1]*d.vIIR >> 15) + b[d.mRDIFF - 1]); // L - to - R254// ___Early Echo(Comb Filter, with input from buffer)__________________________255int32_t Lout = ((d.vCOMB1*b[d.mLCOMB1] + d.vCOMB2*b[d.mLCOMB2] + d.vCOMB3*b[d.mLCOMB3] + d.vCOMB4*b[d.mLCOMB4]) >> 15);256int32_t Rout = ((d.vCOMB1*b[d.mRCOMB1] + d.vCOMB2*b[d.mRCOMB2] + d.vCOMB3*b[d.mRCOMB3] + d.vCOMB4*b[d.mRCOMB4]) >> 15);257// ___Late Reverb APF1(All Pass Filter 1, with input from COMB)________________258b[d.mLAPF1] = clamp_s16(Lout - (d.vAPF1*b[(d.mLAPF1 - d.dAPF1)] >> 15));259Lout = b[(d.mLAPF1 - d.dAPF1)] + (b[d.mLAPF1] * d.vAPF1 >> 15);260b[d.mRAPF1] = clamp_s16(Rout - (d.vAPF1*b[(d.mRAPF1 - d.dAPF1)] >> 15));261Rout = b[(d.mRAPF1 - d.dAPF1)] + (b[d.mRAPF1] * d.vAPF1 >> 15);262// ___Late Reverb APF2(All Pass Filter 2, with input from APF1)________________263b[d.mLAPF2] = clamp_s16(Lout - (d.vAPF2*b[(d.mLAPF2 - d.dAPF2)] >> 15));264Lout = b[(d.mLAPF2 - d.dAPF2)] + (b[d.mLAPF2] * d.vAPF2 >> 15);265b[d.mRAPF2] = clamp_s16(Rout - (d.vAPF2*b[(d.mRAPF2 - d.dAPF2)] >> 15));266Rout = b[(d.mRAPF2 - d.dAPF2)] + (b[d.mRAPF2] * d.vAPF2 >> 15);267// ___Output to Mixer(Output volume multiplied with input from APF2)___________268output[i * 4 + 0] = clamp_s16((Lout * volLeft) >> finalShift);269output[i * 4 + 1] = clamp_s16((Rout * volRight) >> finalShift);270output[i * 4 + 2] = 0;271output[i * 4 + 3] = 0;272273b.Next();274}275276// Save the state in the object.277pos_ = b.GetPosition();278}279280281282