diff --git a/CMakeLists.txt b/CMakeLists.txt index 778161e..c3f088c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.10) project(sst-effects VERSION 0.5 LANGUAGES C CXX) -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 17) add_library(${PROJECT_NAME} INTERFACE) target_include_directories(${PROJECT_NAME} INTERFACE include) diff --git a/include/sst/effects/ConcreteConfig.h b/include/sst/effects/ConcreteConfig.h index 4c314f8..cc21a49 100644 --- a/include/sst/effects/ConcreteConfig.h +++ b/include/sst/effects/ConcreteConfig.h @@ -48,10 +48,6 @@ struct ConcreteConfig // It is painful that sst-filters makes us over-adapt // this class GS(double sr) : sampleRate(sr) {} - - float noteToPitch(float f) { return 0; } - float dbToLinear(float f) { return 1.f; } - float getSampleRateInv() { return 1.0 / sampleRate; } }; struct ES { diff --git a/include/sst/effects/EffectCore.h b/include/sst/effects/EffectCore.h index 3a2e944..3d86ab7 100644 --- a/include/sst/effects/EffectCore.h +++ b/include/sst/effects/EffectCore.h @@ -138,7 +138,8 @@ template struct EffectTemplateBase : public FXConfig::BaseCl static constexpr float blockSize_quad{FXConfig::blockSize >> 2}; using BiquadFilterType = - sst::filters::Biquad::BiquadFilter; + sst::filters::Biquad::BiquadFilter; inline typename FXConfig::BaseClass *asBase() { diff --git a/include/sst/voice-effects/VoiceEffectCore.h b/include/sst/voice-effects/VoiceEffectCore.h index e44ed5f..9d18bc0 100644 --- a/include/sst/voice-effects/VoiceEffectCore.h +++ b/include/sst/voice-effects/VoiceEffectCore.h @@ -26,86 +26,23 @@ * */ -#include -#include - // For shared width calculations #include "sst/basic-blocks/dsp/MidSide.h" #include "sst/basic-blocks/dsp/BlockInterpolators.h" - -#include "sst/basic-blocks/concepts/concepts.h" - #include "sst/filters/BiquadFilter.h" #include namespace sst::voice_effects::core { - -template -concept baseClassAPI = - requires(typename VFXConfig::BaseClass *b, size_t st, float f, int i, uint8_t *u8) { - { - VFXConfig::setFloatParam(b, st, f) - } -> std::convertible_to; - { - VFXConfig::getFloatParam(b, st) - } -> std::floating_point; - - { - VFXConfig::setIntParam(b, st, i) - } -> std::convertible_to; - { - VFXConfig::getIntParam(b, st) - } -> std::integral; - - { - VFXConfig::dbToLinear(b, f) - } -> std::floating_point; - - { - VFXConfig::equalNoteToPitch(b, f) - } -> std::floating_point; - - { - VFXConfig::getSampleRate(b) - } -> std::floating_point; - { - VFXConfig::getSampleRateInv(b) - } -> std::floating_point; - - { - VFXConfig::preReservePool(b, st) - } -> std::convertible_to; - { - VFXConfig::checkoutBlock(b, st) - } -> std::same_as; - { - VFXConfig::returnBlock(b, u8, st) - } -> std::convertible_to; - }; - -template -concept optionalOversampling = std::is_integral_v; - -template -concept optionalEnvelopeTime = requires(typename VFXConfig::BaseClass *b, float f) { - { - VFXConfig::envelope_rate_linear_nowrap(b, f) - } -> std::convertible_to; -}; - -template -concept supportsVoiceConfig = - std::is_class_v && - sst::basic_blocks::concepts::is_power_of_two_ge(VFXConfig::blockSize, (size_t)4) && - baseClassAPI; - // Todo: as we port consider this FXConfig::BaseClass being a bit more configurable. -template - requires(supportsVoiceConfig) -struct VoiceEffectTemplateBase : public VFXConfig::BaseClass +template struct VoiceEffectTemplateBase : public VFXConfig::BaseClass { + static_assert(std::is_integral::value); + static_assert(!(VFXConfig::blockSize & (VFXConfig::blockSize - 1))); // 2^n + static_assert(VFXConfig::blockSize >= 4); // > simd register length + static_assert(std::is_class::value); + typename VFXConfig::BaseClass *asBase() { return static_cast(this); @@ -116,21 +53,58 @@ struct VoiceEffectTemplateBase : public VFXConfig::BaseClass return static_cast(this); } + /* + * Params + */ + static_assert(std::is_same(), + std::declval(), std::declval())), + void>::value, + "Implement setFloatParam"); + + static_assert( + std::is_same(), std::declval())), + float>::value, + "Implement getFloatParam"); + void setFloatParam(int i, float f) { VFXConfig::setFloatParam(asBase(), i, f); } float getFloatParam(int i) const { return VFXConfig::getFloatParam(asBase(), i); } + static_assert(std::is_same(), + std::declval(), std::declval())), + void>::value, + "Implement setIntParam"); + + static_assert( + std::is_same(), std::declval())), + int>::value, + "Implement getIntParam"); + void setIntParam(int i, int f) { VFXConfig::setIntParam(asBase(), i, f); } int getIntParam(int i) const { return VFXConfig::getIntParam(asBase(), i); } /* * Conversions */ + static_assert( + std::is_same(), + std::declval())), + float>::value, + "Implement dbToLinear"); float dbToLinear(float f) { return VFXConfig::dbToLinear(asBase(), f); } static float dbToLinear(typename VFXConfig::BaseClass *b, float f) { return VFXConfig::dbToLinear(b, f); } + static_assert( + std::is_same(), std::declval())), + float>::value, + "Implement getEqualNoteToPitch"); float equalNoteToPitch(float f) { return VFXConfig::equalNoteToPitch(asBase(), f); } float note_to_pitch_ignoring_tuning(float f) { return equalNoteToPitch(f); } static float noteToPitchIgnoringTuning(VoiceEffectTemplateBase *that, float f) @@ -142,12 +116,38 @@ struct VoiceEffectTemplateBase : public VFXConfig::BaseClass return that->getSampleRateInv(); } + static_assert(std::is_same())), + float>::value, + "Implement getSampleRate"); float getSampleRate() { return VFXConfig::getSampleRate(asBase()); } + static_assert(std::is_same())), + float>::value, + "Implement getSampleRateInv"); float getSampleRateInv() { return VFXConfig::getSampleRateInv(asBase()); } /* * We have to provide a memory pool */ + static_assert( + std::is_same(), std::declval())), + void>::value, + "void preReservePool(base*, size_t) not defined"); + + static_assert( + std::is_same(), std::declval())), + uint8_t *>::value, + "uint8_t* checkoutBlock(base *,size_t) not defined"); + + static_assert(std::is_same(), + std::declval(), std::declval())), + void>::value, + "void returnBlock(base *, uint8_t *, size_t) not defined"); + void preReservePool(size_t s) { VFXConfig::preReservePool(asBase(), s); } uint8_t *checkoutBlock(size_t s) { return VFXConfig::checkoutBlock(asBase(), s); } void returnBlock(uint8_t *d, size_t s) { VFXConfig::returnBlock(asBase(), d, s); } @@ -167,9 +167,18 @@ struct VoiceEffectTemplateBase : public VFXConfig::BaseClass } } + template struct has_oversampling : std::false_type + { + }; + + template + struct has_oversampling> : std::true_type + { + }; + constexpr int16_t getOversamplingRatio() { - if constexpr (optionalOversampling) + if constexpr (has_oversampling::value) { return VFXConfig::oversamplingRatio; } @@ -179,20 +188,9 @@ struct VoiceEffectTemplateBase : public VFXConfig::BaseClass } } - float envelope_rate_linear_nowrap(float f) - { - if constexpr (optionalEnvelopeTime) - { - return VFXConfig::envelope_rate_linear_nowrap(this, f); - } - else - { - return VFXConfig::blockSize * VFXConfig::getSampleRateInv(this) * std::pow(2, -f); - } - } - - using BiquadFilterType = sst::filters::Biquad::BiquadFilter, - VFXConfig::blockSize>; + using BiquadFilterType = + sst::filters::Biquad::BiquadFilter, VFXConfig::blockSize, + VoiceEffectTemplateBase>; }; } // namespace sst::voice_effects::core diff --git a/include/sst/voice-effects/modulation/Phaser.h b/include/sst/voice-effects/modulation/Phaser.h index 929a332..c3670f2 100644 --- a/include/sst/voice-effects/modulation/Phaser.h +++ b/include/sst/voice-effects/modulation/Phaser.h @@ -36,8 +36,6 @@ namespace sst::voice_effects::modulation { template struct Phaser : core::VoiceEffectTemplateBase { - using base_t = core::VoiceEffectTemplateBase; - static constexpr const char *effectName{"Phaser"}; static constexpr int numFloatParams{6}; @@ -143,7 +141,7 @@ template struct Phaser : core::VoiceEffectTemplateBase; + using lfo_t = sst::basic_blocks::modulators::SimpleLFO; lfo_t actualLFO{this, 1}; typename lfo_t::Shape lfoShape = lfo_t::Shape::SINE; diff --git a/include/sst/voice-effects/modulation/Tremolo.h b/include/sst/voice-effects/modulation/Tremolo.h index d388aab..0c1bdb7 100644 --- a/include/sst/voice-effects/modulation/Tremolo.h +++ b/include/sst/voice-effects/modulation/Tremolo.h @@ -132,9 +132,14 @@ template struct Tremolo : core::VoiceEffectTemplateBaseinitToParamMetadataDefault(this); } + // This ain't in VFXConfig so put it here (the simpleLFO needs it): + float envelope_rate_linear_nowrap(float f) + { + return VFXConfig::blockSize * VFXConfig::getSampleRateInv(this) * std::pow(2, -f); + } + // Ok, so let's introduce the simpleLFO with an alias. - using lfo_t = sst::basic_blocks::modulators::SimpleLFO, - VFXConfig::blockSize>; + using lfo_t = sst::basic_blocks::modulators::SimpleLFO; // create one... lfo_t actualLFO{this, 1}; // ...set up a variable for lfo shape. diff --git a/tests/create-effect.cpp b/tests/create-effect.cpp index 98e82ec..54898e6 100644 --- a/tests/create-effect.cpp +++ b/tests/create-effect.cpp @@ -38,9 +38,6 @@ struct TestConfig struct GS { - float noteToPitch(float f) { return 0; } - float dbToLinear(float f) { return 1.f; } - float getSampleRateInv() { return 1.0 / 48000; } }; struct ES { diff --git a/tests/create-voice-effect.cpp b/tests/create-voice-effect.cpp index b1f2da4..a1392ac 100644 --- a/tests/create-voice-effect.cpp +++ b/tests/create-voice-effect.cpp @@ -51,7 +51,7 @@ struct VTestConfig std::array fb{}; std::array ib{}; }; - static constexpr size_t blockSize{16}; + static constexpr int blockSize{16}; static void setFloatParam(BaseClass *b, int i, float f) { b->fb[i] = f; } static float getFloatParam(const BaseClass *b, int i) { return b->fb[i]; } @@ -159,6 +159,12 @@ TEST_CASE("Can Create Voice FX") { VTester>::TestVFX(); } - SECTION("Tremolo") { VTester>::TestVFX(); } - SECTION("Phaser") { VTester>::TestVFX(); } + SECTION("Tremolo") + { + VTester>::TestVFX(); + } + SECTION("Phaser") + { + VTester>::TestVFX(); + } } diff --git a/tests/sfinae-test.cpp b/tests/sfinae-test.cpp index 8aea787..25ff104 100644 --- a/tests/sfinae-test.cpp +++ b/tests/sfinae-test.cpp @@ -34,9 +34,6 @@ struct NoExtraConfig struct GS { - float noteToPitch(float f) { return 0; } - float dbToLinear(float f) { return 1.f; } - float getSampleRateInv() { return 1.0 / 48000; } }; struct ES {