package micromod.resamplers;
/**
Table based multipoint FIR resampler.
This took many, many hours to debug! Note to self: No more 12-hour sleep ins.
*/
public class FIRResampler implements Resampler {
protected static final int FIXED_POINT_SHIFT=12;
protected static final int FIXED_POINT_ONE=1<<FIXED_POINT_SHIFT;
protected static final int FIXED_POINT_BITMASK=FIXED_POINT_ONE-1;
protected static final int FIXED_POINT_CONV=16-FIXED_POINT_SHIFT;
protected int points;
protected short[] sinc;
/**
Constructor.
@param numPoints The higher the better but more cpu and memory intensive. Even number, minimum 2.
*/
public FIRResampler( int numPoints ) {
points = numPoints>>1;
if(points<1) points = 1;
genSinc();
System.out.println(" Multipoint FIR Resampler 0.3 Initialised. Using "+(points<<1)+" points.");
}
/**
Do resampling.
*/
public void resample( short[] inputBuf, int samplePos, int subSamplePos, int step, int subStep,
short[] outputBuf, int position, int length ) {
int issp, spos, amp, n, end=position+length;
if(step==0) {
// Convert 16 bit fixed point to 12 bit.
subSamplePos>>=FIXED_POINT_CONV;
subStep>>=FIXED_POINT_CONV;
while( position < end ) {
amp = 0;
issp = FIXED_POINT_ONE-subSamplePos;
spos = samplePos-points+1;
for( n=points-1; n>=0; n--,spos++ )
amp += inputBuf[spos]*sinc[(n<<FIXED_POINT_SHIFT)+subSamplePos] >> 15;
for( n=0; n<points; n++,spos++ )
amp += inputBuf[spos]*sinc[(n<<FIXED_POINT_SHIFT)+issp] >> 15;
outputBuf[position++] = (short)amp;
samplePos += ( subSamplePos += subStep ) >> FIXED_POINT_SHIFT;
subSamplePos &= FIXED_POINT_BITMASK;
}
} else {
// A simple nearest algorithm. Should really implement some filtering beforehand.
while( position < end ) {
outputBuf[position++] = inputBuf[samplePos];
samplePos+=step+((subSamplePos+=subStep) >> FIXED_POINT_SHIFT);
subSamplePos&=FIXED_POINT_BITMASK;
}
}
}
/**
@return the number of extra input samples required before and after the audio.
*/
public int getCushionSize(){
return points;
}
/**
Generate one wing of a Blackman windowed sinc equation.
According to Shannon's sampling theory, a sample should be rendered according to the
equation A Sin(PI*t)/(PI*t) where the sampling frequency is 1/t. This equation goes to
+-infinity so it is multiplied by a window function to make it tail off at the ends.
The more points you use to render each sample, the more accurate the result.
Linear interpolation is actually a very, very course approximation of the above "sinc"
equation, which sorta explains why it works.
*/
protected void genSinc() {
double pit, wpit, t, gain=0.6;
int len = (points<<FIXED_POINT_SHIFT)+1;
sinc = new short[len];
sinc[0] = (short)( 32767 * gain );
for( int n=1; n<len; n++ ) {
t = n/((double)FIXED_POINT_ONE);
pit = Math.PI*t;
wpit = pit/points;
sinc[n] = (short)( (Math.sin(pit)/pit) * (0.42+0.5*Math.cos(wpit)+0.08*Math.cos(2*wpit)) * 32767 * gain );
}
}
/*
Fill the specified buffer with zeros from start to end-1.
*/
protected static void zero( short[] buffer, int start, int end ) {
for( int n=start; n<end; n++ ) buffer[n] = 0;
}
}