From a6df5d952663fe6d3191a58219cfdb5f8e11091c Mon Sep 17 00:00:00 2001 From: Jean Pierre Cimalando Date: Sun, 8 Mar 2020 18:35:27 +0100 Subject: [PATCH 1/7] Add the autopan effect --- src/CMakeLists.txt | 1 + src/sfizz/Effects.cpp | 2 + src/sfizz/effects/Apan.cpp | 154 ++++++++++++++++++++++++++++++++ src/sfizz/effects/Apan.h | 67 ++++++++++++++ src/sfizz/effects/CommonLFO.h | 30 +++++++ src/sfizz/effects/CommonLFO.hpp | 68 ++++++++++++++ 6 files changed, 322 insertions(+) create mode 100644 src/sfizz/effects/Apan.cpp create mode 100644 src/sfizz/effects/Apan.h create mode 100644 src/sfizz/effects/CommonLFO.h create mode 100644 src/sfizz/effects/CommonLFO.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 14d469f6..f1378612 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,7 @@ set (SFIZZ_SOURCES sfizz/effects/Nothing.cpp sfizz/effects/Filter.cpp sfizz/effects/Eq.cpp + sfizz/effects/Apan.cpp sfizz/effects/Lofi.cpp ) include (SfizzSIMDSourceFiles) diff --git a/src/sfizz/Effects.cpp b/src/sfizz/Effects.cpp index 7031582f..3398cc3d 100644 --- a/src/sfizz/Effects.cpp +++ b/src/sfizz/Effects.cpp @@ -12,6 +12,7 @@ #include "effects/Nothing.h" #include "effects/Filter.h" #include "effects/Eq.h" +#include "effects/Apan.h" #include "effects/Lofi.h" #include @@ -22,6 +23,7 @@ void EffectFactory::registerStandardEffectTypes() // TODO registerEffectType("filter", fx::Filter::makeInstance); registerEffectType("eq", fx::Eq::makeInstance); + registerEffectType("apan", fx::Apan::makeInstance); registerEffectType("lofi", fx::Lofi::makeInstance); } diff --git a/src/sfizz/effects/Apan.cpp b/src/sfizz/effects/Apan.cpp new file mode 100644 index 00000000..53d43774 --- /dev/null +++ b/src/sfizz/effects/Apan.cpp @@ -0,0 +1,154 @@ +// 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 + +- [x] apan_waveform +- [x] apan_freq +- [ ] apan_freq_oncc +- [x] apan_phase +- [ ] apan_phase_oncc +- [x] apan_dry +- [ ] apan_dry_oncc +- [x] apan_wet +- [ ] apan_wet_oncc +- [x] apan_depth +- [ ] apan_depth_oncc +*/ + +#include "Apan.h" +#include "CommonLFO.h" +#include "Opcode.h" +#include +#include + +namespace sfz { +namespace fx { + + void Apan::setSampleRate(double sampleRate) + { + _samplePeriod = 1.0 / sampleRate; + } + + void Apan::setSamplesPerBlock(int samplesPerBlock) + { + _lfoOutLeft.reset(new float[samplesPerBlock]); + _lfoOutRight.reset(new float[samplesPerBlock]); + } + + void Apan::clear() + { + _lfoPhase = 0.0; + } + + void Apan::process(const float* const inputs[], float* const outputs[], unsigned nframes) + { + float dry = _dry; + float wet = _wet; + float depth = _depth; + float* modL = _lfoOutLeft.get(); + float* modR = _lfoOutRight.get(); + + computeLfos(modL, modR, nframes); + + const float* inL = inputs[0]; + const float* inR = inputs[1]; + float* outL = outputs[0]; + float* outR = outputs[1]; + + for (unsigned i = 0; i < nframes; ++i) { + float modDD = depth * 0.5f * (modL[i] - modR[i]); // LFO in ±depth + + // uses a linear pan law + float gainL = 1 - modDD; + float gainR = 1 + modDD; + + outL[i] = inL[i] * (gainL * wet + dry); + outR[i] = inR[i] * (gainR * wet + dry); + } + } + + std::unique_ptr Apan::makeInstance(absl::Span members) + { + std::unique_ptr fx { new Apan }; + + for (const Opcode& opc : members) { + switch (opc.lettersOnlyHash) { + case hash("apan_waveform"): + if (auto value = readOpcode(opc.value, Range { 0, std::numeric_limits::max() })) + fx->_lfoWave = *value; + break; + case hash("apan_freq"): + if (auto value = readOpcode(opc.value, Range { 0.0, std::numeric_limits::max() })) + fx->_lfoFrequency = *value; + break; + case hash("apan_phase"): + if (auto value = readOpcode(opc.value, Range { 0.0, 360.0 })) { + float phase = *value / 360.0f; + phase -= (int)phase; + fx->_lfoPhaseOffset = phase; + } + break; + case hash("apan_dry"): + if (auto value = readOpcode(opc.value, Range { 0.0, 100.0 })) + fx->_dry = *value / 100.0f; + break; + case hash("apan_wet"): + if (auto value = readOpcode(opc.value, Range { 0.0, 100.0 })) + fx->_wet = *value / 100.0f; + break; + case hash("apan_depth"): + if (auto value = readOpcode(opc.value, Range { 0.0, 100.0 })) + fx->_depth = *value / 100.0f; + break; + } + } + + return fx; + } + + void Apan::computeLfos(float* left, float* right, unsigned nframes) + { + switch (_lfoWave) { + #define CASE(X) case lfo::X: \ + computeLfos(left, right, nframes); break; + default: + CASE(kTriangle) + CASE(kSine) + CASE(kPulse75) + CASE(kSquare) + CASE(kPulse25) + CASE(kPulse12_5) + CASE(kRamp) + CASE(kSaw) + #undef CASE + } + } + + template void Apan::computeLfos(float* left, float* right, unsigned nframes) + { + float samplePeriod = _samplePeriod; + float frequency = _lfoFrequency; + float offset = _lfoPhaseOffset; + float phaseLeft = _lfoPhase; + + for (unsigned i = 0; i < nframes; ++i) { + float phaseRight = phaseLeft + offset; + phaseRight -= (int)phaseRight; + + left[i] = lfo::evaluateAtPhase(phaseLeft); + right[i] = lfo::evaluateAtPhase(phaseRight); + + phaseLeft += frequency * samplePeriod; + phaseLeft -= (int)phaseLeft; + } + + _lfoPhase = phaseLeft; + } + +} // namespace fx +} // namespace sfz diff --git a/src/sfizz/effects/Apan.h b/src/sfizz/effects/Apan.h new file mode 100644 index 00000000..905316b6 --- /dev/null +++ b/src/sfizz/effects/Apan.h @@ -0,0 +1,67 @@ +// 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 + +namespace sfz { +namespace fx { + + /** + * @brief Effect which does nothing + */ + class Apan : 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: + void computeLfos(float* left, float* right, unsigned nframes); + template void computeLfos(float* left, float* right, unsigned nframes); + + private: + float _samplePeriod = 0.0; + std::unique_ptr _lfoOutLeft; + std::unique_ptr _lfoOutRight; + + // Controls + float _dry = 0.0; + float _wet = 0.0; + float _depth = 0.0; + int _lfoWave = 0; + float _lfoFrequency = 0.0; + float _lfoPhaseOffset = 0.5; + + // State + float _lfoPhase = 0.0; + }; + +} // namespace fx +} // namespace sfz diff --git a/src/sfizz/effects/CommonLFO.h b/src/sfizz/effects/CommonLFO.h new file mode 100644 index 00000000..3129e1c5 --- /dev/null +++ b/src/sfizz/effects/CommonLFO.h @@ -0,0 +1,30 @@ +// 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 + +namespace sfz { +namespace fx { +namespace lfo { + +enum Wave { + kTriangle, + kSine, + kPulse75, + kSquare, + kPulse25, + kPulse12_5, + kRamp, + kSaw, +}; + +template float evaluateAtPhase(float phase); + +} // namespace lfo +} // namespace fx +} // namespace sfz + +#include "CommonLFO.hpp" diff --git a/src/sfizz/effects/CommonLFO.hpp b/src/sfizz/effects/CommonLFO.hpp new file mode 100644 index 00000000..ef6eef36 --- /dev/null +++ b/src/sfizz/effects/CommonLFO.hpp @@ -0,0 +1,68 @@ +// 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 + +#include "CommonLFO.h" +#include + +namespace sfz { +namespace fx { +namespace lfo { + + template <> + inline float evaluateAtPhase(float phase) + { + float y = -4 * phase + 2; + y = (phase < 0.25f) ? (4 * phase) : y; + y = (phase > 0.75f) ? (4 * phase - 4) : y; + return y; + } + + template <> + inline float evaluateAtPhase(float phase) + { + float x = phase + phase - 1; + return 4 * x * (1 - std::fabs(x)); + } + + template <> + inline float evaluateAtPhase(float phase) + { + return (phase < 0.75f) ? +1.0f : -1.0f; + } + + template <> + inline float evaluateAtPhase(float phase) + { + return (phase < 0.5f) ? +1.0f : -1.0f; + } + + template <> + inline float evaluateAtPhase(float phase) + { + return (phase < 0.25f) ? +1.0f : -1.0f; + } + + template <> + inline float evaluateAtPhase(float phase) + { + return (phase < 0.125f) ? +1.0f : -1.0f; + } + + template <> + inline float evaluateAtPhase(float phase) + { + return 2 * phase - 1; + } + + template <> + inline float evaluateAtPhase(float phase) + { + return 1 - 2 * phase; + } + +} // namespace lfo +} // namespace fx +} // namespace sfz From d2af3eaf8dcfc5cb92bb4c71c9e8f03b8df8add1 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sat, 14 Mar 2020 22:54:19 +0100 Subject: [PATCH 2/7] Added a conditional move on return if CXX 11 for polymorphic unique_pointers --- src/sfizz/Macros.h | 3 +++ src/sfizz/effects/Apan.cpp | 3 ++- src/sfizz/effects/Lofi.cpp | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sfizz/Macros.h b/src/sfizz/Macros.h index f610eefb..4eaa1a11 100644 --- a/src/sfizz/Macros.h +++ b/src/sfizz/Macros.h @@ -20,6 +20,9 @@ #if __cplusplus > 201103L #define CXX14_CONSTEXPR constexpr +#define CXX11_MOVE(x) x #else #define CXX14_CONSTEXPR +#define CXX11_MOVE(x) std::move(x) #endif + diff --git a/src/sfizz/effects/Apan.cpp b/src/sfizz/effects/Apan.cpp index 53d43774..7b370c1e 100644 --- a/src/sfizz/effects/Apan.cpp +++ b/src/sfizz/effects/Apan.cpp @@ -21,6 +21,7 @@ */ #include "Apan.h" +#include "Macros.h" #include "CommonLFO.h" #include "Opcode.h" #include @@ -108,7 +109,7 @@ namespace fx { } } - return fx; + return CXX11_MOVE(fx); } void Apan::computeLfos(float* left, float* right, unsigned nframes) diff --git a/src/sfizz/effects/Lofi.cpp b/src/sfizz/effects/Lofi.cpp index f29d9950..87d64264 100644 --- a/src/sfizz/effects/Lofi.cpp +++ b/src/sfizz/effects/Lofi.cpp @@ -92,7 +92,7 @@ namespace fx { } } - return std::move(fx); + return CXX11_MOVE(fx); } /// From a1f3a10b09961892687309b4b9d76d61f33a530d Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sat, 14 Mar 2020 22:55:03 +0100 Subject: [PATCH 3/7] The CMAKE_CXX_STANDARD value passed on command line was ignored --- cmake/SfizzConfig.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/SfizzConfig.cmake b/cmake/SfizzConfig.cmake index 81184a87..1bc5248f 100644 --- a/cmake/SfizzConfig.cmake +++ b/cmake/SfizzConfig.cmake @@ -1,5 +1,5 @@ -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_C_STANDARD 99) +set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ standard to be used") +set(CMAKE_C_STANDARD 99 CACHE STRING "C standard to be used") # Export the compile_commands.json file set (CMAKE_EXPORT_COMPILE_COMMANDS ON) From 620dbb9380b85b6972ef2fd9eb868cd5dd5193a1 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sat, 14 Mar 2020 22:56:33 +0100 Subject: [PATCH 4/7] Corrected bugs and warnings in atomic_queue and LeakDetector --- src/external/atomic_queue/atomic_queue.h | 4 ++-- src/sfizz/LeakDetector.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/external/atomic_queue/atomic_queue.h b/src/external/atomic_queue/atomic_queue.h index e7bcf0f2..731d9a11 100644 --- a/src/external/atomic_queue/atomic_queue.h +++ b/src/external/atomic_queue/atomic_queue.h @@ -150,7 +150,7 @@ protected: // The special member functions are not thread-safe. - AtomicQueueCommon() noexcept = default; + AtomicQueueCommon() = default; AtomicQueueCommon(AtomicQueueCommon const& b) noexcept : head_(b.head_.load(X)) @@ -417,7 +417,7 @@ class AtomicQueue2 : public AtomicQueueCommon Date: Sat, 14 Mar 2020 23:04:14 +0100 Subject: [PATCH 5/7] Moved the verification ranges to Defaults.h --- src/sfizz/Defaults.h | 5 +++++ src/sfizz/effects/Apan.cpp | 12 ++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/sfizz/Defaults.h b/src/sfizz/Defaults.h index 4d01301c..3f53ecc3 100644 --- a/src/sfizz/Defaults.h +++ b/src/sfizz/Defaults.h @@ -206,5 +206,10 @@ namespace Default constexpr bool checkSostenuto { true }; // sostenuto_sw constexpr Range octaveOffsetRange { -10, 10 }; // octave_offset constexpr Range noteOffsetRange { -127, 127 }; // note_offset + + constexpr Range apanWaveformRange { 0, std::numeric_limits::max() }; + constexpr Range apanFrequencyRange { 0, std::numeric_limits::max() }; + constexpr Range apanPhaseRange { 0.0, 360.0 }; + constexpr Range apanLevelRange { 0.0, 100.0 }; } } diff --git a/src/sfizz/effects/Apan.cpp b/src/sfizz/effects/Apan.cpp index 7b370c1e..bac37e74 100644 --- a/src/sfizz/effects/Apan.cpp +++ b/src/sfizz/effects/Apan.cpp @@ -80,30 +80,30 @@ namespace fx { for (const Opcode& opc : members) { switch (opc.lettersOnlyHash) { case hash("apan_waveform"): - if (auto value = readOpcode(opc.value, Range { 0, std::numeric_limits::max() })) + if (auto value = readOpcode(opc.value, Default::apanWaveformRange)) fx->_lfoWave = *value; break; case hash("apan_freq"): - if (auto value = readOpcode(opc.value, Range { 0.0, std::numeric_limits::max() })) + if (auto value = readOpcode(opc.value, Default::apanFrequencyRange)) fx->_lfoFrequency = *value; break; case hash("apan_phase"): - if (auto value = readOpcode(opc.value, Range { 0.0, 360.0 })) { + if (auto value = readOpcode(opc.value, Default::apanPhaseRange)) { float phase = *value / 360.0f; phase -= (int)phase; fx->_lfoPhaseOffset = phase; } break; case hash("apan_dry"): - if (auto value = readOpcode(opc.value, Range { 0.0, 100.0 })) + if (auto value = readOpcode(opc.value, Default::apanLevelRange)) fx->_dry = *value / 100.0f; break; case hash("apan_wet"): - if (auto value = readOpcode(opc.value, Range { 0.0, 100.0 })) + if (auto value = readOpcode(opc.value, Default::apanLevelRange)) fx->_wet = *value / 100.0f; break; case hash("apan_depth"): - if (auto value = readOpcode(opc.value, Range { 0.0, 100.0 })) + if (auto value = readOpcode(opc.value, Default::apanLevelRange)) fx->_depth = *value / 100.0f; break; } From c41f26bf9df870589e12944c9e2e41eb461e21e4 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sat, 14 Mar 2020 23:06:09 +0100 Subject: [PATCH 6/7] Long style cast --- src/sfizz/effects/Apan.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sfizz/effects/Apan.cpp b/src/sfizz/effects/Apan.cpp index bac37e74..92bf0491 100644 --- a/src/sfizz/effects/Apan.cpp +++ b/src/sfizz/effects/Apan.cpp @@ -90,7 +90,7 @@ namespace fx { case hash("apan_phase"): if (auto value = readOpcode(opc.value, Default::apanPhaseRange)) { float phase = *value / 360.0f; - phase -= (int)phase; + phase -= static_cast(phase); fx->_lfoPhaseOffset = phase; } break; @@ -139,13 +139,13 @@ namespace fx { for (unsigned i = 0; i < nframes; ++i) { float phaseRight = phaseLeft + offset; - phaseRight -= (int)phaseRight; + phaseRight -= static_cast(phaseRight); left[i] = lfo::evaluateAtPhase(phaseLeft); right[i] = lfo::evaluateAtPhase(phaseRight); phaseLeft += frequency * samplePeriod; - phaseLeft -= (int)phaseLeft; + phaseLeft -= static_cast(phaseLeft); } _lfoPhase = phaseLeft; From 26646da29c55dc0e0c44ad0ef747089a5d664d92 Mon Sep 17 00:00:00 2001 From: Paul Fd Date: Sat, 14 Mar 2020 23:21:24 +0100 Subject: [PATCH 7/7] Change to sfz::Buffer --- src/sfizz/effects/Apan.cpp | 8 ++++---- src/sfizz/effects/Apan.h | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sfizz/effects/Apan.cpp b/src/sfizz/effects/Apan.cpp index 92bf0491..473985fd 100644 --- a/src/sfizz/effects/Apan.cpp +++ b/src/sfizz/effects/Apan.cpp @@ -37,8 +37,8 @@ namespace fx { void Apan::setSamplesPerBlock(int samplesPerBlock) { - _lfoOutLeft.reset(new float[samplesPerBlock]); - _lfoOutRight.reset(new float[samplesPerBlock]); + _lfoOutLeft.resize(samplesPerBlock); + _lfoOutRight.resize(samplesPerBlock); } void Apan::clear() @@ -51,8 +51,8 @@ namespace fx { float dry = _dry; float wet = _wet; float depth = _depth; - float* modL = _lfoOutLeft.get(); - float* modR = _lfoOutRight.get(); + float* modL = _lfoOutLeft.data(); + float* modR = _lfoOutRight.data(); computeLfos(modL, modR, nframes); diff --git a/src/sfizz/effects/Apan.h b/src/sfizz/effects/Apan.h index 905316b6..52b85dc6 100644 --- a/src/sfizz/effects/Apan.h +++ b/src/sfizz/effects/Apan.h @@ -6,7 +6,7 @@ #pragma once #include "Effects.h" -#include +#include "Buffer.h" namespace sfz { namespace fx { @@ -48,8 +48,8 @@ namespace fx { private: float _samplePeriod = 0.0; - std::unique_ptr _lfoOutLeft; - std::unique_ptr _lfoOutRight; + sfz::Buffer _lfoOutLeft { config::defaultSamplesPerBlock }; + sfz::Buffer _lfoOutRight { config::defaultSamplesPerBlock }; // Controls float _dry = 0.0;