mirror of https://github.com/probonopd/MiniDexed
parent
7f21a03dac
commit
20eeb54ba2
@ -0,0 +1,185 @@ |
||||
/*
|
||||
* Flanger / Chorus Port |
||||
* Ported from https://github.com/ssj71/rkrlv2
|
||||
* Ported from https://github.com/zynaddsubfx/zynaddsubfx
|
||||
*
|
||||
* Javier Nonis (https://github.com/jnonis) - 2024
|
||||
*/ |
||||
#ifndef _EFFECT_FLANGER_H |
||||
#define _EFFECT_FLANGER_H |
||||
|
||||
#include "effect_base.h" |
||||
#include "rkrlv2/Chorus.h" |
||||
|
||||
class AudioEffectFlanger : public AudioEffect |
||||
{ |
||||
public: |
||||
// ID must be unique for each AudioEffect
|
||||
static const unsigned ID = 12; |
||||
static constexpr const char* NAME = "Flanger"; |
||||
|
||||
enum Param |
||||
{ |
||||
BYPASS, |
||||
MIX, |
||||
PAN, |
||||
FL_FREQ, |
||||
FL_RND, |
||||
TYPE, |
||||
STDL, |
||||
FL_DEPTH, |
||||
DELAY, |
||||
FB, |
||||
LRCR, |
||||
MODE, |
||||
SUB, |
||||
AWESOME, |
||||
UNKNOWN |
||||
}; |
||||
|
||||
AudioEffectFlanger(float32_t samplerate) : AudioEffect(samplerate) |
||||
{ |
||||
this->chorus = new RKRChorus(0, 0, (double) samplerate); |
||||
this->init_params = true; |
||||
|
||||
this->chorus->setpreset(5); |
||||
} |
||||
|
||||
virtual ~AudioEffectFlanger() |
||||
{ |
||||
delete this->chorus; |
||||
} |
||||
|
||||
virtual unsigned getId() |
||||
{ |
||||
return AudioEffectFlanger::ID; |
||||
} |
||||
|
||||
virtual std::string getName() |
||||
{ |
||||
return AudioEffectFlanger::NAME; |
||||
} |
||||
|
||||
virtual void initializeSendFX() |
||||
{ |
||||
this->setParameter(AudioEffectFlanger::Param::MIX, 127); |
||||
} |
||||
|
||||
virtual void setParameter(unsigned param, unsigned value) |
||||
{ |
||||
switch (param) |
||||
{ |
||||
case AudioEffectFlanger::Param::BYPASS: |
||||
this->setBypass(value == 1); |
||||
this->chorus->cleanup(); |
||||
break; |
||||
case AudioEffectFlanger::Param::MIX: |
||||
this->chorus->changepar(0, value); |
||||
break; |
||||
case AudioEffectFlanger::Param::PAN: |
||||
this->chorus->changepar(1, value); |
||||
break; |
||||
case AudioEffectFlanger::Param::FL_FREQ: |
||||
this->chorus->changepar(2, value); |
||||
break; |
||||
case AudioEffectFlanger::Param::FL_RND: |
||||
this->chorus->changepar(3, value); |
||||
break; |
||||
case AudioEffectFlanger::Param::TYPE: |
||||
this->chorus->changepar(4, value); |
||||
break; |
||||
case AudioEffectFlanger::Param::STDL: |
||||
this->chorus->changepar(5, value); |
||||
break; |
||||
case AudioEffectFlanger::Param::FL_DEPTH: |
||||
this->chorus->changepar(6, value); |
||||
break; |
||||
case AudioEffectFlanger::Param::DELAY: |
||||
this->chorus->changepar(7, value); |
||||
break; |
||||
case AudioEffectFlanger::Param::FB: |
||||
this->chorus->changepar(8, value); |
||||
break; |
||||
case AudioEffectFlanger::Param::LRCR: |
||||
this->chorus->changepar(9, value); |
||||
break; |
||||
case AudioEffectFlanger::Param::MODE: |
||||
this->chorus->changepar(10, value); |
||||
break; |
||||
case AudioEffectFlanger::Param::SUB: |
||||
this->chorus->changepar(11, value); |
||||
break; |
||||
case AudioEffectFlanger::Param::AWESOME: |
||||
this->chorus->changepar(12, value); |
||||
break; |
||||
default: |
||||
break; |
||||
} |
||||
} |
||||
|
||||
virtual unsigned getParameter(unsigned param) |
||||
{ |
||||
switch (param) |
||||
{ |
||||
case AudioEffectFlanger::Param::BYPASS: |
||||
return this->getBypass() ? 1 : 0; |
||||
case AudioEffectFlanger::Param::MIX: |
||||
return this->chorus->getpar(0); |
||||
case AudioEffectFlanger::Param::PAN: |
||||
return this->chorus->getpar(1); |
||||
case AudioEffectFlanger::Param::FL_FREQ: |
||||
return this->chorus->getpar(2); |
||||
case AudioEffectFlanger::Param::FL_RND: |
||||
return this->chorus->getpar(3); |
||||
case AudioEffectFlanger::Param::TYPE: |
||||
return this->chorus->getpar(4); |
||||
case AudioEffectFlanger::Param::STDL: |
||||
return this->chorus->getpar(5); |
||||
case AudioEffectFlanger::Param::FL_DEPTH: |
||||
return this->chorus->getpar(6); |
||||
case AudioEffectFlanger::Param::DELAY: |
||||
return this->chorus->getpar(7); |
||||
case AudioEffectFlanger::Param::FB: |
||||
return this->chorus->getpar(8); |
||||
case AudioEffectFlanger::Param::LRCR: |
||||
return this->chorus->getpar(9); |
||||
case AudioEffectFlanger::Param::MODE: |
||||
return this->chorus->getpar(10); |
||||
case AudioEffectFlanger::Param::SUB: |
||||
return this->chorus->getpar(11); |
||||
case AudioEffectFlanger::Param::AWESOME: |
||||
return this->chorus->getpar(12); |
||||
default: |
||||
return 0; |
||||
} |
||||
} |
||||
|
||||
protected: |
||||
virtual size_t getParametersSize() |
||||
{ |
||||
return AudioEffectFlanger::Param::UNKNOWN; |
||||
} |
||||
|
||||
virtual void doProcess(const float32_t* inblockL, const float32_t* inblockR, float32_t* outblockL, float32_t* outblockR, uint16_t len) |
||||
{ |
||||
// LFO effects require period be set before setting other params
|
||||
if(this->init_params) |
||||
{ |
||||
this->chorus->PERIOD = len; |
||||
this->init_params = false; // so we only do this once
|
||||
} |
||||
|
||||
// now set out ports and global period size
|
||||
this->chorus->efxoutl = outblockL; |
||||
this->chorus->efxoutr = outblockR; |
||||
|
||||
//now run
|
||||
this->chorus->out((float*) inblockL, (float*) inblockR, len); |
||||
} |
||||
|
||||
private: |
||||
RKRChorus* chorus; |
||||
bool init_params; |
||||
}; |
||||
|
||||
#endif // _EFFECT_FLANGER_H
|
@ -0,0 +1,402 @@ |
||||
/*
|
||||
ZynAddSubFX - a software synthesizer |
||||
|
||||
Chorus.C - Chorus and Flange effects |
||||
Copyright (C) 2002-2005 Nasca Octavian Paul |
||||
Author: Nasca Octavian Paul |
||||
|
||||
Modified for rakarrack by Josep Andreu |
||||
|
||||
This program is free software; you can redistribute it and/or modify |
||||
it under the terms of version 2 of the GNU General Public License |
||||
as published by the Free Software Foundation. |
||||
|
||||
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 (version 2) for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License (version 2) |
||||
along with this program; if not, write to the Free Software Foundation, |
||||
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||||
|
||||
*/ |
||||
|
||||
#include <math.h> |
||||
#include "Chorus.h" |
||||
#include <stdio.h> |
||||
|
||||
RKRChorus::RKRChorus (float * efxoutl_, float * efxoutr_, double sample_rate) |
||||
{ |
||||
fSAMPLE_RATE = sample_rate; |
||||
efxoutl = efxoutl_; |
||||
efxoutr = efxoutr_; |
||||
dlk = 0; |
||||
drk = 0; |
||||
maxdelay = lrintf (MAX_CHORUS_DELAY / 1000.0 * (int)sample_rate); |
||||
delayl = new float[maxdelay]; |
||||
delayr = new float[maxdelay]; |
||||
lfo = new EffectLFO(sample_rate); |
||||
|
||||
float tmp = 0.08f; |
||||
ldelay = new delayline(tmp, 2, sample_rate); |
||||
rdelay = new delayline(tmp, 2, sample_rate); |
||||
ldelay -> set_averaging(0.005f); |
||||
rdelay -> set_averaging(0.005f); |
||||
ldelay->set_mix( 1.0f ); |
||||
rdelay->set_mix( 1.0f ); |
||||
|
||||
Ppreset = 0; |
||||
PERIOD = 256; //make our best guess for the initializing
|
||||
setpreset (Ppreset); |
||||
|
||||
oldr = 0.0f; |
||||
oldl = 0.0f; |
||||
awesome_mode = 0; |
||||
|
||||
lfo->effectlfoout (&lfol, &lfor); |
||||
dl2 = getdelay (lfol); |
||||
dr2 = getdelay (lfor); |
||||
cleanup (); |
||||
}; |
||||
|
||||
RKRChorus::~RKRChorus () |
||||
{ |
||||
delete delayl; |
||||
delete delayr; |
||||
delete ldelay; |
||||
delete rdelay; |
||||
delete lfo; |
||||
}; |
||||
|
||||
/*
|
||||
* get the delay value in samples; xlfo is the current lfo value |
||||
*/ |
||||
float RKRChorus::getdelay (float xlfo) |
||||
{ |
||||
float |
||||
result; |
||||
if (Pflangemode == 0) { |
||||
result = (delay + xlfo * depth) * fSAMPLE_RATE; |
||||
} else |
||||
result = 0; |
||||
|
||||
//check if it is too big delay(caused bu errornous setdelay() and setdepth()
|
||||
if ((result + 0.5) >= maxdelay) { |
||||
fprintf (stderr, "%s", |
||||
"WARNING: Chorus.C::getdelay(..) too big delay (see setdelay and setdepth funcs.)\n"); |
||||
printf ("%f %d\n", result, maxdelay); |
||||
result = (float) maxdelay - 1.0f; |
||||
}; |
||||
return (result); |
||||
}; |
||||
|
||||
/*
|
||||
* Apply the effect |
||||
*/ |
||||
void |
||||
RKRChorus::out (float * smpsl, float * smpsr, uint32_t period) |
||||
{ |
||||
unsigned int i; |
||||
float tmp; |
||||
float fPERIOD = period; |
||||
float outL, outR; |
||||
dl1 = dl2; |
||||
dr1 = dr2; |
||||
lfo->effectlfoout (&lfol, &lfor); |
||||
|
||||
float v1, v2; |
||||
if (outvolume < 0.5f) |
||||
{ |
||||
v1 = 1.0f; |
||||
v2 = outvolume * 2.0f; |
||||
} |
||||
else |
||||
{ |
||||
v1 = (1.0f - outvolume) * 2.0f; |
||||
v2 = 1.0f; |
||||
} |
||||
|
||||
if(awesome_mode) { //use interpolated delay line for better sound
|
||||
float tmpsub; |
||||
|
||||
dl2 = delay + lfol * depth; |
||||
dr2 = delay + lfor * depth; |
||||
if (Poutsub != 0) tmpsub = -1.0f; |
||||
else tmpsub = 1.0f; |
||||
|
||||
for (i = 0; i < period; i++) { |
||||
//Left
|
||||
mdel = (dl1 * (float)(period - i) + dl2 * (float)i) / fPERIOD; |
||||
tmp = smpsl[i] + oldl*fb; |
||||
outL = tmpsub*ldelay->delay(tmp, mdel, 0, 1, 0); |
||||
oldl = outL; |
||||
|
||||
//Right
|
||||
mdel = (dr1 * (float)(period - i) + dr2 * (float)i) / fPERIOD; |
||||
tmp = smpsr[i] + oldr*fb; |
||||
outR = tmpsub*rdelay->delay(tmp, mdel, 0, 1, 0); |
||||
oldr = outR; |
||||
|
||||
outL *= panning; |
||||
outR *= (1.0f - panning); |
||||
|
||||
efxoutl[i] = smpsl[i] * v1 + outL * v2; |
||||
efxoutr[i] = smpsr[i] * v1 + outR * v2; |
||||
} |
||||
|
||||
} else { |
||||
|
||||
dl2 = getdelay (lfol); |
||||
dr2 = getdelay (lfor); |
||||
for (i = 0; i < period; i++) { |
||||
float inl = smpsl[i]; |
||||
float inr = smpsr[i]; |
||||
//LRcross
|
||||
float l = inl; |
||||
float r = inr; |
||||
inl = l * (1.0f - lrcross) + r * lrcross; |
||||
inr = r * (1.0f - lrcross) + l * lrcross; |
||||
|
||||
//Left channel
|
||||
|
||||
//compute the delay in samples using linear interpolation between the lfo delays
|
||||
mdel = (dl1 * (float)(period - i) + dl2 * (float)i) / fPERIOD; |
||||
if (++dlk >= maxdelay) |
||||
dlk = 0; |
||||
tmp = (float) dlk - mdel + (float)maxdelay * 2.0f; //where should I get the sample from
|
||||
|
||||
F2I (tmp, dlhi); |
||||
dlhi %= maxdelay; |
||||
|
||||
dlhi2 = (dlhi - 1 + maxdelay) % maxdelay; |
||||
dllo = 1.0f - fmodf (tmp, 1.0f); |
||||
outL = delayl[dlhi2] * dllo + delayl[dlhi] * (1.0f - dllo); |
||||
delayl[dlk] = inl + outL * fb; |
||||
|
||||
//Right channel
|
||||
|
||||
//compute the delay in samples using linear interpolation between the lfo delays
|
||||
mdel = (dr1 * (float)(period - i) + dr2 * (float)i) / fPERIOD; |
||||
if (++drk >= maxdelay) |
||||
drk = 0; |
||||
tmp = (float)drk - mdel + (float)maxdelay * 2.0f; //where should I get the sample from
|
||||
|
||||
F2I (tmp, dlhi); |
||||
dlhi %= maxdelay; |
||||
|
||||
dlhi2 = (dlhi - 1 + maxdelay) % maxdelay; |
||||
dllo = 1.0f - fmodf (tmp, 1.0f); |
||||
outR = delayr[dlhi2] * dllo + delayr[dlhi] * (1.0f - dllo); |
||||
delayr[dlk] = inr + outR * fb; |
||||
|
||||
if (Poutsub != 0) { |
||||
outL *= -1.0f; |
||||
outR *= -1.0f; |
||||
} |
||||
|
||||
outL *= panning; |
||||
outR *= (1.0f - panning); |
||||
|
||||
efxoutl[i] = smpsl[i] * v1 + outL * v2; |
||||
efxoutr[i] = smpsr[i] * v1 + outR * v2; |
||||
} |
||||
} //end awesome_mode test
|
||||
}; |
||||
|
||||
/*
|
||||
* Cleanup the effect |
||||
*/ |
||||
void |
||||
RKRChorus::cleanup () |
||||
{ |
||||
for (int i = 0; i < maxdelay; i++) { |
||||
delayl[i] = 0.0; |
||||
delayr[i] = 0.0; |
||||
}; |
||||
|
||||
}; |
||||
|
||||
/*
|
||||
* Parameter control |
||||
*/ |
||||
void |
||||
RKRChorus::setdepth (int Pdepth) |
||||
{ |
||||
this->Pdepth = Pdepth; |
||||
depth = (powf (8.0f, ((float)Pdepth / 127.0f) * 2.0f) - 1.0f) / 1000.0f; //seconds
|
||||
}; |
||||
|
||||
void |
||||
RKRChorus::setdelay (int Pdelay) |
||||
{ |
||||
this->Pdelay = Pdelay; |
||||
delay = (powf (10.0f, ((float)Pdelay / 127.0f) * 2.0f) - 1.0f) / 1000.0f; //seconds
|
||||
}; |
||||
|
||||
void |
||||
RKRChorus::setfb (int Pfb) |
||||
{ |
||||
this->Pfb = Pfb; |
||||
fb = ((float)Pfb - 64.0f) / 64.1f; |
||||
}; |
||||
|
||||
void |
||||
RKRChorus::setvolume (int Pvolume) |
||||
{ |
||||
this->Pvolume = Pvolume; |
||||
outvolume = (float)Pvolume / 127.0f; |
||||
}; |
||||
|
||||
void |
||||
RKRChorus::setpanning (int Ppanning) |
||||
{ |
||||
this->Ppanning = Ppanning; |
||||
panning = ((float)Ppanning +.5f) / 127.0f; |
||||
}; |
||||
|
||||
void |
||||
RKRChorus::setlrcross (int Plrcross) |
||||
{ |
||||
this->Plrcross = Plrcross; |
||||
lrcross = (float)Plrcross / 127.0f; |
||||
}; |
||||
|
||||
void |
||||
RKRChorus::setpreset (int npreset) |
||||
{ |
||||
const int PRESET_SIZE = 12; |
||||
const int NUM_PRESETS = 10; |
||||
int presets[NUM_PRESETS][PRESET_SIZE] = { |
||||
//Chorus1
|
||||
{64, 64, 33, 0, 0, 90, 40, 85, 64, 119, 0, 0}, |
||||
//Chorus2
|
||||
{64, 64, 19, 0, 0, 98, 56, 90, 64, 19, 0, 0}, |
||||
//Chorus3
|
||||
{64, 64, 7, 0, 1, 42, 97, 95, 90, 127, 0, 0}, |
||||
//Celeste1
|
||||
{64, 64, 1, 0, 0, 42, 115, 18, 90, 127, 0, 0}, |
||||
//Celeste2
|
||||
{64, 64, 7, 117, 0, 50, 115, 9, 31, 127, 0, 1}, |
||||
//Flange1
|
||||
{64, 64, 39, 0, 0, 60, 23, 3, 62, 0, 0, 0}, |
||||
//Flange2
|
||||
{64, 64, 9, 34, 1, 40, 35, 3, 109, 0, 0, 0}, |
||||
//Flange3
|
||||
{64, 64, 31, 34, 1, 94, 35, 3, 54, 0, 0, 1}, |
||||
//Flange4
|
||||
{64, 64, 14, 0, 1, 62, 12, 19, 97, 0, 0, 0}, |
||||
//Flange5
|
||||
{64, 64, 34, 105, 0, 24, 39, 19, 17, 0, 0, 1} |
||||
}; |
||||
|
||||
|
||||
for (int n = 0; n < PRESET_SIZE; n++) { |
||||
changepar (n, presets[npreset][n]); |
||||
} |
||||
Ppreset = npreset; |
||||
}; |
||||
|
||||
|
||||
void |
||||
RKRChorus::changepar (int npar, int value) |
||||
{ |
||||
switch (npar) { |
||||
case 0: |
||||
setvolume (value); |
||||
break; |
||||
case 1: |
||||
setpanning (value); |
||||
break; |
||||
case 2: |
||||
lfo->Pfreq = value; |
||||
lfo->updateparams (PERIOD); |
||||
break; |
||||
case 3: |
||||
lfo->Prandomness = value; |
||||
lfo->updateparams (PERIOD); |
||||
break; |
||||
case 4: |
||||
lfo->PLFOtype = value; |
||||
lfo->updateparams (PERIOD); |
||||
break; |
||||
case 5: |
||||
lfo->Pstereo = value; |
||||
lfo->updateparams (PERIOD); |
||||
break; |
||||
case 6: |
||||
setdepth (value); |
||||
break; |
||||
case 7: |
||||
setdelay (value); |
||||
break; |
||||
case 8: |
||||
setfb (value); |
||||
break; |
||||
case 9: |
||||
setlrcross (value); |
||||
break; |
||||
case 10: |
||||
if (value > 1) |
||||
value = 1; |
||||
Pflangemode = value; |
||||
break; |
||||
case 11: |
||||
if (value > 1) |
||||
value = 1; |
||||
Poutsub = value; |
||||
break; |
||||
case 12: |
||||
awesome_mode = value; |
||||
break; |
||||
}; |
||||
}; |
||||
|
||||
int |
||||
RKRChorus::getpar (int npar) |
||||
{ |
||||
switch (npar) { |
||||
case 0: |
||||
return (Pvolume); |
||||
break; |
||||
case 1: |
||||
return (Ppanning); |
||||
break; |
||||
case 2: |
||||
return (lfo->Pfreq); |
||||
break; |
||||
case 3: |
||||
return (lfo->Prandomness); |
||||
break; |
||||
case 4: |
||||
return (lfo->PLFOtype); |
||||
break; |
||||
case 5: |
||||
return (lfo->Pstereo); |
||||
break; |
||||
case 6: |
||||
return (Pdepth); |
||||
break; |
||||
case 7: |
||||
return (Pdelay); |
||||
break; |
||||
case 8: |
||||
return (Pfb); |
||||
break; |
||||
case 9: |
||||
return (Plrcross); |
||||
break; |
||||
case 10: |
||||
return (Pflangemode); |
||||
break; |
||||
case 11: |
||||
return (Poutsub); |
||||
break; |
||||
case 12: |
||||
return (awesome_mode); |
||||
break; |
||||
default: |
||||
return (0); |
||||
}; |
||||
|
||||
}; |
@ -0,0 +1,89 @@ |
||||
/*
|
||||
ZynAddSubFX - a software synthesizer |
||||
|
||||
Chorus.h - Chorus and Flange effects |
||||
Copyright (C) 2002-2005 Nasca Octavian Paul |
||||
Author: Nasca Octavian Paul |
||||
|
||||
Modified for rakarrack by Josep Andreu |
||||
|
||||
This program is free software; you can redistribute it and/or modify |
||||
it under the terms of version 2 of the GNU General Public License |
||||
as published by the Free Software Foundation. |
||||
|
||||
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 (version 2) for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License (version 2) |
||||
along with this program; if not, write to the Free Software Foundation, |
||||
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||||
|
||||
*/ |
||||
|
||||
#ifndef RKR_CHORUS_H |
||||
#define RKR_CHORUS_H |
||||
#include "global.h" |
||||
#include "EffectLFO.h" |
||||
#include "delayline.h" |
||||
|
||||
class RKRChorus |
||||
{ |
||||
|
||||
public: |
||||
RKRChorus (float * efxoutl_, float * efxoutr_, double sample_rate); |
||||
~RKRChorus (); |
||||
void out (float * smpsl, float * smpsr, uint32_t period); |
||||
void setpreset (int npreset); |
||||
void changepar (int npar, int value); |
||||
int getpar (int npar); |
||||
void cleanup (); |
||||
|
||||
|
||||
int Ppreset; |
||||
float *efxoutl; |
||||
float *efxoutr; |
||||
float outvolume; //this is the volume of effect and is public because need it in system effect. The out volume of s
|
||||
|
||||
uint32_t PERIOD; |
||||
|
||||
private: |
||||
//Parametrii Chorus
|
||||
EffectLFO* lfo; //lfo-ul chorus
|
||||
int Pvolume; |
||||
int Ppanning; |
||||
int Pdepth; //the depth of the Chorus(ms)
|
||||
int Pdelay; //the delay (ms)
|
||||
int Pfb; //feedback
|
||||
int Plrcross; //feedback
|
||||
int Pflangemode; //how the LFO is scaled, to result chorus or flange
|
||||
int Poutsub; //if I wish to substract the output instead of the adding it
|
||||
|
||||
|
||||
//Control Parametrii
|
||||
void setvolume (int Pvolume); |
||||
void setpanning (int Ppanning); |
||||
void setdepth (int Pdepth); |
||||
void setdelay (int Pdelay); |
||||
void setfb (int Pfb); |
||||
void setlrcross (int Plrcross); |
||||
|
||||
//Valorile interne
|
||||
int maxdelay; |
||||
int dlk, drk, dlhi, dlhi2; |
||||
int awesome_mode; |
||||
|
||||
float depth, delay, fb, lrcross, panning, oldr, oldl; |
||||
float dl1, dl2, dr1, dr2, lfol, lfor; |
||||
float *delayl, *delayr; |
||||
float getdelay (float xlfo); |
||||
float dllo, mdel; |
||||
|
||||
class delayline *ldelay, *rdelay; |
||||
|
||||
float fSAMPLE_RATE; |
||||
|
||||
}; |
||||
|
||||
#endif |
@ -0,0 +1,428 @@ |
||||
/*
|
||||
Author: Ryan BillingV |
||||
|
||||
This program is free software; you can redistribute it and/or modify |
||||
it under the terms of version 3 of the GNU General Public License |
||||
as published by the Free Software Foundation. |
||||
|
||||
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 (version 2) for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License (version 2) |
||||
along with this program; if not, write to the Free Software Foundation, |
||||
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||||
*/ |
||||
#include "delayline.h" |
||||
#include <arm_math.h> |
||||
#include <stdlib.h> |
||||
#include "f_sin.h" |
||||
|
||||
delayline::delayline(float maxdelay, int maxtaps_, double samplerate) |
||||
{ |
||||
fSAMPLE_RATE = samplerate; |
||||
maxtaps = maxtaps_; |
||||
maxtime = fSAMPLE_RATE * maxdelay; |
||||
maxdelaysmps = fSAMPLE_RATE * lrintf(ceilf(maxdelay)); |
||||
ringbuffer = (float *) malloc(sizeof(float) * maxdelaysmps); |
||||
avgtime = (float *) malloc(sizeof(float) * maxtaps); |
||||
time = (float *) malloc(sizeof(float) * maxtaps); |
||||
xfade = (float *) malloc(sizeof(float) * maxtaps); |
||||
cur_smps = (float *) malloc(sizeof(float) * maxtaps); |
||||
oldtime = (int *) malloc(sizeof(int) * maxtaps); |
||||
newtime = (int *) malloc(sizeof(int) * maxtaps); |
||||
crossfade = (int *) malloc(sizeof(int) * maxtaps); |
||||
|
||||
pstruct = (phasevars *) malloc(sizeof(struct phasevars) * maxtaps); |
||||
tapstruct = (tapvars *) malloc(sizeof(struct tapvars) * maxtaps); |
||||
|
||||
zero_index = 0; |
||||
tap = 0; |
||||
rvptr = 0; |
||||
distance = 0; |
||||
|
||||
mix = 0.5f; |
||||
imix = 0.5f; |
||||
|
||||
float dt = 1.0f / fSAMPLE_RATE; |
||||
alpha = dt / (0.15f + dt); |
||||
beta = 1.0f - alpha; //time change smoothing parameters
|
||||
|
||||
cleanup(); |
||||
}; |
||||
|
||||
delayline::~delayline() |
||||
{ |
||||
free(ringbuffer); |
||||
free(avgtime); |
||||
free(time); |
||||
free(xfade); |
||||
free(cur_smps); |
||||
free(oldtime); |
||||
free(newtime); |
||||
free(crossfade); |
||||
free(pstruct); |
||||
free(tapstruct); |
||||
} |
||||
|
||||
void |
||||
delayline::cleanup() |
||||
{ |
||||
zero_index = 0; |
||||
int i, k; |
||||
for (i = 0; i < maxdelaysmps; i++) |
||||
ringbuffer[i] = 0.0; |
||||
for (i = 0; i < maxtaps; i++) { |
||||
avgtime[i] = 0.0; |
||||
time[i] = 0.0; |
||||
for (k = 0; k < 4; k++) { |
||||
pstruct[i].yn1[k] = 0.0f; |
||||
pstruct[i].xn1[k] = 0.0f; |
||||
pstruct[i].gain[k] = 0.0f; |
||||
tapstruct[i].lvars[k] = 0.0f; |
||||
tapstruct[i].ivars[k] = 0.0f; |
||||
tapstruct[i].fracts[k] = 0.0f; |
||||
|
||||
} |
||||
} |
||||
|
||||
for (i = 0; i < maxtaps; i++) { |
||||
avgtime[i] = 0.0f; |
||||
newtime[i] = 0; |
||||
oldtime[i] = 0; |
||||
xfade[i] = 0.0f; |
||||
crossfade[i] = 0; |
||||
cur_smps[i] = 0.0f; |
||||
|
||||
} |
||||
|
||||
set_averaging(0.25f); |
||||
|
||||
}; |
||||
|
||||
|
||||
float |
||||
delayline::delay_simple(float smps, float time_, int tap_, int touch, |
||||
int reverse) |
||||
{ |
||||
int dlytime = 0; |
||||
int bufptr = 0; |
||||
|
||||
if (tap_ >= maxtaps) |
||||
tap = 0; |
||||
else |
||||
tap = tap_; |
||||
|
||||
time[tap] = fSAMPLE_RATE * time_; //convert to something that can be used as a delay line index
|
||||
|
||||
//Do some checks to keep things in bounds
|
||||
if (time[tap] > maxtime) |
||||
time[tap] = maxtime; |
||||
dlytime = lrintf(time[tap]); |
||||
|
||||
if (crossfade[tap]) { |
||||
xfade[tap] += fadetime; |
||||
if (xfade[tap] >= 1.0f) { |
||||
xfade[tap] = 0.0f; |
||||
crossfade[tap] = 0; |
||||
oldtime[tap] = newtime[tap]; |
||||
newtime[tap] = dlytime; |
||||
} |
||||
} |
||||
|
||||
if (crossfade[tap] == 0) { |
||||
if (dlytime != oldtime[tap]) { |
||||
crossfade[tap] = 1; |
||||
xfade[tap] = 0.0f; |
||||
oldtime[tap] = newtime[tap]; |
||||
newtime[tap] = dlytime; |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
dlytime = newtime[tap]; |
||||
|
||||
//now put in the sample
|
||||
if (touch) { //make touch zero if you only want to pull samples off the delay line
|
||||
ringbuffer[zero_index] = smps; |
||||
if (--zero_index < 0) |
||||
zero_index = maxdelaysmps - 1; |
||||
} |
||||
//if we want reverse delay
|
||||
//you need to call this every time to keep the buffers up to date, and it's on a different tap
|
||||
if (reverse) { |
||||
|
||||
bufptr = (dlytime + zero_index); //this points to the sample we want to get
|
||||
if (bufptr >= maxdelaysmps) |
||||
bufptr -= maxdelaysmps; |
||||
if (++rvptr > maxdelaysmps) |
||||
rvptr = 0; |
||||
|
||||
if (bufptr > zero_index) { |
||||
if (rvptr > bufptr) { |
||||
rvptr = zero_index; |
||||
distance = 0; |
||||
} else |
||||
distance = rvptr - zero_index; |
||||
} else if ((bufptr < zero_index) && (rvptr < zero_index)) { |
||||
if (rvptr > bufptr) { |
||||
rvptr = zero_index; |
||||
distance = 0; |
||||
} else |
||||
distance = |
||||
rvptr + maxdelaysmps - zero_index; |
||||
} else |
||||
distance = rvptr - zero_index; |
||||
|
||||
|
||||
bufptr = rvptr; //this points to the sample we want to get
|
||||
|
||||
} else { |
||||
bufptr = (dlytime + zero_index); //this points to the sample we want to get
|
||||
if (bufptr >= maxdelaysmps) |
||||
bufptr -= maxdelaysmps; |
||||
} |
||||
|
||||
int oldnewdiff = newtime[tap] - oldtime[tap]; |
||||
int tmpptr = 0; |
||||
if (crossfade[tap] != 0) { |
||||
tmpptr = bufptr + oldnewdiff; |
||||
if (tmpptr >= maxdelaysmps) |
||||
tmpptr -= maxdelaysmps; |
||||
else if (tmpptr <= 0) |
||||
tmpptr += maxdelaysmps; |
||||
return (xfade[tap] * ringbuffer[bufptr] + (1.0f - xfade[tap]) * ringbuffer[tmpptr]); //fade nicely to new tap
|
||||
} else |
||||
return (ringbuffer[bufptr]); |
||||
|
||||
}; |
||||
|
||||
/*
|
||||
* Interpolated delay line |
||||
*/ |
||||
|
||||
float |
||||
delayline::delay(float smps, float time_, int tap_, int touch, |
||||
int reverse) |
||||
{ |
||||
int dlytime = 0; |
||||
int bufptr = 0; |
||||
|
||||
tap = fabs(tap_); |
||||
if (tap >= maxtaps) |
||||
tap = 0; |
||||
|
||||
if (reverse) avgtime[tap] = alpha * 2.0*time_ + beta * avgtime[tap]; //smoothing the rate of time change
|
||||
else avgtime[tap] = alpha * time_ + beta * avgtime[tap]; //smoothing the rate of time change
|
||||
time[tap] = 1.0f + fSAMPLE_RATE * avgtime[tap]; //convert to something that can be used as a delay line index
|
||||
|
||||
//Do some checks to keep things in bounds
|
||||
if (time[tap] > maxtime) |
||||
time[tap] = maxtime; |
||||
if (time[tap] < 0.0f) |
||||
time[tap] = 0.0f; |
||||
|
||||
float fract = (time[tap] - floorf(time[tap])); //compute fractional delay
|
||||
dlytime = lrintf(floorf(time[tap])); |
||||
|
||||
//now put in the sample
|
||||
if (touch) { //make touch zero if you only want to pull samples off the delay line
|
||||
cur_smps[tap] = ringbuffer[zero_index] = smps; |
||||
if (--zero_index < 0) |
||||
zero_index = maxdelaysmps - 1; |
||||
} |
||||
//if we want reverse delay
|
||||
//you need to call this every time to keep the buffers up to date, and it's on a different tap
|
||||
if (reverse) { |
||||
|
||||
bufptr = (dlytime + zero_index); //this points to the sample we want to get
|
||||
if (bufptr >= maxdelaysmps) |
||||
bufptr -= maxdelaysmps; |
||||
if (++rvptr > maxdelaysmps) |
||||
rvptr = 0; |
||||
|
||||
if (bufptr > zero_index) { |
||||
if (rvptr > bufptr) { |
||||
rvptr = zero_index; |
||||
distance = 0; |
||||
} else |
||||
distance = rvptr - zero_index; |
||||
} else if ((bufptr < zero_index) && (rvptr < zero_index)) { |
||||
if (rvptr > bufptr) { |
||||
rvptr = zero_index; |
||||
distance = 0; |
||||
} else |
||||
distance = |
||||
rvptr + maxdelaysmps - zero_index; |
||||
} else |
||||
distance = rvptr - zero_index; |
||||
|
||||
bufptr = rvptr; //this points to the sample we want to get
|
||||
|
||||
} else { |
||||
bufptr = (dlytime + zero_index); //this points to the sample we want to get
|
||||
if (bufptr >= maxdelaysmps) |
||||
bufptr -= maxdelaysmps; |
||||
} |
||||
|
||||
tapstruct[tap].lvars[3] = tapstruct[tap].lvars[2]; |
||||
tapstruct[tap].lvars[2] = tapstruct[tap].lvars[1]; |
||||
tapstruct[tap].lvars[1] = tapstruct[tap].lvars[0]; |
||||
tapstruct[tap].lvars[0] = ringbuffer[bufptr]; |
||||
|
||||
tapstruct[tap].ivars[3] = tapstruct[tap].ivars[2]; |
||||
tapstruct[tap].ivars[2] = tapstruct[tap].ivars[1]; |
||||
tapstruct[tap].ivars[1] = tapstruct[tap].ivars[0]; |
||||
tapstruct[tap].ivars[0] = cur_smps[tap]; |
||||
|
||||
tapstruct[tap].fracts[3] = tapstruct[tap].fracts[2]; |
||||
tapstruct[tap].fracts[2] = tapstruct[tap].fracts[1]; |
||||
tapstruct[tap].fracts[1] = tapstruct[tap].fracts[0]; |
||||
tapstruct[tap].fracts[0] = fract; |
||||
|
||||
float tmpfrac = |
||||
0.5f * (tapstruct[tap].fracts[1] + tapstruct[tap].fracts[2]); |
||||
//float itmpfrac = 1.0f - tmpfrac;
|
||||
float itmpfrac = 0.5f; //it was the original approximation
|
||||
|
||||
float output = |
||||
mix * lagrange(tapstruct[tap].ivars[0], |
||||
tapstruct[tap].ivars[1], |
||||
tapstruct[tap].ivars[2], |
||||
tapstruct[tap].ivars[3], |
||||
itmpfrac) + imix * lagrange(tapstruct[tap].lvars[0], |
||||
tapstruct[tap].lvars[1], |
||||
tapstruct[tap].lvars[2], |
||||
tapstruct[tap].lvars[3], |
||||
tmpfrac); |
||||
|
||||
return (output); |
||||
|
||||
}; |
||||
|
||||
|
||||
inline float |
||||
delayline::get_phaser(float smps, float lfo, int tap_, int stg) |
||||
{ |
||||
float delta = lfo; |
||||
if (delta > 1.0f) |
||||
delta = 1.0f; |
||||
if (delta < 0.0f) |
||||
delta = 0.0f; |
||||
tap = tap_; |
||||
|
||||
pstruct[tap].gain[0] = (1.0f - delta) / (1.0f + delta); |
||||
pstruct[tap].stages = stg; |
||||
|
||||
return (phaser(smps)); |
||||
}; |
||||
|
||||
|
||||
inline float |
||||
delayline::phaser(float fxn) //All-pass interpolation
|
||||
{ |
||||
|
||||
float xn = fxn; |
||||
for (int st = 0; st < pstruct[tap].stages; st++) { |
||||
pstruct[tap].yn1[st] = |
||||
pstruct[tap].xn1[st] - pstruct[tap].gain[st] * (xn + |
||||
pstruct |
||||
[tap]. |
||||
yn1 |
||||
[st]); |
||||
pstruct[tap].xn1[st] = xn; |
||||
xn = pstruct[tap].yn1[st]; |
||||
} |
||||
|
||||
return xn; |
||||
|
||||
}; |
||||
|
||||
/* Unfactored WYSIWYG implementation of order=4 Lagrange interpolation polynomial
|
||||
inline float |
||||
delayline::lagrange(float p0, float p1, float p2, float p3, float x_) |
||||
{ |
||||
float x = x_; |
||||
|
||||
float xm2xm1 = (x - 1.0f)*(x - 2.0f); |
||||
x = -p0*x*xm2xm1*0.16666666667f + p1*(x + 1.0f)*xm2xm1*0.5f - p2*x*(x + 1.0f)*(x - 2.0f)*0.5f + p3*x*(x + 1.0f)*(x - 1.0f)*0.16666666667f; |
||||
|
||||
return x; |
||||
}; |
||||
*/ |
||||
|
||||
inline float |
||||
delayline::lagrange(float p0, float p1, float p2, float p3, float x_) |
||||
{ |
||||
//factored version for less multiplies
|
||||
float x = x_; |
||||
|
||||
const float c0p0 = -0.16666666667f * p0; |
||||
const float c1p1 = 0.5f * p1; |
||||
const float c2p2 = -0.5f * p2; |
||||
const float c3p3 = 0.16666666667f * p3; |
||||
|
||||
const float a = c3p3 + c2p2 + c1p1 + c0p0; |
||||
const float b = -3.0f * c0p0 - p1 - c2p2; |
||||
const float c = 2.0f * c0p0 - c1p1 + p2 - c3p3; |
||||
const float d = p1; |
||||
|
||||
x = ((a * x + b) * x + c) * x + d; |
||||
return x; |
||||
}; |
||||
|
||||
inline float |
||||
delayline::spline(float p0, float p1, float p2, float p3, float x_) |
||||
{ |
||||
//does not produce any better results than lagrange(), but has less multiplies
|
||||
//seems to produce discontinuities on a low level (-48dB), so not a preferred algorithm
|
||||
|
||||
float x = x_; |
||||
|
||||
float const c0 = p1; |
||||
float const c1 = 0.5f * (p2 - p0); |
||||
float const c2 = p0 - 2.5f * p1 + 2.0f * p2 - 0.5f * p3; |
||||
float const c3 = 0.5f * (p3 - p0) + 1.5f * (p1 - p2); |
||||
|
||||
return (((c3 * x + c2) * x + c1) * x + c0); |
||||
} |
||||
|
||||
void delayline::set_averaging(float tc_) |
||||
{ |
||||
float tc = tc_; |
||||
float dt = 1.0f / fSAMPLE_RATE; |
||||
fadetime = dt * tc; |
||||
alpha = dt / (tc + dt); |
||||
beta = 1.0f - alpha; //time change smoothing parameters
|
||||
}; |
||||
|
||||
void delayline::set_mix(float mix_) //mix amount of dry w/ wet
|
||||
{ |
||||
mix = fabs(mix_); |
||||
imix = 1.0f - mix; |
||||
if(mix_<0.0f) imix*=-1.0f; |
||||
} |
||||
|
||||
float delayline::envelope() |
||||
{ |
||||
float fdist = ((float) distance) / time[tap]; |
||||
if (fdist > 0.5f) { |
||||
if (fdist <= 1.0f) |
||||
fdist = 1.0f - fdist; |
||||
else |
||||
fdist = 0.0f; |
||||
} |
||||
|
||||
if (fdist <= 0.125f) { |
||||
fdist = |
||||
1.0f - f_sin(PI * fdist * 4.0f + 1.5707963267949f); |
||||
} else |
||||
fdist = 1.0f; |
||||
return fdist; |
||||
|
||||
}; |
@ -0,0 +1,99 @@ |
||||
/*
|
||||
Rakarrack Guitar FX |
||||
|
||||
delayline.h - An interpolated delay line. Input new sample and desired delay time for output. |
||||
Copyright (C) 2010 Ryan Billing |
||||
Author: Ryan Billing |
||||
|
||||
This program is free software; you can redistribute it and/or modify |
||||
it under the terms of version 3 of the GNU General Public License |
||||
as published by the Free Software Foundation. |
||||
|
||||
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 (version 2) for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License (version 2) |
||||
along with this program; if not, write to the Free Software Foundation, |
||||
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||||
|
||||
*/ |
||||
|
||||
#ifndef DLINE_H |
||||
#define DLINE_H |
||||
|
||||
#include "global.h" |
||||
|
||||
|
||||
class delayline |
||||
{ |
||||
public: |
||||
delayline(float maxdelay, int maxtaps_, double samplerate); //construct the object with intended maximum delay time
|
||||
~delayline(); |
||||
void cleanup(); |
||||
void set_averaging(float tc_); //use this if you want the time change averaging longer or shorter
|
||||
void set_mix(float mix_); |
||||
float envelope(); |
||||
|
||||
//Delay line simple use case is this:
|
||||
// mydelayed_sample = mydelayline->delay(input, delay_time, 0, 1, 0)
|
||||
float delay(float smps, float time, int tap_, int touch, int reverse); //interpolating delay
|
||||
float delay_simple(float smps, float time, int tap_, int touch, int reverse); //simple ring buffer
|
||||
//smps - The current input sample
|
||||
//time - amount of delay you want
|
||||
//mix - for chorus or flanger how much of original to mix
|
||||
//tap_ - if multi-tap delay, this is the tap you want to access. Usually set touch=0
|
||||
//when accessing multiple taps after input.
|
||||
//touch -set to zero if you want smps written to the delay line. Set nonzero if you only want to read out of delay line
|
||||
//reverse -set to nonzero if you want to play the samples in the delay line backward.
|
||||
//Typically you want to apply an envelope to eliminate the click at wraparound from old to recent.
|
||||
//in this case, multiply the sample by the envelope:
|
||||
// myreversedelayedsample = mydelayline->delay(input, delay_time, 0, 1, 1) * mydelayline->envelope;
|
||||
|
||||
float get_phaser(float smps, float lfo, int tap_, int stg); //Allows you to use phaser directly without delay line
|
||||
//smps - input sample
|
||||
//lfo - ranges from 0 to 1
|
||||
//tap - allows multiple separate phasers with the same object
|
||||
//stg - number of phase stages to process
|
||||
|
||||
private: |
||||
int zero_index; |
||||
int tap, maxtaps; |
||||
float maxtime; |
||||
long maxdelaysmps; |
||||
int rvptr, distance; |
||||
|
||||
float *avgtime, *time; //keeping it from changing too quickly
|
||||
float tconst, alpha, beta, mix, imix; //don't allow change in delay time exceed 1 sample at a time
|
||||
|
||||
int *newtime; |
||||
int *oldtime; |
||||
int *crossfade; |
||||
float *xfade, fadetime; |
||||
float *cur_smps; |
||||
|
||||
struct phasevars { |
||||
float yn1[4]; |
||||
float xn1[4]; |
||||
float gain[4]; |
||||
int stages; |
||||
} *pstruct; |
||||
|
||||
float phaser(float fxn); |
||||
float lagrange(float p0, float p1, float p2, float p3, float x_); |
||||
float spline(float p0, float p1, float p2, float p3, float x_); |
||||
|
||||
struct tapvars { |
||||
float lvars[4]; |
||||
float ivars[4]; |
||||
float fracts[4]; |
||||
} *tapstruct; |
||||
|
||||
float *ringbuffer; |
||||
|
||||
float fSAMPLE_RATE; |
||||
|
||||
}; |
||||
|
||||
#endif |
Loading…
Reference in new issue