Skip to content

Commit

Permalink
Move the LagCollection class to the active_set shared list (#180)
Browse files Browse the repository at this point in the history
  • Loading branch information
baconpaul authored Feb 20, 2025
1 parent e47d9e3 commit 0813146
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 42 deletions.
16 changes: 15 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ if (${SST_BASIC_BLOCKS_BUILD_TESTS})
)
endif ()

if (NOT TARGET sst-cpputils)
CPMAddPackage(NAME sst-cpputils
GITHUB_REPOSITORY surge-synthesizer/sst-cpputils
GIT_TAG main
)
endif()

add_executable(sst-basic-blocks-test
tests/smoketest.cpp
tests/dsp_tests.cpp
Expand Down Expand Up @@ -78,6 +85,13 @@ else()
if (NOT TARGET simde)
message(WARNING "SST Basic Blocks requires access to the 'simde' target from "
"https://github.com/simde-everywhere/simde. This build will only work on x86_64 architecture.")
target_compile_definitions(${PROJECT_NAME} SIMDE_UNAVAILABLE=1)
target_compile_definitions(${PROJECT_NAME} INTERFACE SIMDE_UNAVAILABLE=1)
endif()
endif ()

if (NOT TARGET sst-cpputils)
message(WARNING "SST Basic Blocks requires sst-cpputils for some features. Deactivating those")
target_compile_definitions(${PROJECT_NAME} INTERFACE SST_CPPUTILS_UNAVAILABLE=1)
else()
target_link_libraries(${PROJECT_NAME} INTERFACE sst-cpputils)
endif()
59 changes: 18 additions & 41 deletions include/sst/basic-blocks/dsp/LagCollection.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
#ifndef INCLUDE_SST_BASIC_BLOCKS_DSP_LAGCOLLECTION_H
#define INCLUDE_SST_BASIC_BLOCKS_DSP_LAGCOLLECTION_H

#if SST_CPPUTILS_UNAVAILABLE
#else

#include <cassert>
#include "sst/cpputils/active_set_overlay.h"
#include "Lag.h"

namespace sst::basic_blocks::dsp
Expand All @@ -40,7 +45,7 @@ namespace sst::basic_blocks::dsp
*/
template <int N> struct LagCollection
{
struct Lagger
struct Lagger : sst::cpputils::active_set_overlay<Lagger>::participant
{
void process()
{
Expand All @@ -62,12 +67,11 @@ template <int N> struct LagCollection
}
float *onto{nullptr};
LinearLag<float, true> lag;
Lagger *next{nullptr}, *prev{nullptr};
int index;
};

std::array<Lagger, N> lags;
Lagger *activeHead{nullptr};
sst::cpputils::active_set_overlay<Lagger> activeSet;

LagCollection()
{
Expand All @@ -78,64 +82,37 @@ template <int N> struct LagCollection
void setRateInMilliseconds(double rate, double sampleRate, double blockSizeInv)
{
for (auto &l : lags)
l.lag.setRateInMilliseconds(1000.0 * 64.0 / 48000.0, sampleRate, blockSizeInv);
l.lag.setRateInMilliseconds(1000.0 * rate / 48000.0, sampleRate, blockSizeInv);
}

void setTarget(size_t index, float target, float *onto)
{
assert(index < N);
lags[index].lag.setTarget(target);
lags[index].onto = onto;

if (lags[index].next == nullptr && lags[index].prev == nullptr &&
&lags[index] != activeHead)
{
lags[index].next = activeHead;
activeHead = &lags[index];
}
activeSet.addToActive(lags[index]);
}

void processAll()
{
auto curr = activeHead;
while (curr)
for (auto &lag : activeSet)
{
curr->process();
if (!curr->lag.isActive())
{
if (curr->next)
curr->next->prev = curr->prev;
if (curr->prev)
curr->prev->next = curr->next;
if (curr == activeHead)
activeHead = curr->next;

auto nv = curr->next;
curr->next = nullptr;
curr->prev = nullptr;
curr->onto = nullptr;
curr = nv;
}
else
{
curr = curr->next;
}
lag.process();
if (!lag.lag.isActive())
activeSet.removeFromActive(lag);
}
}

void snapAllActiveToTarget()
{
auto curr = activeHead;
while (curr)
for (auto &lag : activeSet)
{
curr->snapToTarget();
auto nv = curr->next;
curr->next = nullptr;
curr->prev = nullptr;
curr = nv;
lag.snapToTarget();
}
activeHead = nullptr;
while (activeSet.begin() != activeSet.end())
activeSet.removeFromActive(*activeSet.begin());
}
};
} // namespace sst::basic_blocks::dsp
#endif
#endif // LAGCOLLECTION_H
103 changes: 103 additions & 0 deletions tests/dsp_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "sst/basic-blocks/dsp/FastMath.h"
#include "sst/basic-blocks/dsp/Clippers.h"
#include "sst/basic-blocks/dsp/Lag.h"
#include "sst/basic-blocks/dsp/LagCollection.h"
#include "sst/basic-blocks/mechanics/block-ops.h"
#include "sst/basic-blocks/tables/SincTableProvider.h"
#include "sst/basic-blocks/dsp/SSESincDelayLine.h"
Expand Down Expand Up @@ -1256,4 +1257,106 @@ TEST_CASE("OscillatorSupportFunctions", "[dsp]")
us.panLaw(2, L, R);
REQUIRE(L > R);
}
}

