/* For Copyright and License see LICENSE.txt and COPYING.txt in the root directory */
package com.nerdscentral.audio.pitch;
import java.util.List;
import com.nerdscentral.audio.core.SFConstants;
import com.nerdscentral.audio.core.SFSignal;
import com.nerdscentral.sython.Caster;
import com.nerdscentral.sython.SFPL_Operator;
import com.nerdscentral.sython.SFPL_RuntimeException;
public class SF_ShapedLadder implements SFPL_Operator
{
/**
*
*/
private static final long serialVersionUID = 1L;
private class InnerFilter
{
double cutoff;
double res;
double fs;
double y1, y2, y3, y4;
double oldx;
double oldy1, oldy2, oldy3;
double x;
double r;
double p;
double k;
InnerFilter()
{
fs = SFConstants.SAMPLE_RATE;
init();
}
void init()
{
// initialize values
y1 = y2 = y3 = y4 = oldx = oldy1 = oldy2 = oldy3 = 0;
calc();
}
void calc()
{
double f = (cutoff + cutoff) / fs; // [0 - 1]
p = f * (1.8f - 0.8f * f);
k = p + p - 1.f;
double t = (1.f - p) * 1.386249f;
double t2 = 12.f + t * t;
r = res * (t2 + 6.f * t) / (t2 - 6.f * t);
}
double process(double input)
{
// process input
x = input - r * y4;
// Four cascaded onepole filters (bilinear transform)
y1 = x * p + oldx * p - k * y1;
y2 = y1 * p + oldy1 * p - k * y2;
y3 = y2 * p + oldy2 * p - k * y3;
y4 = y3 * p + oldy3 * p - k * y4;
// Clipper band limited sigmoid
y4 -= (y4 * y4 * y4) / 6.f;
oldx = x;
oldy1 = y1;
oldy2 = y2;
oldy3 = y3;
return y4;
}
@SuppressWarnings("unused")
double getCutoff()
{
return cutoff;
}
void setCutoff(double c)
{
// Only recalculate when the change is bigger than
// one cent
double cc = c / cutoff;
if (cc < 1.0) cc = 1.0 / cc;
if (cc > 1.0005777895)
{
// System.out.println("Recomputing c " + c);
cutoff = c;
calc();
}
}
@SuppressWarnings("unused")
double getRes()
{
return res;
}
void setRes(double r1)
{
// Only recalculate when the change is bigger than
// one cent
double rr = r1 / cutoff;
if (rr < 1.0) rr = 1.0 / r1;
if (rr > 1.0005777895)
{
res = r1;
calc();
}
}
}
@Override
public String Word()
{
return Messages.getString("SF_Ladder.0"); //$NON-NLS-1$
}
@Override
public Object Interpret(Object input) throws SFPL_RuntimeException
{
List<Object> in = Caster.makeBunch(input);
SFSignal dataIn = Caster.makeSFSignal(in.get(0));
SFSignal shape = Caster.makeSFSignal(in.get(1));
SFSignal resonance = Caster.makeSFSignal(in.get(2));
SFSignal ret = dataIn.replicateEmpty();
int length = dataIn.getLength();
if (shape.getLength() != length || resonance.getLength() != length)
{
throw new SFPL_RuntimeException(Messages.getString("SF_Ladder.1")); //$NON-NLS-1$
}
InnerFilter filter = new InnerFilter();
filter.init();
for (int index = 0; index < length; ++index)
{
filter.setRes(resonance.getSample(index));
filter.setCutoff(shape.getSample(index));
ret.setSample(index, filter.process(dataIn.getSample(index)));
}
return ret;
}
}