mirror of
https://codeberg.org/armin/ambivalence.git
synced 2026-09-01 12:20:48 +02:00
103 lines
3.8 KiB
C++
103 lines
3.8 KiB
C++
|
|
#include "BiquadFilters.h"
|
||
|
|
#include "MagnitudeResponseFitter.h"
|
||
|
|
#include <JuceHeader.h>
|
||
|
|
#include <cmath>
|
||
|
|
#include <algorithm>
|
||
|
|
|
||
|
|
namespace FDNReverb {
|
||
|
|
namespace FilterDesign {
|
||
|
|
|
||
|
|
static float tanPi(float f, double fs) noexcept {
|
||
|
|
return std::tan(juce::MathConstants<float>::pi * (float)(f / fs));
|
||
|
|
}
|
||
|
|
|
||
|
|
BiquadCoeffs lowShelf(float fcHz, float gainDB, double sampleRate) {
|
||
|
|
float A = std::pow(10.f, gainDB / 40.f);
|
||
|
|
float K = tanPi(fcHz, sampleRate);
|
||
|
|
BiquadCoeffs c;
|
||
|
|
if (gainDB >= 0.f) {
|
||
|
|
float norm = 1.f / (1.f + K);
|
||
|
|
c.b0 = (1.f + A * K) * norm;
|
||
|
|
c.b1 = (A * K - 1.f) * norm;
|
||
|
|
c.b2 = 0.f;
|
||
|
|
c.a1 = (K - 1.f) * norm;
|
||
|
|
c.a2 = 0.f;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
c.b0 = (1.f + K / A) / (1.f + K);
|
||
|
|
c.b1 = (K / A - 1.f) / (1.f + K);
|
||
|
|
c.b2 = 0.f;
|
||
|
|
c.a1 = (K - 1.f) / (1.f + K);
|
||
|
|
c.a2 = 0.f;
|
||
|
|
}
|
||
|
|
return c;
|
||
|
|
}
|
||
|
|
|
||
|
|
BiquadCoeffs highShelf(float fcHz, float gainDB, double sampleRate) {
|
||
|
|
float A = std::pow(10.f, gainDB / 40.f);
|
||
|
|
float K = tanPi(fcHz, sampleRate);
|
||
|
|
BiquadCoeffs c;
|
||
|
|
if (gainDB >= 0.f) {
|
||
|
|
float norm = 1.f / (1.f + K);
|
||
|
|
c.b0 = (A + K) * norm;
|
||
|
|
c.b1 = (K - A) * norm;
|
||
|
|
c.b2 = 0.f;
|
||
|
|
c.a1 = (K - 1.f) * norm;
|
||
|
|
c.a2 = 0.f;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
float norm = 1.f / (1.f + K);
|
||
|
|
c.b0 = (1.f + A * K) * norm;
|
||
|
|
c.b1 = (A * K - 1.f) * norm;
|
||
|
|
c.b2 = 0.f;
|
||
|
|
c.a1 = (K - 1.f) * norm;
|
||
|
|
c.a2 = 0.f;
|
||
|
|
}
|
||
|
|
return c;
|
||
|
|
}
|
||
|
|
|
||
|
|
BiquadCoeffs peak(float fcHz, float gainDB, float Q, double sampleRate) {
|
||
|
|
float A = std::pow(10.f, gainDB / 40.f);
|
||
|
|
float w0 = 2.f * juce::MathConstants<float>::pi * fcHz / (float)sampleRate;
|
||
|
|
float alpha = std::sin(w0) / (2.f * Q);
|
||
|
|
float cos0 = std::cos(w0);
|
||
|
|
BiquadCoeffs c;
|
||
|
|
c.a1 = 2.f * cos0 / (1.f + alpha / A);
|
||
|
|
c.a2 = (1.f - alpha / A) / (1.f + alpha / A);
|
||
|
|
c.b0 = (1.f + alpha * A) / (1.f + alpha / A);
|
||
|
|
c.b1 = -2.f * cos0 / (1.f + alpha / A);
|
||
|
|
c.b2 = (1.f - alpha * A) / (1.f + alpha / A);
|
||
|
|
return c;
|
||
|
|
}
|
||
|
|
|
||
|
|
BiquadCoeffs highPass1st(float fcHz, double sampleRate) {
|
||
|
|
float K = tanPi(fcHz, sampleRate);
|
||
|
|
float n = 1.f + K;
|
||
|
|
BiquadCoeffs c;
|
||
|
|
c.b0 = 1.f / n; c.b1 = -1.f / n; c.b2 = 0.f;
|
||
|
|
c.a1 = (K - 1.f) / n; c.a2 = 0.f;
|
||
|
|
return c;
|
||
|
|
}
|
||
|
|
|
||
|
|
// -------------------------------------------------------------------------
|
||
|
|
// designAbsorption: MagnitudeResponseFitter
|
||
|
|
// -------------------------------------------------------------------------
|
||
|
|
// keeps the existing (UniversalEngine) helper functions,
|
||
|
|
// preserving the internal Stage-1 MRF behavior.
|
||
|
|
//
|
||
|
|
// old implementation : gain + Low/High cascade
|
||
|
|
// new implementation : Jot orthogonalizing 1 filter + LF/HF correction
|
||
|
|
// -------------------------------------------------------------------------
|
||
|
|
std::array<BiquadCoeffs, ABSO_STAGES> designAbsorption(
|
||
|
|
int delaySamples, double sampleRate,
|
||
|
|
const std::array<float, NUM_BANDS>& rt60,
|
||
|
|
float hfDamping, float lfAbsorption)
|
||
|
|
{
|
||
|
|
// MagnitudeResponseFitter processing
|
||
|
|
auto result = MagnitudeResponseFitter::design(
|
||
|
|
delaySamples, sampleRate, rt60, hfDamping, lfAbsorption);
|
||
|
|
return result.coeffs;
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace FilterDesign
|
||
|
|
} // namespace FDNReverb
|