Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bring the NimbusEffect into sst-effects #121

Merged
merged 4 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading