diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 47ecdb0f..9d93e047 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,6 +24,7 @@ set (SFIZZ_SOURCES sfizz/effects/Eq.cpp sfizz/effects/Apan.cpp sfizz/effects/Lofi.cpp + sfizz/effects/Limiter.cpp ) include (SfizzSIMDSourceFiles) diff --git a/src/sfizz/Effects.cpp b/src/sfizz/Effects.cpp index 3398cc3d..78ec6c96 100644 --- a/src/sfizz/Effects.cpp +++ b/src/sfizz/Effects.cpp @@ -14,6 +14,7 @@ #include "effects/Eq.h" #include "effects/Apan.h" #include "effects/Lofi.h" +#include "effects/Limiter.h" #include namespace sfz { @@ -25,6 +26,7 @@ void EffectFactory::registerStandardEffectTypes() registerEffectType("eq", fx::Eq::makeInstance); registerEffectType("apan", fx::Apan::makeInstance); registerEffectType("lofi", fx::Lofi::makeInstance); + registerEffectType("limiter", fx::Limiter::makeInstance); } void EffectFactory::registerEffectType(absl::string_view name, Effect::MakeInstance& make) diff --git a/src/sfizz/effects/Limiter.cpp b/src/sfizz/effects/Limiter.cpp new file mode 100644 index 00000000..577fcba5 --- /dev/null +++ b/src/sfizz/effects/Limiter.cpp @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +/* + Note(jpc): implementation status + + Complete (no opcodes) +*/ + +#include "Limiter.h" +#include "Opcode.h" +#include "gen/limiter.cpp" +#include "absl/memory/memory.h" + +namespace sfz { +namespace fx { + + Limiter::Limiter() + : _limiter(new faustLimiter) + { + _limiter->instanceResetUserInterface(); + } + + Limiter::~Limiter() + { + } + + void Limiter::setSampleRate(double sampleRate) + { + _limiter->classInit(sampleRate); + _limiter->instanceConstants(sampleRate); + clear(); + } + + void Limiter::setSamplesPerBlock(int samplesPerBlock) + { + (void)samplesPerBlock; + } + + void Limiter::clear() + { + _limiter->instanceClear(); + } + + void Limiter::process(const float* const inputs[], float* const outputs[], unsigned nframes) + { + _limiter->compute(nframes, inputs, outputs); + } + + std::unique_ptr Limiter::makeInstance(absl::Span members) + { + auto fx = absl::make_unique(); + + for (const Opcode& opc : members) { + // no opcodes + (void)opc; + } + + return CXX11_MOVE(fx); + } + +} // namespace fx +} // namespace sfz diff --git a/src/sfizz/effects/Limiter.h b/src/sfizz/effects/Limiter.h new file mode 100644 index 00000000..49cb264a --- /dev/null +++ b/src/sfizz/effects/Limiter.h @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#pragma once +#include "Effects.h" +class faustLimiter; + +namespace sfz { +namespace fx { + + /** + * @brief Limiter effect + */ + class Limiter : public Effect { + public: + Limiter(); + ~Limiter(); + + /** + * @brief Initializes with the given sample rate. + */ + void setSampleRate(double sampleRate) override; + + /** + * @brief Sets the maximum number of frames to render at a time. The actual + * value can be lower but should never be higher. + */ + void setSamplesPerBlock(int samplesPerBlock) override; + + /** + * @brief Reset the state to initial. + */ + void clear() override; + + /** + * @brief Computes a cycle of the effect in stereo. + */ + void process(const float* const inputs[], float* const outputs[], unsigned nframes) override; + + /** + * @brief Instantiates given the contents of the block. + */ + static std::unique_ptr makeInstance(absl::Span members); + + private: + std::unique_ptr _limiter; + }; + +} // namespace fx +} // namespace sfz diff --git a/src/sfizz/effects/dsp/limiter.dsp b/src/sfizz/effects/dsp/limiter.dsp new file mode 100644 index 00000000..e8ed03d9 --- /dev/null +++ b/src/sfizz/effects/dsp/limiter.dsp @@ -0,0 +1,10 @@ +import("stdfaust.lib"); + +limiter(x) = gain*x with { + att = 0.0008; + rel = 0.5; + peak = x : an.amp_follower_ud(att, rel); + gain = ba.if(peak>1.0, 1.0/peak, 1.0) : si.smooth(ba.tau2pole(0.5*att)); +}; + +process = limiter, limiter; diff --git a/src/sfizz/effects/gen/limiter.cpp b/src/sfizz/effects/gen/limiter.cpp new file mode 100644 index 00000000..7a3d99db --- /dev/null +++ b/src/sfizz/effects/gen/limiter.cpp @@ -0,0 +1,135 @@ +/* ------------------------------------------------------------ +name: "limiter" +Code generated with Faust 2.20.2 (https://faust.grame.fr) +Compilation options: -lang cpp -inpl -scal -ftz 0 +------------------------------------------------------------ */ + +#ifndef __faustLimiter_H__ +#define __faustLimiter_H__ + +#ifndef FAUSTFLOAT +#define FAUSTFLOAT float +#endif + +#include +#include +#include + +#ifndef FAUSTCLASS +#define FAUSTCLASS faustLimiter +#endif + +#ifdef __APPLE__ +#define exp10f __exp10f +#define exp10 __exp10 +#endif + +class faustLimiter { + +private: + int fSampleRate; + float fConst0; + float fConst1; + float fConst2; + float fConst3; + float fConst4; + float fConst5; + float fConst6; + float fRec2[2]; + float fRec1[2]; + float fRec0[2]; + float fRec5[2]; + float fRec4[2]; + float fRec3[2]; + +public: + static void classInit(int sample_rate) + { + (void)sample_rate; + } + + void instanceConstants(int sample_rate) + { + fSampleRate = sample_rate; + fConst0 = std::min(192000.0f, std::max(1.0f, float(fSampleRate))); + fConst1 = std::exp((0.0f - (2500.0f / fConst0))); + fConst2 = (1.0f - fConst1); + fConst3 = std::exp((0.0f - (1250.0f / fConst0))); + fConst4 = (1.0f - fConst3); + fConst5 = std::exp((0.0f - (2.0f / fConst0))); + fConst6 = (1.0f - fConst5); + } + + void instanceResetUserInterface() + { + } + + void instanceClear() + { + for (int l0 = 0; (l0 < 2); l0 = (l0 + 1)) { + fRec2[l0] = 0.0f; + } + for (int l1 = 0; (l1 < 2); l1 = (l1 + 1)) { + fRec1[l1] = 0.0f; + } + for (int l2 = 0; (l2 < 2); l2 = (l2 + 1)) { + fRec0[l2] = 0.0f; + } + for (int l3 = 0; (l3 < 2); l3 = (l3 + 1)) { + fRec5[l3] = 0.0f; + } + for (int l4 = 0; (l4 < 2); l4 = (l4 + 1)) { + fRec4[l4] = 0.0f; + } + for (int l5 = 0; (l5 < 2); l5 = (l5 + 1)) { + fRec3[l5] = 0.0f; + } + } + + void init(int sample_rate) + { + classInit(sample_rate); + instanceInit(sample_rate); + } + void instanceInit(int sample_rate) + { + instanceConstants(sample_rate); + instanceResetUserInterface(); + instanceClear(); + } + + int getSampleRate() + { + return fSampleRate; + } + + void compute(int count, const FAUSTFLOAT* const* inputs, FAUSTFLOAT* const* outputs) + { + const FAUSTFLOAT* input0 = inputs[0]; + const FAUSTFLOAT* input1 = inputs[1]; + FAUSTFLOAT* output0 = outputs[0]; + FAUSTFLOAT* output1 = outputs[1]; + for (int i = 0; (i < count); i = (i + 1)) { + float fTemp0 = float(input0[i]); + float fTemp1 = float(input1[i]); + float fTemp2 = std::fabs(fTemp0); + fRec2[0] = std::max(fTemp2, ((fConst5 * fRec2[1]) + (fConst6 * fTemp2))); + fRec1[0] = ((fConst3 * fRec1[1]) + (fConst4 * fRec2[0])); + fRec0[0] = ((fConst1 * fRec0[1]) + (fConst2 * ((fRec1[0] > 1.0f) ? (1.0f / fRec1[0]) : 1.0f))); + output0[i] = FAUSTFLOAT((fTemp0 * fRec0[0])); + float fTemp3 = std::fabs(fTemp1); + fRec5[0] = std::max(fTemp3, ((fConst5 * fRec5[1]) + (fConst6 * fTemp3))); + fRec4[0] = ((fConst3 * fRec4[1]) + (fConst4 * fRec5[0])); + fRec3[0] = ((fConst1 * fRec3[1]) + (fConst2 * ((fRec4[0] > 1.0f) ? (1.0f / fRec4[0]) : 1.0f))); + output1[i] = FAUSTFLOAT((fTemp1 * fRec3[0])); + fRec2[1] = fRec2[0]; + fRec1[1] = fRec1[0]; + fRec0[1] = fRec0[0]; + fRec5[1] = fRec5[0]; + fRec4[1] = fRec4[0]; + fRec3[1] = fRec3[0]; + } + } +}; + +#endif