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