mirror of https://github.com/probonopd/MiniDexed
parent
17030e8065
commit
6da53dcac5
@ -0,0 +1,307 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "fx_components.h" |
||||||
|
|
||||||
|
#include <algorithm> |
||||||
|
#include <climits> |
||||||
|
#include <cassert> |
||||||
|
|
||||||
|
#define MAKE_INTEGRAL_FRACTIONAL(x) \ |
||||||
|
int32_t x ## _integral = static_cast<int32_t>(x); \
|
||||||
|
float x ## _fractional = x - static_cast<float>(x ## _integral); |
||||||
|
|
||||||
|
enum Format |
||||||
|
{ |
||||||
|
FORMAT_12_BIT, |
||||||
|
FORMAT_16_BIT, |
||||||
|
FORMAT_32_BIT |
||||||
|
}; |
||||||
|
|
||||||
|
enum LFOIndex |
||||||
|
{ |
||||||
|
LFO_1, |
||||||
|
LFO_2 |
||||||
|
}; |
||||||
|
|
||||||
|
template <Format format> |
||||||
|
struct DataType |
||||||
|
{ |
||||||
|
}; |
||||||
|
|
||||||
|
inline int16_t clip16(int32_t x) |
||||||
|
{ |
||||||
|
if(x > INT16_MAX) |
||||||
|
{ |
||||||
|
return INT16_MAX; |
||||||
|
} |
||||||
|
|
||||||
|
if(x < INT16_MIN) |
||||||
|
{ |
||||||
|
return INT16_MIN; |
||||||
|
} |
||||||
|
|
||||||
|
return static_cast<int16_t>(x); |
||||||
|
} |
||||||
|
|
||||||
|
template <> |
||||||
|
struct DataType<FORMAT_16_BIT> |
||||||
|
{ |
||||||
|
typedef uint16_t T; |
||||||
|
|
||||||
|
static inline float32_t decompress(T value) |
||||||
|
{ |
||||||
|
return static_cast<float>(static_cast<int16_t>(value)) / 4096.0f; |
||||||
|
} |
||||||
|
|
||||||
|
static inline T compress(float32_t value) |
||||||
|
{ |
||||||
|
return clip16(static_cast<int32_t>(value * 4096.0f)); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
template < |
||||||
|
size_t size, |
||||||
|
Format format = FORMAT_16_BIT> |
||||||
|
class FxEngine : public FXBase |
||||||
|
{ |
||||||
|
DISALLOW_COPY_AND_ASSIGN(FxEngine); |
||||||
|
|
||||||
|
public: |
||||||
|
typedef typename DataType<format>::T T; |
||||||
|
|
||||||
|
FxEngine(float32_t sampling_rate) : |
||||||
|
FXBase(sampling_rate) |
||||||
|
{ |
||||||
|
this->buffer_ = new uint16_t[size]; |
||||||
|
this->lfo_[LFO_1] = new LFO(sampling_rate, LFO::Waveform::Sine, 0.0f, 32.0f); |
||||||
|
this->lfo_[LFO_2] = new LFO(sampling_rate, LFO::Waveform::Sine, 0.0f, 32.0f); |
||||||
|
this->clear(); |
||||||
|
} |
||||||
|
|
||||||
|
~FxEngine() |
||||||
|
{ |
||||||
|
delete[] this->buffer_; |
||||||
|
delete this->lfo_[LFO_1]; |
||||||
|
delete this->lfo_[LFO_2];
|
||||||
|
} |
||||||
|
|
||||||
|
void clear() |
||||||
|
{ |
||||||
|
memset(this->buffer_, 0, size * sizeof(uint16_t)); |
||||||
|
this->write_ptr_ = 0; |
||||||
|
} |
||||||
|
|
||||||
|
struct Empty |
||||||
|
{ |
||||||
|
}; |
||||||
|
|
||||||
|
template <int32_t l, typename T = Empty> |
||||||
|
struct Reserve |
||||||
|
{ |
||||||
|
typedef T Tail; |
||||||
|
enum
|
||||||
|
{ |
||||||
|
length = l |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
template <typename Memory, int32_t index> |
||||||
|
struct DelayLine |
||||||
|
{ |
||||||
|
enum
|
||||||
|
{ |
||||||
|
length = DelayLine<typename Memory::Tail, index - 1>::length, |
||||||
|
base = DelayLine<Memory, index - 1>::base + DelayLine<Memory, index - 1>::length + 1 |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
template <typename Memory> |
||||||
|
struct DelayLine<Memory, 0> |
||||||
|
{ |
||||||
|
enum
|
||||||
|
{ |
||||||
|
length = Memory::length, |
||||||
|
base = 0 |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
class Context |
||||||
|
{ |
||||||
|
DISALLOW_COPY_AND_ASSIGN(Context); |
||||||
|
friend class FxEngine; |
||||||
|
|
||||||
|
public: |
||||||
|
Context() : |
||||||
|
accumulator_(0.0f), |
||||||
|
previous_read_(0.0f), |
||||||
|
buffer_(nullptr), |
||||||
|
write_ptr_(0) |
||||||
|
{ |
||||||
|
memset(this->lfo_value_, 0, 2 * sizeof(float32_t)); |
||||||
|
} |
||||||
|
|
||||||
|
~Context() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
inline void load(float32_t value) |
||||||
|
{ |
||||||
|
this->accumulator_ = value; |
||||||
|
} |
||||||
|
|
||||||
|
inline void read(float32_t value, float32_t scale) |
||||||
|
{ |
||||||
|
this->accumulator_ += value * scale; |
||||||
|
} |
||||||
|
|
||||||
|
inline void read(float32_t value) |
||||||
|
{ |
||||||
|
this->accumulator_ += value; |
||||||
|
} |
||||||
|
|
||||||
|
inline void write(float32_t &value) |
||||||
|
{ |
||||||
|
value = this->accumulator_; |
||||||
|
} |
||||||
|
|
||||||
|
inline void write(float32_t &value, float32_t scale) |
||||||
|
{ |
||||||
|
value = this->accumulator_; |
||||||
|
this->accumulator_ *= scale; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename D> |
||||||
|
inline void write(D &d, int32_t offset, float32_t scale) |
||||||
|
{ |
||||||
|
assert(D::base + D::length <= size); |
||||||
|
T w = DataType<format>::compress(this->accumulator_); |
||||||
|
if(offset == -1) |
||||||
|
{ |
||||||
|
this->buffer_[(this->write_ptr_ + D::base + D::length - 1) & MASK] = w; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
this->buffer_[(this->write_ptr_ + D::base + offset) & MASK] = w; |
||||||
|
} |
||||||
|
this->accumulator_ *= scale; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename D> |
||||||
|
inline void write(D &d, float32_t scale) |
||||||
|
{ |
||||||
|
this->write(d, 0, scale); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename D> |
||||||
|
inline void writeAllPass(D &d, int32_t offset, float32_t scale) |
||||||
|
{ |
||||||
|
this->write(d, offset, scale); |
||||||
|
this->accumulator_ += this->previous_read_; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename D> |
||||||
|
inline void writeAllPass(D &d, float32_t scale) |
||||||
|
{ |
||||||
|
this->writeAllPass(d, 0, scale); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename D> |
||||||
|
inline void read(D &d, int32_t offset, float32_t scale) |
||||||
|
{ |
||||||
|
assert(D::base + D::length <= size); |
||||||
|
T r; |
||||||
|
if(offset == -1) |
||||||
|
{ |
||||||
|
r = this->buffer_[(this->write_ptr_ + D::base + D::length - 1) & MASK]; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
r = this->buffer_[(this->write_ptr_ + D::base + offset) & MASK]; |
||||||
|
} |
||||||
|
float32_t r_f = DataType<format>::decompress(r); |
||||||
|
this->previous_read_ = r_f; |
||||||
|
this->accumulator_ += r_f * scale; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename D> |
||||||
|
inline void read(D &d, float32_t scale) |
||||||
|
{ |
||||||
|
this->read(d, 0, scale); |
||||||
|
} |
||||||
|
|
||||||
|
inline void lp(float32_t &state, float32_t coefficient) |
||||||
|
{ |
||||||
|
state += coefficient * (this->accumulator_ - state); |
||||||
|
this->accumulator_ = state; |
||||||
|
} |
||||||
|
|
||||||
|
inline void hp(float32_t &state, float32_t coefficient) |
||||||
|
{ |
||||||
|
state += coefficient * (this->accumulator_ - state); |
||||||
|
this->accumulator_ -= state; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename D> |
||||||
|
inline void interpolate(D &d, float32_t offset, float32_t scale) |
||||||
|
{ |
||||||
|
assert(D::base + D::length <= size); |
||||||
|
MAKE_INTEGRAL_FRACTIONAL(offset); |
||||||
|
float32_t a = DataType<format>::decompress(this->buffer_[(this->write_ptr_ + offset_integral + D::base) & MASK]); |
||||||
|
float32_t b = DataType<format>::decompress(this->buffer_[(this->write_ptr_ + offset_integral + D::base + 1) & MASK]); |
||||||
|
float32_t x = a + (b - a) * offset_fractional; |
||||||
|
this->previous_read_ = x; |
||||||
|
this->accumulator_ += x * scale; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename D> |
||||||
|
inline void interpolate(D &d, float32_t offset, LFOIndex index, float32_t amplitude, float32_t scale) |
||||||
|
{ |
||||||
|
assert(D::base + D::length <= size); |
||||||
|
offset += amplitude * lfo_value_[index]; |
||||||
|
MAKE_INTEGRAL_FRACTIONAL(offset); |
||||||
|
float32_t a = DataType<format>::decompress(this->buffer_[(this->write_ptr_ + offset_integral + D::base) & MASK]); |
||||||
|
float32_t b = DataType<format>::decompress(this->buffer_[(this->write_ptr_ + offset_integral + D::base + 1) & MASK]); |
||||||
|
float32_t x = a + (b - a) * offset_fractional; |
||||||
|
this->previous_read_ = x; |
||||||
|
this->accumulator_ += x * scale; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
float32_t accumulator_; |
||||||
|
float32_t previous_read_; |
||||||
|
float32_t lfo_value_[2]; |
||||||
|
T* buffer_; |
||||||
|
int32_t write_ptr_; |
||||||
|
}; |
||||||
|
|
||||||
|
inline void setLFOFrequency(LFOIndex index, float32_t frequency) |
||||||
|
{ |
||||||
|
this->lfo_[index]->setFrequency(frequency); |
||||||
|
} |
||||||
|
|
||||||
|
inline void start(Context *c) |
||||||
|
{ |
||||||
|
--this->write_ptr_; |
||||||
|
if(this->write_ptr_ < 0) |
||||||
|
{ |
||||||
|
this->write_ptr_ += size; |
||||||
|
} |
||||||
|
c->accumulator_ = 0.0f; |
||||||
|
c->previous_read_ = 0.0f; |
||||||
|
c->buffer_ = buffer_; |
||||||
|
c->write_ptr_ = write_ptr_; |
||||||
|
c->lfo_value_[LFO_1] = this->lfo_[LFO_1]->process(); |
||||||
|
c->lfo_value_[LFO_2] = this->lfo_[LFO_2]->process(); |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
enum
|
||||||
|
{ |
||||||
|
MASK = size - 1 |
||||||
|
}; |
||||||
|
|
||||||
|
uint16_t* buffer_; |
||||||
|
unsigned write_ptr_; |
||||||
|
|
||||||
|
LFO* lfo_[2]; |
||||||
|
}; |
@ -1,116 +0,0 @@ |
|||||||
#include "fx_shimmer_reverb2.h" |
|
||||||
|
|
||||||
#include <cmath> |
|
||||||
#include <algorithm> |
|
||||||
|
|
||||||
ShimmerReverb2::ShimmerReverb2( float32_t sampling_rate,
|
|
||||||
float32_t decay,
|
|
||||||
float32_t diffusion,
|
|
||||||
float32_t pitch_shift) :
|
|
||||||
FXElement(sampling_rate), |
|
||||||
FXUnitModule(), |
|
||||||
reverb_buffer_index_(0) |
|
||||||
{ |
|
||||||
this->setDecay(decay); |
|
||||||
this->setDiffusion(diffusion); |
|
||||||
this->setPitchShift(pitch_shift); |
|
||||||
} |
|
||||||
|
|
||||||
ShimmerReverb2::~ShimmerReverb2() |
|
||||||
{ |
|
||||||
} |
|
||||||
|
|
||||||
void ShimmerReverb2::processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) |
|
||||||
{ |
|
||||||
if(!this->isEnable()) |
|
||||||
{ |
|
||||||
outL = inL; |
|
||||||
outR = inR; |
|
||||||
return; |
|
||||||
} |
|
||||||
|
|
||||||
// Processing left channel
|
|
||||||
{ |
|
||||||
// Read the sample from the reverb buffer
|
|
||||||
float32_t reverb_sample = this->reverb_buffer_L_[this->reverb_buffer_index_]; |
|
||||||
|
|
||||||
// Calculate the pitch-shifted sample
|
|
||||||
float32_t pitch_shift_sample = reverb_sample * this->getPitchShift(); |
|
||||||
|
|
||||||
// Interpolate the pitch-shifted sample to the original pitch
|
|
||||||
float32_t pitch_shift_frac = pitch_shift_sample - std::floor(pitch_shift_sample); |
|
||||||
unsigned pitch_shift_index = static_cast<unsigned>(SHIMMER_BUFFER_SIZE + std::floor(pitch_shift_sample)) % SHIMMER_BUFFER_SIZE; |
|
||||||
float32_t pitch_shift_interp =
|
|
||||||
(1.0f - pitch_shift_frac) * this->reverb_buffer_L_[pitch_shift_index] + |
|
||||||
pitch_shift_frac * this->reverb_buffer_L_[(pitch_shift_index + 1) % SHIMMER_BUFFER_SIZE]; |
|
||||||
|
|
||||||
// Calculate the wet (reverb) and dry (original) samples
|
|
||||||
float32_t dry_level = 1.0f - this->getWetLevel(); |
|
||||||
float32_t wet_sample = dry_level * inL + this->getWetLevel() * pitch_shift_interp; |
|
||||||
float32_t dry_sample = this->getWetLevel() * inL + dry_level * pitch_shift_interp; |
|
||||||
|
|
||||||
outL = dry_sample; |
|
||||||
|
|
||||||
// Write the wet sample to the reverb buffer with the diffusion coefficient applied
|
|
||||||
this->reverb_buffer_L_[this->reverb_buffer_index_] = wet_sample + (reverb_sample * (1.0f - this->getDiffusion() / this->getSamplingRate() / this->getDecay())); |
|
||||||
} |
|
||||||
|
|
||||||
// Processing right channel
|
|
||||||
{ |
|
||||||
// Read the sample from the reverb buffer
|
|
||||||
float32_t reverb_sample = this->reverb_buffer_R_[this->reverb_buffer_index_]; |
|
||||||
|
|
||||||
// Calculate the pitch-shifted sample
|
|
||||||
float32_t pitch_shift_sample = reverb_sample * this->getPitchShift(); |
|
||||||
|
|
||||||
// Interpolate the pitch-shifted sample to the original pitch
|
|
||||||
float32_t pitch_shift_frac = pitch_shift_sample - std::floor(pitch_shift_sample); |
|
||||||
unsigned pitch_shift_index = static_cast<unsigned>(SHIMMER_BUFFER_SIZE + std::floor(pitch_shift_sample)) % SHIMMER_BUFFER_SIZE; |
|
||||||
float32_t pitch_shift_interp =
|
|
||||||
(1.0f - pitch_shift_frac) * this->reverb_buffer_R_[pitch_shift_index] + |
|
||||||
pitch_shift_frac * this->reverb_buffer_R_[(pitch_shift_index + 1) % SHIMMER_BUFFER_SIZE]; |
|
||||||
|
|
||||||
// Calculate the wet (reverb) and dry (original) samples
|
|
||||||
float32_t dry_level = 1.0f - this->getWetLevel(); |
|
||||||
float32_t wet_sample = dry_level * inR + this->getWetLevel() * pitch_shift_interp; |
|
||||||
float32_t dry_sample = this->getWetLevel() * inR + dry_level * pitch_shift_interp; |
|
||||||
|
|
||||||
outR = dry_sample; |
|
||||||
|
|
||||||
// Write the wet sample to the reverb buffer with the diffusion coefficient applied
|
|
||||||
this->reverb_buffer_R_[this->reverb_buffer_index_] = wet_sample + (reverb_sample * (1.0f - this->getDiffusion() / this->getSamplingRate() / this->getDecay())); |
|
||||||
} |
|
||||||
|
|
||||||
// Increment the buffer index and wrap around if necessary
|
|
||||||
this->reverb_buffer_index_ = (this->reverb_buffer_index_ + 1) % SHIMMER_BUFFER_SIZE;
|
|
||||||
} |
|
||||||
|
|
||||||
void ShimmerReverb2::setDecay(float32_t decay)
|
|
||||||
{ |
|
||||||
this->decay_ = constrain(decay, SHIMMER_MIN_DECAY, SHIMMER_MAX_DECAY); |
|
||||||
} |
|
||||||
|
|
||||||
float32_t ShimmerReverb2::getDecay() const
|
|
||||||
{ |
|
||||||
return this->decay_; |
|
||||||
} |
|
||||||
|
|
||||||
void ShimmerReverb2::setDiffusion(float32_t diffusion)
|
|
||||||
{ |
|
||||||
this->diffusion_ = constrain(diffusion, 0.0f, 1.0f); |
|
||||||
} |
|
||||||
|
|
||||||
float32_t ShimmerReverb2::getDiffusion() const
|
|
||||||
{ |
|
||||||
return this->diffusion_; |
|
||||||
} |
|
||||||
|
|
||||||
void ShimmerReverb2::setPitchShift(float32_t pitch_shift)
|
|
||||||
{ |
|
||||||
this->pitch_shift_ = constrain(pitch_shift, SHIMMER_MIN_PITCH_RATIO, SHIMMER_MAX_PITCH_RATIO); |
|
||||||
} |
|
||||||
|
|
||||||
float32_t ShimmerReverb2::getPitchShift() const
|
|
||||||
{ |
|
||||||
return this->pitch_shift_; |
|
||||||
} |
|
@ -1,67 +0,0 @@ |
|||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
//
|
|
||||||
|
|
||||||
//
|
|
||||||
// fx_shimmer_reverb2.h
|
|
||||||
//
|
|
||||||
// Stereo Shimmer reverb proposed in the context of the MiniDexed project
|
|
||||||
//
|
|
||||||
#pragma once |
|
||||||
|
|
||||||
#include "fx_components.h" |
|
||||||
#include "fx_unit.hpp" |
|
||||||
|
|
||||||
#define SHIMMER_BUFFER_SIZE 1024 |
|
||||||
|
|
||||||
#define SHIMMER_MIN_DECAY 0.0f |
|
||||||
#define SHIMMER_MAX_DECAY 10.0f |
|
||||||
|
|
||||||
#define SHIMMER_MIN_PITCH_RATIO 0.5f |
|
||||||
#define SHIMMER_MAX_PITCH_RATIO 2.0f |
|
||||||
|
|
||||||
class ShimmerReverb2 :
|
|
||||||
public virtual FXElement,
|
|
||||||
public virtual FXUnitModule |
|
||||||
{ |
|
||||||
DISALLOW_COPY_AND_ASSIGN(ShimmerReverb2); |
|
||||||
|
|
||||||
public: |
|
||||||
ShimmerReverb2( float32_t sampling_rate,
|
|
||||||
float32_t decay = 3.0f,
|
|
||||||
float32_t diffusion = 0.5f,
|
|
||||||
float32_t pitch_shift = 2.0f); |
|
||||||
|
|
||||||
virtual ~ShimmerReverb2(); |
|
||||||
|
|
||||||
virtual void processSample(float32_t inL, float32_t inR, float32_t& outL, float32_t& outR) override; |
|
||||||
|
|
||||||
void setDecay(float32_t delay_time_L); |
|
||||||
float32_t getDecay() const; |
|
||||||
|
|
||||||
void setDiffusion(float32_t delay_time_R); |
|
||||||
float32_t getDiffusion() const; |
|
||||||
|
|
||||||
void setPitchShift(float32_t frequency); |
|
||||||
float32_t getPitchShift() const; |
|
||||||
|
|
||||||
private: |
|
||||||
float32_t reverb_buffer_L_[SHIMMER_BUFFER_SIZE]; |
|
||||||
float32_t reverb_buffer_R_[SHIMMER_BUFFER_SIZE]; |
|
||||||
unsigned reverb_buffer_index_; |
|
||||||
|
|
||||||
// Current write position for left and right channel delay lines
|
|
||||||
float32_t decay_; // Reverb decay time in seconds (0 - 10)
|
|
||||||
float32_t diffusion_; // The degree to which the reverb is spread out over time (0 - 1)
|
|
||||||
float32_t pitch_shift_; // Determines the pitch shift ratio applied to the reverb (0.5 - 2.0)
|
|
||||||
}; |
|
Loading…
Reference in new issue