Skip to content

Commit

Permalink
Improve startup regularity of noise source
Browse files Browse the repository at this point in the history
the noise source has an implicit filter on history but that
filter needs a few ticks to warm up. So change the attack
process for noise and s&h such that

1. It takes deform into account at outset and
2. It runs the filter long enough to make transient |x|>1 way
   way way less frequent. (Like from 1 in 500 to 1 in 10000)
  • Loading branch information
baconpaul committed Jan 6, 2025
1 parent 2e4f559 commit 6424cad
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 29 deletions.
38 changes: 31 additions & 7 deletions include/sst/basic-blocks/modulators/SimpleLFO.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ template <typename SRProvider, int BLOCK_SIZE> struct SimpleLFO
for (int i = 0; i < BLOCK_SIZE; ++i)
outputBlock[i] = 0;

restartRandomSequence();
restartRandomSequence(0.f);
}

// Move towards this so we can remove the rng member above
Expand All @@ -76,7 +76,7 @@ template <typename SRProvider, int BLOCK_SIZE> struct SimpleLFO
for (int i = 0; i < BLOCK_SIZE; ++i)
outputBlock[i] = 0;

restartRandomSequence();
restartRandomSequence(0.f);
}

enum Shape
Expand All @@ -95,14 +95,31 @@ template <typename SRProvider, int BLOCK_SIZE> struct SimpleLFO
float outputBlock[BLOCK_SIZE];
float phase{0};

inline void restartRandomSequence()
inline void restartRandomSequence(double corr)
{
rngState[0] = urng();
rngState[1] = urng();
for (int i = 0; i < 4; ++i)
// We have to restart and make sure the correlation filter works so do two things
// First, warm it up with a quick blast. Second, if it does produce out of bound value
// then try again.
for (auto i = 0; i < 50; ++i)
{
rngCurrent = dsp::correlated_noise_o2mk2_suppliedrng(rngState[0], rngState[1], 0, urng);
rngHistory[3 - i] = rngCurrent;
rngCurrent =
dsp::correlated_noise_o2mk2_suppliedrng(rngState[0], rngState[1], corr, urng);
}
int its{0};
bool allGood{false};
while (its < 20 && !allGood)
{
allGood = true;
for (int i = 0; i < 4; ++i)
{
rngCurrent =
dsp::correlated_noise_o2mk2_suppliedrng(rngState[0], rngState[1], corr, urng);
rngHistory[3 - i] = rngCurrent;
allGood = allGood && rngHistory[3 - i] > -1 && rngHistory[3 - i] < 1;
}
its++;
}
}

Expand Down Expand Up @@ -137,6 +154,7 @@ template <typename SRProvider, int BLOCK_SIZE> struct SimpleLFO
amplitude = 1;
}

bool needsRandomRestart{false};
inline void attack(const int lshape)
{
phase = 0;
Expand All @@ -146,7 +164,8 @@ template <typename SRProvider, int BLOCK_SIZE> struct SimpleLFO

if (lshape == SH_NOISE || lshape == SMOOTH_NOISE)
{
restartRandomSequence();
needsRandomRestart = true;
phase = 1.000001;
}
}

Expand Down Expand Up @@ -190,6 +209,11 @@ template <typename SRProvider, int BLOCK_SIZE> struct SimpleLFO
{
// The deform can push correlated noise out of bounds
auto ud = d * 0.8;
if (needsRandomRestart)
{
restartRandomSequence(ud);
needsRandomRestart = false;
}
rngCurrent =
dsp::correlated_noise_o2mk2_suppliedrng(rngState[0], rngState[1], ud, urng);

Expand Down
46 changes: 24 additions & 22 deletions tests/modulator_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@
* https://github.com/surge-synthesizer/sst-basic-blocks
*/

#include <iostream>

#include "catch2.hpp"
#include "sst/basic-blocks/simd/setup.h"
#include "sst/basic-blocks/modulators/FXModControl.h"
#include "sst/basic-blocks/modulators/SimpleLFO.h"

#include <iostream>

namespace smod = sst::basic_blocks::modulators;

TEST_CASE("Mod LFO Is Well Behaved", "[mod]")
Expand Down Expand Up @@ -69,34 +69,36 @@ struct SRProvider
float envelope_rate_linear_nowrap(float f) const { return tbs * sampleRateInv * pow(2.f, -f); }
};

TEST_CASE("SimpleLFO is Bounded")
#if 0
// Well it turns out random isn't strictly bounded, so this test is no good, but we are
// close to boudned now so leave it here in case we want to tweak more
TEST_CASE("Random SimpleLFO is Bounded")
{
SRProvider sr;
using slfo_t = sst::basic_blocks::modulators::SimpleLFO<SRProvider, bs>;

sst::basic_blocks::dsp::RNG urng;
for (auto s = (int)slfo_t::SINE; s <= (int)slfo_t::RANDOM_TRIGGER; ++s)
for (auto tries = 0; tries < 10000; ++tries)
{
DYNAMIC_SECTION("Test shape " << s)
auto sd = urng.unifU32();
auto def = urng.unifPM1() * 0.95;
auto rt = urng.unif01() * 6 - 2;

// sd=2663274685; rt=1.37236; def=0.00200983;
urng.reseed(sd);
INFO("sd=" << sd << "; rt=" << rt << "; def=" << def << ";");
auto lfo = slfo_t(&sr, urng);
lfo.attack(slfo_t::Shape::SMOOTH_NOISE);
for (int i = 0; i < 1000; ++i)
{
for (auto tries = 0; tries < 500; ++tries)
lfo.process_block(rt, def, slfo_t::Shape::SMOOTH_NOISE);
for (int j = 0; j < bs; ++j)
{
urng.reseed(urng.unifU32());
auto def = urng.unifPM1() * 0.95;
auto rt = urng.unif01() * 6 - 2;

auto lfo = slfo_t(&sr, urng);
lfo.attack((slfo_t::Shape)s);
for (int i = 0; i < 500; ++i)
{
lfo.process_block(rt, def, (slfo_t::Shape)s);
for (int j = 0; j < bs; ++j)
{
REQUIRE(lfo.outputBlock[j] - 1.0 <= 5e-5);
REQUIRE(lfo.outputBlock[j] + 1.0 >= -5e-5);
}
}
// std::cout << lfo.outputBlock[j] << std::endl;
REQUIRE(lfo.outputBlock[j] - 1.0 <= 5e-5);
REQUIRE(lfo.outputBlock[j] + 1.0 >= -5e-5);
}
}
}
}
}
#endif

0 comments on commit 6424cad

Please sign in to comment.