Add the autopan effect
This commit is contained in:
parent
51be7a68ba
commit
a6df5d9526
6 changed files with 322 additions and 0 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 <algorithm>
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
154
src/sfizz/effects/Apan.cpp
Normal file
154
src/sfizz/effects/Apan.cpp
Normal file
|
|
@ -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 <limits>
|
||||
#include <cmath>
|
||||
|
||||
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<Effect> Apan::makeInstance(absl::Span<const Opcode> members)
|
||||
{
|
||||
std::unique_ptr<Apan> fx { new Apan };
|
||||
|
||||
for (const Opcode& opc : members) {
|
||||
switch (opc.lettersOnlyHash) {
|
||||
case hash("apan_waveform"):
|
||||
if (auto value = readOpcode(opc.value, Range<int> { 0, std::numeric_limits<int>::max() }))
|
||||
fx->_lfoWave = *value;
|
||||
break;
|
||||
case hash("apan_freq"):
|
||||
if (auto value = readOpcode(opc.value, Range<float> { 0.0, std::numeric_limits<float>::max() }))
|
||||
fx->_lfoFrequency = *value;
|
||||
break;
|
||||
case hash("apan_phase"):
|
||||
if (auto value = readOpcode(opc.value, Range<float> { 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<float> { 0.0, 100.0 }))
|
||||
fx->_dry = *value / 100.0f;
|
||||
break;
|
||||
case hash("apan_wet"):
|
||||
if (auto value = readOpcode(opc.value, Range<float> { 0.0, 100.0 }))
|
||||
fx->_wet = *value / 100.0f;
|
||||
break;
|
||||
case hash("apan_depth"):
|
||||
if (auto value = readOpcode(opc.value, Range<float> { 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<lfo::X>(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 <int Wave> 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<Wave>(phaseLeft);
|
||||
right[i] = lfo::evaluateAtPhase<Wave>(phaseRight);
|
||||
|
||||
phaseLeft += frequency * samplePeriod;
|
||||
phaseLeft -= (int)phaseLeft;
|
||||
}
|
||||
|
||||
_lfoPhase = phaseLeft;
|
||||
}
|
||||
|
||||
} // namespace fx
|
||||
} // namespace sfz
|
||||
67
src/sfizz/effects/Apan.h
Normal file
67
src/sfizz/effects/Apan.h
Normal file
|
|
@ -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 <memory>
|
||||
|
||||
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 <effect> block.
|
||||
*/
|
||||
static std::unique_ptr<Effect> makeInstance(absl::Span<const Opcode> members);
|
||||
|
||||
private:
|
||||
void computeLfos(float* left, float* right, unsigned nframes);
|
||||
template <int Wave> void computeLfos(float* left, float* right, unsigned nframes);
|
||||
|
||||
private:
|
||||
float _samplePeriod = 0.0;
|
||||
std::unique_ptr<float[]> _lfoOutLeft;
|
||||
std::unique_ptr<float[]> _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
|
||||
30
src/sfizz/effects/CommonLFO.h
Normal file
30
src/sfizz/effects/CommonLFO.h
Normal file
|
|
@ -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 <int Wave> float evaluateAtPhase(float phase);
|
||||
|
||||
} // namespace lfo
|
||||
} // namespace fx
|
||||
} // namespace sfz
|
||||
|
||||
#include "CommonLFO.hpp"
|
||||
68
src/sfizz/effects/CommonLFO.hpp
Normal file
68
src/sfizz/effects/CommonLFO.hpp
Normal file
|
|
@ -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 <cmath>
|
||||
|
||||
namespace sfz {
|
||||
namespace fx {
|
||||
namespace lfo {
|
||||
|
||||
template <>
|
||||
inline float evaluateAtPhase<kTriangle>(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<kSine>(float phase)
|
||||
{
|
||||
float x = phase + phase - 1;
|
||||
return 4 * x * (1 - std::fabs(x));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline float evaluateAtPhase<kPulse75>(float phase)
|
||||
{
|
||||
return (phase < 0.75f) ? +1.0f : -1.0f;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline float evaluateAtPhase<kSquare>(float phase)
|
||||
{
|
||||
return (phase < 0.5f) ? +1.0f : -1.0f;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline float evaluateAtPhase<kPulse25>(float phase)
|
||||
{
|
||||
return (phase < 0.25f) ? +1.0f : -1.0f;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline float evaluateAtPhase<kPulse12_5>(float phase)
|
||||
{
|
||||
return (phase < 0.125f) ? +1.0f : -1.0f;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline float evaluateAtPhase<kRamp>(float phase)
|
||||
{
|
||||
return 2 * phase - 1;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline float evaluateAtPhase<kSaw>(float phase)
|
||||
{
|
||||
return 1 - 2 * phase;
|
||||
}
|
||||
|
||||
} // namespace lfo
|
||||
} // namespace fx
|
||||
} // namespace sfz
|
||||
Loading…
Add table
Reference in a new issue