public class IIR
{
	public static final byte OFF = 0;
	public static final byte LOWPASS = 1;
	public static final byte HIGHPASS = 2;
	public static final byte RESONANCE = 3;
	public static final byte BANDPASS1 = 4;
	public static final byte BANDPASS2 = 5;
	public static final byte REJECT = 6;
	public static final byte LOWRES = 8;
	public static final byte HIGHRES = 7;
	//
	private int m_sampleRate;
	private double B0, B1, B2, A1, A2, x2, x1, y2, y1;
	//
	public IIR(int sampleRate)
	{
		m_sampleRate = sampleRate;
		reset();
	}
	public void reset()
	{
		B0 = 1.0;
		B1 = B2 = A1 = A2 = x2 = x1 = y2 = y1 = 0.0;
	}
	public void init(byte type, int frequency, double Q, int dBgain)
	{
		double omega = 2.0 * Math.PI * frequency / m_sampleRate;
		double cos = Math.cos( omega );
		double A = Math.sqrt( Math.pow( 10.0, dBgain / 20.0 ) );
		double alpha = Math.sin( omega ) / ( 2.0 * Q );
		//если задана полоса, то другая формула:
	    //alpha = Math.sin( omega ) * Math.sinh( ln(2)/2 * bandWidth * omega / Math.sin( omega ) );
		double beta  = Math.sqrt( A ) / Q;
		//если задан наклон горки, то другая формула:
	    //beta  = sqrt(A)*sqrt[ (A + 1/A)*(1/S - 1) + 2 ]
	    //beta  = sqrt[ (A^2 + 1)/S - (A-1)^2 ]
		double A0 = 1.0 + alpha;
		switch( type )
		{
		default:
		case OFF:
			B0 = 1.0;
			B1 = B2 = A1 = A2 = 0.0;
			break;
		case LOWPASS:
			B1 = ( 1.0 - cos ) / A0;
			B0 = B1 / 2.0;
			B2 = B0;
			A1 = -2.0 * cos / A0;
			A2 = ( 1 - alpha ) / A0;
			break;
		case HIGHPASS:
			B1 = -( 1.0 + cos ) / A0;
			B0 = -B1 / 2.0;
			B2 = B0;
			A1 = -2.0 * cos / A0;
			A2 = ( 1.0 - alpha ) / A0;
			break;
		case RESONANCE:
			A0 = 1.0 + alpha / A;
			B0 = ( 1.0 + alpha * A ) / A0;
			B1 = -2.0 * cos / A0;
			B2 = ( 1.0 - alpha * A ) / A0;
			A1 = B1;
			A2 = ( 1.0 - alpha / A ) / A0;
			break;
		case BANDPASS1://max A = Q
			B0 = Q * alpha / A0;
			B1 = 0.0; 
			B2 = -B0;
			A1 = -2.0 * cos / A0;
			A2 = ( 1.0 - alpha ) / A0;
			break;
		case BANDPASS2:// max A = 0 dB
			B0 = alpha / A0;
			B1 = 0.0;
			B2 = -B0;
			A1 = -2.0 * cos / A0;
			A2 = ( 1.0 - alpha ) / A0;
			break;
		case REJECT:
			B0 = 1.0 / A0;
			B1 = -2 * cos / A0;
			B2 = B0;
			A1 = B1;
			A2 = ( 1 - alpha ) / A0;
			break;
		case LOWRES:
		{
			double ap = A + 1.0;
			double am = A - 1.0;
			double amcos = am * cos;
			double apcos = ap * cos;
			double bsin = beta * Math.sin( omega );
			A0 =             ap + amcos + bsin;
			B0 =       A * ( ap - amcos + bsin ) / A0;
			B1 = 2.0 * A * ( am - apcos ) / A0;
			B2 =       A * ( ap - amcos - bsin ) / A0;
			A1 =    -2.0 * ( am + apcos ) / A0;
			A2 =           ( ap + amcos - bsin ) / A0;
			break;
		}
		case HIGHRES:
		{
			double ap = A + 1.0;
			double am = A - 1.0;
			double amcos = am * cos;
			double apcos = ap * cos;
			double bsin = beta * Math.sin( omega );
			A0 =              ap - amcos + bsin;
			B0 =        A * ( ap + amcos + bsin ) / A0;
			B1 = -2.0 * A * ( am + apcos ) / A0;
			B2 =        A * ( ap + amcos - bsin ) / A0;
			A1 =      2.0 * ( am - apcos ) / A0;
			A2 =            ( ap - amcos - bsin ) / A0;
			break;
		}
		}
	}
	public int proc(int s)
	{
		double y = B0 * (double)s + B1 * x1 + B2 * x2 - A1 * y1 - A2 * y2;
		x2 = x1;
		x1 = s;
		y2 = y1;
		y1 = y;
		return (int)(y > 0 ? y + 0.5 : y - 0.5);
	}
}