133 lines
4.2 KiB
C++
133 lines
4.2 KiB
C++
#include "Reverb.h"
|
|
|
|
namespace serum
|
|
{
|
|
|
|
namespace
|
|
{
|
|
constexpr int combTuning[8] = { 1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617 };
|
|
constexpr int allpassTuning[4] = { 556, 441, 341, 225 };
|
|
}
|
|
|
|
void ReverbUnit::prepare (double sampleRate, int)
|
|
{
|
|
sr = sampleRate;
|
|
const double scale = sr / 44100.0;
|
|
|
|
for (int i = 0; i < 8; ++i)
|
|
{
|
|
const int len = (int) (combTuning[i] * scale) + 1;
|
|
combL[(size_t) i].assign ((size_t) len, 0.0f);
|
|
combR[(size_t) i].assign ((size_t) len, 0.0f);
|
|
}
|
|
for (int i = 0; i < 4; ++i)
|
|
{
|
|
const int len = (int) (allpassTuning[i] * scale) + 1;
|
|
apL[(size_t) i].assign ((size_t) len, 0.0f);
|
|
apR[(size_t) i].assign ((size_t) len, 0.0f);
|
|
}
|
|
|
|
const int preLen = (int) (sr * 0.2);
|
|
preL.assign ((size_t) preLen, 0.0f);
|
|
preR.assign ((size_t) preLen, 0.0f);
|
|
reset();
|
|
}
|
|
|
|
void ReverbUnit::reset()
|
|
{
|
|
for (int i = 0; i < 8; ++i)
|
|
{
|
|
std::fill (combL[(size_t) i].begin(), combL[(size_t) i].end(), 0.0f);
|
|
std::fill (combR[(size_t) i].begin(), combR[(size_t) i].end(), 0.0f);
|
|
combPosL[(size_t) i] = combPosR[(size_t) i] = 0;
|
|
combFiltL[(size_t) i] = combFiltR[(size_t) i] = 0.0f;
|
|
}
|
|
for (int i = 0; i < 4; ++i)
|
|
{
|
|
std::fill (apL[(size_t) i].begin(), apL[(size_t) i].end(), 0.0f);
|
|
std::fill (apR[(size_t) i].begin(), apR[(size_t) i].end(), 0.0f);
|
|
apPosL[(size_t) i] = apPosR[(size_t) i] = 0;
|
|
}
|
|
std::fill (preL.begin(), preL.end(), 0.0f);
|
|
std::fill (preR.begin(), preR.end(), 0.0f);
|
|
prePos = 0;
|
|
}
|
|
|
|
void ReverbUnit::process (float* l, float* r, int numSamples, const float p[4])
|
|
{
|
|
const float feedback = maps::sizeToValue (p[0]);
|
|
const float damping = p[1];
|
|
const float width = p[2];
|
|
const int preLen = (int) preL.size();
|
|
const float preDelay = (preLen > 0) ? p[3] * 0.15f * sr : 0.0f;
|
|
|
|
for (int i = 0; i < numSamples; ++i)
|
|
{
|
|
// Predelay.
|
|
float inL = l[i], inR = r[i];
|
|
if (preLen > 0)
|
|
{
|
|
const int read = (prePos - (int) preDelay + preLen) % preLen;
|
|
preL[(size_t) prePos] = inL;
|
|
preR[(size_t) prePos] = inR;
|
|
inL = preL[(size_t) read];
|
|
inR = preR[(size_t) read];
|
|
prePos = (prePos + 1) % preLen;
|
|
}
|
|
|
|
// Combs.
|
|
float sumL = 0.0f, sumR = 0.0f;
|
|
for (int c = 0; c < 8; ++c)
|
|
{
|
|
auto& buf = combL[(size_t) c];
|
|
const int pos = combPosL[(size_t) c];
|
|
float out = buf[(size_t) pos];
|
|
combFiltL[(size_t) c] = out * (1.0f - damping) + combFiltL[(size_t) c] * damping;
|
|
buf[(size_t) pos] = inL + combFiltL[(size_t) c] * feedback;
|
|
combPosL[(size_t) c] = (pos + 1) % (int) buf.size();
|
|
sumL += out;
|
|
}
|
|
for (int c = 0; c < 8; ++c)
|
|
{
|
|
auto& buf = combR[(size_t) c];
|
|
const int pos = combPosR[(size_t) c];
|
|
float out = buf[(size_t) pos];
|
|
combFiltR[(size_t) c] = out * (1.0f - damping) + combFiltR[(size_t) c] * damping;
|
|
buf[(size_t) pos] = inR + combFiltR[(size_t) c] * feedback;
|
|
combPosR[(size_t) c] = (pos + 1) % (int) buf.size();
|
|
sumR += out;
|
|
}
|
|
|
|
// Allpasses.
|
|
float aL = sumL, aR = sumR;
|
|
for (int a = 0; a < 4; ++a)
|
|
{
|
|
auto& buf = apL[(size_t) a];
|
|
const int pos = apPosL[(size_t) a];
|
|
const float stored = buf[(size_t) pos];
|
|
buf[(size_t) pos] = aL + stored * 0.5f;
|
|
aL = stored - aL;
|
|
apPosL[(size_t) a] = (pos + 1) % (int) buf.size();
|
|
}
|
|
for (int a = 0; a < 4; ++a)
|
|
{
|
|
auto& buf = apR[(size_t) a];
|
|
const int pos = apPosR[(size_t) a];
|
|
const float stored = buf[(size_t) pos];
|
|
buf[(size_t) pos] = aR + stored * 0.5f;
|
|
aR = stored - aR;
|
|
apPosR[(size_t) a] = (pos + 1) % (int) buf.size();
|
|
}
|
|
|
|
// Width (stereo cross-mix).
|
|
const float w = 1.0f - width * 0.5f;
|
|
const float outL = aL * w + aR * (1.0f - w);
|
|
const float outR = aR * w + aL * (1.0f - w);
|
|
|
|
l[i] = outL;
|
|
r[i] = outR;
|
|
}
|
|
}
|
|
|
|
} // namespace serum
|