Skip to content

Commit

Permalink
Bring the NimbusEffect into sst-effects (#121)
Browse files Browse the repository at this point in the history
This ports the nimbus effect from Surge from a DSP and
params perspective, but doesn't impement the dynamic param
names by engine type feature yet. That's a separate commit
in the future once I figure out how I want to do that.
  • Loading branch information
baconpaul authored Jul 26, 2024
1 parent 04a329e commit 903ad07
Show file tree
Hide file tree
Showing 6 changed files with 486 additions and 5 deletions.
18 changes: 17 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ set(CMAKE_CXX_STANDARD 17)

add_library(${PROJECT_NAME} INTERFACE)
target_include_directories(${PROJECT_NAME} INTERFACE include)
# there's a bit more below this

if (${SST_EFFECTS_BUILD_TESTS})
include(cmake/CPM.cmake)
Expand Down Expand Up @@ -37,11 +38,18 @@ if (${SST_EFFECTS_BUILD_TESTS})
)
endif ()

if (NOT TARGET eurorack)
CPMAddPackage(NAME eurorack
GITHUB_REPOSITORY surge-synthesizer/eurorack
GIT_TAG surge
)
endif ()

if (NOT TARGET simde)
CPMAddPackage(NAME simde
GITHUB_REPOSITORY simd-everywhere/simde
VERSION 0.7.2
)
)
add_library(simde INTERFACE)
target_include_directories(simde INTERFACE ${simde_SOURCE_DIR})
endif ()
Expand All @@ -67,3 +75,11 @@ if (${SST_EFFECTS_BUILD_TESTS})
target_compile_definitions(${PROJECT_NAME}-test PRIVATE CATCH_CONFIG_DISABLE_EXCEPTIONS=1)

endif ()


if (TARGET eurorack)
target_link_libraries(${PROJECT_NAME} INTERFACE eurorack)
target_compile_definitions(${PROJECT_NAME} INTERFACE SST_EFFECTS_EURORACK=1)
else()
message(STATUS "sst-effects built without eurorack library; Nimbus effect is no-op")
endif()
5 changes: 1 addition & 4 deletions include/sst/effects/EffectCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,7 @@ template <typename FXConfig> struct EffectTemplateBase : public FXConfig::BaseCl
}
}

inline float intValue(int idx) const
{
return FXConfig::intValueAt(asBase(), valueStorage, idx);
}
inline int intValue(int idx) const { return FXConfig::intValueAt(asBase(), valueStorage, idx); }

