Skip to content

Commit

Permalink
Add a DC Blocker for non-0-0 waveshapers (surge-synthesizer#4785)
Browse files Browse the repository at this point in the history
Waveshapers which don't map 0 to 0 can leave a nasty
click on end of note if note results in silence.
For those, we have a simple dc blocker which kills
vlf dc signals and which we apply to the appropriate
wavetables.

Addresses surge-synthesizer#1964
  • Loading branch information
baconpaul authored Jul 29, 2021
1 parent 2dd1842 commit dd49e03
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 11 deletions.
40 changes: 29 additions & 11 deletions src/common/dsp/QuadFilterWaveshapers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,19 @@ __m128 WS_LUT(QuadFilterWaveshaperState *__restrict s, const float *table, __m12
return x;
}

template <int R1, int R2> inline __m128 dcBlock(QuadFilterWaveshaperState *__restrict s, __m128 x)
{
// https://www.dsprelated.com/freebooks/filters/DC_Blocker.html
// y_n = x_n - x_n-1 + R y_n-1
const auto fac = _mm_set1_ps(0.9999);
auto dx = _mm_sub_ps(x, s->R[0]);
auto filtval = _mm_add_ps(dx, _mm_mul_ps(fac, s->R[1]));
s->R[R1] = x;
s->R[R2] = filtval;
s->init = _mm_setzero_ps();
return filtval;
}

// Given a table of size N+1, N a power of 2, representing data between -1 and 1, interp
template <int N> __m128 WS_PM1_LUT(const float *table, __m128 in)
{
Expand Down Expand Up @@ -259,7 +272,7 @@ template <int scale, __m128 C(QuadFilterWaveshaperState *__restrict, __m128, __m
__m128 Fuzz(QuadFilterWaveshaperState *__restrict s, __m128 x, __m128 drive)
{
static LUTBase<1024, FuzzTable<scale>> table;
return WS_PM1_LUT<1024>(table.data, C(s, x, drive));
return dcBlock<0, 1>(s, WS_PM1_LUT<1024>(table.data, C(s, x, drive)));
}

float FuzzCtrTable(const float x)
Expand All @@ -278,16 +291,21 @@ float FuzzCtrTable(const float x)
__m128 FuzzCtr(QuadFilterWaveshaperState *__restrict s, __m128 x, __m128 drive)
{
static LUTBase<2048, FuzzCtrTable> table;
return WS_PM1_LUT<2048>(table.data, TANH(s, x, drive));
return dcBlock<0, 1>(s, WS_PM1_LUT<2048>(table.data, TANH(s, x, drive)));
}

template <__m128 (*K)(__m128)>
template <__m128 (*K)(__m128), bool useDCBlock>
__m128 CHEBY_CORE(QuadFilterWaveshaperState *__restrict s, __m128 x, __m128 drive)
{
static const auto m1 = _mm_set1_ps(-1.0f);
static const auto p1 = _mm_set1_ps(1.0f);

auto bound = K(_mm_max_ps(_mm_min_ps(x, p1), m1));

if (useDCBlock)
{
bound = dcBlock<0, 1>(s, bound);
}
return TANH(s, bound, drive);
}

Expand Down Expand Up @@ -369,7 +387,7 @@ __m128 Plus12(QuadFilterWaveshaperState *__restrict s, __m128 in, __m128 drive)
{
static const auto ser = ChebSeries<3>({0, 0.5, 0.5});
static const auto scale = _mm_set1_ps(0.66);
return ser.eval(TANH(s, _mm_mul_ps(in, scale), drive));
return dcBlock<0, 1>(s, ser.eval(TANH(s, _mm_mul_ps(in, scale), drive)));
}

__m128 Plus13(QuadFilterWaveshaperState *__restrict s, __m128 in, __m128 drive)
Expand All @@ -383,7 +401,7 @@ __m128 Plus14(QuadFilterWaveshaperState *__restrict s, __m128 in, __m128 drive)
{
static const auto ser = ChebSeries<5>({0, 0.5, 0, 0, 0.5});
static const auto scale = _mm_set1_ps(0.66);
return ser.eval(TANH(s, _mm_mul_ps(in, scale), drive));
return dcBlock<0, 1>(s, ser.eval(TANH(s, _mm_mul_ps(in, scale), drive)));
}

__m128 Plus15(QuadFilterWaveshaperState *__restrict s, __m128 in, __m128 drive)
Expand All @@ -397,15 +415,15 @@ __m128 Plus12345(QuadFilterWaveshaperState *__restrict s, __m128 in, __m128 driv
{
static const auto ser = ChebSeries<6>({0, 0.2, 0.2, 0.2, 0.2, 0.2});
static const auto scale = _mm_set1_ps(0.66);
return ser.eval(TANH(s, _mm_mul_ps(in, scale), drive));
return dcBlock<0, 1>(s, ser.eval(TANH(s, _mm_mul_ps(in, scale), drive)));
}

__m128 PlusSaw3(QuadFilterWaveshaperState *__restrict s, __m128 in, __m128 drive)
{
static const float fac = 0.9f / (1.f + 0.5 + 0.25);
static const auto ser = ChebSeries<4>({0, -fac, fac * 0.5f, -fac * 0.25f});
static const auto scale = _mm_set1_ps(-0.66); // flip direction
return ser.eval(TANH(s, _mm_mul_ps(in, scale), drive));
return dcBlock<0, 1>(s, ser.eval(TANH(s, _mm_mul_ps(in, scale), drive)));
}

__m128 PlusSqr3(QuadFilterWaveshaperState *__restrict s, __m128 in, __m128 drive)
Expand Down Expand Up @@ -634,13 +652,13 @@ WaveshaperQFPtr GetQFPtrWaveshaper(int type)
case wst_digital:
return DIGI_SSE2;
case wst_cheby2:
return CHEBY_CORE<cheb2_kernel>;
return CHEBY_CORE<cheb2_kernel, true>;
case wst_cheby3:
return CHEBY_CORE<cheb3_kernel>;
return CHEBY_CORE<cheb3_kernel, false>;
case wst_cheby4:
return CHEBY_CORE<cheb4_kernel>;
return CHEBY_CORE<cheb4_kernel, true>;
case wst_cheby5:
return CHEBY_CORE<cheb5_kernel>;
return CHEBY_CORE<cheb5_kernel, false>;
case wst_fwrectify:
return ADAA_FULL_WAVE;
case wst_poswav:
Expand Down
9 changes: 9 additions & 0 deletions src/gui/widgets/WaveShaperSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ struct WaveShaperAnalysisWidget : public juce::Component, public juce::Slider::L
{
wss.R[i] = _mm_set1_ps(R[i]);
}
wss.init = _mm_cmpeq_ps(_mm_setzero_ps(), _mm_setzero_ps()); // better way?

auto wsop = GetQFPtrWaveshaper(wstype);

Expand Down Expand Up @@ -212,6 +213,9 @@ struct WaveShaperAnalysisWidget : public juce::Component, public juce::Slider::L
ws2.R[i] = _mm_set1_ps(R[i]);
}

ws1.init = _mm_cmpeq_ps(_mm_setzero_ps(), _mm_setzero_ps()); // better way?
ws2.init = _mm_cmpeq_ps(_mm_setzero_ps(), _mm_setzero_ps()); // better way?

auto wsop = GetQFPtrWaveshaper(wstype);

for (int i = 0; i < npts; i++)
Expand Down Expand Up @@ -286,6 +290,7 @@ void WaveShaperSelector::paint(juce::Graphics &g)
initializeWaveshaperRegister(iValue, R);
for (int i = 0; i < 4; ++i)
s.R[i] = _mm_load_ps(R);
s.init = _mm_cmpeq_ps(_mm_setzero_ps(), _mm_setzero_ps());

float dx = 0.05;
// Give a few warmup pixels for the ADAAs
Expand All @@ -296,6 +301,10 @@ void WaveShaperSelector::paint(juce::Graphics &g)
vals[0] = x;
auto in = _mm_load_ps(vals);
auto r = wsop(&s, in, drive);
for (int i = 0; i < 4; ++i)
s.R[i] = _mm_load_ps(R);
s.init = _mm_cmpeq_ps(_mm_setzero_ps(), _mm_setzero_ps());

_mm_store_ps(vals, r);
if (x >= -2)
wsCurves[iValue].emplace_back(x, vals[0]);
Expand Down

0 comments on commit dd49e03

Please sign in to comment.