TEST_CASE("Lag Collection", "[dsp]")
{
SECTION("Collection Instantiates")
{
sst::basic_blocks::dsp::LagCollection<16> lags;
lags.setRateInMilliseconds(100, 48000, 1.0 / 16);
lags.processAll();
}

SECTION("Collection Instantiates")
{
std::array<float, 16> vals{};
sst::basic_blocks::dsp::LagCollection<16> lags;
std::fill(vals.begin(), vals.end(), 0.f);
lags.setRateInMilliseconds(1000, 48000, 1.0 / 16);
lags.processAll();
lags.setTarget(5, 0.0, &vals[5]);
lags.processAll();
lags.setTarget(5, 1.0, &vals[5]);

auto pv5 = vals[5];
for (int i = 0; i < 3; ++i)
{
lags.processAll();
;
REQUIRE(vals[5] > pv5);
pv5 = vals[5];
}
REQUIRE(vals[5] < 1);
lags.snapAllActiveToTarget();
REQUIRE(vals[5] == 1);
REQUIRE(lags.activeSet.activeCount == 0);
}

SECTION("Lags get done eventually")
{
std::array<float, 16> vals{};
sst::basic_blocks::dsp::LagCollection<16> lags;
std::fill(vals.begin(), vals.end(), 0.f);
lags.setRateInMilliseconds(1000, 48000, 1.0 / 16);
lags.setTarget(5, 0.0, &vals[5]);
lags.processAll();
lags.setTarget(5, 1.0, &vals[5]);
int its{0}, maxIts{10000};
while (its < maxIts && lags.activeSet.activeCount > 0)
{
lags.processAll();
its++;
}
REQUIRE(its != maxIts);
}

SECTION("Fire on Single Targets")
{
std::array<float, 16> vals{};
sst::basic_blocks::dsp::LagCollection<16> lags;
std::fill(vals.begin(), vals.end(), 0.f);
lags.setRateInMilliseconds(1000, 48000, 1.0 / 16);

float lv{0.f};
for (int i = 0; i < 100; ++i)
{
lv = (float)rand() / (float)RAND_MAX;
lags.setTarget(7, lv, &vals[7]);
lags.processAll();
;
}
REQUIRE(vals[7] != lv);
lags.snapAllActiveToTarget();
REQUIRE(vals[7] == lv);
REQUIRE(lags.activeSet.activeCount == 0);
}

SECTION("Fire on Two Targes")
{
std::array<float, 16> vals{};
sst::basic_blocks::dsp::LagCollection<16> lags;
std::fill(vals.begin(), vals.end(), 0.f);
lags.setRateInMilliseconds(1000, 48000, 1.0 / 16);

float lv{0.f};
for (int i = 0; i < 100; ++i)
{
lv = (float)rand() / (float)RAND_MAX;
lags.setTarget(7, lv, &vals[7]);
if (i % 14 == 0)
{
lags.setTarget(3, 0.7 + lv * 0.72, &vals[3]);
}
lags.processAll();
if (i < 14)
REQUIRE(lags.activeSet.activeCount == 1);
else
REQUIRE(lags.activeSet.activeCount == 2);
}
REQUIRE(vals[7] != lv);
lags.snapAllActiveToTarget();
REQUIRE(vals[7] == lv);
REQUIRE(lags.activeSet.activeCount == 0);
}
}

0 comments on commit 0813146

Please sign in to comment.