From bdd2d32621246921337efcf88194d4959dc23599 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 18 Mar 2020 15:29:01 +0100 Subject: [PATCH 1/6] Add limiter --- src/CMakeLists.txt | 1 + src/sfizz/Effects.cpp | 2 + src/sfizz/effects/Limiter.cpp | 66 +++++++++++++++ src/sfizz/effects/Limiter.h | 53 ++++++++++++ src/sfizz/effects/dsp/limiter.dsp | 10 +++ src/sfizz/effects/gen/limiter.cpp | 135 ++++++++++++++++++++++++++++++ 6 files changed, 267 insertions(+) create mode 100644 src/sfizz/effects/Limiter.cpp create mode 100644 src/sfizz/effects/Limiter.h create mode 100644 src/sfizz/effects/dsp/limiter.dsp create mode 100644 src/sfizz/effects/gen/limiter.cpp 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 From 5cd5db877abcaa837a0e95dca294456dc13659c9 Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 18 Mar 2020 01:53:47 +0100 Subject: [PATCH 2/6] Add the rectifier --- src/CMakeLists.txt | 1 + src/sfizz/Effects.cpp | 4 ++ src/sfizz/effects/Rectify.cpp | 106 ++++++++++++++++++++++++++++++++++ src/sfizz/effects/Rectify.h | 58 +++++++++++++++++++ 4 files changed, 169 insertions(+) create mode 100644 src/sfizz/effects/Rectify.cpp create mode 100644 src/sfizz/effects/Rectify.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9d93e047..2fcaeb88 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -25,6 +25,7 @@ set (SFIZZ_SOURCES sfizz/effects/Apan.cpp sfizz/effects/Lofi.cpp sfizz/effects/Limiter.cpp + sfizz/effects/Rectify.cpp ) include (SfizzSIMDSourceFiles) diff --git a/src/sfizz/Effects.cpp b/src/sfizz/Effects.cpp index 78ec6c96..eb6625f3 100644 --- a/src/sfizz/Effects.cpp +++ b/src/sfizz/Effects.cpp @@ -15,6 +15,7 @@ #include "effects/Apan.h" #include "effects/Lofi.h" #include "effects/Limiter.h" +#include "effects/Rectify.h" #include namespace sfz { @@ -27,6 +28,9 @@ void EffectFactory::registerStandardEffectTypes() registerEffectType("apan", fx::Apan::makeInstance); registerEffectType("lofi", fx::Lofi::makeInstance); registerEffectType("limiter", fx::Limiter::makeInstance); + + // extensions (book) + registerEffectType("rectify", fx::Rectify::makeInstance); } void EffectFactory::registerEffectType(absl::string_view name, Effect::MakeInstance& make) diff --git a/src/sfizz/effects/Rectify.cpp b/src/sfizz/effects/Rectify.cpp new file mode 100644 index 00000000..015e5d29 --- /dev/null +++ b/src/sfizz/effects/Rectify.cpp @@ -0,0 +1,106 @@ +// 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): this effect is book-only, mentioned but not documented + + Note(jpc): implementation status + +- [x] rectify_mode +- [x] rectify +- [ ] rectify_oncc + */ + +#include "Rectify.h" +#include "Opcode.h" + +namespace sfz { +namespace fx { + + Rectify::Rectify() + { + } + + void Rectify::setSampleRate(double sampleRate) + { + (void)sampleRate; + + static constexpr double coefs2x[12] = { 0.036681502163648017, 0.13654762463195794, 0.27463175937945444, 0.42313861743656711, 0.56109869787919531, 0.67754004997416184, 0.76974183386322703, 0.83988962484963892, 0.89226081800387902, 0.9315419599631839, 0.96209454837808417, 0.98781637073289585 }; + + for (unsigned c = 0; c < EffectChannels; ++c) { + _downsampler2x[c].set_coefs(coefs2x); + _upsampler2x[c].set_coefs(coefs2x); + } + } + + void Rectify::setSamplesPerBlock(int samplesPerBlock) + { + _tempBuffer.resize(samplesPerBlock); + } + + void Rectify::clear() + { + for (unsigned c = 0; c < EffectChannels; ++c) { + _downsampler2x[c].clear_buffers(); + _upsampler2x[c].clear_buffers(); + } + } + + void Rectify::process(const float* const inputs[], float* const outputs[], unsigned nframes) + { + // Note(jpc) I define opcode `rectify` to be a mix amount. + // half-wave rectification is achieved simply by halving it. + const float baseAmount = _amount * (_full ? 1.0f : 0.5f); + + absl::Span amounts = _tempBuffer.getSpan(0); + std::fill(amounts.begin(), amounts.end(), baseAmount); + + for (unsigned c = 0; c < EffectChannels; ++c) { + const float *input = inputs[c]; + float *output = outputs[c]; + + auto &up2x = _upsampler2x[c]; + auto &down2x = _downsampler2x[c]; + + for (unsigned i = 0; i < nframes; ++i) { + float amount = amounts[i] * 0.01f; + float in = input[i]; + + float in2x[2]; + up2x.process_sample(in2x[0], in2x[1], in); + + float out2x[2]; + out2x[0] = amount * std::fabs(in2x[0]) + (1.0f - amount) * in2x[0]; + out2x[1] = amount * std::fabs(in2x[1]) + (1.0f - amount) * in2x[1]; + + output[i] = down2x.process_sample(out2x); + } + } + } + + std::unique_ptr Rectify::makeInstance(absl::Span members) + { + auto fx = absl::make_unique(); + + for (const Opcode& opc : members) { + switch (opc.lettersOnlyHash) { + case hash("rectify_mode"): + if (opc.value == "full") + fx->_full = true; + else if (opc.value == "half") + fx->_full = false; + break; + case hash("rectify"): + setValueFromOpcode(opc, fx->_amount, { 0.0, 100.0 }); + break; + } + } + + return CXX11_MOVE(fx); + } + +} // namespace fx +} // namespace sfz diff --git a/src/sfizz/effects/Rectify.h b/src/sfizz/effects/Rectify.h new file mode 100644 index 00000000..b6c27a84 --- /dev/null +++ b/src/sfizz/effects/Rectify.h @@ -0,0 +1,58 @@ +// 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" +#include "hiir/Downsampler2xFpu.h" +#include "hiir/Upsampler2xFpu.h" + +namespace sfz { +namespace fx { + + /** + * @brief Half-wave and full-wave rectifier + */ + class Rectify : public Effect { + public: + Rectify(); + + /** + * @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 Copy the input signal to the output + */ + 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: + AudioBuffer _tempBuffer { 1, config::defaultSamplesPerBlock }; + hiir::Downsampler2xFpu<12> _downsampler2x[2]; + hiir::Upsampler2xFpu<12> _upsampler2x[2]; + + float _amount = 0; + bool _full = false; + }; + +} // namespace fx +} // namespace sfz From 55b710687aaa8ef7e79e61011c501dfb497d4bcf Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 18 Mar 2020 20:02:15 +0100 Subject: [PATCH 3/6] Add the gain effect --- src/CMakeLists.txt | 1 + src/sfizz/Effects.cpp | 2 ++ src/sfizz/effects/Gain.cpp | 73 ++++++++++++++++++++++++++++++++++++++ src/sfizz/effects/Gain.h | 50 ++++++++++++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 src/sfizz/effects/Gain.cpp create mode 100644 src/sfizz/effects/Gain.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2fcaeb88..d4c738d2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -26,6 +26,7 @@ set (SFIZZ_SOURCES sfizz/effects/Lofi.cpp sfizz/effects/Limiter.cpp sfizz/effects/Rectify.cpp + sfizz/effects/Gain.cpp ) include (SfizzSIMDSourceFiles) diff --git a/src/sfizz/Effects.cpp b/src/sfizz/Effects.cpp index eb6625f3..bfa8f52c 100644 --- a/src/sfizz/Effects.cpp +++ b/src/sfizz/Effects.cpp @@ -16,6 +16,7 @@ #include "effects/Lofi.h" #include "effects/Limiter.h" #include "effects/Rectify.h" +#include "effects/Gain.h" #include namespace sfz { @@ -31,6 +32,7 @@ void EffectFactory::registerStandardEffectTypes() // extensions (book) registerEffectType("rectify", fx::Rectify::makeInstance); + registerEffectType("gain", fx::Gain::makeInstance); } void EffectFactory::registerEffectType(absl::string_view name, Effect::MakeInstance& make) diff --git a/src/sfizz/effects/Gain.cpp b/src/sfizz/effects/Gain.cpp new file mode 100644 index 00000000..e5b46acc --- /dev/null +++ b/src/sfizz/effects/Gain.cpp @@ -0,0 +1,73 @@ +// 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): this effect is book-only, mentioned but not documented + + Note(jpc): implementation status + +- [x] gain +- [ ] gain_oncc + */ + +#include "Gain.h" +#include "Opcode.h" +#include "SIMDHelpers.h" +#include "absl/memory/memory.h" + +namespace sfz { +namespace fx { + + void Gain::setSampleRate(double sampleRate) + { + (void)sampleRate; + } + + void Gain::setSamplesPerBlock(int samplesPerBlock) + { + _tempBuffer.resize(samplesPerBlock); + } + + void Gain::clear() + { + } + + void Gain::process(const float* const inputs[], float* const outputs[], unsigned nframes) + { + const float baseGain = _gain; + + absl::Span gains = _tempBuffer.getSpan(0); + std::fill(gains.begin(), gains.end(), baseGain); + + // to linear + for (unsigned i = 0; i < nframes; ++i) + gains[i] = std::pow(10.0f, 0.05f * gains[i]); + + for (unsigned c = 0; c < EffectChannels; ++c) { + absl::Span input { inputs[c], nframes }; + absl::Span output { outputs[c], nframes }; + sfz::applyGain(absl::Span { gains }, input, output); + } + } + + std::unique_ptr Gain::makeInstance(absl::Span members) + { + auto fx = absl::make_unique(); + + for (const Opcode& opc : members) { + switch (opc.lettersOnlyHash) { + case hash("gain"): + setValueFromOpcode(opc, fx->_gain, {-96.0f, 96.0f}); + break; + } + } + + return CXX11_MOVE(fx); + } + +} // namespace fx +} // namespace sfz diff --git a/src/sfizz/effects/Gain.h b/src/sfizz/effects/Gain.h new file mode 100644 index 00000000..20a95f59 --- /dev/null +++ b/src/sfizz/effects/Gain.h @@ -0,0 +1,50 @@ +// 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" + +namespace sfz { +namespace fx { + + /** + * @brief Gain effect + */ + class Gain : public Effect { + public: + /** + * @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 Copy the input signal to the output + */ + 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: + float _gain = 0; // in dB + AudioBuffer _tempBuffer { 1, config::defaultSamplesPerBlock }; + }; + +} // namespace fx +} // namespace sfz From 430b54128bf57ead82632b2c199e4af3a342cf7b Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Wed, 18 Mar 2020 20:49:52 +0100 Subject: [PATCH 4/6] Add the width effect --- src/CMakeLists.txt | 1 + src/sfizz/Effects.cpp | 2 + src/sfizz/effects/Width.cpp | 80 +++++++++++++++++++++++++++++++++++++ src/sfizz/effects/Width.h | 50 +++++++++++++++++++++++ 4 files changed, 133 insertions(+) create mode 100644 src/sfizz/effects/Width.cpp create mode 100644 src/sfizz/effects/Width.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d4c738d2..821fb8fe 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,6 +27,7 @@ set (SFIZZ_SOURCES sfizz/effects/Limiter.cpp sfizz/effects/Rectify.cpp sfizz/effects/Gain.cpp + sfizz/effects/Width.cpp ) include (SfizzSIMDSourceFiles) diff --git a/src/sfizz/Effects.cpp b/src/sfizz/Effects.cpp index bfa8f52c..9facbfcb 100644 --- a/src/sfizz/Effects.cpp +++ b/src/sfizz/Effects.cpp @@ -17,6 +17,7 @@ #include "effects/Limiter.h" #include "effects/Rectify.h" #include "effects/Gain.h" +#include "effects/Width.h" #include namespace sfz { @@ -33,6 +34,7 @@ void EffectFactory::registerStandardEffectTypes() // extensions (book) registerEffectType("rectify", fx::Rectify::makeInstance); registerEffectType("gain", fx::Gain::makeInstance); + registerEffectType("width", fx::Width::makeInstance); } void EffectFactory::registerEffectType(absl::string_view name, Effect::MakeInstance& make) diff --git a/src/sfizz/effects/Width.cpp b/src/sfizz/effects/Width.cpp new file mode 100644 index 00000000..9a9fa336 --- /dev/null +++ b/src/sfizz/effects/Width.cpp @@ -0,0 +1,80 @@ +// 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): this effect is book-only, mentioned but not documented + + Note(jpc): implementation status + +- [x] width +- [ ] width_oncc + */ + +#include "Width.h" +#include "Opcode.h" +#include "SIMDHelpers.h" +#include "absl/memory/memory.h" + +namespace sfz { +namespace fx { + + void Width::setSampleRate(double sampleRate) + { + (void)sampleRate; + } + + void Width::setSamplesPerBlock(int samplesPerBlock) + { + _tempBuffer.resize(samplesPerBlock); + } + + void Width::clear() + { + } + + void Width::process(const float* const inputs[], float* const outputs[], unsigned nframes) + { + const float baseWidth = _width; + + absl::Span widths = _tempBuffer.getSpan(0); + std::fill(widths.begin(), widths.end(), baseWidth); + + absl::Span input1 { inputs[0], nframes }; + absl::Span input2 { inputs[1], nframes }; + absl::Span output1 { outputs[0], nframes }; + absl::Span output2 { outputs[1], nframes }; + + for (unsigned i = 0; i < nframes; ++i) { + float l = input1[i]; + float r = input2[i]; + + float w = clamp((widths[i] + 100.0f) * 0.005f, 0.0f, 1.0f); + float coeff1 = _internals::panLookup(w); + float coeff2 = _internals::panLookup(1.0f - w); + + output1[i] = l * coeff2 + r * coeff1; + output2[i] = l * coeff1 + r * coeff2; + } + } + + std::unique_ptr Width::makeInstance(absl::Span members) + { + auto fx = absl::make_unique(); + + for (const Opcode& opc : members) { + switch (opc.lettersOnlyHash) { + case hash("width"): + setValueFromOpcode(opc, fx->_width, {-100.0f, 100.0f}); + break; + } + } + + return CXX11_MOVE(fx); + } + +} // namespace fx +} // namespace sfz diff --git a/src/sfizz/effects/Width.h b/src/sfizz/effects/Width.h new file mode 100644 index 00000000..82ebb7b8 --- /dev/null +++ b/src/sfizz/effects/Width.h @@ -0,0 +1,50 @@ +// 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" + +namespace sfz { +namespace fx { + + /** + * @brief Stereo width effect + */ + class Width : public Effect { + public: + /** + * @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 Copy the input signal to the output + */ + 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: + float _width = 100; + AudioBuffer _tempBuffer { 1, config::defaultSamplesPerBlock }; + }; + +} // namespace fx +} // namespace sfz From 3ab8eb8ad49b9930856d53910e42420adc5cd7ce Mon Sep 17 00:00:00 2001 From: Paul Ferrand Date: Fri, 27 Mar 2020 19:20:32 +0100 Subject: [PATCH 5/6] const all the things --- src/sfizz/effects/Rectify.cpp | 2 +- src/sfizz/effects/Width.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sfizz/effects/Rectify.cpp b/src/sfizz/effects/Rectify.cpp index 015e5d29..1f88a771 100644 --- a/src/sfizz/effects/Rectify.cpp +++ b/src/sfizz/effects/Rectify.cpp @@ -66,7 +66,7 @@ namespace fx { auto &down2x = _downsampler2x[c]; for (unsigned i = 0; i < nframes; ++i) { - float amount = amounts[i] * 0.01f; + const float amount = normalizePercents(amounts[i]); float in = input[i]; float in2x[2]; diff --git a/src/sfizz/effects/Width.cpp b/src/sfizz/effects/Width.cpp index 9a9fa336..8c3893ff 100644 --- a/src/sfizz/effects/Width.cpp +++ b/src/sfizz/effects/Width.cpp @@ -49,12 +49,12 @@ namespace fx { absl::Span output2 { outputs[1], nframes }; for (unsigned i = 0; i < nframes; ++i) { - float l = input1[i]; - float r = input2[i]; + const float l = input1[i]; + const float r = input2[i]; - float w = clamp((widths[i] + 100.0f) * 0.005f, 0.0f, 1.0f); - float coeff1 = _internals::panLookup(w); - float coeff2 = _internals::panLookup(1.0f - w); + const float w = clamp((widths[i] + 100.0f) * 0.005f, 0.0f, 1.0f); + const float coeff1 = _internals::panLookup(w); + const float coeff2 = _internals::panLookup(1.0f - w); output1[i] = l * coeff2 + r * coeff1; output2[i] = l * coeff1 + r * coeff2; From 03b9e19676dc4d5c25495c92a6117fe17c8c3f0e Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 29 Mar 2020 20:17:49 +0200 Subject: [PATCH 6/6] Oversample the limiter by 2x --- src/sfizz/effects/Limiter.cpp | 25 ++++++++++++++++++++++--- src/sfizz/effects/Limiter.h | 5 +++++ src/sfizz/effects/dsp/limiter.dsp | 5 +++-- src/sfizz/effects/gen/limiter.cpp | 2 +- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/sfizz/effects/Limiter.cpp b/src/sfizz/effects/Limiter.cpp index 577fcba5..70f317d8 100644 --- a/src/sfizz/effects/Limiter.cpp +++ b/src/sfizz/effects/Limiter.cpp @@ -12,9 +12,12 @@ #include "Limiter.h" #include "Opcode.h" -#include "gen/limiter.cpp" +#include "AudioSpan.h" #include "absl/memory/memory.h" +static constexpr int _oversampling = 2; +#include "gen/limiter.cpp" + namespace sfz { namespace fx { @@ -32,12 +35,20 @@ namespace fx { { _limiter->classInit(sampleRate); _limiter->instanceConstants(sampleRate); + + static constexpr double coefs2x[12] = { 0.036681502163648017, 0.13654762463195794, 0.27463175937945444, 0.42313861743656711, 0.56109869787919531, 0.67754004997416184, 0.76974183386322703, 0.83988962484963892, 0.89226081800387902, 0.9315419599631839, 0.96209454837808417, 0.98781637073289585 }; + + for (unsigned c = 0; c < EffectChannels; ++c) { + _downsampler2x[c].set_coefs(coefs2x); + _upsampler2x[c].set_coefs(coefs2x); + } + clear(); } void Limiter::setSamplesPerBlock(int samplesPerBlock) { - (void)samplesPerBlock; + _tempBuffer2x.resize(2 * samplesPerBlock); } void Limiter::clear() @@ -47,7 +58,15 @@ namespace fx { void Limiter::process(const float* const inputs[], float* const outputs[], unsigned nframes) { - _limiter->compute(nframes, inputs, outputs); + auto inOut2x = AudioSpan( _tempBuffer2x).first(2 * nframes); + + for (unsigned c = 0; c < EffectChannels; ++c) + _upsampler2x[c].process_block(inOut2x.getSpan(c).data(), inputs[c], nframes); + + _limiter->compute(2 * nframes, inOut2x, inOut2x); + + for (unsigned c = 0; c < EffectChannels; ++c) + _downsampler2x[c].process_block(outputs[c], inOut2x.getSpan(c).data(), nframes); } std::unique_ptr Limiter::makeInstance(absl::Span members) diff --git a/src/sfizz/effects/Limiter.h b/src/sfizz/effects/Limiter.h index 49cb264a..30737377 100644 --- a/src/sfizz/effects/Limiter.h +++ b/src/sfizz/effects/Limiter.h @@ -6,6 +6,8 @@ #pragma once #include "Effects.h" +#include "hiir/Downsampler2xFpu.h" +#include "hiir/Upsampler2xFpu.h" class faustLimiter; namespace sfz { @@ -47,6 +49,9 @@ namespace fx { private: std::unique_ptr _limiter; + AudioBuffer _tempBuffer2x { 2, 2 * config::defaultSamplesPerBlock }; + hiir::Downsampler2xFpu<12> _downsampler2x[EffectChannels]; + hiir::Upsampler2xFpu<12> _upsampler2x[EffectChannels]; }; } // namespace fx diff --git a/src/sfizz/effects/dsp/limiter.dsp b/src/sfizz/effects/dsp/limiter.dsp index e8ed03d9..cbb14b26 100644 --- a/src/sfizz/effects/dsp/limiter.dsp +++ b/src/sfizz/effects/dsp/limiter.dsp @@ -1,8 +1,9 @@ import("stdfaust.lib"); limiter(x) = gain*x with { - att = 0.0008; - rel = 0.5; + att = 0.0008 * over; + rel = 0.5 * over; + over = fconstant(int _oversampling, ); 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)); }; diff --git a/src/sfizz/effects/gen/limiter.cpp b/src/sfizz/effects/gen/limiter.cpp index 7a3d99db..420f6728 100644 --- a/src/sfizz/effects/gen/limiter.cpp +++ b/src/sfizz/effects/gen/limiter.cpp @@ -51,7 +51,7 @@ public: void instanceConstants(int sample_rate) { fSampleRate = sample_rate; - fConst0 = std::min(192000.0f, std::max(1.0f, float(fSampleRate))); + fConst0 = (std::min(192000.0f, std::max(1.0f, float(fSampleRate))) * float(_oversampling)); fConst1 = std::exp((0.0f - (2500.0f / fConst0))); fConst2 = (1.0f - fConst1); fConst3 = std::exp((0.0f - (1250.0f / fConst0)));