inline float temposyncRatio(int idx) const
{
Expand Down
210 changes: 210 additions & 0 deletions include/sst/effects/Nimbus.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
/*
* sst-effects - an open source library of audio effects
* built by Surge Synth Team.
*
* Copyright 2018-2023, various authors, as described in the GitHub
* transaction log.
*
* sst-effects is released under the GNU General Public Licence v3
* or later (GPL-3.0-or-later). The license is found in the "LICENSE"
* file in the root of this repository, or at
* https://www.gnu.org/licenses/gpl-3.0.en.html
*
* The majority of these effects at initiation were factored from
* Surge XT, and so git history prior to April 2023 is found in the
* surge repo, https://github.com/surge-synthesizer/surge
*
* All source in sst-effects available at
* https://github.com/surge-synthesizer/sst-effects
*/

#ifndef INCLUDE_SST_EFFECTS_NIMBUS_H
#define INCLUDE_SST_EFFECTS_NIMBUS_H

#include <cstring>
#include "EffectCore.h"
#include "sst/basic-blocks/params/ParamMetadata.h"
#include "sst/basic-blocks/dsp/Lag.h"
#include "sst/basic-blocks/dsp/BlockInterpolators.h"
#include "sst/basic-blocks/dsp/LanczosResampler.h"
#include "sst/basic-blocks/mechanics/block-ops.h"
#include "sst/basic-blocks/mechanics/simd-ops.h"

/*
* Unlike other effects, Numbus is split into Nimbus and NimbusImpl.h to allow
* inclusion of this in header files without pulling in the eurorack entire core
* into your header as oppposed to TU space
*
* For you, using NimbusImpl may be fine, or you may want to mix and match and
* do an explicit instantiation or so on. If you are reading this and you don't
* know what to do, "just include NimbusImpl.h". And if you do know what to do,
* then do that!
*/

namespace clouds
{
class GranularProcessor;
}

namespace sst::effects::nimbus
{
#if !SST_EFFECTS_EURORACK
template <typename FXConfig> struct Nimbus : core::EffectTemplateBase<FXConfig>
{
Nimbus(typename FXConfig::GlobalStorage *s, typename FXConfig::EffectStorage *e,
typename FXConfig::ValueStorage *p)
: core::EffectTemplateBase<FXConfig>(s, e, p)
{
std::cerr << "Warning: Using nimbus without eurorack module" << std::endl;
}

static constexpr const char *effectName{"nimbus"};
static constexpr int numParams{0};
void initialize() {}
void processBlock(float *__restrict, float *__restrict) {}
void suspendProcessing() {}
int getRingoutDecay() const { return -1; }
sst::basic_blocks::params::ParamMetaData paramAt(int i) const { return {}; }
void onSampleRateChanged() {}
};
#else
namespace sdsp = sst::basic_blocks::dsp;
namespace mech = sst::basic_blocks::mechanics;

template <typename FXConfig> struct Nimbus : core::EffectTemplateBase<FXConfig>
{
enum nmb_params
{
nmb_mode,
nmb_quality,

nmb_position,
nmb_size,
nmb_pitch,
nmb_density,
nmb_texture,
nmb_spread,

nmb_freeze,
nmb_feedback,

nmb_reverb,
nmb_mix,

nmb_num_params,
};
static constexpr int numParams{nmb_num_params};
static constexpr const char *effectName{"nimbus"};

Nimbus(typename FXConfig::GlobalStorage *s, typename FXConfig::EffectStorage *e,
typename FXConfig::ValueStorage *p);
~Nimbus();

void initialize();
void processBlock(float *__restrict L, float *__restrict R);

void suspendProcessing() { initialize(); }
int getRingoutDecay() const { return -1; }
void onSampleRateChanged() { initialize(); }

basic_blocks::params::ParamMetaData paramAt(int idx) const
{
auto np = (nmb_params)idx;
using pmd = sst::basic_blocks::params::ParamMetaData;
switch (np)
{
case nmb_mode:
return pmd()
.asInt()
#if EURORACK_CLOUDS_IS_SUPERPARASITES
.withRange(0, 7)
#else
.withRange(0, 3)
#endif
.withName("Mode")
.withDefault(0)
.withUnorderedMapFormatting({{0, "Granularizer"},
{1, "Pitch Shifter"},
{2, "Looping Delay"},
{3, "Spectral Madness"},
{4, "Oliverb"},
{5, "Reonestor"},
{6, "Kammerl"},
{7, "Spectral Cloud"}});
// TODO: Make this also marked as param-invalidating and use conditions for names
case nmb_quality:
return pmd()
.asInt()
.withRange(0, 3)
.withName("Quality")
.withDefault(0)
.withUnorderedMapFormatting({{0, "32k 16-bit Stereo"},
{1, "32k 16-bit Mono"},
{2, "16k 8-bit Stereo"},
{3, "16k 8-bit Mono"}});
case nmb_position:
return pmd().asPercent().withName("Position").withDefault(0.f);
case nmb_size:
return pmd().asPercentBipolar().withName("Size").withDefault(0.f);
case nmb_pitch:
return pmd().asSemitoneRange(-48, 48).withDefault(0.f).withName("Pitch");
case nmb_density:
return pmd().asPercentBipolar().withName("Density").withDefault(0.f);
case nmb_texture:
return pmd().asPercentBipolar().withName("Texture").withDefault(0.f);
case nmb_spread:
return pmd().asPercent().withName("Spread").withDefault(0.f);
case nmb_freeze:
// TODO: On/Off FOrmatting around 0.5 here
return pmd()
.asFloat()
.withRange(0.f, 1.f)
.withDefault(0.f)
.withName("Freeze")
.withLinearScaleFormatting("");
case nmb_feedback:
return pmd().asPercent().withName("Feedback").withDefault(0.f);
case nmb_reverb:
return pmd().asPercent().withName("Reverb").withDefault(0.f);
case nmb_mix:
return pmd().asPercent().withName("Mix").withDefault(0.f);
case nmb_num_params:
break;
}
return {};
}

// Only used by rack
void setNimbusTrigger(bool b) { nimbusTrigger = b; }

protected:
float L alignas(16)[FXConfig::blockSize], R alignas(16)[FXConfig::blockSize];

sdsp::lipol_sse<FXConfig::blockSize, false> mix;

uint8_t *block_mem, *block_ccm;
clouds::GranularProcessor *processor;
static constexpr int processor_sr = 32000;
static constexpr float processor_sr_inv = 1.f / 32000;
int old_nmb_mode = 0;
bool nimbusTrigger{false};

using resamp_t = sst::basic_blocks::dsp::LanczosResampler<FXConfig::blockSize>;
std::unique_ptr<resamp_t> surgeSR_to_euroSR, euroSR_to_surgeSR;

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

static constexpr int nimbusprocess_blocksize = 8;
float stub_input[2][nimbusprocess_blocksize]; // This is the extra sample we have around
size_t numStubs{0};
int consumed = 0, created = 0;
bool builtBuffer{false};
};

#endif

} // namespace sst::effects::nimbus

#endif
Loading

0 comments on commit 903ad07

Please sign in to comment.