Skip to content

Commit

Permalink
Nimbus Effect at very high SampleRates or small Blocks (#6902)
Browse files Browse the repository at this point in the history
Nimbus at very high sample rates or small blocks would have
the internal oversampler mis-align and glitch or in some
cases produce silence. I think this fixes it and also adds
an assertive regtest.

Addresses #6834
  • Loading branch information
baconpaul authored Apr 2, 2023
1 parent ea6e961 commit 3b988db
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 24 deletions.
53 changes: 31 additions & 22 deletions src/common/dsp/effects/NimbusEffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,38 +175,47 @@ void NimbusEffect::process(float *dataL, float *dataR)

if (frames_to_go > 0)
{
numStubs = frames_to_go;
for (int i = 0; i < numStubs; ++i)
int startSub = numStubs;
int addStub = frames_to_go;
numStubs += frames_to_go;

for (int i = 0; i < addStub; ++i)
{
stub_input[0][i] = resample_into[consume_ptr][0];
stub_input[1][i] = resample_into[consume_ptr][1];
stub_input[0][i + startSub] = resample_into[consume_ptr][0];
stub_input[1][i + startSub] = resample_into[consume_ptr][1];
consume_ptr++;
}
}

SRC_DATA odata;
odata.end_of_input = 0;
odata.src_ratio = processor_sr_inv * storage->samplerate;
odata.data_in = &(resample_this[0][0]);
odata.data_out = &(resample_into[0][0]);
odata.input_frames = outpos;
odata.output_frames = BLOCK_SIZE << 3;
auto reso = src_process(euroSR_to_surgeSR, &odata);
if (!builtBuffer)
created += odata.output_frames_gen;

size_t w = resampWritePtr;
for (int i = 0; i < odata.output_frames_gen; ++i)
if (outpos > 0)
{
resampled_output[w][0] = resample_into[i][0];
resampled_output[w][1] = resample_into[i][1];
SRC_DATA odata;
odata.end_of_input = 0;
odata.src_ratio = processor_sr_inv * storage->samplerate;
odata.data_in = &(resample_this[0][0]);
odata.data_out = &(resample_into[0][0]);
odata.input_frames = outpos;
odata.output_frames = BLOCK_SIZE << 3;
auto reso = src_process(euroSR_to_surgeSR, &odata);
if (!builtBuffer)
created += odata.output_frames_gen;

size_t w = resampWritePtr;
for (int i = 0; i < odata.output_frames_gen; ++i)
{
resampled_output[w][0] = resample_into[i][0];
resampled_output[w][1] = resample_into[i][1];

w = (w + 1U) & (raw_out_sz - 1U);
w = (w + 1U) & (raw_out_sz - 1U);
}
resampWritePtr = w;
}
resampWritePtr = w;
}

bool rpi = (created) > (BLOCK_SIZE + 8); // leave some buffer
// If you hit this you need to adjust this gapping ratio probably.
static_assert(BLOCK_SIZE >= nimbusprocess_blocksize);
int ratio = std::max((int)std::ceil(processor_sr_inv * storage->samplerate) - 2, 0);
bool rpi = (created) > (BLOCK_SIZE * (1 + ratio) + 8); // leave some buffer
if (rpi)
builtBuffer = true;

Expand Down
2 changes: 1 addition & 1 deletion src/common/dsp/effects/NimbusEffect.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class NimbusEffect : public Effect

SRC_STATE_tag *surgeSR_to_euroSR, *euroSR_to_surgeSR;

static constexpr int raw_out_sz = BLOCK_SIZE_OS << 4; // power of 2 pls
static constexpr int raw_out_sz = BLOCK_SIZE_OS << 5; // power of 2 pls
float resampled_output[raw_out_sz][2]; // at sr
size_t resampReadPtr = 0, resampWritePtr = 1; // see comment in init

Expand Down
52 changes: 51 additions & 1 deletion src/surge-testrunner/UnitTestsFX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -315,4 +315,54 @@ TEST_CASE("Waveshaper Pop", "[fx]")
}
}
}
}
}

TEST_CASE("High SampleRate Nimbus", "[fx]")
{
for (auto base : {44100, 48000})
{
for (int m = 1; m <= 8; m *= 2)
{
DYNAMIC_SECTION("High Sample Rate " + std::to_string(base) + " * " + std::to_string(m))
{
auto surge = Surge::Headless::createSurge(base * m);
REQUIRE(surge);

for (int i = 0; i < 100; ++i)
surge->process();

auto *pt = &(surge->storage.getPatch().fx[0].type);
auto awv = 1.f * fxt_nimbus / (pt->val_max.i - pt->val_min.i);

auto did = surge->idForParameter(pt);
surge->setParameter01(did, awv, false);
surge->process();

auto sp = [&](auto id, auto val) {
auto *pawt = &(surge->storage.getPatch().fx[0].p[id]);
auto did = surge->idForParameter(pawt);
surge->setParameter01(did, val, false);
};
sp(2, 0.5f); // position
sp(5, 0.75f); // density
sp(11, 1.f); // mix

surge->playNote(0, 60, 127, 0);
auto maxAmp = -10.f;
for (int i = 0; i < 5000 * m; ++i)
{
surge->process();

for (int s = 0; s < BLOCK_SIZE; ++s)
maxAmp = std::max(maxAmp, surge->output[0][s]);
}
REQUIRE(maxAmp > 0.1);
surge->releaseNote(0, 60, 0);
for (int i = 0; i < 500; ++i)
{
surge->process();
}
}
}
}
}

0 comments on commit 3b988db

Please sign in to comment.